diff --git a/.gitignore b/.gitignore
index 8f2a0681..73b2ad82 100644
--- a/.gitignore
+++ b/.gitignore
@@ -426,9 +426,10 @@
 /tools/swarming_client
 /tools/tryserver
 /tools/win/link_limiter/build
+/ui/file_manager/internal
+/ui/keyboard/keyboard_mojom_bindings.xml
 /ui/surface/surface.xml
 /ui/surface/surface_gpu_tests.xml
-/ui/keyboard/keyboard_mojom_bindings.xml
 /v8
 /webkit/data/bmp_decoder
 /webkit/data/ico_decoder
diff --git a/AUTHORS b/AUTHORS
index 177d255a..03cc1740 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -492,6 +492,7 @@
 Thiago Farina <thiago.farina@gmail.com>
 Thiago Marcos P. Santos <thiago.santos@intel.com>
 Thomas Butter <tbutter@gmail.com>
+Thomas Conti <tomc@amazon.com>
 Tiago Vignatti <tiago.vignatti@intel.com>
 Tim Ansell <mithro@mithis.com>
 Timo Reimann <ttr314@googlemail.com>
diff --git a/BUILD.gn b/BUILD.gn
index a8000b6..74b51b9 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -375,7 +375,7 @@
     if (use_x11) {
       deps += [ "//media:player_x11" ]
       if (target_cpu != "arm") {
-        deps += [ "//gpu:compositor_model_bench" ]
+        deps += [ "//gpu/tools/compositor_model_bench" ]
       }
     }
   }
diff --git a/DEPS b/DEPS
index cda593d..7e7ec66 100644
--- a/DEPS
+++ b/DEPS
@@ -34,31 +34,31 @@
   'llvm_url': 'http://src.chromium.org/llvm-project',
   'llvm_git': 'https://llvm.googlesource.com',
   'webkit_trunk': 'http://src.chromium.org/blink/trunk',
-  'webkit_revision': '5875d93688889d01a6b5c98bddf0306eae08a685', # from svn revision 192423
+  'webkit_revision': 'a5c5be52757c9d75bffaf2e17d0e796e7c232aff', # from svn revision 192653
   'chromium_git': 'https://chromium.googlesource.com',
   'chromiumos_git': 'https://chromium.googlesource.com/chromiumos',
   'pdfium_git': 'https://pdfium.googlesource.com',
   'skia_git': 'https://skia.googlesource.com',
   'boringssl_git': 'https://boringssl.googlesource.com',
-  'libvpx_revision': '2c87306660d049be370ce4475f3d30869e5f2796',
+  'libvpx_revision': '861f35b01c87a021540aace739cb7415c08e987b',
   'sfntly_revision': '1bdaae8fc788a5ac8936d68bf24f37d977a13dac',
-  'skia_revision': '62a320c8d444cd04e4f2952c269ea4cbd58dee64',
+  'skia_revision': '41f88f0251cf48eb06f3f8d143aac8562a230889',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and V8 without interference from each other.
   'v8_branch': 'trunk',
-  'v8_revision': '19b29c5cdb6d818239af70c91c433f0b261f94e1',
+  'v8_revision': 'e4dfe3b9ee365b1b4193a4f00986db9d6da89f6a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling WebRTC
   # and V8 without interference from each other.
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
-  'swarming_revision': 'b61a1802f5ef4bb8c7b81060cc80add47e6cf302',
+  'swarming_revision': '53ef013289e19388fe13e074de23188e489d0225',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'bdd419f9f5b006e913606e7363125942c8ae06bc',
+  'angle_revision': '1ea584c562b74e0a5bb7420d702fb613195175b5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -94,7 +94,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': 'cc3597c9c13978079ef85a31d78dc4bf7f18000c',
+  'nacl_revision': 'b7793fedbadcf1c2c10c7f1a2ffb0f41342750ad',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -161,7 +161,7 @@
     Var('chromium_git') + '/external/snappy.git' + '@' + '762bb32f0c9d2f31ba4958c7c0933d22e80c20bf',
 
   'src/tools/grit':
-    Var('chromium_git') + '/external/grit-i18n.git' + '@' + '0287c187b11ed53590254e4d817e836a44a7a1a7', # from svn revision 186
+    Var('chromium_git') + '/external/grit-i18n.git' + '@' + '0ac6d137916c4d7a45ae7d61f892652a1a4cce33', # from svn revision 188
 
   'src/tools/gyp':
     Var('chromium_git') + '/external/gyp.git' + '@' + 'd174d75bf69c682cb62af9187879e01513b35e52',
@@ -209,7 +209,7 @@
    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + 'b3dc5aa44575049edf6b24e34578487dfaade6a4',
 
   'src/third_party/libjingle/source/talk':
-    Var('chromium_git') + '/external/webrtc/trunk/talk.git' + '@' + 'fed547e9df46fb9a5b5a62db7edf131225e09e36', # commit position 8836
+    Var('chromium_git') + '/external/webrtc/trunk/talk.git' + '@' + '4d55b53695510061f5fd449d6399c75436e4c39e', # commit position 8869
 
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/usrsctplib.git' + '@' + '13718c7b9fd376fde092cbd3c5347d15059ac652', # from svn revision 9167
@@ -233,7 +233,7 @@
    Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '2112dbbe491a5dbf28ed33978530e7fcaedb4a04', # commit position 8837
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '547c64a45a3d5497373deed524c4e2e79d0f6aa1', # commit position 8867
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
@@ -292,7 +292,7 @@
     Var('chromium_git') + '/external/py_trace_event.git' + '@' + 'dd463ea9e2c430de2b9e53dea57a77b4c3ac9b30',
 
   'src/third_party/dom_distiller_js/dist':
-    Var('chromium_git') + '/external/github.com/chromium/dom-distiller-dist.git' + '@' + '964272de851ef5d5d1096d319526b992d8d3d7f6',
+    Var('chromium_git') + '/external/github.com/chromium/dom-distiller-dist.git' + '@' + '419c7e659cf1b35763d5e6ce4a757fe15f4f37db',
 }
 
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 1f70dedf..d0e6b37 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -473,7 +473,10 @@
     },
     'metrics_xml_files': {
       # Subscribe to this to watch for changes to histograms.xml.
-      'filepath': 'tools/metrics/.*\.xml$'
+      'filepath': 'tools/metrics/.*\.xml$',
+    },
+    'midi': {
+      'filepath': 'midi',
     },
     'mojo': {
       'filepath': 'mojo',
@@ -820,8 +823,10 @@
     'android_media': ['avayvod+watch@chromium.org'],
     'android_tab': ['avayvod+watch@chromium.org', 'dtrainor@chromium.org'],
     'android_webview': ['android-webview-reviews@chromium.org'],
-    'app_list': ['tfarina@chromium.org', 'chrome-apps-syd-reviews@chromium.org'],
-    'app_shortcuts': ['chrome-apps-syd-reviews@chromium.org'],
+    'app_list': ['tfarina@chromium.org',
+                 'mgiuca@chromium.org',
+                 'tapted@chromium.org'],
+    'app_shortcuts': ['mgiuca@chromium.org', 'tapted@chromium.org'],
     'appcache': ['michaeln@chromium.org'],
     'apps': ['tfarina@chromium.org', 'chromium-apps-reviews@chromium.org'],
     'ash': ['sadrul@chromium.org',
@@ -962,6 +967,7 @@
     'message_loop': ['sadrul@chromium.org'],
     'metrics': ['asvitkine+watch@chromium.org'],
     'metrics_xml_files': ['asvitkine+watch@chromium.org'],
+    'midi': ['toyoshim+midi@chromium.org'],
     'mojo': ['aa@chromium.org',
              'abarth@chromium.org',
              'ben+mojo@chromium.org',
diff --git a/android_webview/android_webview.gyp b/android_webview/android_webview.gyp
index e78dabe11..ca004138 100644
--- a/android_webview/android_webview.gyp
+++ b/android_webview/android_webview.gyp
@@ -20,7 +20,7 @@
           'action_name': 'repack_android_webview_pack',
           'variables': {
             'pak_inputs': [
-              '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources.pak',
+              '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources_100_percent.pak',
               '<(SHARED_INTERMEDIATE_DIR)/content/app/resources/content_resources_100_percent.pak',
               '<(SHARED_INTERMEDIATE_DIR)/content/content_resources.pak',
               '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.pak',
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index fd82607..f4c0b63 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -392,7 +392,7 @@
       AwBrowserPermissionRequestDelegate::FromID(render_process_id,
                                                  render_view_id);
   switch (permission) {
-    case content::PERMISSION_GEOLOCATION:
+    case content::PermissionType::GEOLOCATION:
       if (!delegate) {
         DVLOG(0) << "Dropping GeolocationPermission request";
         callback.Run(content::PERMISSION_STATUS_DENIED);
@@ -401,7 +401,7 @@
       delegate->RequestGeolocationPermission(
           origin, base::Bind(&CallbackPermisisonStatusWrapper, callback));
       break;
-    case content::PERMISSION_PROTECTED_MEDIA_IDENTIFIER:
+    case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
       if (!delegate) {
         DVLOG(0) << "Dropping ProtectedMediaIdentifierPermission request";
         callback.Run(content::PERMISSION_STATUS_DENIED);
@@ -410,14 +410,15 @@
       delegate->RequestProtectedMediaIdentifierPermission(
           origin, base::Bind(&CallbackPermisisonStatusWrapper, callback));
       break;
-    case content::PERMISSION_MIDI_SYSEX:
-    case content::PERMISSION_NOTIFICATIONS:
-    case content::PERMISSION_PUSH_MESSAGING:
+    case content::PermissionType::MIDI_SYSEX:
+    case content::PermissionType::NOTIFICATIONS:
+    case content::PermissionType::PUSH_MESSAGING:
       NOTIMPLEMENTED() << "RequestPermission not implemented for "
-                       << permission;
+                       << static_cast<int>(permission);
       break;
-    case content::PERMISSION_NUM:
-      NOTREACHED() << "Invalid RequestPermission for " << permission;
+    case content::PermissionType::NUM:
+      NOTREACHED() << "Invalid RequestPermission for "
+                   << static_cast<int>(permission);
       break;
   }
 }
@@ -435,19 +436,21 @@
   if (!delegate)
     return;
   switch (permission) {
-    case content::PERMISSION_GEOLOCATION:
+    case content::PermissionType::GEOLOCATION:
       delegate->CancelGeolocationPermissionRequests(origin);
       break;
-    case content::PERMISSION_PROTECTED_MEDIA_IDENTIFIER:
+    case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
       delegate->CancelProtectedMediaIdentifierPermissionRequests(origin);
       break;
-    case content::PERMISSION_MIDI_SYSEX:
-    case content::PERMISSION_NOTIFICATIONS:
-    case content::PERMISSION_PUSH_MESSAGING:
-      NOTIMPLEMENTED() << "CancelPermission not implemented for " << permission;
+    case content::PermissionType::MIDI_SYSEX:
+    case content::PermissionType::NOTIFICATIONS:
+    case content::PermissionType::PUSH_MESSAGING:
+      NOTIMPLEMENTED() << "CancelPermission not implemented for "
+                       << static_cast<int>(permission);
       break;
-    case content::PERMISSION_NUM:
-      NOTREACHED() << "Invalid CancelPermission for " << permission;
+    case content::PermissionType::NUM:
+      NOTREACHED() << "Invalid CancelPermission for "
+                   << static_cast<int>(permission);
       break;
   }
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 0f8974aa..f3273ac 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -286,12 +286,16 @@
 
     private static final class DestroyRunnable implements Runnable {
         private final long mNativeAwContents;
-        private DestroyRunnable(long nativeAwContents) {
+        private final WindowAndroid mWindowAndroid;
+
+        private DestroyRunnable(long nativeAwContents, WindowAndroid windowAndroid) {
             mNativeAwContents = nativeAwContents;
+            mWindowAndroid = windowAndroid;
         }
         @Override
         public void run() {
             nativeDestroy(mNativeAwContents);
+            mWindowAndroid.destroy();
         }
     }
 
@@ -863,15 +867,13 @@
         // each other, we should update |mBrowserContext| according to the newly received native
         // WebContent's browser context.
 
-        // The native side object has been bound to this java instance, so now is the time to
-        // bind all the native->java relationships.
-        mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeAwContents));
-
         WebContents webContents = nativeGetWebContents(mNativeAwContents);
 
+        // WebView does not currently initialize ApplicationStatus, crbug.com/470582.
+        final boolean listenToActivityState = false;
         Activity activity = ContentViewCore.activityFromContext(mContext);
         mWindowAndroid = activity != null
-                ? new ActivityWindowAndroid(activity)
+                ? new ActivityWindowAndroid(activity, listenToActivityState)
                 : new WindowAndroid(mContext.getApplicationContext());
         mContentViewCore = createAndInitializeContentViewCore(
                 mContainerView, mContext, mInternalAccessAdapter, webContents,
@@ -884,6 +886,11 @@
         mSettings.setWebContents(webContents);
         nativeSetDipScale(mNativeAwContents, (float) mDIPScale);
         mContentViewCore.onShow();
+
+        // The native side object has been bound to this java instance, so now is the time to
+        // bind all the native->java relationships.
+        mCleanupReference =
+                new CleanupReference(this, new DestroyRunnable(mNativeAwContents, mWindowAndroid));
     }
 
     private void installWebContentsObserver() {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
index f600470..2652e851 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
@@ -23,14 +23,14 @@
     // reference to an AwWebContentsObserver instance. This is not intentional,
     // and should be found and cleaned up.
     private final WeakReference<AwContents> mAwContents;
-    private final AwContentsClient mAwContentsClient;
+    private final WeakReference<AwContentsClient> mAwContentsClient;
     private boolean mStartedNonApiProvisionalLoadInMainFrame = false;
 
     public AwWebContentsObserver(
             WebContents webContents, AwContents awContents, AwContentsClient awContentsClient) {
         super(webContents);
-        mAwContents = new WeakReference<AwContents>(awContents);
-        mAwContentsClient = awContentsClient;
+        mAwContents = new WeakReference<>(awContents);
+        mAwContentsClient = new WeakReference<>(awContentsClient);
     }
 
     boolean hasStartedNonApiProvisionalLoadInMainFrame() {
@@ -39,24 +39,28 @@
 
     @Override
     public void didFinishLoad(long frameId, String validatedUrl, boolean isMainFrame) {
+        AwContentsClient client = mAwContentsClient.get();
+        if (client == null) return;
         String unreachableWebDataUrl = AwContentsStatics.getUnreachableWebDataUrl();
         boolean isErrorUrl =
                 unreachableWebDataUrl != null && unreachableWebDataUrl.equals(validatedUrl);
         if (isMainFrame && !isErrorUrl) {
-            mAwContentsClient.onPageFinished(validatedUrl);
+            client.onPageFinished(validatedUrl);
         }
     }
 
     @Override
     public void didFailLoad(boolean isProvisionalLoad,
             boolean isMainFrame, int errorCode, String description, String failingUrl) {
+        AwContentsClient client = mAwContentsClient.get();
+        if (client == null) return;
         String unreachableWebDataUrl = AwContentsStatics.getUnreachableWebDataUrl();
         boolean isErrorUrl =
                 unreachableWebDataUrl != null && unreachableWebDataUrl.equals(failingUrl);
         if (isMainFrame && !isErrorUrl && errorCode == NetError.ERR_ABORTED) {
             // Need to call onPageFinished for backwards compatibility with the classic webview.
             // See also AwContents.IoThreadClientImpl.onReceivedError.
-            mAwContentsClient.onPageFinished(failingUrl);
+            client.onPageFinished(failingUrl);
         }
     }
 
@@ -74,23 +78,30 @@
                         awContents.insertVisualStateCallback(0, new VisualStateCallback() {
                             @Override
                             public void onComplete(long requestId) {
-                                mAwContentsClient.onPageCommitVisible(url);
+                                AwContentsClient client = mAwContentsClient.get();
+                                if (client == null) return;
+                                client.onPageCommitVisible(url);
                             }
                         });
                     }
                 }
             });
         }
+
         // This is here to emulate the Classic WebView firing onPageFinished for main frame
         // navigations where only the hash fragment changes.
         if (isFragmentNavigation) {
-            mAwContentsClient.onPageFinished(url);
+            AwContentsClient client = mAwContentsClient.get();
+            if (client == null) return;
+            client.onPageFinished(url);
         }
     }
 
     @Override
     public void didNavigateAnyFrame(String url, String baseUrl, boolean isReload) {
-        mAwContentsClient.doUpdateVisitedHistory(url, isReload);
+        final AwContentsClient client = mAwContentsClient.get();
+        if (client == null) return;
+        client.doUpdateVisitedHistory(url, isReload);
     }
 
     @Override
diff --git a/android_webview/java/src/org/chromium/android_webview/ErrorCodeConversionHelper.java b/android_webview/java/src/org/chromium/android_webview/ErrorCodeConversionHelper.java
index 7656d8e..a4d38885 100644
--- a/android_webview/java/src/org/chromium/android_webview/ErrorCodeConversionHelper.java
+++ b/android_webview/java/src/org/chromium/android_webview/ErrorCodeConversionHelper.java
@@ -42,8 +42,6 @@
     public static final int ERROR_FILE_NOT_FOUND = -14;
     // Too many requests during this load
     public static final int ERROR_TOO_MANY_REQUESTS = -15;
-    // Request blocked by the browser
-    public static final int ERROR_BLOCKED = -16;
 
     static int convertErrorCode(int netError) {
         // Note: many NetError.Error constants don't have an obvious mapping.
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
index 85db017..0478a00 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
@@ -139,6 +139,14 @@
                 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
     }
 
+    @SuppressFBWarnings("URF_UNREAD_FIELD")
+    private static class StrongRefTestAwContentsClient extends TestAwContentsClient {
+        private AwContents mAwContentsStrongRef;
+        public void setAwContentsStrongRef(AwContents awContents) {
+            mAwContentsStrongRef = awContents;
+        }
+    }
+
     @DisableHardwareAccelerationForTest
     @LargeTest
     @Feature({"AndroidWebView"})
@@ -159,7 +167,13 @@
         });
         for (int i = 0; i < repetitions; ++i) {
             for (int j = 0; j < concurrentInstances; ++j) {
-                AwTestContainerView view = createAwTestContainerViewOnMainSync(mContentsClient);
+                final StrongRefTestAwContentsClient client = new StrongRefTestAwContentsClient();
+                AwTestContainerView view = createAwTestContainerViewOnMainSync(client);
+                // Embedding app can hold onto a strong ref to the WebView from either WebViewClient
+                // or WebChromeClient. That should not prevent WebView from gc-ed. We simulate that
+                // behavior by making the equivalent change here, have AwContentsClient hold a
+                // strong ref to the AwContents object.
+                client.setAwContentsStrongRef(view.getAwContents());
                 loadUrlAsync(view.getAwContents(), "about:blank");
             }
             assertTrue(AwContents.getNativeInstanceCount() >= concurrentInstances);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedError2Test.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedError2Test.java
index 971938d2..c5ab9dae 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedError2Test.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedError2Test.java
@@ -380,37 +380,4 @@
         assertEquals(onReceivedError2CallCount + 1, onReceivedError2Helper.getCallCount());
         assertEquals(BAD_HTML_URL, onReceivedError2Helper.getRequest().url);
     }
-
-    /*
-     * TODO(mnaganov): Implement, add tests for other security blocks in Blink.
-    @SmallTest
-    @Feature({"AndroidWebView"})
-    public void testOnXFrameOptionsDenial() throws Throwable {
-        startWebServer();
-        final String iframeHtml = CommonResources.makeHtmlPageFrom(
-                "", "You shouldn't see me :)");
-        List<Pair<String, String>> iframeHeaders = new ArrayList<Pair<String, String>>();
-        iframeHeaders.add(Pair.create("x-frame-options", "DENY"));
-        final String iframeUrl = mWebServer.setResponse("/iframe.html", iframeHeaders);
-        final String pageHtml = CommonResources.makeHtmlPageFrom(
-                "", "<iframe src='" + iframeUrl + "' />");
-        getAwSettingsOnUiThread(mAwContents).setCacheMode(WebSettings.LOAD_CACHE_ONLY);
-        loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
-                pageHtml, "text/html", false);
-
-        TestAwContentsClient.OnReceivedError2Helper onReceivedError2Helper =
-                mContentsClient.getOnReceivedError2Helper();
-        AwWebResourceRequest request = onReceivedError2Helper.getRequest();
-        assertNotNull(request);
-        assertEquals(iframeUrl, request.url);
-        assertEquals("GET", request.method);
-        assertNotNull(request.requestHeaders);
-        assertFalse(request.requestHeaders.isEmpty());
-        assertFalse(request.isMainFrame);
-        assertFalse(request.hasUserGesture);
-        AwWebResourceError error = onReceivedError2Helper.getError();
-        assertEquals(ErrorCodeConversionHelper.ERROR_BLOCKED, error.errorCode);
-        assertNotNull(error.description);
-    }
-    */
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ConsoleMessagesForBlockedLoadsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ConsoleMessagesForBlockedLoadsTest.java
new file mode 100644
index 0000000..6064b56
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ConsoleMessagesForBlockedLoadsTest.java
@@ -0,0 +1,137 @@
+// 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 android.os.Build;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+import android.webkit.ConsoleMessage;
+
+import org.chromium.android_webview.AwContents;
+import org.chromium.android_webview.AwSettings;
+import org.chromium.android_webview.test.util.CommonResources;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
+import org.chromium.net.test.util.TestWebServer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Verify that content loading blocks initiated by renderer can be detected
+ * by the embedder via WebChromeClient.onConsoleMessage.
+ */
+@MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT)
+public class ConsoleMessagesForBlockedLoadsTest extends AwTestBase {
+
+    private TestAwContentsClient mContentsClient;
+    private AwTestContainerView mTestContainerView;
+    private TestAwContentsClient.AddMessageToConsoleHelper mOnConsoleMessageHelper;
+    private AwContents mAwContents;
+    private TestWebServer mWebServer;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mContentsClient = new TestAwContentsClient();
+        mTestContainerView = createAwTestContainerViewOnMainSync(mContentsClient);
+        mAwContents = mTestContainerView.getAwContents();
+        mOnConsoleMessageHelper = mContentsClient.getAddMessageToConsoleHelper();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mWebServer != null) mWebServer.shutdown();
+        super.tearDown();
+    }
+
+    private void startWebServer() throws Exception {
+        mWebServer = TestWebServer.start();
+    }
+
+    private ConsoleMessage getSingleErrorMessage() {
+        ConsoleMessage result = null;
+        for (ConsoleMessage m : mOnConsoleMessageHelper.getMessages()) {
+            if (m.messageLevel() == ConsoleMessage.MessageLevel.ERROR) {
+                assertNull(result);
+                result = m;
+            }
+        }
+        assertNotNull(result);
+        return result;
+    }
+
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testXFrameOptionsDenial() throws Throwable {
+        startWebServer();
+        final String iframeHtml = CommonResources.makeHtmlPageFrom("", "FAIL");
+        List<Pair<String, String>> iframeHeaders = new ArrayList<Pair<String, String>>();
+        iframeHeaders.add(Pair.create("x-frame-options", "DENY"));
+        final String iframeUrl = mWebServer.setResponse("/iframe.html", iframeHtml, iframeHeaders);
+        final String pageHtml = CommonResources.makeHtmlPageFrom(
+                "", "<iframe src='" + iframeUrl + "' />");
+        final String pageUrl = mWebServer.setResponse("/page.html", pageHtml, null);
+        mOnConsoleMessageHelper.clearMessages();
+        loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
+        ConsoleMessage errorMessage = getSingleErrorMessage();
+        assertTrue(errorMessage.message().indexOf(iframeUrl) != -1);
+    }
+
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testMixedContentDenial() throws Throwable {
+        startWebServer();
+        TestWebServer httpsServer = null;
+        AwSettings settings = getAwSettingsOnUiThread(mAwContents);
+        settings.setMixedContentMode(AwSettings.MIXED_CONTENT_NEVER_ALLOW);
+        try {
+            httpsServer = TestWebServer.startSsl();
+            final String imageUrl = mWebServer.setResponseBase64(
+                    "/insecure.png", CommonResources.FAVICON_DATA_BASE64, null);
+            final String secureHtml = CommonResources.makeHtmlPageFrom(
+                    "", "<img src='" + imageUrl + "' />");
+            String secureUrl = httpsServer.setResponse("/secure.html", secureHtml, null);
+            mOnConsoleMessageHelper.clearMessages();
+            loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), secureUrl);
+            ConsoleMessage errorMessage = getSingleErrorMessage();
+            assertTrue(errorMessage.message().indexOf(imageUrl) != -1);
+            assertTrue(errorMessage.message().indexOf(secureUrl) != -1);
+        } finally {
+            if (httpsServer != null) {
+                httpsServer.shutdown();
+            }
+        }
+    }
+
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testCrossOriginDenial() throws Throwable {
+        startWebServer();
+        final String iframeXsl =
+                "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>"
+                + "<xsl:template match='*'>"
+                + "<html><body>FAIL</body></html>"
+                + "</xsl:template>"
+                + "</xsl:stylesheet>";
+        final String iframeXslUrl = mWebServer.setResponse(
+                "/iframe.xsl", iframeXsl, null).replace("localhost", "127.0.0.1");
+        final String iframeXml =
+                "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<?xml-stylesheet type='text/xsl' href='" + iframeXslUrl + "'?>"
+                + "<html xmlns='http://www.w3.org/1999/xhtml'>"
+                + "<body>PASS</body></html>";
+        final String iframeXmlUrl = mWebServer.setResponse("/iframe.xml", iframeXml, null);
+        final String pageHtml = CommonResources.makeHtmlPageFrom(
+                "", "<iframe src='" + iframeXmlUrl + "' />");
+        final String pageUrl = mWebServer.setResponse("/page.html", pageHtml, null);
+        mOnConsoleMessageHelper.clearMessages();
+        loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
+        ConsoleMessage errorMessage = getSingleErrorMessage();
+        assertTrue(errorMessage.message().indexOf(iframeXslUrl) != -1);
+        assertTrue(errorMessage.message().indexOf(iframeXmlUrl) != -1);
+    }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java b/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java
index a890f5d..d368612 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java
@@ -19,6 +19,9 @@
 import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageStartedHelper;
 import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnReceivedErrorHelper;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * AwContentsClient subclass used for testing.
  */
@@ -327,8 +330,7 @@
 
     @Override
     public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
-        mAddMessageToConsoleHelper.notifyCalled(consoleMessage.messageLevel().ordinal(),
-                consoleMessage.message(), consoleMessage.lineNumber(), consoleMessage.sourceId());
+        mAddMessageToConsoleHelper.notifyCalled(consoleMessage);
         return false;
     }
 
@@ -336,36 +338,42 @@
      * Callback helper for AddMessageToConsole.
      */
     public static class AddMessageToConsoleHelper extends CallbackHelper {
-        private int mLevel;
-        private String mMessage;
-        private int mLineNumber;
-        private String mSourceId;
+        private List<ConsoleMessage> mMessages = new ArrayList<ConsoleMessage>();
+
+        public void clearMessages() {
+            mMessages.clear();
+        }
+
+        public List<ConsoleMessage> getMessages() {
+            return mMessages;
+        }
 
         public int getLevel() {
             assert getCallCount() > 0;
-            return mLevel;
+            return getLastMessage().messageLevel().ordinal();
         }
 
         public String getMessage() {
             assert getCallCount() > 0;
-            return mMessage;
+            return getLastMessage().message();
         }
 
         public int getLineNumber() {
             assert getCallCount() > 0;
-            return mLineNumber;
+            return getLastMessage().lineNumber();
         }
 
         public String getSourceId() {
             assert getCallCount() > 0;
-            return mSourceId;
+            return getLastMessage().sourceId();
         }
 
-        void notifyCalled(int level, String message, int lineNumer, String sourceId) {
-            mLevel = level;
-            mMessage = message;
-            mLineNumber = lineNumer;
-            mSourceId = sourceId;
+        private ConsoleMessage getLastMessage() {
+            return mMessages.get(mMessages.size() - 1);
+        }
+
+        void notifyCalled(ConsoleMessage message) {
+            mMessages.add(message);
             notifyCalled();
         }
     }
diff --git a/android_webview/lib/main/aw_main_delegate.cc b/android_webview/lib/main/aw_main_delegate.cc
index d21241a..127baa7 100644
--- a/android_webview/lib/main/aw_main_delegate.cc
+++ b/android_webview/lib/main/aw_main_delegate.cc
@@ -90,6 +90,9 @@
   // This is needed for sharing textures across the different GL threads.
   cl->AppendSwitch(switches::kEnableThreadedTextureMailboxes);
 
+  // WebView does not yet support screen orientation locking.
+  cl->AppendSwitch(switches::kDisableScreenOrientationLock);
+
   // This is needed to be able to mmap the V8 snapshot and ICU data file
   // directly from the WebView .apk.
   // This needs to be here so that it gets to run before the code in
diff --git a/ash/accelerators/accelerator_interactive_uitest_chromeos.cc b/ash/accelerators/accelerator_interactive_uitest_chromeos.cc
new file mode 100644
index 0000000..4bb7d16
--- /dev/null
+++ b/ash/accelerators/accelerator_interactive_uitest_chromeos.cc
@@ -0,0 +1,223 @@
+// 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 "ash/accelerators/accelerator_controller.h"
+
+#include "ash/shell.h"
+#include "ash/shell_observer.h"
+#include "ash/system/chromeos/network/network_observer.h"
+#include "ash/system/tray/system_tray_delegate.h"
+#include "ash/system/tray/system_tray_notifier.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/test/test_screenshot_delegate.h"
+#include "ash/test/test_volume_control_delegate.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/window_util.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "chromeos/network/network_handler.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/test/ui_controls.h"
+#include "ui/base/ui_base_paths.h"
+#include "ui/gl/gl_surface.h"
+
+namespace ash {
+namespace test {
+
+namespace {
+
+// A network observer to watch for the toggle wifi events.
+class TestNetworkObserver : public NetworkObserver {
+ public:
+  TestNetworkObserver() : wifi_enabled_status_(false) {}
+
+  // ash::NetworkObserver:
+  void RequestToggleWifi() override {
+    wifi_enabled_status_ = !wifi_enabled_status_;
+  }
+
+  bool wifi_enabled_status() const { return wifi_enabled_status_; }
+
+ private:
+  bool wifi_enabled_status_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestNetworkObserver);
+};
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+// This is intended to test few samples from each category of accelerators to
+// make sure they work properly. The test is done as an interactive ui test
+// using ui_controls::Send*() functions.
+// This is to catch any future regressions (crbug.com/469235).
+class AcceleratorInteractiveUITest : public AshTestBase, public ShellObserver {
+ public:
+  AcceleratorInteractiveUITest() : is_in_overview_mode_(false) {}
+
+  void SetUp() override {
+    gfx::GLSurface::InitializeOneOffForTests();
+
+    ui::RegisterPathProvider();
+    ui::ResourceBundle::InitSharedInstanceWithLocale(
+        "en-US", NULL, ui::ResourceBundle::LOAD_COMMON_RESOURCES);
+    base::FilePath resources_pack_path;
+    PathService::Get(base::DIR_MODULE, &resources_pack_path);
+    resources_pack_path =
+        resources_pack_path.Append(FILE_PATH_LITERAL("resources.pak"));
+    ResourceBundle::GetSharedInstance().AddDataPackFromPath(
+        resources_pack_path, ui::SCALE_FACTOR_NONE);
+
+    AshTestBase::SetUp();
+
+    Shell::GetInstance()->AddShellObserver(this);
+
+    chromeos::NetworkHandler::Initialize();
+  }
+
+  void TearDown() override {
+    chromeos::NetworkHandler::Shutdown();
+
+    Shell::GetInstance()->RemoveShellObserver(this);
+
+    AshTestBase::TearDown();
+  }
+
+  // Sends a key press event and waits synchronously until it's completely
+  // processed.
+  void SendKeyPressSync(ui::KeyboardCode key,
+                        bool control,
+                        bool shift,
+                        bool alt) {
+    base::RunLoop loop;
+    ui_controls::SendKeyPressNotifyWhenDone(root_window(), key, control, shift,
+                                            alt, false, loop.QuitClosure());
+    loop.Run();
+  }
+
+  // ash::ShellObserver:
+  void OnOverviewModeStarting() override { is_in_overview_mode_ = true; }
+  void OnOverviewModeEnded() override { is_in_overview_mode_ = false; }
+
+  Shell* shell() const { return Shell::GetInstance(); }
+  aura::Window* root_window() const { return Shell::GetPrimaryRootWindow(); }
+
+ protected:
+  bool is_in_overview_mode_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AcceleratorInteractiveUITest);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(OFFICIAL_BUILD)
+#define MAYBE_NonRepeatableNeedingWindowActions \
+  DISABLED_NonRepeatableNeedingWindowActions
+#define MAYBE_ChromeOsAccelerators DISABLED_ChromeOsAccelerators
+#define MAYBE_ToggleAppList DISABLED_ToggleAppList
+#else
+#define MAYBE_NonRepeatableNeedingWindowActions \
+  NonRepeatableNeedingWindowActions
+#define MAYBE_ChromeOsAccelerators ChromeOsAccelerators
+#define MAYBE_ToggleAppList ToggleAppList
+#endif  // defined(OFFICIAL_BUILD)
+
+// Tests a sample of the non-repeatable accelerators that need windows to be
+// enabled.
+TEST_F(AcceleratorInteractiveUITest, MAYBE_NonRepeatableNeedingWindowActions) {
+  // Create a bunch of windows to work with.
+  aura::Window* window_1 =
+      CreateTestWindowInShellWithBounds(gfx::Rect(0, 0, 100, 100));
+  aura::Window* window_2 =
+      CreateTestWindowInShellWithBounds(gfx::Rect(0, 0, 100, 100));
+  window_1->Show();
+  wm::ActivateWindow(window_1);
+  window_2->Show();
+  wm::ActivateWindow(window_2);
+
+  // Test TOGGLE_OVERVIEW.
+  EXPECT_FALSE(is_in_overview_mode_);
+  SendKeyPressSync(ui::VKEY_MEDIA_LAUNCH_APP1, false, false, false);
+  EXPECT_TRUE(is_in_overview_mode_);
+  SendKeyPressSync(ui::VKEY_MEDIA_LAUNCH_APP1, false, false, false);
+  EXPECT_FALSE(is_in_overview_mode_);
+
+  // Test CYCLE_FORWARD_MRU and CYCLE_BACKWARD_MRU.
+  wm::ActivateWindow(window_1);
+  EXPECT_TRUE(wm::IsActiveWindow(window_1));
+  EXPECT_FALSE(wm::IsActiveWindow(window_2));
+  SendKeyPressSync(ui::VKEY_TAB, false, false, true);  // CYCLE_FORWARD_MRU.
+  EXPECT_TRUE(wm::IsActiveWindow(window_2));
+  EXPECT_FALSE(wm::IsActiveWindow(window_1));
+  SendKeyPressSync(ui::VKEY_TAB, false, true, true);  // CYCLE_BACKWARD_MRU.
+  EXPECT_TRUE(wm::IsActiveWindow(window_1));
+  EXPECT_FALSE(wm::IsActiveWindow(window_2));
+
+  // Test TOGGLE_FULLSCREEN.
+  wm::WindowState* active_window_state = wm::GetActiveWindowState();
+  EXPECT_FALSE(active_window_state->IsFullscreen());
+  SendKeyPressSync(ui::VKEY_MEDIA_LAUNCH_APP2, false, false, false);
+  EXPECT_TRUE(active_window_state->IsFullscreen());
+  SendKeyPressSync(ui::VKEY_MEDIA_LAUNCH_APP2, false, false, false);
+  EXPECT_FALSE(active_window_state->IsFullscreen());
+}
+
+// Tests a sample of ChromeOS specific accelerators.
+TEST_F(AcceleratorInteractiveUITest, ChromeOsAccelerators) {
+  // Test TAKE_SCREENSHOT and TAKE_PARTIAL_SCREENSHOT.
+  TestScreenshotDelegate* screenshot_delegate = GetScreenshotDelegate();
+  screenshot_delegate->set_can_take_screenshot(true);
+  EXPECT_EQ(0, screenshot_delegate->handle_take_screenshot_count());
+  SendKeyPressSync(ui::VKEY_MEDIA_LAUNCH_APP1, true, false, false);
+  EXPECT_EQ(1, screenshot_delegate->handle_take_screenshot_count());
+  SendKeyPressSync(ui::VKEY_PRINT, false, false, false);
+  EXPECT_EQ(2, screenshot_delegate->handle_take_screenshot_count());
+  SendKeyPressSync(ui::VKEY_MEDIA_LAUNCH_APP1, true, true, false);
+  EXPECT_EQ(2, screenshot_delegate->handle_take_screenshot_count());
+  // Press ESC to go out of the partial screenshot mode.
+  SendKeyPressSync(ui::VKEY_ESCAPE, false, false, false);
+
+  // Test VOLUME_MUTE, VOLUME_DOWN, and VOLUME_UP.
+  TestVolumeControlDelegate* volume_delegate = new TestVolumeControlDelegate;
+  shell()->system_tray_delegate()->SetVolumeControlDelegate(
+      scoped_ptr<VolumeControlDelegate>(volume_delegate).Pass());
+  // VOLUME_MUTE.
+  EXPECT_EQ(0, volume_delegate->handle_volume_mute_count());
+  SendKeyPressSync(ui::VKEY_VOLUME_MUTE, false, false, false);
+  EXPECT_EQ(1, volume_delegate->handle_volume_mute_count());
+  // VOLUME_DOWN.
+  EXPECT_EQ(0, volume_delegate->handle_volume_down_count());
+  SendKeyPressSync(ui::VKEY_VOLUME_DOWN, false, false, false);
+  EXPECT_EQ(1, volume_delegate->handle_volume_down_count());
+  // VOLUME_UP.
+  EXPECT_EQ(0, volume_delegate->handle_volume_up_count());
+  SendKeyPressSync(ui::VKEY_VOLUME_UP, false, false, false);
+  EXPECT_EQ(1, volume_delegate->handle_volume_up_count());
+
+  // Test TOGGLE_WIFI.
+  TestNetworkObserver network_observer;
+  shell()->system_tray_notifier()->AddNetworkObserver(&network_observer);
+
+  EXPECT_FALSE(network_observer.wifi_enabled_status());
+  SendKeyPressSync(ui::VKEY_WLAN, false, false, false);
+  EXPECT_TRUE(network_observer.wifi_enabled_status());
+  SendKeyPressSync(ui::VKEY_WLAN, false, false, false);
+  EXPECT_FALSE(network_observer.wifi_enabled_status());
+
+  shell()->system_tray_notifier()->RemoveNetworkObserver(&network_observer);
+}
+
+// Tests the app list accelerator.
+TEST_F(AcceleratorInteractiveUITest, MAYBE_ToggleAppList) {
+  EXPECT_FALSE(shell()->GetAppListTargetVisibility());
+  SendKeyPressSync(ui::VKEY_LWIN, false, false, false);
+  EXPECT_TRUE(shell()->GetAppListTargetVisibility());
+  SendKeyPressSync(ui::VKEY_LWIN, false, false, false);
+  EXPECT_FALSE(shell()->GetAppListTargetVisibility());
+}
+
+}  // namespace test
+}  // namespace ash
diff --git a/ash/accessibility_delegate.h b/ash/accessibility_delegate.h
index 8e7e286..cb821c8 100644
--- a/ash/accessibility_delegate.h
+++ b/ash/accessibility_delegate.h
@@ -44,7 +44,7 @@
   // Invoked to enable Large Cursor.
   virtual void SetLargeCursorEnabled(bool enabled) = 0;
 
-  // Returns ture if Large Cursor is enabled.
+  // Returns true if Large Cursor is enabled.
   virtual bool IsLargeCursorEnabled() const = 0;
 
   // Invoked to enable autoclick.
diff --git a/ash/ash.gyp b/ash/ash.gyp
index bb44467..bead4a60 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -787,6 +787,7 @@
       'keyboard_overlay/keyboard_overlay_delegate_unittest.cc',
       'keyboard_overlay/keyboard_overlay_view_unittest.cc',
       'magnifier/magnification_controller_unittest.cc',
+      'popup_message_unittest.cc',
       'root_window_controller_unittest.cc',
       'screen_util_unittest.cc',
       'shelf/scoped_observer_with_duplicated_sources_unittest.cc',
diff --git a/ash/popup_message.cc b/ash/popup_message.cc
index 6256659163..bdde23b 100644
--- a/ash/popup_message.cc
+++ b/ash/popup_message.cc
@@ -63,6 +63,10 @@
   DISALLOW_COPY_AND_ASSIGN(MessageBubble);
 };
 
+// static
+const int PopupMessage::kCaptionLabelID = 1000;
+const int PopupMessage::kMessageLabelID = 1001;
+
 PopupMessage::MessageBubble::MessageBubble(const base::string16& caption,
                                            const base::string16& message,
                                            IconType message_type,
@@ -128,6 +132,7 @@
   // The caption label.
   if (!caption.empty()) {
     views::Label* caption_label = new views::Label(caption);
+    caption_label->set_id(kCaptionLabelID);
     caption_label->SetMultiLine(true);
     caption_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     caption_label->SetFontList(
@@ -139,6 +144,7 @@
   // The message label.
   if (!message.empty()) {
     views::Label* message_label = new views::Label(message);
+    message_label->set_id(kMessageLabelID);
     message_label->SetMultiLine(true);
     message_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     message_label->SetEnabledColor(kMessageTextColor);
diff --git a/ash/popup_message.h b/ash/popup_message.h
index 87315364..c2b160fa 100644
--- a/ash/popup_message.h
+++ b/ash/popup_message.h
@@ -7,6 +7,7 @@
 
 #include "ash/ash_export.h"
 #include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
 #include "base/strings/string16.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/bubble/bubble_border.h"
@@ -62,8 +63,13 @@
   void Close();
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(PopupMessageTest, Layout);
+
   class MessageBubble;
 
+  static const int kCaptionLabelID;
+  static const int kMessageLabelID;
+
   void CancelHidingAnimation();
 
   MessageBubble* view_;
diff --git a/ash/popup_message_unittest.cc b/ash/popup_message_unittest.cc
new file mode 100644
index 0000000..374e0aa
--- /dev/null
+++ b/ash/popup_message_unittest.cc
@@ -0,0 +1,52 @@
+// 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 "ash/popup_message.h"
+
+#include "ash/test/ash_test_base.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+
+typedef test::AshTestBase PopupMessageTest;
+
+// Verifies the layout of the popup, especially it does not crop the caption and
+// message text. See http://crbug.com/468494.
+TEST_F(PopupMessageTest, Layout) {
+  views::Widget* widget =
+      views::Widget::CreateWindowWithBounds(nullptr, gfx::Rect(0, 0, 100, 100));
+  PopupMessage message(base::ASCIIToUTF16("caption text"),
+                       base::ASCIIToUTF16(
+                           "Message text, which will be usually longer than "
+                           "the caption, so that it's wrapped at some width"),
+                       PopupMessage::ICON_WARNING,
+                       widget->GetContentsView() /* anchor */,
+                       views::BubbleBorder::TOP_LEFT, gfx::Size(), 10);
+
+  views::View* contents_view = message.widget_->GetContentsView();
+  views::View* caption_label =
+      contents_view->GetViewByID(PopupMessage::kCaptionLabelID);
+  views::View* message_label =
+      contents_view->GetViewByID(PopupMessage::kMessageLabelID);
+  ASSERT_TRUE(caption_label);
+  ASSERT_TRUE(message_label);
+
+  // The bubble should have enough heights to show both of the labels.
+  EXPECT_GE(contents_view->height(),
+            caption_label->height() + message_label->height());
+
+  // The labels are not cropped -- the assigned height has enough height to show
+  // the full text.
+  EXPECT_GE(caption_label->height(),
+            caption_label->GetHeightForWidth(caption_label->width()));
+  EXPECT_GE(message_label->height(),
+            message_label->GetHeightForWidth(message_label->width()));
+
+  message.Close();
+  widget->Close();
+}
+
+}  // namespace ash
diff --git a/ash/system/user/user_view.cc b/ash/system/user/user_view.cc
index 6e4a726..6ca3e9b 100644
--- a/ash/system/user/user_view.cc
+++ b/ash/system/user/user_view.cc
@@ -89,18 +89,12 @@
                const base::string16& text,
                bool placeholder)
       : TrayPopupLabelButton(listener, text), placeholder_(placeholder) {
-    SetEnabled(!placeholder_);
+    SetVisible(!placeholder_);
   }
 
   ~LogoutButton() override {}
 
  private:
-  void Paint(gfx::Canvas* canvas, const views::CullSet& cull_set) override {
-    // Just skip paint if this button used as a placeholder.
-    if (!placeholder_)
-      TrayPopupLabelButton::Paint(canvas, cull_set);
-  }
-
   bool placeholder_;
   DISALLOW_COPY_AND_ASSIGN(LogoutButton);
 };
diff --git a/base/android/java/src/org/chromium/base/ApplicationStatus.java b/base/android/java/src/org/chromium/base/ApplicationStatus.java
index af02ea2..8c36b61 100644
--- a/base/android/java/src/org/chromium/base/ApplicationStatus.java
+++ b/base/android/java/src/org/chromium/base/ApplicationStatus.java
@@ -309,9 +309,8 @@
             if (sCachedApplicationState == null) {
                 sCachedApplicationState = determineApplicationState();
             }
+            return sCachedApplicationState.intValue();
         }
-
-        return sCachedApplicationState.intValue();
     }
 
     /**
diff --git a/base/android/linker/BUILD.gn b/base/android/linker/BUILD.gn
index 4817d88e..190ea477 100644
--- a/base/android/linker/BUILD.gn
+++ b/base/android/linker/BUILD.gn
@@ -5,7 +5,6 @@
 import("//build/config/android/config.gni")
 
 assert(is_android)
-assert(!is_android_webview_build)
 
 # GYP: //base/base.gyp:chromium_android_linker
 shared_library("chromium_android_linker") {
diff --git a/base/base.gyp b/base/base.gyp
index ac37a890..f8d7765 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -103,6 +103,7 @@
             }],
           ],
           'dependencies': [
+            'base_java',
             'base_jni_headers',
             '../third_party/ashmem/ashmem.gyp:ashmem',
           ],
@@ -118,11 +119,6 @@
             '../build/android/cpufeatures.gypi',
           ],
         }],
-        ['OS == "android" and _toolset == "target" and android_webview_build == 0', {
-          'dependencies': [
-            'base_java',
-          ],
-        }],
         ['os_bsd==1', {
           'include_dirs': [
             '/usr/local/include',
@@ -1412,15 +1408,9 @@
             'base_java_library_process_type',
             'base_java_memory_pressure_level',
             'base_native_libraries_gen',
+            '../third_party/jsr-305/jsr-305.gyp:jsr_305_javalib',
           ],
           'includes': [ '../build/java.gypi' ],
-          'conditions': [
-            ['android_webview_build==0', {
-              'dependencies': [
-                '../third_party/jsr-305/jsr-305.gyp:jsr_305_javalib',
-              ],
-            }]
-          ],
         },
         {
           # GN: //base:base_java_unittest_support
@@ -1490,25 +1480,18 @@
           # GN: //base/android/linker:chromium_android_linker
           'target_name': 'chromium_android_linker',
           'type': 'shared_library',
-          'conditions': [
-            # Avoid breaking the webview build because it
-            # does not have <(android_ndk_root)/crazy_linker.gyp.
-            # Note that webview never uses the linker anyway.
-            ['android_webview_build == 0', {
-              'sources': [
-                'android/linker/linker_jni.cc',
-              ],
-              # The crazy linker is never instrumented.
-              'cflags!': [
-                '-finstrument-functions',
-              ],
-              'dependencies': [
-                # The NDK contains the crazy_linker here:
-                #   '<(android_ndk_root)/crazy_linker.gyp:crazy_linker'
-                # However, we use our own fork.  See bug 384700.
-                '../third_party/android_crazy_linker/crazy_linker.gyp:crazy_linker',
-              ],
-            }],
+          'sources': [
+            'android/linker/linker_jni.cc',
+          ],
+          # The crazy linker is never instrumented.
+          'cflags!': [
+            '-finstrument-functions',
+          ],
+          'dependencies': [
+            # The NDK contains the crazy_linker here:
+            #   '<(android_ndk_root)/crazy_linker.gyp:crazy_linker'
+            # However, we use our own fork.  See bug 384700.
+            '../third_party/android_crazy_linker/crazy_linker.gyp:crazy_linker',
           ],
         },
         {
diff --git a/base/base.gypi b/base/base.gypi
index 825bca1..c3b6e9d 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -836,13 +836,6 @@
               ['include', '^threading/platform_thread_linux\\.cc$'],
             ],
           }],
-          ['OS == "android" and <(android_webview_build)==1', {
-            'defines': [
-               # WebView builds as part of the system which already has sincos;
-               # avoid defining it again as it causes a linker warning.
-               'ANDROID_SINCOS_PROVIDED',
-            ],
-          }],
           ['<(chromeos) == 1', {
             'sources!': [
               'power_monitor/power_monitor_device_source_posix.cc',
diff --git a/base/i18n/icu_util.cc b/base/i18n/icu_util.cc
index f1fdda74..8bbbc04 100644
--- a/base/i18n/icu_util.cc
+++ b/base/i18n/icu_util.cc
@@ -48,7 +48,9 @@
 // Assert that we are not called more than once.  Even though calling this
 // function isn't harmful (ICU can handle it), being called twice probably
 // indicates a programming error.
+#if !defined(OS_NACL)
 bool g_called_once = false;
+#endif
 bool g_check_called_once = true;
 #endif
 }
@@ -81,6 +83,7 @@
 #endif
 
 
+#if !defined(OS_NACL)
 bool InitializeICU() {
 #if !defined(NDEBUG)
   DCHECK(!g_check_called_once || !g_called_once);
@@ -158,6 +161,7 @@
   return err == U_ZERO_ERROR;
 #endif
 }
+#endif
 
 void AllowMultipleInitializeCallsForTesting() {
 #if !defined(NDEBUG)
diff --git a/base/i18n/icu_util.h b/base/i18n/icu_util.h
index 5094cb0..e9f7c3d8 100644
--- a/base/i18n/icu_util.h
+++ b/base/i18n/icu_util.h
@@ -14,9 +14,11 @@
 
 BASE_I18N_EXPORT extern const char kIcuDataFileName[];
 
+#if !defined(OS_NACL)
 // Call this function to load ICU's data tables for the current process.  This
 // function should be called before ICU is used.
 BASE_I18N_EXPORT bool InitializeICU();
+#endif
 
 #if defined(OS_ANDROID)
 // Android uses a file descriptor passed by browser process to initialize ICU
diff --git a/base/memory/discardable_shared_memory.cc b/base/memory/discardable_shared_memory.cc
index 49e93cd..830d6b9d 100644
--- a/base/memory/discardable_shared_memory.cc
+++ b/base/memory/discardable_shared_memory.cc
@@ -325,22 +325,6 @@
   return true;
 }
 
-bool DiscardableSharedMemory::PurgeAndTruncate(Time current_time) {
-  if (!Purge(current_time))
-    return false;
-
-#if defined(OS_POSIX)
-  // Truncate shared memory to size of SharedState.
-  SharedMemoryHandle handle = shared_memory_.handle();
-  if (SharedMemory::IsHandleValid(handle)) {
-    if (HANDLE_EINTR(ftruncate(handle.fd, sizeof(SharedState))) != 0)
-      DPLOG(ERROR) << "ftruncate() failed";
-  }
-#endif
-
-  return true;
-}
-
 bool DiscardableSharedMemory::IsMemoryResident() const {
   DCHECK(shared_memory_.memory());
 
@@ -357,6 +341,26 @@
   mapped_size_ = 0;
 }
 
+#if defined(DISCARDABLE_SHARED_MEMORY_SHRINKING)
+void DiscardableSharedMemory::Shrink() {
+#if defined(OS_POSIX)
+  SharedMemoryHandle handle = shared_memory_.handle();
+  if (!SharedMemory::IsHandleValid(handle))
+    return;
+
+  // Truncate shared memory to size of SharedState.
+  if (HANDLE_EINTR(
+          ftruncate(handle.fd, AlignToPageSize(sizeof(SharedState)))) != 0) {
+    DPLOG(ERROR) << "ftruncate() failed";
+    return;
+  }
+  mapped_size_ = 0;
+#else
+  NOTIMPLEMENTED();
+#endif
+}
+#endif
+
 Time DiscardableSharedMemory::Now() const {
   return Time::Now();
 }
diff --git a/base/memory/discardable_shared_memory.h b/base/memory/discardable_shared_memory.h
index e3b437c..892d556 100644
--- a/base/memory/discardable_shared_memory.h
+++ b/base/memory/discardable_shared_memory.h
@@ -15,6 +15,12 @@
 #include <set>
 #endif
 
+// Define DISCARDABLE_SHARED_MEMORY_SHRINKING if platform supports shrinking
+// of discardable shared memory segments.
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
+#define DISCARDABLE_SHARED_MEMORY_SHRINKING
+#endif
+
 namespace base {
 
 // Platform abstraction for discardable shared memory.
@@ -93,12 +99,6 @@
   // each call.
   bool Purge(Time current_time);
 
-  // Purge and release as much memory as possible to the OS.
-  // Note: The amount of memory that can be released to the OS is platform
-  // specific. Best case, all but one page is released. Worst case, nothing
-  // is released.
-  bool PurgeAndTruncate(Time current_time);
-
   // Returns true if memory is still resident.
   bool IsMemoryResident() const;
 
@@ -116,6 +116,12 @@
     return shared_memory_.ShareToProcess(process_handle, new_handle);
   }
 
+#if defined(DISCARDABLE_SHARED_MEMORY_SHRINKING)
+  // Release as much memory as possible to the OS. The change in size will
+  // be reflected by the return value of mapped_size().
+  void Shrink();
+#endif
+
  private:
   // Virtual for tests.
   virtual Time Now() const;
diff --git a/base/memory/discardable_shared_memory_unittest.cc b/base/memory/discardable_shared_memory_unittest.cc
index 74d19a645..ae7235d 100644
--- a/base/memory/discardable_shared_memory_unittest.cc
+++ b/base/memory/discardable_shared_memory_unittest.cc
@@ -311,5 +311,21 @@
   EXPECT_EQ(0u, memory.mapped_size());
 }
 
+#if defined(DISCARDABLE_SHARED_MEMORY_SHRINKING)
+TEST(DiscardableSharedMemoryTest, Shrink) {
+  const uint32 kDataSize = 1024;
+
+  TestDiscardableSharedMemory memory;
+  bool rv = memory.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv);
+
+  EXPECT_NE(0u, memory.mapped_size());
+
+  // Mapped size should be 0 after shrinking memory segment.
+  memory.Shrink();
+  EXPECT_EQ(0u, memory.mapped_size());
+}
+#endif
+
 }  // namespace
 }  // namespace base
diff --git a/base/synchronization/waitable_event.h b/base/synchronization/waitable_event.h
index c35af54..ffdd6172 100644
--- a/base/synchronization/waitable_event.h
+++ b/base/synchronization/waitable_event.h
@@ -81,6 +81,8 @@
   // does not necessarily mean that max_time was exceeded.
   //
   // TimedWait can synchronise its own destruction like |Wait|.
+  //
+  // Passing a negative |max_time| is not supported.
   bool TimedWait(const TimeDelta& max_time);
 
 #if defined(OS_WIN)
diff --git a/base/synchronization/waitable_event_posix.cc b/base/synchronization/waitable_event_posix.cc
index 696ffc7..04a262b 100644
--- a/base/synchronization/waitable_event_posix.cc
+++ b/base/synchronization/waitable_event_posix.cc
@@ -151,14 +151,14 @@
 };
 
 void WaitableEvent::Wait() {
-  bool result = TimedWait(TimeDelta::FromSeconds(-1));
+  bool result = TimedWait(TimeDelta::Max());
   DCHECK(result) << "TimedWait() should never fail with infinite timeout";
 }
 
 bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
+  DCHECK_GE(max_time, TimeDelta());
   base::ThreadRestrictions::AssertWaitAllowed();
   const TimeTicks end_time(TimeTicks::Now() + max_time);
-  const bool finite_time = max_time.ToInternalValue() >= 0;
 
   kernel_->lock_.Acquire();
   if (kernel_->signaled_) {
@@ -184,7 +184,7 @@
   for (;;) {
     const TimeTicks current_time(TimeTicks::Now());
 
-    if (sw.fired() || (finite_time && current_time >= end_time)) {
+    if (sw.fired() || current_time >= end_time) {
       const bool return_value = sw.fired();
 
       // We can't acquire @lock_ before releasing the SyncWaiter lock (because
@@ -207,12 +207,7 @@
       return return_value;
     }
 
-    if (finite_time) {
-      const TimeDelta max_wait(end_time - current_time);
-      sw.cv()->TimedWait(max_wait);
-    } else {
-      sw.cv()->Wait();
-    }
+    sw.cv()->TimedWait(end_time - current_time);
   }
 }
 
diff --git a/base/synchronization/waitable_event_unittest.cc b/base/synchronization/waitable_event_unittest.cc
index abba935..392a923 100644
--- a/base/synchronization/waitable_event_unittest.cc
+++ b/base/synchronization/waitable_event_unittest.cc
@@ -73,29 +73,27 @@
 
 class WaitableEventSignaler : public PlatformThread::Delegate {
  public:
-  WaitableEventSignaler(double seconds, WaitableEvent* ev)
-      : seconds_(seconds),
-        ev_(ev) {
+  WaitableEventSignaler(TimeDelta delay, WaitableEvent* event)
+      : delay_(delay),
+        event_(event) {
   }
 
   void ThreadMain() override {
-    PlatformThread::Sleep(TimeDelta::FromSecondsD(seconds_));
-    ev_->Signal();
+    PlatformThread::Sleep(delay_);
+    event_->Signal();
   }
 
  private:
-  const double seconds_;
-  WaitableEvent *const ev_;
+  const TimeDelta delay_;
+  WaitableEvent* event_;
 };
 
+// Tests that a WaitableEvent can be safely deleted when |Wait| is done without
+// additional synchronization.
 TEST(WaitableEventTest, WaitAndDelete) {
-  // This test tests that if a WaitableEvent can be safely deleted
-  // when |Wait| is done without additional synchrnization.
-  // If this test crashes, it is a bug.
-
   WaitableEvent* ev = new WaitableEvent(false, false);
 
-  WaitableEventSignaler signaler(0.01, ev);
+  WaitableEventSignaler signaler(TimeDelta::FromMilliseconds(10), ev);
   PlatformThreadHandle thread;
   PlatformThread::Create(0, &signaler, &thread);
 
@@ -105,16 +103,14 @@
   PlatformThread::Join(thread);
 }
 
+// Tests that a WaitableEvent can be safely deleted when |WaitMany| is done
+// without additional synchronization.
 TEST(WaitableEventTest, WaitMany) {
-  // This test tests that if a WaitableEvent can be safely deleted
-  // when |WaitMany| is done without additional synchrnization.
-  // If this test crashes, it is a bug.
-
   WaitableEvent* ev[5];
   for (unsigned i = 0; i < 5; ++i)
     ev[i] = new WaitableEvent(false, false);
 
-  WaitableEventSignaler signaler(0.01, ev[2]);
+  WaitableEventSignaler signaler(TimeDelta::FromMilliseconds(10), ev[2]);
   PlatformThreadHandle thread;
   PlatformThread::Create(0, &signaler, &thread);
 
@@ -127,4 +123,22 @@
   EXPECT_EQ(2u, index);
 }
 
+// Tests that using TimeDelta::Max() on TimedWait() is not the same as passing
+// a timeout of 0. (crbug.com/465948)
+TEST(WaitableEventTest, TimedWait) {
+  WaitableEvent* ev = new WaitableEvent(false, false);
+
+  TimeDelta thread_delay = TimeDelta::FromMilliseconds(10);
+  WaitableEventSignaler signaler(thread_delay, ev);
+  PlatformThreadHandle thread;
+  TimeTicks start = TimeTicks::Now();
+  PlatformThread::Create(0, &signaler, &thread);
+
+  ev->TimedWait(TimeDelta::Max());
+  EXPECT_GE(TimeTicks::Now() - start, thread_delay);
+  delete ev;
+
+  PlatformThread::Join(thread);
+}
+
 }  // namespace base
diff --git a/base/synchronization/waitable_event_win.cc b/base/synchronization/waitable_event_win.cc
index 770c582..6bec4b42 100644
--- a/base/synchronization/waitable_event_win.cc
+++ b/base/synchronization/waitable_event_win.cc
@@ -4,10 +4,10 @@
 
 #include "base/synchronization/waitable_event.h"
 
-#include <math.h>
 #include <windows.h>
 
 #include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 
@@ -37,7 +37,7 @@
 }
 
 bool WaitableEvent::IsSignaled() {
-  return TimedWait(TimeDelta::FromMilliseconds(0));
+  return TimedWait(TimeDelta());
 }
 
 void WaitableEvent::Wait() {
@@ -50,13 +50,13 @@
 
 bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
   base::ThreadRestrictions::AssertWaitAllowed();
-  DCHECK(max_time >= TimeDelta::FromMicroseconds(0));
+  DCHECK_GE(max_time, TimeDelta());
   // Be careful here.  TimeDelta has a precision of microseconds, but this API
   // is in milliseconds.  If there are 5.5ms left, should the delay be 5 or 6?
   // It should be 6 to avoid returning too early.
-  double timeout = ceil(max_time.InMillisecondsF());
-  DWORD result = WaitForSingleObject(handle_.Get(),
-                                     static_cast<DWORD>(timeout));
+  DWORD timeout = saturated_cast<DWORD>(max_time.InMillisecondsRoundedUp());
+
+  DWORD result = WaitForSingleObject(handle_.Get(), timeout);
   switch (result) {
     case WAIT_OBJECT_0:
       return true;
diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc
index 72b46f9..834f826e 100644
--- a/base/trace_event/trace_event_impl.cc
+++ b/base/trace_event/trace_event_impl.cc
@@ -30,6 +30,7 @@
 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread_id_name_manager.h"
+#include "base/threading/worker_pool.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_synthetic_delay.h"
@@ -73,7 +74,7 @@
     512000000 / kTraceBufferChunkSize;
 const size_t kTraceEventVectorBufferChunks = 256000 / kTraceBufferChunkSize;
 const size_t kTraceEventRingBufferChunks = kTraceEventVectorBufferChunks / 4;
-const size_t kTraceEventBatchChunks = 1000 / kTraceBufferChunkSize;
+const size_t kTraceEventBufferSizeInBytes = 100 * 1024;
 // Can store results for 30 seconds with 1 ms sampling interval.
 const size_t kMonitorTraceEventBufferChunks = 30000 / kTraceBufferChunkSize;
 // ECHO_TO_CONSOLE needs a small buffer to hold the unfinished COMPLETE events.
@@ -1208,7 +1209,8 @@
       event_callback_category_filter_(
           CategoryFilter::kDefaultCategoryFilterString),
       thread_shared_chunk_index_(0),
-      generation_(0) {
+      generation_(0),
+      use_worker_thread_(false) {
   // Trace is enabled or disabled on one thread while other threads are
   // accessing the enabled flag. We don't care whether edge-case events are
   // traced or not, so we allow races on the enabled flag to keep the trace
@@ -1681,7 +1683,9 @@
 //    - The message loop will be removed from thread_message_loops_;
 //    If this is the last message loop, finish the flush;
 // 4. If any thread hasn't finish its flush in time, finish the flush.
-void TraceLog::Flush(const TraceLog::OutputCallback& cb) {
+void TraceLog::Flush(const TraceLog::OutputCallback& cb,
+                     bool use_worker_thread) {
+  use_worker_thread_ = use_worker_thread;
   if (IsEnabled()) {
     // Can't flush when tracing is enabled because otherwise PostTask would
     // - generate more trace events;
@@ -1735,6 +1739,7 @@
   FinishFlush(generation);
 }
 
+// Usually it runs on a different thread.
 void TraceLog::ConvertTraceEventsToTraceFormat(
     scoped_ptr<TraceBuffer> logged_events,
     const TraceLog::OutputCallback& flush_output_callback) {
@@ -1749,19 +1754,17 @@
     scoped_refptr<RefCountedString> json_events_str_ptr =
         new RefCountedString();
 
-    for (size_t i = 0; i < kTraceEventBatchChunks; ++i) {
+    while (json_events_str_ptr->size() < kTraceEventBufferSizeInBytes) {
       const TraceBufferChunk* chunk = logged_events->NextChunk();
-      if (!chunk) {
-        has_more_events = false;
+      has_more_events = chunk != NULL;
+      if (!chunk)
         break;
-      }
       for (size_t j = 0; j < chunk->size(); ++j) {
-        if (i > 0 || j > 0)
+        if (json_events_str_ptr->size())
           json_events_str_ptr->data().append(",\n");
         chunk->GetEventAt(j)->AppendAsJSON(&(json_events_str_ptr->data()));
       }
     }
-
     flush_output_callback.Run(json_events_str_ptr, has_more_events);
   } while (has_more_events);
 }
@@ -1785,6 +1788,16 @@
     flush_output_callback_.Reset();
   }
 
+  if (use_worker_thread_ &&
+      WorkerPool::PostTask(
+          FROM_HERE,
+          Bind(&TraceLog::ConvertTraceEventsToTraceFormat,
+               Passed(&previous_logged_events),
+               flush_output_callback),
+          true)) {
+    return;
+  }
+
   ConvertTraceEventsToTraceFormat(previous_logged_events.Pass(),
                                   flush_output_callback);
 }
diff --git a/base/trace_event/trace_event_impl.h b/base/trace_event/trace_event_impl.h
index efa20c4..c3da0b5e 100644
--- a/base/trace_event/trace_event_impl.h
+++ b/base/trace_event/trace_event_impl.h
@@ -538,10 +538,11 @@
   // Due to the implementation of thread-local buffers, flush can't be
   // done when tracing is enabled. If called when tracing is enabled, the
   // callback will be called directly with (empty_string, false) to indicate
-  // the end of this unsuccessful flush.
+  // the end of this unsuccessful flush. Flush does the serialization
+  // on the same thread if the caller doesn't set use_worker_thread explicitly.
   typedef base::Callback<void(const scoped_refptr<base::RefCountedString>&,
                               bool has_more_events)> OutputCallback;
-  void Flush(const OutputCallback& cb);
+  void Flush(const OutputCallback& cb, bool use_worker_thread = false);
   void FlushButLeaveBufferIntact(const OutputCallback& flush_output_callback);
 
   // Called by TRACE_EVENT* macros, don't call this directly.
@@ -710,7 +711,9 @@
   // |generation| is used in the following callbacks to check if the callback
   // is called for the flush of the current |logged_events_|.
   void FlushCurrentThread(int generation);
-  void ConvertTraceEventsToTraceFormat(scoped_ptr<TraceBuffer> logged_events,
+  // Usually it runs on a different thread.
+  static void ConvertTraceEventsToTraceFormat(
+      scoped_ptr<TraceBuffer> logged_events,
       const TraceLog::OutputCallback& flush_output_callback);
   void FinishFlush(int generation);
   void OnFlushTimeout(int generation);
@@ -803,6 +806,7 @@
   OutputCallback flush_output_callback_;
   scoped_refptr<MessageLoopProxy> flush_message_loop_proxy_;
   subtle::AtomicWord generation_;
+  bool use_worker_thread_;
 
   DISALLOW_COPY_AND_ASSIGN(TraceLog);
 };
diff --git a/build/all.gyp b/build/all.gyp
index 2813ca2..5b2ae64d 100644
--- a/build/all.gyp
+++ b/build/all.gyp
@@ -557,6 +557,7 @@
             ['OS=="win"', {
               'dependencies': [
                 '../chrome/chrome.gyp:crash_service',
+                '../gpu/gpu.gyp:angle_perftests',
               ],
             }],
             ['OS=="win" and target_arch=="ia32"', {
@@ -1160,14 +1161,10 @@
         ['branding=="Chrome"', {
           'targets': [
             {
-              'target_name': 'chrome_official_builder',
+              'target_name': 'chrome_official_builder_no_unittests',
               'type': 'none',
               'dependencies': [
-                '../base/base.gyp:base_unittests',
                 '../chrome/chrome.gyp:app_installer',
-                '../chrome/chrome.gyp:app_installer_unittests',
-                '../chrome/chrome.gyp:browser_tests',
-                '../chrome/chrome.gyp:sync_integration_tests',
                 '../chrome/chrome.gyp:crash_service',
                 '../chrome/chrome.gyp:gcapi_dll',
                 '../chrome/chrome.gyp:pack_policy_templates',
@@ -1175,19 +1172,8 @@
                 '../cloud_print/cloud_print.gyp:cloud_print',
                 '../courgette/courgette.gyp:courgette',
                 '../courgette/courgette.gyp:courgette64',
-                '../ipc/ipc.gyp:ipc_tests',
-                '../media/media.gyp:media_unittests',
-                '../net/net.gyp:net_unittests_run',
-                '../printing/printing.gyp:printing_unittests',
                 '../remoting/remoting.gyp:remoting_webapp',
-                '../sql/sql.gyp:sql_unittests',
-                '../sync/sync.gyp:sync_unit_tests',
                 '../third_party/widevine/cdm/widevine_cdm.gyp:widevinecdmadapter',
-                '../ui/base/ui_base_tests.gyp:ui_base_unittests',
-                '../ui/gfx/gfx_tests.gyp:gfx_unittests',
-                '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection_unittests',
-                '../ui/views/views.gyp:views_unittests',
-                '../url/url.gyp:url_unittests',
               ],
               'conditions': [
                 ['target_arch=="ia32"', {
@@ -1202,6 +1188,27 @@
                   ],
                 }], # component != "shared_library"
               ]
+            }, {
+              'target_name': 'chrome_official_builder',
+              'type': 'none',
+              'dependencies': [
+	        'chrome_official_builder_no_unittests',
+                '../base/base.gyp:base_unittests',
+		'../chrome/chrome.gyp:app_installer_unittests',
+                '../chrome/chrome.gyp:browser_tests',
+                '../chrome/chrome.gyp:sync_integration_tests',
+                '../ipc/ipc.gyp:ipc_tests',
+                '../media/media.gyp:media_unittests',
+                '../net/net.gyp:net_unittests_run',
+                '../printing/printing.gyp:printing_unittests',
+                '../sql/sql.gyp:sql_unittests',
+                '../sync/sync.gyp:sync_unit_tests',
+                '../ui/base/ui_base_tests.gyp:ui_base_unittests',
+                '../ui/gfx/gfx_tests.gyp:gfx_unittests',
+                '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection_unittests',
+                '../ui/views/views.gyp:views_unittests',
+                '../url/url.gyp:url_unittests',
+              ],
             },
           ], # targets
         }], # branding=="Chrome"
diff --git a/build/android/pylib/gtest/filter/content_browsertests_disabled b/build/android/pylib/gtest/filter/content_browsertests_disabled
index 3c36264..39eff4d 100644
--- a/build/android/pylib/gtest/filter/content_browsertests_disabled
+++ b/build/android/pylib/gtest/filter/content_browsertests_disabled
@@ -2,13 +2,6 @@
 # Timeouts
 Http/MediaTest.*
 File/MediaTest.*
-WorkerTest.IncognitoSharedWorkers
-WorkerTest.MultipleSharedWorkers
-WorkerTest.PassMessagePortToSharedWorker
-WorkerTest.PassMessagePortToSharedWorkerDontWaitForConnect
-WorkerTest.SharedWorkerHttpAuth
-WorkerTest.SingleSharedWorker
-WorkerTest.WebSocketSharedWorker
 MediaTest.*
 DatabaseTest.*
 
diff --git a/build/common.gypi b/build/common.gypi
index e900cc4a..bc263e6 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -1021,6 +1021,15 @@
         }, {
           'v8_use_external_startup_data%': 0,
         }],
+
+        # Controls whether Wallet cards can be saved to the local instance of
+        # chrome. TODO(estade): set to 0 for Linux before M43 branch.
+        ['desktop_linux==1', {
+          'enable_save_wallet_cards_locally%': 1,
+        }, {
+          'enable_save_wallet_cards_locally%': 1,
+        }],
+
       ],
 
       # Set this to 1 to enable use of concatenated impulse responses
@@ -1172,6 +1181,7 @@
     'enable_themes%': '<(enable_themes)',
     'enable_autofill_dialog%': '<(enable_autofill_dialog)',
     'enable_prod_wallet_service%': '<(enable_prod_wallet_service)',
+    'enable_save_wallet_cards_locally%': '<(enable_save_wallet_cards_locally)',
     'enable_background%': '<(enable_background)',
     'linux_use_bundled_gold%': '<(linux_use_bundled_gold)',
     'linux_use_bundled_binutils%': '<(linux_use_bundled_binutils)',
@@ -1417,7 +1427,7 @@
 
     # Pseudo locales are special locales which are used for testing and
     # debugging. They don't get copied to the final app. For more info,
-    # check out https://sites.google.com/a/chromium.org/dev/Home/fake-bidi
+    # check out https://www.chromium.org/developers/testing/fake-bidi
     'pseudo_locales': [
       'fake-bidi',
     ],
@@ -2942,6 +2952,9 @@
       ['enable_prod_wallet_service==1', {
         'defines': ['ENABLE_PROD_WALLET_SERVICE=1'],
       }],
+      ['enable_save_wallet_cards_locally==1', {
+        'defines': ['ENABLE_SAVE_WALLET_CARDS_LOCALLY=1'],
+      }],
       ['enable_background==1', {
         'defines': ['ENABLE_BACKGROUND=1'],
       }],
@@ -3603,8 +3616,6 @@
           '-pthread',
           '-fno-strict-aliasing',  # See http://crbug.com/32204
           '-Wall',
-          # TODO(evan): turn this back on once all the builds work.
-          # '-Wextra',
           # Don't warn about unused function params.  We use those everywhere.
           '-Wno-unused-parameter',
           # Don't warn about the "struct foo f = {0};" initialization pattern.
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index 8e30c4a..455ec0d 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -17,27 +17,12 @@
 # Use "is_*" names for intrinsic platform descriptions and build modes, and
 # "use_*" names for optional features libraries, and configurations.
 
-# TODO(dpranke): The os and cpu_arch variables exist for backwards
-# compatibility and should be deleted once all of the build files and
-# bots have been updated to use current_cpu/target_cpu and
-# current_os/target_os instead.
-
 if (target_os == "") {
-  if (defined(os)) {
-    # If os is defined, it was set in an args file and needs to be
-    # used for backwards-compatibility.
-    target_os = os
-  } else {
-    target_os = host_os
-  }
+  target_os = host_os
 }
 
 if (target_cpu == "") {
-  if (defined(cpu_arch)) {
-    # If cpu_arch is defined, it was set in an args file and needs to be
-    # used for backwards-compatibility.
-    target_cpu = cpu_arch
-  } else if (target_os == "android") {
+  if (target_os == "android") {
     # If we're building for Android, we should assume that we want to
     # build for ARM by default, not the host_cpu (which is likely x64).
     # This allows us to not have to specify both target_os and target_cpu
@@ -56,13 +41,6 @@
 }
 
 declare_args() {
-  # TODO(dpranke): These values are here for backwards compatibility and
-  # should be deleted when all of the builders and configs have been updated.
-  cpu_arch = target_cpu
-  os = target_os
-  build_cpu_arch = host_cpu
-  build_os = host_os
-
   # How many symbols to include in the build. This affects the performance of
   # the build since the symbols are large and dealing with them is slow.
   #   2 means regular build with symbols.
@@ -115,10 +93,6 @@
   }
 }
 
-# TODO(dpranke): Remove these asserts when os and cpu_arch are removed.
-assert(current_cpu == cpu_arch)
-assert(current_os == os)
-
 # =============================================================================
 # OS DEFINITIONS
 # =============================================================================
@@ -539,8 +513,8 @@
 } else if (is_nacl) {
   # TODO(GYP): This will need to change when we get NaCl working
   # on multiple platforms, but this whole block of code (how we define
-  # host_toolchain) needs to be reworked regardless to key off of build_os
-  # and build_cpu_arch rather than the is_* variables.
+  # host_toolchain) needs to be reworked regardless to key off of host_os
+  # and host_cpu rather than the is_* variables.
   host_toolchain = "//build/toolchain/linux:clang_x64"
 }
 
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 42dc584..b2d615d 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -573,7 +573,7 @@
 
     # TODO(jdduke) Re-enable on mips after resolving linking
     # issues with libc++ (crbug.com/456380).
-    if (cpu_arch != "mipsel" && cpu_arch != "mips64el") {
+    if (current_cpu != "mipsel" && current_cpu != "mips64el") {
       ldflags += [ "-Wl,--warn-shared-textrel" ]
     }
     ldflags += [ "-nostdlib" ]
@@ -983,7 +983,7 @@
 
     # TODO(jdduke) Re-enable on mips after resolving linking
     # issues with libc++ (crbug.com/456380).
-    if (cpu_arch != "mipsel" && cpu_arch != "mips64el") {
+    if (current_cpu != "mipsel" && current_cpu != "mips64el") {
       common_optimize_on_ldflags += [
         # Warn in case of text relocations.
         "-Wl,--warn-shared-textrel",
diff --git a/build/config/linux/gtk/BUILD.gn b/build/config/linux/gtk/BUILD.gn
index 4968e2d..7130e15 100644
--- a/build/config/linux/gtk/BUILD.gn
+++ b/build/config/linux/gtk/BUILD.gn
@@ -28,7 +28,7 @@
     "//chrome/browser/ui/libgtk2ui",
     "//remoting/host",
   ]
-  direct_dependent_configs = [ ":gtk_internal_config" ]
+  public_configs = [ ":gtk_internal_config" ]
 }
 
 # Depend on "gtkprint" to get this.
@@ -38,5 +38,5 @@
 
 group("gtkprint") {
   visibility = [ "//chrome/browser/ui/libgtk2ui" ]
-  direct_dependent_configs = [ ":gtkprint_internal_config" ]
+  public_configs = [ ":gtkprint_internal_config" ]
 }
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index 4ca22f7..ca7fe6d 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -108,14 +108,15 @@
 
 incremental_linking_on_switch = [ "/INCREMENTAL" ]
 incremental_linking_off_switch = [ "/INCREMENTAL:NO" ]
+if (is_debug) {
+  default_incremental_linking_switch = incremental_linking_on_switch
+} else {
+  default_incremental_linking_switch = incremental_linking_off_switch
+}
 
 # Applies incremental linking or not depending on the current configuration.
 config("default_incremental_linking") {
-  if (is_debug) {
-    ldflags = incremental_linking_on_switch
-  } else {
-    ldflags = incremental_linking_off_switch
-  }
+  ldflags = default_incremental_linking_switch
 }
 
 # Explicitly on or off incremental linking
@@ -130,17 +131,13 @@
 # config should be applied to large modules to turn off incremental linking
 # when it won't work.
 config("default_large_module_incremental_linking") {
-  if (!is_debug) {
-    # Default is always off in release build.
-    ldflags = incremental_linking_off_switch
-  } else if ((symbol_level == 0 || symbol_level == 1) &&
-             (current_cpu == "x86" || !is_component_build)) {
-    # When full symbols are on, don't do incremental linking for large modules
-    # on 32-bit or in non-component mode as the toolchain fails due to the size
-    # of the .ilk files.
+  if (symbol_level > 0 && (current_cpu == "x86" || !is_component_build)) {
+    # When symbols are on, things get so large that the tools fail due to the
+    # size of the .ilk files.
     ldflags = incremental_linking_off_switch
   } else {
-    ldflags = incremental_linking_on_switch
+    # Otherwise just do the default incremental linking for this build type.
+    ldflags = default_incremental_linking_switch
   }
 }
 
diff --git a/build/ios/grit_whitelist.txt b/build/ios/grit_whitelist.txt
index 28407eb..70d97706 100644
--- a/build/ios/grit_whitelist.txt
+++ b/build/ios/grit_whitelist.txt
@@ -530,6 +530,8 @@
 IDS_FLAGS_ENABLE_EXPERIMENTAL_CANVAS_FEATURES_NAME
 IDS_FLAGS_ENABLE_GESTURE_TAP_HIGHLIGHTING_DESCRIPTION
 IDS_FLAGS_ENABLE_GESTURE_TAP_HIGHLIGHTING_NAME
+IDS_FLAGS_ENABLE_ICON_NTP_DESCRIPTION
+IDS_FLAGS_ENABLE_ICON_NTP_NAME
 IDS_FLAGS_ENABLE_JAVASCRIPT_HARMONY_DESCRIPTION
 IDS_FLAGS_ENABLE_JAVASCRIPT_HARMONY_NAME
 IDS_FLAGS_ENABLE_NACL_DEBUG_DESCRIPTION
diff --git a/build/module_args/mojo.gni b/build/module_args/mojo.gni
index d370cfa..13853b3 100644
--- a/build/module_args/mojo.gni
+++ b/build/module_args/mojo.gni
@@ -7,22 +7,26 @@
 
 # Chromium builds the network service from source, as it is the
 # producer of the network service.
-build_network_service_from_source = true
+mojo_build_network_service_from_source = true
 
 # Chromium does not build dart apptests. Disable any Mojo targets which
 # depend on the apptest framework.
-disable_dart_apptest_framework = true
+mojo_disable_dart_apptest_framework = true
+
+# Points to the directory network_service is built from.
+mojo_network_service_root = "//mojo/services"
 
 declare_args() {
   # Specify prebuilt network service mojo file location rather than download
   # from gs://mojo.
   #
-  # This variable only used when build_network_service_from_source is false.
+  # This variable only used when mojo_build_network_service_from_source is
+  # false.
   #
   # Currently only android_arm and linux_64 available on gs://mojo, and no plan
   # to support more(https://codereview.chromium.org/921873003).
   # It is needed for developers works on other platforms like android_x86 and
   # android_x64. And it is also useful for supportted platform as well when
   # developer want to try its own version of network service files.
-  prebuilt_network_service_location = ""
+  mojo_prebuilt_network_service_location = ""
 }
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index 76d2e1e1..69770921 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -63,6 +63,7 @@
 
 // http://crbug.com/158922
 "race:third_party/libvpx/source/libvpx/vp8/encoder/*\n"
+"race:third_party/libvpx/source/libvpx/vp9/encoder/*\n"
 
 // http://crbug.com/189177
 "race:thread_manager\n"
@@ -127,9 +128,6 @@
 // http://crbug.com/268941
 "race:tracked_objects::ThreadData::tls_index_\n"
 
-// http://crbug.com/270037
-"race:gLibCleanupFunctions\n"
-
 // http://crbug.com/272095
 "race:base::g_top_manager\n"
 
diff --git a/build/secondary/third_party/android_tools/BUILD.gn b/build/secondary/third_party/android_tools/BUILD.gn
index 92a088a..f9ad36f 100644
--- a/build/secondary/third_party/android_tools/BUILD.gn
+++ b/build/secondary/third_party/android_tools/BUILD.gn
@@ -65,6 +65,13 @@
   jar_path = "$android_sdk_root/extras/android/support/v7/mediarouter/libs/android-support-v7-mediarouter.jar"
 }
 
+android_java_prebuilt("android_support_v7_recyclerview_java") {
+  deps = [
+    ":android_support_v7_appcompat_java",
+  ]
+  jar_path = "$android_sdk_root/extras/android/support/v7/recyclerview/libs/android-support-v7-recyclerview.jar"
+}
+
 android_resources("google_play_services_default_resources") {
   v14_verify_only = true
   resource_dirs = [ "$android_sdk_root/extras/google/google_play_services/libproject/google-play-services_lib/res" ]
diff --git a/build/secondary/tools/grit/grit_rule.gni b/build/secondary/tools/grit/grit_rule.gni
index 5d3b1e4..117ef9b 100644
--- a/build/secondary/tools/grit/grit_rule.gni
+++ b/build/secondary/tools/grit/grit_rule.gni
@@ -299,11 +299,14 @@
   assert(defined(invoker.source),
          "\"source\" must be defined for the grit template $target_name")
 
+  grit_inputs = [ invoker.source ]
+
   if (defined(invoker.resource_ids)) {
     resource_ids = invoker.resource_ids
   } else {
     resource_ids = grit_resource_id_file
   }
+  grit_inputs += [ resource_ids ]  # Script depends on ID file.
 
   if (defined(invoker.output_dir)) {
     output_dir = invoker.output_dir
@@ -331,8 +334,6 @@
     grit_flags = []  # These are optional so default to empty list.
   }
 
-  grit_inputs = [ invoker.source ]
-
   assert_files_flags = []
 
   # We want to make sure the declared outputs actually match what Grit is
diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni
index 5f49f683..0192faa 100644
--- a/build/toolchain/gcc_toolchain.gni
+++ b/build/toolchain/gcc_toolchain.gni
@@ -211,12 +211,6 @@
       target_os = target_os
       target_cpu = target_cpu
 
-      # TODO(dpranke): These values are here for backwards compatibility and
-      # should be deleted when all of the builders and configs have been
-      # updated.
-      cpu_arch = current_cpu
-      os = current_os
-
       if (defined(invoker.is_clang)) {
         is_clang = invoker.is_clang
       }
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn
index e9461a4..95c2d9f3 100644
--- a/build/toolchain/win/BUILD.gn
+++ b/build/toolchain/win/BUILD.gn
@@ -201,9 +201,6 @@
     # passed to the build. They are ignored when this is the default toolchain.
     toolchain_args() {
       current_cpu = invoker.current_cpu
-
-      # TODO(dpranke): cpu_arch is here for backwards compatibility.
-      cpu_arch = current_cpu
     }
   }
 }
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index df9f2622..5354b7c 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -196,6 +196,8 @@
     "layers/video_layer.h",
     "layers/video_layer_impl.cc",
     "layers/video_layer_impl.h",
+    "layers/viewport.cc",
+    "layers/viewport.h",
     "output/begin_frame_args.cc",
     "output/begin_frame_args.h",
     "output/bsp_tree.cc",
diff --git a/cc/cc.gyp b/cc/cc.gyp
index c5f2033..b2bc0224 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -248,6 +248,8 @@
         'layers/video_layer.h',
         'layers/video_layer_impl.cc',
         'layers/video_layer_impl.h',
+        'layers/viewport.cc',
+        'layers/viewport.h',
         'output/begin_frame_args.cc',
         'output/begin_frame_args.h',
         'output/bsp_tree.cc',
diff --git a/cc/debug/rendering_stats.cc b/cc/debug/rendering_stats.cc
index 3a27c1d..582c763 100644
--- a/cc/debug/rendering_stats.cc
+++ b/cc/debug/rendering_stats.cc
@@ -37,7 +37,8 @@
 RenderingStats::RenderingStats()
     : frame_count(0),
       visible_content_area(0),
-      approximated_visible_content_area(0) {
+      approximated_visible_content_area(0),
+      checkerboarded_visible_content_area(0) {
 }
 
 RenderingStats::~RenderingStats() {
@@ -51,6 +52,8 @@
   record_data->SetInteger("visible_content_area", visible_content_area);
   record_data->SetInteger("approximated_visible_content_area",
                           approximated_visible_content_area);
+  record_data->SetInteger("checkerboarded_visible_content_area",
+                          checkerboarded_visible_content_area);
   draw_duration.AddToTracedValue("draw_duration_ms", record_data.get());
 
   draw_duration_estimate.AddToTracedValue("draw_duration_estimate_ms",
@@ -74,6 +77,8 @@
   frame_count += other.frame_count;
   visible_content_area += other.visible_content_area;
   approximated_visible_content_area += other.approximated_visible_content_area;
+  checkerboarded_visible_content_area +=
+      other.checkerboarded_visible_content_area;
 
   draw_duration.Add(other.draw_duration);
   draw_duration_estimate.Add(other.draw_duration_estimate);
diff --git a/cc/debug/rendering_stats.h b/cc/debug/rendering_stats.h
index 1e97be1..cb7ebb2c 100644
--- a/cc/debug/rendering_stats.h
+++ b/cc/debug/rendering_stats.h
@@ -44,6 +44,7 @@
   int64 frame_count;
   int64 visible_content_area;
   int64 approximated_visible_content_area;
+  int64 checkerboarded_visible_content_area;
 
   TimeDeltaList draw_duration;
   TimeDeltaList draw_duration_estimate;
diff --git a/cc/debug/rendering_stats_instrumentation.cc b/cc/debug/rendering_stats_instrumentation.cc
index 4a28a66..7497c62 100644
--- a/cc/debug/rendering_stats_instrumentation.cc
+++ b/cc/debug/rendering_stats_instrumentation.cc
@@ -81,6 +81,15 @@
   impl_thread_rendering_stats_.approximated_visible_content_area += area;
 }
 
+void RenderingStatsInstrumentation::AddCheckerboardedVisibleContentArea(
+    int64 area) {
+  if (!record_rendering_stats_)
+    return;
+
+  base::AutoLock scoped_lock(lock_);
+  impl_thread_rendering_stats_.checkerboarded_visible_content_area += area;
+}
+
 void RenderingStatsInstrumentation::AddDrawDuration(
     base::TimeDelta draw_duration,
     base::TimeDelta draw_duration_estimate) {
diff --git a/cc/debug/rendering_stats_instrumentation.h b/cc/debug/rendering_stats_instrumentation.h
index 8d95929f..2a6e3b7 100644
--- a/cc/debug/rendering_stats_instrumentation.h
+++ b/cc/debug/rendering_stats_instrumentation.h
@@ -43,6 +43,7 @@
   void IncrementFrameCount(int64 count);
   void AddVisibleContentArea(int64 area);
   void AddApproximatedVisibleContentArea(int64 area);
+  void AddCheckerboardedVisibleContentArea(int64 area);
   void AddDrawDuration(base::TimeDelta draw_duration,
                        base::TimeDelta draw_duration_estimate);
   void AddBeginMainFrameToCommitDuration(
diff --git a/cc/layers/append_quads_data.h b/cc/layers/append_quads_data.h
index 103e1cdd..50f58775 100644
--- a/cc/layers/append_quads_data.h
+++ b/cc/layers/append_quads_data.h
@@ -15,7 +15,8 @@
       : num_incomplete_tiles(0),
         num_missing_tiles(0),
         visible_content_area(0),
-        approximated_visible_content_area(0) {}
+        approximated_visible_content_area(0),
+        checkerboarded_visible_content_area(0) {}
 
   // Set by the layer appending quads.
   int64 num_incomplete_tiles;
@@ -25,6 +26,8 @@
   int64 visible_content_area;
   // Set by the layer appending quads.
   int64 approximated_visible_content_area;
+  // Set by the layer appending quads.
+  int64 checkerboarded_visible_content_area;
 };
 
 }  // namespace cc
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index a1a6161..b4ba876 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -1322,12 +1322,18 @@
         scrollbar_layer->SetVisibleToTotalLengthRatio(visible_ratio);
   } else {
     float visible_ratio = clip_rect.height() / scroll_rect.height();
-    scrollbar_needs_animation |=
+    bool y_offset_did_change =
         scrollbar_layer->SetCurrentPos(current_offset.y());
+    scrollbar_needs_animation |= y_offset_did_change;
     scrollbar_needs_animation |=
         scrollbar_layer->SetMaximum(scroll_rect.height() - clip_rect.height());
     scrollbar_needs_animation |=
         scrollbar_layer->SetVisibleToTotalLengthRatio(visible_ratio);
+    if (y_offset_did_change && layer_tree_impl()->IsActiveTree() &&
+        this == layer_tree_impl()->InnerViewportScrollLayer()) {
+      TRACE_COUNTER_ID1("cc", "scroll_offset_y", this->id(),
+                        current_offset.y());
+    }
   }
   if (scrollbar_needs_animation) {
     layer_tree_impl()->set_needs_update_draw_properties();
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index e1b3724..e60d425f 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -351,6 +351,8 @@
       }
       append_quads_data->approximated_visible_content_area +=
           visible_geometry_rect.width() * visible_geometry_rect.height();
+      append_quads_data->checkerboarded_visible_content_area +=
+          visible_geometry_rect.width() * visible_geometry_rect.height();
       continue;
     }
 
diff --git a/cc/layers/video_frame_provider.h b/cc/layers/video_frame_provider.h
index 784d951f..45e6c411 100644
--- a/cc/layers/video_frame_provider.h
+++ b/cc/layers/video_frame_provider.h
@@ -6,6 +6,7 @@
 #define CC_LAYERS_VIDEO_FRAME_PROVIDER_H_
 
 #include "base/memory/ref_counted.h"
+#include "cc/base/cc_export.h"
 
 namespace media {
 class VideoFrame;
@@ -18,11 +19,11 @@
 // PutCurrentFrame() from the compositor thread. If so, the caller is
 // responsible for making sure Client::DidReceiveFrame() and
 // Client::DidUpdateMatrix() are only called from this same thread.
-class VideoFrameProvider {
+class CC_EXPORT VideoFrameProvider {
  public:
   virtual ~VideoFrameProvider() {}
 
-  class Client {
+  class CC_EXPORT Client {
    public:
     // Provider will call this method to tell the client to stop using it.
     // StopUsingProvider() may be called from any thread. The client should
diff --git a/cc/layers/video_frame_provider_client_impl.cc b/cc/layers/video_frame_provider_client_impl.cc
index 4bbd92f2..68e10dc5 100644
--- a/cc/layers/video_frame_provider_client_impl.cc
+++ b/cc/layers/video_frame_provider_client_impl.cc
@@ -13,17 +13,13 @@
 
 // static
 scoped_refptr<VideoFrameProviderClientImpl>
-    VideoFrameProviderClientImpl::Create(
-        VideoFrameProvider* provider) {
-  return make_scoped_refptr(
-      new VideoFrameProviderClientImpl(provider));
+VideoFrameProviderClientImpl::Create(VideoFrameProvider* provider) {
+  return make_scoped_refptr(new VideoFrameProviderClientImpl(provider));
 }
 
-VideoFrameProviderClientImpl::~VideoFrameProviderClientImpl() {}
-
 VideoFrameProviderClientImpl::VideoFrameProviderClientImpl(
     VideoFrameProvider* provider)
-    : active_video_layer_(nullptr), provider_(provider) {
+    : provider_(provider), active_video_layer_(nullptr), stopped_(false) {
   // This only happens during a commit on the compositor thread while the main
   // thread is blocked. That makes this a thread-safe call to set the video
   // frame provider client that does not require a lock. The same is true of
@@ -39,6 +35,16 @@
       0.0, 0.0, 0.0, 1.0);
 }
 
+VideoFrameProviderClientImpl::~VideoFrameProviderClientImpl() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(stopped_);
+}
+
+VideoLayerImpl* VideoFrameProviderClientImpl::ActiveVideoLayer() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return active_video_layer_;
+}
+
 void VideoFrameProviderClientImpl::SetActiveVideoLayer(
     VideoLayerImpl* video_layer) {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -47,19 +53,19 @@
 }
 
 void VideoFrameProviderClientImpl::Stop() {
-  // It's called when the main thread is blocked, so lock isn't needed.
-  if (!provider_)
-    return;
   DCHECK(thread_checker_.CalledOnValidThread());
-  provider_->SetVideoFrameProviderClient(nullptr);
-  provider_ = nullptr;
+  // It's called when the main thread is blocked, so lock isn't needed.
+  if (provider_) {
+    provider_->SetVideoFrameProviderClient(nullptr);
+    provider_ = nullptr;
+  }
+  active_video_layer_ = nullptr;
+  stopped_ = true;
 }
 
-bool VideoFrameProviderClientImpl::Stopped() {
+bool VideoFrameProviderClientImpl::Stopped() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  // |provider_| is changed while the main thread is blocked, and not changed
-  // thereafter, so lock isn't needed.
-  return !provider_;
+  return stopped_;
 }
 
 scoped_refptr<media::VideoFrame>
@@ -85,6 +91,12 @@
   provider_lock_.Release();
 }
 
+const gfx::Transform& VideoFrameProviderClientImpl::StreamTextureMatrix()
+    const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return stream_texture_matrix_;
+}
+
 void VideoFrameProviderClientImpl::StopUsingProvider() {
   // Block the provider from shutting down until this client is done
   // using the frame.
diff --git a/cc/layers/video_frame_provider_client_impl.h b/cc/layers/video_frame_provider_client_impl.h
index 39f66fb..2688ad2 100644
--- a/cc/layers/video_frame_provider_client_impl.h
+++ b/cc/layers/video_frame_provider_client_impl.h
@@ -8,6 +8,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
+#include "cc/base/cc_export.h"
 #include "cc/layers/video_frame_provider.h"
 #include "ui/gfx/transform.h"
 
@@ -16,42 +17,53 @@
 namespace cc {
 class VideoLayerImpl;
 
-class VideoFrameProviderClientImpl
+// VideoFrameProviderClientImpl liasons with the VideoFrameProvider and the
+// VideoLayer. It receives updates from the provider and updates the layer as a
+// result. It also allows the layer to access the video frame that the provider
+// has.
+class CC_EXPORT VideoFrameProviderClientImpl
     : public VideoFrameProvider::Client,
       public base::RefCounted<VideoFrameProviderClientImpl> {
  public:
+  // Must be created on the impl thread while the main thread is blocked.
   static scoped_refptr<VideoFrameProviderClientImpl> Create(
       VideoFrameProvider* provider);
 
-  VideoLayerImpl* active_video_layer() { return active_video_layer_; }
+  VideoLayerImpl* ActiveVideoLayer() const;
   void SetActiveVideoLayer(VideoLayerImpl* video_layer);
 
+  bool Stopped() const;
+  // Must be called on the impl thread while the main thread is blocked.
   void Stop();
-  bool Stopped();
 
   scoped_refptr<media::VideoFrame> AcquireLockAndCurrentFrame();
   void PutCurrentFrame(const scoped_refptr<media::VideoFrame>& frame);
   void ReleaseLock();
-  const gfx::Transform& stream_texture_matrix() const {
-    return stream_texture_matrix_;
-  }
 
-  // VideoFrameProvider::Client implementation. These methods are all callable
-  // on any thread.
+  const gfx::Transform& StreamTextureMatrix() const;
+
+  // VideoFrameProvider::Client implementation.
+  // Called on the main thread.
   void StopUsingProvider() override;
+  // Called on the impl thread.
   void DidReceiveFrame() override;
   void DidUpdateMatrix(const float* matrix) override;
 
  private:
-  explicit VideoFrameProviderClientImpl(VideoFrameProvider* provider);
   friend class base::RefCounted<VideoFrameProviderClientImpl>;
+
+  explicit VideoFrameProviderClientImpl(VideoFrameProvider* provider);
   ~VideoFrameProviderClientImpl() override;
 
-  VideoLayerImpl* active_video_layer_;
-
-  // Guards the destruction of provider_ and the frame that it provides
-  base::Lock provider_lock_;
   VideoFrameProvider* provider_;
+  VideoLayerImpl* active_video_layer_;
+  bool stopped_;
+
+  // Since the provider lives on another thread, it can be destroyed while the
+  // frame controller are accessing its frame. Before being destroyed the
+  // provider calls StopUsingProvider. provider_lock_ blocks StopUsingProvider
+  // from returning until the frame controller is done using the frame.
+  base::Lock provider_lock_;
   base::ThreadChecker thread_checker_;
 
   gfx::Transform stream_texture_matrix_;
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc
index 228f2cc..99f27422 100644
--- a/cc/layers/video_layer_impl.cc
+++ b/cc/layers/video_layer_impl.cc
@@ -30,18 +30,23 @@
     int id,
     VideoFrameProvider* provider,
     media::VideoRotation video_rotation) {
-  scoped_ptr<VideoLayerImpl> layer(
-      new VideoLayerImpl(tree_impl, id, video_rotation));
-  layer->SetProviderClientImpl(VideoFrameProviderClientImpl::Create(provider));
-  DCHECK(tree_impl->proxy()->IsImplThread());
   DCHECK(tree_impl->proxy()->IsMainThreadBlocked());
-  return layer.Pass();
+  DCHECK(tree_impl->proxy()->IsImplThread());
+
+  scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl =
+      VideoFrameProviderClientImpl::Create(provider);
+
+  return make_scoped_ptr(
+      new VideoLayerImpl(tree_impl, id, provider_client_impl, video_rotation));
 }
 
-VideoLayerImpl::VideoLayerImpl(LayerTreeImpl* tree_impl,
-                               int id,
-                               media::VideoRotation video_rotation)
+VideoLayerImpl::VideoLayerImpl(
+    LayerTreeImpl* tree_impl,
+    int id,
+    scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl,
+    media::VideoRotation video_rotation)
     : LayerImpl(tree_impl, id),
+      provider_client_impl_(provider_client_impl),
       frame_(nullptr),
       video_rotation_(video_rotation) {
 }
@@ -61,14 +66,8 @@
 
 scoped_ptr<LayerImpl> VideoLayerImpl::CreateLayerImpl(
     LayerTreeImpl* tree_impl) {
-  return make_scoped_ptr(new VideoLayerImpl(tree_impl, id(), video_rotation_));
-}
-
-void VideoLayerImpl::PushPropertiesTo(LayerImpl* layer) {
-  LayerImpl::PushPropertiesTo(layer);
-
-  VideoLayerImpl* other = static_cast<VideoLayerImpl*>(layer);
-  other->SetProviderClientImpl(provider_client_impl_);
+  return make_scoped_ptr(new VideoLayerImpl(
+      tree_impl, id(), provider_client_impl_, video_rotation_));
 }
 
 void VideoLayerImpl::DidBecomeActive() {
@@ -275,12 +274,9 @@
       StreamVideoDrawQuad* stream_video_quad =
           render_pass->CreateAndAppendDrawQuad<StreamVideoDrawQuad>();
       stream_video_quad->SetNew(
-          shared_quad_state,
-          quad_rect,
-          opaque_rect,
-          visible_quad_rect,
+          shared_quad_state, quad_rect, opaque_rect, visible_quad_rect,
           frame_resources_[0],
-          scale * provider_client_impl_->stream_texture_matrix());
+          scale * provider_client_impl_->StreamTextureMatrix());
       break;
     }
     case VideoFrameExternalResources::IO_SURFACE: {
@@ -364,11 +360,6 @@
   layer_tree_impl()->SetNeedsRedraw();
 }
 
-void VideoLayerImpl::SetProviderClientImpl(
-    scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl) {
-  provider_client_impl_ = provider_client_impl;
-}
-
 const char* VideoLayerImpl::LayerTypeAsString() const {
   return "cc::VideoLayerImpl";
 }
diff --git a/cc/layers/video_layer_impl.h b/cc/layers/video_layer_impl.h
index d05bd57..5e99923 100644
--- a/cc/layers/video_layer_impl.h
+++ b/cc/layers/video_layer_impl.h
@@ -23,6 +23,8 @@
 
 class CC_EXPORT VideoLayerImpl : public LayerImpl {
  public:
+  // Must be called on the impl thread while the main thread is blocked. This is
+  // so that |provider| stays alive while this is being created.
   static scoped_ptr<VideoLayerImpl> Create(LayerTreeImpl* tree_impl,
                                            int id,
                                            VideoFrameProvider* provider,
@@ -31,7 +33,6 @@
 
   // LayerImpl implementation.
   scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
-  void PushPropertiesTo(LayerImpl* layer) override;
   bool WillDraw(DrawMode draw_mode,
                 ResourceProvider* resource_provider) override;
   void AppendQuads(RenderPass* render_pass,
@@ -41,16 +42,14 @@
   void ReleaseResources() override;
 
   void SetNeedsRedraw();
-
-  void SetProviderClientImpl(
-      scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl);
-
   media::VideoRotation video_rotation() const { return video_rotation_; }
 
  private:
-  VideoLayerImpl(LayerTreeImpl* tree_impl,
-                 int id,
-                 media::VideoRotation video_rotation);
+  VideoLayerImpl(
+      LayerTreeImpl* tree_impl,
+      int id,
+      scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl,
+      media::VideoRotation video_rotation);
 
   const char* LayerTypeAsString() const override;
 
diff --git a/cc/layers/video_layer_impl_unittest.cc b/cc/layers/video_layer_impl_unittest.cc
index 2fde495..57ad33a 100644
--- a/cc/layers/video_layer_impl_unittest.cc
+++ b/cc/layers/video_layer_impl_unittest.cc
@@ -17,12 +17,22 @@
 namespace cc {
 namespace {
 
+// NOTE: We cannot use DebugScopedSetImplThreadAndMainThreadBlocked in these
+// tests because it gets destroyed before the VideoLayerImpl is destroyed. This
+// causes a DCHECK in VideoLayerImpl's destructor to fail.
+static void DebugSetImplThreadAndMainThreadBlocked(Proxy* proxy) {
+#if DCHECK_IS_ON()
+  proxy->SetCurrentThreadIsImplThread(true);
+  proxy->SetMainThreadBlocked(true);
+#endif
+}
+
 TEST(VideoLayerImplTest, Occlusion) {
   gfx::Size layer_size(1000, 1000);
   gfx::Size viewport_size(1000, 1000);
 
   LayerTestCommon::LayerImplTest impl;
-  DebugScopedSetImplThreadAndMainThreadBlocked thread(impl.proxy());
+  DebugSetImplThreadAndMainThreadBlocked(impl.proxy());
 
   scoped_refptr<media::VideoFrame> video_frame =
       media::VideoFrame::CreateFrame(media::VideoFrame::YV12,
@@ -76,7 +86,7 @@
 
 TEST(VideoLayerImplTest, DidBecomeActiveShouldSetActiveVideoLayer) {
   LayerTestCommon::LayerImplTest impl;
-  DebugScopedSetImplThreadAndMainThreadBlocked thread(impl.proxy());
+  DebugSetImplThreadAndMainThreadBlocked(impl.proxy());
 
   FakeVideoFrameProvider provider;
   VideoLayerImpl* video_layer_impl =
@@ -85,10 +95,10 @@
   VideoFrameProviderClientImpl* client =
       static_cast<VideoFrameProviderClientImpl*>(provider.client());
   ASSERT_TRUE(client);
-  EXPECT_FALSE(client->active_video_layer());
 
+  EXPECT_FALSE(client->ActiveVideoLayer());
   video_layer_impl->DidBecomeActive();
-  EXPECT_EQ(video_layer_impl, client->active_video_layer());
+  EXPECT_EQ(video_layer_impl, client->ActiveVideoLayer());
 }
 
 TEST(VideoLayerImplTest, Rotated0) {
@@ -96,7 +106,7 @@
   gfx::Size viewport_size(1000, 500);
 
   LayerTestCommon::LayerImplTest impl;
-  DebugScopedSetImplThreadAndMainThreadBlocked thread(impl.proxy());
+  DebugSetImplThreadAndMainThreadBlocked(impl.proxy());
 
   scoped_refptr<media::VideoFrame> video_frame =
       media::VideoFrame::CreateFrame(media::VideoFrame::YV12,
@@ -132,7 +142,7 @@
   gfx::Size viewport_size(1000, 500);
 
   LayerTestCommon::LayerImplTest impl;
-  DebugScopedSetImplThreadAndMainThreadBlocked thread(impl.proxy());
+  DebugSetImplThreadAndMainThreadBlocked(impl.proxy());
 
   scoped_refptr<media::VideoFrame> video_frame =
       media::VideoFrame::CreateFrame(media::VideoFrame::YV12,
@@ -168,7 +178,7 @@
   gfx::Size viewport_size(1000, 500);
 
   LayerTestCommon::LayerImplTest impl;
-  DebugScopedSetImplThreadAndMainThreadBlocked thread(impl.proxy());
+  DebugSetImplThreadAndMainThreadBlocked(impl.proxy());
 
   scoped_refptr<media::VideoFrame> video_frame =
       media::VideoFrame::CreateFrame(media::VideoFrame::YV12,
@@ -204,7 +214,7 @@
   gfx::Size viewport_size(1000, 500);
 
   LayerTestCommon::LayerImplTest impl;
-  DebugScopedSetImplThreadAndMainThreadBlocked thread(impl.proxy());
+  DebugSetImplThreadAndMainThreadBlocked(impl.proxy());
 
   scoped_refptr<media::VideoFrame> video_frame =
       media::VideoFrame::CreateFrame(media::VideoFrame::YV12,
diff --git a/cc/layers/viewport.cc b/cc/layers/viewport.cc
new file mode 100644
index 0000000..944f1bbd
--- /dev/null
+++ b/cc/layers/viewport.cc
@@ -0,0 +1,133 @@
+// 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 "cc/layers/viewport.h"
+
+#include "base/logging.h"
+#include "cc/input/top_controls_manager.h"
+#include "cc/trees/layer_tree_host_impl.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "ui/gfx/geometry/vector2d_conversions.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace cc {
+
+// static
+scoped_ptr<Viewport> Viewport::Create(
+    LayerTreeHostImpl* host_impl) {
+  return make_scoped_ptr(new Viewport(host_impl));
+}
+
+Viewport::Viewport(LayerTreeHostImpl* host_impl)
+    : host_impl_(host_impl) {
+  DCHECK(host_impl_);
+}
+
+Viewport::ScrollResult Viewport::ScrollBy(const gfx::Vector2dF& delta,
+                                          const gfx::Point& viewport_point,
+                                          bool is_wheel_scroll) {
+  gfx::Vector2dF content_delta = delta;
+  ScrollResult result;
+
+  if (ShouldTopControlsConsumeScroll(delta)) {
+    result.top_controls_applied_delta = ScrollTopControls(delta);
+    content_delta -= result.top_controls_applied_delta;
+  }
+
+  gfx::Vector2dF pending_content_delta = content_delta;
+
+  if (OuterScrollLayer()) {
+    pending_content_delta -= host_impl_->ScrollLayer(OuterScrollLayer(),
+                                                     pending_content_delta,
+                                                     viewport_point,
+                                                     is_wheel_scroll);
+  }
+
+  // TODO(bokan): This shouldn't be needed but removing it causes subtle
+  // viewport movement during top controls manipulation.
+  if (!gfx::ToRoundedVector2d(pending_content_delta).IsZero()) {
+    pending_content_delta -= host_impl_->ScrollLayer(InnerScrollLayer(),
+                                                     pending_content_delta,
+                                                     viewport_point,
+                                                     is_wheel_scroll);
+    result.unused_scroll_delta = AdjustOverscroll(pending_content_delta);
+  }
+
+
+  result.applied_delta = content_delta - pending_content_delta;
+  return result;
+}
+
+gfx::Vector2dF Viewport::ScrollTopControls(const gfx::Vector2dF& delta) {
+  gfx::Vector2dF excess_delta =
+      host_impl_->top_controls_manager()->ScrollBy(delta);
+
+  return delta - excess_delta;
+}
+
+bool Viewport::ShouldTopControlsConsumeScroll(
+    const gfx::Vector2dF& scroll_delta) const {
+  // Always consume if it's in the direction to show the top controls.
+  if (scroll_delta.y() < 0)
+    return true;
+
+  if (TotalScrollOffset().y() < MaxTotalScrollOffset().y())
+    return true;
+
+  return false;
+}
+
+gfx::Vector2dF Viewport::AdjustOverscroll(const gfx::Vector2dF& delta) const {
+  const float kEpsilon = 0.1f;
+  gfx::Vector2dF adjusted = delta;
+
+  if (std::abs(adjusted.x()) < kEpsilon)
+    adjusted.set_x(0.0f);
+  if (std::abs(adjusted.y()) < kEpsilon)
+    adjusted.set_y(0.0f);
+
+  // Disable overscroll on axes which are impossible to scroll.
+  if (host_impl_->settings().report_overscroll_only_for_scrollable_axes) {
+    if (std::abs(MaxTotalScrollOffset().x()) <= kEpsilon ||
+        !InnerScrollLayer()->user_scrollable_horizontal())
+      adjusted.set_x(0.0f);
+    if (std::abs(MaxTotalScrollOffset().y()) <= kEpsilon ||
+        !InnerScrollLayer()->user_scrollable_vertical())
+      adjusted.set_y(0.0f);
+  }
+
+  return adjusted;
+}
+
+gfx::ScrollOffset Viewport::MaxTotalScrollOffset() const {
+  gfx::ScrollOffset offset;
+
+  offset += InnerScrollLayer()->MaxScrollOffset();
+
+  if (OuterScrollLayer())
+    offset += OuterScrollLayer()->MaxScrollOffset();
+
+  return offset;
+}
+
+gfx::ScrollOffset Viewport::TotalScrollOffset() const {
+  gfx::ScrollOffset offset;
+
+  offset += InnerScrollLayer()->CurrentScrollOffset();
+
+  if (OuterScrollLayer())
+    offset += OuterScrollLayer()->CurrentScrollOffset();
+
+  return offset;
+}
+
+LayerImpl* Viewport::InnerScrollLayer() const {
+  return host_impl_->InnerViewportScrollLayer();
+}
+
+LayerImpl* Viewport::OuterScrollLayer() const {
+  return host_impl_->OuterViewportScrollLayer();
+}
+
+}  // namespace cc
diff --git a/cc/layers/viewport.h b/cc/layers/viewport.h
new file mode 100644
index 0000000..25528af0
--- /dev/null
+++ b/cc/layers/viewport.h
@@ -0,0 +1,59 @@
+// 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 CC_LAYERS_VIEWPORT_H_
+#define CC_LAYERS_VIEWPORT_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/layers/layer_impl.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace cc {
+
+class LayerTreeHostImpl;
+
+// Encapsulates gesture handling logic on the viewport layers. The "viewport"
+// is made up of two scrolling layers, the inner viewport (visual) and the
+// outer viewport (layout) scroll layers. These layers have different scroll
+// bubbling behavior from the rest of the layer tree which is encoded in this
+// class.
+class CC_EXPORT Viewport {
+ public:
+  struct ScrollResult {
+    gfx::Vector2dF applied_delta;
+    gfx::Vector2dF unused_scroll_delta;
+    gfx::Vector2dF top_controls_applied_delta;
+  };
+
+  static scoped_ptr<Viewport> Create(LayerTreeHostImpl* host_impl);
+
+  // Scrolls the viewport, applying the unique bubbling between the inner and
+  // outer viewport. Scrolls can be consumed by top controls.
+  ScrollResult ScrollBy(const gfx::Vector2dF& delta,
+                        const gfx::Point& viewport_point,
+                        bool is_wheel_scroll);
+
+ private:
+  explicit Viewport(LayerTreeHostImpl* host_impl);
+
+  bool ShouldTopControlsConsumeScroll(const gfx::Vector2dF& scroll_delta) const;
+  gfx::Vector2dF AdjustOverscroll(const gfx::Vector2dF& delta) const;
+
+  // Sends the delta to the top controls, returns the amount applied.
+  gfx::Vector2dF ScrollTopControls(const gfx::Vector2dF& delta);
+
+  gfx::ScrollOffset MaxTotalScrollOffset() const;
+  gfx::ScrollOffset TotalScrollOffset() const;
+
+  LayerImpl* InnerScrollLayer() const;
+  LayerImpl* OuterScrollLayer() const;
+
+  LayerTreeHostImpl* host_impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(Viewport);
+};
+
+}  // namespace cc
+
+#endif  // CC_LAYERS_VIEWPORT_H_
diff --git a/cc/quads/list_container.cc b/cc/quads/list_container.cc
index 04e1c97..c5cbf87fa 100644
--- a/cc/quads/list_container.cc
+++ b/cc/quads/list_container.cc
@@ -433,11 +433,10 @@
 }
 
 template <typename BaseElementType>
-BaseElementType* ListContainer<BaseElementType>::Allocate(
+void* ListContainer<BaseElementType>::Allocate(
     size_t size_of_actual_element_in_bytes) {
   DCHECK_LE(size_of_actual_element_in_bytes, data_->element_size());
-  void* result = data_->Allocate();
-  return static_cast<BaseElementType*>(result);
+  return data_->Allocate();
 }
 
 template <typename BaseElementType>
diff --git a/cc/quads/list_container.h b/cc/quads/list_container.h
index dd313fc8..7abbe27 100644
--- a/cc/quads/list_container.h
+++ b/cc/quads/list_container.h
@@ -218,7 +218,7 @@
 
  private:
   // Hands out memory location for an element at the end of data structure.
-  BaseElementType* Allocate(size_t size_of_actual_element_in_bytes);
+  void* Allocate(size_t size_of_actual_element_in_bytes);
 
   scoped_ptr<ListContainerCharAllocator> data_;
 
diff --git a/cc/resources/one_copy_tile_task_worker_pool.cc b/cc/resources/one_copy_tile_task_worker_pool.cc
index 1139fbe..3383074 100644
--- a/cc/resources/one_copy_tile_task_worker_pool.cc
+++ b/cc/resources/one_copy_tile_task_worker_pool.cc
@@ -321,9 +321,13 @@
 
     gfx::GpuMemoryBuffer* gpu_memory_buffer = write_lock->GetGpuMemoryBuffer();
     if (gpu_memory_buffer) {
-      TileTaskWorkerPool::PlaybackToMemory(
-          gpu_memory_buffer->Map(), src->format(), src->size(),
-          gpu_memory_buffer->GetStride(), raster_source, rect, scale);
+      void* data = NULL;
+      bool rv = gpu_memory_buffer->Map(&data);
+      DCHECK(rv);
+      uint32 stride;
+      gpu_memory_buffer->GetStride(&stride);
+      TileTaskWorkerPool::PlaybackToMemory(data, src->format(), src->size(),
+                                           stride, raster_source, rect, scale);
       gpu_memory_buffer->Unmap();
     }
   }
diff --git a/cc/resources/zero_copy_tile_task_worker_pool.cc b/cc/resources/zero_copy_tile_task_worker_pool.cc
index 8b80aa3..42e8ada 100644
--- a/cc/resources/zero_copy_tile_task_worker_pool.cc
+++ b/cc/resources/zero_copy_tile_task_worker_pool.cc
@@ -31,9 +31,14 @@
     if (!gpu_memory_buffer)
       return;
 
-    TileTaskWorkerPool::PlaybackToMemory(
-        gpu_memory_buffer->Map(), resource_->format(), resource_->size(),
-        gpu_memory_buffer->GetStride(), raster_source, rect, scale);
+    void* data = NULL;
+    bool rv = gpu_memory_buffer->Map(&data);
+    DCHECK(rv);
+    uint32 stride;
+    gpu_memory_buffer->GetStride(&stride);
+    TileTaskWorkerPool::PlaybackToMemory(data, resource_->format(),
+                                         resource_->size(), stride,
+                                         raster_source, rect, scale);
     gpu_memory_buffer->Unmap();
   }
 
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 29e6e38..37e7c53 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -87,6 +87,8 @@
       primary_frame_source_internal_(external_begin_frame_source.Pass()),
       background_frame_source_internal_(),
       vsync_observer_(NULL),
+      authoritative_vsync_interval_(base::TimeDelta()),
+      last_vsync_timebase_(base::TimeTicks()),
       throttle_frame_production_(scheduler_settings.throttle_frame_production),
       settings_(scheduler_settings),
       client_(client),
@@ -148,9 +150,14 @@
 
 void Scheduler::CommitVSyncParameters(base::TimeTicks timebase,
                                       base::TimeDelta interval) {
-  // TODO(brianderson): We should not be receiving 0 intervals.
-  if (interval == base::TimeDelta())
+  if (authoritative_vsync_interval_ != base::TimeDelta()) {
+    interval = authoritative_vsync_interval_;
+  } else if (interval == base::TimeDelta()) {
+    // TODO(brianderson): We should not be receiving 0 intervals.
     interval = BeginFrameArgs::DefaultInterval();
+  }
+
+  last_vsync_timebase_ = timebase;
 
   if (vsync_observer_)
     vsync_observer_->OnUpdateVSyncParameters(timebase, interval);
@@ -431,6 +438,12 @@
   ProcessScheduledActions();
 }
 
+void Scheduler::SetAuthoritativeVSyncInterval(const base::TimeDelta& interval) {
+  authoritative_vsync_interval_ = interval;
+  if (vsync_observer_)
+    vsync_observer_->OnUpdateVSyncParameters(last_vsync_timebase_, interval);
+}
+
 // BeginRetroFrame is called for BeginFrames that we've deferred because
 // the scheduler was in the middle of processing a previous BeginFrame.
 void Scheduler::BeginRetroFrame() {
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index 8ed584a..3a4af5a1 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -166,6 +166,8 @@
 
   void SetChildrenNeedBeginFrames(bool children_need_begin_frames);
 
+  void SetAuthoritativeVSyncInterval(const base::TimeDelta& interval);
+
  protected:
   Scheduler(SchedulerClient* client,
             const SchedulerSettings& scheduler_settings,
@@ -189,6 +191,9 @@
   scoped_ptr<BeginFrameSource> unthrottled_frame_source_internal_;
 
   VSyncParameterObserver* vsync_observer_;
+  base::TimeDelta authoritative_vsync_interval_;
+  base::TimeTicks last_vsync_timebase_;
+
   bool throttle_frame_production_;
 
   const SchedulerSettings settings_;
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index b59359c0..dacc03ce2 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -2272,5 +2272,33 @@
   client_->Reset();
 }
 
+TEST_F(SchedulerTest, AuthoritativeVSyncInterval) {
+  SetUpScheduler(true);
+
+  base::TimeDelta initial_interval =
+      scheduler_->begin_impl_frame_args().interval;
+  base::TimeDelta authoritative_interval =
+      base::TimeDelta::FromMilliseconds(33);
+
+  scheduler_->SetNeedsCommit();
+  EXPECT_SCOPED(AdvanceFrame());
+
+  EXPECT_EQ(initial_interval, scheduler_->begin_impl_frame_args().interval);
+
+  scheduler_->NotifyBeginMainFrameStarted();
+  scheduler_->NotifyReadyToCommit();
+  task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true));
+
+  scheduler_->SetAuthoritativeVSyncInterval(authoritative_interval);
+
+  EXPECT_SCOPED(AdvanceFrame());
+
+  // At the next BeginFrame, authoritative interval is used instead of previous
+  // interval.
+  EXPECT_NE(initial_interval, scheduler_->begin_impl_frame_args().interval);
+  EXPECT_EQ(authoritative_interval,
+            scheduler_->begin_impl_frame_args().interval);
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/test/fake_proxy.h b/cc/test/fake_proxy.h
index d5ad313..d2a8f32 100644
--- a/cc/test/fake_proxy.h
+++ b/cc/test/fake_proxy.h
@@ -47,6 +47,8 @@
   void SetDebugState(const LayerTreeDebugState& debug_state) override {}
   bool MainFrameWillHappenForTesting() override;
   void SetChildrenNeedBeginFrames(bool children_need_begin_frames) override {}
+  void SetAuthoritativeVSyncInterval(const base::TimeDelta& interval) override {
+  }
 
   virtual RendererCapabilities& GetRendererCapabilities();
   void SetMaxPartialTextureUpdates(size_t max);
diff --git a/cc/test/scheduler_test_common.h b/cc/test/scheduler_test_common.h
index 763ccc8..825c9a3d 100644
--- a/cc/test/scheduler_test_common.h
+++ b/cc/test/scheduler_test_common.h
@@ -186,6 +186,7 @@
 
   BeginFrameSource& frame_source() { return *frame_source_; }
   bool FrameProductionThrottled() { return throttle_frame_production_; }
+  BeginFrameArgs begin_impl_frame_args() { return begin_impl_frame_args_; }
 
   ~TestScheduler() override;
 
diff --git a/cc/test/test_gpu_memory_buffer_manager.cc b/cc/test/test_gpu_memory_buffer_manager.cc
index 7430051..b43cd76c 100644
--- a/cc/test/test_gpu_memory_buffer_manager.cc
+++ b/cc/test/test_gpu_memory_buffer_manager.cc
@@ -41,13 +41,14 @@
         mapped_(false) {}
 
   // Overridden from gfx::GpuMemoryBuffer:
-  void* Map() override {
+  bool Map(void** data) override {
     DCHECK(!mapped_);
     if (!shared_memory_->Map(StrideInBytes(size_.width(), format_) *
                              size_.height()))
-      return NULL;
+      return false;
     mapped_ = true;
-    return shared_memory_->memory();
+    *data = shared_memory_->memory();
+    return true;
   }
   void Unmap() override {
     DCHECK(mapped_);
@@ -56,8 +57,8 @@
   }
   bool IsMapped() const override { return mapped_; }
   Format GetFormat() const override { return format_; }
-  uint32 GetStride() const override {
-    return StrideInBytes(size_.width(), format_);
+  void GetStride(uint32* stride) const override {
+    *stride = StrideInBytes(size_.width(), format_);
   }
   gfx::GpuMemoryBufferHandle GetHandle() const override {
     gfx::GpuMemoryBufferHandle handle;
diff --git a/cc/test/test_task_graph_runner.cc b/cc/test/test_task_graph_runner.cc
index fe9734e..abc0d28 100644
--- a/cc/test/test_task_graph_runner.cc
+++ b/cc/test/test_task_graph_runner.cc
@@ -7,7 +7,7 @@
 namespace cc {
 
 TestTaskGraphRunner::TestTaskGraphRunner()
-    : worker_thread_(this, "CompositorWorker") {
+    : worker_thread_(this, "CompositorTileWorker1") {
   worker_thread_.Start();
 }
 
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 9d2be10..890d3040 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -1273,4 +1273,9 @@
   client_->SendBeginFramesToChildren(args);
 }
 
+void LayerTreeHost::SetAuthoritativeVSyncInterval(
+    const base::TimeDelta& interval) {
+  proxy_->SetAuthoritativeVSyncInterval(interval);
+}
+
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index e1bcad5..ef5cb6a 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -306,6 +306,8 @@
   void SetChildrenNeedBeginFrames(bool children_need_begin_frames) const;
   void SendBeginFramesToChildren(const BeginFrameArgs& args) const;
 
+  void SetAuthoritativeVSyncInterval(const base::TimeDelta& interval);
+
  protected:
   LayerTreeHost(LayerTreeHostClient* client,
                 SharedBitmapManager* shared_bitmap_manager,
diff --git a/cc/trees/layer_tree_host_common_perftest.cc b/cc/trees/layer_tree_host_common_perftest.cc
index 1732e5588..2f2ede40 100644
--- a/cc/trees/layer_tree_host_common_perftest.cc
+++ b/cc/trees/layer_tree_host_common_perftest.cc
@@ -163,6 +163,7 @@
 
 class BspTreePerfTest : public CalcDrawPropsImplTest {
  public:
+  BspTreePerfTest() : num_duplicates_(1) {}
   void RunSortLayers() { RunTest(false, false, false); }
 
   void SetNumberOfDuplicates(int num_duplicates) {
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 2c13fcbc..00ae766 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -39,6 +39,7 @@
 #include "cc/layers/painted_scrollbar_layer_impl.h"
 #include "cc/layers/render_surface_impl.h"
 #include "cc/layers/scrollbar_layer_impl_base.h"
+#include "cc/layers/viewport.h"
 #include "cc/output/compositor_frame_metadata.h"
 #include "cc/output/copy_output_request.h"
 #include "cc/output/delegating_renderer.h"
@@ -240,6 +241,8 @@
       LayerTreeImpl::create(this, new SyncedProperty<ScaleGroup>(),
                             new SyncedTopControls, new SyncedElasticOverscroll);
 
+  viewport_ = Viewport::Create(this);
+
   TRACE_EVENT_OBJECT_CREATED_WITH_ID(
       TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::LayerTreeHostImpl", id_);
 
@@ -841,6 +844,8 @@
         append_quads_data.visible_content_area);
     rendering_stats_instrumentation_->AddApproximatedVisibleContentArea(
         append_quads_data.approximated_visible_content_area);
+    rendering_stats_instrumentation_->AddCheckerboardedVisibleContentArea(
+        append_quads_data.checkerboarded_visible_content_area);
 
     num_missing_tiles += append_quads_data.num_missing_tiles;
     num_incomplete_tiles += append_quads_data.num_incomplete_tiles;
@@ -2035,12 +2040,21 @@
     return;
   }
 
+  // Pass the single-threaded synchronous task graph runner to the worker pool
+  // if we're in synchronous single-threaded mode.
+  TaskGraphRunner* task_graph_runner = task_graph_runner_;
+  if (IsSynchronousSingleThreaded()) {
+    DCHECK(!single_thread_synchronous_task_graph_runner_);
+    single_thread_synchronous_task_graph_runner_.reset(new TaskGraphRunner);
+    task_graph_runner = single_thread_synchronous_task_graph_runner_.get();
+  }
+
   if (use_gpu_rasterization_) {
     *resource_pool =
         ResourcePool::Create(resource_provider_.get(), GL_TEXTURE_2D);
 
     *tile_task_worker_pool = GpuTileTaskWorkerPool::Create(
-        task_runner, task_graph_runner_,
+        task_runner, task_graph_runner,
         static_cast<GpuRasterizer*>(rasterizer_.get()));
     return;
   }
@@ -2058,15 +2072,6 @@
       *resource_pool =
           ResourcePool::Create(resource_provider_.get(), image_target);
 
-      TaskGraphRunner* task_graph_runner;
-      if (IsSynchronousSingleThreaded()) {
-        DCHECK(!single_thread_synchronous_task_graph_runner_);
-        single_thread_synchronous_task_graph_runner_.reset(new TaskGraphRunner);
-        task_graph_runner = single_thread_synchronous_task_graph_runner_.get();
-      } else {
-        task_graph_runner = task_graph_runner_;
-      }
-
       *tile_task_worker_pool = ZeroCopyTileTaskWorkerPool::Create(
           task_runner, task_graph_runner, resource_provider_.get());
       return;
@@ -2080,7 +2085,7 @@
           ResourcePool::Create(resource_provider_.get(), GL_TEXTURE_2D);
 
       *tile_task_worker_pool = OneCopyTileTaskWorkerPool::Create(
-          task_runner, task_graph_runner_, context_provider,
+          task_runner, task_graph_runner, context_provider,
           resource_provider_.get(), staging_resource_pool_.get());
       return;
     }
@@ -2477,7 +2482,6 @@
 
 gfx::Vector2dF LayerTreeHostImpl::ScrollLayerWithViewportSpaceDelta(
     LayerImpl* layer_impl,
-    float scale_from_viewport_to_screen_space,
     const gfx::PointF& viewport_point,
     const gfx::Vector2dF& viewport_delta) {
   // Layers with non-invertible screen space transforms should not have passed
@@ -2491,6 +2495,7 @@
   // layers, we may need to explicitly handle uninvertible transforms here.
   DCHECK(did_invert);
 
+  float scale_from_viewport_to_screen_space = device_scale_factor_;
   gfx::PointF screen_space_point =
       gfx::ScalePoint(viewport_point, scale_from_viewport_to_screen_space);
 
@@ -2565,19 +2570,25 @@
   return gfx::Vector2dF(scrolled.x(), scrolled.y());
 }
 
-bool LayerTreeHostImpl::ShouldTopControlsConsumeScroll(
-    const gfx::Vector2dF& scroll_delta) const {
-  DCHECK(CurrentlyScrollingLayer());
+gfx::Vector2dF LayerTreeHostImpl::ScrollLayer(LayerImpl* layer_impl,
+                                              const gfx::Vector2dF& delta,
+                                              const gfx::Point& viewport_point,
+                                              bool is_wheel_scroll) {
+  // Gesture events need to be transformed from viewport coordinates to
+  // local layer coordinates so that the scrolling contents exactly follow
+  // the user's finger. In contrast, wheel events represent a fixed amount
+  // of scrolling so we can just apply them directly, but the page scale
+  // factor is applied to the scroll delta.
+  if (is_wheel_scroll) {
+    float scale_factor = active_tree()->current_page_scale_factor();
+    return ScrollLayerWithLocalDelta(layer_impl,
+                                     delta,
+                                     scale_factor);
+  }
 
-  // Always consume if it's in the direction to show the top controls.
-  if (scroll_delta.y() < 0)
-    return true;
-
-  if (active_tree()->TotalScrollOffset().y() <
-      active_tree()->TotalMaxScrollOffset().y())
-    return true;
-
-  return false;
+  return ScrollLayerWithViewportSpaceDelta(layer_impl,
+                                           viewport_point,
+                                           delta);
 }
 
 InputHandlerScrollResult LayerTreeHostImpl::ScrollBy(
@@ -2593,127 +2604,68 @@
   bool did_scroll_y = false;
   bool did_scroll_top_controls = false;
 
-  bool consume_by_top_controls = ShouldTopControlsConsumeScroll(scroll_delta);
-
-  // There's an edge case where the outer viewport isn't scrollable when the
-  // scroll starts, however, as the top controls show the outer viewport becomes
-  // scrollable. Therefore, always try scrolling the outer viewport before the
-  // inner.
-  // TODO(bokan): Move the top controls logic out of the loop since the scroll
-  // that causes the outer viewport to become scrollable will still be applied
-  // to the inner viewport.
-  LayerImpl* start_layer = CurrentlyScrollingLayer();
-  if (start_layer == InnerViewportScrollLayer() && OuterViewportScrollLayer())
-      start_layer = OuterViewportScrollLayer();
-
-  for (LayerImpl* layer_impl = start_layer;
+  for (LayerImpl* layer_impl = CurrentlyScrollingLayer();
        layer_impl;
        layer_impl = layer_impl->parent()) {
-    if (!layer_impl->scrollable())
+    // Skip the outer viewport scroll layer so that we try to scroll the
+    // viewport only once. i.e. The inner viewport layer represents the
+    // viewport.
+    if (!layer_impl->scrollable() || layer_impl == OuterViewportScrollLayer())
       continue;
 
-    if (layer_impl == InnerViewportScrollLayer() ||
-        layer_impl == OuterViewportScrollLayer()) {
-      if (consume_by_top_controls) {
-        gfx::Vector2dF excess_delta =
-            top_controls_manager_->ScrollBy(pending_delta);
-        gfx::Vector2dF applied_delta = pending_delta - excess_delta;
-        pending_delta = excess_delta;
-        // Force updating of vertical adjust values if needed.
-        if (applied_delta.y() != 0)
-          did_scroll_top_controls = true;
-      }
-      // Track root layer deltas for reporting overscroll.
-      if (layer_impl == InnerViewportScrollLayer())
-        unused_root_delta = pending_delta;
-    }
-
     gfx::Vector2dF applied_delta;
-    // Gesture events need to be transformed from viewport coordinates to local
-    // layer coordinates so that the scrolling contents exactly follow the
-    // user's finger. In contrast, wheel events represent a fixed amount of
-    // scrolling so we can just apply them directly, but the page scale factor
-    // is applied to the scroll delta.
-    if (!wheel_scrolling_) {
-      float scale_from_viewport_to_screen_space = device_scale_factor_;
-      applied_delta =
-          ScrollLayerWithViewportSpaceDelta(layer_impl,
-                                            scale_from_viewport_to_screen_space,
-                                            viewport_point, pending_delta);
-    } else {
-      applied_delta = ScrollLayerWithLocalDelta(
-          layer_impl, pending_delta, active_tree_->current_page_scale_factor());
-    }
-
-    const float kEpsilon = 0.1f;
     if (layer_impl == InnerViewportScrollLayer()) {
-      unused_root_delta.Subtract(applied_delta);
-      if (std::abs(unused_root_delta.x()) < kEpsilon)
-        unused_root_delta.set_x(0.0f);
-      if (std::abs(unused_root_delta.y()) < kEpsilon)
-        unused_root_delta.set_y(0.0f);
-      // Disable overscroll on axes which is impossible to scroll.
-      if (settings_.report_overscroll_only_for_scrollable_axes) {
-        if (std::abs(active_tree_->TotalMaxScrollOffset().x()) <= kEpsilon ||
-            !layer_impl->user_scrollable_horizontal())
-          unused_root_delta.set_x(0.0f);
-        if (std::abs(active_tree_->TotalMaxScrollOffset().y()) <= kEpsilon ||
-            !layer_impl->user_scrollable_vertical())
-          unused_root_delta.set_y(0.0f);
-      }
+      Viewport::ScrollResult result = viewport()->ScrollBy(pending_delta,
+                                                           viewport_point,
+                                                           wheel_scrolling_);
+      applied_delta = result.applied_delta;
+      unused_root_delta = result.unused_scroll_delta;
+      did_scroll_top_controls = result.top_controls_applied_delta.y() != 0;
+    } else {
+      applied_delta = ScrollLayer(layer_impl,
+                                  pending_delta,
+                                  viewport_point,
+                                  wheel_scrolling_);
     }
 
-    // Scrolls should bubble perfectly between the outer and inner viewports.
-    bool allow_unrestricted_bubbling_for_current_layer =
-        layer_impl == OuterViewportScrollLayer();
-    bool allow_bubbling_for_current_layer =
-        allow_unrestricted_bubbling_for_current_layer || should_bubble_scrolls_;
-
     // If the layer wasn't able to move, try the next one in the hierarchy.
+    const float kEpsilon = 0.1f;
     bool did_move_layer_x = std::abs(applied_delta.x()) > kEpsilon;
     bool did_move_layer_y = std::abs(applied_delta.y()) > kEpsilon;
     did_scroll_x |= did_move_layer_x;
     did_scroll_y |= did_move_layer_y;
-    if (!did_move_layer_x && !did_move_layer_y) {
-      if (allow_bubbling_for_current_layer || !did_lock_scrolling_layer_)
-        continue;
-      else
+
+    if (did_move_layer_x || did_move_layer_y) {
+      did_lock_scrolling_layer_ = true;
+
+      // When scrolls are allowed to bubble, it's important that the original
+      // scrolling layer be preserved. This ensures that, after a scroll
+      // bubbles, the user can reverse scroll directions and immediately resume
+      // scrolling the original layer that scrolled.
+      if (!should_bubble_scrolls_) {
+        active_tree_->SetCurrentlyScrollingLayer(layer_impl);
         break;
-    }
+      }
 
-    did_lock_scrolling_layer_ = true;
-
-    // When scrolls are allowed to bubble, it's important that the original
-    // scrolling layer be preserved. This ensures that, after a scroll bubbles,
-    // the user can reverse scroll directions and immediately resume scrolling
-    // the original layer that scrolled.
-    if (!should_bubble_scrolls_)
-      active_tree_->SetCurrentlyScrollingLayer(layer_impl);
-
-    if (!allow_bubbling_for_current_layer)
-      break;
-
-    if (allow_unrestricted_bubbling_for_current_layer) {
-      pending_delta -= applied_delta;
-    } else {
       // If the applied delta is within 45 degrees of the input delta, bail out
       // to make it easier to scroll just one layer in one direction without
       // affecting any of its parents.
       float angle_threshold = 45;
       if (MathUtil::SmallestAngleBetweenVectors(applied_delta, pending_delta) <
-          angle_threshold) {
-        pending_delta = gfx::Vector2dF();
+          angle_threshold)
         break;
-      }
 
       // Allow further movement only on an axis perpendicular to the direction
       // in which the layer moved.
       gfx::Vector2dF perpendicular_axis(-applied_delta.y(), applied_delta.x());
       pending_delta =
           MathUtil::ProjectVector(pending_delta, perpendicular_axis);
+
+      if (gfx::ToRoundedVector2d(pending_delta).IsZero())
+        break;
     }
 
-    if (gfx::ToRoundedVector2d(pending_delta).IsZero())
+    if (!should_bubble_scrolls_ && did_lock_scrolling_layer_)
       break;
   }
 
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 2efe2dab..388de5e 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -73,6 +73,7 @@
 class UIResourceBitmap;
 class UIResourceRequest;
 struct ScrollAndScaleSet;
+class Viewport;
 
 enum class GpuRasterizationStatus {
   ON,
@@ -524,6 +525,11 @@
     return frame_timing_tracker_.get();
   }
 
+  gfx::Vector2dF ScrollLayer(LayerImpl* layer_impl,
+                             const gfx::Vector2dF& delta,
+                             const gfx::Point& viewport_point,
+                             bool is_wheel_scroll);
+
  protected:
   LayerTreeHostImpl(
       const LayerTreeSettings& settings,
@@ -546,6 +552,11 @@
   Proxy* proxy_;
 
  private:
+  gfx::Vector2dF ScrollLayerWithViewportSpaceDelta(
+      LayerImpl* layer_impl,
+      const gfx::PointF& viewport_point,
+      const gfx::Vector2dF& viewport_delta);
+
   void CreateAndSetRenderer();
   void CreateAndSetTileManager();
   void DestroyTileManager();
@@ -555,6 +566,8 @@
 
   bool IsSynchronousSingleThreaded() const;
 
+  Viewport* viewport() { return viewport_.get(); }
+
   // Scroll by preferring to move the outer viewport first, only moving the
   // inner if the outer is at its scroll extents.
   void ScrollViewportBy(gfx::Vector2dF scroll_delta);
@@ -565,14 +578,6 @@
   void AnimateScrollbars(base::TimeTicks monotonic_time);
   void AnimateTopControls(base::TimeTicks monotonic_time);
 
-  bool ShouldTopControlsConsumeScroll(const gfx::Vector2dF& scroll_delta) const;
-
-  gfx::Vector2dF ScrollLayerWithViewportSpaceDelta(
-      LayerImpl* layer_impl,
-      float scale_from_viewport_to_screen_space,
-      const gfx::PointF& viewport_point,
-      const gfx::Vector2dF& viewport_delta);
-
   void TrackDamageForAllSurfaces(
       LayerImpl* root_draw_layer,
       const LayerImplList& render_surface_layer_list);
@@ -747,6 +752,8 @@
 
   scoped_ptr<FrameTimingTracker> frame_timing_tracker_;
 
+  scoped_ptr<Viewport> viewport_;
+
   DISALLOW_COPY_AND_ASSIGN(LayerTreeHostImpl);
 };
 
diff --git a/cc/trees/layer_tree_host_pixeltest_synchronous.cc b/cc/trees/layer_tree_host_pixeltest_synchronous.cc
index c22075f..7dd6d7d 100644
--- a/cc/trees/layer_tree_host_pixeltest_synchronous.cc
+++ b/cc/trees/layer_tree_host_pixeltest_synchronous.cc
@@ -44,6 +44,36 @@
       PIXEL_TEST_GL, root, base::FilePath(FILE_PATH_LITERAL("green.png")));
 }
 
+class LayerTreeHostSynchronousGPUPixelTest : public LayerTreePixelTest {
+ public:
+  void InitializeSettings(LayerTreeSettings* settings) override {
+    LayerTreePixelTest::InitializeSettings(settings);
+    settings->single_thread_proxy_scheduler = false;
+    settings->gpu_rasterization_enabled = true;
+    settings->gpu_rasterization_forced = true;
+  }
+
+  void BeginTest() override {
+    LayerTreePixelTest::BeginTest();
+    PostCompositeImmediatelyToMainThread();
+  }
+};
+
+TEST_F(LayerTreeHostSynchronousGPUPixelTest, OneContentLayer) {
+  gfx::Size bounds(200, 200);
+
+  FakeContentLayerClient client;
+  SkPaint green_paint;
+  green_paint.setColor(SkColorSetARGB(255, 0, 255, 0));
+  client.add_draw_rect(gfx::RectF(bounds), green_paint);
+  scoped_refptr<PictureLayer> root = PictureLayer::Create(&client);
+  root->SetBounds(bounds);
+  root->SetIsDrawable(true);
+
+  RunSingleThreadedPixelTest(PIXEL_TEST_GL, root,
+                             base::FilePath(FILE_PATH_LITERAL("green.png")));
+}
+
 }  // namespace
 }  // namespace cc
 
diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h
index 945e547..55e54be 100644
--- a/cc/trees/proxy.h
+++ b/cc/trees/proxy.h
@@ -106,6 +106,9 @@
 
   virtual void SetChildrenNeedBeginFrames(bool children_need_begin_frames) = 0;
 
+  virtual void SetAuthoritativeVSyncInterval(
+      const base::TimeDelta& interval) = 0;
+
   // Testing hooks
   virtual bool MainFrameWillHappenForTesting() = 0;
 
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index b6084bc..3633585 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -540,6 +540,11 @@
     scheduler_on_impl_thread_->SetEstimatedParentDrawTime(draw_time);
 }
 
+void SingleThreadProxy::SetMaxSwapsPendingOnImplThread(int max) {
+  if (scheduler_on_impl_thread_)
+    scheduler_on_impl_thread_->SetMaxSwapsPending(max);
+}
+
 void SingleThreadProxy::DidSwapBuffersOnImplThread() {
   TRACE_EVENT0("cc", "SingleThreadProxy::DidSwapBuffersOnImplThread");
   if (scheduler_on_impl_thread_)
@@ -753,6 +758,11 @@
       children_need_begin_frames);
 }
 
+void SingleThreadProxy::SetAuthoritativeVSyncInterval(
+    const base::TimeDelta& interval) {
+  scheduler_on_impl_thread_->SetAuthoritativeVSyncInterval(interval);
+}
+
 void SingleThreadProxy::WillBeginImplFrame(const BeginFrameArgs& args) {
   layer_tree_host_impl_->WillBeginImplFrame(args);
 }
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index cfc746a..0ccd28c 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -62,6 +62,7 @@
   bool SupportsImplScrolling() const override;
   bool MainFrameWillHappenForTesting() override;
   void SetChildrenNeedBeginFrames(bool children_need_begin_frames) override;
+  void SetAuthoritativeVSyncInterval(const base::TimeDelta& interval) override;
 
   // SchedulerClient implementation
   void WillBeginImplFrame(const BeginFrameArgs& args) override;
@@ -87,7 +88,7 @@
   void CommitVSyncParameters(base::TimeTicks timebase,
                              base::TimeDelta interval) override;
   void SetEstimatedParentDrawTime(base::TimeDelta draw_time) override;
-  void SetMaxSwapsPendingOnImplThread(int max) override {}
+  void SetMaxSwapsPendingOnImplThread(int max) override;
   void DidSwapBuffersOnImplThread() override;
   void DidSwapBuffersCompleteOnImplThread() override;
   void OnCanDrawStateChanged(bool can_draw) override;
diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc
index ff2ca46..9a095ef 100644
--- a/cc/trees/thread_proxy.cc
+++ b/cc/trees/thread_proxy.cc
@@ -1139,6 +1139,11 @@
   NOTREACHED() << "Only used by SingleThreadProxy";
 }
 
+void ThreadProxy::SetAuthoritativeVSyncInterval(
+    const base::TimeDelta& interval) {
+  NOTREACHED() << "Only used by SingleThreadProxy";
+}
+
 void ThreadProxy::ReadyToFinalizeTextureUpdates() {
   DCHECK(IsImplThread());
   impl().scheduler->NotifyReadyToCommit();
diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h
index 51cc18c..07f4c6c 100644
--- a/cc/trees/thread_proxy.h
+++ b/cc/trees/thread_proxy.h
@@ -178,6 +178,7 @@
   void SetDebugState(const LayerTreeDebugState& debug_state) override;
   bool MainFrameWillHappenForTesting() override;
   void SetChildrenNeedBeginFrames(bool children_need_begin_frames) override;
+  void SetAuthoritativeVSyncInterval(const base::TimeDelta& interval) override;
 
   // LayerTreeHostImplClient implementation
   void UpdateRendererCapabilitiesOnImplThread() override;
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index f6bb6b7ad..c4ca42f 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -484,7 +484,6 @@
   }
   if (!is_ios) {
     sources += [
-      "$root_gen_dir/blink/public/resources/blink_resources.pak",
       "$root_gen_dir/content/browser/tracing/tracing_resources.pak",
       "$root_gen_dir/content/content_resources.pak",
     ]
@@ -587,7 +586,10 @@
     ]
 
     if (!is_ios) {
-      sources += [ "$root_gen_dir/content/app/resources/content_resources_${percent}_percent.pak" ]
+      sources += [
+        "$root_gen_dir/blink/public/resources/blink_resources_${percent}_percent.pak",
+        "$root_gen_dir/content/app/resources/content_resources_${percent}_percent.pak",
+      ]
       deps += [ "//content:resources" ]
     }
     if (use_ash) {
diff --git a/chrome/VERSION b/chrome/VERSION
index a05bee0c..3ac353ed 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=43
 MINOR=0
-BUILD=2344
+BUILD=2347
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 02226f6..9aed3b7 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -447,6 +447,19 @@
   android_manifest = "shell/javatests/AndroidManifest.xml"
 }
 
+# GYP: //chrome/chrome_tests.gypi:chrome_shell_unit_tests
+java_binary("chrome_shell_unit_tests") {
+  testonly = true
+  java_files = [ "android/junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java" ]
+  main_class = "org.chromium.testing.local.JunitTestMain"
+  deps = [
+    ":chrome_java",
+    "//base:base_java",
+    "//base:base_java_test_support",
+    "//testing/android/junit:junit_test_support",
+  ]
+}
+
 # GYP: //chrome/chrome_tests.gypi:chrome_sync_shell_test_apk
 android_apk("chrome_sync_shell_test_apk") {
   testonly = true
diff --git a/chrome/android/java/res/drawable-hdpi/help_outline.png b/chrome/android/java/res/drawable-hdpi/help_outline.png
index 2e0d407..50bf4cb 100644
--- a/chrome/android/java/res/drawable-hdpi/help_outline.png
+++ b/chrome/android/java/res/drawable-hdpi/help_outline.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_account_child.png b/chrome/android/java/res/drawable-hdpi/ic_account_child.png
index 866ee8db..87dae76a 100644
--- a/chrome/android/java/res/drawable-hdpi/ic_account_child.png
+++ b/chrome/android/java/res/drawable-hdpi/ic_account_child.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/help_outline.png b/chrome/android/java/res/drawable-mdpi/help_outline.png
index 23fac8b..ea1ffde1 100644
--- a/chrome/android/java/res/drawable-mdpi/help_outline.png
+++ b/chrome/android/java/res/drawable-mdpi/help_outline.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_account_child.png b/chrome/android/java/res/drawable-mdpi/ic_account_child.png
index 8d7a0a7..a0462c0 100644
--- a/chrome/android/java/res/drawable-mdpi/ic_account_child.png
+++ b/chrome/android/java/res/drawable-mdpi/ic_account_child.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/help_outline.png b/chrome/android/java/res/drawable-xhdpi/help_outline.png
index da89e13..ff3c142 100644
--- a/chrome/android/java/res/drawable-xhdpi/help_outline.png
+++ b/chrome/android/java/res/drawable-xhdpi/help_outline.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_account_child.png b/chrome/android/java/res/drawable-xhdpi/ic_account_child.png
index 9a666cf..f2158c5 100644
--- a/chrome/android/java/res/drawable-xhdpi/ic_account_child.png
+++ b/chrome/android/java/res/drawable-xhdpi/ic_account_child.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/help_outline.png b/chrome/android/java/res/drawable-xxhdpi/help_outline.png
index 92481e2d..f5ce2c6f5 100644
--- a/chrome/android/java/res/drawable-xxhdpi/help_outline.png
+++ b/chrome/android/java/res/drawable-xxhdpi/help_outline.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_account_child.png b/chrome/android/java/res/drawable-xxhdpi/ic_account_child.png
index 3239b981..b645b07 100644
--- a/chrome/android/java/res/drawable-xxhdpi/ic_account_child.png
+++ b/chrome/android/java/res/drawable-xxhdpi/ic_account_child.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/help_outline.png b/chrome/android/java/res/drawable-xxxhdpi/help_outline.png
index 626321f0..67afef1 100644
--- a/chrome/android/java/res/drawable-xxxhdpi/help_outline.png
+++ b/chrome/android/java/res/drawable-xxxhdpi/help_outline.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_account_child.png b/chrome/android/java/res/drawable-xxxhdpi/ic_account_child.png
index 98f61adb6..f8c2090 100644
--- a/chrome/android/java/res/drawable-xxxhdpi/ic_account_child.png
+++ b/chrome/android/java/res/drawable-xxxhdpi/ic_account_child.png
Binary files differ
diff --git a/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml b/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml
index 068b3ef4..8d50ac3 100644
--- a/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml
+++ b/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml
@@ -10,26 +10,27 @@
     android:layout_height="wrap_content">
 
     <LinearLayout
-        android:id="@+id/main_contents"
+        android:id="@+id/instructions_container"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="16dp"
+        android:layout_alignStart="@+id/controls_container"
+        android:layout_alignEnd="@+id/controls_container"
         android:orientation="vertical">
 
         <TextView
             android:id="@+id/no_retry_error_message"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="23dp"
-            android:layout_marginBottom="2dp"
+            android:layout_marginTop="12dp"
+            android:layout_marginBottom="4dp"
             android:paddingStart="24dp"
             android:paddingEnd="24dp"
             android:paddingTop="12dp"
             android:paddingBottom="15dp"
             android:gravity="start"
             android:textSize="12sp"
-            android:textColor="@color/input_underline_error_color"
-            android:background="#e0e0e0"
+            android:textColor="@android:color/white"
+            android:background="@color/input_underline_error_color"
             android:visibility="gone" />
 
         <TextView
@@ -39,11 +40,20 @@
             android:layout_marginStart="24dp"
             android:layout_marginEnd="10dp"
             android:layout_marginBottom="8dp"
-            android:layout_marginTop="16dp"
-            android:textSize="16sp"
+            android:layout_marginTop="12dp"
+            android:textSize="12sp"
             android:textColor="@color/explanation_text_color"
             android:lineSpacingMultiplier="1.25"
             android:gravity="start" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/controls_container"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/instructions_container"
+        android:layout_marginBottom="16dp"
+        android:orientation="vertical">
 
         <LinearLayout
             android:layout_width="match_parent"
@@ -143,7 +153,7 @@
                 android:layout_marginStart="17dp"
                 android:layout_marginEnd="10dp"
                 android:paddingStart="7dp"
-                android:textSize="14sp"
+                android:textSize="12sp"
                 android:textColor="@color/explanation_text_color"
                 android:text="@string/autofill_card_unmask_prompt_storage_checkbox" />
 
@@ -154,20 +164,18 @@
         android:id="@+id/verification_overlay"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignTop="@+id/main_contents"
-        android:layout_alignBottom="@+id/main_contents"
-        android:layout_alignStart="@+id/main_contents"
-        android:layout_alignEnd="@+id/main_contents"
-        android:orientation="vertical"
+        android:layout_alignTop="@+id/controls_container"
+        android:layout_alignBottom="@+id/controls_container"
+        android:layout_alignStart="@+id/controls_container"
+        android:layout_alignEnd="@+id/controls_container"
+        android:orientation="horizontal"
         android:gravity="center"
         android:visibility="gone">
 
         <ProgressBar
             android:id="@+id/verification_progress_bar"
-            android:layout_width="36dp"
-            android:layout_height="36dp"
-            android:layout_marginBottom="8dp"
-            android:layout_marginTop="30dp"
+            android:layout_width="16dp"
+            android:layout_height="16dp"
             android:visibility="gone" />
 
         <!--  TODO(estade): add a real content description for this image. -->
@@ -183,7 +191,8 @@
             android:id="@+id/verification_message"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_marginStart="8dp"
             android:textColor="?attr/colorAccent"
-            android:textSize="20sp" />
+            android:textSize="12sp" />
     </LinearLayout>
 </RelativeLayout>
diff --git a/chrome/android/java/res/layout/data_reduction_promo_screen.xml b/chrome/android/java/res/layout/data_reduction_promo_screen.xml
index 4e2409c0..a4735d2 100644
--- a/chrome/android/java/res/layout/data_reduction_promo_screen.xml
+++ b/chrome/android/java/res/layout/data_reduction_promo_screen.xml
@@ -45,37 +45,36 @@
             android:layout_marginTop="35dp"
             android:layout_width="match_parent"
             android:orientation="vertical"
-            android:paddingStart="40dp"
-            android:paddingEnd="40dp">
+            android:paddingStart="20dp"
+            android:paddingEnd="20dp">
             <TextView
                 android:gravity="center"
                 android:layout_height="wrap_content"
                 android:layout_width="match_parent"
                 android:text="@string/data_reduction_title_1"
                 android:textColor="#000"
-                android:textSize="30sp" />
-            <TextView
+                android:textSize="28sp" />
+            <org.chromium.ui.widget.TextViewWithClickableSpans
+                android:id="@+id/data_reduction_title_2_link"
                 android:gravity="center"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="20dp"
                 android:layout_width="match_parent"
-                android:lineSpacingExtra="20dp"
+                android:lineSpacingExtra="20sp"
                 android:lineSpacingMultiplier="0"
-                android:paddingStart="20dp"
-                android:paddingEnd="20dp"
-                android:text="@string/data_reduction_title_2"
-                android:textColor="#969696"
+                android:textColor="#333"
+                android:textColorLink="#27b4e7"
                 android:textSize="16sp" />
             <TextView
                 android:gravity="center"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="20dp"
                 android:layout_width="match_parent"
-                android:lineSpacingExtra="25dp"
+                android:lineSpacingExtra="20sp"
                 android:lineSpacingMultiplier="0"
                 android:text="@string/data_reduction_title_3"
-                android:textColor="#000"
-                android:textSize="20sp" />
+                android:textColor="#333"
+                android:textSize="16sp" />
         </LinearLayout>
         <ImageView
             android:layout_alignParentBottom="true"
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml
index 36b54cd..0b652fe9 100644
--- a/chrome/android/java/res/values/colors.xml
+++ b/chrome/android/java/res/values/colors.xml
@@ -10,7 +10,7 @@
     <color name="light_normal_color">#5A5A5A</color>
     <color name="light_active_color">#4285F4</color>
     <color name="input_underline_color">#e5e5e5</color>
-    <color name="input_underline_error_color">#db4437</color>
+    <color name="input_underline_error_color">#d32f2f</color>
     <color name="explanation_text_color">#909090</color>
 
     <!-- Infobar colors -->
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromiumApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromiumApplication.java
index ce26fb2..53cad1c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromiumApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromiumApplication.java
@@ -5,15 +5,10 @@
 package org.chromium.chrome.browser;
 
 import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
-import android.support.v4.app.DialogFragment;
-import android.support.v4.app.FragmentActivity;
 import android.util.Log;
 
 import org.chromium.base.ActivityState;
@@ -21,14 +16,13 @@
 import org.chromium.base.CalledByNative;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.TraceEvent;
-import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.library_loader.LoaderErrors;
 import org.chromium.base.library_loader.ProcessInitException;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.child_accounts.ChildAccountService;
 import org.chromium.chrome.browser.firstrun.FirstRunActivity;
+import org.chromium.chrome.browser.init.InvalidStartupDialog;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
 import org.chromium.chrome.browser.preferences.LocationSettings;
@@ -215,51 +209,10 @@
      */
     public static void reportStartupErrorAndExit(final ProcessInitException e) {
         Activity activity = ApplicationStatus.getLastTrackedFocusedActivity();
-        if (ApplicationStatus.getStateForActivity(activity) == ActivityState.DESTROYED
-                || !(activity instanceof FragmentActivity)) {
+        if (ApplicationStatus.getStateForActivity(activity) == ActivityState.DESTROYED) {
             return;
         }
-        int errorCode = e.getErrorCode();
-        int msg;
-        switch (errorCode) {
-            case LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED:
-                msg = R.string.os_version_missing_features;
-                break;
-            case LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION:
-                msg = R.string.incompatible_libraries;
-                break;
-            default:
-                msg = R.string.native_startup_failed;
-        }
-        final String message = activity.getResources().getString(msg);
-
-        DialogFragment dialog = new DialogFragment() {
-            @Override
-            public Dialog onCreateDialog(Bundle savedInstanceState) {
-                AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
-                dialogBuilder
-                        .setMessage(message)
-                        .setCancelable(true)
-                        .setPositiveButton(getResources().getString(android.R.string.ok),
-                                new DialogInterface.OnClickListener() {
-                                    @SuppressFBWarnings("DM_EXIT")
-                                    @Override
-                                    public void onClick(DialogInterface dialog, int which) {
-                                        System.exit(-1);
-                                    }
-                                })
-                        .setOnCancelListener(new DialogInterface.OnCancelListener() {
-                            @SuppressFBWarnings("DM_EXIT")
-                            @Override
-                            public void onCancel(DialogInterface dialog) {
-                                System.exit(-1);
-                            }
-                        });
-                return dialogBuilder.create();
-            }
-        };
-        dialog.show(
-                ((FragmentActivity) activity).getSupportFragmentManager(), "InvalidStartupDialog");
+        InvalidStartupDialog.show(activity, e.getErrorCode());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/Tab.java
index 210119e..ce8bf91a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/Tab.java
@@ -27,6 +27,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.TabState.WebContentsState;
+import org.chromium.chrome.browser.TabUma.TabCreationState;
 import org.chromium.chrome.browser.banners.AppBannerManager;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.contextmenu.ChromeContextMenuItemDelegate;
@@ -46,6 +47,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
 import org.chromium.chrome.browser.tabmodel.TabModelBase;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.ToolbarModel;
 import org.chromium.chrome.browser.ui.toolbar.ToolbarModelSecurityLevel;
 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
@@ -297,6 +299,12 @@
     private int mFullscreenHungRendererToken = FullscreenManager.INVALID_TOKEN;
 
     /**
+     * The UMA object used to report stats for this tab. Note that this may be null under certain
+     * conditions, such as incognito mode.
+     */
+    private TabUma mTabUma;
+
+    /**
      * Reference to the current sadTabView if one is defined.
      */
     private View mSadTabView;
@@ -727,6 +735,14 @@
     }
 
     /**
+     * Sets the mTabUma object for stats reporting.
+     * @param tabUma TabUma object to use to report UMA stats.
+     */
+    protected void setTabUma(TabUma tabUma) {
+        mTabUma = tabUma;
+    }
+
+    /**
      * Restores member fields from the given TabState.
      * @param state TabState containing information about this Tab.
      */
@@ -1181,13 +1197,6 @@
     }
 
     /**
-     * Called on the foreground tab when the Activity is stopped.
-     */
-    public void onActivityStop() {
-        hide();
-    }
-
-    /**
      * Prepares the tab to be shown. This method is supposed to be called before the tab is
      * displayed. It restores the ContentView if it is not available after the cold start and
      * reloads the tab if its renderer has crashed.
@@ -1206,6 +1215,8 @@
 
             if (mContentViewCore != null) mContentViewCore.onShow();
 
+            if (mTabUma != null) mTabUma.onShow(type, getTimestampMillis());
+
             showInternal(type);
 
             // If the page is still loading, update the progress bar (otherwise it would not show
@@ -1387,25 +1398,36 @@
     }
 
     /**
-     * Adds a {@link ContentViewCore} to this {@link Tab} as an overlay object.  This
-     * {@link ContentViewCore} will be attached to the CC layer hierarchy and have all layout events
-     * propagated to it.  This {@link ContentViewCore} can be removed via
+     * Adds a {@link ContentViewCore} to this {@link Tab} as an overlay object.
+     * If attachLayer is set, the {@link ContentViewCore} will be attached to CC layer hierarchy.
+     * This {@link ContentViewCore} will have all layout events propagated to it.
+     * This {@link ContentViewCore} can be removed via
      * {@link #detachOverlayContentViewCore(ContentViewCore)}.
      * @param content The {@link ContentViewCore} to attach.
      * @param visible Whether or not to make the content visible.
+     * @param attachLayer Whether or not to attach the content view to the CC layer hierarchy.
      */
-    public void attachOverlayContentViewCore(ContentViewCore content, boolean visible) {
+    public void attachOverlayContentViewCore(
+            ContentViewCore content, boolean visible, boolean attachLayer) {
         if (content == null) return;
 
         assert !mOverlayContentViewCores.contains(content);
         mOverlayContentViewCores.add(content);
-        nativeAttachOverlayContentViewCore(mNativeTabAndroid, content, visible);
+        if (attachLayer) nativeAttachOverlayContentViewCore(mNativeTabAndroid, content, visible);
         for (TabObserver observer : mObservers) {
             observer.onOverlayContentViewCoreAdded(this, content);
         }
     }
 
     /**
+     * TODO(aruslan): remove this.
+     * Temporary overload to avoid 2-way commit.
+     */
+    public void attachOverlayContentViewCore(ContentViewCore content, boolean visible) {
+        attachOverlayContentViewCore(content, visible, true);
+    }
+
+    /**
      * Removes a {@link ContentViewCore} overlay object from this {@link Tab}.  This
      * {@link ContentViewCore} must have previously been added via
      * {@link #attachOverlayContentViewCore(ContentViewCore, boolean)}.
@@ -1459,6 +1481,8 @@
                     "Navigation.IsMobileOptimized", mContentViewCore.getIsMobileOptimizedHint());
         }
 
+        if (mTabUma != null) mTabUma.onLoadFinished();
+
         for (TabObserver observer : mObservers) observer.onPageLoadFinished(this);
     }
 
@@ -1469,6 +1493,7 @@
     protected void didFailPageLoad(int errorCode) {
         mIsLoading = false;
         mIsBeingRestored = false;
+        if (mTabUma != null) mTabUma.onLoadFailed(errorCode);
         for (TabObserver observer : mObservers) observer.onPageLoadFailed(this, errorCode);
     }
 
@@ -1838,6 +1863,7 @@
      */
     protected void restoreIfNeededInternal() {
         mIsBeingRestored = true;
+        if (mTabUma != null) mTabUma.onRestoreStarted();
     }
 
     /**
@@ -2395,6 +2421,7 @@
      * Performs any subclass-specific tasks when the Tab crashes.
      */
     protected void handleTabCrash() {
+        if (mTabUma != null) mTabUma.onRendererCrashed();
     }
 
     /**
@@ -2634,6 +2661,50 @@
         sIdCounter.addAndGet(diff);
     }
 
+    /**
+     * @return the UMA object for the tab. Note that this may be null in some
+     * cases.
+     */
+    public TabUma getTabUma() {
+        return mTabUma;
+    }
+
+    /**
+     * Creates a new tab to be loaded lazily. This can be used for tabs opened in the background
+     * that should be loaded when switched to. initialize() needs to be called afterwards to
+     * complete the second level initialization.
+     */
+    @VisibleForTesting
+    static Tab createTabForLazyLoad(Context context, boolean incognito, WindowAndroid nativeWindow,
+            TabLaunchType type, int parentId, LoadUrlParams loadUrlParams,
+            TabModelSelector tabModelSelector) {
+        Tab tab = new Tab(INVALID_TAB_ID, parentId, incognito, context, nativeWindow, type, null);
+        if (context != null) {
+            tab.setTabUma(new TabUma(tab, TabCreationState.FROZEN_FOR_LAZY_LOAD,
+                    tabModelSelector.getModel(incognito)));
+        }
+        tab.setPendingLoadParams(loadUrlParams);
+        return tab;
+    }
+
+    /**
+     * Creates a fresh tab. initialize() needs to be called afterwards to complete the second level
+     * initialization.
+     * @param initiallyHidden true iff the tab being created is initially in background
+     */
+    @VisibleForTesting
+    static Tab createLiveTab(int id, Context context, boolean incognito, WindowAndroid nativeWindow,
+            TabLaunchType type, int parentId, boolean initiallyHidden,
+            TabModelSelector tabModelSelector) {
+        Tab tab = new Tab(id, parentId, incognito, context, nativeWindow, type, null);
+        if (context != null) {
+            tab.setTabUma(new TabUma(tab, initiallyHidden ? TabCreationState.LIVE_IN_BACKGROUND
+                                                          : TabCreationState.LIVE_IN_FOREGROUND,
+                    tabModelSelector.getModel(incognito)));
+        }
+        return tab;
+    }
+
     private native void nativeInit();
     private native void nativeDestroy(long nativeTabAndroid);
     private native void nativeInitWebContents(long nativeTabAndroid, boolean incognito,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/TabUma.java b/chrome/android/java/src/org/chromium/chrome/browser/TabUma.java
new file mode 100644
index 0000000..0f4db83
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/TabUma.java
@@ -0,0 +1,257 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser;
+
+import android.os.SystemClock;
+
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
+import org.chromium.net.NetError;
+
+/**
+ * Centralizes UMA data collection for Tab management.
+ * This will drive our memory optimization efforts, specially tab restoring and
+ * eviction.
+ * All calls must be made from the UI thread.
+ */
+public class TabUma {
+    // TabStatus defined in tools/metrics/histograms/histograms.xml.
+    static final int TAB_STATUS_MEMORY_RESIDENT = 0;
+    static final int TAB_STATUS_RELOAD_EVICTED = 1;
+    static final int TAB_STATUS_RELOAD_COLD_START_FG = 6;
+    static final int TAB_STATUS_RELOAD_COLD_START_BG = 7;
+    static final int TAB_STATUS_LAZY_LOAD_FOR_BG_TAB = 8;
+    static final int TAB_STATUS_LIM = 9;
+
+    // TabBackgroundLoadStatus defined in tools/metrics/histograms/histograms.xml.
+    static final int TAB_BACKGROUND_LOAD_SHOWN = 0;
+    static final int TAB_BACKGROUND_LOAD_LOST = 1;
+    static final int TAB_BACKGROUND_LOAD_SKIPPED = 2;
+    static final int TAB_BACKGROUND_LOAD_LIM = 3;
+
+    // The enum values for the Tab.RestoreResult histogram. The unusual order is to
+    // keep compatibility with the previous instance of the histogram that was using
+    // a boolean.
+    //
+    // Defined in tools/metrics/histograms/histograms.xml.
+    private static final int TAB_RESTORE_RESULT_FAILURE_OTHER = 0;
+    private static final int TAB_RESTORE_RESULT_SUCCESS = 1;
+    private static final int TAB_RESTORE_RESULT_FAILURE_NETWORK_CONNECTIVITY = 2;
+    private static final int TAB_RESTORE_RESULT_COUNT = 3;
+
+    // Counter of tab shows (as per onShow()) for all tabs.
+    private static long sAllTabsShowCount = 0;
+
+    private final Tab mTab;
+    private final TabModel mTabModel;
+
+    /**
+     * State in which the tab was created. This can be used in metric accounting - e.g. to
+     * distinguish reasons for a tab to be restored upon first display.
+     */
+    public enum TabCreationState {
+        LIVE_IN_FOREGROUND,
+        LIVE_IN_BACKGROUND,
+        FROZEN_ON_RESTORE,
+        FROZEN_FOR_LAZY_LOAD
+    }
+
+    private final TabCreationState mTabCreationState;
+
+    // Timestamp when this tab was last shown.
+    private long mLastShowMillis = -1;
+
+    // Timestamp of the beginning of the current tab restore.
+    private long mRestoreStartedAtMillis = -1;
+
+    /**
+     * Constructs a new UMA tracker for a specific tab.
+     * @param tab The tab whose metrics we want to track.
+     * @param creationState In what state the tab was created.
+     * @param model The tab model that contains this tab.
+     */
+    public TabUma(Tab tab, TabCreationState creationState, TabModel model) {
+        mTab = tab;
+        mTabCreationState = creationState;
+        mTabModel = model;
+    }
+
+    /**
+     * Records the tab restore result into several UMA histograms.
+     * @param succeeded Whether or not the tab restore succeeded.
+     * @param time The time taken to perform the tab restore.
+     * @param perceivedTime The perceived time taken to perform the tab restore.
+     * @param errorCode The error code, on failure (as denoted by the |succeeded| parameter).
+     */
+    void recordTabRestoreResult(boolean succeeded, long time, long perceivedTime, int errorCode) {
+        if (succeeded) {
+            RecordHistogram.recordEnumeratedHistogram(
+                    "Tab.RestoreResult", TAB_RESTORE_RESULT_SUCCESS, TAB_RESTORE_RESULT_COUNT);
+            RecordHistogram.recordCountHistogram("Tab.RestoreTime", (int) time);
+            RecordHistogram.recordCountHistogram("Tab.PerceivedRestoreTime", (int) perceivedTime);
+        } else {
+            switch (errorCode) {
+                case NetError.ERR_INTERNET_DISCONNECTED:
+                case NetError.ERR_NAME_RESOLUTION_FAILED:
+                case NetError.ERR_DNS_TIMED_OUT:
+                    RecordHistogram.recordEnumeratedHistogram("Tab.RestoreResult",
+                            TAB_RESTORE_RESULT_FAILURE_NETWORK_CONNECTIVITY,
+                            TAB_RESTORE_RESULT_COUNT);
+                    break;
+                default:
+                    RecordHistogram.recordEnumeratedHistogram("Tab.RestoreResult",
+                            TAB_RESTORE_RESULT_FAILURE_OTHER, TAB_RESTORE_RESULT_COUNT);
+            }
+        }
+    }
+
+    /**
+     * Called upon tab display.
+     * @param selectionType determines how the tab was being shown
+     * @param previousTimestampMillis time of the previous display or creation time for the tabs
+     *                                opened in background and not yet displayed
+     */
+    void onShow(TabSelectionType selectionType, long previousTimestampMillis) {
+        long now = SystemClock.elapsedRealtime();
+        // Do not collect the tab switching data for the first switch to a tab after the cold start
+        // and for the tab switches that were not user-originated (e.g. the user closes the last
+        // incognito tab and the current normal mode tab is shown).
+        if (mLastShowMillis != -1 && selectionType == TabSelectionType.FROM_USER) {
+            long age = now - mLastShowMillis;
+            int rank = computeMRURank(mTab, mTabModel);
+            RecordHistogram.recordCountHistogram("Tab.SwitchedToForegroundAge", (int) age);
+            RecordHistogram.recordCountHistogram("Tab.SwitchedToForegroundMRURank", rank);
+        }
+
+        increaseTabShowCount();
+        boolean isOnBrowserStartup = sAllTabsShowCount == 1;
+        boolean performsLazyLoad =
+                mTabCreationState == TabCreationState.FROZEN_FOR_LAZY_LOAD && mLastShowMillis == -1;
+
+        int status;
+        if (mRestoreStartedAtMillis == -1 && !performsLazyLoad) {
+            // The tab is *not* being restored or loaded lazily on first display.
+            status = TAB_STATUS_MEMORY_RESIDENT;
+        } else if (mLastShowMillis == -1) {
+            // This is first display and the tab is being restored or loaded lazily.
+            if (isOnBrowserStartup) {
+                status = TAB_STATUS_RELOAD_COLD_START_FG;
+            } else if (mTabCreationState == TabCreationState.FROZEN_ON_RESTORE) {
+                status = TAB_STATUS_RELOAD_COLD_START_BG;
+            } else if (mTabCreationState == TabCreationState.FROZEN_FOR_LAZY_LOAD) {
+                status = TAB_STATUS_LAZY_LOAD_FOR_BG_TAB;
+            } else {
+                assert mTabCreationState == TabCreationState.LIVE_IN_FOREGROUND
+                        || mTabCreationState == TabCreationState.LIVE_IN_BACKGROUND;
+                status = TAB_STATUS_RELOAD_EVICTED;
+            }
+        } else {
+            // The tab is being restored and this is *not* the first time the tab is shown.
+            status = TAB_STATUS_RELOAD_EVICTED;
+        }
+
+        // Record only user-visible switches to existing tabs. Do not record displays of newly
+        // created tabs (FROM_NEW) or selections of the previous tab that happen when we close the
+        // tab opened from intent while exiting Chrome (FROM_CLOSE).
+        if (selectionType == TabSelectionType.FROM_USER) {
+            RecordHistogram.recordEnumeratedHistogram(
+                    "Tab.StatusWhenSwitchedBackToForeground", status, TAB_STATUS_LIM);
+        }
+
+        // Record Tab.BackgroundLoadStatus.
+        if (mLastShowMillis == -1) {
+            if (mTabCreationState == TabCreationState.LIVE_IN_BACKGROUND) {
+                if (mRestoreStartedAtMillis == -1) {
+                    RecordHistogram.recordEnumeratedHistogram("Tab.BackgroundLoadStatus",
+                            TAB_BACKGROUND_LOAD_SHOWN, TAB_BACKGROUND_LOAD_LIM);
+                } else {
+                    RecordHistogram.recordEnumeratedHistogram("Tab.BackgroundLoadStatus",
+                            TAB_BACKGROUND_LOAD_LOST, TAB_BACKGROUND_LOAD_LIM);
+                }
+            } else if (mTabCreationState == TabCreationState.FROZEN_FOR_LAZY_LOAD) {
+                assert mRestoreStartedAtMillis == -1;
+                RecordHistogram.recordEnumeratedHistogram("Tab.BackgroundLoadStatus",
+                        TAB_BACKGROUND_LOAD_SKIPPED, TAB_BACKGROUND_LOAD_LIM);
+            }
+        }
+
+        // Record "tab age upon first display" metrics. previousTimestampMillis is persisted through
+        // cold starts.
+        if (mLastShowMillis == -1 && previousTimestampMillis > 0) {
+            if (isOnBrowserStartup) {
+                RecordHistogram.recordCountHistogram("Tabs.ForegroundTabAgeAtStartup",
+                        (int) millisecondsToMinutes(System.currentTimeMillis()
+                                                             - previousTimestampMillis));
+            } else if (selectionType == TabSelectionType.FROM_USER) {
+                RecordHistogram.recordCountHistogram("Tab.AgeUponRestoreFromColdStart",
+                        (int) millisecondsToMinutes(System.currentTimeMillis()
+                                                             - previousTimestampMillis));
+            }
+        }
+
+        mLastShowMillis = now;
+    }
+
+    /** Called when restore of the corresponding tab is triggered. */
+    void onRestoreStarted() {
+        mRestoreStartedAtMillis = SystemClock.elapsedRealtime();
+    }
+
+    /** Called when the correspoding tab completes a page load. */
+    void onLoadFinished() {
+        // Record only tab restores that the user became aware of. If the restore is triggered
+        // speculatively and completes before the user switches to the tab, then this case is
+        // reflected in Tab.StatusWhenSwitchedBackToForeground metric.
+        if (mRestoreStartedAtMillis != -1 && mLastShowMillis >= mRestoreStartedAtMillis) {
+            long now = SystemClock.elapsedRealtime();
+            long restoreTime = now - mRestoreStartedAtMillis;
+            long perceivedRestoreTime = now - mLastShowMillis;
+            recordTabRestoreResult(true, restoreTime, perceivedRestoreTime, -1);
+        }
+        mRestoreStartedAtMillis = -1;
+    }
+
+    /** Called when the correspoding tab fails a page load. */
+    void onLoadFailed(int errorCode) {
+        if (mRestoreStartedAtMillis != -1 && mLastShowMillis >= mRestoreStartedAtMillis) {
+            // Load time is ignored for failed loads.
+            recordTabRestoreResult(false, -1, -1, errorCode);
+        }
+        mRestoreStartedAtMillis = -1;
+    }
+
+    /** Called when the renderer of the correspoding tab crashes. */
+    void onRendererCrashed() {
+        if (mRestoreStartedAtMillis != -1) {
+            // TODO(ppi): Add a bucket in Tab.RestoreResult for restores failed due to
+            //            renderer crashes and start to track that.
+            mRestoreStartedAtMillis = -1;
+        }
+    }
+
+    private static void increaseTabShowCount() {
+        sAllTabsShowCount++;
+    }
+
+    private static long millisecondsToMinutes(long msec) {
+        return msec / 1000 / 60;
+    }
+
+    /**
+     * @return The most recently used rank for this tab in [0 .. tabs.length - 1].
+     */
+    private static int computeMRURank(Tab tab, TabModel model) {
+        final long tabLastShow = tab.getTabUma().mLastShowMillis;
+        int mruRank = 0;
+        for (int i = 0; i < model.getCount(); i++) {
+            Tab otherTab = model.getTabAt(i);
+            if (otherTab != tab && otherTab.getTabUma().mLastShowMillis > tabLastShow) {
+                mruRank++;
+            }
+        }
+        return mruRank;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
index 6b6d07a..8f456d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
@@ -9,6 +9,7 @@
 import android.content.DialogInterface;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
 import android.text.Layout;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
@@ -164,6 +165,10 @@
 
     private static final int MAX_TABLET_DIALOG_WIDTH_DP = 400;
 
+    private static final char FIRST_UNICODE_WHITESPACE = '\u2000';
+    private static final char FINAL_UNICODE_WHITESPACE = '\u200F';
+    private static final char UNICODE_NBSP = '\u00A0';
+
     private final Context mContext;
     private final Profile mProfile;
     private final WebContents mWebContents;
@@ -289,7 +294,8 @@
         }
         mSecurityLevel = ToolbarModel.getSecurityLevelForWebContents(mWebContents);
 
-        SpannableStringBuilder urlBuilder = new SpannableStringBuilder(mFullUrl);
+        String displayUrl = prepareUrlForDisplay(mFullUrl);
+        SpannableStringBuilder urlBuilder = new SpannableStringBuilder(displayUrl);
         OmniboxUrlEmphasizer.emphasizeUrl(urlBuilder, mContext.getResources(), mProfile,
                 mSecurityLevel, mIsInternalPage, true);
         mUrlTitle.setText(urlBuilder);
@@ -300,6 +306,26 @@
     }
 
     /**
+     * Percent-encodes suspicious Unicode whitespace characters in a URL so that it can be safely
+     * displayed.
+     */
+    public static String prepareUrlForDisplay(String urlStr) {
+        StringBuilder urlBuilder = new StringBuilder();
+        for (int i = 0; i < urlStr.length(); i++) {
+            char c = urlStr.charAt(i);
+            if ((c >= FIRST_UNICODE_WHITESPACE
+                        && c <= FINAL_UNICODE_WHITESPACE)
+                    || c == ' '
+                    || c == UNICODE_NBSP) {
+                urlBuilder.append(Uri.encode(Character.toString(c)));
+            } else {
+                urlBuilder.append(c);
+            }
+        }
+        return urlBuilder.toString();
+    }
+
+    /**
      * Sets the visibility of the lower area of the dialog (containing the permissions and 'Site
      * Settings' button).
      *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index b42b6c1..9f27efac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -59,7 +59,7 @@
     private final CheckBox mStoreLocallyCheckbox;
     private final ImageView mStoreLocallyTooltipIcon;
     private PopupWindow mStoreLocallyTooltipPopup;
-    private final ViewGroup mMainContents;
+    private final ViewGroup mControlsContainer;
     private final View mVerificationOverlay;
     private final ProgressBar mVerificationProgressBar;
     private final TextView mVerificationView;
@@ -109,7 +109,7 @@
         mStoreLocallyCheckbox.setChecked(defaultToStoringLocally);
         mStoreLocallyTooltipIcon = (ImageView) v.findViewById(R.id.store_locally_tooltip_icon);
         mStoreLocallyTooltipIcon.setOnLongClickListener(this);
-        mMainContents = (ViewGroup) v.findViewById(R.id.main_contents);
+        mControlsContainer = (ViewGroup) v.findViewById(R.id.controls_container);
         mVerificationOverlay = v.findViewById(R.id.verification_overlay);
         mVerificationProgressBar = (ProgressBar) v.findViewById(R.id.verification_progress_bar);
         mVerificationView = (TextView) v.findViewById(R.id.verification_message);
@@ -295,12 +295,18 @@
      */
     private void setOverlayVisibility(int visibility) {
         mVerificationOverlay.setVisibility(visibility);
+        mControlsContainer.setAlpha(1f);
         boolean contentsShowing = visibility == View.GONE;
-        mMainContents.setAlpha(contentsShowing ? 1.0f : 0.15f);
-        ViewCompat.setImportantForAccessibility(mMainContents,
+        if (!contentsShowing) {
+            int durationMs = 250;
+            mVerificationOverlay.setAlpha(0f);
+            mVerificationOverlay.animate().alpha(1f).setDuration(durationMs);
+            mControlsContainer.animate().alpha(0f).setDuration(durationMs);
+        }
+        ViewCompat.setImportantForAccessibility(mControlsContainer,
                 contentsShowing ? View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
                                 : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-        mMainContents.setDescendantFocusability(
+        mControlsContainer.setDescendantFocusability(
                 contentsShowing ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
                                 : ViewGroup.FOCUS_BLOCK_DESCENDANTS);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
index 344496951..66a6fae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
@@ -270,7 +270,12 @@
             }
         };
         contentView.addOnLayoutChangeListener(mFullscreenOnLayoutChangeListener);
-        contentViewCore.getWebContents().exitFullscreen();
+
+        // getWebContents() will return null if contentViewCore has been destroyed
+        if (contentViewCore.getWebContents() != null) {
+            contentViewCore.getWebContents().exitFullscreen();
+        }
+
         if (mFullscreenInfoBarDelegate != null) {
             mFullscreenInfoBarDelegate.closeFullscreenInfoBar();
             mFullscreenInfoBarDelegate = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AccountChooserInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AccountChooserInfoBar.java
index 0f35999e..801c858b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AccountChooserInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AccountChooserInfoBar.java
@@ -98,10 +98,13 @@
                 }
                 ImageView avatarView = (ImageView) convertView.findViewById(R.id.profile_image);
                 TextView usernameView = (TextView) convertView.findViewById(R.id.username);
-                TextView displayNameView = (TextView) convertView.findViewById(R.id.display_name);
+                TextView smallTextView = (TextView) convertView.findViewById(R.id.display_name);
                 Credential credential = getItem(position);
                 usernameView.setText(credential.getUsername());
-                displayNameView.setText(credential.getDisplayName());
+                String smallText = credential.getFederation().isEmpty()
+                        ? credential.getFederation()
+                        : credential.getDisplayName();
+                smallTextView.setText(smallText);
                 // TODO(melandory): View should show proper avatar. Temporarily the view shows
                 // blue man icon.
                 avatarView.setImageResource(R.drawable.account_management_no_picture);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
index 35d35bb3..adb090b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -16,6 +16,9 @@
 import android.view.ViewTreeObserver.OnPreDrawListener;
 
 import org.chromium.base.TraceEvent;
+import org.chromium.base.library_loader.LoaderErrors;
+import org.chromium.base.library_loader.ProcessInitException;
+import org.chromium.chrome.browser.ChromiumApplication;
 import org.chromium.chrome.browser.WarmupManager;
 import org.chromium.chrome.browser.metrics.MemoryUma;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -96,7 +99,9 @@
 
     @Override
     public void onStartupFailure() {
-        finish();
+        ProcessInitException e =
+                new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_STARTUP_FAILED);
+        ChromiumApplication.reportStartupErrorAndExit(e);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/InvalidStartupDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/init/InvalidStartupDialog.java
new file mode 100644
index 0000000..f5de50d5d
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/InvalidStartupDialog.java
@@ -0,0 +1,85 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.init;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
+import android.util.Log;
+
+import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.base.library_loader.LoaderErrors;
+import org.chromium.chrome.R;
+
+/**
+ * Dialog shown when startup fails.
+ * <br>
+ * Fragments are required to be public with a public empty constructor, hence the visibility.
+ */
+public class InvalidStartupDialog extends DialogFragment {
+    private static final String TAG = "InvalidStartupDialog";
+
+    private static final String MESSAGE_KEY = "InvalidStartupErrorKey";
+
+    /**
+     * Shows the invalid startup dialog for a given error code.
+     *
+     * @param activity The activity showing the dialog.
+     * @param errorCode The error code that triggered the failure.
+     */
+    @SuppressFBWarnings("DM_EXIT")
+    public static void show(Activity activity, int errorCode) {
+        int msg;
+        switch (errorCode) {
+            case LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED:
+                msg = R.string.os_version_missing_features;
+                break;
+            case LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION:
+                msg = R.string.incompatible_libraries;
+                break;
+            default:
+                msg = R.string.native_startup_failed;
+        }
+        final String message = activity.getResources().getString(msg);
+
+        if (!(activity instanceof FragmentActivity)) {
+            Log.e(TAG, "Unable to start chrome due to: " + msg);
+            System.exit(-1);
+            return;
+        }
+
+        Bundle dialogArgs = new Bundle();
+        dialogArgs.putString(MESSAGE_KEY, message);
+
+        InvalidStartupDialog dialog = new InvalidStartupDialog();
+        dialog.setArguments(dialogArgs);
+        dialog.show(((FragmentActivity) activity).getSupportFragmentManager(),
+                "InvalidStartupDialog");
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        Bundle arguments = getArguments();
+        String message = arguments.getString(MESSAGE_KEY, "Failed to start");
+
+        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
+        dialogBuilder
+                .setMessage(message)
+                .setCancelable(true)
+                .setPositiveButton(getResources().getString(android.R.string.ok), null);
+        return dialogBuilder.create();
+    }
+
+    @SuppressFBWarnings("DM_EXIT")
+    @Override
+    public void onDismiss(DialogInterface dialog) {
+        super.onDismiss(dialog);
+        System.exit(-1);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/Credential.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/Credential.java
index d694f0c..823a6c4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/Credential.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/Credential.java
@@ -13,6 +13,7 @@
 public class Credential {
     private final String mUsername;
     private final String mDisplayName;
+    private final String mFederation;
     private final int mType;
     private final int mIndex;
 
@@ -21,13 +22,15 @@
      *                 The value is PasswordForm::username_value.
      * @param displayName user friendly name to show in the UI. It can be empty.
      *                    The value is PasswordForm::display_name.
+     * @param federation Identity provider name for this credential (empty for local credentials).
      * @param type type which should be either local or federated. The value corresponds to a
      *             C++ enum CredentialType.
      * @param index position in array of credentials.
      */
-    public Credential(String username, String displayName, int type, int index) {
+    public Credential(String username, String displayName, String federation, int type, int index) {
         mUsername = username;
         mDisplayName = displayName;
+        mFederation = federation;
         mType = type;
         mIndex = index;
     }
@@ -40,6 +43,10 @@
         return mDisplayName;
     }
 
+    public String getFederation() {
+        return mFederation;
+    }
+
     public int getIndex() {
         return mIndex;
     }
@@ -50,8 +57,8 @@
 
     @CalledByNative
     private static Credential createCredential(
-            String username, String displayName, int type, int index) {
-        return new Credential(username, displayName, type, index);
+            String username, String displayName, String federation, int type, int index) {
+        return new Credential(username, displayName, federation, type, index);
     }
 
     @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/bandwidth/DataReductionPromoScreen.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/bandwidth/DataReductionPromoScreen.java
index a3b3e753..210f76b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/bandwidth/DataReductionPromoScreen.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/bandwidth/DataReductionPromoScreen.java
@@ -11,10 +11,15 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
+import android.text.TextPaint;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.LinearLayout;
 import android.widget.LinearLayout.LayoutParams;
+import android.widget.TextView;
+import android.widget.Toast;
 
 import org.chromium.base.ApplicationStatus;
 import org.chromium.chrome.R;
@@ -22,6 +27,8 @@
 import org.chromium.chrome.browser.metrics.UmaBridge;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
+import org.chromium.ui.text.SpanApplier;
+import org.chromium.ui.text.SpanApplier.SpanInfo;
 
 /**
  * The promo screen encouraging users to enable Data Saver.
@@ -87,6 +94,7 @@
         getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
 
         addListenerOnButton();
+        addClickableSpan();
 
         mState = ACTION_DISMISSED_FIRST_SCREEN;
         UmaBridge.dataReductionProxyPromoDisplayed();
@@ -104,6 +112,32 @@
         }
     }
 
+    private void addClickableSpan() {
+        TextView settings_link = (TextView) findViewById(R.id.data_reduction_title_2_link);
+        ClickableSpan clickableSettingsSpan = new ClickableSpan() {
+            @Override
+            public void onClick(View view) {
+                Context context = getContext();
+                if (context == null) return;
+                Intent intent = PreferencesLauncher.createIntentForSettingsPage(
+                        context, BandwidthReductionPreferences.class.getName());
+                intent.putExtra(FROM_PROMO, true);
+                context.startActivity(intent);
+                dismiss();
+            }
+
+            @Override
+            public void updateDrawState(TextPaint textPaint) {
+                textPaint.setColor(textPaint.linkColor);
+                textPaint.setUnderlineText(false);
+            }
+        };
+        settings_link.setMovementMethod(LinkMovementMethod.getInstance());
+        settings_link.setText(SpanApplier.applySpans(
+                getContext().getString(R.string.data_reduction_title_2),
+                new SpanInfo("<link>", "</link>", clickableSettingsSpan)));
+    }
+
     @Override
     public void onClick(View v) {
         int id = v.getId();
@@ -126,13 +160,12 @@
     }
 
     private void handleEnableButtonPressed() {
-        Context context = getContext();
-        if (context == null) return;
-        Intent intent = PreferencesLauncher.createIntentForSettingsPage(context,
-                BandwidthReductionPreferences.class.getName());
-        intent.putExtra(FROM_PROMO, true);
-        context.startActivity(intent);
+        DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
+                getContext(), true);
+        UmaBridge.dataReductionProxyTurnedOnFromPromo();
         dismiss();
+        Toast.makeText(getContext(), getContext().getString(R.string.data_reduction_enabled_toast),
+                Toast.LENGTH_LONG).show();
     }
 
     private void handleCloseButtonPressed() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
index b643cde..4f7c870 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
@@ -349,6 +349,9 @@
         if ((modelTypeSelection & ModelTypeSelection.AUTOFILL_PROFILE) != 0) {
             syncTypes.add(ModelType.AUTOFILL_PROFILE);
         }
+        if ((modelTypeSelection & ModelTypeSelection.AUTOFILL_WALLET) != 0) {
+            syncTypes.add(ModelType.AUTOFILL_WALLET);
+        }
         if ((modelTypeSelection & ModelTypeSelection.BOOKMARK) != 0) {
             syncTypes.add(ModelType.BOOKMARK);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
index c4f816e..df6cb4bc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
@@ -200,16 +200,6 @@
     }
 
     /**
-     * Sets the SyncNotificationController.
-     *
-     * This is a temporary method for transferring ownership of SyncNotificationController
-     * upstream. SNC is now created in the constructor and this method can be removed once
-     * the downstream call from GoogleServicesManager is removed.
-     */
-    public void setSyncNotificationController(SyncNotificationController snc) {
-    }
-
-    /**
      * Returns the SyncNotificationController.
      */
     public SyncNotificationController getSyncNotificationController() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TransitionPageHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TransitionPageHelper.java
index 72f38668..4f124de8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TransitionPageHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TransitionPageHelper.java
@@ -612,7 +612,7 @@
 
         mTransitionObserver = createTransitionObserver();
 
-        if (mTab != null) mTab.attachOverlayContentViewCore(mTransitionContentViewCore, true);
+        if (mTab != null) mTab.attachOverlayContentViewCore(mTransitionContentViewCore, true, true);
 
         nativeSetWebContents(mNativeTransitionPageHelperPtr, mTransitionContentViewCore);
         setTransitionOpacity(0.0f);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModel.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModel.java
index 5fced42..7402ec8d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModel.java
@@ -195,8 +195,9 @@
     /**
      * Records the ID of the last shown Tab.
      * @param id ID of the last shown Tab.
+     * @return Whether or not the ID had to be updated.
      */
-    void setLastShownId(int id);
+    boolean setLastShownId(int id);
 
     /**
      * Called to begin loading tab state from disk. It will load the prioritized tab first
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelImpl.java
index 620147f..a0ab267 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelImpl.java
@@ -223,7 +223,10 @@
     }
 
     @Override
-    public void setLastShownId(int id) {
+    public boolean setLastShownId(int id) {
+        if (mLastShownTabId == id) return false;
+
+        int previousTabId = mLastShownTabId;
         mLastShownTabId = id;
 
         String prefName =
@@ -232,6 +235,15 @@
         SharedPreferences.Editor editor = prefs.edit();
         editor.putInt(prefName, id);
         editor.apply();
+
+        // TODO(dfalcantara): Figure out how to fire the correct type of TabSelectionType, which is
+        //                    quite hard to do in Document-mode from where we call this.
+        for (TabModelObserver obs : mObservers) {
+            obs.didSelectTab(
+                    TabModelUtils.getCurrentTab(this), TabSelectionType.FROM_USER, previousTabId);
+        }
+
+        return true;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/OffTheRecordDocumentTabModel.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/OffTheRecordDocumentTabModel.java
index 1e8944e..5c6fde54 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/OffTheRecordDocumentTabModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/OffTheRecordDocumentTabModel.java
@@ -142,9 +142,9 @@
     }
 
     @Override
-    public void setLastShownId(int id) {
+    public boolean setLastShownId(int id) {
         ensureTabModelImpl();
-        getDelegateDocumentTabModel().setLastShownId(id);
+        return getDelegateDocumentTabModel().setLastShownId(id);
     }
 
     @Override
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 90a62a98..9200661f 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -653,16 +653,19 @@
 
       <!-- Data Saver Promo -->
       <message name="IDS_DATA_REDUCTION_TITLE_1" desc="First part of the headline for the promo inviting users to enable Data Saver" >
-        You’re invited
+        Browse more for less
       </message>
       <message name="IDS_DATA_REDUCTION_TITLE_2" desc="Second part of the headline for the promo inviting users to enable Data Saver" >
-        Try out a new feature from Chrome
+        Spend less on data by using Google to <ph name="BEGIN_LINK">&lt;link&gt;</ph>optimize the pages you visit<ph name="END_LINK">&lt;/link&gt;</ph>.
       </message>
       <message name="IDS_DATA_REDUCTION_TITLE_3" desc="Third part of the headline for the promo inviting users to enable Data Saver" >
-        Save bandwidth and browse more securely
+        Pages accessed with HTTPS or Incognito will not be optimized or seen by Google.
       </message>
       <message name="IDS_DATA_REDUCTION_ENABLE_BUTTON" desc="Button the user presses if they want to enable Data Saver" >
-        Enable
+        Turn on Data Saver
+      </message>
+      <message name="IDS_DATA_REDUCTION_ENABLED_TOAST" desc="Toast message displayed after the user enables Data Saver from the promo" >
+        Data Saver is on. Manage it in Settings.
       </message>
 
       <!-- About Chrome preferences -->
@@ -1268,6 +1271,9 @@
       <message name="IDS_PAGE_INFO_CONNECTION_MIXED" desc="Message to display in the page info bubble when the main connection is private, but there is content on the page that is not private.">
         Your connection to this site is private, but someone on the network might be able to change the look of the page.
       </message>
+      <message name="IDS_PAGE_INFO_CONNECTION_SHA1" desc="Message to display in the page info bubble when the page is not considered private (because the connection used SHA-1 for certificate signatures, which are not considered secure anymore).">
+        This site uses a weak security configuration (SHA-1 signatures), so your connection may not be private.
+      </message>
       <message name="IDS_PAGE_INFO_CONNECTION_BROKEN_LEADING_TEXT" desc="Message to display highlighted red in the page info bubble when the connection is insecure because the HTTPS certificate is invalid.">
         Your connection to this site is not private.
       </message>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabUmaTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabUmaTest.java
new file mode 100644
index 0000000..4fc5973
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabUmaTest.java
@@ -0,0 +1,184 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser;
+
+import android.test.suitebuilder.annotation.MediumTest;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.MetricsUtils.HistogramDelta;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
+import org.chromium.chrome.shell.ChromeShellActivity;
+import org.chromium.chrome.shell.ChromeShellTestBase;
+import org.chromium.chrome.test.util.TestHttpServerClient;
+import org.chromium.content_public.browser.LoadUrlParams;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Tests for Tab-related histogram collection.
+ */
+public class TabUmaTest extends ChromeShellTestBase {
+    private static final String TEST_URL =
+            TestHttpServerClient.getUrl("chrome/test/data/android/about.html");
+
+    private ChromeShellActivity mActivity;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mActivity = launchChromeShellWithUrl(TEST_URL);
+        assertTrue(waitForActiveShellToBeDoneLoading());
+
+        // This is to fake a "tab show" event that should be happening
+        // on browser startup, but isn't currently. This code should
+        // eventually be taken out when this issue is fixed.
+        final Tab tab = ThreadUtils.runOnUiThreadBlocking(new Callable<Tab>() {
+            @Override
+            public Tab call() {
+                Tab bgTab = Tab.createTabForLazyLoad(mActivity, false, mActivity.getWindowAndroid(),
+                        TabLaunchType.FROM_LONGPRESS_BACKGROUND, Tab.INVALID_TAB_ID,
+                        new LoadUrlParams(TEST_URL), mActivity.getTabModelSelector());
+                bgTab.initialize(null, null, true);
+                return bgTab;
+            }
+        });
+
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                tab.show(TabSelectionType.FROM_NEW);
+            }
+        });
+    }
+
+    /**
+     * Verify that Tab.StatusWhenSwitchedBackToForeground is correctly recording lazy loads.
+     */
+    @MediumTest
+    @Feature({"Uma"})
+    public void testTabStatusWhenSwitchedToLazyLoads()
+            throws ExecutionException, InterruptedException {
+        final Tab tab = ThreadUtils.runOnUiThreadBlocking(new Callable<Tab>() {
+            @Override
+            public Tab call() {
+                Tab bgTab = Tab.createTabForLazyLoad(mActivity, false, mActivity.getWindowAndroid(),
+                        TabLaunchType.FROM_LONGPRESS_BACKGROUND, Tab.INVALID_TAB_ID,
+                        new LoadUrlParams(TEST_URL), mActivity.getTabModelSelector());
+                bgTab.initialize(null, null, true);
+                return bgTab;
+            }
+        });
+
+        String histogram = "Tab.StatusWhenSwitchedBackToForeground";
+        HistogramDelta lazyLoadCount =
+                new HistogramDelta(histogram, TabUma.TAB_STATUS_LAZY_LOAD_FOR_BG_TAB);
+        assertEquals(0, lazyLoadCount.getDelta()); // Sanity check.
+
+        // Show the tab and verify that one sample was recorded in the lazy load bucket.
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                tab.show(TabSelectionType.FROM_USER);
+            }
+        });
+        assertEquals(1, lazyLoadCount.getDelta());
+
+        // Show the tab again and verify that we didn't record another sample.
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                tab.show(TabSelectionType.FROM_USER);
+            }
+        });
+        assertEquals(1, lazyLoadCount.getDelta());
+    }
+
+    /**
+     * Verify that Tab.BackgroundLoadStatus is correctly recorded.
+     */
+    @MediumTest
+    @Feature({"Uma"})
+    public void testTabBackgroundLoadStatus() throws ExecutionException {
+        String histogram = "Tab.BackgroundLoadStatus";
+        HistogramDelta shownLoadCount =
+                new HistogramDelta(histogram, TabUma.TAB_BACKGROUND_LOAD_SHOWN);
+        HistogramDelta lostLoadCount =
+                new HistogramDelta(histogram, TabUma.TAB_BACKGROUND_LOAD_LOST);
+        HistogramDelta skippedLoadCount =
+                new HistogramDelta(histogram, TabUma.TAB_BACKGROUND_LOAD_SKIPPED);
+        assertEquals(0, shownLoadCount.getDelta());
+        assertEquals(0, lostLoadCount.getDelta());
+        assertEquals(0, skippedLoadCount.getDelta());
+
+        // Test a live tab created in background and shown.
+        final Tab liveBgTab = ThreadUtils.runOnUiThreadBlocking(new Callable<Tab>() {
+            @Override
+            public Tab call() {
+                Tab bgTab = Tab.createLiveTab(Tab.INVALID_TAB_ID, mActivity, false,
+                        mActivity.getWindowAndroid(), TabLaunchType.FROM_LONGPRESS_BACKGROUND,
+                        Tab.INVALID_TAB_ID, true, mActivity.getTabModelSelector());
+                bgTab.initialize(null, null, true);
+                bgTab.loadUrl(new LoadUrlParams(TEST_URL));
+                bgTab.show(TabSelectionType.FROM_USER);
+                return bgTab;
+            }
+        });
+        assertEquals(1, shownLoadCount.getDelta());
+        assertEquals(0, lostLoadCount.getDelta());
+        assertEquals(0, skippedLoadCount.getDelta());
+
+        // Test a live tab killed in background before shown.
+        final Tab killedBgTab = ThreadUtils.runOnUiThreadBlocking(new Callable<Tab>() {
+            @Override
+            public Tab call() {
+                Tab bgTab = Tab.createLiveTab(Tab.INVALID_TAB_ID, mActivity, false,
+                        mActivity.getWindowAndroid(), TabLaunchType.FROM_LONGPRESS_BACKGROUND,
+                        Tab.INVALID_TAB_ID, true, mActivity.getTabModelSelector());
+                bgTab.initialize(null, null, true);
+                bgTab.loadUrl(new LoadUrlParams(TEST_URL));
+                // Simulate the renderer being killed by the OS.
+                bgTab.simulateRendererKilledForTesting(false);
+                bgTab.show(TabSelectionType.FROM_USER);
+                return bgTab;
+            }
+        });
+        assertEquals(1, shownLoadCount.getDelta());
+        assertEquals(1, lostLoadCount.getDelta());
+        assertEquals(0, skippedLoadCount.getDelta());
+
+        // Test a tab created in background but not loaded eagerly.
+        final Tab frozenBgTab = ThreadUtils.runOnUiThreadBlocking(new Callable<Tab>() {
+            @Override
+            public Tab call() {
+                Tab bgTab = Tab.createTabForLazyLoad(mActivity, false, mActivity.getWindowAndroid(),
+                        TabLaunchType.FROM_LONGPRESS_BACKGROUND, Tab.INVALID_TAB_ID,
+                        new LoadUrlParams(TEST_URL), mActivity.getTabModelSelector());
+                bgTab.initialize(null, null, true);
+                bgTab.show(TabSelectionType.FROM_USER);
+                return bgTab;
+            }
+        });
+        assertEquals(1, shownLoadCount.getDelta());
+        assertEquals(1, lostLoadCount.getDelta());
+        assertEquals(1, skippedLoadCount.getDelta());
+
+        // Show every tab again and make sure we didn't record more samples - this metric should be
+        // recorded only on first display.
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                liveBgTab.show(TabSelectionType.FROM_USER);
+                killedBgTab.show(TabSelectionType.FROM_USER);
+                frozenBgTab.show(TabSelectionType.FROM_USER);
+            }
+        });
+        assertEquals(1, shownLoadCount.getDelta());
+        assertEquals(1, lostLoadCount.getDelta());
+        assertEquals(1, skippedLoadCount.getDelta());
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/WebsiteSettingsPopupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/WebsiteSettingsPopupTest.java
new file mode 100644
index 0000000..e1f605b
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/WebsiteSettingsPopupTest.java
@@ -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.
+
+package org.chromium.chrome.browser;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for WebsiteSettingsPopup
+ */
+public class WebsiteSettingsPopupTest extends TestCase {
+    @SmallTest
+    public void testPrepareUrlForDisplay() {
+        assertEquals("Encode suspicious message",
+                WebsiteSettingsPopup.prepareUrlForDisplay(
+                        "http://example.com/#  WARNING  \u00A0Chrome has detected malware on your"
+                        + " device!"),
+                "http://example.com/#%20%20WARNING%20%20%C2%A0Chrome%20has%20detected%20malware%20"
+                        + "on%20your%20device!");
+        assertEquals("Do not encode valid Unicode fragment",
+                WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#Düsseldorf"),
+                "http://example.com/#Düsseldorf");
+        assertEquals("Encode fragment with spaces",
+                WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#hi how are you"),
+                "http://example.com/#hi%20how%20are%20you");
+        assertEquals("Encode fragment with Unicode whitespace",
+                WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#em\u2003space"),
+                "http://example.com/#em%E2%80%83space");
+        assertEquals("Do not encode reserved URI characters or valid Unicode",
+                WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/?q=a#Düsseldorf,"
+                        + " Germany"),
+                "http://example.com/?q=a#Düsseldorf,%20Germany");
+        assertEquals("Preserve characters from supplementary Unicode planes",
+                WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#\uD835\uDC9Cstral"),
+                "http://example.com/#\uD835\uDC9Cstral");
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java
new file mode 100644
index 0000000..bb4dbef
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java
@@ -0,0 +1,375 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omaha;
+
+import android.util.Xml;
+
+import org.chromium.base.test.util.Feature;
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.robolectric.annotation.Config;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+/**
+ * Unit tests for the Omaha ResponseParser.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class ResponseParserTest {
+    // Note that the Omaha server appends "/" to the end of the URL codebase.
+    private static final String STRIPPED_URL =
+            "https://play.google.com/store/apps/details?id=com.google.android.apps.chrome";
+    private static final String URL = STRIPPED_URL + "/";
+    private static final String NEXT_VERSION = "1.2.3.4";
+
+    private static final String APP_STATUS_OK = "ok";
+    private static final String APP_STATUS_RESTRICTED = "restricted";
+    private static final String APP_STATUS_ERROR = "error-whatever-else";
+
+    private static final String UPDATE_STATUS_OK = "ok";
+    private static final String UPDATE_STATUS_NOUPDATE = "noupdate";
+    private static final String UPDATE_STATUS_ERROR = "error-osnotsupported";
+    private static final String UPDATE_STATUS_WTF = "omgwtfbbq";
+
+    /**
+     * Create XML for testing.
+     * @param xmlProtocol Version number of the protocol.  Expected to be "3.0" for valid XML.
+     * @param elapsedSeconds Number of seconds since server midnight.
+     * @param appStatus Status to use for the <app> element.
+     * @param addInstall Whether or not to add an install event.
+     * @param addPing Whether or not to add a ping event.
+     * @param updateStatus Status to use for the <updatecheck> element.
+     * @return The completed XML.
+     */
+    private static String createTestXML(String xmlProtocol, String elapsedSeconds,
+            String appStatus, boolean addInstall, boolean addPing, String updateStatus,
+            String updateUrl) {
+        StringWriter writer = new StringWriter();
+        try {
+            XmlSerializer serializer = Xml.newSerializer();
+            serializer.setOutput(writer);
+            serializer.startDocument("UTF-8", true);
+
+            // Set up <response ...>
+            serializer.startTag(null, "response");
+            serializer.attribute(null, "server", "prod");
+            if (xmlProtocol != null) {
+                serializer.attribute(null, "protocol", xmlProtocol);
+            }
+
+            // Create <daystart> element.
+            if (elapsedSeconds != null) {
+                serializer.startTag(null, "daystart");
+                serializer.attribute(null, "elapsed_seconds", elapsedSeconds);
+                serializer.endTag(null, "daystart");
+            }
+
+            // Create <app> element with unused attribute.
+            serializer.startTag(null, "app");
+            serializer.attribute(null, "appid", "{APP_ID}");
+            serializer.attribute(null, "status", appStatus);
+            serializer.attribute(null, "unused", "attribute");
+
+            if (addInstall) {
+                serializer.startTag(null, "event");
+                serializer.attribute(null, "status", "ok");
+                serializer.endTag(null, "event");
+            }
+
+            if (addPing) {
+                serializer.startTag(null, "ping");
+                serializer.attribute(null, "status", "ok");
+                serializer.endTag(null, "ping");
+            }
+
+            if (updateStatus != null) {
+                serializer.startTag(null, "updatecheck");
+                serializer.attribute(null, "status", updateStatus);
+                if (UPDATE_STATUS_OK.equals(updateStatus)) {
+                    createUpdateXML(serializer, updateUrl);
+                }
+                serializer.endTag(null, "updatecheck");
+            }
+            serializer.endTag(null, "app");
+
+            // Create extraneous tag.
+            serializer.startTag(null, "extraneous");
+            serializer.attribute(null, "useless", "yes");
+            serializer.endTag(null, "extraneous");
+
+            serializer.endTag(null, "response");
+            serializer.endDocument();
+        } catch (IOException e) {
+            Assert.fail("Caught an IOException creating the XML: " + e);
+        } catch (IllegalArgumentException e) {
+            Assert.fail("Caught an IllegalArgumentException creating the XML: " + e);
+        } catch (IllegalStateException e) {
+            Assert.fail("Caught an IllegalStateException creating the XML: " + e);
+        }
+
+        return writer.toString();
+    }
+
+    private static void createUpdateXML(XmlSerializer serializer, String updateUrl)
+            throws IOException {
+        // End result should look something like:
+        // <updatecheck status="ok">
+        //  <urls>
+        //    <url codebase="URL" />
+        //  </urls>
+        //  <manifest garbage="attribute" version="NEXT_VERSION">
+        //    <packages>
+        //      <package hash="0" name="dummy.apk" required="true" size="0" />
+        //    </packages>
+        //    <actions>
+        //      <action event="install" run="dummy.apk" />
+        //      <action event="postinstall" />
+        //    </actions>
+        //  </manifest>
+        //  <better be="ignored" />
+        //</updatecheck>
+
+        // Create <urls> and its descendants.
+        serializer.startTag(null, "urls");
+        if (updateUrl != null) {
+            serializer.startTag(null, "url");
+            serializer.attribute(null, "codebase", updateUrl);
+            serializer.endTag(null, "url");
+        }
+        serializer.endTag(null, "urls");
+
+        // Create <manifest> and its descendants.
+        serializer.startTag(null, "manifest");
+        serializer.attribute(null, "garbage", "attribute");
+        serializer.attribute(null, "version", NEXT_VERSION);
+
+        // Create <packages> and its children.
+        serializer.startTag(null, "packages");
+        serializer.startTag(null, "package");
+        serializer.attribute(null, "hash", "0");
+        serializer.attribute(null, "name", "dummy.apk");
+        serializer.attribute(null, "required", "true");
+        serializer.attribute(null, "size", "0");
+        serializer.endTag(null, "package");
+        serializer.endTag(null, "packages");
+
+        // Create <actions> and its children.
+        serializer.startTag(null, "actions");
+        serializer.startTag(null, "action");
+        serializer.attribute(null, "event", "install");
+        serializer.attribute(null, "run", "dummy.apk");
+        serializer.endTag(null, "action");
+
+        serializer.startTag(null, "action");
+        serializer.attribute(null, "event", "postinstall");
+        serializer.endTag(null, "action");
+        serializer.endTag(null, "actions");
+        serializer.endTag(null, "manifest");
+
+        // Create a dummy element for testing to make sure it's ignored.
+        serializer.startTag(null, "dummy");
+        serializer.attribute(null, "hopefully", "ignored");
+        serializer.endTag(null, "dummy");
+    }
+
+    /**
+     * Runs a test that is expected to pass.
+     * @param appStatus Status to use for the <app> element.
+     * @param addInstall Whether or not to add an install event.
+     * @param addPing Whether or not to add a ping.
+     * @param updateStatus Status to use for the <updatecheck> element.
+     * @throws RequestFailureException Thrown if the test fails.
+     */
+    private static void runSuccessTest(String appStatus, boolean addInstall, boolean addPing,
+            String updateStatus) throws RequestFailureException {
+        String xml =
+                createTestXML("3.0", "12345", appStatus, addInstall, addPing, updateStatus, URL);
+        ResponseParser parser =
+                new ResponseParser(true, "{APP_ID}", addInstall, addPing, updateStatus != null);
+        parser.parseResponse(xml);
+
+        Assert.assertEquals("elapsed_seconds doesn't match.", 12345, parser.getDaystartSeconds());
+        Assert.assertEquals("<app> status doesn't match.", appStatus, parser.getAppStatus());
+        Assert.assertEquals(
+                "<updatecheck> status doesn't match.", updateStatus, parser.getUpdateStatus());
+        if (UPDATE_STATUS_OK.equals(updateStatus)) {
+            Assert.assertEquals(
+                    "Version number doesn't match.", "1.2.3.4", parser.getNewVersion());
+            Assert.assertEquals(
+                    "Market URL doesn't match.", STRIPPED_URL, parser.getURL());
+        } else {
+            Assert.assertEquals(
+                    "Version number doesn't match.", null, parser.getNewVersion());
+            Assert.assertEquals(
+                    "Market URL doesn't match.", null, parser.getURL());
+        }
+    }
+
+    /**
+     * Runs a test that is expected to fail in a particular way.
+     * @param xml XML to parse.
+     * @param expectedErrorCode Expected error code.
+     * @param expectInstall Whether or not the parser should expect an install event.
+     * @param expectPing Whether or not the parser should expect a ping element.
+     * @param expectUpdate Whether or not the parser should expect an update check.
+     */
+    private static void runFailureTest(String xml, int expectedErrorCode,
+            boolean expectInstall, boolean expectPing, boolean expectUpdate) {
+        ResponseParser parser =
+                new ResponseParser(true, "{APP_ID}", expectInstall, expectPing, expectUpdate);
+
+        try {
+            parser.parseResponse(xml);
+        } catch (RequestFailureException e) {
+            Assert.assertEquals("Incorrect error code received.", expectedErrorCode, e.errorCode);
+            return;
+        }
+
+        Assert.fail("Failed to throw RequestFailureException for bad XML.");
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testValidAllTypes() throws RequestFailureException {
+        runSuccessTest(APP_STATUS_OK, true, true, UPDATE_STATUS_OK);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testValidNoInstall() throws RequestFailureException {
+        runSuccessTest(APP_STATUS_OK, false, true, UPDATE_STATUS_OK);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testValidNoPing() throws RequestFailureException {
+        runSuccessTest(APP_STATUS_OK, true, false, UPDATE_STATUS_OK);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testValidNoUpdatecheck() throws RequestFailureException {
+        runSuccessTest(APP_STATUS_OK, true, true, null);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testValidUpdatecheckNoUpdate() throws RequestFailureException {
+        runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_NOUPDATE);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testValidUpdatecheckError() throws RequestFailureException {
+        runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_ERROR);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testValidUpdatecheckUnknown() throws RequestFailureException {
+        runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_WTF);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testValidAppStatusRestricted() throws RequestFailureException {
+        runSuccessTest(APP_STATUS_RESTRICTED, false, false, null);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testFailBogusResponse() {
+        String xml = "Bogus";
+        runFailureTest(xml, RequestFailureException.ERROR_MALFORMED_XML, false, false, false);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testBadResponseProtocol() {
+        String xml =
+                createTestXML("2.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL);
+        runFailureTest(xml, RequestFailureException.ERROR_PARSE_RESPONSE, false, false, false);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testFailMissingDaystart() {
+        String xml =
+                createTestXML("3.0", null, APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL);
+        runFailureTest(xml, RequestFailureException.ERROR_PARSE_DAYSTART, false, false, true);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testAppTagMissingUpdatecheck() {
+        String xml =
+                createTestXML("3.0", "12345", APP_STATUS_OK, true, false, null, URL);
+        runFailureTest(xml, RequestFailureException.ERROR_PARSE_UPDATECHECK, true, false, true);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testAppTagUnexpectedUpdatecheck() {
+        String xml =
+                createTestXML("3.0", "12345", APP_STATUS_OK, true, false, UPDATE_STATUS_OK, URL);
+        runFailureTest(xml, RequestFailureException.ERROR_PARSE_UPDATECHECK, true, false, false);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testAppTagMissingPing() {
+        String xml =
+                createTestXML("3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL);
+        runFailureTest(xml, RequestFailureException.ERROR_PARSE_PING, false, true, true);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testAppTagUnexpectedPing() {
+        String xml =
+                createTestXML("3.0", "12345", APP_STATUS_OK, false, true, UPDATE_STATUS_OK, URL);
+        runFailureTest(xml, RequestFailureException.ERROR_PARSE_PING, false, false, true);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testAppTagMissingInstall() {
+        String xml =
+                createTestXML("3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL);
+        runFailureTest(xml, RequestFailureException.ERROR_PARSE_EVENT, true, false, true);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testAppTagUnexpectedInstall() {
+        String xml =
+                createTestXML("3.0", "12345", APP_STATUS_OK, true, false, UPDATE_STATUS_OK, URL);
+        runFailureTest(xml, RequestFailureException.ERROR_PARSE_EVENT, false, false, true);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testAppTagStatusError() {
+        String xml =
+                createTestXML("3.0", "12345", APP_STATUS_ERROR, false, false, null, URL);
+        runFailureTest(xml, RequestFailureException.ERROR_PARSE_APP, false, false, false);
+    }
+
+    @Test
+    @Feature({"Omaha"})
+    public void testUpdatecheckMissingUrl() {
+        String xml = createTestXML(
+                "3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, null);
+        runFailureTest(xml, RequestFailureException.ERROR_PARSE_URLS, false, false, true);
+    }
+}
diff --git a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java
index 2e3208fe..f3cb500 100644
--- a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java
+++ b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java
@@ -81,7 +81,8 @@
             new ActivityWindowAndroidFactory() {
                 @Override
                 public ActivityWindowAndroid getActivityWindowAndroid(Activity activity) {
-                    return new ActivityWindowAndroid(activity);
+                    final boolean listenToActivityState = true;
+                    return new ActivityWindowAndroid(activity, listenToActivityState);
                 }
             };
 
@@ -271,9 +272,6 @@
         super.onStop();
 
         if (mToolbar != null) mToolbar.hideSuggestions();
-
-        Tab activeTab = getActiveTab();
-        if (activeTab != null) activeTab.onActivityStop();
     }
 
     @Override
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 273bc7c..6db8daf 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -94,40 +94,40 @@
 #define IDC_ENCODING_AUTO_DETECT        35500
 #define IDC_ENCODING_UTF8               35501
 #define IDC_ENCODING_UTF16LE            35502
-#define IDC_ENCODING_WINDOWS1252        35504
-#define IDC_ENCODING_GBK                35505
-#define IDC_ENCODING_GB18030            35506
-#define IDC_ENCODING_BIG5               35507
-#define IDC_ENCODING_BIG5HKSCS          35508
-#define IDC_ENCODING_KOREAN             35509
-#define IDC_ENCODING_SHIFTJIS           35510
-#define IDC_ENCODING_ISO2022JP          35511
-#define IDC_ENCODING_EUCJP              35512
-#define IDC_ENCODING_THAI               35513
-#define IDC_ENCODING_ISO885915          35514
-#define IDC_ENCODING_MACINTOSH          35515
-#define IDC_ENCODING_ISO88592           35516
-#define IDC_ENCODING_WINDOWS1250        35517
-#define IDC_ENCODING_ISO88595           35518
-#define IDC_ENCODING_WINDOWS1251        35519
-#define IDC_ENCODING_KOI8R              35520
-#define IDC_ENCODING_KOI8U              35521
-#define IDC_ENCODING_ISO88597           35522
-#define IDC_ENCODING_WINDOWS1253        35523
-#define IDC_ENCODING_ISO88594           35524
-#define IDC_ENCODING_ISO885913          35525
-#define IDC_ENCODING_WINDOWS1257        35526
-#define IDC_ENCODING_ISO88593           35527
-#define IDC_ENCODING_ISO885910          35528
-#define IDC_ENCODING_ISO885914          35529
-#define IDC_ENCODING_ISO885916          35530
-#define IDC_ENCODING_WINDOWS1254        35531
-#define IDC_ENCODING_ISO88596           35532
-#define IDC_ENCODING_WINDOWS1256        35533
-#define IDC_ENCODING_ISO88598           35534
-#define IDC_ENCODING_WINDOWS1255        35535
-#define IDC_ENCODING_WINDOWS1258        35536
-#define IDC_ENCODING_ISO88598I          35537
+#define IDC_ENCODING_WINDOWS1252        35503
+#define IDC_ENCODING_GBK                35504
+#define IDC_ENCODING_GB18030            35505
+#define IDC_ENCODING_BIG5               35506
+#define IDC_ENCODING_KOREAN             35507
+#define IDC_ENCODING_SHIFTJIS           35508
+#define IDC_ENCODING_ISO2022JP          35509
+#define IDC_ENCODING_EUCJP              35510
+#define IDC_ENCODING_THAI               35511
+#define IDC_ENCODING_ISO885915          35512
+#define IDC_ENCODING_MACINTOSH          35513
+#define IDC_ENCODING_ISO88592           35514
+#define IDC_ENCODING_WINDOWS1250        35515
+#define IDC_ENCODING_ISO88595           35516
+#define IDC_ENCODING_WINDOWS1251        35517
+#define IDC_ENCODING_KOI8R              35518
+#define IDC_ENCODING_KOI8U              35519
+#define IDC_ENCODING_ISO88597           35520
+#define IDC_ENCODING_WINDOWS1253        35521
+#define IDC_ENCODING_ISO88594           35522
+#define IDC_ENCODING_ISO885913          35523
+#define IDC_ENCODING_WINDOWS1257        35524
+#define IDC_ENCODING_ISO88593           35525
+#define IDC_ENCODING_ISO885910          35526
+#define IDC_ENCODING_ISO885914          35527
+#define IDC_ENCODING_ISO885916          35528
+#define IDC_ENCODING_WINDOWS1254        35529
+#define IDC_ENCODING_ISO88596           35530
+#define IDC_ENCODING_WINDOWS1256        35531
+#define IDC_ENCODING_ISO88598           35532
+#define IDC_ENCODING_WINDOWS1255        35533
+#define IDC_ENCODING_WINDOWS1258        35534
+#define IDC_ENCODING_ISO88598I          35535
+#define IDC_ENCODING_IBM866             35536
 
 // Clipboard commands
 #define IDC_CUT                         36000
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index dae424d3..a7218b7 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -918,9 +918,6 @@
         <message name="IDS_SRT_BUBBLE_TITLE" desc="Text for the title of the software removal tool bubble view.">
             Chromium behaving strangely?
         </message>
-        <message name="IDS_SRT_BUBBLE_TEXT" desc="Text for the software removal tool bubble view full description.">
-          Chromium detected that your browser settings may have been changed by a program on your computer without your knowledge. You can use  Google's Software Removal Tool to remove that program.
-        </message>
       </if>
       <!-- Sync/sign-in error messages -->
       <if expr="not chromeos">
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index ff1be6a..d9a181f 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4726,6 +4726,9 @@
         Read and change anything you type including task switching keys like ALT+TAB
       </message>
       </if>
+      <message name="IDS_EXTENSION_PROMPT_WARNING_SETTINGS_PRIVATE" desc="Permission string for access to settings.">
+        Read and change user and device settings
+      </message>
 
       <!-- Extension/App error messages -->
       <message name="IDS_EXTENSION_CANT_GET_ABSOLUTE_PATH" desc="Warning displayed in pack dialog when the absolute path to the extension directory can not be found.">
@@ -6473,6 +6476,12 @@
       <message name="IDS_FLAGS_PERFORMANCE_MONITOR_GATHERING_DESCRIPTION" desc="Description for the flag to enable Performance Monitor.">
         Enable passive gathering of performance-related metrics and events and provide the option to view this data in a graphical fashion. To view data, visit chrome://performance.
       </message>
+      <message name="IDS_FLAGS_ENABLE_ICON_NTP_NAME" desc="Name of the flag to enable large icons on the NTP.">
+        Enable large icons on the New Tab page
+      </message>
+      <message name="IDS_FLAGS_ENABLE_ICON_NTP_DESCRIPTION" desc="Description for the flag to enable large icons on the NTP.">
+        Enable the experimental New Tab page using large icons.
+      </message>
       <message name="IDS_FLAGS_ENABLE_STALE_WHILE_REVALIDATE_NAME" desc="Name of the flag to enable the stale-while-revalidate cache directive.">
         Enable the stale-while-revalidate cache directive
       </message>
@@ -7185,9 +7194,6 @@
       <message name="IDS_PLUGIN_NOT_FOUND" desc="The placeholder text for an unknown plugin that is not installed.">
         No plugin available to display this content.
       </message>
-      <message name="IDS_PLUGIN_SEARCHING" desc="The placeholder text when searching for a missing plugin.">
-        Looking for plugin...
-      </message>
       <message name="IDS_PLUGIN_DOWNLOADING" desc="The placeholder text when downloading a missing plugin.">
         Downloading <ph name="PLUGIN_NAME">$1<ex>Realplayer</ex></ph>...
       </message>
@@ -7675,6 +7681,9 @@
       <message name="IDS_PASSWORDS_EXCEPTIONS_TAB_TITLE" desc="Title for 'Never saved' tab">
         Never saved
       </message>
+      <message name="IDS_PASSWORDS_VIA_FEDERATION" desc="Text for federated credential's value.">
+        via <ph name="PROVIDER">$1<ex>facebook.com</ex></ph>
+      </message>
       <message name="IDS_PASSWORDS_PAGE_SEARCH_PASSWORDS" desc="Placeholder text shown in password table search box">
         Search passwords
       </message>
@@ -8004,6 +8013,9 @@
       <message name="IDS_SUPERVISED_USER_NEW_AVATAR_LABEL" desc="Label shown in the new avatar menu for a supervised user.">
         <ph name="PROFILE_DISPLAY_NAME">$1<ex>Markus</ex></ph> (Supervised)
       </message>
+      <message name="IDS_CHILD_AVATAR_LABEL" desc="Label shown in the new avatar menu for a child user.">
+        <ph name="PROFILE_DISPLAY_NAME">$1<ex>Markus</ex></ph> (Account for kids)
+      </message>
 
       <!-- Supervised User Block Interstitial data -->
       <message name="IDS_BLOCK_INTERSTITIAL_TITLE" desc="A title for the supervised-user block interstitial page.">
@@ -8556,6 +8568,9 @@
       <message name="IDS_SRT_BUBBLE_RUN_BUTTON_TEXT" desc="Run button of the software removal tool bubble">
         Run the Software Removal Tool
       </message>
+      <message name="IDS_SRT_BUBBLE_TEXT" desc="Text for the software removal tool bubble view full description.">
+          For example, causing crashes, unusual startup pages or toolbars, unexpected ads you can't get rid of, or otherwise changing your browsing experience? You may be able to fix the problem by running the Software Removal Tool.
+      </message>
 
       <!-- Upgrade bubble messages -->
       <message name="IDS_REENABLE_UPDATES" desc="Text for the button the user clicks to re-enable automatic updates.">
@@ -9100,6 +9115,9 @@
       <message name="IDS_ERRORPAGES_BUTTON_SHOW_SAVED_COPY_HELP" desc="Explanation for the BUTTON_SHOW_SAVED button">
         Show a saved (i.e. known to be out of date) copy of this page.
       </message>
+      <message name="IDS_ERRORPAGE_FUN_DISABLED" desc="Explanation for when easter egg has been disabled by administrator">
+        The owner of this device turned off the dinosaur game.
+      </message>
       <if expr="chromeos">
         <message name="IDS_ERRORPAGES_BUTTON_DIAGNOSE" desc="Label for the button that invokes the connection diagnostic tool on the error page">
           Diagnose errors...
@@ -12203,18 +12221,6 @@
         </message>
       </if>
 
-      <message name="IDS_PLUGININSTALLER_INSTALLPLUGIN_PROMPT" desc="Info Bar message to prompt installing missing plugin">
-        <ph name="PLUGIN_NAME">$1<ex>Quicktime</ex></ph> is required to display some elements on this page.
-      </message>
-      <message name="IDS_PLUGININSTALLER_INSTALLPLUGIN_BUTTON" desc="Info Bar button to install missing plugin">
-        Install plugin...
-      </message>
-      <message name="IDS_PLUGININSTALLER_PROBLEMSINSTALLING" desc="Infobar text for link to help center">
-        Problems installing?
-      </message>
-      <message name="IDS_PLUGININSTALLER_PROBLEMSUPDATING" desc="Infobar text for link to help center">
-        Problems updating?
-      </message>
       <message name="IDS_PLUGIN_OUTDATED_PROMPT" desc="Info Bar message when an outdated plugin was disabled">
         <ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph> was blocked because it is out of date.
       </message>
@@ -14484,11 +14490,11 @@
           Enable IME extensions to supply custom views for user input such as  virtual keyboards.
         </message>
 
-        <message name="IDS_FLAGS_ENABLE_NEW_MD_INPUT_VIEW_NAME" desc="Name of about::flags option to enable material style of input view virtual keyboard.">
-          Enable input view keyboard in material design.
+        <message name="IDS_FLAGS_DISABLE_NEW_MD_INPUT_VIEW_NAME" desc="Name of about::flags option to disable material style of input view virtual keyboard.">
+          Disable input view keyboard in material design.
         </message>
-        <message name="IDS_FLAGS_ENABLE_NEW_MD_INPUT_VIEW_DESCRIPTION" desc="Description of about::flags option to enable the material style of input view virtual keyboard.">
-          Enable input view keyboards in material design.
+        <message name="IDS_FLAGS_DISABLE_NEW_MD_INPUT_VIEW_DESCRIPTION" desc="Description of about::flags option to disable the material style of input view virtual keyboard.">
+          Disable input view keyboards in material design.
         </message>
 
         <message name="IDS_FLAGS_ENABLE_NEW_KOREAN_IME_NAME" desc="Name of about::flags option to enable the new Korean IME">
@@ -14590,6 +14596,14 @@
         The Simple Cache for HTTP is a new cache. It relies on the filesystem for disk space allocation.
       </message>
 
+      <!-- Spelling feedback field trial. -->
+      <message name="IDS_FLAGS_ENABLE_SPELLING_FEEDBACK_FIELD_TRIAL_NAME" desc="Name of about:flags option to enable the field trial for sending feedback to spelling service.">
+        Spelling Feedback Field Trial.
+      </message>
+      <message name="IDS_FLAGS_ENABLE_SPELLING_FEEDBACK_FIELD_TRIAL_DESCRIPTION" desc="Description of about:flags option to enable the field trial for sending feedback to spelling service.">
+        Enable the field trial for sending user feedback to spelling service.
+      </message>
+
       <!-- Web MIDI API. -->
       <message name="IDS_FLAGS_ENABLE_WEB_MIDI_NAME"
                desc="Name of about:flag option to turn on Web MIDI API">
@@ -15818,6 +15832,12 @@
         <ph name="EVENT_NAME">$3<ex>Event Description</ex></ph>
       </message>
 
+      <message name="IDS_ENABLE_INVALID_CERT_COLLECTION" desc="Name of the 'Enable invalid certificate collection' flag.">
+        Enable opt-in for reporting invalid TLS/SSL certificate chains
+      </message>
+      <message name="IDS_ENABLE_INVALID_CERT_COLLECTION_DESCRIPTION" desc="Description of the 'Enable invalid certificate collection' flag.">
+        Allows users to opt in to the collection of invalid TLS/SSL certificate chains.
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 24fce29..d76eac9 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -841,9 +841,6 @@
         <message name="IDS_SRT_BUBBLE_TITLE" desc="Text for the title of the software removal tool bubble view.">
             Chrome behaving strangely?
         </message>
-        <message name="IDS_SRT_BUBBLE_TEXT" desc="Text for the software removal tool bubble view full description.">
-          Chrome detected that your browser settings may have been changed by a program on your computer without your knowledge. You can use Google's Software Removal Tool to remove that program.
-        </message>
       </if>
       <!-- Sync/sign-in error messages -->
       <if expr="not chromeos">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 1fab285..0a0df19a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1123,6 +1123,14 @@
         switches::kScrollEndEffect, "0")
   },
   {
+    "enable-icon-ntp",
+    IDS_FLAGS_ENABLE_ICON_NTP_NAME,
+    IDS_FLAGS_ENABLE_ICON_NTP_DESCRIPTION,
+    kOsAndroid | kOsMac | kOsWin | kOsLinux | kOsCrOS,
+    ENABLE_DISABLE_VALUE_TYPE(switches::kEnableIconNtp,
+                              switches::kDisableIconNtp)
+  },
+  {
     "enable-touch-drag-drop",
     IDS_FLAGS_ENABLE_TOUCH_DRAG_DROP_NAME,
     IDS_FLAGS_ENABLE_TOUCH_DRAG_DROP_DESCRIPTION,
@@ -1307,11 +1315,11 @@
     SINGLE_VALUE_TYPE(chromeos::switches::kEnableNewKoreanIme)
   },
   {
-    "enable-new-md-input-view",
-    IDS_FLAGS_ENABLE_NEW_MD_INPUT_VIEW_NAME,
-    IDS_FLAGS_ENABLE_NEW_MD_INPUT_VIEW_DESCRIPTION,
+    "disable-new-md-input-view",
+    IDS_FLAGS_DISABLE_NEW_MD_INPUT_VIEW_NAME,
+    IDS_FLAGS_DISABLE_NEW_MD_INPUT_VIEW_DESCRIPTION,
     kOsCrOS,
-    SINGLE_VALUE_TYPE(chromeos::switches::kEnableNewMDInputView)
+    SINGLE_VALUE_TYPE(chromeos::switches::kDisableNewMDInputView)
   },
   {
     "enable-physical-keyboard-autocorrect",
@@ -1430,6 +1438,15 @@
     SINGLE_VALUE_TYPE(switches::kDisableHideInactiveStackedTabCloseButtons)
   },
 #endif
+#if defined(ENABLE_SPELLCHECK)
+  {
+    "enable-spelling-feedback-field-trial",
+    IDS_FLAGS_ENABLE_SPELLING_FEEDBACK_FIELD_TRIAL_NAME,
+    IDS_FLAGS_ENABLE_SPELLING_FEEDBACK_FIELD_TRIAL_DESCRIPTION,
+    kOsAll,
+    SINGLE_VALUE_TYPE(switches::kEnableSpellingFeedbackFieldTrial)
+  },
+#endif
   {
     "enable-webgl-draft-extensions",
     IDS_FLAGS_ENABLE_WEBGL_DRAFT_EXTENSIONS_NAME,
@@ -2260,7 +2277,14 @@
     IDS_FLAGS_ENABLE_MEDIA_ROUTER_DESCRIPTION,
     kOsAll,
     SINGLE_VALUE_TYPE(switches::kEnableMediaRouter)
-  }
+  },
+  {
+    "enable-cert-collection",
+    IDS_ENABLE_INVALID_CERT_COLLECTION,
+    IDS_ENABLE_INVALID_CERT_COLLECTION_DESCRIPTION,
+    kOsAll,
+    SINGLE_VALUE_TYPE(switches::kEnableInvalidCertCollection)
+  },
 
   // NOTE: Adding new command-line switches requires adding corresponding
   // entries to enum "LoginCustomFlags" in histograms.xml. See note in
diff --git a/chrome/browser/android/bookmarks/bookmarks_bridge.cc b/chrome/browser/android/bookmarks/bookmarks_bridge.cc
index e51ca3e..6e953a8 100644
--- a/chrome/browser/android/bookmarks/bookmarks_bridge.cc
+++ b/chrome/browser/android/bookmarks/bookmarks_bridge.cc
@@ -97,7 +97,7 @@
       bookmark_model_(NULL),
       client_(NULL),
       partner_bookmarks_shim_(NULL) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   profile_ = ProfileAndroid::FromProfileAndroid(j_profile);
   bookmark_model_ = BookmarkModelFactory::GetForProfile(profile_);
   client_ = ChromeBookmarkClientFactory::GetForProfile(profile_);
@@ -603,7 +603,7 @@
 void BookmarksBridge::DeleteBookmark(JNIEnv* env,
                                      jobject obj,
                                      jobject j_bookmark_id_obj) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(IsLoaded());
 
   long bookmark_id = JavaBookmarkIdGetId(env, j_bookmark_id_obj);
@@ -627,7 +627,7 @@
                                    jobject j_bookmark_id_obj,
                                    jobject j_parent_id_obj,
                                    jint index) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(IsLoaded());
 
   long bookmark_id = JavaBookmarkIdGetId(env, j_bookmark_id_obj);
@@ -710,7 +710,7 @@
 }
 
 void BookmarksBridge::Undo(JNIEnv* env, jobject obj) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(IsLoaded());
   BookmarkUndoService* undo_service =
       BookmarkUndoServiceFactory::GetForProfile(profile_);
@@ -719,7 +719,7 @@
 }
 
 void BookmarksBridge::StartGroupingUndos(JNIEnv* env, jobject obj) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(IsLoaded());
   DCHECK(!grouped_bookmark_actions_.get()); // shouldn't have started already
   grouped_bookmark_actions_.reset(
@@ -727,7 +727,7 @@
 }
 
 void BookmarksBridge::EndGroupingUndos(JNIEnv* env, jobject obj) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(IsLoaded());
   DCHECK(grouped_bookmark_actions_.get()); // should only call after start
   grouped_bookmark_actions_.reset();
diff --git a/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc b/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc
index 4df9a29..5a04979 100644
--- a/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc
+++ b/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc
@@ -34,7 +34,7 @@
                     const GURL& page_url, const GURL& icon_url,
                     const std::vector<unsigned char>& image_data,
                     favicon_base::IconType icon_type) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_refptr<base::RefCountedMemory> bitmap_data(
       new base::RefCountedBytes(image_data));
   gfx::Size pixel_size(gfx::kFaviconSize, gfx::kFaviconSize);
@@ -115,7 +115,7 @@
 
 void PartnerBookmarksReader::PartnerBookmarksCreationComplete(JNIEnv*,
                                                               jobject) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   partner_bookmarks_shim_->SetPartnerBookmarksRoot(
       wip_partner_bookmarks_root_.release());
   wip_next_available_id_ = 0;
@@ -126,7 +126,7 @@
 }
 
 void PartnerBookmarksReader::Reset(JNIEnv* env, jobject obj) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   wip_partner_bookmarks_root_.reset();
   wip_next_available_id_ = 0;
 }
diff --git a/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc b/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc
index 565e723e..433b0ca 100644
--- a/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc
+++ b/chrome/browser/android/bookmarks/partner_bookmarks_shim.cc
@@ -51,7 +51,7 @@
 // static
 PartnerBookmarksShim* PartnerBookmarksShim::BuildForBrowserContext(
     content::BrowserContext* browser_context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   PartnerBookmarksShim* data =
       static_cast<PartnerBookmarksShim*>(
@@ -176,7 +176,7 @@
 }
 
 void PartnerBookmarksShim::SetPartnerBookmarksRoot(BookmarkNode* root_node) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   g_partner_model_keeper.Get().partner_bookmarks_root.reset(root_node);
   g_partner_model_keeper.Get().loaded = true;
   FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
@@ -198,7 +198,7 @@
 // static
 void PartnerBookmarksShim::ClearInBrowserContextForTesting(
     content::BrowserContext* browser_context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   browser_context->SetUserData(kPartnerBookmarksShimUserDataKey, 0);
 }
 
@@ -237,7 +237,7 @@
 }
 
 void PartnerBookmarksShim::ReloadNodeMapping() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   node_rename_remove_map_.clear();
   if (!prefs_)
@@ -272,7 +272,7 @@
 }
 
 void PartnerBookmarksShim::SaveNodeMapping() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!prefs_)
     return;
 
diff --git a/chrome/browser/android/logo_service.cc b/chrome/browser/android/logo_service.cc
index ddcb6263..17ec968 100644
--- a/chrome/browser/android/logo_service.cc
+++ b/chrome/browser/android/logo_service.cc
@@ -36,41 +36,36 @@
   return google_base_url.ReplaceComponents(replacements);
 }
 
-class LogoDecoderDelegate : public ImageDecoder::Delegate {
+class LogoDecoderDelegate : public ImageDecoder::ImageRequest {
  public:
   LogoDecoderDelegate(
-      const scoped_refptr<ImageDecoder>& image_decoder,
       const base::Callback<void(const SkBitmap&)>& image_decoded_callback)
-      : image_decoder_(image_decoder),
+      : ImageRequest(base::MessageLoopProxy::current()),
         image_decoded_callback_(image_decoded_callback),
         weak_ptr_factory_(this) {
     // If the ImageDecoder crashes or otherwise never completes, call
     // OnImageDecodeTimedOut() eventually to ensure that image_decoded_callback_
     // is run.
     base::MessageLoopProxy::current()->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(&LogoDecoderDelegate::OnDecodeImageFailed,
-                   weak_ptr_factory_.GetWeakPtr(),
-                   (const ImageDecoder*) NULL),
+        FROM_HERE, base::Bind(&LogoDecoderDelegate::OnDecodeImageFailed,
+                              weak_ptr_factory_.GetWeakPtr()),
         base::TimeDelta::FromSeconds(kDecodeLogoTimeoutSeconds));
   }
 
-  ~LogoDecoderDelegate() override { image_decoder_->set_delegate(NULL); }
+  ~LogoDecoderDelegate() override {}
 
-  // ImageDecoder::Delegate:
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override {
+  // ImageDecoder::ImageRequest:
+  void OnImageDecoded(const SkBitmap& decoded_image) override {
     image_decoded_callback_.Run(decoded_image);
     delete this;
   }
 
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override {
+  void OnDecodeImageFailed() override {
     image_decoded_callback_.Run(SkBitmap());
     delete this;
   }
 
  private:
-  scoped_refptr<ImageDecoder> image_decoder_;
   base::Callback<void(const SkBitmap&)> image_decoded_callback_;
   base::WeakPtrFactory<LogoDecoderDelegate> weak_ptr_factory_;
 
@@ -86,14 +81,9 @@
   void DecodeUntrustedImage(
       const scoped_refptr<base::RefCountedString>& encoded_image,
       base::Callback<void(const SkBitmap&)> image_decoded_callback) override {
-    scoped_refptr<ImageDecoder> image_decoder = new ImageDecoder(
-        NULL,
-        encoded_image->data(),
-        ImageDecoder::DEFAULT_CODEC);
     LogoDecoderDelegate* delegate =
-        new LogoDecoderDelegate(image_decoder, image_decoded_callback);
-    image_decoder->set_delegate(delegate);
-    image_decoder->Start(base::MessageLoopProxy::current());
+        new LogoDecoderDelegate(image_decoded_callback);
+    ImageDecoder::Start(delegate, encoded_image->data());
   }
 
  private:
diff --git a/chrome/browser/android/most_visited_sites.cc b/chrome/browser/android/most_visited_sites.cc
index 3e01aaf..71bc6a15 100644
--- a/chrome/browser/android/most_visited_sites.cc
+++ b/chrome/browser/android/most_visited_sites.cc
@@ -61,8 +61,6 @@
 const char kNumServerTilesHistogramName[] = "NewTabPage.NumberOfExternalTiles";
 // Client suggestion opened.
 const char kOpenedItemClientHistogramName[] = "NewTabPage.MostVisited.client";
-// Control group suggestion opened.
-const char kOpenedItemControlHistogramName[] = "NewTabPage.MostVisited.client0";
 // Server suggestion opened, no provider.
 const char kOpenedItemServerHistogramName[] = "NewTabPage.MostVisited.server";
 // Server suggestion opened with provider.
@@ -71,9 +69,6 @@
 // Client impression.
 const char kImpressionClientHistogramName[] =
     "NewTabPage.SuggestionsImpression.client";
-// Control group impression.
-const char kImpressionControlHistogramName[] =
-    "NewTabPage.SuggestionsImpression.client0";
 // Server suggestion impression, no provider.
 const char kImpressionServerHistogramName[] =
     "NewTabPage.SuggestionsImpression.server";
@@ -107,7 +102,7 @@
 
 void AddForcedURLOnUIThread(scoped_refptr<history::TopSites> top_sites,
                             const GURL& url) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   top_sites->AddForcedURL(url, base::Time::Now());
 }
 
@@ -189,9 +184,9 @@
 }  // namespace
 
 MostVisitedSites::MostVisitedSites(Profile* profile)
-    : profile_(profile), num_sites_(0), is_control_group_(false),
-      initial_load_done_(false), num_local_thumbs_(0), num_server_thumbs_(0),
-      num_empty_thumbs_(0), scoped_observer_(this), weak_ptr_factory_(this) {
+    : profile_(profile), num_sites_(0), initial_load_done_(false),
+      num_local_thumbs_(0), num_server_thumbs_(0), num_empty_thumbs_(0),
+      scoped_observer_(this), weak_ptr_factory_(this) {
   // Register the debugging page for the Suggestions Service and the thumbnails
   // debugging page.
   content::URLDataSource::Add(profile_,
@@ -249,7 +244,7 @@
                                        jobject obj,
                                        jstring url,
                                        jobject j_callback_obj) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ScopedJavaGlobalRef<jobject>* j_callback =
       new ScopedJavaGlobalRef<jobject>();
   j_callback->Reset(env, j_callback_obj);
@@ -316,9 +311,7 @@
                                                    jint index) {
   switch (mv_source_) {
     case TOP_SITES: {
-      const std::string histogram = is_control_group_ ?
-          kOpenedItemControlHistogramName : kOpenedItemClientHistogramName;
-      LogHistogramEvent(histogram, index, num_sites_);
+      UMA_HISTOGRAM_SPARSE_SLOWLY(kOpenedItemClientHistogramName, index);
       break;
     }
     case SUGGESTIONS_SERVICE: {
@@ -393,10 +386,8 @@
   if (!initial_load_done_) {
     int num_tiles = urls.size();
     UMA_HISTOGRAM_SPARSE_SLOWLY(kNumTilesHistogramName, num_tiles);
-    const std::string histogram = is_control_group_ ?
-        kImpressionControlHistogramName : kImpressionClientHistogramName;
     for (int i = 0; i < num_tiles; ++i) {
-      LogHistogramEvent(histogram, i, num_sites_);
+      UMA_HISTOGRAM_SPARSE_SLOWLY(kImpressionClientHistogramName, i);
     }
   }
   initial_load_done_ = true;
@@ -413,21 +404,14 @@
     ScopedJavaGlobalRef<jobject>* j_observer,
     const SuggestionsProfile& suggestions_profile) {
   int size = suggestions_profile.suggestions_size();
-
-  // Determine if the user is in a control group (they would have received
-  // suggestions, but are in a group where they shouldn't).
-  is_control_group_ = size && SuggestionsService::IsControlGroup();
-
-  // If no suggestions data is available or the user is in a control group,
-  // initiate Top Sites query.
-  if (is_control_group_ || !size) {
+  // With no server suggestions, fall back to local Most Visited.
+  if (!size) {
     InitiateTopSitesQuery();
     return;
   }
 
   std::vector<base::string16> titles;
   std::vector<std::string> urls;
-
   int i = 0;
   for (; i < size && i < num_sites_; ++i) {
     const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
@@ -464,7 +448,7 @@
 void MostVisitedSites::OnObtainedThumbnail(
     ScopedJavaGlobalRef<jobject>* bitmap,
     ScopedJavaGlobalRef<jobject>* j_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   JNIEnv* env = AttachCurrentThread();
   if (bitmap->obj()) {
     num_local_thumbs_++;
@@ -490,7 +474,7 @@
     ScopedJavaGlobalRef<jobject>* j_callback,
     const GURL& url,
     const SkBitmap* bitmap) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   JNIEnv* env = AttachCurrentThread();
 
   ScopedJavaGlobalRef<jobject>* j_bitmap_ref =
diff --git a/chrome/browser/android/most_visited_sites.h b/chrome/browser/android/most_visited_sites.h
index 6e5a7f52a..ab4b419 100644
--- a/chrome/browser/android/most_visited_sites.h
+++ b/chrome/browser/android/most_visited_sites.h
@@ -108,9 +108,6 @@
   // The maximum number of most visited sites to return.
   int num_sites_;
 
-  // Whether the user is in a control group for the purposes of logging.
-  bool is_control_group_;
-
   // Keeps track of whether the initial NTP load has been done.
   bool initial_load_done_;
 
diff --git a/chrome/browser/android/new_tab_page_url_handler.cc b/chrome/browser/android/new_tab_page_url_handler.cc
index 8561c22..da57f579 100644
--- a/chrome/browser/android/new_tab_page_url_handler.cc
+++ b/chrome/browser/android/new_tab_page_url_handler.cc
@@ -13,6 +13,7 @@
 
 namespace {
 const char kBookmarkFolderPath[] = "folder/";
+const char kLegacyWelcomeHost[] = "welcome";
 }
 
 namespace chrome {
@@ -20,10 +21,14 @@
 
 bool HandleAndroidNativePageURL(GURL* url,
                                 content::BrowserContext* browser_context) {
-  if (url->SchemeIs(content::kChromeUIScheme) &&
-      url->host() == chrome::kChromeUINewTabHost) {
-    *url = GURL(chrome::kChromeUINativeNewTabURL);
-    return true;
+  if (url->SchemeIs(content::kChromeUIScheme)) {
+    // TODO(newt): stop redirecting chrome://welcome to chrome-native://newtab
+    // when M39 is a distant memory. http://crbug.com/455427
+    if (url->host() == chrome::kChromeUINewTabHost ||
+        url->host() == kLegacyWelcomeHost) {
+      *url = GURL(chrome::kChromeUINativeNewTabURL);
+      return true;
+    }
   }
 
   if (url->SchemeIs(chrome::kChromeNativeScheme) &&
diff --git a/chrome/browser/android/preferences/pref_service_bridge.cc b/chrome/browser/android/preferences/pref_service_bridge.cc
index cb5f1517..f173415 100644
--- a/chrome/browser/android/preferences/pref_service_bridge.cc
+++ b/chrome/browser/android/preferences/pref_service_bridge.cc
@@ -445,7 +445,10 @@
   HostContentSettingsMap* host_content_settings_map =
       GetOriginalProfile()->GetHostContentSettingsMap();
   host_content_settings_map->SetDefaultContentSetting(
-      CONTENT_SETTINGS_TYPE_MEDIASTREAM,
+      CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
+      allow ? CONTENT_SETTING_ASK : CONTENT_SETTING_BLOCK);
+  host_content_settings_map->SetDefaultContentSetting(
+      CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
       allow ? CONTENT_SETTING_ASK : CONTENT_SETTING_BLOCK);
 }
 
@@ -551,15 +554,22 @@
 }
 
 static jboolean GetCameraMicEnabled(JNIEnv* env, jobject obj) {
-  return GetBooleanForContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM);
+  return GetBooleanForContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC) &&
+         GetBooleanForContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
 }
 
 static jboolean GetCameraMicUserModifiable(JNIEnv* env, jobject obj) {
-  return IsContentSettingUserModifiable(CONTENT_SETTINGS_TYPE_MEDIASTREAM);
+  return IsContentSettingUserModifiable(
+             CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC) &&
+         IsContentSettingUserModifiable(
+             CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
 }
 
 static jboolean GetCameraMicManagedByCustodian(JNIEnv* env, jobject obj) {
-  return IsContentSettingManagedByCustodian(CONTENT_SETTINGS_TYPE_MEDIASTREAM);
+  return IsContentSettingManagedByCustodian(
+             CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC) &&
+         IsContentSettingManagedByCustodian(
+             CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
 }
 
 static jboolean GetAutologinEnabled(JNIEnv* env, jobject obj) {
diff --git a/chrome/browser/android/preferences/website_preference_bridge.cc b/chrome/browser/android/preferences/website_preference_bridge.cc
index f3d2252..6bc4986b 100644
--- a/chrome/browser/android/preferences/website_preference_bridge.cc
+++ b/chrome/browser/android/preferences/website_preference_bridge.cc
@@ -567,7 +567,7 @@
   friend class base::RefCountedThreadSafe<StorageDataDeleter>;
 
   void OnHostDataDeleted(storage::QuotaStatusCode) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
     quota_manager_->ResetUsageTracker(type_);
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
diff --git a/chrome/browser/android/provider/chrome_browser_provider.cc b/chrome/browser/android/provider/chrome_browser_provider.cc
index 8ee893b..1c40a1d 100644
--- a/chrome/browser/android/provider/chrome_browser_provider.cc
+++ b/chrome/browser/android/provider/chrome_browser_provider.cc
@@ -216,7 +216,7 @@
                             const bool is_folder,
                             const int64 parent_id,
                             int64* result) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(result);
     GURL gurl = ParseAndMaybeAppendScheme(url, kDefaultUrlScheme);
 
@@ -259,7 +259,7 @@
   }
 
   static void RunOnUIThread(BookmarkModel* model, const int64 id) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
     if (node && node->parent()) {
       const BookmarkNode* parent_node = node->parent();
@@ -298,7 +298,7 @@
   }
 
   static void RunOnUIThread(BookmarkModel* model) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     LOG(ERROR) << "begin model->RemoveAllUserBookmarks";
     model->RemoveAllUserBookmarks();
     LOG(ERROR) << "after model->RemoveAllUserBookmarks";
@@ -333,7 +333,7 @@
                             const base::string16& title,
                             const base::string16& url,
                             const int64 parent_id) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
     if (node) {
       if (node->GetTitle() != title)
@@ -388,7 +388,7 @@
   static void RunOnUIThread(BookmarkModel* model,
                             const int64 id,
                             bool* result) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(result);
     *result = bookmarks::GetBookmarkNodeByID(model, id) != NULL;
   }
@@ -414,7 +414,7 @@
   static void RunOnUIThread(BookmarkModel* model,
                             const int64 id,
                             bool *result) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(result);
     const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
     const BookmarkNode* mobile_node = model->mobile_node();
@@ -448,7 +448,7 @@
                             const base::string16& title,
                             const int64 parent_id,
                             int64* result) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(result);
 
     // Invalid ids are assumed to refer to the Mobile Bookmarks folder.
@@ -496,7 +496,7 @@
   static void RunOnUIThread(ChromeBookmarkClient* client,
                             BookmarkModel* model,
                             ScopedJavaGlobalRef<jobject>* jroot) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     const BookmarkNode* root = model->root_node();
     if (!root || !root->is_folder())
       return;
@@ -562,7 +562,7 @@
                             bool get_parent,
                             bool get_children,
                             ScopedJavaGlobalRef<jobject>* jnode) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
     if (!node || !jnode)
       return;
@@ -617,7 +617,7 @@
   }
 
   static void RunOnUIThread(BookmarkModel* model, const BookmarkNode** result) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(result);
     *result = model->mobile_node();
   }
@@ -896,7 +896,7 @@
   // Fill SearchRow's keyword_id and url fields according the given
   // search_term. Return true if succeeded.
   void BuildSearchRow(history::SearchRow* row) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     TemplateURLService* template_service =
         TemplateURLServiceFactory::GetForProfile(profile_);
@@ -940,7 +940,7 @@
 
  private:
   void MakeRequestOnUIThread(const history::SearchRow& row) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     history::SearchRow internal_row = row;
     BuildSearchRow(&internal_row);
     service()->InsertSearchTerm(
@@ -1022,7 +1022,7 @@
       const history::SearchRow& row,
       const std::string& selection,
       const std::vector<base::string16>& selection_args) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     history::SearchRow internal_row = row;
     BuildSearchRow(&internal_row);
     service()->UpdateSearchTerms(
@@ -1165,7 +1165,7 @@
     : weak_java_provider_(env, obj),
       history_service_observer_(this),
       handling_extensive_changes_(false) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   profile_ = g_browser_process->profile_manager()->GetLastUsedProfile();
   bookmark_model_ = BookmarkModelFactory::GetForProfile(profile_);
   top_sites_ = TopSitesFactory::GetForProfile(profile_);
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index e9289de..120ad488 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -802,7 +802,7 @@
 
 void TabAndroid::SetInterceptNavigationDelegate(JNIEnv* env, jobject obj,
                                                jobject delegate) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   InterceptNavigationDelegate::Associate(
       web_contents(),
       make_scoped_ptr(new ChromeInterceptNavigationDelegate(env, delegate)));
@@ -844,7 +844,8 @@
                                                          jcontent_view_core);
   DCHECK(content_view_core);
 
-  content_view_core->GetLayer()->RemoveFromParent();
+  if (content_view_core->GetLayer()->parent() == content_layer_)
+    content_view_core->GetLayer()->RemoveFromParent();
 }
 
 static void Init(JNIEnv* env, jobject obj) {
diff --git a/chrome/browser/app_controller_mac.h b/chrome/browser/app_controller_mac.h
index 4b2dc898..015d156 100644
--- a/chrome/browser/app_controller_mac.h
+++ b/chrome/browser/app_controller_mac.h
@@ -85,9 +85,6 @@
   // to it.
   IBOutlet NSMenu* helpMenu_;
 
-  // Indicates wheter an NSPopover is currently being shown.
-  BOOL hasPopover_;
-
   // If we are expecting a workspace change in response to a reopen
   // event, the time we got the event. A null time otherwise.
   base::TimeTicks reopenTime_;
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 866f7c0..b6c2f6f 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -111,13 +111,6 @@
 
 namespace {
 
-// Declare notification names from the 10.7 SDK.
-#if !defined(MAC_OS_X_VERSION_10_7) || \
-    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
-NSString* NSPopoverDidShowNotification = @"NSPopoverDidShowNotification";
-NSString* NSPopoverDidCloseNotification = @"NSPopoverDidCloseNotification";
-#endif
-
 // How long we allow a workspace change notification to wait to be
 // associated with a dock activation. The animation lasts 250ms. See
 // applicationShouldHandleReopen:hasVisibleWindows:.
@@ -353,19 +346,6 @@
              name:NSWindowDidResignMainNotification
            object:nil];
 
-  if (base::mac::IsOSLionOrLater()) {
-    [notificationCenter
-        addObserver:self
-           selector:@selector(popoverDidShow:)
-               name:NSPopoverDidShowNotification
-             object:nil];
-    [notificationCenter
-        addObserver:self
-           selector:@selector(popoverDidClose:)
-               name:NSPopoverDidCloseNotification
-             object:nil];
-  }
-
   // Register for space change notifications.
   [[[NSWorkspace sharedWorkspace] notificationCenter]
     addObserver:self
@@ -562,21 +542,26 @@
 // Close Tab/Close Window accordingly.
 - (void)menuNeedsUpdate:(NSMenu*)menu {
   DCHECK(menu == [closeTabMenuItem_ menu]);
-  NSWindow* window = [NSApp keyWindow];
-  NSWindow* mainWindow = [NSApp mainWindow];
-  if (!window || ([window parentWindow] == mainWindow)) {
-    // If the key window is a child of the main window (e.g. a bubble), the main
-    // window should be the one that handles the close menu item action.
-    // Also, there might be a small amount of time where there is no key window;
-    // in that case as well, just use our main browser window if there is one.
-    // You might think that we should just always use the main window, but the
-    // "About Chrome" window serves as a counterexample.
-    window = mainWindow;
+
+  BOOL enableCloseTabShortcut = NO;
+  id target = [NSApp targetForAction:@selector(performClose:)];
+
+  // |target| is an instance of NSPopover or NSWindow.
+  // If a popover (likely the dictionary lookup popover), we want Cmd-W to
+  // close the popover so map it to "Close Window".
+  // Otherwise, map Cmd-W to "Close Tab" if it's a browser window.
+  if ([target isKindOfClass:[NSWindow class]]) {
+    NSWindow* window = target;
+    NSWindow* mainWindow = [NSApp mainWindow];
+    if (!window || ([window parentWindow] == mainWindow)) {
+      // If the target window is a child of the main window (e.g. a bubble), the
+      // main window should be the one that handles the close menu item action.
+      window = mainWindow;
+    }
+    enableCloseTabShortcut =
+        [[window windowController] isKindOfClass:[TabWindowController class]];
   }
 
-  BOOL hasTabs =
-      [[window windowController] isKindOfClass:[TabWindowController class]];
-  BOOL enableCloseTabShortcut = hasTabs && !hasPopover_;
   [self adjustCloseWindowMenuItemKeyEquivalent:enableCloseTabShortcut];
   [self adjustCloseTabMenuItemKeyEquivalent:enableCloseTabShortcut];
 }
@@ -639,16 +624,6 @@
   isPoweringOff_ = YES;
 }
 
-// Called on Lion and later when a popover (e.g. dictionary) is shown.
-- (void)popoverDidShow:(NSNotification*)notify {
-  hasPopover_ = YES;
-}
-
-// Called on Lion and later when a popover (e.g. dictionary) is closed.
-- (void)popoverDidClose:(NSNotification*)notify {
-  hasPopover_ = NO;
-}
-
 - (void)checkForAnyKeyWindows {
   if ([NSApp keyWindow])
     return;
diff --git a/chrome/browser/apps/drive/drive_app_converter.cc b/chrome/browser/apps/drive/drive_app_converter.cc
index daaa24d3..7752c863 100644
--- a/chrome/browser/apps/drive/drive_app_converter.cc
+++ b/chrome/browser/apps/drive/drive_app_converter.cc
@@ -32,18 +32,17 @@
 //   https://developers.google.com/drive/v2/reference/apps#resource
 // Each icon url represents a single image associated with a certain size.
 class DriveAppConverter::IconFetcher : public net::URLFetcherDelegate,
-                                       public ImageDecoder::Delegate {
+                                       public ImageDecoder::ImageRequest {
  public:
   IconFetcher(DriveAppConverter* converter,
               const GURL& icon_url,
               int expected_size)
-      : converter_(converter),
+      : ImageRequest(
+            BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)),
+        converter_(converter),
         icon_url_(icon_url),
         expected_size_(expected_size) {}
-  ~IconFetcher() override {
-    if (image_decoder_.get())
-      image_decoder_->set_delegate(NULL);
-  }
+  ~IconFetcher() override {}
 
   void Start() {
     fetcher_.reset(
@@ -71,30 +70,23 @@
     std::string unsafe_icon_data;
     fetcher->GetResponseAsString(&unsafe_icon_data);
 
-    image_decoder_ =
-        new ImageDecoder(this, unsafe_icon_data, ImageDecoder::DEFAULT_CODEC);
-    image_decoder_->Start(
-        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI));
+    ImageDecoder::Start(this, unsafe_icon_data);
   }
 
-  // ImageDecoder::Delegate overrides:
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override {
+  // ImageDecoder::ImageRequest overrides:
+  void OnImageDecoded(const SkBitmap& decoded_image) override {
     if (decoded_image.width() == expected_size_)
       icon_ = decoded_image;
     converter_->OnIconFetchComplete(this);
   }
 
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override {
-    converter_->OnIconFetchComplete(this);
-  }
+  void OnDecodeImageFailed() override { converter_->OnIconFetchComplete(this); }
 
   DriveAppConverter* converter_;
   const GURL icon_url_;
   const int expected_size_;
 
   scoped_ptr<net::URLFetcher> fetcher_;
-  scoped_refptr<ImageDecoder> image_decoder_;
   SkBitmap icon_;
 
   DISALLOW_COPY_AND_ASSIGN(IconFetcher);
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index f2a225c..672f033c 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -130,6 +130,39 @@
   DISALLOW_COPY_AND_ASSIGN(WebContentsHiddenObserver);
 };
 
+// Watches for context menu to be shown, records count of how many times
+// context menu was shown.
+class ContextMenuCallCountObserver {
+ public:
+  ContextMenuCallCountObserver ()
+      : num_times_shown_(0),
+        menu_observer_(chrome::NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_SHOWN,
+                       base::Bind(&ContextMenuCallCountObserver::OnMenuShown,
+                                  base::Unretained(this))) {
+  }
+  ~ContextMenuCallCountObserver() {}
+
+  bool OnMenuShown(const content::NotificationSource& source,
+                   const content::NotificationDetails& details) {
+    ++num_times_shown_;
+    auto context_menu = content::Source<RenderViewContextMenu>(source).ptr();
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE, base::Bind(&RenderViewContextMenuBase::Cancel,
+                              base::Unretained(context_menu)));
+    return true;
+  }
+
+  void Wait() { menu_observer_.Wait(); }
+
+  int num_times_shown() { return num_times_shown_; }
+
+ private:
+  int num_times_shown_;
+  content::WindowedNotificationObserver menu_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContextMenuCallCountObserver);
+};
+
 class EmbedderWebContentsObserver : public content::WebContentsObserver {
  public:
   explicit EmbedderWebContentsObserver(content::WebContents* web_contents)
@@ -699,6 +732,17 @@
     }
   }
 
+  void OpenContextMenu(content::WebContents* web_contents) {
+    blink::WebMouseEvent mouse_event;
+    mouse_event.type = blink::WebInputEvent::MouseDown;
+    mouse_event.button = blink::WebMouseEvent::ButtonRight;
+    mouse_event.x = 1;
+    mouse_event.y = 1;
+    web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
+    mouse_event.type = blink::WebInputEvent::MouseUp;
+    web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
+  }
+
   content::WebContents* GetGuestWebContents() {
     return guest_web_contents_;
   }
@@ -1973,6 +2017,36 @@
   return true;
 }
 
+IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenusAPI_PreventDefault) {
+  LoadAppWithGuest("web_view/context_menus/basic");
+
+  content::WebContents* guest_web_contents = GetGuestWebContents();
+  content::WebContents* embedder = GetEmbedderWebContents();
+  ASSERT_TRUE(embedder);
+
+  // Add a preventDefault() call on context menu event so context menu
+  // does not show up.
+  ExtensionTestMessageListener prevent_default_listener(
+      "WebViewTest.CONTEXT_MENU_DEFAULT_PREVENTED", false);
+  EXPECT_TRUE(content::ExecuteScript(embedder, "registerPreventDefault()"));
+  ContextMenuCallCountObserver context_menu_shown_observer;
+
+  OpenContextMenu(guest_web_contents);
+
+  EXPECT_TRUE(prevent_default_listener.WaitUntilSatisfied());
+  // Expect the menu to not show up.
+  EXPECT_EQ(0, context_menu_shown_observer.num_times_shown());
+
+  // Now remove the preventDefault() and expect context menu to be shown.
+  ExecuteScriptWaitForTitle(
+      embedder, "removePreventDefault()", "PREVENT_DEFAULT_LISTENER_REMOVED");
+  OpenContextMenu(guest_web_contents);
+
+  // We expect to see a context menu for the second call to |OpenContextMenu|.
+  context_menu_shown_observer.Wait();
+  EXPECT_EQ(1, context_menu_shown_observer.num_times_shown());
+}
+
 // Tests that a context menu is created when right-clicking in the webview. This
 // also tests that the 'contextmenu' event is handled correctly.
 IN_PROC_BROWSER_TEST_F(WebViewTest, TestContextMenu) {
@@ -1984,15 +2058,7 @@
       chrome::NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_SHOWN,
       base::Bind(ContextMenuNotificationCallback));
 
-  // Open a context menu.
-  blink::WebMouseEvent mouse_event;
-  mouse_event.type = blink::WebInputEvent::MouseDown;
-  mouse_event.button = blink::WebMouseEvent::ButtonRight;
-  mouse_event.x = 1;
-  mouse_event.y = 1;
-  guest_web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
-  mouse_event.type = blink::WebInputEvent::MouseUp;
-  guest_web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
+  OpenContextMenu(guest_web_contents);
 
   // Wait for the context menu to be visible.
   menu_observer.Wait();
diff --git a/chrome/browser/autocomplete/history_url_provider.cc b/chrome/browser/autocomplete/history_url_provider.cc
index 0c395ef..b11014e 100644
--- a/chrome/browser/autocomplete/history_url_provider.cc
+++ b/chrome/browser/autocomplete/history_url_provider.cc
@@ -568,7 +568,7 @@
   if (url_db) {
     DoAutocomplete(NULL, url_db, params.get());
     matches_.clear();
-    PromoteMatchIfNecessary(*params);
+    PromoteMatchesIfNecessary(*params);
     // NOTE: We don't reset |params| here since at least the |promote_type|
     // field on it will be read by the second pass -- see comments in
     // DoAutocomplete().
@@ -766,7 +766,7 @@
   // searching has been disabled by policy. In the cases where we've parsed as
   // UNKNOWN, we'll still show an accidental search infobar if need be.
   VisitClassifier classifier(this, params->input, db);
-  bool have_what_you_typed_match =
+  params->have_what_you_typed_match =
       (params->input.type() != metrics::OmniboxInputType::QUERY) &&
       ((params->input.type() != metrics::OmniboxInputType::UNKNOWN) ||
        (classifier.type() == VisitClassifier::UNVISITED_INTRANET) ||
@@ -774,7 +774,7 @@
        (AutocompleteInput::NumNonHostComponents(params->input.parts()) > 0) ||
        !params->default_search_provider);
   const bool have_shorter_suggestion_suitable_for_inline_autocomplete =
-      PromoteOrCreateShorterSuggestion(db, have_what_you_typed_match, params);
+      PromoteOrCreateShorterSuggestion(db, params);
 
   // Check whether what the user typed appears in history.
   const bool can_check_history_for_exact_match =
@@ -802,13 +802,24 @@
   // this input, so we can promote that as the best match.
   if (params->exact_suggestion_is_in_history) {
     params->promote_type = HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH;
-  } else if (!params->prevent_inline_autocomplete && !params->matches.empty() &&
-      (have_shorter_suggestion_suitable_for_inline_autocomplete ||
-       CanPromoteMatchForInlineAutocomplete(params->matches[0]))) {
+  } else if (!params->matches.empty() &&
+             (have_shorter_suggestion_suitable_for_inline_autocomplete ||
+              CanPromoteMatchForInlineAutocomplete(params->matches[0]))) {
+    // Note that we promote this inline-autocompleted match even when
+    // params->prevent_inline_autocomplete is true.  This is safe because in
+    // this case the match will be marked as "not allowed to be default", and
+    // a non-inlined match that is "allowed to be default" will be reordered
+    // above it by the controller/AutocompleteResult.  We ensure there is such
+    // a match in two ways:
+    //   * If params->have_what_you_typed_match is true, we force the
+    //     what-you-typed match to be added in this case.  See comments in
+    //     PromoteMatchesIfNecessary().
+    //   * Otherwise, we should have some sort of QUERY or UNKNOWN input that
+    //     the SearchProvider will provide a defaultable WYT match for.
     params->promote_type = HistoryURLProviderParams::FRONT_HISTORY_MATCH;
   } else {
     // Failed to promote any URLs.  Use the What You Typed match, if we have it.
-    params->promote_type = have_what_you_typed_match ?
+    params->promote_type = params->have_what_you_typed_match ?
         HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH :
         HistoryURLProviderParams::NEITHER;
   }
@@ -826,15 +837,33 @@
   }
 }
 
-void HistoryURLProvider::PromoteMatchIfNecessary(
+void HistoryURLProvider::PromoteMatchesIfNecessary(
     const HistoryURLProviderParams& params) {
   if (params.promote_type == HistoryURLProviderParams::NEITHER)
     return;
-  matches_.push_back(
-      (params.promote_type == HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH) ?
-      params.what_you_typed_match :
-      HistoryMatchToACMatch(params, 0, INLINE_AUTOCOMPLETE,
-                            CalculateRelevance(INLINE_AUTOCOMPLETE, 0)));
+  if (params.promote_type == HistoryURLProviderParams::FRONT_HISTORY_MATCH) {
+    matches_.push_back(
+        HistoryMatchToACMatch(params, 0, INLINE_AUTOCOMPLETE,
+                              CalculateRelevance(INLINE_AUTOCOMPLETE, 0)));
+  }
+  // There are two cases where we need to add the what-you-typed-match:
+  //   * If params.promote_type is WHAT_YOU_TYPED_MATCH, we're being explicitly
+  //     directed to.
+  //   * If params.have_what_you_typed_match is true, then params.promote_type
+  //     can't be NEITHER (see code near the end of DoAutocomplete()), so if
+  //     it's not WHAT_YOU_TYPED_MATCH, it must be FRONT_HISTORY_MATCH, and
+  //     we'll have promoted the history match above.  If
+  //     params.prevent_inline_autocomplete is also true, then this match
+  //     will be marked "not allowed to be default", and we need to add the
+  //     what-you-typed match to ensure there's a legal default match for the
+  //     controller/AutocompleteResult to promote.  (If
+  //     params.have_what_you_typed_match is false, the SearchProvider should
+  //     take care of adding this defaultable match.)
+  if ((params.promote_type == HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH) ||
+      (params.prevent_inline_autocomplete &&
+       params.have_what_you_typed_match)) {
+    matches_.push_back(params.what_you_typed_match);
+  }
 }
 
 void HistoryURLProvider::QueryComplete(
@@ -855,7 +884,7 @@
   // match in it, whereas |params->matches| will be empty.
   if (!params->failed) {
     matches_.clear();
-    PromoteMatchIfNecessary(*params);
+    PromoteMatchesIfNecessary(*params);
 
     // Determine relevance of highest scoring match, if any.
     int relevance = matches_.empty() ?
@@ -956,7 +985,6 @@
 
 bool HistoryURLProvider::PromoteOrCreateShorterSuggestion(
     history::URLDatabase* db,
-    bool have_what_you_typed_match,
     HistoryURLProviderParams* params) {
   if (params->matches.empty())
     return false;  // No matches, nothing to do.
@@ -966,7 +994,7 @@
   // the same" as any "what you typed" match.
   const history::HistoryMatch& match = params->matches[0];
   GURL search_base = ConvertToHostOnly(match, params->input.text());
-  bool can_add_search_base_to_matches = !have_what_you_typed_match;
+  bool can_add_search_base_to_matches = !params->have_what_you_typed_match;
   if (search_base.is_empty()) {
     // Search from what the user typed when we couldn't reduce the best match
     // to a host.  Careful: use a substring of |match| here, rather than the
diff --git a/chrome/browser/autocomplete/history_url_provider.h b/chrome/browser/autocomplete/history_url_provider.h
index 2fcf830..4a43e2d 100644
--- a/chrome/browser/autocomplete/history_url_provider.h
+++ b/chrome/browser/autocomplete/history_url_provider.h
@@ -157,6 +157,11 @@
   // this to.  See comments in DoAutocomplete().
   PromoteType promote_type;
 
+  // True if |what_you_typed_match| is eligible for display.  If this is true,
+  // PromoteMatchesIfNecessary() may choose to place |what_you_typed_match| on
+  // |matches_| even when |promote_type| is not WHAT_YOU_TYPED_MATCH.
+  bool have_what_you_typed_match;
+
   // Languages we should pass to gfx::GetCleanStringFromUrl.
   std::string languages;
 
@@ -246,10 +251,11 @@
                       history::URLDatabase* db,
                       HistoryURLProviderParams* params);
 
-  // May promote either the what you typed match or first history match in
-  // params->matches to the front of |matches_|, depending on the value of
-  // params->promote_type.
-  void PromoteMatchIfNecessary(const HistoryURLProviderParams& params);
+  // May promote the what you typed match, the first history match in
+  // params->matches, or both to the front of |matches_|, depending on the
+  // values of params->promote_type, params->have_what_you_typed_match, and
+  // params->prevent_inline_autocomplete.
+  void PromoteMatchesIfNecessary(const HistoryURLProviderParams& params);
 
   // Dispatches the results to the autocomplete controller. Called on the
   // main thread by ExecuteWithDB when the results are available.
@@ -281,7 +287,6 @@
   // willing to inline autocomplete.
   bool PromoteOrCreateShorterSuggestion(
       history::URLDatabase* db,
-      bool have_what_you_typed_match,
       HistoryURLProviderParams* params);
 
   // Removes results that have been rarely typed or visited, and not any time
diff --git a/chrome/browser/autocomplete/history_url_provider_unittest.cc b/chrome/browser/autocomplete/history_url_provider_unittest.cc
index 38810aa..68120d52 100644
--- a/chrome/browser/autocomplete/history_url_provider_unittest.cc
+++ b/chrome/browser/autocomplete/history_url_provider_unittest.cc
@@ -413,23 +413,40 @@
   // shorter URL that's "good enough".  The host doesn't match the user input
   // and so should not appear.
   const UrlAndLegalDefault short_3[] = {
-    { "http://foo.com/d", true },
     { "http://foo.com/dir/another/", false },
+    { "http://foo.com/d", true },
     { "http://foo.com/dir/another/again/myfile.html", false },
     { "http://foo.com/dir/", false }
   };
   RunTest(ASCIIToUTF16("foo.com/d"), std::string(), true, short_3,
           arraysize(short_3));
+  // If prevent_inline_autocomplete is false, we won't bother creating the
+  // URL-what-you-typed match because we have promoted inline autocompletions.
+  const UrlAndLegalDefault short_3_allow_inline[] = {
+    { "http://foo.com/dir/another/", true },
+    { "http://foo.com/dir/another/again/myfile.html", true },
+    { "http://foo.com/dir/", true }
+  };
+  RunTest(ASCIIToUTF16("foo.com/d"), std::string(), false, short_3_allow_inline,
+          arraysize(short_3_allow_inline));
 
   // We shouldn't promote shorter URLs than the best if they're not good
   // enough.
   const UrlAndLegalDefault short_4[] = {
-    { "http://foo.com/dir/another/a", true },
     { "http://foo.com/dir/another/again/myfile.html", false },
+    { "http://foo.com/dir/another/a", true },
     { "http://foo.com/dir/another/again/", false }
   };
   RunTest(ASCIIToUTF16("foo.com/dir/another/a"), std::string(), true, short_4,
           arraysize(short_4));
+  // If prevent_inline_autocomplete is false, we won't bother creating the
+  // URL-what-you-typed match because we have promoted inline autocompletions.
+  const UrlAndLegalDefault short_4_allow_inline[] = {
+    { "http://foo.com/dir/another/again/myfile.html", true },
+    { "http://foo.com/dir/another/again/", true }
+  };
+  RunTest(ASCIIToUTF16("foo.com/dir/another/a"), std::string(), false,
+          short_4_allow_inline, arraysize(short_4_allow_inline));
 
   // Exact matches should always be best no matter how much more another match
   // has been typed.
@@ -485,11 +502,21 @@
   // "what you typed" result, plus this one.
   const base::string16 typing(ASCIIToUTF16("http://redirects/"));
   const UrlAndLegalDefault expected_results[] = {
-    { base::UTF16ToUTF8(typing), true },
-    { test_cases[0].url, false }
+    { test_cases[0].url, false },
+    { base::UTF16ToUTF8(typing), true }
   };
   RunTest(typing, std::string(), true, expected_results,
           arraysize(expected_results));
+
+  // If prevent_inline_autocomplete is false, we won't bother creating the
+  // URL-what-you-typed match because we have promoted inline autocompletions.
+  // The result set should instead consist of a single URL representing the
+  // whole set of redirects.
+  const UrlAndLegalDefault expected_results_allow_inlining[] = {
+    { test_cases[0].url, true }
+  };
+  RunTest(typing, std::string(), false, expected_results_allow_inlining,
+          arraysize(expected_results_allow_inlining));
 }
 
 TEST_F(HistoryURLProviderTestNoSearchProvider, WhatYouTypedNoSearchProvider) {
diff --git a/chrome/browser/autocomplete/scored_history_match.cc b/chrome/browser/autocomplete/scored_history_match.cc
index 849c216..bf689ec 100644
--- a/chrome/browser/autocomplete/scored_history_match.cc
+++ b/chrome/browser/autocomplete/scored_history_match.cc
@@ -587,10 +587,10 @@
   const size_t max_visit_to_score =
       std::min(visits.size(), ScoredHistoryMatch::kMaxVisitsToScore);
   for (size_t i = 0; i < max_visit_to_score; ++i) {
-    const bool typed_visit = fix_frequency_bugs_ ?
-        (visits[i].second & ui::PAGE_TRANSITION_TYPED) :
-        (visits[i].second == ui::PAGE_TRANSITION_TYPED);
-    int value_of_transition = typed_visit ? 20 : 1;
+    const ui::PageTransition page_transition = fix_frequency_bugs_ ?
+      ui::PageTransitionStripQualifier(visits[i].second) : visits[i].second;
+    int value_of_transition =
+        (page_transition == ui::PAGE_TRANSITION_TYPED) ? 20 : 1;
     if (bookmarked)
       value_of_transition = std::max(value_of_transition, bookmark_value_);
     const float bucket_weight =
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.cc b/chrome/browser/autofill/android/personal_data_manager_android.cc
index b6214ef..d0ef526 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.cc
+++ b/chrome/browser/autofill/android/personal_data_manager_android.cc
@@ -11,12 +11,11 @@
 #include "base/prefs/pref_service.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/autofill/options_util.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/sync/profile_sync_service.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/common/pref_names.h"
 #include "components/autofill/core/browser/autofill_country.h"
 #include "components/autofill/core/browser/autofill_type.h"
@@ -339,13 +338,7 @@
 
 // Returns whether the Wallet import feature is available.
 static jboolean IsWalletImportFeatureAvailable(JNIEnv* env, jclass clazz) {
-  // TODO(estade): what to do in the IsManaged case?
-  ProfileSyncService* service =
-      ProfileSyncServiceFactory::GetInstance()->GetForProfile(GetProfile());
-  PersonalDataManager* pdm = PersonalDataManagerFactory::GetForProfile(
-      GetProfile());
-  return service && service->IsSyncEnabledAndLoggedIn() &&
-      pdm->IsExperimentalWalletIntegrationEnabled();
+  return WalletIntegrationAvailableForProfile(GetProfile());
 }
 
 // Returns whether the Wallet import feature is enabled.
diff --git a/chrome/browser/autofill/options_util.cc b/chrome/browser/autofill/options_util.cc
new file mode 100644
index 0000000..ef87ea15
--- /dev/null
+++ b/chrome/browser/autofill/options_util.cc
@@ -0,0 +1,21 @@
+// 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/autofill/options_util.h"
+
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+
+namespace autofill {
+
+bool WalletIntegrationAvailableForProfile(Profile* profile) {
+  // TODO(estade): what to do in the IsManaged case?
+  ProfileSyncService* service =
+      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
+  return service && service->IsSyncEnabledAndLoggedIn() &&
+         service->GetPreferredDataTypes().Has(syncer::AUTOFILL_PROFILE) &&
+         service->GetRegisteredDataTypes().Has(syncer::AUTOFILL_WALLET_DATA);
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/autofill/options_util.h b/chrome/browser/autofill/options_util.h
new file mode 100644
index 0000000..dfc2f3b
--- /dev/null
+++ b/chrome/browser/autofill/options_util.h
@@ -0,0 +1,18 @@
+// 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_AUTOFILL_OPTIONS_UTIL_H_
+#define CHROME_BROWSER_AUTOFILL_OPTIONS_UTIL_H_
+
+class Profile;
+
+namespace autofill {
+
+// Decides whether the Autofill Wallet integration is available (i.e. can be
+// enabled or disabled by the user).
+bool WalletIntegrationAvailableForProfile(Profile* profile);
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_AUTOFILL_OPTIONS_UTIL_H_
diff --git a/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc b/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc
index ae8ab65..e413fa1 100644
--- a/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc
+++ b/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc
@@ -12,7 +12,10 @@
 namespace chrome {
 
 BitmapFetcher::BitmapFetcher(const GURL& url, BitmapFetcherDelegate* delegate)
-    : url_(url), delegate_(delegate) {
+    : ImageRequest(content::BrowserThread::GetMessageLoopProxyForThread(
+          content::BrowserThread::UI)),
+      url_(url),
+      delegate_(delegate) {
 }
 
 BitmapFetcher::~BitmapFetcher() {
@@ -45,15 +48,10 @@
 
   std::string image_data;
   source->GetResponseAsString(&image_data);
-  image_decoder_ =
-      new ImageDecoder(this, image_data, ImageDecoder::DEFAULT_CODEC);
 
   // Call start to begin decoding.  The ImageDecoder will call OnImageDecoded
   // with the data when it is done.
-  scoped_refptr<base::MessageLoopProxy> task_runner =
-      content::BrowserThread::GetMessageLoopProxyForThread(
-          content::BrowserThread::UI);
-  image_decoder_->Start(task_runner);
+  ImageDecoder::Start(this, image_data);
 }
 
 void BitmapFetcher::OnURLFetchDownloadProgress(const net::URLFetcher* source,
@@ -62,15 +60,14 @@
   // Do nothing here.
 }
 
-// Methods inherited from ImageDecoder::Delegate.
+// Methods inherited from ImageDecoder::ImageRequest.
 
-void BitmapFetcher::OnImageDecoded(const ImageDecoder* decoder,
-                                   const SkBitmap& decoded_image) {
+void BitmapFetcher::OnImageDecoded(const SkBitmap& decoded_image) {
   // Report success.
   delegate_->OnFetchComplete(url_, &decoded_image);
 }
 
-void BitmapFetcher::OnDecodeImageFailed(const ImageDecoder* decoder) {
+void BitmapFetcher::OnDecodeImageFailed() {
   ReportFailure();
 }
 
diff --git a/chrome/browser/bitmap_fetcher/bitmap_fetcher.h b/chrome/browser/bitmap_fetcher/bitmap_fetcher.h
index ac26bbf6..11055eaf 100644
--- a/chrome/browser/bitmap_fetcher/bitmap_fetcher.h
+++ b/chrome/browser/bitmap_fetcher/bitmap_fetcher.h
@@ -23,7 +23,7 @@
 // Asynchrounously fetches an image from the given URL and returns the
 // decoded Bitmap to the provided BitmapFetcherDelegate.
 class BitmapFetcher : public net::URLFetcherDelegate,
-                      public ImageDecoder::Delegate {
+                      public ImageDecoder::ImageRequest {
  public:
   BitmapFetcher(const GURL& url, BitmapFetcherDelegate* delegate);
   ~BitmapFetcher() override;
@@ -54,23 +54,21 @@
                                   int64 current,
                                   int64 total) override;
 
-  // Methods inherited from ImageDecoder::Delegate
+  // Methods inherited from ImageDecoder::ImageRequest
 
   // Called when image is decoded. |decoder| is used to identify the image in
   // case of decoding several images simultaneously.  This will not be called
   // on the UI thread.
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override;
+  void OnImageDecoded(const SkBitmap& decoded_image) override;
 
   // Called when decoding image failed.
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override;
+  void OnDecodeImageFailed() override;
 
  private:
   // Alerts the delegate that a failure occurred.
   void ReportFailure();
 
   scoped_ptr<net::URLFetcher> url_fetcher_;
-  scoped_refptr<ImageDecoder> image_decoder_;
   const GURL url_;
   BitmapFetcherDelegate* const delegate_;
 
diff --git a/chrome/browser/bitmap_fetcher/bitmap_fetcher_browsertest.cc b/chrome/browser/bitmap_fetcher/bitmap_fetcher_browsertest.cc
index 9a8db76..72d5098 100644
--- a/chrome/browser/bitmap_fetcher/bitmap_fetcher_browsertest.cc
+++ b/chrome/browser/bitmap_fetcher/bitmap_fetcher_browsertest.cc
@@ -141,7 +141,7 @@
 
   BitmapFetcher fetcher(url, &delegate);
 
-  fetcher.OnImageDecoded(NULL, image);
+  fetcher.OnImageDecoded(image);
 
   // Ensure image is marked as succeeded.
   EXPECT_TRUE(delegate.success());
diff --git a/chrome/browser/bitmap_fetcher/bitmap_fetcher_service_unittest.cc b/chrome/browser/bitmap_fetcher/bitmap_fetcher_service_unittest.cc
index 6468b8a6..73513c8 100644
--- a/chrome/browser/bitmap_fetcher/bitmap_fetcher_service_unittest.cc
+++ b/chrome/browser/bitmap_fetcher/bitmap_fetcher_service_unittest.cc
@@ -78,14 +78,13 @@
     image.allocN32Pixels(2, 2);
     image.eraseColor(SK_ColorGREEN);
 
-    const_cast<chrome::BitmapFetcher*>(fetcher)->OnImageDecoded(NULL, image);
+    const_cast<chrome::BitmapFetcher*>(fetcher)->OnImageDecoded(image);
   }
 
   void FailFetch(const GURL& url) {
     const chrome::BitmapFetcher* fetcher = service_->FindFetcherForUrl(url);
     ASSERT_TRUE(NULL != fetcher);
-    const_cast<chrome::BitmapFetcher*>(fetcher)
-        ->OnImageDecoded(NULL, SkBitmap());
+    const_cast<chrome::BitmapFetcher*>(fetcher)->OnImageDecoded(SkBitmap());
   }
 
  protected:
diff --git a/chrome/browser/bookmarks/enhanced_bookmarks_features.cc b/chrome/browser/bookmarks/enhanced_bookmarks_features.cc
index 5b7b38a..8f69f07 100644
--- a/chrome/browser/bookmarks/enhanced_bookmarks_features.cc
+++ b/chrome/browser/bookmarks/enhanced_bookmarks_features.cc
@@ -8,34 +8,37 @@
 #include "base/prefs/pref_service.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/variations/variations_associated_data.h"
+
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
 #include "extensions/common/features/feature.h"
 #include "extensions/common/features/feature_provider.h"
+#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
 #if defined(OS_ANDROID)
 #include "base/android/build_info.h"
-#endif
+#endif  // defined(OS_ANDROID)
 
 namespace {
 
 const char kFieldTrialName[] = "EnhancedBookmarks";
 
-}  // namespace
-
 bool GetBookmarksExperimentExtensionID(std::string* extension_id) {
   *extension_id = variations::GetVariationParamValue(kFieldTrialName, "id");
   if (extension_id->empty())
     return false;
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_IOS)
   return true;
 #else
   const extensions::FeatureProvider* feature_provider =
       extensions::FeatureProvider::GetPermissionFeatures();
   extensions::Feature* feature = feature_provider->GetFeature("metricsPrivate");
   return feature && feature->IsIdInWhitelist(*extension_id);
-#endif
+#endif  // defined(OS_ANDROID) || defined(OS_IOS)
 }
 
+}  // namespace
+
 #if defined(OS_ANDROID)
 bool IsEnhancedBookmarkImageFetchingEnabled(const PrefService* user_prefs) {
   if (IsEnhancedBookmarksEnabled())
@@ -50,29 +53,32 @@
       kFieldTrialName, "DisableImagesFetching");
   return disable_fetching.empty();
 }
+#endif  // defined(OS_ANDROID)
 
 bool IsEnhancedBookmarksEnabled() {
   std::string extension_id;
   return IsEnhancedBookmarksEnabled(&extension_id);
 }
-#endif
 
 bool IsEnhancedBookmarksEnabled(std::string* extension_id) {
   // kEnhancedBookmarksExperiment flag could have values "", "1" and "0".
-  // "0" - user opted out.
-  bool opt_out = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-                     switches::kEnhancedBookmarksExperiment) == "0";
+  // "0" - user opted out. "1" is only possible on mobile as desktop needs a
+  // extension id that would not be available by just using the flag.
 
-#if defined(OS_ANDROID)
-  opt_out |= base::android::BuildInfo::GetInstance()->sdk_int() <
-                 base::android::SdkVersion::SDK_VERSION_ICE_CREAM_SANDWICH_MR1;
-
-  // Android tests use command line flag to force enhanced bookmark to be on.
+#if defined(OS_ANDROID) || defined(OS_IOS)
+  // Tests use command line flag to force enhanced bookmark to be on.
   bool opt_in = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
                     switches::kEnhancedBookmarksExperiment) == "1";
   if (opt_in)
     return true;
-#endif
+#endif  // defined(OS_ANDROID) || defined(OS_IOS)
+
+  bool opt_out = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+                     switches::kEnhancedBookmarksExperiment) == "0";
+#if defined(OS_ANDROID)
+  opt_out |= base::android::BuildInfo::GetInstance()->sdk_int() <
+                 base::android::SdkVersion::SDK_VERSION_ICE_CREAM_SANDWICH_MR1;
+#endif  // defined(OS_ANDROID)
 
   if (opt_out)
     return false;
diff --git a/chrome/browser/bookmarks/enhanced_bookmarks_features.h b/chrome/browser/bookmarks/enhanced_bookmarks_features.h
index 8418312..7990f3c 100644
--- a/chrome/browser/bookmarks/enhanced_bookmarks_features.h
+++ b/chrome/browser/bookmarks/enhanced_bookmarks_features.h
@@ -15,10 +15,10 @@
 // Returns true if enhanced bookmark salient image prefetching is enabled.
 // This can be controlled by field trial.
 bool IsEnhancedBookmarkImageFetchingEnabled(const PrefService* user_prefs);
+#endif  // defined(OS_ANDROID)
 
 // Returns true if enhanced bookmarks is enabled.
 bool IsEnhancedBookmarksEnabled();
-#endif
 
 // Returns true and sets |extension_id| if enhanced bookmarks experiment is
 // enabled. Returns false if no bookmark experiment or extension id is empty.
diff --git a/chrome/browser/browser_process_platform_part_chromeos.cc b/chrome/browser/browser_process_platform_part_chromeos.cc
index 520ba39..12d6ce6a 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.cc
+++ b/chrome/browser/browser_process_platform_part_chromeos.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/chromeos/system/device_disabling_manager_default_delegate.h"
 #include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_switches.h"
 #include "chromeos/geolocation/simple_geolocation_provider.h"
 #include "chromeos/timezone/timezone_resolver.h"
 #include "components/session_manager/core/session_manager.h"
@@ -45,6 +46,7 @@
 }
 
 void BrowserProcessPlatformPart::InitializeChromeUserManager() {
+  DisableDinoEasterEggIfEnrolled();
   DCHECK(!chrome_user_manager_);
   chrome_user_manager_ =
       chromeos::ChromeUserManagerImpl::CreateChromeUserManager();
@@ -111,6 +113,14 @@
       g_browser_process->browser_policy_connector());
 }
 
+void BrowserProcessPlatformPart::DisableDinoEasterEggIfEnrolled() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  const bool is_enterprise_managed = g_browser_process->platform_part()->
+             browser_policy_connector_chromeos()->IsEnterpriseManaged();
+  if (is_enterprise_managed)
+    command_line->AppendSwitch(switches::kDisableDinosaurEasterEgg);
+}
+
 chromeos::TimeZoneResolver* BrowserProcessPlatformPart::GetTimezoneResolver() {
   if (!timezone_resolver_.get()) {
     timezone_resolver_.reset(new chromeos::TimeZoneResolver(
diff --git a/chrome/browser/browser_process_platform_part_chromeos.h b/chrome/browser/browser_process_platform_part_chromeos.h
index 8e574da..6012af9 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.h
+++ b/chrome/browser/browser_process_platform_part_chromeos.h
@@ -60,6 +60,10 @@
                                 bool is_running_test);
   void ShutdownSessionManager();
 
+  // Disable the offline interstitial easter egg if the device is enterprise
+  // enrolled.
+  void DisableDinoEasterEggIfEnrolled();
+
   // Returns the SessionManager instance that is used to initialize and
   // start user sessions as well as responsible on launching pre-session UI like
   // out-of-box or login.
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 23570ca9..0758f8ba 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -23,7 +23,6 @@
         <structure name="IDR_APP_LIST_START_PAGE_CSS" file="resources\app_list\start_page.css" flattenhtml="true" type="chrome_html" />
         <structure name="IDR_APP_LIST_START_PAGE_HTML" file="resources\app_list\start_page.html" flattenhtml="true" type="chrome_html" />
         <structure name="IDR_APP_LIST_START_PAGE_JS" file="resources\app_list\start_page.js" flattenhtml="true" type="chrome_html" />
-        <structure name="IDR_APP_LIST_HOTWORD_NACL_NMF" file="resources\app_list\hotword_nacl.nmf" flattenhtml="true" type="chrome_html" />
       </if>
       <if expr="is_android">
         <structure name="IDR_CONTEXTUAL_SEARCH_PROMO_CSS" file="resources\contextual_search\promo.css" flattenhtml="true" type="chrome_html" />
@@ -149,7 +148,6 @@
         <!-- Hangout Services extension, included in Google Chrome builds only. -->
         <include name="IDR_HANGOUT_SERVICES_MANIFEST" file="resources\hangout_services\manifest.json" type="BINDATA" />
       </if>
-      <include name="IDR_HOTWORD_HELPER_MANIFEST" file="resources\hotword_helper\manifest.json" type="BINDATA" />
       <include name="IDR_HOTWORD_MANIFEST" file="resources\hotword\manifest.json" type="BINDATA" />
       <include name="IDR_HOTWORD_AUDIO_VERIFICATION_MANIFEST" file="resources\hotword_audio_verification\manifest.json" type="BINDATA" />
       <if expr="not is_android">
@@ -448,6 +446,7 @@
       <include name="IDR_MD_SETTINGS_UI_HTML" file="resources\md_settings\md_settings.html" type="BINDATA" />
       <include name="IDR_MD_SETTINGS_UI_CSS" file="resources\md_settings\md_settings.css" type="BINDATA" />
       <if expr="enable_media_router">
+        <include name="IDR_MEDIA_ROUTER_DATA_JS" file="resources\media_router\media_router_data.js" type="BINDATA" />
         <include name="IDR_MEDIA_ROUTER_CHROMECAST_ICON" file="resources\media_router\elements\icon\chromecast-icon.png" type="BINDATA" />
         <include name="IDR_MEDIA_ROUTER_CHROMECAST_2X_ICON" file="resources\media_router\elements\icon\chromecast-icon2x.png" type="BINDATA" />
         <include name="IDR_CLOSE_GRAY_ICON" file="resources\media_router\elements\icon\close-gray.png" type="BINDATA" />
diff --git a/chrome/browser/browsing_data/browsing_data_remover.cc b/chrome/browser/browsing_data/browsing_data_remover.cc
index 3d7bd207..1539390 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/browsing_data/storage_partition_http_cache_data_remover.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/domain_reliability/service_factory.h"
 #include "chrome/browser/download/download_prefs.h"
@@ -54,28 +55,20 @@
 #include "components/search_engines/template_url_service.h"
 #include "components/web_cache/browser/web_cache_manager.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/dom_storage_context.h"
 #include "content/public/browser/download_manager.h"
-#include "content/public/browser/local_storage_usage_info.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/plugin_data_remover.h"
-#include "content/public/browser/session_storage_usage_info.h"
 #include "content/public/browser/ssl_host_state_delegate.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/user_metrics.h"
 #include "net/base/net_errors.h"
-#include "net/base/sdch_manager.h"
 #include "net/cookies/cookie_store.h"
-#include "net/disk_cache/disk_cache.h"
-#include "net/http/http_cache.h"
 #include "net/http/transport_security_state.h"
 #include "net/ssl/channel_id_service.h"
 #include "net/ssl/channel_id_store.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
-#include "storage/browser/quota/quota_manager.h"
 #include "storage/browser/quota/special_storage_policy.h"
-#include "storage/common/quota/quota_types.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -208,8 +201,6 @@
     : profile_(profile),
       delete_begin_(delete_begin),
       delete_end_(delete_end),
-      next_cache_state_(STATE_NONE),
-      cache_(NULL),
       main_context_getter_(profile->GetRequestContext()),
       media_context_getter_(profile->GetMediaRequestContext()),
       deauthorize_content_licenses_request_id_(0),
@@ -614,14 +605,15 @@
     // Tell the renderers to clear their cache.
     web_cache::WebCacheManager::GetInstance()->ClearCache();
 
-    // Invoke DoClearCache on the IO thread.
-    waiting_for_clear_cache_ = true;
     content::RecordAction(UserMetricsAction("ClearBrowsingData_Cache"));
 
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE,
-        base::Bind(&BrowsingDataRemover::ClearCacheOnIOThread,
-                   base::Unretained(this)));
+    waiting_for_clear_cache_ = true;
+    // StoragePartitionHttpCacheDataRemover deletes itself when it is done.
+    StoragePartitionHttpCacheDataRemover::CreateForRange(
+        BrowserContext::GetDefaultStoragePartition(profile_), delete_begin_,
+        delete_end_)
+        ->Remove(base::Bind(&BrowsingDataRemover::ClearedCache,
+                            base::Unretained(this)));
 
 #if !defined(DISABLE_NACL)
     waiting_for_clear_nacl_cache_ = true;
@@ -963,98 +955,6 @@
   NotifyAndDeleteIfDone();
 }
 
-void BrowsingDataRemover::ClearCacheOnIOThread() {
-  // This function should be called on the IO thread.
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK_EQ(STATE_NONE, next_cache_state_);
-  DCHECK(main_context_getter_.get());
-  DCHECK(media_context_getter_.get());
-
-  next_cache_state_ = STATE_CREATE_MAIN;
-  DoClearCache(net::OK);
-}
-
-// The expected state sequence is STATE_NONE --> STATE_CREATE_MAIN -->
-// STATE_DELETE_MAIN --> STATE_CREATE_MEDIA --> STATE_DELETE_MEDIA -->
-// STATE_DONE, and any errors are ignored.
-void BrowsingDataRemover::DoClearCache(int rv) {
-  DCHECK_NE(STATE_NONE, next_cache_state_);
-
-  while (rv != net::ERR_IO_PENDING && next_cache_state_ != STATE_NONE) {
-    switch (next_cache_state_) {
-      case STATE_CREATE_MAIN:
-      case STATE_CREATE_MEDIA: {
-        // Get a pointer to the cache.
-        net::URLRequestContextGetter* getter =
-            (next_cache_state_ == STATE_CREATE_MAIN)
-                ? main_context_getter_.get()
-                : media_context_getter_.get();
-        net::HttpCache* http_cache =
-            getter->GetURLRequestContext()->http_transaction_factory()->
-                GetCache();
-
-        next_cache_state_ = (next_cache_state_ == STATE_CREATE_MAIN) ?
-                                STATE_DELETE_MAIN : STATE_DELETE_MEDIA;
-
-        // Clear QUIC server information from memory and the disk cache.
-        http_cache->GetSession()->quic_stream_factory()->
-            ClearCachedStatesInCryptoConfig();
-
-        // Clear SDCH dictionary state.
-        net::SdchManager* sdch_manager =
-            getter->GetURLRequestContext()->sdch_manager();
-        // The test is probably overkill, since chrome should always have an
-        // SdchManager.  But in general the URLRequestContext  is *not*
-        // guaranteed to have an SdchManager, so checking is wise.
-        if (sdch_manager)
-          sdch_manager->ClearData();
-
-        rv = http_cache->GetBackend(
-            &cache_, base::Bind(&BrowsingDataRemover::DoClearCache,
-                                base::Unretained(this)));
-        break;
-      }
-      case STATE_DELETE_MAIN:
-      case STATE_DELETE_MEDIA: {
-        next_cache_state_ = (next_cache_state_ == STATE_DELETE_MAIN) ?
-                                STATE_CREATE_MEDIA : STATE_DONE;
-
-        // |cache_| can be null if it cannot be initialized.
-        if (cache_) {
-          if (delete_begin_.is_null()) {
-            rv = cache_->DoomAllEntries(
-                base::Bind(&BrowsingDataRemover::DoClearCache,
-                           base::Unretained(this)));
-          } else {
-            rv = cache_->DoomEntriesBetween(
-                delete_begin_, delete_end_,
-                base::Bind(&BrowsingDataRemover::DoClearCache,
-                           base::Unretained(this)));
-          }
-          cache_ = NULL;
-        }
-        break;
-      }
-      case STATE_DONE: {
-        cache_ = NULL;
-        next_cache_state_ = STATE_NONE;
-
-        // Notify the UI thread that we are done.
-        BrowserThread::PostTask(
-            BrowserThread::UI, FROM_HERE,
-            base::Bind(&BrowsingDataRemover::ClearedCache,
-                       base::Unretained(this)));
-        return;
-      }
-      default: {
-        NOTREACHED() << "bad state";
-        next_cache_state_ = STATE_NONE;  // Stop looping.
-        return;
-      }
-    }
-  }
-}
-
 #if !defined(DISABLE_NACL)
 void BrowsingDataRemover::ClearedNaClCache() {
   // This function should be called on the UI thread.
diff --git a/chrome/browser/browsing_data/browsing_data_remover.h b/chrome/browser/browsing_data/browsing_data_remover.h
index 84160cb..0e470f2 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.h
+++ b/chrome/browser/browsing_data/browsing_data_remover.h
@@ -23,7 +23,6 @@
 #include "storage/common/quota/quota_types.h"
 #include "url/gurl.h"
 
-class ExtensionSpecialStoragePolicy;
 class IOThread;
 class Profile;
 
@@ -36,24 +35,10 @@
 class StoragePartition;
 }
 
-namespace disk_cache {
-class Backend;
-}
-
 namespace net {
 class URLRequestContextGetter;
 }
 
-namespace storage {
-class QuotaManager;
-}
-
-namespace content {
-class DOMStorageContext;
-struct LocalStorageUsageInfo;
-struct SessionStorageUsageInfo;
-}
-
 // BrowsingDataRemover is responsible for removing data related to browsing:
 // visits in url database, downloads, cookies ...
 
@@ -237,15 +222,6 @@
   // TODO(mkwst): See http://crbug.com/113621
   friend class BrowsingDataRemoverTest;
 
-  enum CacheState {
-    STATE_NONE,
-    STATE_CREATE_MAIN,
-    STATE_CREATE_MEDIA,
-    STATE_DELETE_MAIN,
-    STATE_DELETE_MEDIA,
-    STATE_DONE
-  };
-
   // Setter for |is_removing_|; DCHECKs that we can only start removing if we're
   // not already removing, and vice-versa.
   static void set_removing(bool is_removing);
@@ -332,13 +308,6 @@
   // Callback for when the cache has been deleted. Invokes
   // NotifyAndDeleteIfDone.
   void ClearedCache();
-
-  // Invoked on the IO thread to delete from the cache.
-  void ClearCacheOnIOThread();
-
-  // Performs the actual work to delete the cache.
-  void DoClearCache(int rv);
-
 #if !defined(DISABLE_NACL)
   // Callback for when the NaCl cache has been deleted. Invokes
   // NotifyAndDeleteIfDone.
@@ -417,9 +386,6 @@
   // to artificially delay completion. Used for testing.
   static CompletionInhibitor* completion_inhibitor_;
 
-  CacheState next_cache_state_;
-  disk_cache::Backend* cache_;
-
   // Used to delete data from HTTP cache.
   scoped_refptr<net::URLRequestContextGetter> main_context_getter_;
   scoped_refptr<net::URLRequestContextGetter> media_context_getter_;
diff --git a/chrome/browser/browsing_data/storage_partition_http_cache_data_remover.cc b/chrome/browser/browsing_data/storage_partition_http_cache_data_remover.cc
new file mode 100644
index 0000000..d92bf43
--- /dev/null
+++ b/chrome/browser/browsing_data/storage_partition_http_cache_data_remover.cc
@@ -0,0 +1,156 @@
+// 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/browsing_data/storage_partition_http_cache_data_remover.h"
+
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
+#include "net/disk_cache/disk_cache.h"
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using content::BrowserThread;
+
+StoragePartitionHttpCacheDataRemover::StoragePartitionHttpCacheDataRemover(
+    base::Time delete_begin,
+    base::Time delete_end,
+    net::URLRequestContextGetter* main_context_getter,
+    net::URLRequestContextGetter* media_context_getter)
+    : delete_begin_(delete_begin),
+      delete_end_(delete_end),
+      main_context_getter_(main_context_getter),
+      media_context_getter_(media_context_getter),
+      next_cache_state_(STATE_NONE),
+      cache_(nullptr) {
+}
+
+StoragePartitionHttpCacheDataRemover::~StoragePartitionHttpCacheDataRemover() {
+}
+
+// static.
+StoragePartitionHttpCacheDataRemover*
+StoragePartitionHttpCacheDataRemover::CreateForRange(
+    content::StoragePartition* storage_partition,
+    base::Time delete_begin,
+    base::Time delete_end) {
+  return new StoragePartitionHttpCacheDataRemover(
+      delete_begin, delete_end, storage_partition->GetURLRequestContext(),
+      storage_partition->GetMediaURLRequestContext());
+}
+
+void StoragePartitionHttpCacheDataRemover::Remove(
+    const base::Closure& done_callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(!done_callback.is_null());
+  done_callback_ = done_callback;
+
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(
+          &StoragePartitionHttpCacheDataRemover::ClearHttpCacheOnIOThread,
+          base::Unretained(this)));
+}
+
+void StoragePartitionHttpCacheDataRemover::ClearHttpCacheOnIOThread() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  next_cache_state_ = STATE_NONE;
+  DCHECK_EQ(STATE_NONE, next_cache_state_);
+  DCHECK(main_context_getter_.get());
+  DCHECK(media_context_getter_.get());
+
+  next_cache_state_ = STATE_CREATE_MAIN;
+  DoClearCache(net::OK);
+}
+
+void StoragePartitionHttpCacheDataRemover::ClearedHttpCache() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  done_callback_.Run();
+  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+// The expected state sequence is STATE_NONE --> STATE_CREATE_MAIN -->
+// STATE_DELETE_MAIN --> STATE_CREATE_MEDIA --> STATE_DELETE_MEDIA -->
+// STATE_DONE, and any errors are ignored.
+void StoragePartitionHttpCacheDataRemover::DoClearCache(int rv) {
+  DCHECK_NE(STATE_NONE, next_cache_state_);
+
+  while (rv != net::ERR_IO_PENDING && next_cache_state_ != STATE_NONE) {
+    switch (next_cache_state_) {
+      case STATE_CREATE_MAIN:
+      case STATE_CREATE_MEDIA: {
+        // Get a pointer to the cache.
+        net::URLRequestContextGetter* getter =
+            (next_cache_state_ == STATE_CREATE_MAIN)
+                ? main_context_getter_.get()
+                : media_context_getter_.get();
+        net::HttpCache* http_cache = getter->GetURLRequestContext()
+                                         ->http_transaction_factory()
+                                         ->GetCache();
+
+        next_cache_state_ = (next_cache_state_ == STATE_CREATE_MAIN)
+                                ? STATE_DELETE_MAIN
+                                : STATE_DELETE_MEDIA;
+
+        // Clear QUIC server information from memory and the disk cache.
+        http_cache->GetSession()
+            ->quic_stream_factory()
+            ->ClearCachedStatesInCryptoConfig();
+
+        // Clear SDCH dictionary state.
+        net::SdchManager* sdch_manager =
+            getter->GetURLRequestContext()->sdch_manager();
+        // The test is probably overkill, since chrome should always have an
+        // SdchManager.  But in general the URLRequestContext  is *not*
+        // guaranteed to have an SdchManager, so checking is wise.
+        if (sdch_manager)
+          sdch_manager->ClearData();
+
+        rv = http_cache->GetBackend(
+            &cache_,
+            base::Bind(&StoragePartitionHttpCacheDataRemover::DoClearCache,
+                       base::Unretained(this)));
+        break;
+      }
+      case STATE_DELETE_MAIN:
+      case STATE_DELETE_MEDIA: {
+        next_cache_state_ = (next_cache_state_ == STATE_DELETE_MAIN)
+                                ? STATE_CREATE_MEDIA
+                                : STATE_DONE;
+
+        // |cache_| can be null if it cannot be initialized.
+        if (cache_) {
+          if (delete_begin_.is_null()) {
+            rv = cache_->DoomAllEntries(
+                base::Bind(&StoragePartitionHttpCacheDataRemover::DoClearCache,
+                           base::Unretained(this)));
+          } else {
+            rv = cache_->DoomEntriesBetween(
+                delete_begin_, delete_end_,
+                base::Bind(&StoragePartitionHttpCacheDataRemover::DoClearCache,
+                           base::Unretained(this)));
+          }
+          cache_ = NULL;
+        }
+        break;
+      }
+      case STATE_DONE: {
+        cache_ = NULL;
+        next_cache_state_ = STATE_NONE;
+
+        // Notify the UI thread that we are done.
+        BrowserThread::PostTask(
+            BrowserThread::UI, FROM_HERE,
+            base::Bind(&StoragePartitionHttpCacheDataRemover::ClearedHttpCache,
+                       base::Unretained(this)));
+        return;
+      }
+      default: {
+        NOTREACHED() << "bad state";
+        next_cache_state_ = STATE_NONE;  // Stop looping.
+        return;
+      }
+    }
+  }
+}
diff --git a/chrome/browser/browsing_data/storage_partition_http_cache_data_remover.h b/chrome/browser/browsing_data/storage_partition_http_cache_data_remover.h
new file mode 100644
index 0000000..b09e31e
--- /dev/null
+++ b/chrome/browser/browsing_data/storage_partition_http_cache_data_remover.h
@@ -0,0 +1,78 @@
+// 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_BROWSING_DATA_STORAGE_PARTITION_HTTP_CACHE_DATA_REMOVER_H_
+#define CHROME_BROWSER_BROWSING_DATA_STORAGE_PARTITION_HTTP_CACHE_DATA_REMOVER_H_
+
+#include "base/callback.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "base/time/time.h"
+
+namespace content {
+class StoragePartition;
+}
+
+namespace disk_cache {
+class Backend;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+// Helper to remove http cache data from a StoragePartition.
+class StoragePartitionHttpCacheDataRemover {
+ public:
+  static StoragePartitionHttpCacheDataRemover* CreateForRange(
+      content::StoragePartition* storage_partition,
+      base::Time delete_begin,
+      base::Time delete_end);
+
+  // Calls |done_callback| upon completion and also destroys itself.
+  void Remove(const base::Closure& done_callback);
+
+ private:
+  enum CacheState {
+    STATE_NONE,
+    STATE_CREATE_MAIN,
+    STATE_CREATE_MEDIA,
+    STATE_DELETE_MAIN,
+    STATE_DELETE_MEDIA,
+    STATE_DONE
+  };
+
+  StoragePartitionHttpCacheDataRemover(
+      base::Time delete_begin,
+      base::Time delete_end,
+      net::URLRequestContextGetter* main_context_getter,
+      net::URLRequestContextGetter* media_context_getter);
+
+  // StoragePartitionHttpCacheDataRemover deletes itself (using DeleteHelper)
+  // and is not supposed to be deleted by other objects so make destructor
+  // private and DeleteHelper a friend.
+  friend class base::DeleteHelper<StoragePartitionHttpCacheDataRemover>;
+
+  ~StoragePartitionHttpCacheDataRemover();
+
+  void ClearHttpCacheOnIOThread();
+  void ClearedHttpCache();
+  // Performs the actual work to delete the cache.
+  void DoClearCache(int rv);
+
+  const base::Time delete_begin_;
+  const base::Time delete_end_;
+
+  const scoped_refptr<net::URLRequestContextGetter> main_context_getter_;
+  const scoped_refptr<net::URLRequestContextGetter> media_context_getter_;
+
+  base::Closure done_callback_;
+
+  // IO.
+  int next_cache_state_;
+  disk_cache::Backend* cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(StoragePartitionHttpCacheDataRemover);
+};
+
+#endif  // CHROME_BROWSER_BROWSING_DATA_STORAGE_PARTITION_HTTP_CACHE_DATA_REMOVER_H_
diff --git a/chrome/browser/character_encoding.cc b/chrome/browser/character_encoding.cc
index e9e2521..91ac5505a 100644
--- a/chrome/browser/character_encoding.cc
+++ b/chrome/browser/character_encoding.cc
@@ -40,7 +40,6 @@
   { IDC_ENCODING_GBK, "GBK", IDS_ENCODING_SIMP_CHINESE },
   { IDC_ENCODING_GB18030, "gb18030", IDS_ENCODING_SIMP_CHINESE },
   { IDC_ENCODING_BIG5, "Big5", IDS_ENCODING_TRAD_CHINESE },
-  { IDC_ENCODING_BIG5HKSCS, "Big5-HKSCS", IDS_ENCODING_TRAD_CHINESE },
   { IDC_ENCODING_KOREAN, "EUC-KR", IDS_ENCODING_KOREAN },
   { IDC_ENCODING_SHIFTJIS, "Shift_JIS", IDS_ENCODING_JAPANESE },
   { IDC_ENCODING_EUCJP, "EUC-JP", IDS_ENCODING_JAPANESE },
@@ -54,6 +53,7 @@
   { IDC_ENCODING_WINDOWS1251, "windows-1251", IDS_ENCODING_CYRILLIC },
   { IDC_ENCODING_KOI8R, "KOI8-R", IDS_ENCODING_CYRILLIC },
   { IDC_ENCODING_KOI8U, "KOI8-U", IDS_ENCODING_CYRILLIC },
+  { IDC_ENCODING_IBM866, "IBM866", IDS_ENCODING_CYRILLIC },
   { IDC_ENCODING_ISO88597, "ISO-8859-7", IDS_ENCODING_GREEK },
   { IDC_ENCODING_WINDOWS1253, "windows-1253", IDS_ENCODING_GREEK },
   { IDC_ENCODING_WINDOWS1254, "windows-1254", IDS_ENCODING_TURKISH },
@@ -194,7 +194,6 @@
   IDC_ENCODING_GBK,
   IDC_ENCODING_GB18030,
   IDC_ENCODING_BIG5,
-  IDC_ENCODING_BIG5HKSCS,
   IDC_ENCODING_KOREAN,
   IDC_ENCODING_SHIFTJIS,
   IDC_ENCODING_EUCJP,
@@ -208,6 +207,7 @@
   IDC_ENCODING_WINDOWS1251,
   IDC_ENCODING_KOI8R,
   IDC_ENCODING_KOI8U,
+  IDC_ENCODING_IBM866,
   IDC_ENCODING_ISO88597,
   IDC_ENCODING_WINDOWS1253,
   IDC_ENCODING_WINDOWS1254,
diff --git a/chrome/browser/chrome_browser_field_trials.cc b/chrome/browser/chrome_browser_field_trials.cc
index 1571bd8..55f83b7 100644
--- a/chrome/browser/chrome_browser_field_trials.cc
+++ b/chrome/browser/chrome_browser_field_trials.cc
@@ -47,7 +47,6 @@
   // The following trials are used from renderer process.
   // Mark here so they will be sync-ed.
   base::FieldTrialList::FindValue("CLD1VsCLD2");
-  base::FieldTrialList::FindValue("MouseEventPreconnect");
   base::FieldTrialList::FindValue("DisplayList2dCanvas");
   // Activate the autocomplete dynamic field trials.
   OmniboxFieldTrial::ActivateDynamicTrials();
diff --git a/chrome/browser/chrome_browser_main_mac.mm b/chrome/browser/chrome_browser_main_mac.mm
index 98c1591..e37d3a5 100644
--- a/chrome/browser/chrome_browser_main_mac.mm
+++ b/chrome/browser/chrome_browser_main_mac.mm
@@ -5,14 +5,12 @@
 #include "chrome/browser/chrome_browser_main_mac.h"
 
 #import <Cocoa/Cocoa.h>
-#include <sys/sysctl.h>
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/mac/bundle_locations.h"
 #include "base/mac/mac_util.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
 #include "base/path_service.h"
 #import "chrome/browser/app_controller_mac.h"
 #include "chrome/browser/apps/app_shim/app_shim_host_manager_mac.h"
@@ -32,125 +30,6 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/resource/resource_handle.h"
 
-namespace {
-
-// This is one enum instead of two so that the values can be correlated in a
-// histogram.
-enum CatSixtyFour {
-  // Older than any expected cat.
-  SABER_TOOTHED_CAT_32 = 0,
-  SABER_TOOTHED_CAT_64,
-
-  // Known cats.
-  SNOW_LEOPARD_32,
-  SNOW_LEOPARD_64,
-  LION_32,  // Unexpected, Lion requires a 64-bit CPU.
-  LION_64,
-  MOUNTAIN_LION_32,  // Unexpected, Mountain Lion requires a 64-bit CPU.
-  MOUNTAIN_LION_64,
-  MAVERICKS_32,  // Unexpected, Mavericks requires a 64-bit CPU.
-  MAVERICKS_64,
-
-  // DON'T add new constants here. It's important to keep the constant values,
-  // um, constant. Add new constants at the bottom.
-
-  // What if the bitsiness of the CPU can't be determined?
-  SABER_TOOTHED_CAT_DUNNO,
-  SNOW_LEOPARD_DUNNO,
-  LION_DUNNO,
-  MOUNTAIN_LION_DUNNO,
-  MAVERICKS_DUNNO,
-
-  // More known cats.
-  YOSEMITE_32,  // Unexpected, Yosemite requires a 64-bit CPU.
-  YOSEMITE_64,
-  YOSEMITE_DUNNO,
-
-  // Newer than any known cat.
-  FUTURE_CAT_32,  // Unexpected, it's unlikely Apple will un-obsolete old CPUs.
-  FUTURE_CAT_64,
-  FUTURE_CAT_DUNNO,
-
-  // As new versions of Mac OS X are released with sillier and sillier names,
-  // rename the FUTURE_CAT enum values to match those names, and re-create
-  // FUTURE_CAT_[32|64|DUNNO] here.
-
-  CAT_SIXTY_FOUR_MAX
-};
-
-CatSixtyFour CatSixtyFourValue() {
-#if defined(ARCH_CPU_64_BITS)
-  // If 64-bit code is running, then it's established that this CPU can run
-  // 64-bit code, and no further inquiry is necessary.
-  int cpu64 = 1;
-  bool cpu64_known = true;
-#else
-  // Check a sysctl conveniently provided by the kernel that identifies
-  // whether the CPU supports 64-bit operation. Note that this tests the
-  // actual hardware capabilities, not the bitsiness of the running process,
-  // and not the bitsiness of the running kernel. The value thus determines
-  // whether the CPU is capable of running 64-bit programs (in the presence of
-  // proper OS runtime support) without regard to whether the current program
-  // is 64-bit (it may not be) or whether the current kernel is (the kernel
-  // can launch cross-bitted user-space tasks).
-
-  int cpu64;
-  size_t len = sizeof(cpu64);
-  const char kSysctlName[] = "hw.cpu64bit_capable";
-  bool cpu64_known = sysctlbyname(kSysctlName, &cpu64, &len, NULL, 0) == 0;
-  if (!cpu64_known) {
-    PLOG(WARNING) << "sysctlbyname(\"" << kSysctlName << "\")";
-  }
-#endif
-
-  if (base::mac::IsOSSnowLeopard()) {
-    return cpu64_known ? (cpu64 ? SNOW_LEOPARD_64 : SNOW_LEOPARD_32) :
-                         SNOW_LEOPARD_DUNNO;
-  }
-  if (base::mac::IsOSLion()) {
-    return cpu64_known ? (cpu64 ? LION_64 : LION_32) :
-                         LION_DUNNO;
-  }
-  if (base::mac::IsOSMountainLion()) {
-    return cpu64_known ? (cpu64 ? MOUNTAIN_LION_64 : MOUNTAIN_LION_32) :
-                         MOUNTAIN_LION_DUNNO;
-  }
-  if (base::mac::IsOSMavericks()) {
-    return cpu64_known ? (cpu64 ? MAVERICKS_64 : MAVERICKS_32) :
-                         MAVERICKS_DUNNO;
-  }
-  if (base::mac::IsOSYosemite()) {
-    return cpu64_known ? (cpu64 ? YOSEMITE_64 : YOSEMITE_32) :
-                         YOSEMITE_DUNNO;
-  }
-  if (base::mac::IsOSLaterThanYosemite_DontCallThis()) {
-    return cpu64_known ? (cpu64 ? FUTURE_CAT_64 : FUTURE_CAT_32) :
-                         FUTURE_CAT_DUNNO;
-  }
-
-  // If it's not any of the expected OS versions or later than them, it must
-  // be prehistoric.
-  return cpu64_known ? (cpu64 ? SABER_TOOTHED_CAT_64 : SABER_TOOTHED_CAT_32) :
-                       SABER_TOOTHED_CAT_DUNNO;
-}
-
-void RecordCatSixtyFour() {
-  CatSixtyFour cat_sixty_four = CatSixtyFourValue();
-
-  // Set this higher than the highest value in the CatSixtyFour enum to provide
-  // some headroom and then leave it alone. See UMA_HISTOGRAM_ENUMERATION in
-  // base/metrics/histogram.h.
-  const int kMaxCatsAndSixtyFours = 32;
-  static_assert(kMaxCatsAndSixtyFours >= CAT_SIXTY_FOUR_MAX,
-                "kMaxCatsAndSixtyFours is too large");
-
-  UMA_HISTOGRAM_ENUMERATION("OSX.CatSixtyFour",
-                            cat_sixty_four,
-                            kMaxCatsAndSixtyFours);
-}
-
-}  // namespace
-
 // ChromeBrowserMainPartsMac ---------------------------------------------------
 
 ChromeBrowserMainPartsMac::ChromeBrowserMainPartsMac(
@@ -173,8 +52,6 @@
         base::CommandLine::ForCurrentProcess();
     singleton_command_line->AppendSwitch(switches::kNoStartupWindow);
   }
-
-  RecordCatSixtyFour();
 }
 
 void ChromeBrowserMainPartsMac::PreMainMessageLoopStart() {
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 836c59e..64b6426 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -60,6 +60,7 @@
 #include "chrome/browser/push_messaging/push_messaging_permission_context_factory.h"
 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
 #include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/browser/search/instant_service_factory.h"
 #include "chrome/browser/search/search.h"
@@ -153,6 +154,7 @@
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
 #include "chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/system/input_device_settings.h"
 #include "chromeos/chromeos_switches.h"
 #include "components/user_manager/user_manager.h"
@@ -609,18 +611,18 @@
 PermissionContextBase* GetPermissionContext(Profile* profile,
     content::PermissionType permission) {
   switch (permission) {
-    case content::PERMISSION_MIDI_SYSEX:
+    case content::PermissionType::MIDI_SYSEX:
       return MidiPermissionContextFactory::GetForProfile(profile);
-    case content::PERMISSION_NOTIFICATIONS:
+    case content::PermissionType::NOTIFICATIONS:
 #if defined(ENABLE_NOTIFICATIONS)
       return DesktopNotificationServiceFactory::GetForProfile(profile);
 #else
       NOTIMPLEMENTED();
       break;
 #endif
-    case content::PERMISSION_GEOLOCATION:
+    case content::PermissionType::GEOLOCATION:
       return GeolocationPermissionContextFactory::GetForProfile(profile);
-    case content::PERMISSION_PROTECTED_MEDIA_IDENTIFIER:
+    case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
       return ProtectedMediaIdentifierPermissionContextFactory::GetForProfile(
           profile);
@@ -628,10 +630,11 @@
       NOTIMPLEMENTED();
       break;
 #endif
-    case content::PERMISSION_PUSH_MESSAGING:
+    case content::PermissionType::PUSH_MESSAGING:
       return PushMessagingPermissionContextFactory::GetForProfile(profile);
-    case content::PERMISSION_NUM:
-      NOTREACHED() << "Invalid RequestPermission for " << permission;
+    case content::PermissionType::NUM:
+      NOTREACHED() << "Invalid RequestPermission for "
+                   << static_cast<int>(permission);
       break;
   }
   return nullptr;
@@ -641,20 +644,21 @@
 ContentSettingsType PermissionToContentSetting(
     content::PermissionType permission) {
   switch (permission) {
-    case content::PERMISSION_MIDI_SYSEX:
+    case content::PermissionType::MIDI_SYSEX:
       return CONTENT_SETTINGS_TYPE_MIDI_SYSEX;
-    case content::PERMISSION_PUSH_MESSAGING:
+    case content::PermissionType::PUSH_MESSAGING:
       return CONTENT_SETTINGS_TYPE_PUSH_MESSAGING;
-    case content::PERMISSION_NOTIFICATIONS:
+    case content::PermissionType::NOTIFICATIONS:
       return CONTENT_SETTINGS_TYPE_NOTIFICATIONS;
-    case content::PERMISSION_GEOLOCATION:
+    case content::PermissionType::GEOLOCATION:
       return CONTENT_SETTINGS_TYPE_GEOLOCATION;
 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
-    case content::PERMISSION_PROTECTED_MEDIA_IDENTIFIER:
+    case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
       return CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER;
 #endif
     default:
-      NOTREACHED() << "Unknown content setting for permission " << permission;
+      NOTREACHED() << "Unknown content setting for permission "
+                   << static_cast<int>(permission);
       return CONTENT_SETTINGS_TYPE_DEFAULT;
   }
 }
@@ -795,9 +799,10 @@
   // SiteInstance URL - "chrome-guest://app_id/persist?partition".
   if (site.SchemeIs(content::kGuestScheme)) {
     partition_id = site.spec();
-  } else if (site.GetOrigin().spec() == kChromeUIChromeSigninURL) {
-    // Chrome signin page has an embedded iframe of extension and web content,
-    // thus it must be isolated from other webUI pages.
+  } else if (!switches::IsEnableWebviewBasedSignin() &&
+             site.GetOrigin().spec() == kChromeUIChromeSigninURL) {
+    // The non-webview Chrome signin page has an embedded iframe of extension
+    // and web content, thus it must be isolated from other webUI pages.
     partition_id = site.GetOrigin().spec();
   }
 
@@ -859,9 +864,10 @@
   }
 #endif
 
-  if (!success && (site.GetOrigin().spec() == kChromeUIChromeSigninURL)) {
-    // Chrome signin page has an embedded iframe of extension and web content,
-    // thus it must be isolated from other webUI pages.
+  if (!success && (!switches::IsEnableWebviewBasedSignin() &&
+                   site.GetOrigin().spec() == kChromeUIChromeSigninURL)) {
+    // The non-webview Chrome signin page has an embedded iframe of extension
+    // and web content, thus it must be isolated from other webUI pages.
     *partition_domain = chrome::kChromeUIChromeSigninHost;
   }
 
@@ -1280,6 +1286,12 @@
 #endif
 
 #if defined(OS_CHROMEOS)
+  static const char* const kChromeOSSwitches[] = {
+    switches::kDisableDinosaurEasterEgg,
+  };
+  command_line->CopySwitchesFrom(browser_command_line, kChromeOSSwitches,
+                                 arraysize(kChromeOSSwitches));
+
   // On Chrome OS need to pass primary user homedir (in multi-profiles session).
   base::FilePath homedir;
   PathService::Get(base::DIR_HOME, &homedir);
@@ -1792,8 +1804,13 @@
   if (expired_previous_decision)
     options_mask |= SSLBlockingPage::EXPIRED_BUT_PREVIOUSLY_ALLOWED;
 
+  SafeBrowsingService* safe_browsing_service =
+      g_browser_process->safe_browsing_service();
   SSLErrorHandler::HandleSSLError(
-      tab, cert_error, ssl_info, request_url, options_mask, callback);
+      tab, cert_error, ssl_info, request_url, options_mask,
+      safe_browsing_service ? safe_browsing_service->ui_manager().get()
+                            : nullptr,
+      callback);
 }
 
 void ChromeContentBrowserClient::SelectClientCertificate(
@@ -2329,7 +2346,6 @@
       new drive::FileSystemBackendDelegate,
       new chromeos::file_system_provider::BackendDelegate,
       new chromeos::MTPFileSystemBackendDelegate(storage_partition_path),
-      browser_context->GetSpecialStoragePolicy(),
       external_mount_points,
       storage::ExternalMountPoints::GetSystemInstance());
   backend->AddSystemMountPoints();
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index c0235f1..f1175a1f 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -16,11 +16,14 @@
 #include "components/variations/entropy_provider.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/permission_type.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+using content::PermissionType;
+
 namespace chrome {
 
 using ChromeContentBrowserClientTest = testing::Test;
@@ -201,7 +204,7 @@
  public:
   PermissionBrowserClientTest() : url_("https://www.google.com") {}
 
-  void CheckPermissionStatus(content::PermissionType type,
+  void CheckPermissionStatus(PermissionType type,
                              content::PermissionStatus expected) {
     EXPECT_EQ(expected, client_.GetPermissionStatus(type, &profile_,
                                                     url_.GetOrigin(),
@@ -224,12 +227,12 @@
 
 TEST_F(PermissionBrowserClientTest, GetPermissionStatusDefault) {
   using namespace content;
-  CheckPermissionStatus(PERMISSION_MIDI_SYSEX, PERMISSION_STATUS_ASK);
-  CheckPermissionStatus(PERMISSION_PUSH_MESSAGING, PERMISSION_STATUS_ASK);
-  CheckPermissionStatus(PERMISSION_NOTIFICATIONS, PERMISSION_STATUS_ASK);
-  CheckPermissionStatus(PERMISSION_GEOLOCATION, PERMISSION_STATUS_ASK);
+  CheckPermissionStatus(PermissionType::MIDI_SYSEX, PERMISSION_STATUS_ASK);
+  CheckPermissionStatus(PermissionType::PUSH_MESSAGING, PERMISSION_STATUS_ASK);
+  CheckPermissionStatus(PermissionType::NOTIFICATIONS, PERMISSION_STATUS_ASK);
+  CheckPermissionStatus(PermissionType::GEOLOCATION, PERMISSION_STATUS_ASK);
 #if defined(OS_ANDROID)
-  CheckPermissionStatus(PERMISSION_PROTECTED_MEDIA_IDENTIFIER,
+  CheckPermissionStatus(PermissionType::PROTECTED_MEDIA_IDENTIFIER,
                         PERMISSION_STATUS_ASK);
 #endif
 }
@@ -237,21 +240,23 @@
 TEST_F(PermissionBrowserClientTest, GetPermissionStatusAfterSet) {
   using namespace content;
   SetPermission(CONTENT_SETTINGS_TYPE_GEOLOCATION, CONTENT_SETTING_ALLOW);
-  CheckPermissionStatus(PERMISSION_GEOLOCATION, PERMISSION_STATUS_GRANTED);
+  CheckPermissionStatus(PermissionType::GEOLOCATION, PERMISSION_STATUS_GRANTED);
 
   SetPermission(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_ALLOW);
-  CheckPermissionStatus(PERMISSION_NOTIFICATIONS, PERMISSION_STATUS_GRANTED);
+  CheckPermissionStatus(PermissionType::NOTIFICATIONS,
+                        PERMISSION_STATUS_GRANTED);
 
   SetPermission(CONTENT_SETTINGS_TYPE_MIDI_SYSEX, CONTENT_SETTING_ALLOW);
-  CheckPermissionStatus(PERMISSION_MIDI_SYSEX, PERMISSION_STATUS_GRANTED);
+  CheckPermissionStatus(PermissionType::MIDI_SYSEX, PERMISSION_STATUS_GRANTED);
 
   SetPermission(CONTENT_SETTINGS_TYPE_PUSH_MESSAGING, CONTENT_SETTING_ALLOW);
-  CheckPermissionStatus(PERMISSION_PUSH_MESSAGING, PERMISSION_STATUS_GRANTED);
+  CheckPermissionStatus(PermissionType::PUSH_MESSAGING,
+                        PERMISSION_STATUS_GRANTED);
 
 #if defined(OS_ANDROID)
   SetPermission(CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER,
                 CONTENT_SETTING_ALLOW);
-  CheckPermissionStatus(PERMISSION_PROTECTED_MEDIA_IDENTIFIER,
+  CheckPermissionStatus(PermissionType::PROTECTED_MEDIA_IDENTIFIER,
                         PERMISSION_STATUS_GRANTED);
 #endif
 }
diff --git a/chrome/browser/chrome_site_per_process_browsertest.cc b/chrome/browser/chrome_site_per_process_browsertest.cc
index 1ed8186..10e8783 100644
--- a/chrome/browser/chrome_site_per_process_browsertest.cc
+++ b/chrome/browser/chrome_site_per_process_browsertest.cc
@@ -54,8 +54,11 @@
 // and FileSystem APIs.  These features involve a check on the
 // WebSecurityOrigin of the topmost WebFrame in ContentSettingsObserver, and
 // this test ensures this check works when the top frame is remote.
+//
+// Disabled due to a shutdown race condition that can lead to UAF in the
+// renderer (https://crbug.com/470055).
 IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
-                       OriginReplicationAllowsAccessToStorage) {
+                       DISABLED_OriginReplicationAllowsAccessToStorage) {
   // Navigate to a page with a same-site iframe.
   GURL main_url(embedded_test_server()->GetURL("a.com", "/iframe.html"));
   ui_test_utils::NavigateToURL(browser(), main_url);
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
index f87a773..4172ee0 100644
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
@@ -263,16 +263,15 @@
 IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, FocusToolbar) {
   EnableChromeVox();
   chrome::ExecuteCommand(browser(), IDC_FOCUS_TOOLBAR);
-  EXPECT_TRUE(
-      MatchPattern(speech_monitor_.GetNextUtterance(),
-                   "about:blank*toolbar Reload Button"));
+  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(),
+                           "about:blank*Tool bar Reload Button"));
 }
 
 IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, TypeInOmnibox) {
   EnableChromeVox();
 
   chrome::ExecuteCommand(browser(), IDC_FOCUS_LOCATION);
-  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(), "*Edit text"));
+  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(), "*Edit text*"));
 
   SendKeyPress(ui::VKEY_X);
   EXPECT_EQ("x", speech_monitor_.GetNextUtterance());
@@ -292,8 +291,8 @@
 
   EXPECT_TRUE(PerformAcceleratorAction(ash::FOCUS_SHELF));
   const char* expected = app_list::switches::IsExperimentalAppListEnabled()
-                             ? "Shelf toolbar Launcher Button"
-                             : "Shelf toolbar Apps Button";
+                             ? "Shelf Tool bar Launcher Button"
+                             : "Shelf Tool bar Apps Button";
   EXPECT_EQ(expected, speech_monitor_.GetNextUtterance());
 
   SendKeyPress(ui::VKEY_TAB);
@@ -542,9 +541,8 @@
 
   chrome::ExecuteCommand(browser(), IDC_FOCUS_TOOLBAR);
 
-  EXPECT_TRUE(
-      MatchPattern(speech_monitor_.GetNextUtterance(),
-                   "about:blank*toolbar Reload Button"));
+  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(),
+                           "about:blank*Tool bar Reload Button"));
 }
 
 //
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc
index af29edac..5245aad4 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc
@@ -167,7 +167,7 @@
   }
 
   void NotifyFinishedOnUIThread() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     if (client_)
       client_->OnCrxLoadFinished(this);
@@ -191,7 +191,7 @@
 // KioskAppData::IconLoader
 // Loads locally stored icon data and decode it.
 
-class KioskAppData::IconLoader : public ImageDecoder::Delegate {
+class KioskAppData::IconLoader {
  public:
   enum LoadResult {
     SUCCESS,
@@ -219,7 +219,31 @@
  private:
   friend class base::RefCountedThreadSafe<IconLoader>;
 
-  ~IconLoader() override {}
+  ~IconLoader() {}
+
+  class IconImageRequest : public ImageDecoder::ImageRequest {
+   public:
+    IconImageRequest(
+        const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+        IconLoader* icon_loader)
+        : ImageRequest(task_runner), icon_loader_(icon_loader) {}
+
+    void OnImageDecoded(const SkBitmap& decoded_image) override {
+      icon_loader_->icon_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
+      icon_loader_->icon_.MakeThreadSafe();
+      icon_loader_->ReportResultOnBlockingPool(SUCCESS);
+      delete this;
+    }
+
+    void OnDecodeImageFailed() override {
+      icon_loader_->ReportResultOnBlockingPool(FAILED_TO_DECODE);
+      delete this;
+    }
+
+   private:
+    ~IconImageRequest() override {}
+    IconLoader* icon_loader_;
+  };
 
   // Loads the icon from locally stored |icon_path_| on the blocking pool
   void LoadOnBlockingPool() {
@@ -232,9 +256,8 @@
     }
     raw_icon_ = base::RefCountedString::TakeString(&data);
 
-    scoped_refptr<ImageDecoder> image_decoder = new ImageDecoder(
-        this, raw_icon_->data(), ImageDecoder::DEFAULT_CODEC);
-    image_decoder->Start(task_runner_);
+    IconImageRequest* image_request = new IconImageRequest(task_runner_, this);
+    ImageDecoder::Start(image_request, raw_icon_->data());
   }
 
   void ReportResultOnBlockingPool(LoadResult result) {
@@ -259,24 +282,12 @@
   }
 
   void ReportResultOnUIThread() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     NotifyClient();
     delete this;
   }
 
-  // ImageDecoder::Delegate overrides:
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override {
-    icon_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
-    icon_.MakeThreadSafe();
-    ReportResultOnBlockingPool(SUCCESS);
-  }
-
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override {
-    ReportResultOnBlockingPool(FAILED_TO_DECODE);
-  }
-
   base::WeakPtr<KioskAppData> client_;
   base::FilePath icon_path_;
 
@@ -307,7 +318,6 @@
         new extensions::WebstoreInstallHelper(this,
                                               app_id,
                                               manifest,
-                                              "",  // No icon data.
                                               icon_url,
                                               context_getter);
     webstore_helper->Start();
@@ -533,7 +543,7 @@
 }
 
 void KioskAppData::OnIconLoadSuccess(const gfx::ImageSkia& icon) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   icon_ = icon;
   SetStatus(STATUS_LOADED);
 }
diff --git a/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc b/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc
index 2bfe2e275b..2004ba3 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc
@@ -127,7 +127,7 @@
 KioskProfileLoader::~KioskProfileLoader() {}
 
 void KioskProfileLoader::Start() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   login_performer_.reset();
   cryptohomed_checker_.reset(new CryptohomedChecker(this));
   cryptohomed_checker_->StartCheck();
@@ -139,7 +139,7 @@
 }
 
 void KioskProfileLoader::ReportLaunchResult(KioskAppLaunchError::Error error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error != KioskAppLaunchError::NONE) {
     delegate_->OnProfileLoadFailed(error);
diff --git a/chrome/browser/chromeos/boot_times_recorder.cc b/chrome/browser/chromeos/boot_times_recorder.cc
index 4da58d0e..e9a3cec 100644
--- a/chrome/browser/chromeos/boot_times_recorder.cc
+++ b/chrome/browser/chromeos/boot_times_recorder.cc
@@ -310,7 +310,7 @@
 }
 
 void BootTimesRecorder::LoginDone(bool is_user_new) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (login_done_)
     return;
 
@@ -417,7 +417,7 @@
 }
 
 void BootTimesRecorder::RecordLoginAttempted() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (login_done_)
     return;
 
diff --git a/chrome/browser/chromeos/camera_detector.cc b/chrome/browser/chromeos/camera_detector.cc
index 2bb2a62..8c5deff 100644
--- a/chrome/browser/chromeos/camera_detector.cc
+++ b/chrome/browser/chromeos/camera_detector.cc
@@ -41,7 +41,7 @@
 
 // static
 void CameraDetector::StartPresenceCheck(const base::Closure& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (presence_check_in_progress_)
     return;
   DVLOG(1) << "Starting camera presence check";
@@ -57,7 +57,7 @@
 // static
 void CameraDetector::OnPresenceCheckDone(const base::Closure& callback,
                                          bool present) {
-  DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   camera_presence_ = present ? kCameraPresent : kCameraAbsent;
   presence_check_in_progress_ = false;
   callback.Run();
diff --git a/chrome/browser/chromeos/customization/customization_document.cc b/chrome/browser/chromeos/customization/customization_document.cc
index 9a3b877..9aae363e 100644
--- a/chrome/browser/chromeos/customization/customization_document.cc
+++ b/chrome/browser/chromeos/customization/customization_document.cc
@@ -539,7 +539,7 @@
 void ServicesCustomizationDocument::ReadFileInBackground(
     base::WeakPtr<ServicesCustomizationDocument> self,
     const base::FilePath& file) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
 
   std::string manifest;
   if (!base::ReadFileToString(file, &manifest)) {
@@ -874,7 +874,7 @@
 void ServicesCustomizationDocument::OnCheckedWallpaperCacheExists(
     scoped_ptr<bool> exists,
     scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(exists);
   DCHECK(applying);
 
diff --git a/chrome/browser/chromeos/device/input_service_proxy.cc b/chrome/browser/chromeos/device/input_service_proxy.cc
index 3d27ab1..1a18319 100644
--- a/chrome/browser/chromeos/device/input_service_proxy.cc
+++ b/chrome/browser/chromeos/device/input_service_proxy.cc
@@ -20,7 +20,7 @@
 
 class InputServiceProxy::ServiceObserver : public InputServiceLinux::Observer {
  public:
-  ServiceObserver() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); }
+  ServiceObserver() { DCHECK_CURRENTLY_ON(BrowserThread::UI); }
   ~ServiceObserver() override { DCHECK(CalledOnValidThread()); }
 
   void Initialize(const base::WeakPtr<InputServiceProxy>& proxy) {
@@ -89,7 +89,7 @@
       task_runner_(BrowserThread::GetMessageLoopProxyForThread(
           thread_identifier_)),
       weak_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&InputServiceProxy::ServiceObserver::Initialize,
diff --git a/chrome/browser/chromeos/drive/change_list_loader.cc b/chrome/browser/chromeos/drive/change_list_loader.cc
index dbe26ed..081d9a7 100644
--- a/chrome/browser/chromeos/drive/change_list_loader.cc
+++ b/chrome/browser/chromeos/drive/change_list_loader.cc
@@ -48,7 +48,7 @@
   ~FullFeedFetcher() override {}
 
   void Run(const FeedFetcherCallback& callback) override {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(!callback.is_null());
 
     // Remember the time stamp for usage stats.
@@ -64,7 +64,7 @@
   void OnFileListFetched(const FeedFetcherCallback& callback,
                          google_apis::DriveApiErrorCode status,
                          scoped_ptr<google_apis::FileList> file_list) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(!callback.is_null());
 
     FileError error = GDataToFileError(status);
@@ -113,7 +113,7 @@
   ~DeltaFeedFetcher() override {}
 
   void Run(const FeedFetcherCallback& callback) override {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(!callback.is_null());
 
     scheduler_->GetChangeList(
@@ -126,7 +126,7 @@
   void OnChangeListFetched(const FeedFetcherCallback& callback,
                            google_apis::DriveApiErrorCode status,
                            scoped_ptr<google_apis::ChangeList> change_list) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(!callback.is_null());
 
     FileError error = GDataToFileError(status);
@@ -165,15 +165,15 @@
 LoaderController::LoaderController()
     : lock_count_(0),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 LoaderController::~LoaderController() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 scoped_ptr<base::ScopedClosureRunner> LoaderController::GetLock() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   ++lock_count_;
   return make_scoped_ptr(new base::ScopedClosureRunner(
@@ -182,7 +182,7 @@
 }
 
 void LoaderController::ScheduleRun(const base::Closure& task) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!task.is_null());
 
   if (lock_count_ > 0) {
@@ -193,7 +193,7 @@
 }
 
 void LoaderController::Unlock() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_LT(0, lock_count_);
 
   if (--lock_count_ > 0)
@@ -215,7 +215,7 @@
 
 void AboutResourceLoader::GetAboutResource(
     const google_apis::AboutResourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   // If the latest UpdateAboutResource task is still running. Wait for it,
@@ -239,7 +239,7 @@
 
 void AboutResourceLoader::UpdateAboutResource(
     const google_apis::AboutResourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   ++current_update_task_id_;
@@ -255,7 +255,7 @@
     int task_id,
     google_apis::DriveApiErrorCode status,
     scoped_ptr<google_apis::AboutResource> about_resource) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   FileError error = GDataToFileError(status);
 
   const std::vector<google_apis::AboutResourceCallback> callbacks =
@@ -312,17 +312,17 @@
 }
 
 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observers_.AddObserver(observer);
 }
 
 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observers_.RemoveObserver(observer);
 }
 
 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   // We only start to check for updates iff the load is done.
@@ -349,7 +349,7 @@
 }
 
 void ChangeListLoader::LoadIfNeeded(const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   // If the metadata is not yet loaded, start loading.
@@ -358,7 +358,7 @@
 }
 
 void ChangeListLoader::Load(const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   // Check if this is the first time this ChangeListLoader do loading.
@@ -390,7 +390,7 @@
     bool is_initial_load,
     const int64* local_changestamp,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error != FILE_ERROR_OK) {
     OnChangeListLoadComplete(error);
@@ -417,7 +417,7 @@
     int64 local_changestamp,
     google_apis::DriveApiErrorCode status,
     scoped_ptr<google_apis::AboutResource> about_resource) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   FileError error = GDataToFileError(status);
   if (error != FILE_ERROR_OK) {
@@ -445,7 +445,7 @@
 }
 
 void ChangeListLoader::OnChangeListLoadComplete(FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!loaded_ && error == FILE_ERROR_OK) {
     loaded_ = true;
@@ -471,7 +471,7 @@
 void ChangeListLoader::OnAboutResourceUpdated(
     google_apis::DriveApiErrorCode error,
     scoped_ptr<google_apis::AboutResource> resource) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (drive::GDataToFileError(error) != drive::FILE_ERROR_OK) {
     logger_->Log(logging::LOG_ERROR,
@@ -485,7 +485,7 @@
 }
 
 void ChangeListLoader::LoadChangeListFromServer(int64 start_changestamp) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!change_feed_fetcher_);
   DCHECK(about_resource_loader_->cached_about_resource());
 
@@ -514,7 +514,7 @@
     bool is_delta_update,
     FileError error,
     ScopedVector<ChangeList> change_lists) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(about_resource);
 
   // Delete the fetcher first.
@@ -556,7 +556,7 @@
     bool should_notify_changed_directories,
     const base::Time& start_time,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   const base::TimeDelta elapsed = base::Time::Now() - start_time;
   logger_->Log(logging::LOG_INFO,
diff --git a/chrome/browser/chromeos/drive/debug_info_collector.cc b/chrome/browser/chromeos/drive/debug_info_collector.cc
index d3bb6826..6159ea1 100644
--- a/chrome/browser/chromeos/drive/debug_info_collector.cc
+++ b/chrome/browser/chromeos/drive/debug_info_collector.cc
@@ -68,7 +68,7 @@
 void DebugInfoCollector::GetResourceEntry(
     const base::FilePath& file_path,
     const GetResourceEntryCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   scoped_ptr<ResourceEntry> entry(new ResourceEntry);
@@ -86,7 +86,7 @@
 void DebugInfoCollector::ReadDirectory(
     const base::FilePath& file_path,
     const ReadDirectoryCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   scoped_ptr<ResourceEntryVector> entries(new ResourceEntryVector);
@@ -104,7 +104,7 @@
 void DebugInfoCollector::IterateFileCache(
     const IterateFileCacheCallback& iteration_callback,
     const base::Closure& completion_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!iteration_callback.is_null());
   DCHECK(!completion_callback.is_null());
 
@@ -118,7 +118,7 @@
 
 void DebugInfoCollector::GetMetadata(
     const GetFilesystemMetadataCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   // Currently, this is just a proxy to the FileSystem.
diff --git a/chrome/browser/chromeos/drive/directory_loader.cc b/chrome/browser/chromeos/drive/directory_loader.cc
index b1512ad..7d60858 100644
--- a/chrome/browser/chromeos/drive/directory_loader.cc
+++ b/chrome/browser/chromeos/drive/directory_loader.cc
@@ -106,7 +106,7 @@
   }
 
   void Run(const FileOperationCallback& callback) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(!callback.is_null());
     DCHECK(!directory_fetch_info_.resource_id().empty());
 
@@ -123,7 +123,7 @@
   void OnFileListFetched(const FileOperationCallback& callback,
                          google_apis::DriveApiErrorCode status,
                          scoped_ptr<google_apis::FileList> file_list) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(!callback.is_null());
 
     FileError error = GDataToFileError(status);
@@ -159,7 +159,7 @@
       const GURL& next_url,
       const std::vector<ResourceEntry>* refreshed_entries,
       FileError error) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(!callback.is_null());
 
     if (error != FILE_ERROR_OK) {
@@ -216,12 +216,12 @@
 }
 
 void DirectoryLoader::AddObserver(ChangeListLoaderObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observers_.AddObserver(observer);
 }
 
 void DirectoryLoader::RemoveObserver(ChangeListLoaderObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observers_.RemoveObserver(observer);
 }
 
@@ -229,7 +229,7 @@
     const base::FilePath& directory_path,
     const ReadDirectoryEntriesCallback& entries_callback,
     const FileOperationCallback& completion_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!completion_callback.is_null());
 
   ResourceEntry* entry = new ResourceEntry;
@@ -256,7 +256,7 @@
     bool should_try_loading_parent,
     const ResourceEntry* entry,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!completion_callback.is_null());
 
   if (error == FILE_ERROR_NOT_FOUND &&
@@ -308,7 +308,7 @@
     const ReadDirectoryEntriesCallback& entries_callback,
     const FileOperationCallback& completion_callback,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!completion_callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -337,7 +337,7 @@
     const std::string& local_id,
     google_apis::DriveApiErrorCode status,
     scoped_ptr<google_apis::AboutResource> about_resource) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   FileError error = GDataToFileError(status);
   if (error != FILE_ERROR_OK) {
@@ -374,7 +374,7 @@
     const ResourceEntry* entry,
     const int64* local_changestamp,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(about_resource);
 
   if (error != FILE_ERROR_OK) {
@@ -412,7 +412,7 @@
 
 void DirectoryLoader::OnDirectoryLoadComplete(const std::string& local_id,
                                               FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   LoadCallbackMap::iterator it = pending_load_callback_.find(local_id);
   if (it == pending_load_callback_.end())
@@ -487,7 +487,7 @@
 
 void DirectoryLoader::LoadDirectoryFromServer(
     const DirectoryFetchInfo& directory_fetch_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!directory_fetch_info.empty());
   DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString();
 
@@ -516,7 +516,7 @@
     const DirectoryFetchInfo& directory_fetch_info,
     FeedFetcher* fetcher,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!directory_fetch_info.empty());
 
   // Delete the fetcher.
@@ -556,7 +556,7 @@
     const DirectoryFetchInfo& directory_fetch_info,
     const base::FilePath* directory_path,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString();
   OnDirectoryLoadComplete(directory_fetch_info.local_id(), error);
diff --git a/chrome/browser/chromeos/drive/download_handler.cc b/chrome/browser/chromeos/drive/download_handler.cc
index ed33735..108aa80 100644
--- a/chrome/browser/chromeos/drive/download_handler.cc
+++ b/chrome/browser/chromeos/drive/download_handler.cc
@@ -246,7 +246,7 @@
 
 void DownloadHandler::OnDownloadUpdated(
     DownloadManager* manager, DownloadItem* download) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Only accept downloads that have the Drive meta data associated with them.
   DriveUserData* data = GetDriveUserData(download);
diff --git a/chrome/browser/chromeos/drive/drive_file_stream_reader.cc b/chrome/browser/chromeos/drive/drive_file_stream_reader.cc
index db54b04..8a1650e 100644
--- a/chrome/browser/chromeos/drive/drive_file_stream_reader.cc
+++ b/chrome/browser/chromeos/drive/drive_file_stream_reader.cc
@@ -96,7 +96,7 @@
     : file_reader_(file_reader.Pass()),
       remaining_length_(length),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(file_reader_);
 }
 
@@ -105,7 +105,7 @@
 
 int LocalReaderProxy::Read(net::IOBuffer* buffer, int buffer_length,
                            const net::CompletionCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(file_reader_);
 
   if (buffer_length > remaining_length_) {
@@ -135,7 +135,7 @@
 
 void LocalReaderProxy::OnReadCompleted(const net::CompletionCallback& callback,
                                        int read_result) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(file_reader_);
 
   if (read_result >= 0) {
@@ -160,7 +160,7 @@
       error_code_(net::OK),
       buffer_length_(0),
       job_canceller_(job_canceller) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
 NetworkReaderProxy::~NetworkReaderProxy() {
@@ -171,7 +171,7 @@
 
 int NetworkReaderProxy::Read(net::IOBuffer* buffer, int buffer_length,
                              const net::CompletionCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // Check if there is no pending Read operation.
   DCHECK(!buffer_.get());
   DCHECK_EQ(buffer_length_, 0);
@@ -218,7 +218,7 @@
 }
 
 void NetworkReaderProxy::OnGetContent(scoped_ptr<std::string> data) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(data && !data->empty());
 
   if (remaining_offset_ >= static_cast<int64>(data->length())) {
@@ -253,7 +253,7 @@
 }
 
 void NetworkReaderProxy::OnCompleted(FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // The downloading is completed, so we do not need to cancel the job
   // in the destructor.
   job_canceller_.Reset();
@@ -288,7 +288,7 @@
     const GetFileContentInitializedCallback& initialized_callback,
     const google_apis::GetContentCallback& get_content_callback,
     const FileOperationCallback& completion_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   FileSystemInterface* file_system = file_system_getter.Run();
   if (!file_system) {
@@ -311,7 +311,7 @@
     const google_apis::GetContentCallback& get_content_callback,
     const FileOperationCallback& completion_callback,
     const base::Callback<void(const base::Closure&)>& reply_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   BrowserThread::PostTaskAndReplyWithResult(
       BrowserThread::UI,
@@ -333,14 +333,14 @@
     : file_system_getter_(file_system_getter),
       file_task_runner_(file_task_runner),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
 DriveFileStreamReader::~DriveFileStreamReader() {
 }
 
 bool DriveFileStreamReader::IsInitialized() const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   return reader_proxy_.get() != NULL;
 }
 
@@ -348,7 +348,7 @@
     const base::FilePath& drive_file_path,
     const net::HttpByteRange& byte_range,
     const InitializeCompletionCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!callback.is_null());
 
   GetFileContent(
@@ -370,7 +370,7 @@
 
 int DriveFileStreamReader::Read(net::IOBuffer* buffer, int buffer_length,
                                 const net::CompletionCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(reader_proxy_);
   DCHECK(buffer);
   DCHECK(!callback.is_null());
@@ -379,7 +379,7 @@
 
 void DriveFileStreamReader::StoreCancelDownloadClosure(
     const base::Closure& cancel_download_closure) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   cancel_download_closure_ = cancel_download_closure;
 }
 
@@ -389,7 +389,7 @@
     FileError error,
     const base::FilePath& local_cache_file_path,
     scoped_ptr<ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // StoreCancelDownloadClosure() should be called before this function.
   DCHECK(!cancel_download_closure_.is_null());
 
@@ -445,7 +445,7 @@
     scoped_ptr<ResourceEntry> entry,
     scoped_ptr<util::LocalFileReader> file_reader,
     int open_result) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (open_result != net::OK) {
     callback.Run(net::ERR_FAILED, scoped_ptr<ResourceEntry>());
@@ -460,7 +460,7 @@
 void DriveFileStreamReader::OnGetContent(
     google_apis::DriveApiErrorCode error_code,
     scoped_ptr<std::string> data) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(reader_proxy_);
   reader_proxy_->OnGetContent(data.Pass());
 }
@@ -468,7 +468,7 @@
 void DriveFileStreamReader::OnGetFileContentCompletion(
     const InitializeCompletionCallback& callback,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (reader_proxy_) {
     // If the proxy object available, send the error to it.
diff --git a/chrome/browser/chromeos/drive/drive_integration_service.cc b/chrome/browser/chromeos/drive/drive_integration_service.cc
index 293fd5f..eb05e35 100644
--- a/chrome/browser/chromeos/drive/drive_integration_service.cc
+++ b/chrome/browser/chromeos/drive/drive_integration_service.cc
@@ -219,7 +219,7 @@
       cache_root_directory_(!test_cache_root.empty() ?
                             test_cache_root : util::GetCacheRootPath(profile)),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(profile && !profile->IsOffTheRecord());
 
   logger_.reset(new EventLogger);
@@ -281,11 +281,11 @@
 }
 
 DriveIntegrationService::~DriveIntegrationService() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void DriveIntegrationService::Shutdown() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   weak_ptr_factory_.InvalidateWeakPtrs();
 
@@ -357,13 +357,13 @@
 
 void DriveIntegrationService::AddObserver(
     DriveIntegrationServiceObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observers_.AddObserver(observer);
 }
 
 void DriveIntegrationService::RemoveObserver(
     DriveIntegrationServiceObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observers_.RemoveObserver(observer);
 }
 
@@ -382,7 +382,7 @@
 
 void DriveIntegrationService::ClearCacheAndRemountFileSystem(
     const base::Callback<void(bool)>& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (state_ != INITIALIZED) {
@@ -405,7 +405,7 @@
 void DriveIntegrationService::AddBackDriveMountPoint(
     const base::Callback<void(bool)>& callback,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   state_ = error == FILE_ERROR_OK ? INITIALIZED : NOT_INITIALIZED;
@@ -421,7 +421,7 @@
 }
 
 void DriveIntegrationService::AddDriveMountPoint() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(INITIALIZED, state_);
   DCHECK(enabled_);
 
@@ -447,7 +447,7 @@
 }
 
 void DriveIntegrationService::RemoveDriveMountPoint() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!mount_point_name_.empty()) {
     job_list()->CancelAllJobs();
@@ -465,7 +465,7 @@
 }
 
 void DriveIntegrationService::Initialize() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(NOT_INITIALIZED, state_);
   DCHECK(enabled_);
 
@@ -486,7 +486,7 @@
 
 void DriveIntegrationService::InitializeAfterMetadataInitialized(
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(INITIALIZING, state_);
 
   SigninManagerBase* signin_manager =
diff --git a/chrome/browser/chromeos/drive/fake_file_system.cc b/chrome/browser/chromeos/drive/fake_file_system.cc
index 306ece4..3017d8cf 100644
--- a/chrome/browser/chromeos/drive/fake_file_system.cc
+++ b/chrome/browser/chromeos/drive/fake_file_system.cc
@@ -33,48 +33,48 @@
 }
 
 void FakeFileSystem::AddObserver(FileSystemObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::RemoveObserver(FileSystemObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::CheckForUpdates() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::TransferFileFromLocalToRemote(
     const base::FilePath& local_src_file_path,
     const base::FilePath& remote_dest_file_path,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::OpenFile(const base::FilePath& file_path,
                               OpenMode open_mode,
                               const std::string& mime_type,
                               const OpenFileCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::Copy(const base::FilePath& src_file_path,
                           const base::FilePath& dest_file_path,
                           bool preserve_last_modified,
                           const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::Move(const base::FilePath& src_file_path,
                           const base::FilePath& dest_file_path,
                           const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::Remove(const base::FilePath& file_path,
                             bool is_recursive,
                             const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::CreateDirectory(
@@ -82,47 +82,47 @@
     bool is_exclusive,
     bool is_recursive,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::CreateFile(const base::FilePath& file_path,
                                 bool is_exclusive,
                                 const std::string& mime_type,
                                 const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::TouchFile(const base::FilePath& file_path,
                                const base::Time& last_access_time,
                                const base::Time& last_modified_time,
                                const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::TruncateFile(const base::FilePath& file_path,
                                   int64 length,
                                   const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::Pin(const base::FilePath& file_path,
                          const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::Unpin(const base::FilePath& file_path,
                            const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::GetFile(const base::FilePath& file_path,
                              const GetFileCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::GetFileForSaving(const base::FilePath& file_path,
                                       const GetFileCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 base::Closure FakeFileSystem::GetFileContent(
@@ -130,7 +130,7 @@
     const GetFileContentInitializedCallback& initialized_callback,
     const google_apis::GetContentCallback& get_content_callback,
     const FileOperationCallback& completion_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   GetResourceEntry(
       file_path,
@@ -144,7 +144,7 @@
 void FakeFileSystem::GetResourceEntry(
     const base::FilePath& file_path,
     const GetResourceEntryCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (file_path == util::GetDriveMyDriveRootPath()) {
     // Specialized for the root entry.
@@ -168,13 +168,13 @@
     const base::FilePath& file_path,
     const ReadDirectoryEntriesCallback& entries_callback,
     const FileOperationCallback& completion_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::Search(const std::string& search_query,
                             const GURL& next_link,
                             const SearchCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::SearchMetadata(
@@ -182,48 +182,48 @@
     int options,
     int at_most_num_matches,
     const SearchMetadataCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::SearchByHashes(const std::set<std::string>& hashes,
                                     const SearchByHashesCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::GetAvailableSpace(
     const GetAvailableSpaceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::GetShareUrl(
     const base::FilePath& file_path,
     const GURL& embed_origin,
     const GetShareUrlCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::GetMetadata(
     const GetFilesystemMetadataCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::MarkCacheFileAsMounted(
     const base::FilePath& drive_file_path,
     const MarkMountedCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::MarkCacheFileAsUnmounted(
     const base::FilePath& cache_file_path,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::AddPermission(const base::FilePath& drive_file_path,
                                    const std::string& email,
                                    google_apis::drive::PermissionRole role,
                                    const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::SetProperty(
@@ -232,7 +232,7 @@
     const std::string& key,
     const std::string& value,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::Reset(const FileOperationCallback& callback) {
@@ -241,13 +241,13 @@
 void FakeFileSystem::GetPathFromResourceId(
     const std::string& resource_id,
     const GetFilePathCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FakeFileSystem::FreeDiskSpaceIfNeededFor(
     int64 num_bytes,
     const FreeDiskSpaceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 // Implementation of GetFileContent.
@@ -257,7 +257,7 @@
     const FileOperationCallback& completion_callback,
     FileError error,
     scoped_ptr<ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error != FILE_ERROR_OK) {
     completion_callback.Run(error);
@@ -288,7 +288,7 @@
     const FileOperationCallback& completion_callback,
     google_apis::DriveApiErrorCode gdata_error,
     scoped_ptr<google_apis::FileResource> gdata_entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
@@ -333,7 +333,7 @@
     const FileOperationCallback& completion_callback,
     google_apis::DriveApiErrorCode gdata_error,
     const base::FilePath& temp_file) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   completion_callback.Run(GDataToFileError(gdata_error));
 }
 
@@ -342,7 +342,7 @@
     const GetResourceEntryCallback& callback,
     google_apis::DriveApiErrorCode gdata_error,
     scoped_ptr<google_apis::AboutResource> about_resource) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
@@ -363,7 +363,7 @@
     const GetResourceEntryCallback& callback,
     FileError error,
     scoped_ptr<ResourceEntry> parent_entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error != FILE_ERROR_OK) {
     callback.Run(error, scoped_ptr<ResourceEntry>());
@@ -383,7 +383,7 @@
     const GetResourceEntryCallback& callback,
     google_apis::DriveApiErrorCode gdata_error,
     scoped_ptr<google_apis::FileList> file_list) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
diff --git a/chrome/browser/chromeos/drive/file_cache.cc b/chrome/browser/chromeos/drive/file_cache.cc
index 0fea321..c8bd557 100644
--- a/chrome/browser/chromeos/drive/file_cache.cc
+++ b/chrome/browser/chromeos/drive/file_cache.cc
@@ -49,7 +49,7 @@
       free_disk_space_getter_(free_disk_space_getter),
       weak_ptr_factory_(this) {
   DCHECK(blocking_task_runner_.get());
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 FileCache::~FileCache() {
@@ -414,7 +414,7 @@
 }
 
 void FileCache::Destroy() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Destroy myself on the blocking pool.
   // Note that base::DeletePointer<> cannot be used as the destructor of this
diff --git a/chrome/browser/chromeos/drive/file_system.cc b/chrome/browser/chromeos/drive/file_system.cc
index a433eaf4..a09eac4 100644
--- a/chrome/browser/chromeos/drive/file_system.cc
+++ b/chrome/browser/chromeos/drive/file_system.cc
@@ -90,7 +90,7 @@
 void RunGetResourceEntryCallback(const GetResourceEntryCallback& callback,
                                  scoped_ptr<ResourceEntry> entry,
                                  FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK)
@@ -160,7 +160,7 @@
     const GetFilesystemMetadataCallback& callback,
     const int64* largest_changestamp,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   metadata.largest_changestamp = *largest_changestamp;
@@ -205,7 +205,7 @@
 void GetPathFromResourceIdAfterGetPath(base::FilePath* file_path,
                                        const GetFilePathCallback& callback,
                                        FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   callback.Run(error, *file_path);
 }
 
@@ -218,7 +218,7 @@
 // Used to implement ReadDirectory().
 void FilterHostedDocuments(const ReadDirectoryEntriesCallback& callback,
                            scoped_ptr<ResourceEntryVector> entries) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (entries) {
@@ -294,13 +294,13 @@
       blocking_task_runner_(blocking_task_runner),
       temporary_file_directory_(temporary_file_directory),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   ResetComponents();
 }
 
 FileSystem::~FileSystem() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   directory_loader_->RemoveObserver(this);
   change_list_loader_->RemoveObserver(this);
@@ -411,7 +411,7 @@
 }
 
 void FileSystem::CheckForUpdates() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DVLOG(1) << "CheckForUpdates";
 
   change_list_loader_->CheckForUpdates(
@@ -419,19 +419,19 @@
 }
 
 void FileSystem::OnUpdateChecked(FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DVLOG(1) << "CheckForUpdates finished: " << FileErrorToString(error);
   last_update_check_time_ = base::Time::Now();
   last_update_check_error_ = error;
 }
 
 void FileSystem::AddObserver(FileSystemObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observers_.AddObserver(observer);
 }
 
 void FileSystem::RemoveObserver(FileSystemObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observers_.RemoveObserver(observer);
 }
 
@@ -439,7 +439,7 @@
     const base::FilePath& local_src_file_path,
     const base::FilePath& remote_dest_file_path,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
   copy_operation_->TransferFileFromLocalToRemote(local_src_file_path,
                                                  remote_dest_file_path,
@@ -450,7 +450,7 @@
                       const base::FilePath& dest_file_path,
                       bool preserve_last_modified,
                       const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
   copy_operation_->Copy(
       src_file_path, dest_file_path, preserve_last_modified, callback);
@@ -459,7 +459,7 @@
 void FileSystem::Move(const base::FilePath& src_file_path,
                       const base::FilePath& dest_file_path,
                       const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
   move_operation_->Move(src_file_path, dest_file_path, callback);
 }
@@ -467,7 +467,7 @@
 void FileSystem::Remove(const base::FilePath& file_path,
                         bool is_recursive,
                         const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
   remove_operation_->Remove(file_path, is_recursive, callback);
 }
@@ -477,7 +477,7 @@
     bool is_exclusive,
     bool is_recursive,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   CreateDirectoryParams params;
@@ -495,7 +495,7 @@
 
 void FileSystem::CreateDirectoryAfterRead(const CreateDirectoryParams& params,
                                           FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!params.callback.is_null());
 
   DVLOG_IF(1, error != FILE_ERROR_OK) << "ReadDirectory failed. "
@@ -510,7 +510,7 @@
                             bool is_exclusive,
                             const std::string& mime_type,
                             const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
   create_file_operation_->CreateFile(
       file_path, is_exclusive, mime_type, callback);
@@ -520,7 +520,7 @@
                            const base::Time& last_access_time,
                            const base::Time& last_modified_time,
                            const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
   touch_operation_->TouchFile(
       file_path, last_access_time, last_modified_time, callback);
@@ -529,14 +529,14 @@
 void FileSystem::TruncateFile(const base::FilePath& file_path,
                               int64 length,
                               const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
   truncate_operation_->Truncate(file_path, length, callback);
 }
 
 void FileSystem::Pin(const base::FilePath& file_path,
                      const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   std::string* local_id = new std::string;
@@ -553,7 +553,7 @@
 void FileSystem::FinishPin(const FileOperationCallback& callback,
                            const std::string* local_id,
                            FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error == FILE_ERROR_OK)
@@ -563,7 +563,7 @@
 
 void FileSystem::Unpin(const base::FilePath& file_path,
                        const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   std::string* local_id = new std::string;
@@ -581,7 +581,7 @@
 void FileSystem::FinishUnpin(const FileOperationCallback& callback,
                              const std::string* local_id,
                              FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error == FILE_ERROR_OK)
@@ -591,7 +591,7 @@
 
 void FileSystem::GetFile(const base::FilePath& file_path,
                          const GetFileCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   download_operation_->EnsureFileDownloadedByPath(
@@ -604,7 +604,7 @@
 
 void FileSystem::GetFileForSaving(const base::FilePath& file_path,
                                   const GetFileCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   get_file_for_saving_operation_->GetFileForSaving(file_path, callback);
@@ -615,7 +615,7 @@
     const GetFileContentInitializedCallback& initialized_callback,
     const google_apis::GetContentCallback& get_content_callback,
     const FileOperationCallback& completion_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!initialized_callback.is_null());
   DCHECK(!get_content_callback.is_null());
   DCHECK(!completion_callback.is_null());
@@ -632,7 +632,7 @@
 void FileSystem::GetResourceEntry(
     const base::FilePath& file_path,
     const GetResourceEntryCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   ReadDirectory(file_path.DirName(),
@@ -647,7 +647,7 @@
     const base::FilePath& file_path,
     const GetResourceEntryCallback& callback,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   DVLOG_IF(1, error != FILE_ERROR_OK) << "ReadDirectory failed. "
@@ -670,7 +670,7 @@
     const base::FilePath& directory_path,
     const ReadDirectoryEntriesCallback& entries_callback_in,
     const FileOperationCallback& completion_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!completion_callback.is_null());
 
   const bool hide_hosted_docs =
@@ -689,7 +689,7 @@
 
 void FileSystem::GetAvailableSpace(
     const GetAvailableSpaceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   about_resource_loader_->GetAboutResource(
@@ -702,7 +702,7 @@
     const GetAvailableSpaceCallback& callback,
     google_apis::DriveApiErrorCode status,
     scoped_ptr<google_apis::AboutResource> about_resource) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   FileError error = GDataToFileError(status);
@@ -720,7 +720,7 @@
 void FileSystem::GetShareUrl(const base::FilePath& file_path,
                              const GURL& embed_origin,
                              const GetShareUrlCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   // Resolve the resource id.
@@ -746,7 +746,7 @@
     const GetShareUrlCallback& callback,
     ResourceEntry* entry,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -772,7 +772,7 @@
     const GetShareUrlCallback& callback,
     google_apis::DriveApiErrorCode status,
     const GURL& share_url) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   FileError error = GDataToFileError(status);
@@ -792,7 +792,7 @@
 void FileSystem::Search(const std::string& search_query,
                         const GURL& next_link,
                         const SearchCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
   search_operation_->Search(search_query, next_link, callback);
 }
@@ -801,7 +801,7 @@
                                 int options,
                                 int at_most_num_matches,
                                 const SearchMetadataCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // TODO(satorux): Stop handling hide_hosted_docs here. crbug.com/256520.
   if (pref_service_->GetBoolean(prefs::kDisableDriveHostedFiles))
@@ -815,7 +815,7 @@
 
 void FileSystem::SearchByHashes(const std::set<std::string>& hashes,
                                 const SearchByHashesCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   drive::internal::SearchMetadata(
       blocking_task_runner_, resource_metadata_,
       /* any file name */ "", base::Bind(&CheckHashes, hashes),
@@ -824,7 +824,7 @@
 }
 
 void FileSystem::OnFileChangedByOperation(const FileChange& changed_files) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   FOR_EACH_OBSERVER(
       FileSystemObserver, observers_, OnFileChanged(changed_files));
@@ -868,27 +868,27 @@
 }
 
 void FileSystem::OnDirectoryReloaded(const base::FilePath& directory_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   FOR_EACH_OBSERVER(
       FileSystemObserver, observers_, OnDirectoryChanged(directory_path));
 }
 
 void FileSystem::OnFileChanged(const FileChange& changed_files) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   FOR_EACH_OBSERVER(
       FileSystemObserver, observers_, OnFileChanged(changed_files));
 }
 
 void FileSystem::OnLoadFromServerComplete() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   sync_client_->StartCheckingExistingPinnedFiles();
 }
 
 void FileSystem::OnInitialLoadComplete() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   blocking_task_runner_->PostTask(FROM_HERE,
                                   base::Bind(&internal::RemoveStaleCacheFiles,
@@ -899,7 +899,7 @@
 
 void FileSystem::GetMetadata(
     const GetFilesystemMetadataCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   FileSystemMetadata metadata;
@@ -925,7 +925,7 @@
 void FileSystem::MarkCacheFileAsMounted(
     const base::FilePath& drive_file_path,
     const MarkMountedCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   base::FilePath* cache_file_path = new base::FilePath;
@@ -944,7 +944,7 @@
 void FileSystem::MarkCacheFileAsUnmounted(
     const base::FilePath& cache_file_path,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (!cache_->IsUnderFileCacheDirectory(cache_file_path)) {
@@ -965,7 +965,7 @@
                                const std::string& email,
                                google_apis::drive::PermissionRole role,
                                const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   // Resolve the resource id.
@@ -991,7 +991,7 @@
     const FileOperationCallback& callback,
     ResourceEntry* entry,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error != FILE_ERROR_OK) {
     callback.Run(error);
@@ -1011,7 +1011,7 @@
     const std::string& key,
     const std::string& value,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   set_property_operation_->SetProperty(drive_file_path, visibility, key, value,
@@ -1022,7 +1022,7 @@
                           OpenMode open_mode,
                           const std::string& mime_type,
                           const OpenFileCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   open_file_operation_->OpenFile(file_path, open_mode, mime_type, callback);
@@ -1030,7 +1030,7 @@
 
 void FileSystem::GetPathFromResourceId(const std::string& resource_id,
                                        const GetFilePathCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   base::FilePath* const file_path = new base::FilePath();
@@ -1049,7 +1049,7 @@
 void FileSystem::FreeDiskSpaceIfNeededFor(
     int64 num_bytes,
     const FreeDiskSpaceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(), FROM_HERE,
diff --git a/chrome/browser/chromeos/drive/file_system/copy_operation.cc b/chrome/browser/chromeos/drive/file_system/copy_operation.cc
index 00c1716f..39b959a 100644
--- a/chrome/browser/chromeos/drive/file_system/copy_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/copy_operation.cc
@@ -288,18 +288,18 @@
                                                    delegate,
                                                    metadata)),
     weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 CopyOperation::~CopyOperation() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void CopyOperation::Copy(const base::FilePath& src_file_path,
                          const base::FilePath& dest_file_path,
                          bool preserve_last_modified,
                          const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   CopyParams* params = new CopyParams;
@@ -328,7 +328,7 @@
     const bool* directory_changed,
     const bool* should_copy_on_server,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!params->callback.is_null());
 
   for (const auto& id : *updated_local_ids) {
@@ -366,7 +366,7 @@
 
 void CopyOperation::CopyAfterParentSync(const CopyParams& params,
                                         FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!params.callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -391,7 +391,7 @@
 void CopyOperation::CopyAfterGetParentResourceId(const CopyParams& params,
                                                  const ResourceEntry* parent,
                                                  FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!params.callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -420,7 +420,7 @@
     const base::FilePath& local_src_path,
     const base::FilePath& remote_dest_path,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   std::string* gdoc_resource_id = new std::string;
@@ -446,7 +446,7 @@
     std::string* gdoc_resource_id,
     ResourceEntry* parent_entry,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -483,7 +483,7 @@
 void CopyOperation::TransferJsonGdocFileAfterLocalWork(
     TransferJsonGdocParams* params,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error != FILE_ERROR_OK) {
     params->callback.Run(error);
@@ -541,7 +541,7 @@
     const std::string& new_title,
     const base::Time& last_modified,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   scheduler_->CopyResource(
@@ -555,7 +555,7 @@
     const FileOperationCallback& callback,
     google_apis::DriveApiErrorCode status,
     scoped_ptr<google_apis::FileResource> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   FileError error = GDataToFileError(status);
@@ -589,7 +589,7 @@
     base::FilePath* file_path,
     const ResourceEntry* entry,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error == FILE_ERROR_OK) {
@@ -604,7 +604,7 @@
     const base::FilePath& local_src_path,
     const base::FilePath& remote_dest_path,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   create_file_operation_->CreateFile(
@@ -621,7 +621,7 @@
     const base::FilePath& remote_dest_path,
     const FileOperationCallback& callback,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -656,7 +656,7 @@
     const ResourceEntry* entry,
     std::string* local_id,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error == FILE_ERROR_OK) {
diff --git a/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc b/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc
index 9a8098a..b746b2d 100644
--- a/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc
@@ -128,11 +128,11 @@
       delegate_(delegate),
       metadata_(metadata),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 CreateDirectoryOperation::~CreateDirectoryOperation() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void CreateDirectoryOperation::CreateDirectory(
@@ -140,7 +140,7 @@
     bool is_exclusive,
     bool is_recursive,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   std::set<std::string>* updated_local_ids = new std::set<std::string>;
@@ -168,7 +168,7 @@
     const std::set<std::string>* updated_local_ids,
     const FileChange* changed_files,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   for (const auto& id : *updated_local_ids) {
diff --git a/chrome/browser/chromeos/drive/file_system/create_file_operation.cc b/chrome/browser/chromeos/drive/file_system/create_file_operation.cc
index 68c4806..afbc20cf 100644
--- a/chrome/browser/chromeos/drive/file_system/create_file_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/create_file_operation.cc
@@ -78,18 +78,18 @@
       delegate_(delegate),
       metadata_(metadata),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 CreateFileOperation::~CreateFileOperation() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void CreateFileOperation::CreateFile(const base::FilePath& file_path,
                                      bool is_exclusive,
                                      const std::string& mime_type,
                                      const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   ResourceEntry* entry = new ResourceEntry;
@@ -115,7 +115,7 @@
     bool is_exclusive,
     ResourceEntry* entry,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error == FILE_ERROR_EXISTS) {
diff --git a/chrome/browser/chromeos/drive/file_system/download_operation.cc b/chrome/browser/chromeos/drive/file_system/download_operation.cc
index 8c0f165..1aeb5e7a 100644
--- a/chrome/browser/chromeos/drive/file_system/download_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/download_operation.cc
@@ -360,7 +360,7 @@
     const GetFileContentInitializedCallback& initialized_callback,
     const google_apis::GetContentCallback& get_content_callback,
     const GetFileCallback& completion_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!completion_callback.is_null());
 
   CheckPreconditionForEnsureFileDownloadedParams params;
@@ -401,7 +401,7 @@
     const GetFileContentInitializedCallback& initialized_callback,
     const google_apis::GetContentCallback& get_content_callback,
     const GetFileCallback& completion_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!completion_callback.is_null());
 
   CheckPreconditionForEnsureFileDownloadedParams params;
@@ -442,7 +442,7 @@
     base::FilePath* cache_file_path,
     base::FilePath* temp_download_file_path,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(params);
   DCHECK(drive_file_path);
   DCHECK(cache_file_path);
@@ -489,7 +489,7 @@
     scoped_ptr<DownloadParams> params,
     google_apis::DriveApiErrorCode gdata_error,
     const base::FilePath& downloaded_file_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   DownloadParams* params_ptr = params.get();
   ResourceEntry* entry_after_update = new ResourceEntry;
@@ -519,7 +519,7 @@
     scoped_ptr<ResourceEntry> entry_after_update,
     base::FilePath* cache_file_path,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error != FILE_ERROR_OK) {
     params->OnError(error);
diff --git a/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation.cc b/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation.cc
index 8dc69b8..0709a1dc 100644
--- a/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation.cc
@@ -69,7 +69,7 @@
 void GetFileForSavingOperation::GetFileForSaving(
     const base::FilePath& file_path,
     const GetFileCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   create_file_operation_->CreateFile(
@@ -86,7 +86,7 @@
     const base::FilePath& file_path,
     const GetFileCallback& callback,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -109,7 +109,7 @@
     FileError error,
     const base::FilePath& cache_path,
     scoped_ptr<ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -144,7 +144,7 @@
     scoped_ptr<ResourceEntry> entry,
     scoped_ptr<base::ScopedClosureRunner>* file_closer,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -171,7 +171,7 @@
     const base::FilePath& cache_path,
     scoped_ptr<ResourceEntry> entry,
     bool success) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   logger_->Log(logging::LOG_INFO, "Started watching modification to %s [%s].",
diff --git a/chrome/browser/chromeos/drive/file_system/move_operation.cc b/chrome/browser/chromeos/drive/file_system/move_operation.cc
index 5ff61737..402ee53 100644
--- a/chrome/browser/chromeos/drive/file_system/move_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/move_operation.cc
@@ -74,17 +74,17 @@
       delegate_(delegate),
       metadata_(metadata),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 MoveOperation::~MoveOperation() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void MoveOperation::Move(const base::FilePath& src_file_path,
                          const base::FilePath& dest_file_path,
                          const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   FileChange* changed_files = new FileChange;
@@ -110,7 +110,7 @@
     const FileChange* changed_files,
     const std::string* local_id,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (error == FILE_ERROR_OK) {
     // Notify the change of directory.
     delegate_->OnFileChangedByOperation(*changed_files);
diff --git a/chrome/browser/chromeos/drive/file_system/open_file_operation.cc b/chrome/browser/chromeos/drive/file_system/open_file_operation.cc
index 2aec134..6f4e9bd 100644
--- a/chrome/browser/chromeos/drive/file_system/open_file_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/open_file_operation.cc
@@ -50,7 +50,7 @@
                                  OpenMode open_mode,
                                  const std::string& mime_type,
                                  const OpenFileCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   switch (open_mode) {
@@ -83,7 +83,7 @@
     const base::FilePath& file_path,
     const OpenFileCallback& callback,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -106,7 +106,7 @@
     FileError error,
     const base::FilePath& local_file_path,
     scoped_ptr<ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error == FILE_ERROR_OK) {
@@ -145,7 +145,7 @@
     const OpenFileCallback& callback,
     scoped_ptr<base::ScopedClosureRunner>* file_closer,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -164,7 +164,7 @@
 void OpenFileOperation::CloseFile(
     const std::string& local_id,
     scoped_ptr<base::ScopedClosureRunner> file_closer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_GT(open_files_[local_id], 0);
 
   if (--open_files_[local_id] == 0) {
diff --git a/chrome/browser/chromeos/drive/file_system/remove_operation.cc b/chrome/browser/chromeos/drive/file_system/remove_operation.cc
index 9a4b3043..c3f7a1ed 100644
--- a/chrome/browser/chromeos/drive/file_system/remove_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/remove_operation.cc
@@ -70,17 +70,17 @@
       metadata_(metadata),
       cache_(cache),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 RemoveOperation::~RemoveOperation() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void RemoveOperation::Remove(const base::FilePath& path,
                              bool is_recursive,
                              const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   std::string* local_id = new std::string;
@@ -111,7 +111,7 @@
     const ResourceEntry* entry,
     const base::FilePath* changed_path,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (!changed_path->empty()) {
diff --git a/chrome/browser/chromeos/drive/file_system/search_operation.cc b/chrome/browser/chromeos/drive/file_system/search_operation.cc
index 535c875..8b32317 100644
--- a/chrome/browser/chromeos/drive/file_system/search_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/search_operation.cc
@@ -98,7 +98,7 @@
 void SearchOperation::Search(const std::string& search_query,
                              const GURL& next_link,
                              const SearchCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (next_link.is_empty()) {
@@ -120,7 +120,7 @@
     const SearchCallback& callback,
     google_apis::DriveApiErrorCode gdata_error,
     scoped_ptr<google_apis::FileList> file_list) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   FileError error = GDataToFileError(gdata_error);
@@ -167,7 +167,7 @@
     const GURL& next_link,
     scoped_ptr<std::vector<SearchResultInfo> > result,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
   DCHECK(result);
 
diff --git a/chrome/browser/chromeos/drive/file_system/set_property_operation.cc b/chrome/browser/chromeos/drive/file_system/set_property_operation.cc
index 8226957..111f9b7 100644
--- a/chrome/browser/chromeos/drive/file_system/set_property_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/set_property_operation.cc
@@ -90,7 +90,7 @@
     const std::string& key,
     const std::string& value,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   ResourceEntry* entry = new ResourceEntry;
@@ -106,7 +106,7 @@
     const FileOperationCallback& callback,
     const ResourceEntry* entry,
     FileError result) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (result == FILE_ERROR_OK) {
diff --git a/chrome/browser/chromeos/drive/file_system/touch_operation.cc b/chrome/browser/chromeos/drive/file_system/touch_operation.cc
index c6fbe33..217bd92 100644
--- a/chrome/browser/chromeos/drive/file_system/touch_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/touch_operation.cc
@@ -60,7 +60,7 @@
                                const base::Time& last_access_time,
                                const base::Time& last_modified_time,
                                const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   ResourceEntry* entry = new ResourceEntry;
@@ -78,7 +78,7 @@
     const FileOperationCallback& callback,
     const ResourceEntry* entry,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   FileChange changed_files;
diff --git a/chrome/browser/chromeos/drive/file_system/truncate_operation.cc b/chrome/browser/chromeos/drive/file_system/truncate_operation.cc
index 46a7dda..44c3302 100644
--- a/chrome/browser/chromeos/drive/file_system/truncate_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/truncate_operation.cc
@@ -80,7 +80,7 @@
 void TruncateOperation::Truncate(const base::FilePath& file_path,
                                  int64 length,
                                  const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (length < 0) {
@@ -107,7 +107,7 @@
     FileError error,
     const base::FilePath& local_file_path,
     scoped_ptr<ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -136,7 +136,7 @@
     const std::string& local_id,
     const FileOperationCallback& callback,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   delegate_->OnEntryUpdatedByOperation(ClientContext(USER_INITIATED), local_id);
diff --git a/chrome/browser/chromeos/drive/file_system_util.cc b/chrome/browser/chromeos/drive/file_system_util.cc
index 60c81da..6a351ff 100644
--- a/chrome/browser/chromeos/drive/file_system_util.cc
+++ b/chrome/browser/chromeos/drive/file_system_util.cc
@@ -133,7 +133,7 @@
 }
 
 FileSystemInterface* GetFileSystemByProfile(Profile* profile) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   DriveIntegrationService* integration_service =
       GetIntegrationServiceByProfile(profile);
@@ -141,7 +141,7 @@
 }
 
 FileSystemInterface* GetFileSystemByProfileId(void* profile_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // |profile_id| needs to be checked with ProfileManager::IsValidProfile
   // before using it.
@@ -152,7 +152,7 @@
 }
 
 DriveAppRegistry* GetDriveAppRegistryByProfile(Profile* profile) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   DriveIntegrationService* integration_service =
       GetIntegrationServiceByProfile(profile);
@@ -162,7 +162,7 @@
 }
 
 DriveServiceInterface* GetDriveServiceByProfile(Profile* profile) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   DriveIntegrationService* integration_service =
       GetIntegrationServiceByProfile(profile);
@@ -192,7 +192,7 @@
 }
 
 Profile* ExtractProfileFromPath(const base::FilePath& path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   const std::vector<Profile*>& profiles =
       g_browser_process->profile_manager()->GetLoadedProfiles();
@@ -268,7 +268,7 @@
 void PrepareWritableFileAndRun(Profile* profile,
                                const base::FilePath& path,
                                const PrepareWritableFileCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   FileSystemInterface* file_system = GetFileSystemByProfile(profile);
@@ -287,7 +287,7 @@
 void EnsureDirectoryExists(Profile* profile,
                            const base::FilePath& directory,
                            const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
   if (IsUnderDriveMountPoint(directory)) {
     FileSystemInterface* file_system = GetFileSystemByProfile(profile);
@@ -325,7 +325,7 @@
 }
 
 bool IsDriveEnabledForProfile(Profile* profile) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!chromeos::IsProfileAssociatedWithGaiaAccount(profile))
     return false;
diff --git a/chrome/browser/chromeos/drive/file_write_watcher.cc b/chrome/browser/chromeos/drive/file_write_watcher.cc
index 0affb5f1..48d7500 100644
--- a/chrome/browser/chromeos/drive/file_write_watcher.cc
+++ b/chrome/browser/chromeos/drive/file_write_watcher.cc
@@ -80,11 +80,11 @@
 FileWriteWatcher::FileWriteWatcherImpl::FileWriteWatcherImpl()
     : delay_(base::TimeDelta::FromSeconds(kWriteEventDelayInSeconds)),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FileWriteWatcher::FileWriteWatcherImpl::Destroy() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Just forwarding the call to FILE thread.
   BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)->PostTask(
@@ -97,7 +97,7 @@
     const base::FilePath& path,
     const StartWatchCallback& on_start_callback,
     const base::Closure& on_write_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Forwarding the call to FILE thread and relaying the |callback|.
   BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)->PostTask(
@@ -110,13 +110,13 @@
 }
 
 FileWriteWatcher::FileWriteWatcherImpl::~FileWriteWatcherImpl() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
 
   STLDeleteContainerPairSecondPointers(watchers_.begin(), watchers_.end());
 }
 
 void FileWriteWatcher::FileWriteWatcherImpl::DestroyOnFileThread() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
 
   delete this;
 }
@@ -125,7 +125,7 @@
     const base::FilePath& path,
     const StartWatchCallback& on_start_callback,
     const base::Closure& on_write_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
 
   std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path);
   if (it != watchers_.end()) {
@@ -149,7 +149,7 @@
 void FileWriteWatcher::FileWriteWatcherImpl::OnWriteEvent(
     const base::FilePath& path,
     bool error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
 
   if (error)
     return;
@@ -170,7 +170,7 @@
 
 void FileWriteWatcher::FileWriteWatcherImpl::InvokeCallback(
     const base::FilePath& path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
 
   std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path);
   DCHECK(it != watchers_.end());
@@ -186,17 +186,17 @@
 
 FileWriteWatcher::FileWriteWatcher()
     : watcher_impl_(new FileWriteWatcherImpl) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 FileWriteWatcher::~FileWriteWatcher() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void FileWriteWatcher::StartWatch(const base::FilePath& file_path,
                                   const StartWatchCallback& on_start_callback,
                                   const base::Closure& on_write_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   watcher_impl_->StartWatch(file_path, on_start_callback, on_write_callback);
 }
 
diff --git a/chrome/browser/chromeos/drive/fileapi/async_file_util.cc b/chrome/browser/chromeos/drive/fileapi/async_file_util.cc
index 9adf7871..904d601 100644
--- a/chrome/browser/chromeos/drive/fileapi/async_file_util.cc
+++ b/chrome/browser/chromeos/drive/fileapi/async_file_util.cc
@@ -48,7 +48,7 @@
     const fileapi_internal::FileSystemGetter& file_system_getter,
     const base::Callback<void(FileSystemInterface*)>& function,
     const base::Closure& on_error_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   BrowserThread::PostTask(
       BrowserThread::UI,
@@ -67,7 +67,7 @@
     const AsyncFileUtil::CreateOrOpenCallback& callback,
     base::File file,
     const base::Closure& close_callback_on_ui_thread) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   // It is necessary to make a closure, which runs on file closing here.
   // It will be provided as a FileSystem::OpenFileCallback's argument later.
@@ -90,7 +90,7 @@
 void RunEnsureFileExistsCallback(
     const AsyncFileUtil::EnsureFileExistsCallback& callback,
     base::File::Error error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   // Remember if the file is actually created or not.
   bool created = (error == base::File::FILE_OK);
@@ -112,7 +112,7 @@
   // ShareableFileReference is thread *unsafe* class. So it is necessary to
   // create the instance (by invoking GetOrCreate) on IO thread, though
   // most drive file system related operations run on UI thread.
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   scoped_refptr<storage::ShareableFileReference> file_reference =
       storage::ShareableFileReference::GetOrCreate(storage::ScopedFile(
@@ -133,7 +133,7 @@
     const storage::FileSystemURL& url,
     int file_flags,
     const CreateOrOpenCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
   if (file_path.empty()) {
@@ -157,7 +157,7 @@
     scoped_ptr<storage::FileSystemOperationContext> context,
     const storage::FileSystemURL& url,
     const EnsureFileExistsCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
   if (file_path.empty()) {
@@ -180,7 +180,7 @@
     bool exclusive,
     bool recursive,
     const StatusCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
   if (file_path.empty()) {
@@ -200,7 +200,7 @@
     scoped_ptr<storage::FileSystemOperationContext> context,
     const storage::FileSystemURL& url,
     const GetFileInfoCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
   if (file_path.empty()) {
@@ -220,7 +220,7 @@
     scoped_ptr<storage::FileSystemOperationContext> context,
     const storage::FileSystemURL& url,
     const ReadDirectoryCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
   if (file_path.empty()) {
@@ -242,7 +242,7 @@
     const base::Time& last_access_time,
     const base::Time& last_modified_time,
     const StatusCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
   if (file_path.empty()) {
@@ -263,7 +263,7 @@
     const storage::FileSystemURL& url,
     int64 length,
     const StatusCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
   if (file_path.empty()) {
@@ -285,7 +285,7 @@
     CopyOrMoveOption option,
     const CopyFileProgressCallback& progress_callback,
     const StatusCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url);
   base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
@@ -317,7 +317,7 @@
     const storage::FileSystemURL& dest_url,
     CopyOrMoveOption option,
     const StatusCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url);
   base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
@@ -342,7 +342,7 @@
     const base::FilePath& src_file_path,
     const storage::FileSystemURL& dest_url,
     const StatusCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
   if (dest_path.empty()) {
@@ -362,7 +362,7 @@
     scoped_ptr<storage::FileSystemOperationContext> context,
     const storage::FileSystemURL& url,
     const StatusCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
   if (file_path.empty()) {
@@ -382,7 +382,7 @@
     scoped_ptr<storage::FileSystemOperationContext> context,
     const storage::FileSystemURL& url,
     const StatusCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
   if (file_path.empty()) {
@@ -402,7 +402,7 @@
     scoped_ptr<storage::FileSystemOperationContext> context,
     const storage::FileSystemURL& url,
     const StatusCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
   if (file_path.empty()) {
@@ -422,7 +422,7 @@
     scoped_ptr<storage::FileSystemOperationContext> context,
     const storage::FileSystemURL& url,
     const CreateSnapshotFileCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
   if (file_path.empty()) {
diff --git a/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.cc b/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.cc
index 8a7af155..7999420 100644
--- a/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.cc
+++ b/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.cc
@@ -79,7 +79,7 @@
 
 storage::AsyncFileUtil* FileSystemBackendDelegate::GetAsyncFileUtil(
     storage::FileSystemType type) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK_EQ(storage::kFileSystemTypeDrive, type);
   return async_file_util_.get();
 }
@@ -91,7 +91,7 @@
     int64 max_bytes_to_read,
     const base::Time& expected_modification_time,
     storage::FileSystemContext* context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK_EQ(storage::kFileSystemTypeDrive, url.type());
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
@@ -112,7 +112,7 @@
     const storage::FileSystemURL& url,
     int64 offset,
     storage::FileSystemContext* context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK_EQ(storage::kFileSystemTypeDrive, url.type());
 
   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
diff --git a/chrome/browser/chromeos/drive/fileapi/fileapi_worker.cc b/chrome/browser/chromeos/drive/fileapi/fileapi_worker.cc
index eb08c65..ceb34fe 100644
--- a/chrome/browser/chromeos/drive/fileapi/fileapi_worker.cc
+++ b/chrome/browser/chromeos/drive/fileapi/fileapi_worker.cc
@@ -140,7 +140,7 @@
     FileError error,
     const base::FilePath& local_path,
     const base::Closure& close_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   callback.Run(FileErrorToBaseFileError(error), local_path, close_callback);
 }
 
@@ -161,7 +161,7 @@
                                      FileError error,
                                      const base::FilePath& local_path,
                                      const base::Closure& close_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error != FILE_ERROR_OK) {
     callback.Run(base::File(FileErrorToBaseFileError(error)), base::Closure());
@@ -194,7 +194,7 @@
 }  // namespace
 
 FileSystemInterface* GetFileSystemFromUrl(const storage::FileSystemURL& url) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   Profile* profile = util::ExtractProfileFromPath(url.path());
   return profile ? util::GetFileSystemByProfile(profile) : NULL;
@@ -204,7 +204,7 @@
     const FileSystemGetter& file_system_getter,
     const base::Callback<void(FileSystemInterface*)>& callback,
     const base::Closure& on_error_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   FileSystemInterface* file_system = file_system_getter.Run();
 
   if (!file_system) {
@@ -219,7 +219,7 @@
 void GetFileInfo(const base::FilePath& file_path,
                  const GetFileInfoCallback& callback,
                  FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->GetResourceEntry(
       file_path,
       base::Bind(&RunGetFileInfoCallback, callback));
@@ -230,7 +230,7 @@
           bool preserve_last_modified,
           const StatusCallback& callback,
           FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->Copy(src_file_path, dest_file_path, preserve_last_modified,
                     base::Bind(&RunStatusCallbackByFileError, callback));
 }
@@ -239,7 +239,7 @@
           const base::FilePath& dest_file_path,
           const StatusCallback& callback,
           FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->Move(src_file_path, dest_file_path,
                     base::Bind(&RunStatusCallbackByFileError, callback));
 }
@@ -248,7 +248,7 @@
                        const base::FilePath& dest_file_path,
                        const StatusCallback& callback,
                        FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->TransferFileFromLocalToRemote(
       src_foreign_file_path, dest_file_path,
       base::Bind(&RunStatusCallbackByFileError, callback));
@@ -257,7 +257,7 @@
 void ReadDirectory(const base::FilePath& file_path,
                    const ReadDirectoryCallback& callback,
                    FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->ReadDirectory(
       file_path,
       base::Bind(&RunReadDirectoryCallbackWithEntries, callback),
@@ -268,7 +268,7 @@
             bool is_recursive,
             const StatusCallback& callback,
             FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->Remove(file_path, is_recursive,
                       base::Bind(&RunStatusCallbackByFileError, callback));
 }
@@ -278,7 +278,7 @@
                      bool is_recursive,
                      const StatusCallback& callback,
                      FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->CreateDirectory(
       file_path, is_exclusive, is_recursive,
       base::Bind(&RunStatusCallbackByFileError, callback));
@@ -288,7 +288,7 @@
                 bool is_exclusive,
                 const StatusCallback& callback,
                 FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->CreateFile(file_path, is_exclusive,
                           std::string(),  // no mime type; guess from file_path
                           base::Bind(&RunStatusCallbackByFileError, callback));
@@ -298,7 +298,7 @@
               int64 length,
               const StatusCallback& callback,
               FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->TruncateFile(
       file_path, length,
       base::Bind(&RunStatusCallbackByFileError, callback));
@@ -307,7 +307,7 @@
 void CreateSnapshotFile(const base::FilePath& file_path,
                         const CreateSnapshotFileCallback& callback,
                         FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->GetFile(file_path,
                        base::Bind(&RunCreateSnapshotFileCallback, callback));
 }
@@ -316,7 +316,7 @@
     const base::FilePath& file_path,
     const CreateWritableSnapshotFileCallback& callback,
     FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->OpenFile(
       file_path,
       OPEN_FILE,
@@ -328,7 +328,7 @@
               int file_flags,
               const OpenFileCallback& callback,
               FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Returns an error if any unsupported flag is found.
   if (file_flags & ~(base::File::FLAG_OPEN |
@@ -359,7 +359,7 @@
                const base::Time& last_modified_time,
                const StatusCallback& callback,
                FileSystemInterface* file_system) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   file_system->TouchFile(file_path, last_access_time, last_modified_time,
                          base::Bind(&RunStatusCallbackByFileError, callback));
 
diff --git a/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_reader_impl.cc b/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_reader_impl.cc
index 6ec846c..87a8cc79 100644
--- a/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_reader_impl.cc
+++ b/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_reader_impl.cc
@@ -42,7 +42,7 @@
 int WebkitFileStreamReaderImpl::Read(net::IOBuffer* buffer,
                                      int buffer_length,
                                      const net::CompletionCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(stream_reader_);
   DCHECK(buffer);
   DCHECK(!callback.is_null());
@@ -67,7 +67,7 @@
 
 int64 WebkitFileStreamReaderImpl::GetLength(
     const net::Int64CompletionCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(stream_reader_);
   DCHECK(!callback.is_null());
 
@@ -94,7 +94,7 @@
     const net::CompletionCallback& callback,
     int error,
     scoped_ptr<ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(stream_reader_);
   DCHECK(!callback.is_null());
 
@@ -117,7 +117,7 @@
     int buffer_length,
     const net::CompletionCallback& callback,
     int initialization_result) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!callback.is_null());
 
   if (initialization_result != net::OK) {
@@ -134,7 +134,7 @@
 void WebkitFileStreamReaderImpl::GetLengthAfterStreamReaderInitialized(
     const net::Int64CompletionCallback& callback,
     int initialization_result) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!callback.is_null());
 
   if (initialization_result != net::OK) {
diff --git a/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_writer_impl.cc b/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_writer_impl.cc
index d6228a6..53a9cb6 100644
--- a/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_writer_impl.cc
+++ b/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_writer_impl.cc
@@ -25,7 +25,7 @@
     const WebkitFileStreamWriterImpl::FileSystemGetter& file_system_getter,
     const base::FilePath& drive_path,
     const fileapi_internal::CreateWritableSnapshotFileCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   BrowserThread::PostTask(
       BrowserThread::UI,
diff --git a/chrome/browser/chromeos/drive/job_scheduler.cc b/chrome/browser/chromeos/drive/job_scheduler.cc
index 328f14f..a74485e 100644
--- a/chrome/browser/chromeos/drive/job_scheduler.cc
+++ b/chrome/browser/chromeos/drive/job_scheduler.cc
@@ -171,11 +171,11 @@
     : job_info(type),
       context(ClientContext(USER_INITIATED)),
       retry_count(0) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 JobScheduler::JobEntry::~JobEntry() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 struct JobScheduler::ResumeUploadParams {
@@ -197,7 +197,7 @@
       uploader_(new DriveUploader(drive_service, blocking_task_runner)),
       pref_service_(pref_service),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   for (int i = 0; i < NUM_QUEUES; ++i)
     queue_[i].reset(new JobQueue(kMaxJobCount[i], NUM_CONTEXT_TYPES));
@@ -206,7 +206,7 @@
 }
 
 JobScheduler::~JobScheduler() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   size_t num_queued_jobs = 0;
   for (int i = 0; i < NUM_QUEUES; ++i)
@@ -224,17 +224,17 @@
 }
 
 void JobScheduler::AddObserver(JobListObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observer_list_.AddObserver(observer);
 }
 
 void JobScheduler::RemoveObserver(JobListObserver* observer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observer_list_.RemoveObserver(observer);
 }
 
 void JobScheduler::CancelJob(JobID job_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   JobEntry* job = job_map_.Lookup(job_id);
   if (job) {
@@ -251,7 +251,7 @@
 }
 
 void JobScheduler::CancelAllJobs() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // CancelJob may remove the entry from |job_map_|. That's OK. IDMap supports
   // removable during iteration.
@@ -261,7 +261,7 @@
 
 void JobScheduler::GetAboutResource(
     const google_apis::AboutResourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_GET_ABOUT_RESOURCE);
@@ -277,7 +277,7 @@
 }
 
 void JobScheduler::GetAppList(const google_apis::AppListCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_GET_APP_LIST);
@@ -294,7 +294,7 @@
 
 void JobScheduler::GetAllFileList(
     const google_apis::FileListCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_GET_ALL_RESOURCE_LIST);
@@ -312,7 +312,7 @@
 void JobScheduler::GetFileListInDirectory(
     const std::string& directory_resource_id,
     const google_apis::FileListCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(
@@ -331,7 +331,7 @@
 
 void JobScheduler::Search(const std::string& search_query,
                           const google_apis::FileListCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_SEARCH);
@@ -350,7 +350,7 @@
 void JobScheduler::GetChangeList(
     int64 start_changestamp,
     const google_apis::ChangeListCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_GET_CHANGE_LIST);
@@ -369,7 +369,7 @@
 void JobScheduler::GetRemainingChangeList(
     const GURL& next_link,
     const google_apis::ChangeListCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_GET_REMAINING_CHANGE_LIST);
@@ -388,7 +388,7 @@
 void JobScheduler::GetRemainingFileList(
     const GURL& next_link,
     const google_apis::FileListCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_GET_REMAINING_FILE_LIST);
@@ -408,7 +408,7 @@
     const std::string& resource_id,
     const ClientContext& context,
     const google_apis::FileResourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_GET_RESOURCE_ENTRY);
@@ -430,7 +430,7 @@
     const GURL& embed_origin,
     const ClientContext& context,
     const google_apis::GetShareUrlCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_GET_SHARE_URL);
@@ -452,7 +452,7 @@
     const std::string& resource_id,
     const ClientContext& context,
     const google_apis::EntryActionCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_TRASH_RESOURCE);
@@ -475,7 +475,7 @@
     const std::string& new_title,
     const base::Time& last_modified,
     const google_apis::FileResourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_COPY_RESOURCE);
@@ -503,7 +503,7 @@
     const google_apis::drive::Properties& properties,
     const ClientContext& context,
     const google_apis::FileResourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_UPDATE_RESOURCE);
@@ -523,7 +523,7 @@
     const std::string& parent_resource_id,
     const std::string& resource_id,
     const google_apis::EntryActionCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_ADD_RESOURCE_TO_DIRECTORY);
@@ -545,7 +545,7 @@
     const std::string& resource_id,
     const ClientContext& context,
     const google_apis::EntryActionCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   JobEntry* new_job = CreateNewJob(TYPE_REMOVE_RESOURCE_FROM_DIRECTORY);
   new_job->context = context;
@@ -568,7 +568,7 @@
     const DriveServiceInterface::AddNewDirectoryOptions& options,
     const ClientContext& context,
     const google_apis::FileResourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   JobEntry* new_job = CreateNewJob(TYPE_ADD_NEW_DIRECTORY);
   new_job->context = context;
@@ -594,7 +594,7 @@
     const ClientContext& context,
     const google_apis::DownloadActionCallback& download_action_callback,
     const google_apis::GetContentCallback& get_content_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Temporary histogram for crbug.com/229650.
   CollectCopyHistogramSample("Drive.DownloadFromDriveFileSize",
@@ -631,7 +631,7 @@
     const DriveUploader::UploadNewFileOptions& options,
     const ClientContext& context,
     const google_apis::FileResourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   JobEntry* new_job = CreateNewJob(TYPE_UPLOAD_NEW_FILE);
   new_job->job_info.file_path = drive_file_path;
@@ -675,7 +675,7 @@
     const DriveUploader::UploadExistingFileOptions& options,
     const ClientContext& context,
     const google_apis::FileResourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   JobEntry* new_job = CreateNewJob(TYPE_UPLOAD_EXISTING_FILE);
   new_job->job_info.file_path = drive_file_path;
@@ -715,7 +715,7 @@
     const std::string& email,
     google_apis::drive::PermissionRole role,
     const google_apis::EntryActionCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   JobEntry* new_job = CreateNewJob(TYPE_ADD_PERMISSION);
@@ -747,7 +747,7 @@
 }
 
 void JobScheduler::QueueJob(JobID job_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   JobEntry* job_entry = job_map_.Lookup(job_id);
   DCHECK(job_entry);
@@ -780,7 +780,7 @@
 }
 
 void JobScheduler::DoJobLoop(QueueType queue_type) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   const int accepted_priority = GetCurrentAcceptedPriority(queue_type);
 
@@ -831,7 +831,7 @@
 }
 
 int JobScheduler::GetCurrentAcceptedPriority(QueueType queue_type) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   const int kNoJobShouldRun = -1;
 
@@ -856,7 +856,7 @@
 }
 
 void JobScheduler::UpdateWait() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (disable_throttling_ || throttle_count_ == 0)
     return;
@@ -872,7 +872,7 @@
 
 bool JobScheduler::OnJobDone(JobID job_id,
                              google_apis::DriveApiErrorCode error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   JobEntry* job_entry = job_map_.Lookup(job_id);
   DCHECK(job_entry);
@@ -933,7 +933,7 @@
     const google_apis::FileListCallback& callback,
     google_apis::DriveApiErrorCode error,
     scoped_ptr<google_apis::FileList> file_list) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (OnJobDone(job_id, error))
@@ -945,7 +945,7 @@
     const google_apis::ChangeListCallback& callback,
     google_apis::DriveApiErrorCode error,
     scoped_ptr<google_apis::ChangeList> change_list) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (OnJobDone(job_id, error))
@@ -957,7 +957,7 @@
     const google_apis::FileResourceCallback& callback,
     google_apis::DriveApiErrorCode error,
     scoped_ptr<google_apis::FileResource> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (OnJobDone(job_id, error))
@@ -969,7 +969,7 @@
     const google_apis::AboutResourceCallback& callback,
     google_apis::DriveApiErrorCode error,
     scoped_ptr<google_apis::AboutResource> about_resource) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (OnJobDone(job_id, error))
@@ -981,7 +981,7 @@
     const google_apis::GetShareUrlCallback& callback,
     google_apis::DriveApiErrorCode error,
     const GURL& share_url) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (OnJobDone(job_id, error))
@@ -993,7 +993,7 @@
     const google_apis::AppListCallback& callback,
     google_apis::DriveApiErrorCode error,
     scoped_ptr<google_apis::AppList> app_list) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (OnJobDone(job_id, error))
@@ -1004,7 +1004,7 @@
     JobID job_id,
     const google_apis::EntryActionCallback& callback,
     google_apis::DriveApiErrorCode error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (OnJobDone(job_id, error))
@@ -1016,7 +1016,7 @@
     const google_apis::DownloadActionCallback& callback,
     google_apis::DriveApiErrorCode error,
     const base::FilePath& temp_file) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (OnJobDone(job_id, error))
@@ -1030,7 +1030,7 @@
     google_apis::DriveApiErrorCode error,
     const GURL& upload_location,
     scoped_ptr<google_apis::FileResource> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (!upload_location.is_empty()) {
@@ -1067,7 +1067,7 @@
     google_apis::DriveApiErrorCode error,
     const GURL& upload_location,
     scoped_ptr<google_apis::FileResource> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!original_task.is_null());
   DCHECK(!callback.is_null());
 
@@ -1095,7 +1095,7 @@
 
 void JobScheduler::OnConnectionTypeChanged(
     net::NetworkChangeNotifier::ConnectionType type) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Resume the job loop.
   // Note that we don't need to check the network connection status as it will
@@ -1107,7 +1107,7 @@
 void JobScheduler::OnGotFileSizeForJob(JobID job_id,
                                        const std::string& histogram_name,
                                        int64* size) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (*size == -1)
     return;
 
@@ -1154,7 +1154,7 @@
 
 void JobScheduler::AbortNotRunningJob(JobEntry* job,
                                       google_apis::DriveApiErrorCode error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   const base::TimeDelta elapsed = base::Time::Now() - job->job_info.start_time;
   const QueueType queue_type = GetJobQueueType(job->job_info.job_type);
@@ -1175,19 +1175,19 @@
 }
 
 void JobScheduler::NotifyJobAdded(const JobInfo& job_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   FOR_EACH_OBSERVER(JobListObserver, observer_list_, OnJobAdded(job_info));
 }
 
 void JobScheduler::NotifyJobDone(const JobInfo& job_info,
                                  google_apis::DriveApiErrorCode error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   FOR_EACH_OBSERVER(JobListObserver, observer_list_,
                     OnJobDone(job_info, GDataToFileError(error)));
 }
 
 void JobScheduler::NotifyJobUpdated(const JobInfo& job_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   FOR_EACH_OBSERVER(JobListObserver, observer_list_, OnJobUpdated(job_info));
 }
 
diff --git a/chrome/browser/chromeos/drive/resource_metadata.cc b/chrome/browser/chromeos/drive/resource_metadata.cc
index 84b1d8b..990b9a5 100644
--- a/chrome/browser/chromeos/drive/resource_metadata.cc
+++ b/chrome/browser/chromeos/drive/resource_metadata.cc
@@ -72,7 +72,7 @@
     : blocking_task_runner_(blocking_task_runner),
       storage_(storage),
       cache_(cache) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 FileError ResourceMetadata::Initialize() {
@@ -81,7 +81,7 @@
 }
 
 void ResourceMetadata::Destroy() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   blocking_task_runner_->PostTask(
       FROM_HERE,
diff --git a/chrome/browser/chromeos/drive/resource_metadata_storage.cc b/chrome/browser/chromeos/drive/resource_metadata_storage.cc
index 799f9f3..8322a77 100644
--- a/chrome/browser/chromeos/drive/resource_metadata_storage.cc
+++ b/chrome/browser/chromeos/drive/resource_metadata_storage.cc
@@ -267,6 +267,7 @@
   leveldb::Options options;
   options.max_open_files = 0;  // Use minimum.
   options.create_if_missing = false;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   if (!leveldb::DB::Open(options, resource_map_path.AsUTF8Unsafe(), &db).ok())
     return false;
   scoped_ptr<leveldb::DB> resource_map(db);
@@ -549,6 +550,7 @@
   leveldb::Options options;
   options.max_open_files = 0;  // Use minimum.
   options.create_if_missing = false;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
 
   DBInitStatus open_existing_result = DB_INIT_NOT_FOUND;
   leveldb::Status status;
@@ -600,6 +602,7 @@
     options.max_open_files = 0;  // Use minimum.
     options.create_if_missing = true;
     options.error_if_exists = true;
+    options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
 
     status = leveldb::DB::Open(options, resource_map_path.AsUTF8Unsafe(), &db);
     if (status.ok()) {
@@ -638,6 +641,7 @@
   leveldb::Options options;
   options.max_open_files = 0;  // Use minimum.
   options.create_if_missing = false;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
 
   // Trashed DB may be broken, repair it first.
   leveldb::Status status;
diff --git a/chrome/browser/chromeos/drive/search_metadata.cc b/chrome/browser/chromeos/drive/search_metadata.cc
index 1ba2763..e7a7da9 100644
--- a/chrome/browser/chromeos/drive/search_metadata.cc
+++ b/chrome/browser/chromeos/drive/search_metadata.cc
@@ -265,7 +265,7 @@
     const SearchMetadataPredicate& predicate,
     size_t at_most_num_matches,
     const SearchMetadataCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   const base::TimeTicks start_time = base::TimeTicks::Now();
diff --git a/chrome/browser/chromeos/drive/sync/entry_revert_performer.cc b/chrome/browser/chromeos/drive/sync/entry_revert_performer.cc
index 0d8146e..221fd0b 100644
--- a/chrome/browser/chromeos/drive/sync/entry_revert_performer.cc
+++ b/chrome/browser/chromeos/drive/sync/entry_revert_performer.cc
@@ -93,17 +93,17 @@
       scheduler_(scheduler),
       metadata_(metadata),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 EntryRevertPerformer::~EntryRevertPerformer() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void EntryRevertPerformer::RevertEntry(const std::string& local_id,
                                        const ClientContext& context,
                                        const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   scoped_ptr<ResourceEntry> entry(new ResourceEntry);
@@ -123,7 +123,7 @@
     const FileOperationCallback& callback,
     scoped_ptr<ResourceEntry> entry,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error == FILE_ERROR_OK && entry->resource_id().empty())
@@ -146,7 +146,7 @@
     const std::string& local_id,
     google_apis::DriveApiErrorCode status,
     scoped_ptr<google_apis::FileResource> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   FileChange* changed_files = new FileChange;
@@ -169,7 +169,7 @@
     const FileOperationCallback& callback,
     const FileChange* changed_files,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   delegate_->OnFileChangedByOperation(*changed_files);
diff --git a/chrome/browser/chromeos/drive/sync/entry_update_performer.cc b/chrome/browser/chromeos/drive/sync/entry_update_performer.cc
index f2c5084..0f8e069b 100644
--- a/chrome/browser/chromeos/drive/sync/entry_update_performer.cc
+++ b/chrome/browser/chromeos/drive/sync/entry_update_performer.cc
@@ -240,17 +240,17 @@
                                                        scheduler,
                                                        metadata)),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 EntryUpdatePerformer::~EntryUpdatePerformer() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void EntryUpdatePerformer::UpdateEntry(const std::string& local_id,
                                        const ClientContext& context,
                                        const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   scoped_ptr<LocalState> local_state(new LocalState);
@@ -269,7 +269,7 @@
     const FileOperationCallback& callback,
     scoped_ptr<LocalState> local_state,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK) {
@@ -407,7 +407,7 @@
     scoped_ptr<base::ScopedClosureRunner> loader_lock,
     google_apis::DriveApiErrorCode status,
     scoped_ptr<google_apis::FileResource> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (status == google_apis::HTTP_FORBIDDEN) {
@@ -437,7 +437,7 @@
     const FileOperationCallback& callback,
     const FileChange* changed_files,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   delegate_->OnFileChangedByOperation(*changed_files);
diff --git a/chrome/browser/chromeos/drive/sync/remove_performer.cc b/chrome/browser/chromeos/drive/sync/remove_performer.cc
index d5115985..db1a34a5 100644
--- a/chrome/browser/chromeos/drive/sync/remove_performer.cc
+++ b/chrome/browser/chromeos/drive/sync/remove_performer.cc
@@ -63,11 +63,11 @@
                                                        scheduler,
                                                        metadata)),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 RemovePerformer::~RemovePerformer() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 // Returns |entry| corresponding to |local_id|.
@@ -84,7 +84,7 @@
 void RemovePerformer::Remove(const std::string& local_id,
                              const ClientContext& context,
                              const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   ResourceEntry* entry = new ResourceEntry;
@@ -104,7 +104,7 @@
     const FileOperationCallback& callback,
     const ResourceEntry* entry,
     FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (error != FILE_ERROR_OK || entry->resource_id().empty()) {
@@ -132,7 +132,7 @@
                                     const FileOperationCallback& callback,
                                     const std::string& resource_id,
                                     const std::string& local_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   scheduler_->TrashResource(
@@ -147,7 +147,7 @@
     const FileOperationCallback& callback,
     const std::string& local_id,
     google_apis::DriveApiErrorCode status) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   if (status == google_apis::HTTP_FORBIDDEN) {
@@ -174,7 +174,7 @@
                                        const FileOperationCallback& callback,
                                        const std::string& resource_id,
                                        const std::string& local_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   scheduler_->GetFileResource(
@@ -190,7 +190,7 @@
     const std::string& local_id,
     google_apis::DriveApiErrorCode status,
     scoped_ptr<google_apis::FileResource> file_resource) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   FileError error = GDataToFileError(status);
@@ -239,7 +239,7 @@
     const FileOperationCallback& callback,
     const std::string& local_id,
     google_apis::DriveApiErrorCode status) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   FileError error = GDataToFileError(status);
diff --git a/chrome/browser/chromeos/drive/sync_client.cc b/chrome/browser/chromeos/drive/sync_client.cc
index 432ea353..267edb2 100644
--- a/chrome/browser/chromeos/drive/sync_client.cc
+++ b/chrome/browser/chromeos/drive/sync_client.cc
@@ -165,15 +165,15 @@
       delay_(base::TimeDelta::FromSeconds(kDelaySeconds)),
       long_delay_(base::TimeDelta::FromSeconds(kLongDelaySeconds)),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 SyncClient::~SyncClient() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void SyncClient::StartProcessingBacklog() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   std::vector<std::string>* to_fetch = new std::vector<std::string>;
   std::vector<std::string>* to_update = new std::vector<std::string>;
@@ -187,7 +187,7 @@
 }
 
 void SyncClient::StartCheckingExistingPinnedFiles() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   std::vector<std::string>* local_ids = new std::vector<std::string>;
   blocking_task_runner_->PostTaskAndReply(
@@ -202,12 +202,12 @@
 }
 
 void SyncClient::AddFetchTask(const std::string& local_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   AddFetchTaskInternal(local_id, delay_);
 }
 
 void SyncClient::RemoveFetchTask(const std::string& local_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   SyncTasks::iterator it = tasks_.find(SyncTasks::key_type(FETCH, local_id));
   if (it == tasks_.end())
@@ -228,14 +228,14 @@
 
 void SyncClient::AddUpdateTask(const ClientContext& context,
                                const std::string& local_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   AddUpdateTaskInternal(context, local_id, delay_);
 }
 
 bool SyncClient:: WaitForUpdateTaskToComplete(
     const std::string& local_id,
     const FileOperationCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   SyncTasks::iterator it = tasks_.find(SyncTasks::key_type(UPDATE, local_id));
   if (it == tasks_.end())
@@ -248,7 +248,7 @@
 
 base::Closure SyncClient::PerformFetchTask(const std::string& local_id,
                                            const ClientContext& context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return download_operation_->EnsureFileDownloadedByLocalId(
       local_id,
       context,
@@ -261,7 +261,7 @@
 
 void SyncClient::AddFetchTaskInternal(const std::string& local_id,
                                       const base::TimeDelta& delay) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   SyncTask task;
   task.state = PENDING;
@@ -274,7 +274,7 @@
 
 base::Closure SyncClient::PerformUpdateTask(const std::string& local_id,
                                             const ClientContext& context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   entry_update_performer_->UpdateEntry(
       local_id,
       context,
@@ -288,7 +288,7 @@
 void SyncClient::AddUpdateTaskInternal(const ClientContext& context,
                                        const std::string& local_id,
                                        const base::TimeDelta& delay) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   SyncTask task;
   task.state = PENDING;
@@ -302,7 +302,7 @@
 void SyncClient::AddTask(const SyncTasks::key_type& key,
                          const SyncTask& task,
                          const base::TimeDelta& delay) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   SyncTasks::iterator it = tasks_.find(key);
   if (it != tasks_.end()) {
@@ -391,7 +391,7 @@
 void SyncClient::OnGetLocalIdsOfBacklog(
     const std::vector<std::string>* to_fetch,
     const std::vector<std::string>* to_update) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Give priority to upload tasks over fetch tasks, so that dirty files are
   // uploaded as soon as possible.
@@ -409,7 +409,7 @@
 }
 
 void SyncClient::AddFetchTasks(const std::vector<std::string>* local_ids) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   for (size_t i = 0; i < local_ids->size(); ++i)
     AddFetchTask((*local_ids)[i]);
@@ -418,7 +418,7 @@
 void SyncClient::OnTaskComplete(SyncType type,
                                 const std::string& local_id,
                                 FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   const SyncTasks::key_type key(type, local_id);
   SyncTasks::iterator it = tasks_.find(key);
@@ -478,7 +478,7 @@
                                      FileError error,
                                      const base::FilePath& local_path,
                                      scoped_ptr<ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   OnTaskComplete(FETCH, local_id, error);
   if (error == FILE_ERROR_ABORT) {
     // If user cancels download, unpin the file so that we do not sync the file
diff --git a/chrome/browser/chromeos/drive/write_on_cache_file.cc b/chrome/browser/chromeos/drive/write_on_cache_file.cc
index 367cc04..340cf4e 100644
--- a/chrome/browser/chromeos/drive/write_on_cache_file.cc
+++ b/chrome/browser/chromeos/drive/write_on_cache_file.cc
@@ -36,7 +36,7 @@
     FileError error,
     const base::FilePath& local_cache_path,
     const base::Closure& close_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BrowserThread::GetBlockingPool()->PostTaskAndReply(
       FROM_HERE,
@@ -59,7 +59,7 @@
                               const std::string& mime_type,
                               const WriteOnCacheFileCallback& callback,
                               const FileOperationCallback& reply) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(file_system);
   DCHECK(!callback.is_null());
   DCHECK(!reply.is_null());
diff --git a/chrome/browser/chromeos/extensions/echo_private_api.cc b/chrome/browser/chromeos/extensions/echo_private_api.cc
index 9943dbd..6b3e6cf 100644
--- a/chrome/browser/chromeos/extensions/echo_private_api.cc
+++ b/chrome/browser/chromeos/extensions/echo_private_api.cc
@@ -155,7 +155,7 @@
 // If we can get the timestamp info, return it as yyyy-mm-dd, otherwise, return
 // an empty string.
 bool EchoPrivateGetOobeTimestampFunction::GetOobeTimestampOnFileThread() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
 
   const char kOobeTimestampFile[] = "/home/chronos/.oobe_completed";
   std::string timestamp = "";
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router.cc b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
index 4c54cf2..e141a56 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
@@ -345,7 +345,7 @@
           base::Bind(&EventRouter::DispatchDirectoryChangeEventImpl,
                      base::Unretained(this))),
       weak_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ObserveEvents();
 }
 
@@ -353,7 +353,7 @@
 }
 
 void EventRouter::Shutdown() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   DLOG_IF(WARNING, !file_watchers_.empty())
       << "Not all file watchers are "
@@ -446,7 +446,7 @@
                                const base::FilePath& virtual_path,
                                const std::string& extension_id,
                                const BoolCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   base::FilePath watch_path = local_path;
@@ -486,7 +486,7 @@
 
 void EventRouter::RemoveFileWatch(const base::FilePath& local_path,
                                   const std::string& extension_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   base::FilePath watch_path = local_path;
   // Tweak watch path for remote sources - we need to drop leading /special
@@ -510,7 +510,7 @@
                                   const GURL& source_url,
                                   const GURL& destination_url,
                                   base::File::Error error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   file_manager_private::CopyProgressStatus status;
   if (error == base::File::FILE_OK) {
@@ -536,7 +536,7 @@
     const GURL& source_url,
     const GURL& destination_url,
     int64 size) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   file_manager_private::CopyProgressStatus status;
   status.type = CopyProgressTypeToCopyProgressStatusType(type);
@@ -684,7 +684,7 @@
 }
 
 void EventRouter::OnRefreshTokenInvalid() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Raise a DriveConnectionStatusChanged event to notify the status offline.
   BroadcastEvent(
@@ -694,7 +694,7 @@
 }
 
 void EventRouter::OnReadyToSendRequests() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Raise a DriveConnectionStatusChanged event to notify the status online.
   BroadcastEvent(
@@ -706,7 +706,7 @@
 void EventRouter::HandleFileWatchNotification(const drive::FileChange* list,
                                               const base::FilePath& local_path,
                                               bool got_error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   WatcherMap::const_iterator iter = file_watchers_.find(local_path);
   if (iter == file_watchers_.end()) {
@@ -837,28 +837,28 @@
 
 void EventRouter::OnDiskAdded(
     const DiskMountManager::Disk& disk, bool mounting) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Do nothing.
 }
 
 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Do nothing.
 }
 
 void EventRouter::OnDeviceAdded(const std::string& device_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Do nothing.
 }
 
 void EventRouter::OnDeviceRemoved(const std::string& device_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Do nothing.
 }
 
 void EventRouter::OnVolumeMounted(chromeos::MountError error_code,
                                   const VolumeInfo& volume_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
   // happen at shutdown. This should be removed after removing Drive mounting
   // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
@@ -874,7 +874,7 @@
 
 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
                                     const VolumeInfo& volume_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DispatchMountCompletedEvent(
       file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT,
       error_code,
@@ -900,13 +900,13 @@
 
 void EventRouter::OnFormatStarted(const std::string& device_path,
                                   bool success) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Do nothing.
 }
 
 void EventRouter::OnFormatCompleted(const std::string& device_path,
                                     bool success) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Do nothing.
 }
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
index 9adacf6..f213af5 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
@@ -188,7 +188,7 @@
     const base::FilePath& suggested_name,
     const std::vector<std::string>& allowed_extensions,
     Browser* browser) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!dialog_.get());
   DCHECK(browser);
 
@@ -240,7 +240,7 @@
 
 void FileSelectorImpl::SendResponse(bool success,
                                     const base::FilePath& selected_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // We don't want to send multiple responses.
   if (function_.get())
@@ -308,7 +308,7 @@
 void FileBrowserHandlerInternalSelectFileFunction::OnFilePathSelected(
     bool success,
     const base::FilePath& full_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!success) {
     Respond(EntryDefinition(), false);
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc
index 45ab3c0a..c5d9a7d 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc
@@ -63,7 +63,7 @@
 void FileManagerPrivateSelectFileFunction::GetSelectedFileInfoResponse(
     int index,
     const std::vector<ui::SelectedFileInfo>& files) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (files.size() != 1) {
     SendResponse(false);
     return;
@@ -97,7 +97,7 @@
 
 void FileManagerPrivateSelectFilesFunction::GetSelectedFileInfoResponse(
     const std::vector<ui::SelectedFileInfo>& files) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (files.empty()) {
     SendResponse(false);
     return;
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index e97e59b..8b38af923 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -172,7 +172,7 @@
                     const std::set<EntryPropertyName>& names,
                     Profile* const profile,
                     const ResultCallback& callback) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     SingleEntryPropertiesGetterForDrive* instance =
         new SingleEntryPropertiesGetterForDrive(local_path, names, profile,
@@ -201,7 +201,7 @@
   }
 
   void StartProcess() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     file_path_ = drive::util::ExtractDrivePath(local_path_);
     file_owner_profile_ = drive::util::ExtractProfileFromPath(local_path_);
@@ -230,7 +230,7 @@
 
   void OnGetFileInfo(drive::FileError error,
                      scoped_ptr<drive::ResourceEntry> entry) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     if (error != drive::FILE_ERROR_OK) {
       CompleteGetEntryProperties(error);
@@ -261,7 +261,7 @@
 
   void OnGetRunningPath(drive::FileError error,
                         const base::FilePath& file_path) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     if (error != drive::FILE_ERROR_OK) {
       // The running profile does not know the file.
@@ -285,7 +285,7 @@
 
   void OnGetShareInfo(drive::FileError error,
                       scoped_ptr<drive::ResourceEntry> entry) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     if (error != drive::FILE_ERROR_OK) {
       CompleteGetEntryProperties(error);
@@ -297,7 +297,7 @@
   }
 
   void StartParseFileInfo(bool shared_with_me) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     FillEntryPropertiesValueForDrive(
         *owner_resource_entry_, shared_with_me, properties_.get());
@@ -352,7 +352,7 @@
   }
 
   void CompleteGetEntryProperties(drive::FileError error) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(!callback_.is_null());
 
     callback_.Run(properties_.Pass(), drive::FileErrorToBaseFileError(error));
@@ -382,7 +382,7 @@
   static void Start(const storage::FileSystemURL file_system_url,
                     const std::set<EntryPropertyName>& names,
                     const ResultCallback& callback) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     SingleEntryPropertiesGetterForFileSystemProvider* instance =
         new SingleEntryPropertiesGetterForFileSystemProvider(file_system_url,
@@ -408,7 +408,7 @@
   }
 
   void StartProcess() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     FileSystemURLParser parser(file_system_url_);
     if (!parser.Parse()) {
@@ -434,7 +434,7 @@
 
   void OnGetMetadataCompleted(scoped_ptr<EntryMetadata> metadata,
                               base::File::Error result) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     if (result != base::File::FILE_OK) {
       CompleteGetEntryProperties(result);
@@ -452,7 +452,7 @@
   }
 
   void CompleteGetEntryProperties(base::File::Error result) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(!callback_.is_null());
 
     callback_.Run(properties_.Pass(), result);
@@ -483,7 +483,7 @@
 }
 
 bool FileManagerPrivateGetEntryPropertiesFunction::RunAsync() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   using api::file_manager_private::GetEntryProperties::Params;
   const scoped_ptr<Params> params(Params::Create(*args_));
@@ -533,7 +533,7 @@
     const storage::FileSystemURL& url,
     scoped_ptr<EntryProperties> properties,
     base::File::Error error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(0 <= processed_count_ && processed_count_ < properties_list_.size());
 
   properties_list_[index] = make_linked_ptr(properties.release());
@@ -552,7 +552,7 @@
 }
 
 bool FileManagerPrivatePinDriveFileFunction::RunAsync() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   using extensions::api::file_manager_private::PinDriveFile::Params;
   const scoped_ptr<Params> params(Params::Create(*args_));
@@ -580,7 +580,7 @@
 
 void FileManagerPrivatePinDriveFileFunction::
     OnPinStateSet(drive::FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error == drive::FILE_ERROR_OK) {
     SendResponse(true);
@@ -1025,7 +1025,7 @@
 void FileManagerPrivateGetDownloadUrlFunction::OnGetResourceEntry(
     drive::FileError error,
     scoped_ptr<drive::ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error != drive::FILE_ERROR_OK) {
     SetError("Download Url for this item is not available.");
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
index 6cf6825..4d10064 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
@@ -85,7 +85,7 @@
 
 // Returns EventRouter for the |profile_id| if available.
 file_manager::EventRouter* GetEventRouterByProfileId(void* profile_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // |profile_id| needs to be checked with ProfileManager::IsValidProfile
   // before using it.
@@ -104,7 +104,7 @@
     const FileSystemURL& source_url,
     const FileSystemURL& destination_url,
     int64 size) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   file_manager::EventRouter* event_router =
       GetEventRouterByProfileId(profile_id);
@@ -123,7 +123,7 @@
     const FileSystemURL& source_url,
     const FileSystemURL& destination_url,
     int64 size) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
@@ -139,7 +139,7 @@
     const FileSystemURL& source_url,
     const FileSystemURL& destination_url,
     base::File::Error error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   file_manager::EventRouter* event_router =
       GetEventRouterByProfileId(profile_id);
@@ -157,7 +157,7 @@
     const FileSystemURL& source_url,
     const FileSystemURL& destination_url,
     base::File::Error error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
@@ -172,7 +172,7 @@
     scoped_refptr<storage::FileSystemContext> file_system_context,
     const FileSystemURL& source_url,
     const FileSystemURL& destination_url) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   // Note: |operation_id| is owned by the callback for
   // FileSystemOperationRunner::Copy(). It is always called in the next message
@@ -193,7 +193,7 @@
 }
 
 void OnCopyCancelled(base::File::Error error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   // We just ignore the status if the copy is actually cancelled or not,
   // because failing cancellation means the operation is not running now.
@@ -205,7 +205,7 @@
 void CancelCopyOnIOThread(
     scoped_refptr<storage::FileSystemContext> file_system_context,
     storage::FileSystemOperationRunner::OperationID operation_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   file_system_context->operation_runner()->Cancel(
       operation_id, base::Bind(&OnCopyCancelled));
@@ -223,7 +223,7 @@
 void ComputeChecksumRespondOnUIThread(
     const base::Callback<void(const std::string&)>& callback,
     const std::string& hash) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                           base::Bind(callback, hash));
 }
@@ -233,7 +233,7 @@
     const storage::FileSystemOperation::GetMetadataCallback& callback,
     base::File::Error result,
     const base::File::Info& file_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                           base::Bind(callback, result, file_info));
 }
@@ -293,14 +293,14 @@
 }
 
 void FileWatchFunctionBase::Respond(bool success) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   SetResult(new base::FundamentalValue(success));
   SendResponse(success);
 }
 
 bool FileWatchFunctionBase::RunAsync() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!render_view_host() || !render_view_host()->GetProcess())
     return false;
@@ -330,7 +330,7 @@
     scoped_refptr<storage::FileSystemContext> file_system_context,
     const storage::FileSystemURL& file_system_url,
     const std::string& extension_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   file_manager::EventRouter* const event_router =
       file_manager::EventRouterFactory::GetForProfile(GetProfile());
@@ -358,7 +358,7 @@
     scoped_refptr<storage::FileSystemContext> file_system_context,
     const storage::FileSystemURL& file_system_url,
     const std::string& extension_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   file_manager::EventRouter* const event_router =
       file_manager::EventRouterFactory::GetForProfile(GetProfile());
@@ -518,20 +518,20 @@
     scoped_refptr<storage::FileSystemContext> file_system_context,
     const FileSystemURL& url,
     const storage::FileSystemOperation::GetMetadataCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   file_system_context->operation_runner()->GetMetadata(
       url, base::Bind(&GetFileMetadataRespondOnUIThread, callback));
 }
 
 // Checks if the available space of the |path| is enough for required |bytes|.
 bool CheckLocalDiskSpaceOnIOThread(const base::FilePath& path, int64 bytes) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   return bytes <= base::SysInfo::AmountOfFreeDiskSpace(path) -
                       cryptohome::kMinFreeSpaceInBytes;
 }
 
 bool FileManagerPrivateStartCopyFunction::RunAsync() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   using  extensions::api::file_manager_private::StartCopy::Params;
   const scoped_ptr<Params> params(Params::Create(*args_));
@@ -586,7 +586,7 @@
 void FileManagerPrivateStartCopyFunction::RunAfterGetFileMetadata(
     base::File::Error result,
     const base::File::Info& file_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (result != base::File::FILE_OK) {
     SetError("NotFoundError");
@@ -617,7 +617,7 @@
 
 void FileManagerPrivateStartCopyFunction::RunAfterFreeDiskSpace(
     bool available) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!available) {
     SetError("QuotaExceededError");
@@ -640,14 +640,14 @@
 
 void FileManagerPrivateStartCopyFunction::RunAfterStartCopy(
     int operation_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   SetResult(new base::FundamentalValue(operation_id));
   SendResponse(true);
 }
 
 bool FileManagerPrivateCancelCopyFunction::RunAsync() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   using extensions::api::file_manager_private::CancelCopy::Params;
   const scoped_ptr<Params> params(Params::Create(*args_));
@@ -780,7 +780,7 @@
 
 void FileManagerPrivateComputeChecksumFunction::Respond(
     const std::string& hash) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   SetResult(new base::StringValue(hash));
   SendResponse(true);
 }
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc
index 877801e..218574fe 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc
@@ -145,7 +145,7 @@
     const base::FilePath& display_name,
     drive::FileError error,
     const base::FilePath& file_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error != drive::FILE_ERROR_OK) {
     SendResponse(false);
diff --git a/chrome/browser/chromeos/extensions/input_method_api.cc b/chrome/browser/chromeos/extensions/input_method_api.cc
index b2c2472d..ba9c0f4 100644
--- a/chrome/browser/chromeos/extensions/input_method_api.cc
+++ b/chrome/browser/chromeos/extensions/input_method_api.cc
@@ -42,8 +42,8 @@
                      !base::CommandLine::ForCurrentProcess()->HasSwitch(
                          chromeos::switches::kDisableVoiceInput));
   output->SetBoolean("isNewMDInputViewEnabled",
-                     base::CommandLine::ForCurrentProcess()->HasSwitch(
-                         chromeos::switches::kEnableNewMDInputView));
+                     !base::CommandLine::ForCurrentProcess()->HasSwitch(
+                         chromeos::switches::kDisableNewMDInputView));
   return RespondNow(OneArgument(output));
 #endif
 }
diff --git a/chrome/browser/chromeos/extensions/wallpaper_function_base.cc b/chrome/browser/chromeos/extensions/wallpaper_function_base.cc
index 3adda8e..3331b7b 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_function_base.cc
+++ b/chrome/browser/chromeos/extensions/wallpaper_function_base.cc
@@ -42,34 +42,30 @@
 }  // namespace wallpaper_api_util
 
 class WallpaperFunctionBase::UnsafeWallpaperDecoder
-    : public ImageDecoder::Delegate {
+    : public ImageDecoder::ImageRequest {
  public:
   explicit UnsafeWallpaperDecoder(scoped_refptr<WallpaperFunctionBase> function)
-      : function_(function) {
-  }
+      : ImageRequest(
+            BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)),
+        function_(function) {}
 
   void Start(const std::vector<char>& image_data) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     // This function can only be called after user login. It is fine to use
     // unsafe image decoder here. Before user login, a robust jpeg decoder will
     // be used.
     CHECK(chromeos::LoginState::Get()->IsUserLoggedIn());
-    unsafe_image_decoder_ = new ImageDecoder(this, image_data,
-                                             ImageDecoder::DEFAULT_CODEC);
-    unsafe_image_decoder_->set_shrink_to_fit(true);
-
-    scoped_refptr<base::MessageLoopProxy> task_runner =
-        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
-    unsafe_image_decoder_->Start(task_runner);
+    std::string image_data_str(image_data.begin(), image_data.end());
+    ImageDecoder::StartWithOptions(this, image_data_str,
+                                   ImageDecoder::DEFAULT_CODEC, true);
   }
 
   void Cancel() {
     cancel_flag_.Set();
   }
 
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override {
+  void OnImageDecoded(const SkBitmap& decoded_image) override {
     // Make the SkBitmap immutable as we won't modify it. This is important
     // because otherwise it gets duplicated during painting, wasting memory.
     SkBitmap immutable(decoded_image);
@@ -85,7 +81,7 @@
     delete this;
   }
 
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override {
+  void OnDecodeImageFailed() override {
     function_->OnFailure(
         l10n_util::GetStringUTF8(IDS_WALLPAPER_MANAGER_INVALID_WALLPAPER));
     delete this;
@@ -93,7 +89,6 @@
 
  private:
   scoped_refptr<WallpaperFunctionBase> function_;
-  scoped_refptr<ImageDecoder> unsafe_image_decoder_;
   base::CancellationFlag cancel_flag_;
 
   DISALLOW_COPY_AND_ASSIGN(UnsafeWallpaperDecoder);
@@ -109,7 +104,7 @@
 }
 
 void WallpaperFunctionBase::StartDecode(const std::vector<char>& data) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (unsafe_wallpaper_decoder_)
     unsafe_wallpaper_decoder_->Cancel();
   unsafe_wallpaper_decoder_ = new UnsafeWallpaperDecoder(this);
diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
index d4983fe6..206f1376 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
+++ b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
@@ -148,6 +148,9 @@
   // aura::WindowObserver overrides.
   void OnWindowDestroyed(aura::Window* window) override;
 
+  // aura::WindowObserver overrides.
+  void OnWindowStackingChanged(aura::Window* window) override;
+
   // Map of user id hash and associated list of minimized windows.
   UserIDHashWindowListMap user_id_hash_window_list_map_;
 
@@ -203,7 +206,6 @@
     if (*iter == active_window || ash::wm::GetWindowState(*iter)->IsMinimized())
       continue;
 
-    // TODO(bshe): Add WindowStateObserver too. http://crbug.com/323252
     if (!(*iter)->HasObserver(this))
       (*iter)->AddObserver(this);
 
@@ -254,6 +256,16 @@
   }
 }
 
+void WindowStateManager::OnWindowStackingChanged(aura::Window* window) {
+  // If user interacted with the |window| while wallpaper picker is opening,
+  // removes the |window| from observed list.
+  for (auto iter = user_id_hash_window_list_map_.begin();
+       iter != user_id_hash_window_list_map_.end(); ++iter) {
+    iter->second.erase(window);
+  }
+  window->RemoveObserver(this);
+}
+
 }  // namespace
 
 bool WallpaperPrivateGetStringsFunction::RunSync() {
diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api_unittest.cc b/chrome/browser/chromeos/extensions/wallpaper_private_api_unittest.cc
index fc2c6652..16843b7c 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_private_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/wallpaper_private_api_unittest.cc
@@ -70,6 +70,7 @@
 
 TEST_F(WallpaperPrivateApiUnittest, HideAndRestoreWindows) {
   fake_user_manager()->AddUser(kTestAccount1);
+  scoped_ptr<aura::Window> window4(CreateTestWindowInShellWithId(4));
   scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(3));
   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(2));
   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(1));
@@ -79,6 +80,7 @@
   ash::wm::WindowState* window1_state = ash::wm::GetWindowState(window1.get());
   ash::wm::WindowState* window2_state = ash::wm::GetWindowState(window2.get());
   ash::wm::WindowState* window3_state = ash::wm::GetWindowState(window3.get());
+  ash::wm::WindowState* window4_state = ash::wm::GetWindowState(window4.get());
 
   window3_state->Minimize();
   window1_state->Maximize();
@@ -88,6 +90,7 @@
   EXPECT_FALSE(window1_state->IsMinimized());
   EXPECT_FALSE(window2_state->IsMinimized());
   EXPECT_TRUE(window3_state->IsMinimized());
+  EXPECT_FALSE(window4_state->IsMinimized());
 
   // We then activate window 0 (i.e. wallpaper picker) and call the minimize
   // function.
@@ -102,6 +105,11 @@
   EXPECT_TRUE(window1_state->IsMinimized());
   EXPECT_TRUE(window2_state->IsMinimized());
   EXPECT_TRUE(window3_state->IsMinimized());
+  EXPECT_TRUE(window4_state->IsMinimized());
+
+  // Activates window 4 and then minimizes it.
+  window4_state->Activate();
+  window4_state->Minimize();
 
   // Then we destroy window 0 and call the restore function.
   window0.reset();
@@ -110,10 +118,12 @@
   EXPECT_TRUE(restore_function->RunAsync());
 
   // Windows 1 and 2 should no longer be minimized. Window 1 should again
-  // be maximized. Window 3 should still be minimized.
+  // be maximized. Window 3 should still be minimized. Window 4 should remain
+  // minimized since user interacted with it while wallpaper picker was open.
   EXPECT_TRUE(window1_state->IsMaximized());
   EXPECT_FALSE(window2_state->IsMinimized());
   EXPECT_TRUE(window3_state->IsMinimized());
+  EXPECT_TRUE(window4_state->IsMinimized());
 }
 
 // Test for multiple calls to |MinimizeInactiveWindows| before call
diff --git a/chrome/browser/chromeos/external_metrics.cc b/chrome/browser/chromeos/external_metrics.cc
index d9fe9cd..2f1d1f9 100644
--- a/chrome/browser/chromeos/external_metrics.cc
+++ b/chrome/browser/chromeos/external_metrics.cc
@@ -50,7 +50,7 @@
 
 // Establishes field trial for wifi scanning in chromeos.  crbug.com/242733.
 void SetupProgressiveScanFieldTrial() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   const char name_of_experiment[] = "ProgressiveScan";
   const base::FilePath group_file_path(
       "/home/chronos/.progressive_scan_variation");
@@ -251,7 +251,7 @@
 }
 
 void ExternalMetrics::SetupFieldTrialsOnFileThread() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   // Field trials that do not read from files can be initialized in
   // ExternalMetrics::Start() above.
   SetupProgressiveScanFieldTrial();
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index b74efb3c..793e992 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -820,7 +820,8 @@
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
 // Fails on official build. http://crbug.com/429294
-#if !defined(NDEBUG) || defined(OFFICIAL_BUILD)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_FileDisplay DISABLED_FileDisplay
 #else
 #define MAYBE_FileDisplay FileDisplay
@@ -835,7 +836,8 @@
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
 // Fails on official build. http://crbug.com/429294
-#if !defined(NDEBUG) || defined(OFFICIAL_BUILD)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_OpenVideoFiles DISABLED_OpenVideoFiles
 #else
 #define MAYBE_OpenVideoFiles OpenVideoFiles
@@ -849,7 +851,8 @@
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
 // Fails on official build. http://crbug.com/429294
-#if !defined(NDEBUG) || defined(OFFICIAL_BUILD)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_OpenAudioFiles DISABLED_OpenAudioFiles
 #else
 #define MAYBE_OpenAudioFiles OpenAudioFiles
@@ -869,7 +872,8 @@
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
 // Fails on official build. http://crbug.com/429294
-#if !defined(NDEBUG) || defined(OFFICIAL_BUILD)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_OpenImageFiles DISABLED_OpenImageFiles
 #else
 #define MAYBE_OpenImageFiles OpenImageFiles
@@ -882,7 +886,8 @@
                       TestParameter(NOT_IN_GUEST_MODE, "imageOpenDrive")));
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
-#if !defined(NDEBUG) || defined(OFFICIAL_BUILD)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_CreateNewFolder DISABLED_CreateNewFolder
 #else
 #define MAYBE_CreateNewFolder CreateNewFolder
@@ -901,7 +906,8 @@
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
 // Fails on official build. http://crbug.com/429294
-#if !defined(NDEBUG) || defined(OFFICIAL_BUILD)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_KeyboardOperations DISABLED_KeyboardOperations
 #else
 #define MAYBE_KeyboardOperations KeyboardOperations
@@ -928,7 +934,8 @@
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
 // Fails on official build. http://crbug.com/429294
-#if !defined(NDEBUG) || defined(OFFICIAL_BUILD)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_DriveSpecific DISABLED_DriveSpecific
 #else
 #define MAYBE_DriveSpecific DriveSpecific
@@ -947,7 +954,8 @@
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
 // Fails on official build. http://crbug.com/429294
-#if !defined(NDEBUG) || defined(OFFICIAL_BUILD)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_Transfer DISABLED_Transfer
 #else
 #define MAYBE_Transfer Transfer
@@ -967,7 +975,8 @@
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
 // Fails on official build. http://crbug.com/429294
-#if !defined(NDEBUG) || defined(OFFICIAL_BUILD)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_RestorePrefs DISABLED_RestorePrefs
 #else
 #define MAYBE_RestorePrefs RestorePrefs
@@ -981,7 +990,8 @@
                       TestParameter(NOT_IN_GUEST_MODE, "restoreCurrentView")));
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
-#if !defined(NDEBUG)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(MEMORY_SANITIZER)
 #define MAYBE_ShareDialog DISABLED_ShareDialog
 #else
 #define MAYBE_ShareDialog ShareDialog
@@ -993,7 +1003,8 @@
                       TestParameter(NOT_IN_GUEST_MODE, "shareDirectory")));
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
-#if !defined(NDEBUG)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(MEMORY_SANITIZER)
 #define MAYBE_RestoreGeometry DISABLED_RestoreGeometry
 #else
 #define MAYBE_RestoreGeometry RestoreGeometry
@@ -1004,9 +1015,9 @@
     ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "restoreGeometry"),
                       TestParameter(IN_GUEST_MODE, "restoreGeometry")));
 
-// Slow tests are disabled on debug and msan build.
-// http://crbug.com/327719, 457365
-#if !defined(NDEBUG)
+// Slow tests are disabled on debug build. http://crbug.com/327719
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(MEMORY_SANITIZER)
 #define MAYBE_Traverse DISABLED_Traverse
 #else
 #define MAYBE_Traverse Traverse
@@ -1019,7 +1030,8 @@
                       TestParameter(NOT_IN_GUEST_MODE, "traverseDrive")));
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
-#if !defined(NDEBUG)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(MEMORY_SANITIZER)
 #define MAYBE_SuggestAppDialog DISABLED_SuggestAppDialog
 #else
 #define MAYBE_SuggestAppDialog SuggestAppDialog
@@ -1030,7 +1042,8 @@
     ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "suggestAppDialog")));
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
-#if !defined(NDEBUG)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(MEMORY_SANITIZER)
 #define MAYBE_ExecuteDefaultTaskOnDownloads \
   DISABLED_ExecuteDefaultTaskOnDownloads
 #else
@@ -1044,7 +1057,8 @@
         TestParameter(IN_GUEST_MODE, "executeDefaultTaskOnDownloads")));
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
-#if !defined(NDEBUG)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(MEMORY_SANITIZER)
 #define MAYBE_ExecuteDefaultTaskOnDrive DISABLED_ExecuteDefaultTaskOnDrive
 #else
 #define MAYBE_ExecuteDefaultTaskOnDrive ExecuteDefaultTaskOnDrive
@@ -1056,7 +1070,8 @@
         TestParameter(NOT_IN_GUEST_MODE, "executeDefaultTaskOnDrive")));
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
-#if !defined(NDEBUG)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(MEMORY_SANITIZER)
 #define MAYBE_DefaultActionDialog DISABLED_DefaultActionDialog
 #else
 #define MAYBE_DefaultActionDialog DefaultActionDialog
@@ -1070,7 +1085,8 @@
         TestParameter(NOT_IN_GUEST_MODE, "defaultActionDialogOnDrive")));
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
-#if !defined(NDEBUG)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(MEMORY_SANITIZER)
 #define MAYBE_GenericTask DISABLED_GenericTask
 #else
 #define MAYBE_GenericTask GenericTask
@@ -1083,7 +1099,8 @@
         TestParameter(NOT_IN_GUEST_MODE, "genericAndNonGenericTasksAreMixed")));
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
-#if !defined(NDEBUG)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(MEMORY_SANITIZER)
 #define MAYBE_FolderShortcuts DISABLED_FolderShortcuts
 #else
 #define MAYBE_FolderShortcuts FolderShortcuts
@@ -1112,7 +1129,8 @@
                                     "tabindexFocusDirectorySelected")));
 
 // Fails on official build. http://crbug.com/429294
-#if !defined(NDEBUG) || defined(OFFICIAL_BUILD)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_OpenFileDialog DISABLED_OpenFileDialog
 #else
 #define MAYBE_OpenFileDialog OpenFileDialog
@@ -1152,7 +1170,8 @@
         TestParameter(NOT_IN_GUEST_MODE, "copyBetweenWindowsUsbToLocal")));
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
-#if !defined(NDEBUG)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(MEMORY_SANITIZER)
 #define MAYBE_ShowGridView DISABLED_ShowGridView
 #else
 #define MAYBE_ShowGridView ShowGridView
@@ -1250,7 +1269,7 @@
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
 // Fails on official build. http://crbug.com/429294
-// Disabled under MSAN as well. http://crbug.com/468982.
+// Disabled under MSAN as well. http://crbug.com/468980.
 #if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_PRE_BasicDownloads DISABLED_PRE_BasicDownloads
 #define MAYBE_BasicDownloads DISABLED_BasicDownloads
@@ -1274,7 +1293,8 @@
 
 // Slow tests are disabled on debug build. http://crbug.com/327719
 // Fails on official build. http://crbug.com/429294
-#if !defined(NDEBUG) || defined(OFFICIAL_BUILD)
+// Disabled under MSAN as well. http://crbug.com/468980.
+#if !defined(NDEBUG) || defined(OFFICIAL_BUILD) || defined(MEMORY_SANITIZER)
 #define MAYBE_PRE_BasicDrive DISABLED_PRE_BasicDrive
 #define MAYBE_BasicDrive DISABLED_BasicDrive
 #else
diff --git a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
index 0e71d93..5ee2b0c 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
@@ -572,36 +572,41 @@
   // Foo.app can handle ".txt" and ".html".
   // This one is an extension, and has "file_browser_handlers"
   extensions::ExtensionBuilder foo_app;
-  foo_app.SetManifest(extensions::DictionaryBuilder()
-                      .Set("name", "Foo")
-                      .Set("version", "1.0.0")
-                      .Set("manifest_version", 2)
-                      .Set("file_browser_handlers",
-                           extensions::ListBuilder()
-                           .Append(extensions::DictionaryBuilder()
-                                   .Set("id", "open")
-                                   .Set("default_title", "open")
-                                   .Set("file_filters",
-                                        extensions::ListBuilder()
-                                        .Append("filesystem:*.txt")
-                                        .Append("filesystem:*.html")))));
+  foo_app.SetManifest(
+      extensions::DictionaryBuilder()
+          .Set("name", "Foo")
+          .Set("version", "1.0.0")
+          .Set("manifest_version", 2)
+          .Set("permissions",
+               extensions::ListBuilder().Append("fileBrowserHandler"))
+          .Set(
+              "file_browser_handlers",
+              extensions::ListBuilder().Append(
+                  extensions::DictionaryBuilder()
+                      .Set("id", "open")
+                      .Set("default_title", "open")
+                      .Set("file_filters", extensions::ListBuilder()
+                                               .Append("filesystem:*.txt")
+                                               .Append("filesystem:*.html")))));
   foo_app.SetID(kFooId);
   extension_service_->AddExtension(foo_app.Build().get());
 
   // Bar.app can only handle ".txt".
   extensions::ExtensionBuilder bar_app;
-  bar_app.SetManifest(extensions::DictionaryBuilder()
-                      .Set("name", "Bar")
-                      .Set("version", "1.0.0")
-                      .Set("manifest_version", 2)
-                      .Set("file_browser_handlers",
-                           extensions::ListBuilder()
-                           .Append(extensions::DictionaryBuilder()
-                                   .Set("id", "open")
-                                   .Set("default_title", "open")
-                                   .Set("file_filters",
-                                        extensions::ListBuilder()
-                                        .Append("filesystem:*.txt")))));
+  bar_app.SetManifest(
+      extensions::DictionaryBuilder()
+          .Set("name", "Bar")
+          .Set("version", "1.0.0")
+          .Set("manifest_version", 2)
+          .Set("permissions",
+               extensions::ListBuilder().Append("fileBrowserHandler"))
+          .Set("file_browser_handlers",
+               extensions::ListBuilder().Append(
+                   extensions::DictionaryBuilder()
+                       .Set("id", "open")
+                       .Set("default_title", "open")
+                       .Set("file_filters", extensions::ListBuilder().Append(
+                                                "filesystem:*.txt")))));
   bar_app.SetID(kBarId);
   extension_service_->AddExtension(bar_app.Build().get());
 
@@ -614,14 +619,15 @@
           .Set("name", "Ephemeral")
           .Set("version", "1.0.0")
           .Set("manifest_version", 2)
+          .Set("permissions",
+               extensions::ListBuilder().Append("fileBrowserHandler"))
           .Set("file_browser_handlers",
                extensions::ListBuilder().Append(
                    extensions::DictionaryBuilder()
                        .Set("id", "open")
                        .Set("default_title", "open")
-                       .Set("file_filters",
-                            extensions::ListBuilder().Append(
-                                "filesystem:*.txt")))));
+                       .Set("file_filters", extensions::ListBuilder().Append(
+                                                "filesystem:*.txt")))));
   ephemeral_app.SetID(kEphemeralId);
   scoped_refptr<extensions::Extension> built_ephemeral_app(
       ephemeral_app.Build());
@@ -704,18 +710,20 @@
   // Bar.app can only handle ".txt".
   // This is an extension (file browser handler).
   extensions::ExtensionBuilder bar_app;
-  bar_app.SetManifest(extensions::DictionaryBuilder()
-                      .Set("name", "Bar")
-                      .Set("version", "1.0.0")
-                      .Set("manifest_version", 2)
-                      .Set("file_browser_handlers",
-                           extensions::ListBuilder()
-                           .Append(extensions::DictionaryBuilder()
-                                   .Set("id", "open")
-                                   .Set("default_title", "open")
-                                   .Set("file_filters",
-                                        extensions::ListBuilder()
-                                        .Append("filesystem:*.txt")))));
+  bar_app.SetManifest(
+      extensions::DictionaryBuilder()
+          .Set("name", "Bar")
+          .Set("version", "1.0.0")
+          .Set("manifest_version", 2)
+          .Set("permissions",
+               extensions::ListBuilder().Append("fileBrowserHandler"))
+          .Set("file_browser_handlers",
+               extensions::ListBuilder().Append(
+                   extensions::DictionaryBuilder()
+                       .Set("id", "open")
+                       .Set("default_title", "open")
+                       .Set("file_filters", extensions::ListBuilder().Append(
+                                                "filesystem:*.txt")))));
   bar_app.SetID(kBarId);
   extension_service_->AddExtension(bar_app.Build().get());
 
@@ -793,18 +801,20 @@
   // Bar.app can handle ".gdoc" files.
   // This is an extension (file browser handler).
   extensions::ExtensionBuilder bar_app;
-  bar_app.SetManifest(extensions::DictionaryBuilder()
-                      .Set("name", "Bar")
-                      .Set("version", "1.0.0")
-                      .Set("manifest_version", 2)
-                      .Set("file_browser_handlers",
-                           extensions::ListBuilder()
-                           .Append(extensions::DictionaryBuilder()
-                                   .Set("id", "open")
-                                   .Set("default_title", "open")
-                                   .Set("file_filters",
-                                        extensions::ListBuilder()
-                                        .Append("filesystem:*.gdoc")))));
+  bar_app.SetManifest(
+      extensions::DictionaryBuilder()
+          .Set("name", "Bar")
+          .Set("version", "1.0.0")
+          .Set("manifest_version", 2)
+          .Set("permissions",
+               extensions::ListBuilder().Append("fileBrowserHandler"))
+          .Set("file_browser_handlers",
+               extensions::ListBuilder().Append(
+                   extensions::DictionaryBuilder()
+                       .Set("id", "open")
+                       .Set("default_title", "open")
+                       .Set("file_filters", extensions::ListBuilder().Append(
+                                                "filesystem:*.gdoc")))));
   bar_app.SetID(kBarId);
   extension_service_->AddExtension(bar_app.Build().get());
 
@@ -812,18 +822,20 @@
   // The ID "kFileManagerAppId" used here is precisely the one that identifies
   // the Chrome OS Files.app application.
   extensions::ExtensionBuilder files_app;
-  files_app.SetManifest(extensions::DictionaryBuilder()
-                       .Set("name", "Files")
-                       .Set("version", "1.0.0")
-                       .Set("manifest_version", 2)
-                       .Set("file_browser_handlers",
-                            extensions::ListBuilder()
-                            .Append(extensions::DictionaryBuilder()
-                                    .Set("id", "open")
-                                    .Set("default_title", "open")
-                                    .Set("file_filters",
-                                         extensions::ListBuilder()
-                                         .Append("filesystem:*.gdoc")))));
+  files_app.SetManifest(
+      extensions::DictionaryBuilder()
+          .Set("name", "Files")
+          .Set("version", "1.0.0")
+          .Set("manifest_version", 2)
+          .Set("permissions",
+               extensions::ListBuilder().Append("fileBrowserHandler"))
+          .Set("file_browser_handlers",
+               extensions::ListBuilder().Append(
+                   extensions::DictionaryBuilder()
+                       .Set("id", "open")
+                       .Set("default_title", "open")
+                       .Set("file_filters", extensions::ListBuilder().Append(
+                                                "filesystem:*.gdoc")))));
   files_app.SetID(kFileManagerAppId);
   extension_service_->AddExtension(files_app.Build().get());
 
diff --git a/chrome/browser/chromeos/fileapi/external_file_url_request_job.cc b/chrome/browser/chromeos/fileapi/external_file_url_request_job.cc
index 972f68d..ca8aea8f 100644
--- a/chrome/browser/chromeos/fileapi/external_file_url_request_job.cc
+++ b/chrome/browser/chromeos/fileapi/external_file_url_request_job.cc
@@ -241,7 +241,7 @@
 
 void ExternalFileURLRequestJob::OnRedirectURLObtained(
     const GURL& redirect_url) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   redirect_url_ = redirect_url;
   if (!redirect_url_.is_empty()) {
     NotifyHeadersComplete();
@@ -258,7 +258,7 @@
 void ExternalFileURLRequestJob::OnFileInfoObtained(
     base::File::Error result,
     const base::File::Info& file_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (result == base::File::FILE_ERROR_NOT_FOUND) {
     NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
@@ -298,7 +298,7 @@
 }
 
 void ExternalFileURLRequestJob::Kill() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   stream_reader_.reset();
   isolated_file_system_scope_.reset();
@@ -308,14 +308,14 @@
 }
 
 bool ExternalFileURLRequestJob::GetMimeType(std::string* mime_type) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   mime_type->assign(mime_type_);
   return !mime_type->empty();
 }
 
 bool ExternalFileURLRequestJob::IsRedirectResponse(GURL* location,
                                                    int* http_status_code) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (redirect_url_.is_empty())
     return false;
 
@@ -329,7 +329,7 @@
 bool ExternalFileURLRequestJob::ReadRawData(net::IOBuffer* buf,
                                             int buf_size,
                                             int* bytes_read) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(stream_reader_);
 
   if (remaining_bytes_ == 0) {
@@ -364,7 +364,7 @@
 }
 
 void ExternalFileURLRequestJob::OnReadCompleted(int read_result) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (read_result < 0) {
     DCHECK_NE(read_result, net::ERR_IO_PENDING);
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.cc b/chrome/browser/chromeos/fileapi/file_system_backend.cc
index a0a7580..96f88bd 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend.cc
@@ -39,11 +39,9 @@
     FileSystemBackendDelegate* drive_delegate,
     FileSystemBackendDelegate* file_system_provider_delegate,
     FileSystemBackendDelegate* mtp_delegate,
-    scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
     scoped_refptr<storage::ExternalMountPoints> mount_points,
     storage::ExternalMountPoints* system_mount_points)
-    : special_storage_policy_(special_storage_policy),
-      file_access_permissions_(new FileAccessPermissions()),
+    : file_access_permissions_(new FileAccessPermissions()),
       local_file_util_(storage::AsyncFileUtil::CreateForLocalFileSystem()),
       drive_delegate_(drive_delegate),
       file_system_provider_delegate_(file_system_provider_delegate),
@@ -185,25 +183,12 @@
     return true;
   }
 
-  // Check first to make sure this extension has fileBrowserHander permissions.
-  if (!special_storage_policy_.get() ||
-      !special_storage_policy_->IsFileHandler(extension_id))
-    return false;
-
   return file_access_permissions_->HasAccessPermission(extension_id,
                                                        url.virtual_path());
 }
 
 void FileSystemBackend::GrantFileAccessToExtension(
     const std::string& extension_id, const base::FilePath& virtual_path) {
-  if (!special_storage_policy_.get())
-    return;
-  // All we care about here is access from extensions for now.
-  if (!special_storage_policy_->IsFileHandler(extension_id)) {
-    NOTREACHED();
-    return;
-  }
-
   std::string id;
   storage::FileSystemType type;
   std::string cracked_id;
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.h b/chrome/browser/chromeos/fileapi/file_system_backend.h
index 529a1dc..af402868 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend.h
+++ b/chrome/browser/chromeos/fileapi/file_system_backend.h
@@ -14,7 +14,6 @@
 #include "base/memory/scoped_ptr.h"
 #include "storage/browser/fileapi/file_system_backend.h"
 #include "storage/browser/fileapi/task_runner_bound_observer_list.h"
-#include "storage/browser/quota/special_storage_policy.h"
 #include "storage/common/fileapi/file_system_types.h"
 
 namespace storage {
@@ -74,7 +73,6 @@
       FileSystemBackendDelegate* drive_delegate,
       FileSystemBackendDelegate* file_system_provider_delegate,
       FileSystemBackendDelegate* mtp_delegate,
-      scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
       scoped_refptr<storage::ExternalMountPoints> mount_points,
       storage::ExternalMountPoints* system_mount_points);
   ~FileSystemBackend() override;
@@ -142,7 +140,6 @@
       const base::FilePath& entry_path) const override;
 
  private:
-  scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy_;
   scoped_ptr<FileAccessPermissions> file_access_permissions_;
   scoped_ptr<storage::AsyncFileUtil> local_file_util_;
 
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
index cf304963..f163faf 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
@@ -8,7 +8,6 @@
 
 #include "base/files/file_path.h"
 #include "chromeos/dbus/cros_disks_client.h"
-#include "content/public/test/mock_special_storage_policy.h"
 #include "storage/browser/fileapi/external_mount_points.h"
 #include "storage/browser/fileapi/file_system_url.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -35,15 +34,12 @@
   // to avoid flakiness.
   storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems();
 
-  scoped_refptr<storage::SpecialStoragePolicy> storage_policy =
-      new content::MockSpecialStoragePolicy();
   scoped_refptr<storage::ExternalMountPoints> mount_points(
       storage::ExternalMountPoints::CreateRefCounted());
   chromeos::FileSystemBackend backend(
       NULL,  // drive_delegate
       NULL,  // file_system_provider_delegate
       NULL,  // mtp_delegate
-      storage_policy,
       mount_points.get(),
       storage::ExternalMountPoints::GetSystemInstance());
   backend.AddSystemMountPoints();
@@ -61,8 +57,6 @@
 }
 
 TEST(ChromeOSFileSystemBackendTest, GetRootDirectories) {
-  scoped_refptr<storage::SpecialStoragePolicy> storage_policy =
-      new content::MockSpecialStoragePolicy();
   scoped_refptr<storage::ExternalMountPoints> mount_points(
       storage::ExternalMountPoints::CreateRefCounted());
 
@@ -72,7 +66,6 @@
   chromeos::FileSystemBackend backend(NULL,  // drive_delegate
                                       NULL,  // file_system_provider_delegate
                                       NULL,  // mtp_delegate
-                                      storage_policy,
                                       mount_points.get(),
                                       system_mount_points.get());
 
@@ -110,8 +103,6 @@
 TEST(ChromeOSFileSystemBackendTest, AccessPermissions) {
   url::AddStandardScheme("chrome-extension");
 
-  scoped_refptr<content::MockSpecialStoragePolicy> storage_policy =
-      new content::MockSpecialStoragePolicy();
   scoped_refptr<storage::ExternalMountPoints> mount_points(
       storage::ExternalMountPoints::CreateRefCounted());
   scoped_refptr<storage::ExternalMountPoints> system_mount_points(
@@ -119,14 +110,11 @@
   chromeos::FileSystemBackend backend(NULL,  // drive_delegate
                                       NULL,  // file_system_provider_delegate
                                       NULL,  // mtp_delegate
-                                      storage_policy,
                                       mount_points.get(),
                                       system_mount_points.get());
 
   std::string extension("ddammdhioacbehjngdmkjcjbnfginlla");
 
-  storage_policy->AddFileHandler(extension);
-
   // Initialize mount points.
   ASSERT_TRUE(system_mount_points->RegisterFileSystem(
       "system",
@@ -183,8 +171,6 @@
 }
 
 TEST(ChromeOSFileSystemBackendTest, GetVirtualPathConflictWithSystemPoints) {
-  scoped_refptr<content::MockSpecialStoragePolicy> storage_policy =
-      new content::MockSpecialStoragePolicy();
   scoped_refptr<storage::ExternalMountPoints> mount_points(
       storage::ExternalMountPoints::CreateRefCounted());
   scoped_refptr<storage::ExternalMountPoints> system_mount_points(
@@ -192,7 +178,6 @@
   chromeos::FileSystemBackend backend(NULL,  // drive_delegate
                                       NULL,  // file_system_provider_delegate
                                       NULL,  // mtp_delegate
-                                      storage_policy,
                                       mount_points.get(),
                                       system_mount_points.get());
 
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index da230ba..caa0933 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -326,7 +326,7 @@
 }
 
 void ChromeRestartRequest::RestartJob() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   DBusThreadManager::Get()->GetSessionManagerClient()->RestartJob(
       pid_, command_line_);
@@ -364,7 +364,7 @@
 }
 
 void RestartChrome(const std::string& command_line) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   BootTimesRecorder::Get()->set_restart_requested();
 
   static bool restart_requested = false;
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc
index aa9615b1..db71d440 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -589,9 +589,15 @@
 
   StopPublicSessionAutoLoginTimer();
 
+  // Truth table of |has_auth_cookies|:
+  //                          Regular        SAML
+  //  /ServiceLogin              T            T
+  //  /ChromeOsEmbeddedSetup     F            T
+  //  Bootstrap experiment       F            N/A
   const bool has_auth_cookies =
       login_performer_->auth_mode() == LoginPerformer::AUTH_MODE_EXTENSION &&
-      user_context.GetAuthCode().empty() &&
+      (user_context.GetAuthCode().empty() ||
+       user_context.GetAuthFlow() == UserContext::AUTH_FLOW_GAIA_WITH_SAML) &&
       user_context.GetAuthFlow() != UserContext::AUTH_FLOW_EASY_BOOTSTRAP;
 
   // LoginPerformer instance will delete itself in case of successful auth.
diff --git a/chrome/browser/chromeos/login/help_app_launcher.cc b/chrome/browser/chromeos/login/help_app_launcher.cc
index 4a046c7..bc6cd6a 100644
--- a/chrome/browser/chromeos/login/help_app_launcher.cc
+++ b/chrome/browser/chromeos/login/help_app_launcher.cc
@@ -63,7 +63,7 @@
 
 void HelpAppLauncher::ShowHelpTopicDialog(Profile* profile,
                                           const GURL& topic_url) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   LoginWebDialog* dialog = new LoginWebDialog(
       profile,
       NULL,
diff --git a/chrome/browser/chromeos/login/helper.cc b/chrome/browser/chromeos/login/helper.cc
index efb9890..692c8315 100644
--- a/chrome/browser/chromeos/login/helper.cc
+++ b/chrome/browser/chromeos/login/helper.cc
@@ -4,13 +4,22 @@
 
 #include "chrome/browser/chromeos/login/helper.h"
 
+#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
+#include "chrome/browser/chromeos/login/ui/webui_login_view.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/guest_view/guest_view_manager.h"
+#include "extensions/browser/guest_view/web_view/web_view_guest.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/image/image_skia.h"
@@ -18,6 +27,57 @@
 
 namespace chromeos {
 
+namespace {
+
+// Gets the WebContents instance of current login display. If there is none,
+// returns nullptr.
+content::WebContents* GetLoginWebContents() {
+  LoginDisplayHost* host = LoginDisplayHostImpl::default_host();
+  if (!host || !host->GetWebUILoginView())
+    return nullptr;
+
+  return host->GetWebUILoginView()->GetWebContents();
+}
+
+// Callback used by GetPartition below to return the first guest contents with a
+// matching partition name.
+bool FindGuestByPartitionName(const std::string& partition_name,
+                              content::WebContents** out_guest_contents,
+                              content::WebContents* guest_contents) {
+  std::string domain;
+  std::string name;
+  bool in_memory;
+  extensions::WebViewGuest::GetGuestPartitionConfigForSite(
+      guest_contents->GetSiteInstance()->GetSiteURL(), &domain, &name,
+      &in_memory);
+  if (partition_name != name)
+    return false;
+
+  *out_guest_contents = guest_contents;
+  return true;
+}
+
+// Gets the storage partition of guest contents of a given embedder.
+// If a name is given, returns the partition associated with the name.
+// Otherwise, returns the default shared in-memory partition. Returns nullptr if
+// a matching partition could not be found.
+content::StoragePartition* GetPartition(content::WebContents* embedder,
+                                        const std::string& partition_name) {
+  extensions::GuestViewManager* manager =
+      extensions::GuestViewManager::FromBrowserContext(
+          embedder->GetBrowserContext());
+  content::WebContents* guest_contents = nullptr;
+  manager->ForEachGuest(embedder, base::Bind(&FindGuestByPartitionName,
+                                             partition_name, &guest_contents));
+
+  return guest_contents ? content::BrowserContext::GetStoragePartition(
+                              guest_contents->GetBrowserContext(),
+                              guest_contents->GetSiteInstance())
+                        : nullptr;
+}
+
+}  // namespace
+
 gfx::Rect CalculateScreenBounds(const gfx::Size& size) {
   gfx::Rect bounds =
       gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().bounds();
@@ -82,6 +142,19 @@
       chromeos::NetworkTypePattern::Default()) != NULL;
 }
 
+content::StoragePartition* GetSigninPartition() {
+  // Note the partition name must match the sign-in webview used. For now,
+  // this is the default unnamed, shared, in-memory partition.
+  return GetPartition(GetLoginWebContents(), std::string());
+}
+
+net::URLRequestContextGetter* GetSigninContext() {
+  if (StartupUtils::IsWebviewSigninEnabled())
+    return GetSigninPartition()->GetURLRequestContext();
+
+  return ProfileHelper::GetSigninProfile()->GetRequestContext();
+}
+
 }  // namespace login
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/helper.h b/chrome/browser/chromeos/login/helper.h
index ef4e870c..2809d66 100644
--- a/chrome/browser/chromeos/login/helper.h
+++ b/chrome/browser/chromeos/login/helper.h
@@ -21,6 +21,14 @@
 class Size;
 }  // namespace gfx
 
+namespace content {
+class StoragePartition;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
 namespace chromeos {
 
 // Returns bounds of the screen to use for login wizard.
@@ -66,6 +74,20 @@
   DISALLOW_COPY_AND_ASSIGN(NetworkStateHelper);
 };
 
+//
+// Webview based login helpers.
+//
+
+// Returns the storage partition for the sign-in webview. Note the function gets
+// the partition via the sign-in WebContents thus returns nullptr if the sign-in
+// webui is torn down.
+content::StoragePartition* GetSigninPartition();
+
+// Returns the request context that contains sign-in cookies. For old iframe
+// based flow, the context of the sign-in profile is returned. For webview based
+// flow, the context of the sign-in webview storage partition is returned.
+net::URLRequestContextGetter* GetSigninContext();
+
 }  // namespace login
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/profile_auth_data.cc b/chrome/browser/chromeos/login/profile_auth_data.cc
index d75c9d0..f8aef08 100644
--- a/chrome/browser/chromeos/login/profile_auth_data.cc
+++ b/chrome/browser/chromeos/login/profile_auth_data.cc
@@ -40,8 +40,8 @@
 class ProfileAuthDataTransferer {
  public:
   ProfileAuthDataTransferer(
-      content::BrowserContext* from_context,
-      content::BrowserContext* to_context,
+      net::URLRequestContextGetter* from_context,
+      net::URLRequestContextGetter* to_context,
       bool transfer_auth_cookies_and_channel_ids_on_first_login,
       bool transfer_saml_auth_cookies_on_subsequent_login,
       const base::Closure& completion_callback);
@@ -119,13 +119,13 @@
 };
 
 ProfileAuthDataTransferer::ProfileAuthDataTransferer(
-    content::BrowserContext* from_context,
-    content::BrowserContext* to_context,
+    net::URLRequestContextGetter* from_context,
+    net::URLRequestContextGetter* to_context,
     bool transfer_auth_cookies_and_channel_ids_on_first_login,
     bool transfer_saml_auth_cookies_on_subsequent_login,
     const base::Closure& completion_callback)
-    : from_context_(from_context->GetRequestContext()),
-      to_context_(to_context->GetRequestContext()),
+    : from_context_(from_context),
+      to_context_(to_context),
       transfer_auth_cookies_and_channel_ids_on_first_login_(
           transfer_auth_cookies_and_channel_ids_on_first_login),
       transfer_saml_auth_cookies_on_subsequent_login_(
@@ -137,7 +137,7 @@
 }
 
 void ProfileAuthDataTransferer::BeginTransfer() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // If we aren't transferring auth cookies or channel IDs, post the completion
   // callback immediately. Otherwise, it will be called when the transfer
   // finishes.
@@ -155,7 +155,7 @@
 }
 
 void ProfileAuthDataTransferer::BeginTransferOnIOThread() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   TransferProxyAuthCache();
   if (transfer_auth_cookies_and_channel_ids_on_first_login_ ||
       transfer_saml_auth_cookies_on_subsequent_login_) {
@@ -173,7 +173,7 @@
 }
 
 void ProfileAuthDataTransferer::TransferProxyAuthCache() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   net::HttpAuthCache* new_cache = to_context_->GetURLRequestContext()->
       http_transaction_factory()->GetSession()->http_auth_cache();
   new_cache->UpdateAllFrom(*from_context_->GetURLRequestContext()->
@@ -182,7 +182,7 @@
 
 void ProfileAuthDataTransferer::OnTargetCookieJarContentsRetrieved(
     const net::CookieList& target_cookies) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   first_login_ = target_cookies.empty();
   if (first_login_) {
     // On first login, transfer all auth cookies and channel IDs if
@@ -209,7 +209,7 @@
 }
 
 void ProfileAuthDataTransferer::RetrieveCookiesToTransfer() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   net::CookieStore* from_store =
       from_context_->GetURLRequestContext()->cookie_store();
   net::CookieMonster* from_monster = from_store->GetCookieMonster();
@@ -221,7 +221,7 @@
 
 void ProfileAuthDataTransferer::OnCookiesToTransferRetrieved(
     const net::CookieList& cookies_to_transfer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   waiting_for_auth_cookies_ = false;
   cookies_to_transfer_ = cookies_to_transfer;
 
@@ -247,7 +247,7 @@
 }
 
 void ProfileAuthDataTransferer::RetrieveChannelIDsToTransfer() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   net::ChannelIDService* from_service =
       from_context_->GetURLRequestContext()->channel_id_service();
   from_service->GetChannelIDStore()->GetAllChannelIDs(
@@ -258,7 +258,7 @@
 
 void ProfileAuthDataTransferer::OnChannelIDsToTransferRetrieved(
     const net::ChannelIDStore::ChannelIDList& channel_ids_to_transfer) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   channel_ids_to_transfer_ = channel_ids_to_transfer;
   waiting_for_channel_ids_ = false;
   MaybeTransferCookiesAndChannelIDs();
@@ -278,7 +278,7 @@
 }
 
 void ProfileAuthDataTransferer::MaybeTransferCookiesAndChannelIDs() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (waiting_for_auth_cookies_ || waiting_for_channel_ids_)
     return;
 
@@ -305,7 +305,7 @@
 }
 
 void ProfileAuthDataTransferer::Finish() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!completion_callback_.is_null())
     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, completion_callback_);
   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
@@ -314,12 +314,12 @@
 }  // namespace
 
 void ProfileAuthData::Transfer(
-    content::BrowserContext* from_context,
-    content::BrowserContext* to_context,
+    net::URLRequestContextGetter* from_context,
+    net::URLRequestContextGetter* to_context,
     bool transfer_auth_cookies_and_channel_ids_on_first_login,
     bool transfer_saml_auth_cookies_on_subsequent_login,
     const base::Closure& completion_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   (new ProfileAuthDataTransferer(
        from_context,
        to_context,
diff --git a/chrome/browser/chromeos/login/profile_auth_data.h b/chrome/browser/chromeos/login/profile_auth_data.h
index 59a35a2..5984feb7 100644
--- a/chrome/browser/chromeos/login/profile_auth_data.h
+++ b/chrome/browser/chromeos/login/profile_auth_data.h
@@ -8,8 +8,8 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 
-namespace content {
-class BrowserContext;
+namespace net {
+class URLRequestContextGetter;
 }
 
 namespace chromeos {
@@ -31,8 +31,8 @@
   //   |transfer_saml_auth_cookies_on_subsequent_login| is true and
   //   |to_context|'s cookie jar is not empty.
   static void Transfer(
-      content::BrowserContext* from_context,
-      content::BrowserContext* to_context,
+      net::URLRequestContextGetter* from_context,
+      net::URLRequestContextGetter* to_context,
       bool transfer_auth_cookies_and_channel_ids_on_first_login,
       bool transfer_saml_auth_cookies_on_subsequent_login,
       const base::Closure& completion_callback);
diff --git a/chrome/browser/chromeos/login/profile_auth_data_unittest.cc b/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
index bf6bc33c..d2385fae 100644
--- a/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
+++ b/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
@@ -128,8 +128,8 @@
     bool transfer_saml_auth_cookies_on_subsequent_login) {
   base::RunLoop run_loop;
   ProfileAuthData::Transfer(
-      &login_browser_context_,
-      &user_browser_context_,
+      login_browser_context_.GetRequestContext(),
+      user_browser_context_.GetRequestContext(),
       transfer_auth_cookies_and_channel_ids_on_first_login,
       transfer_saml_auth_cookies_on_subsequent_login,
       run_loop.QuitClosure());
diff --git a/chrome/browser/chromeos/login/saml/saml_browsertest.cc b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
index 047a7aa..9bfde3e 100644
--- a/chrome/browser/chromeos/login/saml/saml_browsertest.cc
+++ b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
@@ -293,23 +293,28 @@
 
   bool SetUpUserDataDirectory() override {
     if (UseWebView()) {
-      // Enable webview signin.
+      // Fake Dev channel to enable webview signin.
       scoped_channel_.reset(new extensions::ScopedCurrentChannel(
           chrome::VersionInfo::CHANNEL_DEV));
 
-      base::DictionaryValue local_state_dict;
-      local_state_dict.SetBoolean(prefs::kWebviewSigninEnabled, true);
-      // OobeCompleted to skip controller-pairing-screen which still uses
-      // iframe and ends up in a JS error in oobe page init.
-      // See http://crbug.com/467147
-      local_state_dict.SetBoolean(prefs::kOobeComplete, true);
-
       base::FilePath user_data_dir;
       CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
       base::FilePath local_state_path =
           user_data_dir.Append(chrome::kLocalStateFilename);
-      CHECK(JSONFileValueSerializer(local_state_path)
-                .Serialize(local_state_dict));
+
+      // Set webview enabled flag only when local state file does not exist.
+      // Otherwise, we break PRE tests that leave state in it.
+      if (!base::PathExists(local_state_path)) {
+        base::DictionaryValue local_state_dict;
+        local_state_dict.SetBoolean(prefs::kWebviewSigninEnabled, true);
+        // OobeCompleted to skip controller-pairing-screen which still uses
+        // iframe and ends up in a JS error in oobe page init.
+        // See http://crbug.com/467147
+        local_state_dict.SetBoolean(prefs::kOobeComplete, true);
+
+        CHECK(JSONFileValueSerializer(local_state_path)
+                  .Serialize(local_state_dict));
+      }
     }
 
     return InProcessBrowserTest::SetUpUserDataDirectory();
@@ -1250,6 +1255,6 @@
 // TODO(xiyuan): Update once cookies are properly handled.
 INSTANTIATE_TEST_CASE_P(SamlSuite,
                         SAMLPolicyTest,
-                        testing::Values(false));
+                        testing::Bool());
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/update_screen.cc b/chrome/browser/chromeos/login/screens/update_screen.cc
index ca9d4fc..4515341 100644
--- a/chrome/browser/chromeos/login/screens/update_screen.cc
+++ b/chrome/browser/chromeos/login/screens/update_screen.cc
@@ -66,6 +66,11 @@
 // An upper bound for possible downloading time left estimations.
 const double kMaxTimeLeft = 24 * 60 * 60;
 
+// Delay before showing error message if captive portal is detected.
+// We wait for this delay to let captive portal to perform redirect and show
+// its login page before error message appears.
+const int kDelayErrorMessageSec = 10;
+
 // Invoked from call to RequestUpdateCheck upon completion of the DBus call.
 void StartUpdateCallback(UpdateScreen* screen,
                          UpdateEngineClient::UpdateCheckResult result) {
@@ -83,7 +88,7 @@
 // static
 UpdateScreen::InstanceSet& UpdateScreen::GetInstanceSet() {
   CR_DEFINE_STATIC_LOCAL(std::set<UpdateScreen*>, instance_set, ());
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));  // not threadsafe.
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);  // not threadsafe.
   return instance_set;
 }
 
@@ -251,7 +256,7 @@
 void UpdateScreen::OnPortalDetectionCompleted(
     const NetworkState* network,
     const NetworkPortalDetector::CaptivePortalState& state) {
-  LOG(WARNING) << "UpdateScreen::PortalDetectionCompleted(): "
+  LOG(WARNING) << "UpdateScreen::OnPortalDetectionCompleted(): "
                << "network=" << (network ? network->path() : "") << ", "
                << "state.status=" << state.status << ", "
                << "state.response_code=" << state.response_code;
@@ -291,7 +296,11 @@
       StartUpdateCheck();
     } else {
       UpdateErrorMessage(network, status);
-      ShowErrorMessage();
+
+      if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL)
+        DelayErrorMessage();
+      else
+        ShowErrorMessage();
     }
   }
 }
@@ -509,6 +518,9 @@
 }
 
 void UpdateScreen::StartUpdateCheck() {
+  error_message_timer_.Stop();
+  GetErrorScreen()->HideCaptivePortal();
+
   NetworkPortalDetector::Get()->RemoveObserver(this);
   if (state_ == STATE_ERROR)
     HideErrorMessage();
@@ -521,6 +533,9 @@
 
 void UpdateScreen::ShowErrorMessage() {
   LOG(WARNING) << "UpdateScreen::ShowErrorMessage()";
+
+  error_message_timer_.Stop();
+
   state_ = STATE_ERROR;
   GetErrorScreen()->SetUIState(NetworkError::UI_STATE_UPDATE);
   get_base_screen_delegate()->ShowErrorScreen();
@@ -571,4 +586,19 @@
   }
 }
 
+void UpdateScreen::DelayErrorMessage() {
+  if (error_message_timer_.IsRunning())
+    return;
+
+  state_ = STATE_ERROR;
+  error_message_timer_.Start(
+      FROM_HERE, base::TimeDelta::FromSeconds(kDelayErrorMessageSec), this,
+      &UpdateScreen::ShowErrorMessage);
+}
+
+base::OneShotTimer<UpdateScreen>&
+UpdateScreen::GetErrorMessageTimerForTesting() {
+  return error_message_timer_;
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/update_screen.h b/chrome/browser/chromeos/login/screens/update_screen.h
index e9b291e..60a289dc 100644
--- a/chrome/browser/chromeos/login/screens/update_screen.h
+++ b/chrome/browser/chromeos/login/screens/update_screen.h
@@ -77,6 +77,8 @@
   // Skip update UI, usually used only in debug builds/tests.
   void CancelUpdate();
 
+  base::OneShotTimer<UpdateScreen>& GetErrorMessageTimerForTesting();
+
  private:
   FRIEND_TEST_ALL_PREFIXES(UpdateScreenTest, TestBasic);
   FRIEND_TEST_ALL_PREFIXES(UpdateScreenTest, TestUpdateAvailable);
@@ -116,6 +118,9 @@
   void UpdateErrorMessage(
       const NetworkState* network,
       const NetworkPortalDetector::CaptivePortalStatus status);
+
+  void DelayErrorMessage();
+
   // Timer for the interval to wait for the reboot.
   // If reboot didn't happen - ask user to reboot manually.
   base::OneShotTimer<UpdateScreen> reboot_timer_;
@@ -170,6 +175,11 @@
 
   scoped_ptr<ErrorScreensHistogramHelper> histogram_helper_;
 
+  // Timer for the captive portal detector to show portal login page.
+  // If redirect did not happen during this delay, error message is shown
+  // instead.
+  base::OneShotTimer<UpdateScreen> error_message_timer_;
+
   base::WeakPtrFactory<UpdateScreen> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(UpdateScreen);
diff --git a/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc
index 771650b..d284371 100644
--- a/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/update_screen_browsertest.cc
@@ -240,7 +240,7 @@
   portal_state.response_code = 200;
   SetDetectionResults(kStubEthernetGuid, portal_state);
 
-  // Update screen will show error message about portal state because
+  // Update screen will delay error message about portal state because
   // ethernet is behind captive portal.
   EXPECT_CALL(*mock_error_screen_,
               MockSetUIState(NetworkError::UI_STATE_UPDATE)).Times(1);
@@ -252,6 +252,15 @@
 
   update_screen_->StartNetworkCheck();
 
+  // Force timer expiration.
+  {
+    base::Closure timed_callback =
+        update_screen_->GetErrorMessageTimerForTesting().user_task();
+    ASSERT_FALSE(timed_callback.is_null());
+    update_screen_->GetErrorMessageTimerForTesting().Reset();
+    timed_callback.Run();
+  }
+
   NetworkPortalDetector::CaptivePortalState online_state;
   online_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE;
   online_state.response_code = 204;
@@ -282,7 +291,7 @@
   portal_state.response_code = 200;
   SetDetectionResults(kStubEthernetGuid, portal_state);
 
-  // Update screen will show error message about portal state because
+  // Update screen will delay error message about portal state because
   // ethernet is behind captive portal.
   EXPECT_CALL(*mock_error_screen_,
               MockSetUIState(NetworkError::UI_STATE_UPDATE)).Times(1);
@@ -294,6 +303,15 @@
 
   update_screen_->StartNetworkCheck();
 
+  // Force timer expiration.
+  {
+    base::Closure timed_callback =
+        update_screen_->GetErrorMessageTimerForTesting().user_task();
+    ASSERT_FALSE(timed_callback.is_null());
+    update_screen_->GetErrorMessageTimerForTesting().Reset();
+    timed_callback.Run();
+  }
+
   // Change active network to the wifi behind proxy.
   NetworkPortalDetector::CaptivePortalState proxy_state;
   proxy_state.status =
@@ -349,7 +367,7 @@
   portal_state.response_code = 200;
   SetDetectionResults(kStubEthernetGuid, portal_state);
 
-  // Update screen will show error message about portal state because
+  // Update screen will delay error message about portal state because
   // ethernet is behind captive portal.
   EXPECT_CALL(*mock_error_screen_,
               MockSetUIState(NetworkError::UI_STATE_UPDATE)).Times(1);
@@ -361,6 +379,15 @@
 
   update_screen_->StartNetworkCheck();
 
+  // Force timer expiration.
+  {
+    base::Closure timed_callback =
+        update_screen_->GetErrorMessageTimerForTesting().user_task();
+    ASSERT_FALSE(timed_callback.is_null());
+    update_screen_->GetErrorMessageTimerForTesting().Reset();
+    timed_callback.Run();
+  }
+
   // User re-selects the same network manually. In this case, hide
   // offline message and skip network check. Since ethernet is still
   // behind portal, update engine fails to update.
diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.cc b/chrome/browser/chromeos/login/screens/user_image_screen.cc
index 3ea07ea..d62c9702 100644
--- a/chrome/browser/chromeos/login/screens/user_image_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_image_screen.cc
@@ -66,6 +66,8 @@
 UserImageScreen::UserImageScreen(BaseScreenDelegate* base_screen_delegate,
                                  UserImageView* view)
     : UserImageModel(base_screen_delegate),
+      ImageRequest(
+          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)),
       view_(view),
       accept_photo_after_decoding_(false),
       selected_image_(user_manager::User::USER_IMAGE_INVALID),
@@ -89,8 +91,6 @@
   CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
   if (view_)
     view_->Unbind();
-  if (image_decoder_.get())
-    image_decoder_->set_delegate(NULL);
 }
 
 void UserImageScreen::OnScreenReady() {
@@ -100,15 +100,10 @@
 }
 
 void UserImageScreen::OnPhotoTaken(const std::string& raw_data) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   user_photo_ = gfx::ImageSkia();
-  if (image_decoder_.get())
-    image_decoder_->set_delegate(NULL);
-  image_decoder_ = new ImageDecoder(this, raw_data,
-                                    ImageDecoder::DEFAULT_CODEC);
-  scoped_refptr<base::MessageLoopProxy> task_runner =
-      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
-  image_decoder_->Start(task_runner);
+  ImageDecoder::Cancel(this);
+  ImageDecoder::Start(this, raw_data);
 }
 
 void UserImageScreen::OnCameraPresenceCheckDone(bool is_camera_present) {
@@ -120,15 +115,13 @@
     view_->HideCurtain();
 }
 
-void UserImageScreen::OnImageDecoded(const ImageDecoder* decoder,
-                                     const SkBitmap& decoded_image) {
-  DCHECK_EQ(image_decoder_.get(), decoder);
+void UserImageScreen::OnImageDecoded(const SkBitmap& decoded_image) {
   user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
   if (accept_photo_after_decoding_)
     OnImageAccepted();
 }
 
-void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) {
+void UserImageScreen::OnDecodeImageFailed() {
   NOTREACHED() << "Failed to decode PNG image from WebUI";
 }
 
diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.h b/chrome/browser/chromeos/login/screens/user_image_screen.h
index cec8d66..9a6df08 100644
--- a/chrome/browser/chromeos/login/screens/user_image_screen.h
+++ b/chrome/browser/chromeos/login/screens/user_image_screen.h
@@ -32,7 +32,7 @@
 class UserImageView;
 
 class UserImageScreen : public UserImageModel,
-                        public ImageDecoder::Delegate,
+                        public ImageDecoder::ImageRequest,
                         public content::NotificationObserver,
                         public UserImageSyncObserver::Observer,
                         public CameraPresenceNotifier::Observer {
@@ -62,10 +62,9 @@
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
-  // ImageDecoder::Delegate implementation:
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override;
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override;
+  // ImageDecoder::ImageRequest implementation:
+  void OnImageDecoded(const SkBitmap& decoded_image) override;
+  void OnDecodeImageFailed() override;
 
   // CameraPresenceNotifier::Observer implementation:
   void OnCameraPresenceCheckDone(bool is_camera_present) override;
@@ -108,10 +107,6 @@
 
   UserImageView* view_;
 
-  // Last ImageDecoder instance used to decode an image blob received by
-  // HandlePhotoTaken.
-  scoped_refptr<ImageDecoder> image_decoder_;
-
   // Last user photo, if taken.
   gfx::ImageSkia user_photo_;
 
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 6764105f..b595ad8 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -34,6 +34,7 @@
 #include "chrome/browser/chromeos/login/chrome_restart_request.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_app_launcher.h"
 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
+#include "chrome/browser/chromeos/login/helper.h"
 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
 #include "chrome/browser/chromeos/login/profile_auth_data.h"
 #include "chrome/browser/chromeos/login/saml/saml_offline_signin_limiter.h"
@@ -72,7 +73,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/logging_chrome.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
 #include "chromeos/cert_loader.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/cryptohome/cryptohome_util.h"
@@ -974,8 +974,8 @@
     const bool transfer_auth_cookies_and_channel_ids_on_first_login =
         has_auth_cookies_;
     ProfileAuthData::Transfer(
-        authenticator_->authentication_context(),
-        profile,
+        GetAuthRequestContext(),
+        profile->GetRequestContext(),
         transfer_auth_cookies_and_channel_ids_on_first_login,
         transfer_saml_auth_cookies_on_subsequent_login,
         base::Bind(&UserSessionManager::CompleteProfileCreateAfterAuthTransfer,
@@ -1204,27 +1204,10 @@
   OAuth2LoginManager* login_manager =
       OAuth2LoginManagerFactory::GetInstance()->GetForProfile(profile);
   login_manager->AddObserver(this);
-  net::URLRequestContextGetter* auth_request_context = NULL;
 
-  if (StartupUtils::IsWebviewSigninEnabled()) {
-    // Webview uses different partition storage than iframe. We need to get
-    // cookies from the right storage for url request to get auth token into
-    // session.
-    GURL oobe_url(chrome::kChromeUIOobeURL);
-    GURL guest_url(std::string(content::kGuestScheme) +
-                   url::kStandardSchemeSeparator + oobe_url.GetContent());
-    content::StoragePartition* partition =
-        content::BrowserContext::GetStoragePartitionForSite(
-            ProfileHelper::GetSigninProfile(), guest_url);
-    auth_request_context = partition->GetURLRequestContext();
-  } else if (authenticator_.get() && authenticator_->authentication_context()) {
-    auth_request_context =
-        authenticator_->authentication_context()->GetRequestContext();
-  }
-
-  login_manager->RestoreSession(auth_request_context, session_restore_strategy_,
-                                user_context_.GetRefreshToken(),
-                                user_context_.GetAuthCode());
+  login_manager->RestoreSession(
+      GetAuthRequestContext(), session_restore_strategy_,
+      user_context_.GetRefreshToken(), user_context_.GetAuthCode());
 }
 
 void UserSessionManager::InitRlzImpl(Profile* profile, bool disabled) {
@@ -1411,6 +1394,22 @@
                  user_context.GetUserID()));
 }
 
+net::URLRequestContextGetter*
+UserSessionManager::GetAuthRequestContext() const {
+  net::URLRequestContextGetter* auth_request_context = NULL;
+
+  if (StartupUtils::IsWebviewSigninEnabled()) {
+    // Webview uses different partition storage than iframe. We need to get
+    // cookies from the right storage for url request to get auth token into
+    // session.
+    auth_request_context = login::GetSigninPartition()->GetURLRequestContext();
+  } else if (authenticator_.get() && authenticator_->authentication_context()) {
+    auth_request_context =
+        authenticator_->authentication_context()->GetRequestContext();
+  }
+  return auth_request_context;
+}
+
 void UserSessionManager::AttemptRestart(Profile* profile) {
   if (CheckEasyUnlockKeyOps(base::Bind(&UserSessionManager::AttemptRestart,
                                        AsWeakPtr(), profile))) {
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.h b/chrome/browser/chromeos/login/session/user_session_manager.h
index 3e171608..9874aef 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.h
+++ b/chrome/browser/chromeos/login/session/user_session_manager.h
@@ -28,6 +28,10 @@
 class PrefService;
 class Profile;
 
+namespace net {
+class URLRequestContextGetter;
+}
+
 namespace user_manager {
 class User;
 }  // namespace user_manager
@@ -220,6 +224,9 @@
   // Update Easy unlock cryptohome keys for given user context.
   void UpdateEasyUnlockKeys(const UserContext& user_context);
 
+  // Returns the auth request context associated with auth data.
+  net::URLRequestContextGetter* GetAuthRequestContext() const;
+
   // Removes a profile from the per-user input methods states map.
   void RemoveProfileForTesting(Profile* profile);
 
diff --git a/chrome/browser/chromeos/login/signin/merge_session_load_page.cc b/chrome/browser/chromeos/login/signin/merge_session_load_page.cc
index e0c013e4..6d83350 100644
--- a/chrome/browser/chromeos/login/signin/merge_session_load_page.cc
+++ b/chrome/browser/chromeos/login/signin/merge_session_load_page.cc
@@ -64,7 +64,7 @@
 }
 
 MergeSessionLoadPage::~MergeSessionLoadPage() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void MergeSessionLoadPage::Show() {
@@ -106,13 +106,13 @@
 }
 
 void MergeSessionLoadPage::OnProceed() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   proceeded_ = true;
   NotifyBlockingPageComplete();
 }
 
 void MergeSessionLoadPage::OnDontProceed() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Ignore if it's already proceeded.
   if (proceeded_)
     return;
@@ -157,7 +157,7 @@
 
 void MergeSessionLoadPage::OnSessionRestoreStateChanged(
     Profile* user_profile, OAuth2LoginManager::SessionRestoreState state) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   OAuth2LoginManager* manager = GetOAuth2LoginManager();
   DVLOG(1) << "Merge session should "
diff --git a/chrome/browser/chromeos/login/signin/merge_session_throttle.cc b/chrome/browser/chromeos/login/signin/merge_session_throttle.cc
index b4ce9d65..442de83 100644
--- a/chrome/browser/chromeos/login/signin/merge_session_throttle.cc
+++ b/chrome/browser/chromeos/login/signin/merge_session_throttle.cc
@@ -77,7 +77,7 @@
 }
 
 MergeSessionThrottle::~MergeSessionThrottle() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
 void MergeSessionThrottle::WillStartRequest(bool* defer) {
@@ -112,7 +112,7 @@
 }
 
 void MergeSessionThrottle::OnBlockingPageComplete() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   controller()->Resume();
 }
 
@@ -160,7 +160,7 @@
 bool MergeSessionThrottle::ShouldDelayRequest(
     int render_process_id,
     int render_view_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!user_manager::UserManager::Get()->IsUserLoggedIn()) {
     return false;
@@ -247,7 +247,7 @@
     int render_view_id,
     const GURL& url,
     const CompletionCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (ShouldDelayRequest(render_process_id, render_view_id)) {
     // There is a chance that the tab closed after we decided to show
diff --git a/chrome/browser/chromeos/login/signin/merge_session_xhr_request_waiter.cc b/chrome/browser/chromeos/login/signin/merge_session_xhr_request_waiter.cc
index b9a255dd..f3d76db0 100644
--- a/chrome/browser/chromeos/login/signin/merge_session_xhr_request_waiter.cc
+++ b/chrome/browser/chromeos/login/signin/merge_session_xhr_request_waiter.cc
@@ -59,7 +59,7 @@
 void MergeSessionXHRRequestWaiter::OnSessionRestoreStateChanged(
     Profile* user_profile,
     OAuth2LoginManager::SessionRestoreState state) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   OAuth2LoginManager* manager =
       OAuth2LoginManagerFactory::GetInstance()->GetForProfile(profile_);
@@ -81,7 +81,7 @@
 }
 
 void MergeSessionXHRRequestWaiter::NotifyBlockingDone() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!callback_.is_null()) {
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE, callback_);
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc b/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc
index 807e283..54169b5 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc
@@ -64,7 +64,7 @@
 }
 
 void OAuth2LoginVerifier::VerifyUserCookies(Profile* profile) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Delay the verification if the network is not connected or on a captive
   // portal.
@@ -75,7 +75,7 @@
 }
 
 void OAuth2LoginVerifier::VerifyProfileTokens(Profile* profile) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Delay the verification if the network is not connected or on a captive
   // portal.
@@ -85,7 +85,7 @@
 }
 
 void OAuth2LoginVerifier::VerifyProfileTokensImpl(Profile* profile) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   gaia_token_.clear();
   if (access_token_.empty()) {
@@ -132,7 +132,7 @@
 
 void OAuth2LoginVerifier::OnUberAuthTokenFailure(
     const GoogleServiceAuthError& error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   LOG(WARNING) << "OAuthLogin(uber_token) failed,"
                << " error: " << error.state();
   RetryOnError("OAuthLoginUberToken", error,
@@ -152,7 +152,7 @@
 }
 
 void OAuth2LoginVerifier::OnMergeSessionSuccess(const std::string& data) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   VLOG(1) << "MergeSession successful.";
   delegate_->OnSessionMergeSuccess();
   // Schedule post-merge verification to analyze how many LSID/SID overruns
@@ -207,7 +207,7 @@
 void OAuth2LoginVerifier::OnGetTokenFailure(
     const OAuth2TokenService::Request* request,
     const GoogleServiceAuthError& error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(login_token_request_.get(), request);
   login_token_request_.reset();
 
@@ -222,14 +222,14 @@
 
 void OAuth2LoginVerifier::OnListAccountsSuccess(
     const std::string& data) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   VLOG(1) << "ListAccounts successful.";
   delegate_->OnListAccountsSuccess(data);
 }
 
 void OAuth2LoginVerifier::OnListAccountsFailure(
     const GoogleServiceAuthError& error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   LOG(WARNING) << "Failed to get list of session accounts, "
                << " error: " << error.state();
   RetryOnError(
diff --git a/chrome/browser/chromeos/login/signin/oauth2_token_fetcher.cc b/chrome/browser/chromeos/login/signin/oauth2_token_fetcher.cc
index efc27ce..b898018f 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_token_fetcher.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_token_fetcher.cc
@@ -42,7 +42,7 @@
 void OAuth2TokenFetcher::StartExchangeFromCookies(
     const std::string& session_index,
     const std::string& signin_scoped_device_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   session_index_ = session_index;
   signin_scoped_device_id_ = signin_scoped_device_id;
   // Delay the verification if the network is not connected or on a captive
@@ -69,7 +69,7 @@
 
 void OAuth2TokenFetcher::StartExchangeFromAuthCode(
     const std::string& auth_code) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auth_code_ = auth_code;
   // Delay the verification if the network is not connected or on a captive
   // portal.
@@ -92,7 +92,7 @@
 
 void OAuth2TokenFetcher::OnClientOAuthSuccess(
     const GaiaAuthConsumer::ClientOAuthResult& oauth_tokens) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   VLOG(1) << "Got OAuth2 tokens!";
   retry_count_ = 0;
   oauth_tokens_ = oauth_tokens;
@@ -101,7 +101,7 @@
 
 void OAuth2TokenFetcher::OnClientOAuthFailure(
     const GoogleServiceAuthError& error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RetryOnError(error,
                auth_code_.empty()
                    ? base::Bind(&OAuth2TokenFetcher::StartExchangeFromCookies,
@@ -118,7 +118,7 @@
 void OAuth2TokenFetcher::RetryOnError(const GoogleServiceAuthError& error,
                                       const base::Closure& task,
                                       const base::Closure& error_handler) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if ((error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
        error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE ||
        error.state() == GoogleServiceAuthError::REQUEST_CANCELED) &&
diff --git a/chrome/browser/chromeos/login/signin/token_handler_util.cc b/chrome/browser/chromeos/login/signin/token_handler_util.cc
new file mode 100644
index 0000000..61af843
--- /dev/null
+++ b/chrome/browser/chromeos/login/signin/token_handler_util.cc
@@ -0,0 +1,116 @@
+// 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/chromeos/login/signin/token_handler_util.h"
+
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/user_manager/user_id.h"
+#include "components/user_manager/user_manager.h"
+#include "google_apis/gaia/gaia_oauth_client.h"
+
+namespace {
+
+const char kTokenHandlePref[] = "PasswordTokenHandle";
+static const int kMaxRetries = 3;
+}
+
+TokenHandlerUtil::TokenHandlerUtil(user_manager::UserManager* user_manager)
+    : user_manager_(user_manager), weak_factory_(this) {
+}
+
+TokenHandlerUtil::~TokenHandlerUtil() {
+  weak_factory_.InvalidateWeakPtrs();
+  gaia_client_.reset();
+}
+
+bool TokenHandlerUtil::HasToken(const user_manager::UserID& user_id) {
+  const base::DictionaryValue* dict = nullptr;
+  std::string token;
+  if (!user_manager_->FindKnownUserPrefs(user_id, &dict))
+    return false;
+  if (!dict->GetString(kTokenHandlePref, &token))
+    return false;
+  return !token.empty();
+}
+
+void TokenHandlerUtil::DeleteToken(const user_manager::UserID& user_id) {
+  const base::DictionaryValue* dict = nullptr;
+  if (!user_manager_->FindKnownUserPrefs(user_id, &dict))
+    return;
+  scoped_ptr<base::DictionaryValue> dict_copy(dict->DeepCopy());
+  if (!dict_copy->Remove(kTokenHandlePref, nullptr))
+    return;
+  user_manager_->UpdateKnownUserPrefs(user_id, *dict_copy.get(),
+                                      /* replace values */ true);
+}
+
+void TokenHandlerUtil::CheckToken(const user_manager::UserID& user_id,
+                                  const TokenValidationCallback& callback) {
+  const base::DictionaryValue* dict = nullptr;
+  std::string token;
+  if (!user_manager_->FindKnownUserPrefs(user_id, &dict)) {
+    callback.Run(user_id, UNKNOWN);
+    return;
+  }
+  if (!dict->GetString(kTokenHandlePref, &token)) {
+    callback.Run(user_id, UNKNOWN);
+    return;
+  }
+
+  if (!gaia_client_.get()) {
+    auto request_context =
+        chromeos::ProfileHelper::Get()->GetSigninProfile()->GetRequestContext();
+    gaia_client_.reset(new gaia::GaiaOAuthClient(request_context));
+  }
+
+  validation_delegates_.set(
+      token, scoped_ptr<TokenValidationDelegate>(new TokenValidationDelegate(
+                 weak_factory_.GetWeakPtr(), user_id, token, callback)));
+  gaia_client_->GetTokenHandleInfo(token, kMaxRetries,
+                                   validation_delegates_.get(token));
+}
+
+void TokenHandlerUtil::OnValidationComplete(const std::string& token) {
+  validation_delegates_.erase(token);
+}
+
+TokenHandlerUtil::TokenValidationDelegate::TokenValidationDelegate(
+    const base::WeakPtr<TokenHandlerUtil>& owner,
+    const user_manager::UserID& user_id,
+    const std::string& token,
+    const TokenValidationCallback& callback)
+    : owner_(owner), user_id_(user_id), token_(token), callback_(callback) {
+}
+
+TokenHandlerUtil::TokenValidationDelegate::~TokenValidationDelegate() {
+}
+
+void TokenHandlerUtil::TokenValidationDelegate::OnOAuthError() {
+  callback_.Run(user_id_, INVALID);
+  if (owner_)
+    owner_->OnValidationComplete(token_);
+}
+
+void TokenHandlerUtil::TokenValidationDelegate::OnNetworkError(
+    int response_code) {
+  callback_.Run(user_id_, UNKNOWN);
+  if (owner_)
+    owner_->OnValidationComplete(token_);
+}
+
+void TokenHandlerUtil::TokenValidationDelegate::OnGetTokenInfoResponse(
+    scoped_ptr<base::DictionaryValue> token_info) {
+  TokenHandleStatus outcome = UNKNOWN;
+  if (!token_info->HasKey("error")) {
+    int expires_in = 0;
+    if (token_info->GetInteger("expires_in", &expires_in))
+      outcome = (expires_in < 0) ? INVALID : VALID;
+  }
+  callback_.Run(user_id_, outcome);
+  if (owner_)
+    owner_->OnValidationComplete(token_);
+}
diff --git a/chrome/browser/chromeos/login/signin/token_handler_util.h b/chrome/browser/chromeos/login/signin/token_handler_util.h
new file mode 100644
index 0000000..d2c9169
--- /dev/null
+++ b/chrome/browser/chromeos/login/signin/token_handler_util.h
@@ -0,0 +1,90 @@
+// 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_CHROMEOS_LOGIN_SIGNIN_TOKEN_HANDLER_UTIL_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_SIGNIN_TOKEN_HANDLER_UTIL_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/memory/weak_ptr.h"
+#include "components/user_manager/user_id.h"
+#include "google_apis/gaia/gaia_oauth_client.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace user_manager {
+class UserManager;
+}
+
+// This class is responsible for operations with External Token Handle.
+// Handle is an extra token associated with OAuth refresh token that have
+// exactly same lifetime. It is not secure, and it's only purpose is checking
+// validity of corresponding refresh token in the insecure environment.
+class TokenHandlerUtil {
+ public:
+  explicit TokenHandlerUtil(user_manager::UserManager* user_manager);
+  ~TokenHandlerUtil();
+
+  enum TokenHandleStatus { VALID, INVALID, UNKNOWN };
+
+  typedef base::Callback<void(const user_manager::UserID&, TokenHandleStatus)>
+      TokenValidationCallback;
+
+  // Returns true if UserManager has token handle associated with |user_id|.
+  bool HasToken(const user_manager::UserID& user_id);
+
+  // Removes token handle for |user_id| from UserManager storage.
+  void DeleteToken(const user_manager::UserID& user_id);
+
+  // Performs token handle check for |user_id|. Will call |callback| with
+  // corresponding result.
+  void CheckToken(const user_manager::UserID& user_id,
+                  const TokenValidationCallback& callback);
+
+ private:
+  // Associates GaiaOAuthClient::Delegate with User ID and Token.
+  class TokenValidationDelegate : public gaia::GaiaOAuthClient::Delegate {
+   public:
+    TokenValidationDelegate(const base::WeakPtr<TokenHandlerUtil>& owner,
+                            const user_manager::UserID& user_id,
+                            const std::string& token,
+                            const TokenValidationCallback& callback);
+    ~TokenValidationDelegate() override;
+    void OnOAuthError() override;
+    void OnNetworkError(int response_code) override;
+    void OnGetTokenInfoResponse(
+        scoped_ptr<base::DictionaryValue> token_info) override;
+
+   private:
+    base::WeakPtr<TokenHandlerUtil> owner_;
+    user_manager::UserID user_id_;
+    std::string token_;
+    TokenValidationCallback callback_;
+
+    DISALLOW_COPY_AND_ASSIGN(TokenValidationDelegate);
+  };
+
+  void OnValidationComplete(const std::string& token);
+
+  // UserManager that stores corresponding user data.
+  user_manager::UserManager* user_manager_;
+
+  // Map of pending check operations.
+  base::ScopedPtrHashMap<std::string, TokenValidationDelegate>
+      validation_delegates_;
+
+  // Instance of GAIA Client.
+  scoped_ptr<gaia::GaiaOAuthClient> gaia_client_;
+
+  base::WeakPtrFactory<TokenHandlerUtil> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(TokenHandlerUtil);
+};
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_SIGNIN_TOKEN_HANDLER_UTIL_H_
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc b/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc
index 21b26113..a0f9eab 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc
@@ -30,7 +30,7 @@
                     scoped_refptr<SupervisedUserAuthenticator> resolver,
                     bool success,
                     cryptohome::MountError return_code) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   attempt->RecordCryptohomeStatus(success, return_code);
   resolver->Resolve();
 }
@@ -40,7 +40,7 @@
                           scoped_refptr<SupervisedUserAuthenticator> resolver,
                           bool success,
                           const std::string& result) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   attempt->RecordHash(result);
   resolver->Resolve();
 }
@@ -61,7 +61,7 @@
            scoped_refptr<SupervisedUserAuthenticator> resolver,
            int flags,
            const std::string& system_salt) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   chromeos::BootTimesRecorder::Get()->AddLoginTimeMarker(
       "CryptohomeMount-LMU-Start", false);
 
@@ -86,7 +86,7 @@
             scoped_refptr<SupervisedUserAuthenticator> resolver,
             const std::string& plain_text_master_key,
             const std::string& system_salt) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   chromeos::BootTimesRecorder::Get()->AddLoginTimeMarker(
       "CryptohomeAddKey-LMU-Start", false);
 
@@ -159,7 +159,7 @@
 void SupervisedUserAuthenticator::OnAuthenticationSuccess(
     const std::string& mount_hash,
     bool add_key) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   VLOG(1) << "Supervised user authentication success";
   if (consumer_) {
     if (add_key)
@@ -171,14 +171,14 @@
 
 void SupervisedUserAuthenticator::OnAuthenticationFailure(
     SupervisedUserAuthenticator::AuthState state) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   LOG(WARNING) << "Supervised user authentication failure";
   if (consumer_)
     consumer_->OnAuthenticationFailure(state);
 }
 
 void SupervisedUserAuthenticator::Resolve() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   SupervisedUserAuthenticator::AuthState state = ResolveState();
   VLOG(1) << "Resolved state to: " << state;
   switch (state) {
@@ -239,7 +239,7 @@
 
 SupervisedUserAuthenticator::AuthState
 SupervisedUserAuthenticator::ResolveState() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // If we haven't mounted the user's home dir yet, we can't be done.
   // We never get past here if a cryptohome op is still pending.
   // This is an important invariant.
@@ -262,7 +262,7 @@
 
 SupervisedUserAuthenticator::AuthState
     SupervisedUserAuthenticator::ResolveCryptohomeFailureState() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   LOG(ERROR) << "Failed to authenticate supervised user, code: "
              << current_state_->cryptohome_code();
   if (current_state_->cryptohome_code() ==
@@ -283,7 +283,7 @@
 
 SupervisedUserAuthenticator::AuthState
     SupervisedUserAuthenticator::ResolveCryptohomeSuccessState() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return SUCCESS;
 }
 
@@ -304,7 +304,7 @@
 void SupervisedUserAuthenticator::AuthAttempt::RecordCryptohomeStatus(
     bool cryptohome_outcome,
     cryptohome::MountError cryptohome_code) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   cryptohome_complete_ = true;
   cryptohome_outcome_ = cryptohome_outcome;
   cryptohome_code_ = cryptohome_code;
@@ -312,34 +312,34 @@
 
 void SupervisedUserAuthenticator::AuthAttempt::RecordHash(
     const std::string& hash) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   hash_obtained_ = true;
   hash_ = hash;
 }
 
 bool SupervisedUserAuthenticator::AuthAttempt::cryptohome_complete() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return cryptohome_complete_;
 }
 
 bool SupervisedUserAuthenticator::AuthAttempt::cryptohome_outcome() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return cryptohome_outcome_;
 }
 
 cryptohome::MountError
     SupervisedUserAuthenticator::AuthAttempt::cryptohome_code() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return cryptohome_code_;
 }
 
 bool SupervisedUserAuthenticator::AuthAttempt::hash_obtained() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return hash_obtained_;
 }
 
 std::string SupervisedUserAuthenticator::AuthAttempt::hash() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return hash_;
 }
 
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
index 2ffad5e..7e784d65 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
@@ -100,12 +100,13 @@
     BaseScreenDelegate* base_screen_delegate,
     SupervisedUserCreationScreenHandler* actor)
     : BaseScreen(base_screen_delegate),
+      ImageRequest(content::BrowserThread::GetMessageLoopProxyForThread(
+          content::BrowserThread::UI)),
       actor_(actor),
       on_error_screen_(false),
       manager_signin_in_progress_(false),
       last_page_(kNameOfIntroScreen),
       sync_service_(NULL),
-      image_decoder_(NULL),
       apply_photo_after_decoding_(false),
       selected_image_(0),
       histogram_helper_(new ErrorScreensHistogramHelper("Supervised")),
@@ -121,8 +122,6 @@
     sync_service_->RemoveObserver(this);
   if (actor_)
     actor_->SetDelegate(NULL);
-  if (image_decoder_.get())
-    image_decoder_->set_delegate(NULL);
   NetworkPortalDetector::Get()->RemoveObserver(this);
 }
 
@@ -592,27 +591,18 @@
     const std::string& raw_data) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   user_photo_ = gfx::ImageSkia();
-  if (image_decoder_.get())
-    image_decoder_->set_delegate(NULL);
-  image_decoder_ = new ImageDecoder(this, raw_data,
-                                    ImageDecoder::DEFAULT_CODEC);
-  scoped_refptr<base::MessageLoopProxy> task_runner =
-      content::BrowserThread::GetMessageLoopProxyForThread(
-          content::BrowserThread::UI);
-  image_decoder_->Start(task_runner);
+  ImageDecoder::Cancel(this);
+  ImageDecoder::Start(this, raw_data);
 }
 
 void SupervisedUserCreationScreen::OnImageDecoded(
-    const ImageDecoder* decoder,
     const SkBitmap& decoded_image) {
-  DCHECK_EQ(image_decoder_.get(), decoder);
   user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
   if (apply_photo_after_decoding_)
     ApplyPicture();
 }
 
-void SupervisedUserCreationScreen::OnDecodeImageFailed(
-    const ImageDecoder* decoder) {
+void SupervisedUserCreationScreen::OnDecodeImageFailed() {
   NOTREACHED() << "Failed to decode PNG image from WebUI";
 }
 
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h
index a41cd6d..62bf1ea 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h
@@ -32,7 +32,7 @@
       public SupervisedUserCreationScreenHandler::Delegate,
       public SupervisedUserCreationController::StatusConsumer,
       public SupervisedUserSyncServiceObserver,
-      public ImageDecoder::Delegate,
+      public ImageDecoder::ImageRequest,
       public NetworkPortalDetector::Observer,
       public CameraPresenceNotifier::Observer {
  public:
@@ -116,10 +116,9 @@
   void OnImageSelected(const std::string& image_url,
                        const std::string& image_type) override;
   void OnImageAccepted() override;
-  // ImageDecoder::Delegate overrides:
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override;
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override;
+  // ImageDecoder::ImageRequest overrides:
+  void OnImageDecoded(const SkBitmap& decoded_image) override;
+  void OnDecodeImageFailed() override;
 
  private:
   void ApplyPicture();
@@ -137,7 +136,6 @@
   SupervisedUserSyncService* sync_service_;
 
   gfx::ImageSkia user_photo_;
-  scoped_refptr<ImageDecoder> image_decoder_;
   bool apply_photo_after_decoding_;
   int selected_image_;
 
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.cc b/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.cc
index cbbbc6e..d47863c 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.cc
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_login_flow.cc
@@ -72,7 +72,7 @@
 
 void SupervisedUserLoginFlow::OnSyncSetupDataLoaded(
     const std::string& token) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ConfigureSync(token);
 }
 
diff --git a/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.cc b/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.cc
index 24b00ec..c0762b0 100644
--- a/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.cc
+++ b/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/login/ui/captive_portal_window_proxy.h"
 
+#include "base/metrics/histogram_macros.h"
 #include "chrome/browser/chromeos/login/ui/captive_portal_view.h"
 #include "chrome/browser/chromeos/login/ui/proxy_settings_dialog.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -90,8 +91,14 @@
 }
 
 void CaptivePortalWindowProxy::OnRedirected() {
-  if (GetState() == STATE_WAITING_FOR_REDIRECTION)
+  if (GetState() == STATE_WAITING_FOR_REDIRECTION) {
+    if (!started_loading_at_.is_null()) {
+      UMA_HISTOGRAM_TIMES("CaptivePortal.RedirectTime",
+                          base::Time::Now() - started_loading_at_);
+      started_loading_at_ = base::Time();
+    }
     Show();
+  }
   delegate_->OnPortalDetected();
 }
 
@@ -124,6 +131,8 @@
         new CaptivePortalView(ProfileHelper::GetSigninProfile(), this));
     captive_portal_view_for_testing_ = captive_portal_view_.get();
   }
+
+  started_loading_at_ = base::Time::Now();
   captive_portal_view_->StartLoad();
 }
 
diff --git a/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.h b/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.h
index 53482b2..f301d46 100644
--- a/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.h
+++ b/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.h
@@ -7,6 +7,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
 #include "ui/views/widget/widget_observer.h"
 
 namespace content {
@@ -113,6 +114,8 @@
 
   CaptivePortalView* captive_portal_view_for_testing_;
 
+  base::Time started_loading_at_;
+
   DISALLOW_COPY_AND_ASSIGN(CaptivePortalWindowProxy);
 };
 
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_impl.cc b/chrome/browser/chromeos/login/ui/login_display_host_impl.cc
index 38b426af..79e18d95 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_impl.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_impl.cc
@@ -37,6 +37,7 @@
 #include "chrome/browser/chromeos/login/helper.h"
 #include "chrome/browser/chromeos/login/login_wizard.h"
 #include "chrome/browser/chromeos/login/screens/core_oobe_actor.h"
+#include "chrome/browser/chromeos/login/signin/token_handler_util.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/ui/input_events_blocker.h"
 #include "chrome/browser/chromeos/login/ui/keyboard_driven_oobe_key_handler.h"
@@ -71,6 +72,7 @@
 #include "chromeos/settings/timezone_settings.h"
 #include "chromeos/timezone/timezone_resolver.h"
 #include "components/session_manager/core/session_manager.h"
+#include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
@@ -628,6 +630,22 @@
   SetStatusAreaVisible(true);
   existing_user_controller_->Init(users);
 
+  // Validate user OAuth tokens.
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableOAuthTokenHandlers)) {
+    token_handler_util_.reset(
+        new TokenHandlerUtil(user_manager::UserManager::Get()));
+    for (auto* user : users) {
+      auto user_id = user->GetUserID();
+      if (token_handler_util_->HasToken(user_id)) {
+        token_handler_util_->CheckToken(
+            user_id, base::Bind(&LoginDisplayHostImpl::OnTokenHandlerChecked,
+                                pointer_factory_.GetWeakPtr()));
+      }
+    }
+  }
+
   // We might be here after a reboot that was triggered after OOBE was complete,
   // so check for auto-enrollment again. This might catch a cached decision from
   // a previous oobe flow, or might start a new check with the server.
@@ -654,6 +672,16 @@
       "login-wait-for-signin-state-initialize");
 }
 
+void LoginDisplayHostImpl::OnTokenHandlerChecked(
+    const user_manager::UserID& user_id,
+    TokenHandlerUtil::TokenHandleStatus token_status) {
+  if (token_status == TokenHandlerUtil::INVALID) {
+    user_manager::UserManager::Get()->SaveUserOAuthStatus(
+        user_id, user_manager::User::OAUTH2_TOKEN_STATUS_INVALID);
+    token_handler_util_->DeleteToken(user_id);
+  }
+}
+
 void LoginDisplayHostImpl::OnPreferencesChanged() {
   if (is_showing_login_)
     webui_login_display_->OnPreferencesChanged();
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_impl.h b/chrome/browser/chromeos/login/ui/login_display_host_impl.h
index 00624fd..1a18b87 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_impl.h
+++ b/chrome/browser/chromeos/login/ui/login_display_host_impl.h
@@ -15,6 +15,7 @@
 #include "chrome/browser/chromeos/login/app_launch_controller.h"
 #include "chrome/browser/chromeos/login/auth/auth_prewarmer.h"
 #include "chrome/browser/chromeos/login/existing_user_controller.h"
+#include "chrome/browser/chromeos/login/signin/token_handler_util.h"
 #include "chrome/browser/chromeos/login/signin_screen_controller.h"
 #include "chrome/browser/chromeos/login/ui/login_display.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
@@ -196,6 +197,10 @@
   // Called when login-prompt-visible signal is caught.
   void OnLoginPromptVisible();
 
+  // Called when user oauth token handler check is completed.
+  void OnTokenHandlerChecked(const user_manager::UserID& user_id,
+                             TokenHandlerUtil::TokenHandleStatus token_status);
+
   // Used to calculate position of the screens and background.
   gfx::Rect background_bounds_;
 
@@ -288,6 +293,9 @@
   // Handles special keys for keyboard driven oobe.
   scoped_ptr<KeyboardDrivenOobeKeyHandler> keyboard_driven_oobe_key_handler_;
 
+  // Handles token handle operations.
+  scoped_ptr<TokenHandlerUtil> token_handler_util_;
+
   FinalizeAnimationType finalize_animation_type_;
 
   // Time when login prompt visible signal is received. Used for
diff --git a/chrome/browser/chromeos/login/users/avatar/user_image_loader.cc b/chrome/browser/chromeos/login/users/avatar/user_image_loader.cc
index 17088c3..33361a2 100644
--- a/chrome/browser/chromeos/login/users/avatar/user_image_loader.cc
+++ b/chrome/browser/chromeos/login/users/avatar/user_image_loader.cc
@@ -31,6 +31,19 @@
 UserImageLoader::ImageInfo::~ImageInfo() {
 }
 
+UserImageLoader::UserImageRequest::UserImageRequest(
+    const ImageInfo& image_info,
+    const std::string& image_data,
+    UserImageLoader* user_image_loader)
+    : ImageRequest(user_image_loader->background_task_runner_),
+      image_info_(image_info),
+      image_data_(image_data.begin(), image_data.end()),
+      user_image_loader_(user_image_loader) {
+}
+
+UserImageLoader::UserImageRequest::~UserImageRequest() {
+}
+
 UserImageLoader::UserImageLoader(
     ImageDecoder::ImageCodec image_codec,
     scoped_refptr<base::SequencedTaskRunner> background_task_runner)
@@ -79,26 +92,16 @@
                                   const ImageInfo& image_info) {
   DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
 
-  scoped_refptr<ImageDecoder> image_decoder =
-      new ImageDecoder(this, *data, image_codec_);
-  image_info_map_.insert(std::make_pair(image_decoder.get(), image_info));
-  image_decoder->Start(background_task_runner_);
+  UserImageRequest* image_request =
+      new UserImageRequest(image_info, *data, this);
+  ImageDecoder::StartWithOptions(image_request, *data, image_codec_, false);
 }
 
-void UserImageLoader::OnImageDecoded(const ImageDecoder* decoder,
-                                     const SkBitmap& decoded_image) {
-  DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
+void UserImageLoader::UserImageRequest::OnImageDecoded(
+    const SkBitmap& decoded_image) {
+  DCHECK(task_runner()->RunsTasksOnCurrentThread());
 
-  ImageInfoMap::iterator it = image_info_map_.find(decoder);
-  if (it == image_info_map_.end()) {
-    NOTREACHED();
-    return;
-  }
-  const std::string file_path = it->second.file_path;
-  const int target_size = it->second.pixels_per_side;
-  const LoadedCallback loaded_cb = it->second.loaded_cb;
-  image_info_map_.erase(it);
-
+  const int target_size = image_info_.pixels_per_side;
   SkBitmap final_image = decoded_image;
 
   if (target_size > 0) {
@@ -126,28 +129,20 @@
   gfx::ImageSkia final_image_skia =
       gfx::ImageSkia::CreateFrom1xBitmap(final_image);
   final_image_skia.MakeThreadSafe();
-  user_manager::UserImage user_image(final_image_skia,
-                                     decoder->get_image_data());
-  user_image.set_file_path(file_path);
-  if (image_codec_ == ImageDecoder::ROBUST_JPEG_CODEC)
+  user_manager::UserImage user_image(final_image_skia, image_data_);
+  user_image.set_file_path(image_info_.file_path);
+  if (user_image_loader_->image_codec_ == ImageDecoder::ROBUST_JPEG_CODEC)
     user_image.MarkAsSafe();
-  foreground_task_runner_->PostTask(FROM_HERE,
-                                    base::Bind(loaded_cb, user_image));
+  user_image_loader_->foreground_task_runner_->PostTask(
+      FROM_HERE, base::Bind(image_info_.loaded_cb, user_image));
+  delete this;
 }
 
-void UserImageLoader::OnDecodeImageFailed(const ImageDecoder* decoder) {
-  DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
-
-  ImageInfoMap::iterator it = image_info_map_.find(decoder);
-  if (it == image_info_map_.end()) {
-    NOTREACHED();
-    return;
-  }
-  const LoadedCallback loaded_cb = it->second.loaded_cb;
-  image_info_map_.erase(it);
-
-  foreground_task_runner_->PostTask(
-      FROM_HERE, base::Bind(loaded_cb, user_manager::UserImage()));
+void UserImageLoader::UserImageRequest::OnDecodeImageFailed() {
+  DCHECK(task_runner()->RunsTasksOnCurrentThread());
+  user_image_loader_->foreground_task_runner_->PostTask(
+      FROM_HERE, base::Bind(image_info_.loaded_cb, user_manager::UserImage()));
+  delete this;
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/users/avatar/user_image_loader.h b/chrome/browser/chromeos/login/users/avatar/user_image_loader.h
index 10c7be4..ebaab5b4 100644
--- a/chrome/browser/chromeos/login/users/avatar/user_image_loader.h
+++ b/chrome/browser/chromeos/login/users/avatar/user_image_loader.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_USERS_AVATAR_USER_IMAGE_LOADER_H_
 #define CHROME_BROWSER_CHROMEOS_LOGIN_USERS_AVATAR_USER_IMAGE_LOADER_H_
 
-#include <map>
 #include <string>
 
 #include "base/callback.h"
@@ -27,8 +26,7 @@
 
 // Helper that reads, decodes and optionally resizes an image on a background
 // thread. Returns the image in the form of an SkBitmap.
-class UserImageLoader : public base::RefCountedThreadSafe<UserImageLoader>,
-                        public ImageDecoder::Delegate {
+class UserImageLoader : public base::RefCountedThreadSafe<UserImageLoader> {
  public:
   // Callback used to return the result of an image load operation.
   typedef base::Callback<void(const user_manager::UserImage& user_image)>
@@ -67,9 +65,26 @@
     const LoadedCallback loaded_cb;
   };
 
-  typedef std::map<const ImageDecoder*, ImageInfo> ImageInfoMap;
+  class UserImageRequest : public ImageDecoder::ImageRequest {
+   public:
+    UserImageRequest(const ImageInfo& image_info,
+                     const std::string& image_data,
+                     UserImageLoader* user_image_loader);
 
-  ~UserImageLoader() override;
+    // ImageDecoder::ImageRequest implementation. These callbacks will only be
+    // invoked via user_image_loader_'s background_task_runner_.
+    void OnImageDecoded(const SkBitmap& decoded_image) override;
+    void OnDecodeImageFailed() override;
+
+   private:
+    ~UserImageRequest() override;
+
+    const ImageInfo image_info_;
+    std::vector<unsigned char> image_data_;
+    UserImageLoader* user_image_loader_;
+  };
+
+  ~UserImageLoader();
 
   // Reads the image from |image_info.file_path| and starts the decoding
   // process. This method may only be invoked via the |background_task_runner_|.
@@ -80,12 +95,6 @@
   void DecodeImage(const scoped_ptr<std::string> data,
                    const ImageInfo& image_info);
 
-  // ImageDecoder::Delegate implementation. These callbacks will only be invoked
-  // via the |background_task_runner_|.
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override;
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override;
-
   // The foreground task runner on which |this| is instantiated, Start() is
   // called and LoadedCallbacks are invoked.
   scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
@@ -97,10 +106,6 @@
   // Specify how the file should be decoded in the utility process.
   const ImageDecoder::ImageCodec image_codec_;
 
-  // Holds information about the images currently being decoded. Accessed via
-  // |background_task_runner_| only.
-  ImageInfoMap image_info_map_;
-
   DISALLOW_COPY_AND_ASSIGN(UserImageLoader);
 };
 
diff --git a/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.cc b/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.cc
index 7f66649..8860df4 100644
--- a/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.cc
+++ b/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.cc
@@ -44,7 +44,8 @@
   return true;
 }
 
-ImageLoader::ImageLoader(const base::FilePath& path) : path_(path) {
+ImageLoader::ImageLoader(const base::FilePath& path)
+    : ImageRequest(base::MessageLoopProxy::current()), path_(path) {
 }
 
 ImageLoader::~ImageLoader() {
@@ -53,23 +54,19 @@
 scoped_ptr<gfx::ImageSkia> ImageLoader::Load() {
   std::string image_data;
   ReadFileToString(path_, &image_data);
-  scoped_refptr<ImageDecoder> image_decoder = new ImageDecoder(
-      this,
-      image_data,
-      ImageDecoder::ROBUST_JPEG_CODEC);
-  image_decoder->Start(base::MessageLoopProxy::current());
+  ImageDecoder::StartWithOptions(this, image_data,
+                                 ImageDecoder::ROBUST_JPEG_CODEC, false);
   run_loop_.Run();
   return decoded_image_.Pass();
 }
 
-void ImageLoader::OnImageDecoded(const ImageDecoder* decoder,
-                                 const SkBitmap& decoded_image) {
+void ImageLoader::OnImageDecoded(const SkBitmap& decoded_image) {
   decoded_image_.reset(
       new gfx::ImageSkia(gfx::ImageSkiaRep(decoded_image, 1.0f)));
   run_loop_.Quit();
 }
 
-void ImageLoader::OnDecodeImageFailed(const ImageDecoder* decoder) {
+void ImageLoader::OnDecodeImageFailed() {
   run_loop_.Quit();
 }
 
diff --git a/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.h b/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.h
index 93b992d..6e237ec 100644
--- a/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.h
+++ b/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.h
@@ -31,17 +31,16 @@
 // Returns |true| if the two given images are pixel-for-pixel identical.
 bool AreImagesEqual(const gfx::ImageSkia& first, const gfx::ImageSkia& second);
 
-class ImageLoader : public ImageDecoder::Delegate {
+class ImageLoader : public ImageDecoder::ImageRequest {
  public:
   explicit ImageLoader(const base::FilePath& path);
   ~ImageLoader() override;
 
   scoped_ptr<gfx::ImageSkia> Load();
 
-  // ImageDecoder::Delegate:
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override;
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override;
+  // ImageDecoder::ImageRequest:
+  void OnImageDecoded(const SkBitmap& decoded_image) override;
+  void OnDecodeImageFailed() override;
 
  private:
   base::FilePath path_;
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index 4cdadd9..14e5255 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -109,7 +109,7 @@
   UpdateNumberOfUsers();
 
   // UserManager instance should be used only on UI thread.
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   registrar_.Add(this,
                  chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED,
                  content::NotificationService::AllSources());
@@ -156,7 +156,7 @@
 }
 
 void ChromeUserManagerImpl::Shutdown() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ChromeUserManager::Shutdown();
 
   local_accounts_subscription_.reset();
@@ -289,7 +289,7 @@
 }
 
 void ChromeUserManagerImpl::SessionStarted() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ChromeUserManager::SessionStarted();
 
   content::NotificationService::current()->Notify(
@@ -328,7 +328,7 @@
 void ChromeUserManagerImpl::SaveUserOAuthStatus(
     const std::string& user_id,
     user_manager::User::OAuthTokenStatus oauth_token_status) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ChromeUserManager::SaveUserOAuthStatus(user_id, oauth_token_status);
 
   GetUserFlow(user_id)->HandleOAuthTokenStatusChange(oauth_token_status);
@@ -337,7 +337,7 @@
 void ChromeUserManagerImpl::SaveUserDisplayName(
     const std::string& user_id,
     const base::string16& display_name) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ChromeUserManager::SaveUserDisplayName(user_id, display_name);
 
   // Do not update local state if data stored or cached outside the user's
@@ -624,7 +624,7 @@
 }
 
 void ChromeUserManagerImpl::GuestUserLoggedIn() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ChromeUserManager::GuestUserLoggedIn();
 
   // TODO(nkostylev): Add support for passing guest session cryptohome
@@ -657,7 +657,7 @@
 
 void ChromeUserManagerImpl::RegularUserLoggedInAsEphemeral(
     const std::string& user_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ChromeUserManager::RegularUserLoggedInAsEphemeral(user_id);
 
   GetUserImageManager(user_id)->UserLoggedIn(IsCurrentUserNew(), false);
@@ -721,7 +721,7 @@
 }
 
 void ChromeUserManagerImpl::KioskAppLoggedIn(const std::string& app_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   policy::DeviceLocalAccount::Type device_local_account_type;
   DCHECK(policy::IsDeviceLocalAccountUser(app_id, &device_local_account_type));
   DCHECK_EQ(policy::DeviceLocalAccount::TYPE_KIOSK_APP,
@@ -770,7 +770,7 @@
 }
 
 void ChromeUserManagerImpl::DemoAccountLoggedIn() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   active_user_ =
       user_manager::User::CreateKioskAppUser(DemoAppLauncher::kDemoUserName);
   active_user_->SetStubImage(
@@ -793,7 +793,7 @@
 }
 
 void ChromeUserManagerImpl::NotifyOnLogin() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   UserSessionManager::OverrideHomedir();
   UpdateNumberOfUsers();
@@ -981,14 +981,14 @@
 }
 
 UserFlow* ChromeUserManagerImpl::GetCurrentUserFlow() const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!IsUserLoggedIn())
     return GetDefaultUserFlow();
   return GetUserFlow(GetLoggedInUser()->email());
 }
 
 UserFlow* ChromeUserManagerImpl::GetUserFlow(const std::string& user_id) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   FlowMap::const_iterator it = specific_flows_.find(user_id);
   if (it != specific_flows_.end())
     return it->second;
@@ -997,13 +997,13 @@
 
 void ChromeUserManagerImpl::SetUserFlow(const std::string& user_id,
                                         UserFlow* flow) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ResetUserFlow(user_id);
   specific_flows_[user_id] = flow;
 }
 
 void ChromeUserManagerImpl::ResetUserFlow(const std::string& user_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   FlowMap::iterator it = specific_flows_.find(user_id);
   if (it != specific_flows_.end()) {
     delete it->second;
@@ -1019,7 +1019,7 @@
 }
 
 UserFlow* ChromeUserManagerImpl::GetDefaultUserFlow() const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!default_flow_.get())
     default_flow_.reset(new DefaultUserFlow());
   return default_flow_.get();
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
index 683c672..299058b 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
@@ -173,4 +173,16 @@
   return default_flow_.get();
 }
 
+bool FakeChromeUserManager::FindKnownUserPrefs(
+    const user_manager::UserID& user_id,
+    const base::DictionaryValue** out_value) {
+  return false;
+}
+
+void FakeChromeUserManager::UpdateKnownUserPrefs(
+    const user_manager::UserID& user_id,
+    const base::DictionaryValue& values,
+    bool clear) {
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
index db4c96f..238f21c 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
@@ -59,6 +59,11 @@
   void SessionStarted() override;
   void RemoveUser(const std::string& email,
                   user_manager::RemoveUserDelegate* delegate) override;
+  bool FindKnownUserPrefs(const user_manager::UserID& user_id,
+                          const base::DictionaryValue** out_value) override;
+  void UpdateKnownUserPrefs(const user_manager::UserID& user_id,
+                            const base::DictionaryValue& values,
+                            bool clear) override;
 
   void set_owner_email(const std::string& owner_email) {
     owner_email_ = owner_email;
diff --git a/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc b/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc
index 9c5ca41a..9339c1f 100644
--- a/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc
@@ -133,7 +133,7 @@
     ChromeUserManagerImpl* owner)
     : owner_(owner), cros_settings_(CrosSettings::Get()) {
   // SupervisedUserManager instance should be used only on UI thread.
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   authentication_.reset(new SupervisedUserAuthentication(this));
 }
 
@@ -357,7 +357,7 @@
 
 const user_manager::User* SupervisedUserManagerImpl::FindByDisplayName(
     const base::string16& display_name) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   const user_manager::UserList& users = owner_->GetUsers();
   for (user_manager::UserList::const_iterator it = users.begin();
        it != users.end();
@@ -372,7 +372,7 @@
 
 const user_manager::User* SupervisedUserManagerImpl::FindBySyncId(
     const std::string& sync_id) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   const user_manager::UserList& users = owner_->GetUsers();
   for (user_manager::UserList::const_iterator it = users.begin();
        it != users.end();
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
index 9faaa97..1c4aa18 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
@@ -192,7 +192,7 @@
 
   // This method is usually triggered by timer to actually load request.
   void ProcessRequest() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     timer.Stop();  // Erase reference to self.
 
@@ -231,7 +231,7 @@
 
   // This method is called by callback, when load request is finished.
   void OnWallpaperSet() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     // The only known case for this check to fail is global destruction during
     // wallpaper load. It should never happen.
@@ -278,7 +278,6 @@
 WallpaperManager::~WallpaperManager() {
   show_user_name_on_signin_subscription_.reset();
   user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
-  ClearObsoleteWallpaperPrefs();
   weak_factory_.InvalidateWeakPtrs();
 }
 
@@ -303,7 +302,7 @@
 
 WallpaperManager::WallpaperResolution
 WallpaperManager::GetAppropriateResolution() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   gfx::Size size =
       ash::DesktopBackgroundController::GetMaxDisplaySizeInNative();
   return (size.width() > wallpaper::kSmallWallpaperMaxWidth ||
@@ -339,7 +338,7 @@
 }
 
 void WallpaperManager::InitializeWallpaper() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
 
   // Apply device customization.
@@ -384,7 +383,7 @@
 void WallpaperManager::Observe(int type,
                                const content::NotificationSource& source,
                                const content::NotificationDetails& details) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   switch (type) {
     case chrome::NOTIFICATION_LOGIN_USER_CHANGED: {
       ClearDisposableWallpaperCache();
@@ -457,7 +456,7 @@
     user_manager::User::WallpaperType type,
     const gfx::ImageSkia& image,
     bool update_wallpaper) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // There is no visible background in kiosk mode.
   if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp())
@@ -596,7 +595,7 @@
 void WallpaperManager::SetUserWallpaperInfo(const std::string& user_id,
                                             const WallpaperInfo& info,
                                             bool is_persistent) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   current_user_wallpaper_info_ = info;
   if (!is_persistent)
     return;
@@ -616,7 +615,7 @@
 
 void WallpaperManager::ScheduleSetUserWallpaper(const std::string& user_id,
                                                 bool delayed) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Some unit tests come here without a UserManager or without a pref system.q
   if (!user_manager::UserManager::IsInitialized() ||
       !g_browser_process->local_state()) {
@@ -775,18 +774,6 @@
     FOR_EACH_OBSERVER(Observer, observers_, OnPendingListEmptyForTesting());
 }
 
-void WallpaperManager::ClearObsoleteWallpaperPrefs() {
-  PrefService* prefs = g_browser_process->local_state();
-  // LocalState can be NULL in tests. Skip for tests.
-  if (prefs) {
-    DictionaryPrefUpdate wallpaper_properties_pref(prefs,
-        wallpaper::kUserWallpapersProperties);
-    wallpaper_properties_pref->Clear();
-    DictionaryPrefUpdate wallpapers_pref(prefs, wallpaper::kUserWallpapers);
-    wallpapers_pref->Clear();
-  }
-}
-
 void WallpaperManager::InitializeRegisteredDeviceWallpaper() {
   if (user_manager::UserManager::Get()->IsUserLoggedIn())
     return;
@@ -818,7 +805,7 @@
 
 bool WallpaperManager::GetUserWallpaperInfo(const std::string& user_id,
                                             WallpaperInfo* info) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
           user_id)) {
@@ -867,7 +854,7 @@
     bool update_wallpaper,
     MovableOnDestroyCallbackHolder on_finish,
     const user_manager::UserImage& user_image) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   TRACE_EVENT_ASYNC_END0("ui", "LoadAndDecodeWallpaper", this);
 
   // If decoded wallpaper is empty, we have probably failed to decode the file.
@@ -900,7 +887,7 @@
                                  bool update_wallpaper,
                                  const base::FilePath& wallpaper_path,
                                  MovableOnDestroyCallbackHolder on_finish) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   TRACE_EVENT_ASYNC_BEGIN0("ui", "LoadAndDecodeWallpaper", this);
   if (update_wallpaper) {
     // We are now about to change the wallpaper, so update the path and remove
@@ -952,7 +939,7 @@
     scoped_ptr<bool> success,
     scoped_ptr<gfx::ImageSkia> small_wallpaper_image,
     scoped_ptr<gfx::ImageSkia> large_wallpaper_image) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(rescaled_files);
   DCHECK(success.get());
   if (!*success) {
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h
index 1faa36813..5aefa6a 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h
@@ -141,7 +141,6 @@
   void RemovePendingWallpaperFromList(PendingWallpaper* pending);
 
   // WallpaperManagerBase overrides:
-  void ClearObsoleteWallpaperPrefs() override;
   void InitializeRegisteredDeviceWallpaper() override;
   bool GetUserWallpaperInfo(const std::string& user_id,
                             wallpaper::WallpaperInfo* info) const override;
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 8c3e0cd..893a547 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -1175,7 +1175,7 @@
 void WizardController::OnTimezoneResolved(
     scoped_ptr<TimeZoneResponseData> timezone,
     bool server_error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(timezone.get());
   // To check that "this" is not destroyed try to access some member
   // (timezone_provider_) in this case. Expect crash here.
@@ -1227,7 +1227,7 @@
 void WizardController::OnLocationResolved(const Geoposition& position,
                                           bool server_error,
                                           const base::TimeDelta elapsed) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   const base::TimeDelta timeout =
       base::TimeDelta::FromSeconds(kResolveTimeZoneTimeoutSeconds);
diff --git a/chrome/browser/chromeos/memory/low_memory_observer.cc b/chrome/browser/chromeos/memory/low_memory_observer.cc
index f402225..fbbb3fd 100644
--- a/chrome/browser/chromeos/memory/low_memory_observer.cc
+++ b/chrome/browser/chromeos/memory/low_memory_observer.cc
@@ -117,7 +117,7 @@
   DCHECK_LE(file_descriptor_, 0)
       << "Attempted to start observation when it was already started.";
   DCHECK(watcher_.get() == NULL);
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   DCHECK(base::MessageLoopForIO::current());
 
   file_descriptor_ = ::open(kLowMemFile, O_RDONLY);
@@ -135,7 +135,7 @@
   // If StartObserving failed, StopObserving will still get called.
   timer_.Stop();
   if (file_descriptor_ >= 0) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+    DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     watcher_.reset(NULL);
     ::close(file_descriptor_);
     file_descriptor_ = -1;
@@ -151,7 +151,7 @@
 
 void LowMemoryObserverImpl::StartWatchingDescriptor() {
   DCHECK(watcher_.get());
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   DCHECK(base::MessageLoopForIO::current());
   if (file_descriptor_ < 0)
     return;
diff --git a/chrome/browser/chromeos/memory/oom_priority_manager.cc b/chrome/browser/chromeos/memory/oom_priority_manager.cc
index 8d496db..3f0ed21b 100644
--- a/chrome/browser/chromeos/memory/oom_priority_manager.cc
+++ b/chrome/browser/chromeos/memory/oom_priority_manager.cc
@@ -273,7 +273,7 @@
 // such as tabs created with JavaScript window.open().  We might want to
 // discard the entire set together, or use that in the priority computation.
 bool OomPriorityManager::DiscardTab() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   TabStatsList stats = GetTabStatsOnUIThread();
   if (stats.empty())
     return false;
@@ -289,7 +289,7 @@
 }
 
 void OomPriorityManager::LogMemoryAndDiscardTab() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Deletes itself upon completion.
   OomMemoryDetails* details = new OomMemoryDetails();
   details->StartFetch(MemoryDetails::FROM_CHROME_ONLY);
@@ -416,7 +416,7 @@
 }
 
 void OomPriorityManager::RecordRecentTabDiscard() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // If we change the interval we need to change the histogram name.
   UMA_HISTOGRAM_BOOLEAN("Tabs.Discard.DiscardInLastMinute",
                         recent_tab_discard_);
@@ -484,7 +484,7 @@
 }
 
 void OomPriorityManager::AdjustFocusedTabScoreOnFileThread() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   base::AutoLock oom_score_autolock(oom_score_lock_);
   base::ProcessHandle pid = focused_tab_process_info_.second;
   content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
@@ -596,7 +596,7 @@
 }
 
 OomPriorityManager::TabStatsList OomPriorityManager::GetTabStatsOnUIThread() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   TabStatsList stats_list;
   stats_list.reserve(32);  // 99% of users have < 30 tabs open
   bool browser_active = true;
@@ -664,7 +664,7 @@
 
 void OomPriorityManager::AdjustOomPrioritiesOnFileThread(
     TabStatsList stats_list) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   base::AutoLock oom_score_autolock(oom_score_lock_);
 
   // Remove any duplicate PIDs. Order of the list is maintained, so each
diff --git a/chrome/browser/chromeos/mobile/mobile_activator.cc b/chrome/browser/chromeos/mobile/mobile_activator.cc
index a538cf1..73b5d44 100644
--- a/chrome/browser/chromeos/mobile/mobile_activator.cc
+++ b/chrome/browser/chromeos/mobile/mobile_activator.cc
@@ -101,7 +101,7 @@
 }
 
 void CellularConfigDocument::LoadCellularConfigFile() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   // Load partner customization startup manifest if it is available.
   base::FilePath config_path(kCellularConfigPath);
   if (!base::PathExists(config_path))
diff --git a/chrome/browser/chromeos/mobile_config.cc b/chrome/browser/chromeos/mobile_config.cc
index 8eea570..63eaebef 100644
--- a/chrome/browser/chromeos/mobile_config.cc
+++ b/chrome/browser/chromeos/mobile_config.cc
@@ -355,7 +355,7 @@
 void MobileConfig::ReadConfigInBackground(
     const base::FilePath& global_config_file,
     const base::FilePath& local_config_file) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   std::string global_config;
   std::string local_config;
   if (!base::ReadFileToString(global_config_file, &global_config)) {
diff --git a/chrome/browser/chromeos/offline/offline_load_page.cc b/chrome/browser/chromeos/offline/offline_load_page.cc
index 55f590e..2e80a225 100644
--- a/chrome/browser/chromeos/offline/offline_load_page.cc
+++ b/chrome/browser/chromeos/offline/offline_load_page.cc
@@ -60,7 +60,7 @@
 }
 
 OfflineLoadPage::~OfflineLoadPage() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
 }
 
@@ -111,13 +111,13 @@
 }
 
 void OfflineLoadPage::OnProceed() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   proceeded_ = true;
   NotifyBlockingPageComplete(true);
 }
 
 void OfflineLoadPage::OnDontProceed() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Ignore if it's already proceeded.
   if (proceeded_)
     return;
@@ -157,7 +157,7 @@
 
 void OfflineLoadPage::OnConnectionTypeChanged(
     net::NetworkChangeNotifier::ConnectionType type) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   const bool online = type != net::NetworkChangeNotifier::CONNECTION_NONE;
   DVLOG(1) << "ConnectionTypeObserver notification received: state="
            << (online ? "online" : "offline");
diff --git a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
index 48a81cb..bac0fba 100644
--- a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
+++ b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
 
+#include <algorithm>
 #include <string>
 
 #include "base/bind.h"
@@ -15,7 +16,6 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/device_settings_provider.h"
 #include "chrome/browser/chromeos/settings/session_manager_operation.h"
@@ -174,7 +174,6 @@
   NOTREACHED();
   return false;
 }
-
 }  // namespace
 
 OwnerSettingsServiceChromeOS::ManagementSettings::ManagementSettings() {
@@ -192,6 +191,7 @@
       profile_(profile),
       waiting_for_profile_creation_(true),
       waiting_for_tpm_token_(true),
+      has_pending_fixups_(false),
       has_pending_management_settings_(false),
       weak_factory_(this),
       store_settings_factory_(this) {
@@ -249,6 +249,11 @@
   ReloadKeypair();
 }
 
+bool OwnerSettingsServiceChromeOS::HasPendingChanges() const {
+  return !pending_changes_.empty() || tentative_settings_.get() ||
+         has_pending_management_settings_ || has_pending_fixups_;
+}
+
 bool OwnerSettingsServiceChromeOS::HandlesSetting(const std::string& setting) {
   return DeviceSettingsProvider::IsDeviceSetting(setting);
 }
@@ -414,7 +419,9 @@
 scoped_ptr<em::PolicyData> OwnerSettingsServiceChromeOS::AssemblePolicy(
     const std::string& user_id,
     const em::PolicyData* policy_data,
-    const em::ChromeDeviceSettingsProto* settings) {
+    bool apply_pending_management_settings,
+    const ManagementSettings& pending_management_settings,
+    em::ChromeDeviceSettingsProto* settings) {
   scoped_ptr<em::PolicyData> policy(new em::PolicyData());
   if (policy_data) {
     // Preserve management settings.
@@ -429,10 +436,28 @@
     // setting is set. We set the management mode to LOCAL_OWNER initially.
     policy->set_management_mode(em::PolicyData::LOCAL_OWNER);
   }
+  if (apply_pending_management_settings) {
+    policy::SetManagementMode(*policy,
+                              pending_management_settings.management_mode);
+
+    if (pending_management_settings.request_token.empty())
+      policy->clear_request_token();
+    else
+      policy->set_request_token(pending_management_settings.request_token);
+
+    if (pending_management_settings.device_id.empty())
+      policy->clear_device_id();
+    else
+      policy->set_device_id(pending_management_settings.device_id);
+  }
   policy->set_policy_type(policy::dm_protocol::kChromeDevicePolicyType);
   policy->set_timestamp(
       (base::Time::Now() - base::Time::UnixEpoch()).InMilliseconds());
   policy->set_username(user_id);
+  if (policy_data->management_mode() == em::PolicyData::LOCAL_OWNER ||
+      policy_data->management_mode() == em::PolicyData::CONSUMER_MANAGED) {
+    FixupLocalOwnerPolicy(user_id, settings);
+  }
   if (!settings->SerializeToString(policy->mutable_policy_value()))
     return scoped_ptr<em::PolicyData>();
 
@@ -440,6 +465,21 @@
 }
 
 // static
+void OwnerSettingsServiceChromeOS::FixupLocalOwnerPolicy(
+    const std::string& user_id,
+    enterprise_management::ChromeDeviceSettingsProto* settings) {
+  if (!settings->has_allow_new_users())
+    settings->mutable_allow_new_users()->set_allow_new_users(true);
+
+  em::UserWhitelistProto* whitelist_proto = settings->mutable_user_whitelist();
+  if (whitelist_proto->user_whitelist().end() ==
+      std::find(whitelist_proto->user_whitelist().begin(),
+                whitelist_proto->user_whitelist().end(), user_id)) {
+    whitelist_proto->add_user_whitelist(user_id);
+  }
+}
+
+// static
 void OwnerSettingsServiceChromeOS::UpdateDeviceSettings(
     const std::string& path,
     const base::Value& value,
@@ -674,6 +714,8 @@
   const bool is_owner = IsOwner() || IsOwnerInTests(user_id_);
   if (is_owner && device_settings_service_)
     device_settings_service_->InitOwner(user_id_, weak_factory_.GetWeakPtr());
+
+  has_pending_fixups_ = true;
 }
 
 void OwnerSettingsServiceChromeOS::ReloadKeypairImpl(const base::Callback<
@@ -695,7 +737,7 @@
 }
 
 void OwnerSettingsServiceChromeOS::StorePendingChanges() {
-  if (!has_pending_changes() || store_settings_factory_.HasWeakPtrs() ||
+  if (!HasPendingChanges() || store_settings_factory_.HasWeakPtrs() ||
       !device_settings_service_ || user_id_.empty()) {
     return;
   }
@@ -716,23 +758,11 @@
     UpdateDeviceSettings(change.first, *change.second, settings);
   pending_changes_.clear();
 
-  scoped_ptr<em::PolicyData> policy = AssemblePolicy(
-      user_id_, device_settings_service_->policy_data(), &settings);
-
-  if (has_pending_management_settings_) {
-    policy::SetManagementMode(*policy,
-                              pending_management_settings_.management_mode);
-
-    if (pending_management_settings_.request_token.empty())
-      policy->clear_request_token();
-    else
-      policy->set_request_token(pending_management_settings_.request_token);
-
-    if (pending_management_settings_.device_id.empty())
-      policy->clear_device_id();
-    else
-      policy->set_device_id(pending_management_settings_.device_id);
-  }
+  scoped_ptr<em::PolicyData> policy =
+      AssemblePolicy(user_id_, device_settings_service_->policy_data(),
+                     has_pending_management_settings_,
+                     pending_management_settings_, &settings);
+  has_pending_fixups_ = false;
   has_pending_management_settings_ = false;
 
   bool rv = AssembleAndSignPolicyAsync(
diff --git a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h
index f1394b4..4429ba35 100644
--- a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h
+++ b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h
@@ -65,6 +65,8 @@
 
   void OnTPMTokenReady(bool tpm_token_enabled);
 
+  bool HasPendingChanges() const;
+
   // ownership::OwnerSettingsService implementation:
   bool HandlesSetting(const std::string& setting) override;
   bool Set(const std::string& setting, const base::Value& value) override;
@@ -100,12 +102,14 @@
       const scoped_refptr<ownership::OwnerKeyUtil>& owner_key_util,
       const IsOwnerCallback& callback);
 
-  // Assembles PolicyData based on |settings|, |policy_data| and
-  // |user_id|.
+  // Assembles PolicyData based on |settings|, |policy_data|, |user_id| and
+  // |pending_management_settings|. Applies local-owner policy fixups if needed.
   static scoped_ptr<enterprise_management::PolicyData> AssemblePolicy(
       const std::string& user_id,
       const enterprise_management::PolicyData* policy_data,
-      const enterprise_management::ChromeDeviceSettingsProto* settings);
+      bool apply_pending_mangement_settings,
+      const ManagementSettings& pending_management_settings,
+      enterprise_management::ChromeDeviceSettingsProto* settings);
 
   // Updates device |settings|.
   static void UpdateDeviceSettings(
@@ -113,11 +117,6 @@
       const base::Value& value,
       enterprise_management::ChromeDeviceSettingsProto& settings);
 
-  bool has_pending_changes() const {
-    return !pending_changes_.empty() || tentative_settings_.get() ||
-           has_pending_management_settings_;
-  }
-
  protected:
   OwnerSettingsServiceChromeOS(
       DeviceSettingsService* device_settings_service,
@@ -127,6 +126,13 @@
  private:
   friend class OwnerSettingsServiceChromeOSFactory;
 
+  // Perform fixups required to ensure sensical local-owner device policy:
+  //  1) user whitelisting must be explicitly allowed or disallowed, and
+  //  2) the owner user must be on the whitelist, if it's enforced.
+  static void FixupLocalOwnerPolicy(
+      const std::string& user_id,
+      enterprise_management::ChromeDeviceSettingsProto* settings);
+
   // OwnerSettingsService protected interface overrides:
 
   // Reloads private key from profile's NSS slots, responds via |callback|.
@@ -168,6 +174,9 @@
   // Whether TPM token still needs to be initialized.
   bool waiting_for_tpm_token_;
 
+  // True if local-owner policy fixups are still pending.
+  bool has_pending_fixups_;
+
   // A set of pending changes to device settings.
   base::ScopedPtrHashMap<std::string, base::Value> pending_changes_;
 
diff --git a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos_unittest.cc b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos_unittest.cc
index a06337e..aa4d68909 100644
--- a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos_unittest.cc
@@ -45,7 +45,7 @@
 
   // OwnerSettingsService::Observer implementation:
   void OnSignedPolicyStored(bool success) override {
-    if (service_->has_pending_changes())
+    if (service_->HasPendingChanges())
       return;
 
     while (!set_requests_.empty()) {
@@ -78,6 +78,13 @@
   DISALLOW_COPY_AND_ASSIGN(PrefsChecker);
 };
 
+bool FindInListValue(const std::string& needle, const base::Value* haystack) {
+  const base::ListValue* list;
+  if (!haystack->GetAsList(&list))
+    return false;
+  return list->end() != list->Find(base::StringValue(needle));
+}
+
 }  // namespace
 
 class OwnerSettingsServiceChromeOSTest : public DeviceSettingsTestBase {
@@ -123,6 +130,7 @@
     management_settings_set_ = success;
   }
 
+ protected:
   OwnerSettingsServiceChromeOS* service_;
   ScopedTestingLocalState local_state_;
   scoped_ptr<DeviceSettingsProvider> provider_;
@@ -294,6 +302,15 @@
   EXPECT_EQ("fake_device_id", policy_data->device_id());
 }
 
+TEST_F(OwnerSettingsServiceChromeOSTest, ForceWhitelist) {
+  EXPECT_FALSE(FindInListValue(device_policy_.policy_data().username(),
+                               provider_->Get(kAccountsPrefUsers)));
+  // Force a settings write.
+  TestSingleSet(service_, kReleaseChannel, base::StringValue("dev-channel"));
+  EXPECT_TRUE(FindInListValue(device_policy_.policy_data().username(),
+                              provider_->Get(kAccountsPrefUsers)));
+}
+
 class OwnerSettingsServiceChromeOSNoOwnerTest
     : public OwnerSettingsServiceChromeOSTest {
  public:
@@ -321,4 +338,16 @@
   ASSERT_FALSE(service_->SetBoolean(kAccountsPrefAllowGuest, false));
 }
 
+TEST_F(OwnerSettingsServiceChromeOSNoOwnerTest, TakeOwnershipForceWhitelist) {
+  EXPECT_FALSE(FindInListValue(device_policy_.policy_data().username(),
+                               provider_->Get(kAccountsPrefUsers)));
+  owner_key_util_->SetPrivateKey(device_policy_.GetSigningKey());
+  InitOwner(device_policy_.policy_data().username(), true);
+  ReloadDeviceSettings();
+  ASSERT_TRUE(service_->IsOwner());
+
+  EXPECT_TRUE(FindInListValue(device_policy_.policy_data().username(),
+                              provider_->Get(kAccountsPrefUsers)));
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
index a6d1a07f..af14d11 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
@@ -94,7 +94,7 @@
                             const GetCertDBCallback& callback,
                             NSSOperationState* state,
                             net::NSSCertDatabase* cert_db) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!cert_db) {
     LOG(ERROR) << "Couldn't get NSSCertDatabase.";
     state->OnError(FROM_HERE, kErrorInternal);
@@ -124,7 +124,7 @@
                                const GetCertDBCallback& callback,
                                content::ResourceContext* context,
                                NSSOperationState* state) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   net::NSSCertDatabase* cert_db = GetNSSCertDatabaseForResourceContext(
       context, base::Bind(&DidGetCertDBOnIOThread, token_id, callback, state));
 
@@ -426,7 +426,7 @@
 // GenerateRSAKey().
 void GenerateRSAKeyWithDB(scoped_ptr<GenerateRSAKeyState> state,
                           net::NSSCertDatabase* cert_db) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // Only the slot and not the NSSCertDatabase is required. Ignore |cert_db|.
   base::WorkerPool::PostTask(
       FROM_HERE,
@@ -517,7 +517,7 @@
 // Continues signing with the obtained NSSCertDatabase. Used by Sign().
 void SignRSAWithDB(scoped_ptr<SignRSAState> state,
                    net::NSSCertDatabase* cert_db) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // Only the slot and not the NSSCertDatabase is required. Ignore |cert_db|.
   base::WorkerPool::PostTask(
       FROM_HERE, base::Bind(&SignRSAOnWorkerThread, base::Passed(&state)),
@@ -529,7 +529,7 @@
 // SelectCertificatesOnIOThread().
 void DidSelectCertificatesOnIOThread(
     scoped_ptr<SelectCertificatesState> state) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   state->CallBack(FROM_HERE, state->certs_.Pass(),
                   std::string() /* no error */);
 }
@@ -537,7 +537,7 @@
 // Continues selecting certificates on the IO thread. Used by
 // SelectClientCertificates().
 void SelectCertificatesOnIOThread(scoped_ptr<SelectCertificatesState> state) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   state->cert_store_.reset(new net::ClientCertStoreChromeOS(
       make_scoped_ptr(new chromeos::ClientCertFilterChromeOS(
           state->use_system_key_slot_, state->username_hash_)),
@@ -578,7 +578,7 @@
 // GetCertificatesWithDB().
 void DidGetCertificates(scoped_ptr<GetCertificatesState> state,
                         scoped_ptr<net::CertificateList> all_certs) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   state->certs_ = all_certs.Pass();
   base::WorkerPool::PostTask(
       FROM_HERE,
@@ -590,7 +590,7 @@
 // GetCertificates().
 void GetCertificatesWithDB(scoped_ptr<GetCertificatesState> state,
                            net::NSSCertDatabase* cert_db) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // Get the pointer to slot before base::Passed releases |state|.
   PK11SlotInfo* slot = state->slot_.get();
   cert_db->ListCertsInSlot(
@@ -601,7 +601,7 @@
 // ImportCertificate().
 void ImportCertificateWithDB(scoped_ptr<ImportCertificateState> state,
                              net::NSSCertDatabase* cert_db) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // TODO(pneubeck): Use |state->slot_| to verify that we're really importing to
   // the correct token.
   // |cert_db| is not required, ignore it.
@@ -640,7 +640,7 @@
 void DidRemoveCertificate(scoped_ptr<RemoveCertificateState> state,
                           bool certificate_found,
                           bool success) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // CertificateNotFound error has precedence over an internal error.
   if (!certificate_found) {
     state->OnError(FROM_HERE, kErrorCertificateNotFound);
@@ -658,7 +658,7 @@
 // RemoveCertificate().
 void RemoveCertificateWithDB(scoped_ptr<RemoveCertificateState> state,
                              net::NSSCertDatabase* cert_db) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // Get the pointer before base::Passed clears |state|.
   scoped_refptr<net::X509Certificate> certificate = state->certificate_;
   bool certificate_found = certificate->os_cert_handle()->isperm;
@@ -671,7 +671,7 @@
 // Does the actual work to determine which tokens are available.
 void GetTokensWithDB(scoped_ptr<GetTokensState> state,
                      net::NSSCertDatabase* cert_db) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   scoped_ptr<std::vector<std::string> > token_ids(new std::vector<std::string>);
 
   // The user's token is always available.
@@ -690,7 +690,7 @@
                     unsigned int modulus_length_bits,
                     const GenerateKeyCallback& callback,
                     BrowserContext* browser_context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_ptr<GenerateRSAKeyState> state(
       new GenerateRSAKeyState(modulus_length_bits, callback));
 
@@ -713,7 +713,7 @@
                         HashAlgorithm hash_algorithm,
                         const SignCallback& callback,
                         content::BrowserContext* browser_context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_ptr<SignRSAState> state(
       new SignRSAState(data, public_key, false /* digest before signing */,
                        hash_algorithm, callback));
@@ -732,7 +732,7 @@
                      const std::string& public_key,
                      const SignCallback& callback,
                      content::BrowserContext* browser_context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_ptr<SignRSAState> state(new SignRSAState(
       data, public_key, true /* sign directly without hashing */,
       HASH_ALGORITHM_NONE, callback));
@@ -749,7 +749,7 @@
 void SelectClientCertificates(const ClientCertificateRequest& request,
                               const SelectCertificatesCallback& callback,
                               content::BrowserContext* browser_context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
       new net::SSLCertRequestInfo);
@@ -821,7 +821,7 @@
 void GetCertificates(const std::string& token_id,
                      const GetCertificatesCallback& callback,
                      BrowserContext* browser_context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_ptr<GetCertificatesState> state(new GetCertificatesState(callback));
   // Get the pointer to |state| before base::Passed releases |state|.
   NSSOperationState* state_ptr = state.get();
@@ -835,7 +835,7 @@
                        const scoped_refptr<net::X509Certificate>& certificate,
                        const ImportCertificateCallback& callback,
                        BrowserContext* browser_context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_ptr<ImportCertificateState> state(
       new ImportCertificateState(certificate, callback));
   // Get the pointer to |state| before base::Passed releases |state|.
@@ -854,7 +854,7 @@
                        const scoped_refptr<net::X509Certificate>& certificate,
                        const RemoveCertificateCallback& callback,
                        BrowserContext* browser_context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_ptr<RemoveCertificateState> state(
       new RemoveCertificateState(certificate, callback));
   // Get the pointer to |state| before base::Passed releases |state|.
@@ -870,7 +870,7 @@
 
 void GetTokens(const GetTokensCallback& callback,
                content::BrowserContext* browser_context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_ptr<GetTokensState> state(new GetTokensState(callback));
   // Get the pointer to |state| before base::Passed releases |state|.
   NSSOperationState* state_ptr = state.get();
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
index 34228d3..d4c38a6 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
@@ -651,7 +651,7 @@
                                          unsigned int modulus_length,
                                          const std::string& extension_id,
                                          const GenerateKeyCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   platform_keys::subtle::GenerateRSAKey(
       token_id, modulus_length,
@@ -667,7 +667,7 @@
     platform_keys::HashAlgorithm hash_algorithm,
     const std::string& extension_id,
     const SignCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   StartOrQueueTask(make_scoped_ptr(new SignTask(
       token_id, data, public_key, false /* digest before signing */,
       hash_algorithm, extension_id, callback, this)));
@@ -678,7 +678,7 @@
                                           const std::string& public_key,
                                           const std::string& extension_id,
                                           const SignCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   StartOrQueueTask(make_scoped_ptr(new SignTask(
       token_id, data, public_key, true /* sign directly without hashing */,
       platform_keys::HASH_ALGORITHM_NONE, extension_id, callback, this)));
@@ -690,7 +690,7 @@
     const std::string& extension_id,
     const SelectCertificatesCallback& callback,
     content::WebContents* web_contents) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   StartOrQueueTask(make_scoped_ptr(new SelectTask(
       request, interactive, extension_id, callback, web_contents, this)));
 }
diff --git a/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc b/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc
index 7508e24..f5688fa 100644
--- a/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc
+++ b/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc
@@ -103,7 +103,7 @@
 
 void PolicyOAuth2TokenFetcher::RetryOnError(const GoogleServiceAuthError& error,
                                             const base::Closure& task) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if ((error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
        error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE ||
        error.state() == GoogleServiceAuthError::REQUEST_CANCELED) &&
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
index b0a2959..3929680 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
@@ -12,10 +12,10 @@
 #include "base/sequenced_task_runner.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/helper.h"
 #include "chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h"
 #include "chrome/browser/chromeos/policy/wildcard_login_checker.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/common/chrome_content_client.h"
 #include "components/policy/core/common/cloud/cloud_external_data_manager.h"
@@ -212,7 +212,7 @@
   // access token is already available.
   if (!client()->is_registered()) {
     if (wait_for_policy_fetch_) {
-      FetchPolicyOAuthTokenUsingSigninProfile();
+      FetchPolicyOAuthTokenUsingSigninContext();
     } else if (!access_token_.empty()) {
       OnAccessTokenAvailable(access_token_);
     }
@@ -286,13 +286,11 @@
   SetEnterpriseUsersDefaults(policy_map);
 }
 
-void UserCloudPolicyManagerChromeOS::FetchPolicyOAuthTokenUsingSigninProfile() {
-  scoped_refptr<net::URLRequestContextGetter> signin_context;
-  Profile* signin_profile = chromeos::ProfileHelper::GetSigninProfile();
-  if (signin_profile)
-    signin_context = signin_profile->GetRequestContext();
+void UserCloudPolicyManagerChromeOS::FetchPolicyOAuthTokenUsingSigninContext() {
+  scoped_refptr<net::URLRequestContextGetter> signin_context =
+      chromeos::login::GetSigninContext();
   if (!signin_context.get()) {
-    LOG(ERROR) << "No signin Profile for policy oauth token fetch!";
+    LOG(ERROR) << "No signin context for policy oauth token fetch!";
     OnOAuth2PolicyTokenFetched(
         std::string(), GoogleServiceAuthError(GoogleServiceAuthError::NONE));
     return;
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h
index ca073611..fdc7766 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h
@@ -110,8 +110,8 @@
 
  private:
   // Fetches a policy token using the authentication context of the signin
-  // Profile, and calls back to OnOAuth2PolicyTokenFetched when done.
-  void FetchPolicyOAuthTokenUsingSigninProfile();
+  // context, and calls back to OnOAuth2PolicyTokenFetched when done.
+  void FetchPolicyOAuthTokenUsingSigninContext();
 
   // Called once the policy access token is available, and starts the
   // registration with the policy server if the token was successfully fetched.
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
index d630d653..7d7f3994 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
@@ -342,15 +342,10 @@
   TestingProfile* profile_;
   TestingProfile* signin_profile_;
 
-  static const char kSigninProfile[];
-
  private:
   DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyManagerChromeOSTest);
 };
 
-const char UserCloudPolicyManagerChromeOSTest::kSigninProfile[] =
-    "signin_profile";
-
 TEST_F(UserCloudPolicyManagerChromeOSTest, BlockingFirstFetch) {
   // Tests the initialization of a manager whose Profile is waiting for the
   // initial fetch, when the policy cache is empty.
diff --git a/chrome/browser/chromeos/profiles/profile_list_chromeos.cc b/chrome/browser/chromeos/profiles/profile_list_chromeos.cc
index d8f7591..e18db97 100644
--- a/chrome/browser/chromeos/profiles/profile_list_chromeos.cc
+++ b/chrome/browser/chromeos/profiles/profile_list_chromeos.cc
@@ -63,7 +63,9 @@
     item->name = (*it)->GetDisplayName();
     item->sync_state = profile_info_->GetUserNameOfProfileAtIndex(i);
     item->profile_path = profile_info_->GetPathOfProfileAtIndex(i);
-    item->supervised = false;
+    DCHECK(!profile_info_->ProfileIsLegacySupervisedAtIndex(i));
+    item->legacy_supervised = false;
+    item->child_account = profile_info_->ProfileIsChildAtIndex(i);
     item->signed_in = true;
     item->active = profile_info_->GetPathOfProfileAtIndex(i) ==
         active_profile_path_;
diff --git a/chrome/browser/chromeos/system_logs/command_line_log_source.cc b/chrome/browser/chromeos/system_logs/command_line_log_source.cc
index 9ceeaaf..c0b58e5 100644
--- a/chrome/browser/chromeos/system_logs/command_line_log_source.cc
+++ b/chrome/browser/chromeos/system_logs/command_line_log_source.cc
@@ -105,7 +105,7 @@
 }
 
 void CommandLineLogSource::Fetch(const SysLogsSourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   SystemLogsResponse* response = new SystemLogsResponse;
diff --git a/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc b/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc
index 47bc739..030536d4 100644
--- a/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc
+++ b/chrome/browser/chromeos/system_logs/touch_log_source_ozone.cc
@@ -119,7 +119,7 @@
 void OnEventLogCollected(scoped_ptr<system_logs::SystemLogsResponse> response,
                          const system_logs::SysLogsSourceCallback& callback,
                          scoped_ptr<std::vector<base::FilePath>> log_paths) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // We cannot eliminate these temporaries and inline these closures because the
   // compiler may call release() before get().
@@ -139,7 +139,7 @@
 void OnStatusLogCollected(scoped_ptr<system_logs::SystemLogsResponse> response,
                           const system_logs::SysLogsSourceCallback& callback,
                           scoped_ptr<std::string> log) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   (*response)[kDeviceStatusLogDataKey] = *log;
 
   // Collect touch event logs.
@@ -167,7 +167,7 @@
 namespace system_logs {
 
 void TouchLogSource::Fetch(const SysLogsSourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   scoped_ptr<SystemLogsResponse> response(new SystemLogsResponse);
diff --git a/chrome/browser/chromeos/system_logs/touch_log_source_x11.cc b/chrome/browser/chromeos/system_logs/touch_log_source_x11.cc
index fa27399..0d1d833 100644
--- a/chrome/browser/chromeos/system_logs/touch_log_source_x11.cc
+++ b/chrome/browser/chromeos/system_logs/touch_log_source_x11.cc
@@ -55,7 +55,7 @@
 namespace system_logs {
 
 void TouchLogSource::Fetch(const SysLogsSourceCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   SystemLogsResponse* response = new SystemLogsResponse;
diff --git a/chrome/browser/component_updater/test/chrome_component_updater_configurator_unittest.cc b/chrome/browser/component_updater/test/chrome_component_updater_configurator_unittest.cc
new file mode 100644
index 0000000..9e97aaa5
--- /dev/null
+++ b/chrome/browser/component_updater/test/chrome_component_updater_configurator_unittest.cc
@@ -0,0 +1,64 @@
+// 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 "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/component_updater/chrome_component_updater_configurator.h"
+#include "components/component_updater/component_updater_switches.h"
+#include "components/update_client/configurator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace component_updater {
+
+TEST(ChromeComponentUpdaterConfiguratorTest, TestDisablePings) {
+  base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+  cmdline->AppendSwitchASCII(switches::kComponentUpdater, "disable-pings");
+
+  scoped_ptr<update_client::Configurator> config(
+      MakeChromeComponentUpdaterConfigurator(cmdline, NULL));
+
+  const std::vector<GURL> pingUrls = config->PingUrl();
+  EXPECT_TRUE(pingUrls.empty());
+}
+
+TEST(ChromeComponentUpdaterConfiguratorTest, TestFastUpdate) {
+  base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+  cmdline->AppendSwitchASCII(switches::kComponentUpdater, "fast-update");
+
+  scoped_ptr<update_client::Configurator> config(
+      MakeChromeComponentUpdaterConfigurator(cmdline, NULL));
+
+  ASSERT_EQ(1, config->InitialDelay());
+}
+
+TEST(ChromeComponentUpdaterConfiguratorTest, TestOverrideUrl) {
+  const char overrideUrl[] = "http://0.0.0.0/";
+
+  base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+  std::string val = "url-source";
+  val.append("=");
+  val.append(overrideUrl);
+  cmdline->AppendSwitchASCII(switches::kComponentUpdater, val.c_str());
+
+  scoped_ptr<update_client::Configurator> config(
+      MakeChromeComponentUpdaterConfigurator(cmdline, NULL));
+
+  const std::vector<GURL> urls = config->UpdateUrl();
+
+  ASSERT_EQ(1U, urls.size());
+  ASSERT_EQ(overrideUrl, urls.at(0).possibly_invalid_spec());
+}
+
+TEST(ChromeComponentUpdaterConfiguratorTest, TestSwitchRequestParam) {
+  base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+  cmdline->AppendSwitchASCII(switches::kComponentUpdater, "test-request");
+
+  scoped_ptr<update_client::Configurator> config(
+      MakeChromeComponentUpdaterConfigurator(cmdline, NULL));
+
+  EXPECT_FALSE(config->ExtraRequestParams().empty());
+}
+
+}  // namespace component_updater
diff --git a/chrome/browser/content_settings/host_content_settings_map_unittest.cc b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
index 6ec5c36..001ca3b2 100644
--- a/chrome/browser/content_settings/host_content_settings_map_unittest.cc
+++ b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
@@ -961,6 +961,16 @@
                   prefs, CONTENT_SETTING_ASK,
                   CONTENT_SETTINGS_TYPE_FULLSCREEN));
 
+  EXPECT_FALSE(HostContentSettingsMap::IsSettingAllowedForType(
+                   prefs, CONTENT_SETTING_ALLOW,
+                   CONTENT_SETTINGS_TYPE_MEDIASTREAM));
+  EXPECT_FALSE(HostContentSettingsMap::IsSettingAllowedForType(
+                   prefs, CONTENT_SETTING_ASK,
+                   CONTENT_SETTINGS_TYPE_MEDIASTREAM));
+  EXPECT_FALSE(HostContentSettingsMap::IsSettingAllowedForType(
+                   prefs, CONTENT_SETTING_BLOCK,
+                   CONTENT_SETTINGS_TYPE_MEDIASTREAM));
+
   // TODO(msramek): Add more checks for setting type - setting pairs where
   // it is not obvious whether or not they are allowed.
 }
diff --git a/chrome/browser/content_settings/permission_context_uma_util.cc b/chrome/browser/content_settings/permission_context_uma_util.cc
index 2f496384..dd4c827 100644
--- a/chrome/browser/content_settings/permission_context_uma_util.cc
+++ b/chrome/browser/content_settings/permission_context_uma_util.cc
@@ -2,29 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/permission_context_uma_util.h"
+#include "components/rappor/rappor_utils.h"
 #include "content/public/browser/permission_type.h"
 #include "url/gurl.h"
 
 // UMA keys need to be statically initialized so plain function would not
 // work. Use a Macro instead.
-#define PERMISSION_ACTION_UMA(secure_origin,                                   \
-            permission, permission_secure, permission_insecure, action)        \
-    UMA_HISTOGRAM_ENUMERATION(                                                 \
-        permission,                                                            \
-        action,                                                                \
-        PERMISSION_ACTION_NUM);                                                \
-    if (secure_origin) {                                                       \
-        UMA_HISTOGRAM_ENUMERATION(permission_secure,                           \
-                                  action,                                      \
-                                  PERMISSION_ACTION_NUM);                      \
-    } else {                                                                   \
-        UMA_HISTOGRAM_ENUMERATION(permission_insecure,                         \
-                                  action,                                      \
-                                  PERMISSION_ACTION_NUM);                      \
-    }
-
+#define PERMISSION_ACTION_UMA(secure_origin, permission, permission_secure, \
+                              permission_insecure, action)                  \
+  UMA_HISTOGRAM_ENUMERATION(permission, action, PERMISSION_ACTION_NUM);     \
+  if (secure_origin) {                                                      \
+    UMA_HISTOGRAM_ENUMERATION(permission_secure, action,                    \
+                              PERMISSION_ACTION_NUM);                       \
+  } else {                                                                  \
+    UMA_HISTOGRAM_ENUMERATION(permission_insecure, action,                  \
+                              PERMISSION_ACTION_NUM);                       \
+  }
 
 namespace {
 
@@ -41,10 +38,50 @@
   PERMISSION_ACTION_NUM,
 };
 
-void RecordPermissionAction(
-      ContentSettingsType permission,
-      PermissionAction action,
-      bool secure_origin) {
+const std::string GetRapporMetric(ContentSettingsType permission,
+                                  PermissionAction action) {
+  std::string action_str;
+  switch (action) {
+    case GRANTED:
+      action_str = "Granted";
+      break;
+    case DENIED:
+      action_str = "Denied";
+      break;
+    case DISMISSED:
+      action_str = "Dismissed";
+      break;
+    case IGNORED:
+      action_str = "Ignored";
+      break;
+    case PERMISSION_ACTION_NUM:
+      NOTREACHED();
+      break;
+  }
+  std::string permission_str;
+  switch (permission) {
+    case CONTENT_SETTINGS_TYPE_GEOLOCATION:
+      permission_str = "Geolocation";
+      break;
+    case CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
+      permission_str = "Notifications";
+      break;
+    default:
+      permission_str = "";
+      break;
+  }
+
+  if (permission_str.empty())
+    return "";
+  return base::StringPrintf("ContentSettings.PermissionActions_%s.%s.Url",
+                            permission_str.c_str(), action_str.c_str());
+}
+
+void RecordPermissionAction(ContentSettingsType permission,
+                            PermissionAction action,
+                            const GURL& requesting_origin) {
+  bool secure_origin = requesting_origin.SchemeIsSecure();
+
   switch (permission) {
       case CONTENT_SETTINGS_TYPE_GEOLOCATION:
         PERMISSION_ACTION_UMA(
@@ -91,27 +128,42 @@
       default:
         NOTREACHED() << "PERMISSION " << permission << " not accounted for";
     }
+
+    const std::string& rappor_metric = GetRapporMetric(permission, action);
+    if (!rappor_metric.empty())
+      rappor::SampleDomainAndRegistryFromGURL(
+          g_browser_process->rappor_service(), rappor_metric,
+          requesting_origin);
 }
 
-void RecordPermissionRequest(
-    ContentSettingsType permission, bool secure_origin) {
+void RecordPermissionRequest(ContentSettingsType permission,
+                             const GURL& requesting_origin) {
+  bool secure_origin = requesting_origin.SchemeIsSecure();
   content::PermissionType type;
   switch (permission) {
     case CONTENT_SETTINGS_TYPE_GEOLOCATION:
-      type = content::PERMISSION_GEOLOCATION;
+      type = content::PermissionType::GEOLOCATION;
+      rappor::SampleDomainAndRegistryFromGURL(
+          g_browser_process->rappor_service(),
+          "ContentSettings.PermissionRequested.Geolocation.Url",
+          requesting_origin);
       break;
     case CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
-      type = content::PERMISSION_NOTIFICATIONS;
+      type = content::PermissionType::NOTIFICATIONS;
+      rappor::SampleDomainAndRegistryFromGURL(
+          g_browser_process->rappor_service(),
+          "ContentSettings.PermissionRequested.Notifications.Url",
+          requesting_origin);
       break;
     case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
-      type = content::PERMISSION_MIDI_SYSEX;
+      type = content::PermissionType::MIDI_SYSEX;
       break;
     case CONTENT_SETTINGS_TYPE_PUSH_MESSAGING:
-      type = content::PERMISSION_PUSH_MESSAGING;
+      type = content::PermissionType::PUSH_MESSAGING;
       break;
 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
     case CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER:
-      type = content::PERMISSION_PROTECTED_MEDIA_IDENTIFIER;
+      type = content::PermissionType::PROTECTED_MEDIA_IDENTIFIER;
       break;
 #endif
     default:
@@ -119,17 +171,19 @@
       return;
   }
   UMA_HISTOGRAM_ENUMERATION(
-      "ContentSettings.PermissionRequested", type, content::PERMISSION_NUM);
+      "ContentSettings.PermissionRequested",
+      static_cast<base::HistogramBase::Sample>(type),
+      static_cast<base::HistogramBase::Sample>(content::PermissionType::NUM));
   if (secure_origin) {
     UMA_HISTOGRAM_ENUMERATION(
         "ContentSettings.PermissionRequested_SecureOrigin",
-        type,
-        content::PERMISSION_NUM);
+        static_cast<base::HistogramBase::Sample>(type),
+        static_cast<base::HistogramBase::Sample>(content::PermissionType::NUM));
   } else {
     UMA_HISTOGRAM_ENUMERATION(
         "ContentSettings.PermissionRequested_InsecureOrigin",
-        type,
-        content::PERMISSION_NUM);
+        static_cast<base::HistogramBase::Sample>(type),
+        static_cast<base::HistogramBase::Sample>(content::PermissionType::NUM));
   }
 }
 
@@ -139,29 +193,25 @@
 // add new permission
 void PermissionContextUmaUtil::PermissionRequested(
     ContentSettingsType permission, const GURL& requesting_origin) {
-  RecordPermissionRequest(permission, requesting_origin.SchemeIsSecure());
+  RecordPermissionRequest(permission, requesting_origin);
 }
 
 void PermissionContextUmaUtil::PermissionGranted(
     ContentSettingsType permission, const GURL& requesting_origin) {
-  RecordPermissionAction(permission, GRANTED,
-                         requesting_origin.SchemeIsSecure());
+  RecordPermissionAction(permission, GRANTED, requesting_origin);
 }
 
 void PermissionContextUmaUtil::PermissionDenied(
     ContentSettingsType permission, const GURL& requesting_origin) {
-  RecordPermissionAction(permission, DENIED,
-                         requesting_origin.SchemeIsSecure());
+  RecordPermissionAction(permission, DENIED, requesting_origin);
 }
 
 void PermissionContextUmaUtil::PermissionDismissed(
     ContentSettingsType permission, const GURL& requesting_origin) {
-  RecordPermissionAction(permission, DISMISSED,
-                         requesting_origin.SchemeIsSecure());
+  RecordPermissionAction(permission, DISMISSED, requesting_origin);
 }
 
 void PermissionContextUmaUtil::PermissionIgnored(
     ContentSettingsType permission, const GURL& requesting_origin) {
-  RecordPermissionAction(permission, IGNORED,
-                         requesting_origin.SchemeIsSecure());
+  RecordPermissionAction(permission, IGNORED, requesting_origin);
 }
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.cc b/chrome/browser/content_settings/tab_specific_content_settings.cc
index e1d8a63..364c7d52 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.cc
+++ b/chrome/browser/content_settings/tab_specific_content_settings.cc
@@ -236,10 +236,6 @@
     settings->OnFileSystemAccessed(url, blocked_by_policy);
 }
 
-const base::string16 TabSpecificContentSettings::GetBlockedPluginNames() const {
-  return JoinString(blocked_plugin_names_, base::ASCIIToUTF16(", "));
-}
-
 bool TabSpecificContentSettings::IsContentBlocked(
     ContentSettingsType content_type) const {
   DCHECK(content_type != CONTENT_SETTINGS_TYPE_GEOLOCATION)
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.h b/chrome/browser/content_settings/tab_specific_content_settings.h
index 2a6705b..8851b89 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.h
+++ b/chrome/browser/content_settings/tab_specific_content_settings.h
@@ -180,7 +180,9 @@
   bool IsContentAllowed(ContentSettingsType content_type) const;
 
   // Returns the names of plugins that have been blocked for this tab.
-  const base::string16 GetBlockedPluginNames() const;
+  const std::vector<base::string16>& blocked_plugin_names() const {
+    return blocked_plugin_names_;
+  }
 
   const GURL& media_stream_access_origin() const {
     return media_stream_access_origin_;
diff --git a/chrome/browser/devtools/device/android_device_manager.cc b/chrome/browser/devtools/device/android_device_manager.cc
index e19c3bb..0459dc8 100644
--- a/chrome/browser/devtools/device/android_device_manager.cc
+++ b/chrome/browser/devtools/device/android_device_manager.cc
@@ -433,14 +433,14 @@
 // static
 scoped_refptr<AndroidDeviceManager::HandlerThread>
 AndroidDeviceManager::HandlerThread::GetInstance() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!instance_)
     new HandlerThread();
   return instance_;
 }
 
 AndroidDeviceManager::HandlerThread::HandlerThread() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   instance_ = this;
   thread_ = new base::Thread(kDevToolsAdbBridgeThreadName);
   base::Thread::Options options;
@@ -463,7 +463,7 @@
 }
 
 AndroidDeviceManager::HandlerThread::~HandlerThread() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   instance_ = NULL;
   if (!thread_)
     return;
diff --git a/chrome/browser/devtools/device/android_web_socket.cc b/chrome/browser/devtools/device/android_web_socket.cc
index e5c29b5..84eea20 100644
--- a/chrome/browser/devtools/device/android_web_socket.cc
+++ b/chrome/browser/devtools/device/android_web_socket.cc
@@ -140,7 +140,7 @@
       socket_impl_(nullptr),
       delegate_(delegate),
       weak_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(delegate_);
   DCHECK(device_);
   device_->sockets_.insert(this);
@@ -150,13 +150,13 @@
 }
 
 AndroidDeviceManager::AndroidWebSocket::~AndroidWebSocket() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   Terminate();
 }
 
 void AndroidDeviceManager::AndroidWebSocket::SendFrame(
     const std::string& message) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(socket_impl_);
   DCHECK(device_);
   device_->message_loop_proxy_->PostTask(
@@ -169,7 +169,7 @@
     int result,
     const std::string& extensions,
     scoped_ptr<net::StreamSocket> socket) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (result != net::OK || !socket.get()) {
     OnSocketClosed();
     return;
@@ -187,18 +187,18 @@
 
 void AndroidDeviceManager::AndroidWebSocket::OnFrameRead(
     const std::string& message) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   delegate_->OnFrameRead(message);
 }
 
 void AndroidDeviceManager::AndroidWebSocket::OnSocketClosed() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   Terminate();
   delegate_->OnSocketClosed();
 }
 
 void AndroidDeviceManager::AndroidWebSocket::Terminate() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (socket_impl_) {
     DCHECK(device_);
     device_->message_loop_proxy_->DeleteSoon(FROM_HERE, socket_impl_);
diff --git a/chrome/browser/devtools/device/devtools_android_bridge.cc b/chrome/browser/devtools/device/devtools_android_bridge.cc
index 4f6762a..f4fe8c7d8 100644
--- a/chrome/browser/devtools/device/devtools_android_bridge.cc
+++ b/chrome/browser/devtools/device/devtools_android_bridge.cc
@@ -66,6 +66,8 @@
 const int kMinVersionNewWithURL = 32;
 const int kNewPageNavigateDelayMs = 500;
 
+const char kWebViewSocketPrefix[] = "webview_devtools_remote";
+
 bool IsWebRTCDeviceProviderEnabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kEnableDevToolsExperiments);
@@ -114,19 +116,19 @@
     AndroidDeviceManager* device_manager,
     const DeviceListCallback& callback)
     : callback_(callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   device_manager->QueryDevices(
       base::Bind(&DiscoveryRequest::ReceivedDevices, this));
 }
 
 DevToolsAndroidBridge::DiscoveryRequest::~DiscoveryRequest() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   callback_.Run(complete_devices_);
 }
 
 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedDevices(
     const AndroidDeviceManager::Devices& devices) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   for (const auto& device : devices) {
     device->QueryDeviceInfo(
         base::Bind(&DiscoveryRequest::ReceivedDeviceInfo, this, device));
@@ -136,7 +138,7 @@
 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedDeviceInfo(
     scoped_refptr<AndroidDeviceManager::Device> device,
     const AndroidDeviceManager::DeviceInfo& device_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_refptr<RemoteDevice> remote_device =
       new RemoteDevice(device->serial(), device_info);
   complete_devices_.push_back(std::make_pair(device, remote_device));
@@ -157,7 +159,7 @@
     scoped_refptr<RemoteBrowser> browser,
     int result,
     const std::string& response) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (result < 0)
     return;
   // Parse version, append to package name if available,
@@ -185,7 +187,7 @@
     scoped_refptr<RemoteBrowser> browser,
     int result,
     const std::string& response) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (result < 0)
     return;
   scoped_ptr<base::Value> value(base::JSONReader::Read(response));
@@ -193,10 +195,8 @@
   if (value && value->GetAsList(&list_value)) {
     for (const auto& page_value : *list_value) {
       base::DictionaryValue* dict;
-      if (page_value->GetAsDictionary(&dict)) {
-        browser->pages_.push_back(
-            new RemotePage(browser->browser_id_, *dict, browser->IsWebView()));
-      }
+      if (page_value->GetAsDictionary(&dict))
+        browser->pages_.push_back(new RemotePage(browser->browser_id_, *dict));
     }
   }
 }
@@ -308,16 +308,14 @@
       DevToolsAndroidBridge* bridge,
       const std::string& id,
       const BrowserId& browser_id,
-      const std::string& debug_url,
-      bool is_web_view);
+      const std::string& debug_url);
 
  private:
   AgentHostDelegate(
       DevToolsAndroidBridge* bridge,
       const std::string& id,
       const BrowserId& browser_id,
-      const std::string& debug_url,
-      bool is_web_view);
+      const std::string& debug_url);
   ~AgentHostDelegate() override;
   void Attach(content::DevToolsExternalAgentProxy* proxy) override;
   void Detach() override;
@@ -331,7 +329,6 @@
   BrowserId browser_id_;
   std::string debug_url_;
   bool socket_opened_;
-  bool is_web_view_;
   std::vector<std::string> pending_messages_;
   scoped_refptr<AndroidDeviceManager::Device> device_;
   scoped_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_;
@@ -346,15 +343,14 @@
     DevToolsAndroidBridge* bridge,
     const std::string& id,
     const BrowserId& browser_id,
-    const std::string& debug_url,
-    bool is_web_view) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    const std::string& debug_url) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   AgentHostDelegates::iterator it = bridge->host_delegates_.find(id);
   if (it != bridge->host_delegates_.end())
     return it->second->agent_host_;
 
   AgentHostDelegate* delegate =
-      new AgentHostDelegate(bridge, id, browser_id, debug_url, is_web_view);
+      new AgentHostDelegate(bridge, id, browser_id, debug_url);
   scoped_refptr<content::DevToolsAgentHost> result =
       content::DevToolsAgentHost::Create(delegate);
   delegate->agent_host_ = result.get();
@@ -365,14 +361,12 @@
     DevToolsAndroidBridge* bridge,
     const std::string& id,
     const BrowserId& browser_id,
-    const std::string& debug_url,
-    bool is_web_view)
+    const std::string& debug_url)
     : id_(id),
       bridge_(bridge->AsWeakPtr()),
       browser_id_(browser_id),
       debug_url_(debug_url),
       socket_opened_(false),
-      is_web_view_(is_web_view),
       agent_host_(NULL),
       proxy_(NULL) {
   bridge_->host_delegates_[id] = this;
@@ -386,7 +380,7 @@
 void DevToolsAndroidBridge::AgentHostDelegate::Attach(
     content::DevToolsExternalAgentProxy* proxy) {
   proxy_ = proxy;
-  content::RecordAction(is_web_view_ ?
+  content::RecordAction(browser_id_.second.find(kWebViewSocketPrefix) == 0 ?
       base::UserMetricsAction("DevTools_InspectAndroidWebView") :
       base::UserMetricsAction("DevTools_InspectAndroidPage"));
 
@@ -439,8 +433,7 @@
  public:
   RemotePageTarget(DevToolsAndroidBridge* bridge,
                    const BrowserId& browser_id,
-                   const base::DictionaryValue& value,
-                   bool is_web_view_);
+                   const base::DictionaryValue& value);
   ~RemotePageTarget() override;
 
   // DevToolsTargetImpl overrides.
@@ -501,14 +494,12 @@
 DevToolsAndroidBridge::RemotePageTarget::RemotePageTarget(
     DevToolsAndroidBridge* bridge,
     const BrowserId& browser_id,
-    const base::DictionaryValue& value,
-    bool is_web_view)
+    const base::DictionaryValue& value)
     : DevToolsTargetImpl(AgentHostDelegate::GetOrCreateAgentHost(
                              bridge,
                              BuildUniqueTargetId(browser_id, value),
                              browser_id,
-                             GetDebugURL(value),
-                             is_web_view)),
+                             GetDebugURL(value))),
       bridge_(bridge->AsWeakPtr()),
       browser_id_(browser_id),
       debug_url_(GetDebugURL(value)),
@@ -588,13 +579,10 @@
 
 // DevToolsAndroidBridge::RemotePage ------------------------------------------
 
-DevToolsAndroidBridge::RemotePage::RemotePage(
-    const BrowserId& browser_id,
-    const base::DictionaryValue& dict,
-    bool is_web_view)
+DevToolsAndroidBridge::RemotePage::RemotePage(const BrowserId& browser_id,
+                                              const base::DictionaryValue& dict)
     : browser_id_(browser_id),
       frontend_url_(GetFrontendURL(dict)),
-      is_web_view_(is_web_view),
       dict_(dict.DeepCopy()) {
 }
 
@@ -616,10 +604,6 @@
   return type_ == AndroidDeviceManager::BrowserInfo::kTypeChrome;
 }
 
-bool DevToolsAndroidBridge::RemoteBrowser::IsWebView() {
-  return type_ == AndroidDeviceManager::BrowserInfo::kTypeWebView;
-}
-
 std::string DevToolsAndroidBridge::RemoteBrowser::GetId() {
   return serial() + ":" + socket();
 }
@@ -639,8 +623,7 @@
 
 DevToolsTargetImpl*
 DevToolsAndroidBridge::CreatePageTarget(scoped_refptr<RemotePage> page) {
-  return new RemotePageTarget(this, page->browser_id_, *page->dict_,
-                              page->is_web_view_);
+  return new RemotePageTarget(this, page->browser_id_, *page->dict_);
 }
 
 void DevToolsAndroidBridge::SendJsonRequest(
@@ -662,7 +645,7 @@
     const std::string& method,
     scoped_ptr<base::DictionaryValue> params,
     const base::Closure callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (debug_url.empty())
     return;
   scoped_refptr<AndroidDeviceManager::Device> device(
@@ -684,8 +667,7 @@
       this,
       "adb:" + browser->serial() + ":" + browser->socket(),
       browser->browser_id_,
-      kBrowserTargetSocket,
-      browser->IsWebView());
+      kBrowserTargetSocket);
 }
 
 void DevToolsAndroidBridge::SendJsonRequest(
@@ -711,25 +693,22 @@
     const RemotePageCallback& callback,
     int result,
     const std::string& response) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (result < 0) {
     callback.Run(NULL);
     return;
   }
   scoped_ptr<base::Value> value(base::JSONReader::Read(response));
   base::DictionaryValue* dict;
-  if (value && value->GetAsDictionary(&dict)) {
-    scoped_refptr<RemotePage> new_page(
-        new RemotePage(browser->browser_id_, *dict, browser->IsWebView()));
-    callback.Run(new_page);
-  }
+  if (value && value->GetAsDictionary(&dict))
+    callback.Run(new RemotePage(browser->browser_id_, *dict));
 }
 
 void DevToolsAndroidBridge::OpenRemotePage(
     scoped_refptr<RemoteBrowser> browser,
     const std::string& input_url,
     const DevToolsAndroidBridge::RemotePageCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   GURL gurl(input_url);
   if (!gurl.is_valid()) {
     gurl = GURL("http://" + input_url);
@@ -761,7 +740,7 @@
     const std::string& url,
     int result,
     const std::string& response) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (result < 0)
     return;
@@ -780,13 +759,12 @@
     int result,
     const std::string& response,
     const std::string& url) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_ptr<base::Value> value(base::JSONReader::Read(response));
   base::DictionaryValue* dict;
 
   if (value && value->GetAsDictionary(&dict)) {
-    RemotePageTarget new_page(this, browser->browser_id_, *dict,
-                              browser->IsWebView());
+    RemotePageTarget new_page(this, browser->browser_id_, *dict);
     new_page.Navigate(url,
         base::Bind(&DevToolsAndroidBridge::RespondToOpenOnUIThread,
                    AsWeakPtr(), browser, callback, result, response));
@@ -829,7 +807,7 @@
       task_scheduler_(base::Bind(&DevToolsAndroidBridge::ScheduleTaskDefault)),
       port_forwarding_controller_(new PortForwardingController(profile, this)),
       weak_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   pref_change_registrar_.Init(profile_->GetPrefs());
   pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
       base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders,
@@ -839,7 +817,7 @@
 
 void DevToolsAndroidBridge::AddDeviceListListener(
     DeviceListListener* listener) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   bool polling_was_off = !NeedsDeviceListPolling();
   device_list_listeners_.push_back(listener);
   if (polling_was_off)
@@ -848,7 +826,7 @@
 
 void DevToolsAndroidBridge::RemoveDeviceListListener(
     DeviceListListener* listener) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DeviceListListeners::iterator it = std::find(
       device_list_listeners_.begin(), device_list_listeners_.end(), listener);
   DCHECK(it != device_list_listeners_.end());
@@ -866,7 +844,7 @@
 
 void DevToolsAndroidBridge::RemoveDeviceCountListener(
     DeviceCountListener* listener) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DeviceCountListeners::iterator it = std::find(
       device_count_listeners_.begin(), device_count_listeners_.end(), listener);
   DCHECK(it != device_count_listeners_.end());
@@ -896,12 +874,12 @@
 }
 
 bool DevToolsAndroidBridge::HasDevToolsWindow(const std::string& agent_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return host_delegates_.find(agent_id) != host_delegates_.end();
 }
 
 DevToolsAndroidBridge::~DevToolsAndroidBridge() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(device_list_listeners_.empty());
   DCHECK(device_count_listeners_.empty());
   DCHECK(port_forwarding_listeners_.empty());
@@ -924,7 +902,7 @@
 
 void DevToolsAndroidBridge::RequestDeviceList(
     const DeviceListCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!NeedsDeviceListPolling() ||
       !callback.Equals(device_list_callback_.callback()))
@@ -935,7 +913,7 @@
 
 void DevToolsAndroidBridge::ReceivedDeviceList(
     const CompleteDevices& complete_devices) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   device_map_.clear();
   RemoteDevices remote_devices;
@@ -976,7 +954,7 @@
 
 void DevToolsAndroidBridge::RequestDeviceCount(
     const base::Callback<void(int)>& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (device_count_listeners_.empty() ||
       !callback.Equals(device_count_callback_.callback()))
@@ -986,7 +964,7 @@
 }
 
 void DevToolsAndroidBridge::ReceivedDeviceCount(int count) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   DeviceCountListeners copy(device_count_listeners_);
   for (DeviceCountListeners::iterator it = copy.begin(); it != copy.end(); ++it)
diff --git a/chrome/browser/devtools/device/devtools_android_bridge.h b/chrome/browser/devtools/device/devtools_android_bridge.h
index 381984ec..bb05d2ab 100644
--- a/chrome/browser/devtools/device/devtools_android_bridge.h
+++ b/chrome/browser/devtools/device/devtools_android_bridge.h
@@ -69,21 +69,17 @@
     const std::string& serial() { return browser_id_.first; }
     const std::string& socket() { return browser_id_.second; }
     const std::string& frontend_url() { return frontend_url_; }
-    bool is_web_view() { return is_web_view_; }
 
    private:
     friend class base::RefCounted<RemotePage>;
     friend class DevToolsAndroidBridge;
 
-    RemotePage(const BrowserId& browser_id,
-               const base::DictionaryValue& dict,
-               bool is_web_view);
+    RemotePage(const BrowserId& browser_id, const base::DictionaryValue& dict);
 
     virtual ~RemotePage();
 
     BrowserId browser_id_;
     std::string frontend_url_;
-    bool is_web_view_;
     scoped_ptr<base::DictionaryValue> dict_;
 
     DISALLOW_COPY_AND_ASSIGN(RemotePage);
@@ -102,7 +98,6 @@
     const RemotePages& pages() { return pages_; }
 
     bool IsChrome();
-    bool IsWebView();
     std::string GetId();
 
     typedef std::vector<int> ParsedVersion;
diff --git a/chrome/browser/devtools/device/port_forwarding_controller.cc b/chrome/browser/devtools/device/port_forwarding_controller.cc
index 25bdc392..35f0ebb 100644
--- a/chrome/browser/devtools/device/port_forwarding_controller.cc
+++ b/chrome/browser/devtools/device/port_forwarding_controller.cc
@@ -323,7 +323,7 @@
       connected_(false),
       forwarding_map_(forwarding_map),
       weak_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   controller_->registry_[browser->serial()] = this;
   scoped_refptr<AndroidDeviceManager::Device> device(
       controller_->bridge_->FindDevice(browser->serial()));
@@ -334,7 +334,7 @@
 }
 
 PortForwardingController::Connection::~Connection() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(controller_->registry_.find(browser_->serial()) !=
          controller_->registry_.end());
   controller_->registry_.erase(browser_->serial());
@@ -342,7 +342,7 @@
 
 void PortForwardingController::Connection::UpdateForwardingMap(
     const ForwardingMap& new_forwarding_map) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (connected_) {
     SerializeChanges(tethering::unbind::kName,
         new_forwarding_map, forwarding_map_);
@@ -356,7 +356,7 @@
     const std::string& method,
     const ForwardingMap& old_map,
     const ForwardingMap& new_map) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   for (ForwardingMap::const_iterator new_it(new_map.begin());
       new_it != new_map.end(); ++new_it) {
     int port = new_it->first;
@@ -371,7 +371,7 @@
 
 void PortForwardingController::Connection::SendCommand(
     const std::string& method, int port) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue);
   if (method == tethering::bind::kName) {
     params->SetInteger(tethering::bind::kParamPort, port);
@@ -451,7 +451,7 @@
 void PortForwardingController::Connection::UpdateSocketCount(
     int port, int increment) {
 #if defined(DEBUG_DEVTOOLS)
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   PortStatusMap::iterator it = port_status_.find(port);
   if (it == port_status_.end())
     return;
@@ -463,12 +463,12 @@
 
 const PortForwardingController::PortStatusMap&
 PortForwardingController::Connection::GetPortStatusMap() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return port_status_;
 }
 
 void PortForwardingController::Connection::OnSocketOpened() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   connected_ = true;
   SerializeChanges(tethering::bind::kName, ForwardingMap(), forwarding_map_);
 }
@@ -479,7 +479,7 @@
 
 void PortForwardingController::Connection::OnFrameRead(
     const std::string& message) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (ProcessResponse(message))
     return;
 
diff --git a/chrome/browser/devtools/device/usb/android_usb_browsertest.cc b/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
index af2dd46..0c96628 100644
--- a/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
+++ b/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
@@ -586,7 +586,7 @@
   }
 
   void ScheduleDeviceCountRequest(const base::Closure& request) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     scheduler_invoked_++;
     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, request);
   }
@@ -690,7 +690,7 @@
   };
 
   void ShutdownOnUIThread() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     if (reposts_left_-- == 0) {
       base::MessageLoop::current()->Quit();
     } else {
@@ -703,7 +703,7 @@
   }
 
   void ShutdownOnFileThread() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+    DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     BrowserThread::PostTask(BrowserThread::UI,
                             FROM_HERE,
                             base::Bind(&MockCountListener::ShutdownOnUIThread,
diff --git a/chrome/browser/devtools/device/usb/android_usb_device.cc b/chrome/browser/devtools/device/usb/android_usb_device.cc
index 0ff40cd..4ced939 100644
--- a/chrome/browser/devtools/device/usb/android_usb_device.cc
+++ b/chrome/browser/devtools/device/usb/android_usb_device.cc
@@ -180,7 +180,7 @@
     const AndroidUsbDevicesCallback& callback,
     AndroidUsbDevices* devices,
     scoped_refptr<base::MessageLoopProxy> caller_message_loop_proxy) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   caller_message_loop_proxy->PostTask(
       FROM_HERE,
       base::Bind(&RespondOnCallerThread, callback, devices));
@@ -193,7 +193,7 @@
     scoped_refptr<UsbDevice> device,
     int interface_id,
     bool success) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   if (success) {
     base::string16 serial;
     if (device->GetSerialNumber(&serial) && !serial.empty()) {
@@ -215,7 +215,7 @@
 }
 
 static int CountOnFileThread() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   UsbService* service = device::DeviceClient::Get()->GetUsbService();
   UsbDevices usb_devices;
   if (service != NULL)
@@ -238,7 +238,7 @@
     crypto::RSAPrivateKey* rsa_key,
     const AndroidUsbDevicesCallback& callback,
     scoped_refptr<base::MessageLoopProxy> caller_message_loop_proxy) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
 
   UsbService* service = device::DeviceClient::Get()->GetUsbService();
   UsbDevices usb_devices;
@@ -612,7 +612,7 @@
 
 void AndroidUsbDevice::TerminateIfReleased(
     scoped_refptr<UsbDeviceHandle> usb_handle) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   if (usb_handle->GetDevice().get())
     return;
   message_loop_->PostTask(FROM_HERE,
diff --git a/chrome/browser/devtools/device/webrtc/devtools_bridge_client.cc b/chrome/browser/devtools/device/webrtc/devtools_bridge_client.cc
index 055b8040..99af8a0 100644
--- a/chrome/browser/devtools/device/webrtc/devtools_bridge_client.cc
+++ b/chrome/browser/devtools/device/webrtc/devtools_bridge_client.cc
@@ -52,7 +52,7 @@
     Profile* profile,
     SigninManagerBase* signin_manager,
     ProfileOAuth2TokenService* token_service) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto instance =
       new DevToolsBridgeClient(profile, signin_manager, token_service);
   return instance->weak_factory_.GetWeakPtr();
@@ -67,7 +67,7 @@
       identity_provider_(signin_manager, token_service, nullptr),
       worker_is_loaded_(false),
       weak_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   identity_provider_.AddObserver(this);
   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
@@ -78,13 +78,13 @@
 }
 
 DevToolsBridgeClient::~DevToolsBridgeClient() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   identity_provider_.RemoveObserver(this);
 }
 
 void DevToolsBridgeClient::DeleteSelf() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   delete this;
 }
 
@@ -178,7 +178,7 @@
 }
 
 void DevToolsBridgeClient::CreateBackgroundWorker() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   background_worker_.reset(
       WebContents::Create(WebContents::CreateParams(profile_)));
@@ -204,19 +204,19 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
 
   delete this;
 }
 
 void DevToolsBridgeClient::OnActiveAccountLogin() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   CreateBackgroundWorker();
 }
 
 void DevToolsBridgeClient::OnActiveAccountLogout() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   background_worker_.reset();
   browser_list_request_.reset();
   send_command_request_.reset();
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
index 8709880..5505565 100644
--- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
+++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
@@ -186,10 +186,6 @@
   d->RegisterHandler("zoomIn", &Delegate::ZoomIn, delegate);
   d->RegisterHandler("zoomOut", &Delegate::ZoomOut, delegate);
   d->RegisterHandler("resetZoom", &Delegate::ResetZoom, delegate);
-  d->RegisterHandler("openUrlOnRemoteDeviceAndInspect",
-                     &Delegate::OpenUrlOnRemoteDeviceAndInspect, delegate);
-  d->RegisterHandler("setDeviceCountUpdatesEnabled",
-                     &Delegate::SetDeviceCountUpdatesEnabled, delegate);
   d->RegisterHandler("setDevicesUpdatesEnabled",
                      &Delegate::SetDevicesUpdatesEnabled, delegate);
   d->RegisterHandler("sendMessageToBrowser",
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
index 4371c26..665040c2 100644
--- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
+++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
@@ -65,9 +65,6 @@
     virtual void ZoomIn() = 0;
     virtual void ZoomOut() = 0;
     virtual void ResetZoom() = 0;
-    virtual void OpenUrlOnRemoteDeviceAndInspect(const std::string& browser_id,
-                                                 const std::string& url) = 0;
-    virtual void SetDeviceCountUpdatesEnabled(bool enabled) = 0;
     virtual void SetDevicesUpdatesEnabled(bool enabled) = 0;
     virtual void SendMessageToBrowser(const std::string& message) = 0;
     virtual void RecordActionUMA(const std::string& name, int action) = 0;
diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc
index 78bc9a4..11f270bb 100644
--- a/chrome/browser/devtools/devtools_file_helper.cc
+++ b/chrome/browser/devtools/devtools_file_helper.cc
@@ -118,21 +118,21 @@
 };
 
 void WriteToFile(const base::FilePath& path, const std::string& content) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   DCHECK(!path.empty());
 
   base::WriteFile(path, content.c_str(), content.length());
 }
 
 void AppendToFile(const base::FilePath& path, const std::string& content) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   DCHECK(!path.empty());
 
   base::AppendToFile(path, content.c_str(), content.size());
 }
 
 storage::IsolatedContext* isolated_context() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   storage::IsolatedContext* isolated_context =
       storage::IsolatedContext::GetInstance();
   DCHECK(isolated_context);
@@ -142,7 +142,7 @@
 std::string RegisterFileSystem(WebContents* web_contents,
                                const base::FilePath& path,
                                std::string* registered_name) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   CHECK(web_contents->GetURL().SchemeIs(content::kChromeDevToolsScheme));
   std::string file_system_id = isolated_context()->RegisterFileSystemForPath(
       storage::kFileSystemTypeNativeLocal,
@@ -410,7 +410,7 @@
 }
 
 void DevToolsFileHelper::RemoveFileSystem(const std::string& file_system_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
   isolated_context()->RevokeFileSystemByPath(path);
 
@@ -422,7 +422,7 @@
 
 bool DevToolsFileHelper::IsFileSystemAdded(
     const std::string& file_system_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   set<std::string> file_system_paths = GetAddedFileSystemPaths(profile_);
   return file_system_paths.find(file_system_path) != file_system_paths.end();
 }
diff --git a/chrome/browser/devtools/devtools_file_system_indexer.cc b/chrome/browser/devtools/devtools_file_system_indexer.cc
index de03d68..3a7cc14 100644
--- a/chrome/browser/devtools/devtools_file_system_indexer.cc
+++ b/chrome/browser/devtools/devtools_file_system_indexer.cc
@@ -137,7 +137,7 @@
 Index::~Index() {}
 
 Time Index::LastModifiedTimeForFile(const FilePath& file_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   Time last_modified_time;
   if (index_times_.find(file_path) != index_times_.end())
     last_modified_time = index_times_[file_path];
@@ -147,7 +147,7 @@
 void Index::SetTrigramsForFile(const FilePath& file_path,
                                const vector<Trigram>& index,
                                const Time& time) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   FileId file_id = GetFileId(file_path);
   vector<Trigram>::const_iterator it = index.begin();
   for (; it != index.end(); ++it) {
@@ -159,7 +159,7 @@
 }
 
 vector<FilePath> Index::Search(string query) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   const char* data = query.c_str();
   vector<TrigramChar> trigram_chars;
   trigram_chars.reserve(query.size());
@@ -203,7 +203,7 @@
 }
 
 FileId Index::GetFileId(const FilePath& file_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   string file_path_str = file_path.AsUTF8Unsafe();
   if (file_ids_.find(file_path) != file_ids_.end())
     return file_ids_[file_path];
@@ -212,7 +212,7 @@
 }
 
 void Index::NormalizeVectors() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   for (size_t i = 0; i < kTrigramCount; ++i) {
     if (!is_normalized_[i]) {
       std::sort(index_[i].begin(), index_[i].end());
@@ -224,7 +224,7 @@
 }
 
 void Index::PrintStats() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   LOG(ERROR) << "Index stats:";
   size_t size = 0;
   size_t maxSize = 0;
@@ -267,7 +267,7 @@
 DevToolsFileSystemIndexer::FileSystemIndexingJob::~FileSystemIndexingJob() {}
 
 void DevToolsFileSystemIndexer::FileSystemIndexingJob::Start() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   BrowserThread::PostTask(
       BrowserThread::FILE,
       FROM_HERE,
@@ -275,7 +275,7 @@
 }
 
 void DevToolsFileSystemIndexer::FileSystemIndexingJob::Stop() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   BrowserThread::PostTask(BrowserThread::FILE,
                           FROM_HERE,
                           Bind(&FileSystemIndexingJob::StopOnFileThread, this));
@@ -286,7 +286,7 @@
 }
 
 void DevToolsFileSystemIndexer::FileSystemIndexingJob::CollectFilesToIndex() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   if (stopped_)
     return;
   if (!file_enumerator_) {
@@ -317,7 +317,7 @@
 }
 
 void DevToolsFileSystemIndexer::FileSystemIndexingJob::IndexFiles() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   if (stopped_)
     return;
   if (indexing_it_ == file_path_times_.end()) {
@@ -393,7 +393,7 @@
 
 void DevToolsFileSystemIndexer::FileSystemIndexingJob::FinishFileIndexing(
     bool success) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   CloseFile();
   if (success) {
     FilePath file_path = indexing_it_->first;
@@ -441,7 +441,7 @@
     const TotalWorkCallback& total_work_callback,
     const WorkedCallback& worked_callback,
     const DoneCallback& done_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_refptr<FileSystemIndexingJob> indexing_job =
       new FileSystemIndexingJob(FilePath::FromUTF8Unsafe(file_system_path),
                                 total_work_callback,
@@ -454,7 +454,7 @@
 void DevToolsFileSystemIndexer::SearchInPath(const string& file_system_path,
                                              const string& query,
                                              const SearchCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   BrowserThread::PostTask(
       BrowserThread::FILE,
       FROM_HERE,
@@ -469,7 +469,7 @@
     const string& file_system_path,
     const string& query,
     const SearchCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   vector<FilePath> file_paths = g_trigram_index.Get().Search(query);
   vector<string> result;
   FilePath path = FilePath::FromUTF8Unsafe(file_system_path);
diff --git a/chrome/browser/devtools/devtools_target_impl.cc b/chrome/browser/devtools/devtools_target_impl.cc
index b03a737..be73926 100644
--- a/chrome/browser/devtools/devtools_target_impl.cc
+++ b/chrome/browser/devtools/devtools_target_impl.cc
@@ -272,7 +272,7 @@
 
 // static
 void DevToolsTargetImpl::EnumerateAllTargets(Callback callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   std::set<WebContents*> tab_web_contents;
   for (TabContentsIterator it; !it.done(); it.Next())
diff --git a/chrome/browser/devtools/devtools_targets_ui.cc b/chrome/browser/devtools/devtools_targets_ui.cc
index 09066a3a1..f5aa559c3 100644
--- a/chrome/browser/devtools/devtools_targets_ui.cc
+++ b/chrome/browser/devtools/devtools_targets_ui.cc
@@ -132,14 +132,14 @@
   }
 
   void NotifyOnIOThread() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
         base::Bind(&WorkerObserver::NotifyOnUIThread, this));
   }
 
   void NotifyOnUIThread() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     if (callback_.is_null())
       return;
     callback_.Run();
@@ -269,9 +269,7 @@
   AdbTargetsUIHandler(const Callback& callback, Profile* profile);
   ~AdbTargetsUIHandler() override;
 
-  void Open(const std::string& browser_id,
-            const std::string& url,
-            const DevToolsTargetsUIHandler::TargetCallback&) override;
+  void Open(const std::string& browser_id, const std::string& url) override;
 
   scoped_refptr<content::DevToolsAgentHost> GetBrowserAgentHost(
       const std::string& browser_id) override;
@@ -305,17 +303,11 @@
   android_bridge_->RemoveDeviceListListener(this);
 }
 
-static void CallOnTarget(
-    const DevToolsTargetsUIHandler::TargetCallback& callback,
-    DevToolsAndroidBridge* bridge,
-    scoped_refptr<DevToolsAndroidBridge::RemotePage> page) {
-  callback.Run(bridge && page.get() ? bridge->CreatePageTarget(page) : nullptr);
+static void NoOp(scoped_refptr<DevToolsAndroidBridge::RemotePage> page) {
 }
 
-void AdbTargetsUIHandler::Open(
-    const std::string& browser_id,
-    const std::string& url,
-    const DevToolsTargetsUIHandler::TargetCallback& callback) {
+void AdbTargetsUIHandler::Open(const std::string& browser_id,
+                               const std::string& url) {
   RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
   if (it == remote_browsers_.end())
     return;
@@ -323,7 +315,7 @@
   android_bridge_->OpenRemotePage(
       it->second,
       url,
-      base::Bind(&CallOnTarget, callback, android_bridge_));
+      base::Bind(&NoOp));
 }
 
 scoped_refptr<content::DevToolsAgentHost>
@@ -440,9 +432,7 @@
 }
 
 void DevToolsTargetsUIHandler::Open(const std::string& browser_id,
-                                    const std::string& url,
-                                    const TargetCallback& callback) {
-  callback.Run(NULL);
+                                    const std::string& url) {
 }
 
 scoped_refptr<content::DevToolsAgentHost>
diff --git a/chrome/browser/devtools/devtools_targets_ui.h b/chrome/browser/devtools/devtools_targets_ui.h
index fbe96ed..4174bd81 100644
--- a/chrome/browser/devtools/devtools_targets_ui.h
+++ b/chrome/browser/devtools/devtools_targets_ui.h
@@ -24,7 +24,6 @@
  public:
   typedef base::Callback<void(const std::string&,
                               const base::ListValue&)> Callback;
-  typedef base::Callback<void(DevToolsTargetImpl*)> TargetCallback;
 
   DevToolsTargetsUIHandler(const std::string& source_id,
                            const Callback& callback);
@@ -40,8 +39,7 @@
 
   DevToolsTargetImpl* GetTarget(const std::string& target_id);
 
-  virtual void Open(const std::string& browser_id, const std::string& url,
-                    const TargetCallback& callback);
+  virtual void Open(const std::string& browser_id, const std::string& url);
 
   virtual scoped_refptr<content::DevToolsAgentHost> GetBrowserAgentHost(
       const std::string& browser_id);
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index 68ddd01..03c3c97 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -409,7 +409,6 @@
       android_bridge_(DevToolsAndroidBridge::Factory::GetForProfile(profile_)),
       web_contents_(web_contents),
       delegate_(new DefaultBindingsDelegate(web_contents_)),
-      device_count_updates_enabled_(false),
       devices_updates_enabled_(false),
       frontend_loaded_(false),
       weak_factory_(this) {
@@ -453,7 +452,6 @@
     jobs_it->second->Stop();
   }
   indexing_jobs_.clear();
-  SetDeviceCountUpdatesEnabled(false);
   SetDevicesUpdatesEnabled(false);
 
   // Remove self from global list.
@@ -655,7 +653,7 @@
 
 void DevToolsUIBindings::IndexPath(int index_request_id,
                                    const std::string& file_system_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
   if (!file_helper_->IsFileSystemAdded(file_system_path)) {
     IndexingDone(index_request_id, file_system_path);
@@ -682,7 +680,7 @@
 }
 
 void DevToolsUIBindings::StopIndexing(int index_request_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   IndexingJobsMap::iterator it = indexing_jobs_.find(index_request_id);
   if (it == indexing_jobs_.end())
     return;
@@ -693,7 +691,7 @@
 void DevToolsUIBindings::SearchInPath(int search_request_id,
                                       const std::string& file_system_path,
                                       const std::string& query) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
   if (!file_helper_->IsFileSystemAdded(file_system_path)) {
     SearchCompleted(search_request_id,
@@ -725,35 +723,6 @@
   ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_RESET);
 }
 
-static void InspectTarget(Profile* profile, DevToolsTargetImpl* target) {
-  if (target)
-    target->Inspect(profile);
-}
-
-void DevToolsUIBindings::OpenUrlOnRemoteDeviceAndInspect(
-    const std::string& browser_id,
-    const std::string& url) {
-  if (remote_targets_handler_) {
-    remote_targets_handler_->Open(browser_id, url,
-        base::Bind(&InspectTarget, profile_));
-  }
-}
-
-void DevToolsUIBindings::SetDeviceCountUpdatesEnabled(bool enabled) {
-  if (device_count_updates_enabled_ == enabled)
-    return;
-  DevToolsAndroidBridge* adb_bridge =
-      DevToolsAndroidBridge::Factory::GetForProfile(profile_);
-  if (!adb_bridge)
-    return;
-
-  device_count_updates_enabled_ = enabled;
-  if (enabled)
-    adb_bridge->AddDeviceCountListener(this);
-  else
-    adb_bridge->RemoveDeviceCountListener(this);
-}
-
 void DevToolsUIBindings::SetDevicesUpdatesEnabled(bool enabled) {
   if (devices_updates_enabled_ == enabled)
     return;
@@ -880,7 +849,7 @@
     int request_id,
     const std::string& file_system_path,
     int total_work) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::FundamentalValue request_id_value(request_id);
   base::StringValue file_system_path_value(file_system_path);
   base::FundamentalValue total_work_value(total_work);
@@ -892,7 +861,7 @@
 void DevToolsUIBindings::IndexingWorked(int request_id,
                                         const std::string& file_system_path,
                                         int worked) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::FundamentalValue request_id_value(request_id);
   base::StringValue file_system_path_value(file_system_path);
   base::FundamentalValue worked_value(worked);
@@ -903,7 +872,7 @@
 void DevToolsUIBindings::IndexingDone(int request_id,
                                       const std::string& file_system_path) {
   indexing_jobs_.erase(request_id);
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::FundamentalValue request_id_value(request_id);
   base::StringValue file_system_path_value(file_system_path);
   CallClientFunction("DevToolsAPI.indexingDone", &request_id_value,
@@ -914,7 +883,7 @@
     int request_id,
     const std::string& file_system_path,
     const std::vector<std::string>& file_paths) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::ListValue file_paths_value;
   for (std::vector<std::string>::const_iterator it(file_paths.begin());
        it != file_paths.end(); ++it) {
diff --git a/chrome/browser/devtools/devtools_ui_bindings.h b/chrome/browser/devtools/devtools_ui_bindings.h
index bd84f68..dd238c7 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.h
+++ b/chrome/browser/devtools/devtools_ui_bindings.h
@@ -130,9 +130,6 @@
   void ZoomIn() override;
   void ZoomOut() override;
   void ResetZoom() override;
-  void OpenUrlOnRemoteDeviceAndInspect(const std::string& browser_id,
-                                       const std::string& url) override;
-  void SetDeviceCountUpdatesEnabled(bool enabled) override;
   void SetDevicesUpdatesEnabled(bool enabled) override;
   void SendMessageToBrowser(const std::string& message) override;
   void RecordActionUMA(const std::string& name, int action) override;
@@ -207,7 +204,6 @@
       IndexingJobsMap;
   IndexingJobsMap indexing_jobs_;
 
-  bool device_count_updates_enabled_;
   bool devices_updates_enabled_;
   bool frontend_loaded_;
   scoped_ptr<DevToolsTargetsUIHandler> remote_targets_handler_;
diff --git a/chrome/browser/download/download_dir_policy_handler.cc b/chrome/browser/download/download_dir_policy_handler.cc
index be9ea9df..1e1bc26 100644
--- a/chrome/browser/download/download_dir_policy_handler.cc
+++ b/chrome/browser/download/download_dir_policy_handler.cc
@@ -114,3 +114,9 @@
 #endif
   }
 }
+
+void DownloadDirPolicyHandler::ApplyPolicySettings(
+    const policy::PolicyMap& /* policies */,
+    PrefValueMap* /* prefs */) {
+  NOTREACHED();
+}
diff --git a/chrome/browser/download/download_dir_policy_handler.h b/chrome/browser/download/download_dir_policy_handler.h
index 214d3a3..2fbf0d7 100644
--- a/chrome/browser/download/download_dir_policy_handler.h
+++ b/chrome/browser/download/download_dir_policy_handler.h
@@ -30,6 +30,10 @@
       const policy::PolicyHandlerParameters& parameters,
       PrefValueMap* prefs) override;
 
+ protected:
+  void ApplyPolicySettings(const policy::PolicyMap& policies,
+                           PrefValueMap* prefs) override;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(DownloadDirPolicyHandler);
 };
diff --git a/chrome/browser/errorpage_browsertest.cc b/chrome/browser/errorpage_browsertest.cc
index 4ec198a..a0ff0817 100644
--- a/chrome/browser/errorpage_browsertest.cc
+++ b/chrome/browser/errorpage_browsertest.cc
@@ -53,6 +53,18 @@
 #include "net/url_request/url_request_test_util.h"
 #include "ui/base/l10n/l10n_util.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/chrome_browser_main_chromeos.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/policy/stub_enterprise_install_attributes.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_types.h"
+#include "content/public/test/browser_test_utils.h"
+#include "ui/base/l10n/l10n_util.h"
+#endif
+
 using content::BrowserThread;
 using content::NavigationController;
 using net::URLRequestFailedJob;
@@ -881,6 +893,32 @@
   EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
 }
 
+// Check that the easter egg is present and initialised and is not disabled.
+IN_PROC_BROWSER_TEST_F(ErrorPageTest, CheckEasterEggIsNotDisabled) {
+  ui_test_utils::NavigateToURL(browser(),
+      URLRequestFailedJob::GetMockHttpUrl(net::ERR_INTERNET_DISCONNECTED));
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Check for no disabled message container.
+  std::string command = base::StringPrintf(
+      "var hasDisableContainer = document.querySelectorAll('.snackbar').length;"
+      "domAutomationController.send(hasDisableContainer);");
+  int result;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
+               web_contents, command, &result));
+  EXPECT_EQ(0, result);
+
+  // Presence of the canvas container.
+  command = base::StringPrintf(
+    "var runnerCanvas = document.querySelectorAll('.runner-canvas').length;"
+    "domAutomationController.send(runnerCanvas);");
+  EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
+               web_contents, command, &result));
+  EXPECT_EQ(1, result);
+}
+
 class ErrorPageAutoReloadTest : public InProcessBrowserTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -1079,6 +1117,56 @@
   EXPECT_FALSE(IsDisplayingText(browser(), GetShowSavedButtonLabel()));
 }
 
+#if defined(OS_CHROMEOS)
+class ErrorPageOfflineTest : public ErrorPageTest {
+ protected:
+  // Mock policy provider for both user and device policies.
+  policy::MockConfigurationPolicyProvider policy_provider_;
+
+  void SetUpInProcessBrowserTestFixture() override {
+    // Set up fake install attributes.
+    scoped_ptr<policy::StubEnterpriseInstallAttributes> attributes(
+        new policy::StubEnterpriseInstallAttributes());
+    attributes->SetDomain("example.com");
+    attributes->SetRegistrationUser("user@example.com");
+    policy::BrowserPolicyConnectorChromeOS::SetInstallAttributesForTesting(
+        attributes.release());
+
+    // Sets up a mock policy provider for user and device policies.
+    EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
+        .WillRepeatedly(testing::Return(true));
+    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
+        &policy_provider_);
+
+    ErrorPageTest::SetUpInProcessBrowserTestFixture();
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(ErrorPageOfflineTest, CheckEasterEggIsDisabled) {
+  // Check for enterprise enrollment.
+  policy::BrowserPolicyConnectorChromeOS* connector =
+      g_browser_process->platform_part()->browser_policy_connector_chromeos();
+  EXPECT_TRUE(connector->IsEnterpriseManaged());
+
+  ui_test_utils::NavigateToURL(browser(),
+      URLRequestFailedJob::GetMockHttpUrl(net::ERR_INTERNET_DISCONNECTED));
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  std::string command = base::StringPrintf(
+      "var hasText = document.querySelector('.snackbar').innerText;"
+      "domAutomationController.send(hasText);");
+  std::string result = "";
+  EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+               web_contents, command, &result));
+
+  std::string disabled_text =
+      l10n_util::GetStringUTF8(IDS_ERRORPAGE_FUN_DISABLED);
+  EXPECT_EQ(disabled_text, result);
+}
+#endif
+
 // A test fixture that simulates failing requests for an IDN domain name.
 class ErrorPageForIDNTest : public InProcessBrowserTest {
  public:
diff --git a/chrome/browser/extensions/api/hotword_private/hotword_private_api.cc b/chrome/browser/extensions/api/hotword_private/hotword_private_api.cc
index 04c16a6b..d38013a 100644
--- a/chrome/browser/extensions/api/hotword_private/hotword_private_api.cc
+++ b/chrome/browser/extensions/api/hotword_private/hotword_private_api.cc
@@ -226,8 +226,7 @@
       hotword_service->NotifyHotwordTriggered();
     } else if (hotword_service->client()) {
       hotword_service->client()->OnHotwordRecognized(preamble);
-    } else if (HotwordService::IsExperimentalHotwordingEnabled() &&
-               hotword_service->IsAlwaysOnEnabled()) {
+    } else if (hotword_service->IsAlwaysOnEnabled()) {
       Browser* browser = GetCurrentBrowser();
       // If a Browser does not exist, fall back to the universally available,
       // but not recommended, way.
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
index 7f4c8360..574b812 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
@@ -71,6 +71,12 @@
     StringResult(success_callback, failure_callback);
   }
 
+  void ForgetNetwork(const std::string& guid,
+                     const VoidCallback& success_callback,
+                     const FailureCallback& failure_callback) override {
+    VoidResult(success_callback, failure_callback);
+  }
+
   void GetNetworks(const std::string& network_type,
                    bool configured_only,
                    bool visible_only,
@@ -338,6 +344,10 @@
   EXPECT_TRUE(RunNetworkingSubtest("createNetwork")) << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(NetworkingPrivateApiTest, ForgetNetwork) {
+  EXPECT_TRUE(RunNetworkingSubtest("forgetNetwork")) << message_;
+}
+
 IN_PROC_BROWSER_TEST_F(NetworkingPrivateApiTest, GetNetworks) {
   EXPECT_TRUE(RunNetworkingSubtest("getNetworks")) << message_;
 }
@@ -431,6 +441,10 @@
   EXPECT_FALSE(RunNetworkingSubtest("createNetwork")) << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(NetworkingPrivateApiTestFail, ForgetNetwork) {
+  EXPECT_FALSE(RunNetworkingSubtest("forgetNetwork")) << message_;
+}
+
 IN_PROC_BROWSER_TEST_F(NetworkingPrivateApiTestFail, GetNetworks) {
   EXPECT_FALSE(RunNetworkingSubtest("getNetworks")) << message_;
 }
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
index 021110e..7d48ccb7 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
@@ -470,6 +470,10 @@
   EXPECT_TRUE(RunNetworkingSubtest("createNetwork")) << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(NetworkingPrivateChromeOSApiTest, ForgetNetwork) {
+  EXPECT_TRUE(RunNetworkingSubtest("forgetNetwork")) << message_;
+}
+
 // TODO(stevenjb): Find a better way to set this up on Chrome OS.
 IN_PROC_BROWSER_TEST_F(NetworkingPrivateChromeOSApiTest, GetManagedProperties) {
   const std::string uidata_blob =
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
index 15f7a9f1..11761873 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
@@ -24,9 +24,13 @@
 namespace {
 
 const char kErrorAlgorithmNotSupported[] = "Algorithm not supported.";
+const char kErrorAlgorithmNotPermittedByCertificate[] =
+    "The requested Algorithm is not permitted by the certificate.";
 const char kErrorInvalidX509Cert[] =
     "Certificate is not a valid X.509 certificate.";
 
+const char kWebCryptoRSASSA_PKCS1_v1_5[] = "RSASSA-PKCS1-v1_5";
+
 struct PublicKeyInfo {
   // The X.509 Subject Public Key Info of the key in DER encoding.
   std::string public_key_spki_der;
@@ -46,7 +50,7 @@
 void BuildWebCryptoRSAAlgorithmDictionary(const PublicKeyInfo& key_info,
                                           base::DictionaryValue* algorithm) {
   CHECK_EQ(net::X509Certificate::kPublicKeyTypeRSA, key_info.key_type);
-  algorithm->SetStringWithoutPathExpansion("name", "RSASSA-PKCS1-v1_5");
+  algorithm->SetStringWithoutPathExpansion("name", kWebCryptoRSASSA_PKCS1_v1_5);
   algorithm->SetIntegerWithoutPathExpansion("modulusLength",
                                             key_info.key_size_bits);
 
@@ -122,6 +126,13 @@
     return RespondNow(Error(kErrorAlgorithmNotSupported));
   }
 
+  // Currently, the only supported combination is:
+  //   A certificate declaring rsaEncryption in the SubjectPublicKeyInfo used
+  //   with the RSASSA-PKCS1-v1.5 algorithm.
+  if (params->algorithm_name != kWebCryptoRSASSA_PKCS1_v1_5) {
+    return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate));
+  }
+
   api_pki::GetPublicKey::Results::Algorithm algorithm;
   BuildWebCryptoRSAAlgorithmDictionary(key_info,
                                        &algorithm.additional_properties);
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_api.cc b/chrome/browser/extensions/api/settings_private/settings_private_api.cc
new file mode 100644
index 0000000..e585fca
--- /dev/null
+++ b/chrome/browser/extensions/api/settings_private/settings_private_api.cc
@@ -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.
+
+#include "chrome/browser/extensions/api/settings_private/settings_private_api.h"
+
+#include "base/values.h"
+#include "chrome/common/extensions/api/settings_private.h"
+#include "extensions/browser/extension_function_registry.h"
+
+namespace extensions {
+
+////////////////////////////////////////////////////////////////////////////////
+// SettingsPrivateSetBooleanPrefFunction
+
+SettingsPrivateSetBooleanPrefFunction::
+    ~SettingsPrivateSetBooleanPrefFunction() {
+}
+
+ExtensionFunction::ResponseAction SettingsPrivateSetBooleanPrefFunction::Run() {
+  scoped_ptr<api::settings_private::SetBooleanPref::Params> parameters =
+      api::settings_private::SetBooleanPref::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(parameters.get());
+  VLOG(1) << "chrome.settingsPrivate.setBooleanPref(" << parameters->name
+      << ", " << (!!parameters->value ? "true" : "false") << ")";
+
+  // TODO(orenb): Implement with a real check and not just true.
+  return RespondNow(OneArgument(new base::FundamentalValue(true)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SettingsPrivateSetNumericPrefFunction
+
+SettingsPrivateSetNumericPrefFunction::
+    ~SettingsPrivateSetNumericPrefFunction() {
+}
+
+ExtensionFunction::ResponseAction SettingsPrivateSetNumericPrefFunction::Run() {
+  scoped_ptr<api::settings_private::SetNumericPref::Params> parameters =
+      api::settings_private::SetNumericPref::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(parameters.get());
+  VLOG(1) << "chrome.settingsPrivate.setNumericPref(" << parameters->name
+      << ", " << parameters->value << ")";
+
+  // TODO(orenb): Implement with a real check and not just true.
+  return RespondNow(OneArgument(new base::FundamentalValue(true)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SettingsPrivateSetStringPrefFunction
+
+SettingsPrivateSetStringPrefFunction::
+    ~SettingsPrivateSetStringPrefFunction() {
+}
+
+ExtensionFunction::ResponseAction SettingsPrivateSetStringPrefFunction::Run() {
+  scoped_ptr<api::settings_private::SetStringPref::Params> parameters =
+      api::settings_private::SetStringPref::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(parameters.get());
+  VLOG(1) << "chrome.settingsPrivate.setStringPref(" << parameters->name
+      << ", " << parameters->value << ")";
+
+  // TODO(orenb): Implement with a real check and not just true.
+  return RespondNow(OneArgument(new base::FundamentalValue(true)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SettingsPrivateSetURLPrefFunction
+
+SettingsPrivateSetURLPrefFunction::
+    ~SettingsPrivateSetURLPrefFunction() {
+}
+
+ExtensionFunction::ResponseAction SettingsPrivateSetURLPrefFunction::Run() {
+  scoped_ptr<api::settings_private::SetURLPref::Params> parameters =
+      api::settings_private::SetURLPref::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(parameters.get());
+  VLOG(1) << "chrome.settingsPrivate.setURLPref(" << parameters->name
+      << ", " << parameters->value << ")";
+
+  // TODO(orenb): Implement with a real check and not just true.
+  return RespondNow(OneArgument(new base::FundamentalValue(true)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SettingsPrivateGetAllPrefsFunction
+
+SettingsPrivateGetAllPrefsFunction::~SettingsPrivateGetAllPrefsFunction() {
+}
+
+ExtensionFunction::ResponseAction SettingsPrivateGetAllPrefsFunction::Run() {
+  // TODO(orenb): Implement with real prefs.
+  return RespondNow(OneArgument(new base::FundamentalValue(true)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SettingsPrivateGetPrefFunction
+
+SettingsPrivateGetPrefFunction::~SettingsPrivateGetPrefFunction() {
+}
+
+ExtensionFunction::ResponseAction SettingsPrivateGetPrefFunction::Run() {
+  scoped_ptr<api::settings_private::GetPref::Params> parameters =
+      api::settings_private::GetPref::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(parameters.get());
+  VLOG(1) << "chrome.settingsPrivate.getPref(" << parameters->name << ")";
+
+  // TODO(orenb): Implement with real pref.
+  api::settings_private::PrefObject* prefObject =
+      new api::settings_private::PrefObject();
+
+  prefObject->type = api::settings_private::PrefType::PREF_TYPE_BOOLEAN;
+  prefObject->value.reset(new base::FundamentalValue(true));
+  prefObject->source = api::settings_private::PrefSource::PREF_SOURCE_USER;
+
+  return RespondNow(OneArgument(prefObject->ToValue().release()));
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_api.h b/chrome/browser/extensions/api/settings_private/settings_private_api.h
new file mode 100644
index 0000000..1cf4b41
--- /dev/null
+++ b/chrome/browser/extensions/api/settings_private/settings_private_api.h
@@ -0,0 +1,115 @@
+// 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_EXTENSIONS_API_SETTINGS_PRIVATE_SETTINGS_PRIVATE_API_H_
+#define CHROME_BROWSER_EXTENSIONS_API_SETTINGS_PRIVATE_SETTINGS_PRIVATE_API_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "extensions/browser/extension_function.h"
+
+namespace extensions {
+
+// Implements the chrome.settingsPrivate.setBooleanPref method.
+class SettingsPrivateSetBooleanPrefFunction : public UIThreadExtensionFunction {
+ public:
+  SettingsPrivateSetBooleanPrefFunction() {}
+  DECLARE_EXTENSION_FUNCTION("settingsPrivate.setBooleanPref",
+                             SETTINGSPRIVATE_SETBOOLEANPREF);
+
+ protected:
+  ~SettingsPrivateSetBooleanPrefFunction() override;
+
+  // ExtensionFunction overrides.
+  ResponseAction Run() override;
+
+  DISALLOW_COPY_AND_ASSIGN(SettingsPrivateSetBooleanPrefFunction);
+};
+
+// Implements the chrome.settingsPrivate.setNumericPref method.
+class SettingsPrivateSetNumericPrefFunction : public UIThreadExtensionFunction {
+ public:
+  SettingsPrivateSetNumericPrefFunction() {}
+  DECLARE_EXTENSION_FUNCTION("settingsPrivate.setNumericPref",
+                             SETTINGSPRIVATE_SETNUMERICPREF);
+
+ protected:
+  ~SettingsPrivateSetNumericPrefFunction() override;
+
+  // ExtensionFunction overrides.
+  ResponseAction Run() override;
+
+  DISALLOW_COPY_AND_ASSIGN(SettingsPrivateSetNumericPrefFunction);
+};
+
+// Implements the chrome.settingsPrivate.setStringPref method.
+class SettingsPrivateSetStringPrefFunction : public UIThreadExtensionFunction {
+ public:
+  SettingsPrivateSetStringPrefFunction() {}
+  DECLARE_EXTENSION_FUNCTION("settingsPrivate.setStringPref",
+                             SETTINGSPRIVATE_SETSTRINGPREF);
+
+ protected:
+  ~SettingsPrivateSetStringPrefFunction() override;
+
+  // ExtensionFunction overrides.
+  ResponseAction Run() override;
+
+  DISALLOW_COPY_AND_ASSIGN(SettingsPrivateSetStringPrefFunction);
+};
+
+// Implements the chrome.settingsPrivate.setURLPref method.
+class SettingsPrivateSetURLPrefFunction : public UIThreadExtensionFunction {
+ public:
+  SettingsPrivateSetURLPrefFunction() {}
+  DECLARE_EXTENSION_FUNCTION("settingsPrivate.setURLPref",
+                             SETTINGSPRIVATE_SETURLPREF);
+
+ protected:
+  ~SettingsPrivateSetURLPrefFunction() override;
+
+  // ExtensionFunction overrides.
+  ResponseAction Run() override;
+
+  DISALLOW_COPY_AND_ASSIGN(SettingsPrivateSetURLPrefFunction);
+};
+
+// Implements the chrome.settingsPrivate.getAllPrefs method.
+class SettingsPrivateGetAllPrefsFunction : public UIThreadExtensionFunction {
+ public:
+  SettingsPrivateGetAllPrefsFunction() {}
+  DECLARE_EXTENSION_FUNCTION("settingsPrivate.getAllPrefs",
+                             SETTINGSPRIVATE_GETALLPREFS);
+
+ protected:
+  ~SettingsPrivateGetAllPrefsFunction() override;
+
+  // AsyncExtensionFunction overrides.
+  ResponseAction Run() override;
+
+  DISALLOW_COPY_AND_ASSIGN(SettingsPrivateGetAllPrefsFunction);
+};
+
+// Implements the chrome.settingsPrivate.getPref method.
+class SettingsPrivateGetPrefFunction : public UIThreadExtensionFunction {
+ public:
+  SettingsPrivateGetPrefFunction() {}
+  DECLARE_EXTENSION_FUNCTION("settingsPrivate.getPref",
+                             SETTINGSPRIVATE_GETPREF);
+
+ protected:
+  ~SettingsPrivateGetPrefFunction() override;
+
+  // AsyncExtensionFunction overrides.
+  ResponseAction Run() override;
+
+  DISALLOW_COPY_AND_ASSIGN(SettingsPrivateGetPrefFunction);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_SETTINGS_PRIVATE_SETTINGS_PRIVATE_API_H_
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
index 902011de..86171d5 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
@@ -262,9 +262,6 @@
     }
   }
 
-  std::string icon_data = params_->details.icon_data ?
-      *params_->details.icon_data : std::string();
-
   ExtensionFunction::ResponseValue response = RunExtraForResponse();
   if (response)
     return RespondNow(response.Pass());
@@ -274,7 +271,7 @@
     context_getter = browser_context()->GetRequestContext();
 
   scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
-      this, params_->details.id, params_->details.manifest, icon_data, icon_url,
+      this, params_->details.id, params_->details.manifest, icon_url,
       context_getter);
 
   // The helper will call us back via OnWebstoreParseSuccess or
diff --git a/chrome/browser/extensions/bundle_installer.cc b/chrome/browser/extensions/bundle_installer.cc
index b29fb53..0d2677f 100644
--- a/chrome/browser/extensions/bundle_installer.cc
+++ b/chrome/browser/extensions/bundle_installer.cc
@@ -220,7 +220,7 @@
 
   for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) {
     scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
-        this, i->first, i->second.manifest, std::string(), GURL(), NULL);
+        this, i->first, i->second.manifest, GURL(), nullptr);
     helper->Start();
   }
 }
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 1ebdc29..2362e12 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -334,8 +334,7 @@
 }
 
 void ComponentLoader::AddHotwordAudioVerificationApp() {
-  if (HotwordService::IsExperimentalHotwordingEnabled() &&
-      HotwordServiceFactory::IsAlwaysOnAvailable()) {
+  if (HotwordServiceFactory::IsAlwaysOnAvailable()) {
     Add(IDR_HOTWORD_AUDIO_VERIFICATION_MANIFEST,
         base::FilePath(FILE_PATH_LITERAL("hotword_audio_verification")));
   }
@@ -343,13 +342,8 @@
 
 void ComponentLoader::AddHotwordHelperExtension() {
   if (HotwordServiceFactory::IsHotwordAllowed(browser_context_)) {
-    if (HotwordService::IsExperimentalHotwordingEnabled()) {
-      Add(IDR_HOTWORD_MANIFEST,
-          base::FilePath(FILE_PATH_LITERAL("hotword")));
-    } else {
-      Add(IDR_HOTWORD_HELPER_MANIFEST,
-          base::FilePath(FILE_PATH_LITERAL("hotword_helper")));
-    }
+    Add(IDR_HOTWORD_MANIFEST,
+        base::FilePath(FILE_PATH_LITERAL("hotword")));
   }
 }
 
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index cc83981..babd9753 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -1674,12 +1674,12 @@
 #if defined(ENABLE_SUPERVISED_USERS)
     // If a custodian-installed extension is disabled for a supervised user due
     // to a permissions increase, send a request to the custodian, since the
-    // supervised user itself can't re-enable the extension.
+    // supervised user themselves can't re-enable the extension.
     if (extensions::util::IsExtensionSupervised(extension, profile_)) {
       SupervisedUserService* supervised_user_service =
           SupervisedUserServiceFactory::GetForProfile(profile_);
       supervised_user_service->AddExtensionUpdateRequest(
-          extension->id(),
+          extension->id(), *extension->version(),
           base::Bind(ExtensionUpdateRequestSent, extension->id()));
     }
 #endif
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 7d1a0db..ba02841a 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -6642,7 +6642,7 @@
   }
 
   MOCK_METHOD2(CreateExtensionUpdateRequest,
-               void(const std::string& extension_id,
+               void(const std::string& id,
                     const SupervisedUserService::SuccessCallback& callback));
 
  private:
@@ -6737,7 +6737,8 @@
   std::string old_version = extension->VersionString();
 
   // Update to a new version with increased permissions.
-  EXPECT_CALL(*creator, CreateExtensionUpdateRequest(id, testing::_));
+  EXPECT_CALL(*creator,
+              CreateExtensionUpdateRequest(id + ":2", testing::_));
   path = base_path.AppendASCII("v2");
   PackCRXAndUpdateExtension(id, path, pem_path, DISABLED);
 
diff --git a/chrome/browser/extensions/extension_special_storage_policy.cc b/chrome/browser/extensions/extension_special_storage_policy.cc
index 06c5dc2..102d11b 100644
--- a/chrome/browser/extensions/extension_special_storage_policy.cc
+++ b/chrome/browser/extensions/extension_special_storage_policy.cc
@@ -129,12 +129,6 @@
   return false;
 }
 
-bool ExtensionSpecialStoragePolicy::IsFileHandler(
-    const std::string& extension_id) {
-  base::AutoLock locker(lock_);
-  return file_handler_extensions_.ContainsExtension(extension_id);
-}
-
 bool ExtensionSpecialStoragePolicy::HasIsolatedStorage(const GURL& origin) {
   base::AutoLock locker(lock_);
   return isolated_extensions_.Contains(origin);
diff --git a/chrome/browser/extensions/extension_special_storage_policy.h b/chrome/browser/extensions/extension_special_storage_policy.h
index 58282f0..edaa543 100644
--- a/chrome/browser/extensions/extension_special_storage_policy.h
+++ b/chrome/browser/extensions/extension_special_storage_policy.h
@@ -36,7 +36,6 @@
   bool IsStorageUnlimited(const GURL& origin) override;
   bool IsStorageSessionOnly(const GURL& origin) override;
   bool CanQueryDiskSize(const GURL& origin) override;
-  bool IsFileHandler(const std::string& extension_id) override;
   bool HasIsolatedStorage(const GURL& origin) override;
   bool HasSessionOnlyOrigins() override;
 
diff --git a/chrome/browser/extensions/extension_uninstall_dialog.cc b/chrome/browser/extensions/extension_uninstall_dialog.cc
index c65bf78..b805d03 100644
--- a/chrome/browser/extensions/extension_uninstall_dialog.cc
+++ b/chrome/browser/extensions/extension_uninstall_dialog.cc
@@ -129,20 +129,8 @@
 }
 
 bool ExtensionUninstallDialog::ShouldShowReportAbuseCheckbox() const {
-  static const char kExperimentName[] = "ExtensionUninstall.ReportAbuse";
-  static const char kDefaultGroupName[] = "Default";
-  static const char kShowCheckboxGroup[] = "ShowCheckbox";
-  // TODO(devlin): Turn on this field trial. See crbug.com/441377.
-  scoped_refptr<base::FieldTrial> trial(
-      base::FieldTrialList::FactoryGetFieldTrial(
-          kExperimentName,
-          100,  // Total probability.
-          kDefaultGroupName,
-          2015, 7, 31,  // End date.
-          base::FieldTrial::ONE_TIME_RANDOMIZED,
-          nullptr));
-  int experiment_group = trial->AppendGroup(kShowCheckboxGroup, 0);
-  return base::FieldTrialList::FindValue(kExperimentName) == experiment_group;
+  return base::FieldTrialList::FindFullName("ExtensionUninstall.ReportAbuse") ==
+      "ShowCheckbox";
 }
 
 void ExtensionUninstallDialog::HandleReportAbuse() {
diff --git a/chrome/browser/extensions/external_component_loader.cc b/chrome/browser/extensions/external_component_loader.cc
index e242332..2734e13f4 100644
--- a/chrome/browser/extensions/external_component_loader.cc
+++ b/chrome/browser/extensions/external_component_loader.cc
@@ -57,10 +57,7 @@
                     extension_urls::GetWebstoreUpdateUrl().spec());
 
   if (HotwordServiceFactory::IsHotwordAllowed(profile_)) {
-    std::string hotword_id = extension_misc::kHotwordExtensionId;
-    if (HotwordService::IsExperimentalHotwordingEnabled()) {
-      hotword_id = extension_misc::kHotwordSharedModuleId;
-    }
+    std::string hotword_id = extension_misc::kHotwordSharedModuleId;
     prefs_->SetString(hotword_id + ".external_update_url",
                       extension_urls::GetWebstoreUpdateUrl().spec());
   }
diff --git a/chrome/browser/extensions/mock_extension_special_storage_policy.cc b/chrome/browser/extensions/mock_extension_special_storage_policy.cc
index edea80d3..d4d609b 100644
--- a/chrome/browser/extensions/mock_extension_special_storage_policy.cc
+++ b/chrome/browser/extensions/mock_extension_special_storage_policy.cc
@@ -24,11 +24,6 @@
   return false;
 }
 
-bool MockExtensionSpecialStoragePolicy::IsFileHandler(
-    const std::string& extension_id) {
-  return false;
-}
-
 bool MockExtensionSpecialStoragePolicy::HasSessionOnlyOrigins() {
   return false;
 }
diff --git a/chrome/browser/extensions/mock_extension_special_storage_policy.h b/chrome/browser/extensions/mock_extension_special_storage_policy.h
index a295862..a485284 100644
--- a/chrome/browser/extensions/mock_extension_special_storage_policy.h
+++ b/chrome/browser/extensions/mock_extension_special_storage_policy.h
@@ -23,7 +23,6 @@
   bool IsStorageUnlimited(const GURL& origin) override;
   bool IsStorageSessionOnly(const GURL& origin) override;
   bool CanQueryDiskSize(const GURL& origin) override;
-  bool IsFileHandler(const std::string& extension_id) override;
   bool HasSessionOnlyOrigins() override;
 
   void AddProtected(const GURL& origin) {
diff --git a/chrome/browser/extensions/webstore_install_helper.cc b/chrome/browser/extensions/webstore_install_helper.cc
index 47926c6..dff9f35f 100644
--- a/chrome/browser/extensions/webstore_install_helper.cc
+++ b/chrome/browser/extensions/webstore_install_helper.cc
@@ -32,26 +32,27 @@
     Delegate* delegate,
     const std::string& id,
     const std::string& manifest,
-    const std::string& icon_data,
     const GURL& icon_url,
     net::URLRequestContextGetter* context_getter)
-    : delegate_(delegate),
+    : ImageRequest(
+          content::BrowserThread::GetMessageLoopProxyForThread(
+              content::BrowserThread::IO)),
+      delegate_(delegate),
       id_(id),
       manifest_(manifest),
-      icon_base64_data_(icon_data),
       icon_url_(icon_url),
       context_getter_(context_getter),
       icon_decode_complete_(false),
       manifest_parse_complete_(false),
-      parse_error_(Delegate::UNKNOWN_ERROR) {}
+      parse_error_(Delegate::UNKNOWN_ERROR) {
+}
 
 WebstoreInstallHelper::~WebstoreInstallHelper() {}
 
 void WebstoreInstallHelper::Start() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  CHECK(icon_base64_data_.empty() || icon_url_.is_empty());
 
-  if (icon_base64_data_.empty() && icon_url_.is_empty())
+  if (icon_url_.is_empty())
     icon_decode_complete_ = true;
 
   BrowserThread::PostTask(
@@ -78,10 +79,6 @@
       this, base::MessageLoopProxy::current().get())->AsWeakPtr();
   utility_host_->StartBatchMode();
 
-  if (!icon_base64_data_.empty())
-    utility_host_->Send(
-        new ChromeUtilityMsg_DecodeImageBase64(icon_base64_data_));
-
   utility_host_->Send(new ChromeUtilityMsg_ParseJSON(manifest_));
 }
 
@@ -94,38 +91,20 @@
   if (!source->GetStatus().is_success() ||
       response_code / 100 == 4 || response_code / 100 == 5) {
     BrowserThread::PostTask(
-        BrowserThread::IO,
-        FROM_HERE,
+        BrowserThread::IO, FROM_HERE,
         base::Bind(&WebstoreInstallHelper::OnDecodeImageFailed, this));
   } else {
     std::string response_data;
     source->GetResponseAsString(&response_data);
-    fetched_icon_data_.insert(fetched_icon_data_.begin(),
-                              response_data.begin(),
-                              response_data.end());
-    BrowserThread::PostTask(
-        BrowserThread::IO,
-        FROM_HERE,
-        base::Bind(&WebstoreInstallHelper::StartFetchedImageDecode, this));
+
+    ImageDecoder::Start(this, response_data);
   }
   url_fetcher_.reset();
 }
 
-void WebstoreInstallHelper::StartFetchedImageDecode() {
-  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  CHECK(utility_host_.get());
-  utility_host_->Send(new ChromeUtilityMsg_DecodeImage(fetched_icon_data_,
-                                                       false));
-}
-
-
 bool WebstoreInstallHelper::OnMessageReceived(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(WebstoreInstallHelper, message)
-    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Succeeded,
-                        OnDecodeImageSucceeded)
-    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Failed,
-                        OnDecodeImageFailed)
     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Succeeded,
                         OnJSONParseSucceeded)
     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Failed,
@@ -135,9 +114,7 @@
   return handled;
 }
 
-
-void WebstoreInstallHelper::OnDecodeImageSucceeded(
-    const SkBitmap& decoded_image) {
+void WebstoreInstallHelper::OnImageDecoded(const SkBitmap& decoded_image) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   icon_ = decoded_image;
   icon_decode_complete_ = true;
diff --git a/chrome/browser/extensions/webstore_install_helper.h b/chrome/browser/extensions/webstore_install_helper.h
index 36cdd244..4a432b9 100644
--- a/chrome/browser/extensions/webstore_install_helper.h
+++ b/chrome/browser/extensions/webstore_install_helper.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/image_decoder.h"
 #include "content/public/browser/utility_process_host_client.h"
 #include "net/url_request/url_fetcher_delegate.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -36,7 +37,8 @@
 // sending work to the utility process for parsing manifests and
 // fetching/decoding icon data. Clients must implement the
 // WebstoreInstallHelper::Delegate interface to receive the parsed data.
-class WebstoreInstallHelper : public content::UtilityProcessHostClient,
+class WebstoreInstallHelper : public ImageDecoder::ImageRequest,
+                              public content::UtilityProcessHostClient,
                               public net::URLFetcherDelegate {
  public:
   class Delegate {
@@ -65,12 +67,10 @@
     virtual ~Delegate() {}
   };
 
-  // Only one of |icon_data| (based64-encoded icon data) or |icon_url| can be
-  // specified, but it is legal for both to be empty.
+  // It is legal for |icon_url| to be empty.
   WebstoreInstallHelper(Delegate* delegate,
                         const std::string& id,
                         const std::string& manifest,
-                        const std::string& icon_data,
                         const GURL& icon_url,
                         net::URLRequestContextGetter* context_getter);
   void Start();
@@ -79,7 +79,6 @@
   ~WebstoreInstallHelper() override;
 
   void StartWorkOnIOThread();
-  void StartFetchedImageDecode();
   void ReportResultsIfComplete();
   void ReportResultFromUIThread();
 
@@ -90,11 +89,13 @@
   bool OnMessageReceived(const IPC::Message& message) override;
 
   // Message handlers.
-  void OnDecodeImageSucceeded(const SkBitmap& decoded_image);
-  void OnDecodeImageFailed();
   void OnJSONParseSucceeded(const base::ListValue& wrapper);
   void OnJSONParseFailed(const std::string& error_message);
 
+  // ImageDecoder::ImageRequest implementation.
+  void OnImageDecoded(const SkBitmap& decoded_image) override;
+  void OnDecodeImageFailed() override;
+
   // The client who we'll report results back to.
   Delegate* delegate_;
 
@@ -104,13 +105,9 @@
   // The manifest to parse.
   std::string manifest_;
 
-  // Only one of these should be non-empty. If |icon_base64_data_| is non-emtpy,
-  // it's a base64-encoded string that needs to be parsed into an SkBitmap. If
-  // |icon_url_| is non-empty, it needs to be fetched and decoded into an
+  // If |icon_url_| is non-empty, it needs to be fetched and decoded into an
   // SkBitmap.
-  std::string icon_base64_data_;
   GURL icon_url_;
-  std::vector<unsigned char> fetched_icon_data_;
 
   // For fetching the icon, if needed.
   scoped_ptr<net::URLFetcher> url_fetcher_;
diff --git a/chrome/browser/extensions/webstore_standalone_installer.cc b/chrome/browser/extensions/webstore_standalone_installer.cc
index babe0f36..5b3ad15 100644
--- a/chrome/browser/extensions/webstore_standalone_installer.cc
+++ b/chrome/browser/extensions/webstore_standalone_installer.cc
@@ -349,7 +349,6 @@
       new WebstoreInstallHelper(this,
                                 id_,
                                 manifest,
-                                std::string(),  // We don't have any icon data.
                                 icon_url,
                                 profile_->GetRequestContext());
   // The helper will call us back via OnWebstoreParseSucces or
diff --git a/chrome/browser/favicon/favicon_tab_helper.cc b/chrome/browser/favicon/favicon_tab_helper.cc
index 8094003..6d9c9dc 100644
--- a/chrome/browser/favicon/favicon_tab_helper.cc
+++ b/chrome/browser/favicon/favicon_tab_helper.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/favicon/favicon_tab_helper.h"
 
-#include "base/metrics/field_trial.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/favicon/chrome_favicon_client.h"
@@ -21,6 +20,7 @@
 #include "components/favicon/core/favicon_tab_helper_observer.h"
 #include "components/favicon_base/favicon_types.h"
 #include "components/history/core/browser/history_service.h"
+#include "components/variations/variations_associated_data.h"
 #include "content/public/browser/favicon_status.h"
 #include "content/public/browser/invalidate_type.h"
 #include "content/public/browser/navigation_controller.h"
@@ -47,8 +47,7 @@
 
 // Returns whether icon NTP is enabled.
 bool IsIconNTPEnabled() {
-  return StartsWithASCII(base::FieldTrialList::FindFullName("IconNTP"),
-                         "Enabled", true);
+  return variations::GetVariationParamValue("IconNTP", "state") == "enabled";
 }
 
 }  // namespace
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_guest_delegate.cc b/chrome/browser/guest_view/web_view/chrome_web_view_guest_delegate.cc
index 89e6dae..ec4208d20 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_guest_delegate.cc
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_guest_delegate.cc
@@ -54,7 +54,7 @@
   args->Set(webview::kContextMenuItems, items.release());
   args->SetInteger(webview::kRequestId, request_id);
   web_view_guest()->DispatchEventToView(
-      new GuestViewBase::Event(webview::kEventContextMenu, args.Pass()));
+      new GuestViewBase::Event(webview::kEventContextMenuShow, args.Pass()));
   return true;
 }
 
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
index 4896760..8bf88d0 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
@@ -48,10 +48,6 @@
                         OnBlockedUnauthorizedPlugin)
     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NPAPINotSupported,
                         OnNPAPINotSupported)
-#if defined(ENABLE_PLUGIN_INSTALLATION)
-    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FindMissingPlugin,
-                        OnFindMissingPlugin)
-#endif
     IPC_MESSAGE_UNHANDLED(return false)
   IPC_END_MESSAGE_MAP()
 
@@ -112,12 +108,6 @@
 }
 
 #if defined(ENABLE_PLUGIN_INSTALLATION)
-void ChromeWebViewPermissionHelperDelegate::OnFindMissingPlugin(
-    int placeholder_id,
-    const std::string& mime_type) {
-  Send(new ChromeViewMsg_DidNotFindMissingPlugin(placeholder_id));
-}
-
 void ChromeWebViewPermissionHelperDelegate::OnRemovePluginPlaceholderHost(
     int placeholder_id) {
 }
diff --git a/chrome/browser/guest_view/web_view/context_menu_content_type_web_view.cc b/chrome/browser/guest_view/web_view/context_menu_content_type_web_view.cc
index 3d78022..c8f60cdb 100644
--- a/chrome/browser/guest_view/web_view/context_menu_content_type_web_view.cc
+++ b/chrome/browser/guest_view/web_view/context_menu_content_type_web_view.cc
@@ -6,6 +6,8 @@
 
 #include "base/command_line.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/chrome_version_info.h"
+#include "extensions/browser/guest_view/web_view/web_view_guest.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest.h"
 
@@ -33,10 +35,22 @@
       // Show contextMenus API items.
       return true;
     case ITEM_GROUP_DEVELOPER:
-      // TODO(lazyboy): Enable this for mac too when http://crbug.com/380405 is
-      // fixed.
-#if !defined(OS_MACOSX)
       {
+        if (chrome::VersionInfo::GetChannel() >=
+                chrome::VersionInfo::CHANNEL_DEV) {
+          // Hide dev tools items in guests inside WebUI if we are not running
+          // canary or tott.
+          auto web_view_guest =
+              extensions::WebViewGuest::FromWebContents(source_web_contents());
+          if (web_view_guest &&
+                  web_view_guest->owner_web_contents()->GetWebUI()) {
+              return false;
+          }
+        }
+
+        // TODO(lazyboy): Enable this for mac too when http://crbug.com/380405
+        // is fixed.
+#if !defined(OS_MACOSX)
         // Add dev tools for unpacked extensions.
         const extensions::Extension* embedder_platform_app = GetExtension();
         return !embedder_platform_app ||
@@ -44,10 +58,10 @@
                    embedder_platform_app->location()) ||
                base::CommandLine::ForCurrentProcess()->HasSwitch(
                    switches::kDebugPackedApps);
-      }
 #else
-      return ContextMenuContentType::SupportsGroup(group);
+        return ContextMenuContentType::SupportsGroup(group);
 #endif
+      }
     default:
       return ContextMenuContentType::SupportsGroup(group);
   }
diff --git a/chrome/browser/history/thumbnail_database_unittest.cc b/chrome/browser/history/thumbnail_database_unittest.cc
index 04b9a5a1..cfebd304 100644
--- a/chrome/browser/history/thumbnail_database_unittest.cc
+++ b/chrome/browser/history/thumbnail_database_unittest.cc
@@ -671,34 +671,8 @@
   ASSERT_TRUE(db.get() != NULL);
   VerifyTablesAndColumns(&db->db_);
 
-  EXPECT_TRUE(CheckPageHasIcon(db.get(),
-                               kPageUrl1,
-                               favicon_base::FAVICON,
-                               kIconUrl1,
-                               gfx::Size(),
-                               sizeof(kBlob1),
-                               kBlob1));
-  EXPECT_TRUE(CheckPageHasIcon(db.get(),
-                               kPageUrl2,
-                               favicon_base::FAVICON,
-                               kIconUrl2,
-                               gfx::Size(),
-                               sizeof(kBlob2),
-                               kBlob2));
-  EXPECT_TRUE(CheckPageHasIcon(db.get(),
-                               kPageUrl3,
-                               favicon_base::FAVICON,
-                               kIconUrl1,
-                               gfx::Size(),
-                               sizeof(kBlob1),
-                               kBlob1));
-  EXPECT_TRUE(CheckPageHasIcon(db.get(),
-                               kPageUrl3,
-                               favicon_base::TOUCH_ICON,
-                               kIconUrl3,
-                               gfx::Size(),
-                               sizeof(kBlob2),
-                               kBlob2));
+  // Version 5 is deprecated, the data should all be gone.
+  VerifyDatabaseEmpty(&db->db_);
 }
 
 // Test loading version 6 database.
diff --git a/chrome/browser/image_decoder.cc b/chrome/browser/image_decoder.cc
index 2f9e83b..cfd887e0 100644
--- a/chrome/browser/image_decoder.cc
+++ b/chrome/browser/image_decoder.cc
@@ -13,36 +13,132 @@
 using content::BrowserThread;
 using content::UtilityProcessHost;
 
-ImageDecoder::ImageDecoder(Delegate* delegate,
-                           const std::string& image_data,
-                           ImageCodec image_codec)
-    : delegate_(delegate),
-      image_data_(image_data.begin(), image_data.end()),
-      image_codec_(image_codec),
-      task_runner_(NULL),
-      shrink_to_fit_(false) {
+namespace {
+
+// static, Leaky to allow access from any thread.
+base::LazyInstance<ImageDecoder>::Leaky g_decoder = LAZY_INSTANCE_INITIALIZER;
+
+// How long to wait after the last request has been received before ending
+// batch mode.
+const int kBatchModeTimeoutSeconds = 5;
+
+}  // namespace
+
+ImageDecoder::ImageDecoder()
+    : image_request_id_counter_(0), last_request_(base::TimeTicks::Now()) {
+  // A single ImageDecoder instance should live for the life of the program.
+  // Explicitly add a reference so the object isn't deleted.
+  AddRef();
 }
 
-ImageDecoder::ImageDecoder(Delegate* delegate,
-                           const std::vector<char>& image_data,
-                           ImageCodec image_codec)
-    : delegate_(delegate),
-      image_data_(image_data.begin(), image_data.end()),
-      image_codec_(image_codec),
-      task_runner_(NULL),
-      shrink_to_fit_(false) {
+ImageDecoder::~ImageDecoder() {
 }
 
-ImageDecoder::~ImageDecoder() {}
+ImageDecoder::ImageRequest::ImageRequest(
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+    : task_runner_(task_runner) {
+}
 
-void ImageDecoder::Start(scoped_refptr<base::SequencedTaskRunner> task_runner) {
-  task_runner_ = task_runner;
+ImageDecoder::ImageRequest::~ImageRequest() {
+  ImageDecoder::Cancel(this);
+}
+
+// static
+void ImageDecoder::Start(ImageRequest* image_request,
+                         const std::string& image_data) {
+  StartWithOptions(image_request, image_data, DEFAULT_CODEC, false);
+}
+
+// static
+void ImageDecoder::StartWithOptions(ImageRequest* image_request,
+                                    const std::string& image_data,
+                                    ImageCodec image_codec,
+                                    bool shrink_to_fit) {
+  DCHECK(image_request);
+  DCHECK(image_request->task_runner());
   BrowserThread::PostTask(
-     BrowserThread::IO, FROM_HERE,
-     base::Bind(&ImageDecoder::DecodeImageInSandbox, this, image_data_));
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(
+          &ImageDecoder::DecodeImageInSandbox,
+          g_decoder.Pointer(), image_request,
+          std::vector<unsigned char>(image_data.begin(), image_data.end()),
+          image_codec, shrink_to_fit));
 }
 
-bool ImageDecoder::OnMessageReceived(const IPC::Message& message) {
+// static
+void ImageDecoder::Cancel(ImageRequest* image_request) {
+  DCHECK(image_request);
+  g_decoder.Pointer()->CancelImpl(image_request);
+}
+
+void ImageDecoder::DecodeImageInSandbox(
+    ImageRequest* image_request,
+    const std::vector<unsigned char>& image_data,
+    ImageCodec image_codec,
+    bool shrink_to_fit) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!utility_process_host_) {
+    StartBatchMode();
+  }
+
+  last_request_ = base::TimeTicks::Now();
+  base::AutoLock lock(map_lock_);
+  image_request_id_map_.insert(
+      std::make_pair(image_request_id_counter_, image_request));
+
+  switch (image_codec) {
+    case ROBUST_JPEG_CODEC:
+      utility_process_host_->Send(new ChromeUtilityMsg_RobustJPEGDecodeImage(
+          image_data, image_request_id_counter_));
+      break;
+    case DEFAULT_CODEC:
+      utility_process_host_->Send(new ChromeUtilityMsg_DecodeImage(
+          image_data, shrink_to_fit, image_request_id_counter_));
+      break;
+  }
+
+  ++image_request_id_counter_;
+}
+
+void ImageDecoder::CancelImpl(ImageRequest* image_request) {
+  base::AutoLock lock(map_lock_);
+  for (auto it = image_request_id_map_.begin();
+       it != image_request_id_map_.end();) {
+    if (it->second == image_request) {
+      image_request_id_map_.erase(it++);
+    } else {
+      ++it;
+    }
+  }
+}
+
+void ImageDecoder::StartBatchMode() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  utility_process_host_ =
+      UtilityProcessHost::Create(this, base::MessageLoopProxy::current().get())
+          ->AsWeakPtr();
+  utility_process_host_->StartBatchMode();
+  batch_mode_timer_.Start(
+      FROM_HERE, base::TimeDelta::FromSeconds(kBatchModeTimeoutSeconds),
+      this, &ImageDecoder::StopBatchMode);
+}
+
+void ImageDecoder::StopBatchMode() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if ((base::TimeTicks::Now() - last_request_)
+      < base::TimeDelta::FromSeconds(kBatchModeTimeoutSeconds)) {
+    return;
+  }
+
+  if (utility_process_host_) {
+    utility_process_host_->EndBatchMode();
+    utility_process_host_.reset();
+  }
+  batch_mode_timer_.Stop();
+}
+
+bool ImageDecoder::OnMessageReceived(
+    const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(ImageDecoder, message)
     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Succeeded,
@@ -54,28 +150,32 @@
   return handled;
 }
 
-void ImageDecoder::OnDecodeImageSucceeded(const SkBitmap& decoded_image) {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
-  if (delegate_)
-    delegate_->OnImageDecoded(this, decoded_image);
+void ImageDecoder::OnDecodeImageSucceeded(
+    const SkBitmap& decoded_image,
+    int request_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  base::AutoLock lock(map_lock_);
+  auto it = image_request_id_map_.find(request_id);
+  if (it != image_request_id_map_.end()) {
+    ImageRequest* image_request = it->second;
+    image_request->task_runner()->PostTask(
+        FROM_HERE, base::Bind(&ImageRequest::OnImageDecoded,
+                              base::Unretained(image_request), decoded_image));
+
+    image_request_id_map_.erase(it);
+  }
 }
 
-void ImageDecoder::OnDecodeImageFailed() {
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
-  if (delegate_)
-    delegate_->OnDecodeImageFailed(this);
-}
+void ImageDecoder::OnDecodeImageFailed(int request_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  base::AutoLock lock(map_lock_);
+  auto it = image_request_id_map_.find(request_id);
+  if (it != image_request_id_map_.end()) {
+    ImageRequest* image_request = it->second;
+    image_request->task_runner()->PostTask(
+        FROM_HERE, base::Bind(&ImageRequest::OnDecodeImageFailed,
+                              base::Unretained(image_request)));
 
-void ImageDecoder::DecodeImageInSandbox(
-    const std::vector<unsigned char>& image_data) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  UtilityProcessHost* utility_process_host;
-  utility_process_host = UtilityProcessHost::Create(this, task_runner_.get());
-  if (image_codec_ == ROBUST_JPEG_CODEC) {
-    utility_process_host->Send(
-        new ChromeUtilityMsg_RobustJPEGDecodeImage(image_data));
-  } else {
-    utility_process_host->Send(
-        new ChromeUtilityMsg_DecodeImage(image_data, shrink_to_fit_));
+    image_request_id_map_.erase(it);
   }
 }
diff --git a/chrome/browser/image_decoder.h b/chrome/browser/image_decoder.h
index 2206dd9f..d4615cf 100644
--- a/chrome/browser/image_decoder.h
+++ b/chrome/browser/image_decoder.h
@@ -5,33 +5,55 @@
 #ifndef CHROME_BROWSER_IMAGE_DECODER_H_
 #define CHROME_BROWSER_IMAGE_DECODER_H_
 
+#include <map>
 #include <string>
 #include <vector>
 
 #include "base/compiler_specific.h"
+#include "base/lazy_instance.h"
 #include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
 #include "base/threading/sequenced_worker_pool.h"
+#include "base/timer/timer.h"
+#include "content/public/browser/utility_process_host.h"
 #include "content/public/browser/utility_process_host_client.h"
 
 class SkBitmap;
 
-// Decodes an image in a sandboxed process.
+// This is a helper class for decoding images safely in a utility process. To
+// use this, call ImageDecoder::Start(...) or
+// ImageDecoder::StartWithOptions(...) on any thread.
+//
+// Internally, most of the work happens on the IO thread, and then
+// the callback (ImageRequest::OnImageDecoded or
+// ImageRequest::OnDecodeImageFailed) is posted back to the |task_runner_|
+// associated with the ImageRequest.
+// The Cancel() method runs on whichever thread called it. |map_lock_| is used
+// to protect the data that is accessed from multiple threads.
 class ImageDecoder : public content::UtilityProcessHostClient {
  public:
-  class Delegate {
+  class ImageRequest {
    public:
     // Called when image is decoded.
-    // |decoder| is used to identify the image in case of decoding several
-    // images simultaneously.
-    virtual void OnImageDecoded(const ImageDecoder* decoder,
-                                const SkBitmap& decoded_image) = 0;
+    virtual void OnImageDecoded(const SkBitmap& decoded_image) = 0;
 
-    // Called when decoding image failed. Delegate can do some cleanup in
+    // Called when decoding image failed. ImageRequest can do some cleanup in
     // this handler.
-    virtual void OnDecodeImageFailed(const ImageDecoder* decoder) {}
+    virtual void OnDecodeImageFailed() {}
+
+    base::SequencedTaskRunner* task_runner() const {
+      return task_runner_.get();
+    }
 
    protected:
-    virtual ~Delegate() {}
+    explicit ImageRequest(
+        const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+    virtual ~ImageRequest();
+
+   private:
+    // The thread to post OnImageDecoded or OnDecodeImageFailed once the
+    // the image has been decoded.
+    const scoped_refptr<base::SequencedTaskRunner> task_runner_;
   };
 
   enum ImageCodec {
@@ -39,44 +61,71 @@
     ROBUST_JPEG_CODEC,  // Restrict decoding to robust jpeg codec.
   };
 
-  ImageDecoder(Delegate* delegate,
-               const std::string& image_data,
-               ImageCodec image_codec);
-
-  ImageDecoder(Delegate* delegate,
-               const std::vector<char>& image_data,
-               ImageCodec image_codec);
+  // Calls StartWithOptions with ImageCodec::DEFAULT_CODEC and
+  // shrink_to_fit = false.
+  static void Start(ImageRequest* image_request,
+                    const std::string& image_data);
 
   // Starts asynchronous image decoding. Once finished, the callback will be
-  // posted back to |task_runner|.
-  void Start(scoped_refptr<base::SequencedTaskRunner> task_runner);
+  // posted back to image_request's |task_runner_|.
+  static void StartWithOptions(ImageRequest* image_request,
+                               const std::string& image_data,
+                               ImageCodec image_codec,
+                               bool shrink_to_fit);
 
-  const std::vector<unsigned char>& get_image_data() const {
-    return image_data_;
-  }
-
-  void set_delegate(Delegate* delegate) { delegate_ = delegate; }
-  void set_shrink_to_fit(bool shrink_to_fit) { shrink_to_fit_ = shrink_to_fit; }
+  // Removes all instances of image_request from |image_request_id_map_|,
+  // ensuring callbacks are not made to the image_request after it is destroyed.
+  static void Cancel(ImageRequest* image_request);
 
  private:
+  friend struct base::DefaultLazyInstanceTraits<ImageDecoder>;
+
+  ImageDecoder();
   // It's a reference counted object, so destructor is private.
   ~ImageDecoder() override;
 
-  // Overidden from UtilityProcessHostClient:
+  // Sends a request to the sandboxed process to decode the image. Starts
+  // batch mode if necessary.
+  void DecodeImageInSandbox(ImageRequest* image_request,
+                            const std::vector<unsigned char>& image_data,
+                            ImageCodec image_codec,
+                            bool shrink_to_fit);
+
+  void CancelImpl(ImageRequest* image_request);
+
+  using RequestMap = std::map<int, ImageRequest*>;
+
+  // Starts UtilityProcessHost in batch mode and starts |batch_mode_timer_|.
+  void StartBatchMode();
+
+  // Stops batch mode if no requests have come in since
+  // kBatchModeTimeoutSeconds.
+  void StopBatchMode();
+
+  // Overidden from UtilityProcessHostClient.
   bool OnMessageReceived(const IPC::Message& message) override;
 
   // IPC message handlers.
-  void OnDecodeImageSucceeded(const SkBitmap& decoded_image);
-  void OnDecodeImageFailed();
+  void OnDecodeImageSucceeded(const SkBitmap& decoded_image, int request_id);
+  void OnDecodeImageFailed(int request_id);
 
-  // Launches sandboxed process that will decode the image.
-  void DecodeImageInSandbox(const std::vector<unsigned char>& image_data);
+  // id to use for the next Start request that comes in.
+  int image_request_id_counter_;
 
-  Delegate* delegate_;
-  std::vector<unsigned char> image_data_;
-  const ImageCodec image_codec_;
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  bool shrink_to_fit_; // if needed for IPC msg size limit
+  // Map of request id's to ImageRequests.
+  RequestMap image_request_id_map_;
+
+  // Protects |image_request_id_map_| and |image_request_id_counter_|.
+  base::Lock map_lock_;
+
+  // The UtilityProcessHost requests are sent to.
+  base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
+
+  // Calls StopBatchMode after kBatchModeTimeoutSeconds have elapsed.
+  base::RepeatingTimer<ImageDecoder> batch_mode_timer_;
+
+  // The time Start was last called.
+  base::TimeTicks last_request_;
 
   DISALLOW_COPY_AND_ASSIGN(ImageDecoder);
 };
diff --git a/chrome/browser/interstitials/security_interstitial_metrics_helper.cc b/chrome/browser/interstitials/security_interstitial_metrics_helper.cc
index ab2a143..433fcab 100644
--- a/chrome/browser/interstitials/security_interstitial_metrics_helper.cc
+++ b/chrome/browser/interstitials/security_interstitial_metrics_helper.cc
@@ -151,6 +151,9 @@
     case RELOAD:
     case OPEN_TIME_SETTINGS:
     case TOTAL_VISITS:
+    case SET_EXTENDED_REPORTING_ENABLED:
+    case SET_EXTENDED_REPORTING_DISABLED:
+    case EXTENDED_REPORTING_IS_ENABLED:
     case MAX_INTERACTION:
       break;
   }
diff --git a/chrome/browser/interstitials/security_interstitial_metrics_helper.h b/chrome/browser/interstitials/security_interstitial_metrics_helper.h
index 1808852..0224bc9 100644
--- a/chrome/browser/interstitials/security_interstitial_metrics_helper.h
+++ b/chrome/browser/interstitials/security_interstitial_metrics_helper.h
@@ -42,6 +42,9 @@
     SHOW_LEARN_MORE,
     RELOAD,
     OPEN_TIME_SETTINGS,
+    SET_EXTENDED_REPORTING_ENABLED,
+    SET_EXTENDED_REPORTING_DISABLED,
+    EXTENDED_REPORTING_IS_ENABLED,
     MAX_INTERACTION
   };
 
diff --git a/chrome/browser/interstitials/security_interstitial_page.cc b/chrome/browser/interstitials/security_interstitial_page.cc
index 8a72d874..3551d12 100644
--- a/chrome/browser/interstitials/security_interstitial_page.cc
+++ b/chrome/browser/interstitials/security_interstitial_page.cc
@@ -5,16 +5,40 @@
 #include "chrome/browser/interstitials/security_interstitial_page.h"
 
 #include "base/i18n/rtl.h"
+
+#include "base/metrics/histogram.h"
+#include "base/prefs/pref_service.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/interstitials/security_interstitial_metrics_helper.h"
+#include "chrome/browser/net/referrer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/google/core/browser/google_util.h"
 #include "content/public/browser/interstitial_page.h"
+#include "content/public/browser/page_navigator.h"
 #include "content/public/browser/web_contents.h"
+#include "net/base/net_util.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/webui/jstemplate_builder.h"
 #include "ui/base/webui/web_ui_util.h"
 
+namespace interstitials {
+const char kBoxChecked[] = "boxchecked";
+const char kDisplayCheckBox[] = "displaycheckbox";
+const char kOptInLink[] = "optInLink";
+const char kPrivacyLinkHtml[] =
+    "<a id=\"privacy-link\" href=\"\" onclick=\"sendCommand(%d); "
+    "return false;\" onmousedown=\"return false;\">%s</a>";
+}
+
+using content::OpenURLParams;
+using content::Referrer;
+
 SecurityInterstitialPage::SecurityInterstitialPage(
     content::WebContents* web_contents,
     const GURL& request_url)
@@ -54,8 +78,51 @@
   interstitial_page_->Show();
 }
 
+void SecurityInterstitialPage::SetReportingPreference(bool report) {
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+  PrefService* pref = profile->GetPrefs();
+  pref->SetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled, report);
+  metrics_helper()->RecordUserInteraction(
+      report
+          ? SecurityInterstitialMetricsHelper::SET_EXTENDED_REPORTING_ENABLED
+          : SecurityInterstitialMetricsHelper::SET_EXTENDED_REPORTING_DISABLED);
+}
+
+bool SecurityInterstitialPage::IsPrefEnabled(const char* pref) {
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+  return profile->GetPrefs()->GetBoolean(pref);
+}
+
+void SecurityInterstitialPage::OpenExtendedReportingPrivacyPolicy() {
+  metrics_helper()->RecordUserInteraction(
+      SecurityInterstitialMetricsHelper::SHOW_PRIVACY_POLICY);
+  GURL privacy_url(
+      l10n_util::GetStringUTF8(IDS_SAFE_BROWSING_PRIVACY_POLICY_URL));
+  privacy_url = google_util::AppendGoogleLocaleParam(
+      privacy_url, g_browser_process->GetApplicationLocale());
+  OpenURLParams params(privacy_url, Referrer(), CURRENT_TAB,
+                       ui::PAGE_TRANSITION_LINK, false);
+  web_contents()->OpenURL(params);
+}
+
+SecurityInterstitialMetricsHelper* SecurityInterstitialPage::metrics_helper() {
+  return metrics_helper_.get();
+}
+
+void SecurityInterstitialPage::set_metrics_helper(
+    SecurityInterstitialMetricsHelper* metrics_helper) {
+  metrics_helper_.reset(metrics_helper);
+}
+
 base::string16 SecurityInterstitialPage::GetFormattedHostName() const {
-  base::string16 host(base::UTF8ToUTF16(request_url_.host()));
+  std::string languages;
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+  if (profile)
+    languages = profile->GetPrefs()->GetString(prefs::kAcceptLanguages);
+  base::string16 host = net::IDNToUnicode(request_url_.host(), languages);
   if (base::i18n::IsRTL())
     base::i18n::WrapStringWithLTRFormatting(&host);
   return host;
diff --git a/chrome/browser/interstitials/security_interstitial_page.h b/chrome/browser/interstitials/security_interstitial_page.h
index 0e96ebc..2f1520ed 100644
--- a/chrome/browser/interstitials/security_interstitial_page.h
+++ b/chrome/browser/interstitials/security_interstitial_page.h
@@ -18,12 +18,25 @@
 class WebContents;
 }
 
+namespace interstitials {
+// Constants used to communicate with the JavaScript.
+extern const char kBoxChecked[];
+extern const char kDisplayCheckBox[];
+extern const char kOptInLink[];
+extern const char kPrivacyLinkHtml[];
+}
+
+class SecurityInterstitialMetricsHelper;
+
 class SecurityInterstitialPage : public content::InterstitialPageDelegate {
  public:
   // These represent the commands sent from the interstitial JavaScript.
   // DO NOT reorder or change these without also changing the JavaScript!
   // See chrome/browser/resources/security_warnings/interstitial_v2.js
   enum SecurityInterstitialCommands {
+    // Used by tests
+    CMD_TEXT_FOUND = -2,
+    CMD_TEXT_NOT_FOUND = -1,
     // Decisions
     CMD_DONT_PROCEED = 0,
     CMD_PROCEED = 1,
@@ -69,7 +82,21 @@
   content::WebContents* web_contents() const;
   GURL request_url() const;
 
+  // Record the user's preference for reporting information about
+  // malware and SSL errors.
+  void SetReportingPreference(bool report);
+
+  // Returns the boolean value of the given |pref| from the PrefService of the
+  // Profile associated with |web_contents_|.
+  bool IsPrefEnabled(const char* pref);
+
+  void OpenExtendedReportingPrivacyPolicy();
+
+  SecurityInterstitialMetricsHelper* metrics_helper();
+  void set_metrics_helper(SecurityInterstitialMetricsHelper* metrics_helper);
+
  private:
+  scoped_ptr<SecurityInterstitialMetricsHelper> metrics_helper_;
   content::WebContents* web_contents_;
   const GURL request_url_;
   // Once shown, |interstitial_page| takes ownership of this
diff --git a/chrome/browser/interstitials/security_interstitial_page_test_utils.cc b/chrome/browser/interstitials/security_interstitial_page_test_utils.cc
new file mode 100644
index 0000000..6e41b1d1
--- /dev/null
+++ b/chrome/browser/interstitials/security_interstitial_page_test_utils.cc
@@ -0,0 +1,72 @@
+// 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/interstitials/security_interstitial_page_test_utils.h"
+
+#include <string>
+
+#include "base/prefs/pref_service.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/interstitials/security_interstitial_page.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/interstitial_page.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace chrome_browser_interstitials {
+
+bool IsInterstitialDisplayingText(content::InterstitialPage* interstitial,
+                                  const std::string& text) {
+  // It's valid for |text| to contain "\'", but simply look for "'" instead
+  // since this function is used for searching host names and a predefined
+  // string.
+  DCHECK(text.find("\'") == std::string::npos);
+  std::string command = base::StringPrintf(
+      "var hasText = document.body.textContent.indexOf('%s') >= 0;"
+      "window.domAutomationController.send(hasText ? %d : %d);",
+      text.c_str(), SecurityInterstitialPage::CMD_TEXT_FOUND,
+      SecurityInterstitialPage::CMD_TEXT_NOT_FOUND);
+  int result = 0;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractInt(interstitial->GetMainFrame(),
+                                                  command, &result));
+  return result == SecurityInterstitialPage::CMD_TEXT_FOUND;
+}
+
+void SecurityInterstitialIDNTest::SetUpOnMainThread() {
+  // Clear AcceptLanguages to force punycode decoding.
+  browser()->profile()->GetPrefs()->SetString(prefs::kAcceptLanguages,
+                                              std::string());
+}
+
+testing::AssertionResult SecurityInterstitialIDNTest::VerifyIDNDecoded() const {
+  const char kHostname[] = "xn--d1abbgf6aiiy.xn--p1ai";
+  const char kHostnameJSUnicode[] =
+      "\\u043f\\u0440\\u0435\\u0437\\u0438\\u0434\\u0435\\u043d\\u0442."
+      "\\u0440\\u0444";
+  std::string request_url_spec = base::StringPrintf("https://%s/", kHostname);
+  GURL request_url(request_url_spec);
+
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  DCHECK(contents);
+  SecurityInterstitialPage* blocking_page =
+      CreateInterstitial(contents, request_url);
+  blocking_page->Show();
+
+  WaitForInterstitialAttach(contents);
+  if (!WaitForRenderFrameReady(contents->GetInterstitialPage()->GetMainFrame()))
+    return testing::AssertionFailure() << "Render frame not ready";
+
+  if (IsInterstitialDisplayingText(contents->GetInterstitialPage(),
+                                   kHostnameJSUnicode)) {
+    return testing::AssertionSuccess();
+  }
+  return testing::AssertionFailure() << "Interstitial not displaying text";
+}
+}  // namespace chrome_browser_interstitials
diff --git a/chrome/browser/interstitials/security_interstitial_page_test_utils.h b/chrome/browser/interstitials/security_interstitial_page_test_utils.h
new file mode 100644
index 0000000..34c613a
--- /dev/null
+++ b/chrome/browser/interstitials/security_interstitial_page_test_utils.h
@@ -0,0 +1,45 @@
+// 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_INTERSTITIALS_SECURITY_INTERSTITIAL_PAGE_TEST_UTILS_H_
+#define CHROME_BROWSER_INTERSTITIALS_SECURITY_INTERSTITIAL_PAGE_TEST_UTILS_H_
+
+#include <string>
+
+#include "chrome/test/base/in_process_browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+class InterstitialPage;
+class WebContents;
+}
+
+class GURL;
+class SecurityInterstitialPage;
+
+namespace chrome_browser_interstitials {
+
+bool IsInterstitialDisplayingText(content::InterstitialPage* interstitial,
+                                  const std::string& text);
+
+// This class is used for testing the display of IDN names in security
+// interstitials.
+class SecurityInterstitialIDNTest : public InProcessBrowserTest {
+ public:
+  // InProcessBrowserTest implementation
+  void SetUpOnMainThread() override;
+
+  // Run a test that creates an interstitial with an IDN request URL
+  // and checks that it is properly decoded.
+  testing::AssertionResult VerifyIDNDecoded() const;
+
+ protected:
+  virtual SecurityInterstitialPage* CreateInterstitial(
+      content::WebContents* contents,
+      const GURL& request_url) const = 0;
+};
+
+}  // namespace chrome_browser_interstitials
+
+#endif  // CHROME_BROWSER_INTERSTITIALS_SECURITY_INTERSTITIAL_PAGE_TEST_UTILS_H_
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index ed2f0d6..5f8941a 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -136,6 +136,7 @@
 const char kSpdyFieldTrialHoldbackGroupNamePrefix[] = "SpdyDisabled";
 const char kSpdyFieldTrialSpdy31GroupNamePrefix[] = "Spdy31Enabled";
 const char kSpdyFieldTrialSpdy4GroupNamePrefix[] = "Spdy4Enabled";
+const char kSpdyFieldTrialParametrizedPrefix[] = "Parametrized";
 
 // Field trial for Cache-Control: stale-while-revalidate directive.
 const char kStaleWhileRevalidateFieldTrialName[] = "StaleWhileRevalidate";
@@ -319,6 +320,100 @@
   return group_name == "RequirementEnforced";
 }
 
+// Parse kUseSpdy command line flag options, which may contain the following:
+//
+//   "off"                      : Disables SPDY support entirely.
+//   "ssl"                      : Forces SPDY for all HTTPS requests.
+//   "no-ssl"                   : Forces SPDY for all HTTP requests.
+//   "no-ping"                  : Disables SPDY ping connection testing.
+//   "exclude=<host>"           : Disables SPDY support for the host <host>.
+//   "no-compress"              : Disables SPDY header compression.
+//   "no-alt-protocols          : Disables alternate protocol support.
+//   "force-alt-protocols       : Forces an alternate protocol of SPDY/3
+//                                on port 443.
+//   "single-domain"            : Forces all spdy traffic to a single domain.
+//   "init-max-streams=<limit>" : Specifies the maximum number of concurrent
+//                                streams for a SPDY session, unless the
+//                                specifies a different value via SETTINGS.
+void ConfigureSpdyGlobalsFromUseSpdyArgument(const std::string& mode,
+                                             IOThread::Globals* globals) {
+  static const char kOff[] = "off";
+  static const char kSSL[] = "ssl";
+  static const char kDisableSSL[] = "no-ssl";
+  static const char kDisablePing[] = "no-ping";
+  static const char kExclude[] = "exclude";  // Hosts to exclude
+  static const char kDisableCompression[] = "no-compress";
+  static const char kDisableAltProtocols[] = "no-alt-protocols";
+  static const char kForceAltProtocols[] = "force-alt-protocols";
+  static const char kSingleDomain[] = "single-domain";
+
+  static const char kInitialMaxConcurrentStreams[] = "init-max-streams";
+
+  std::vector<std::string> spdy_options;
+  base::SplitString(mode, ',', &spdy_options);
+
+  for (const std::string& element : spdy_options) {
+    std::vector<std::string> name_value;
+    base::SplitString(element, '=', &name_value);
+    const std::string& option =
+        name_value.size() > 0 ? name_value[0] : std::string();
+    const std::string value =
+        name_value.size() > 1 ? name_value[1] : std::string();
+
+    if (option == kOff) {
+      net::HttpStreamFactory::set_spdy_enabled(false);
+      continue;
+    }
+    if (option == kDisableSSL) {
+      globals->spdy_default_protocol.set(net::kProtoSPDY31);
+      globals->force_spdy_over_ssl.set(false);
+      globals->force_spdy_always.set(true);
+      continue;
+    }
+    if (option == kSSL) {
+      globals->spdy_default_protocol.set(net::kProtoSPDY31);
+      globals->force_spdy_over_ssl.set(true);
+      globals->force_spdy_always.set(true);
+      continue;
+    }
+    if (option == kDisablePing) {
+      globals->enable_spdy_ping_based_connection_checking.set(false);
+      continue;
+    }
+    if (option == kExclude) {
+      globals->forced_spdy_exclusions.insert(
+          net::HostPortPair::FromURL(GURL(value)));
+      continue;
+    }
+    if (option == kDisableCompression) {
+      globals->enable_spdy_compression.set(false);
+      continue;
+    }
+    if (option == kDisableAltProtocols) {
+      globals->use_alternate_protocols.set(false);
+      continue;
+    }
+    if (option == kForceAltProtocols) {
+      net::AlternateProtocolInfo pair(443, net::NPN_SPDY_3, 1);
+      net::HttpServerPropertiesImpl::ForceAlternateProtocol(pair);
+      continue;
+    }
+    if (option == kSingleDomain) {
+      DVLOG(1) << "FORCING SINGLE DOMAIN";
+      globals->force_spdy_single_domain.set(true);
+      continue;
+    }
+    if (option == kInitialMaxConcurrentStreams) {
+      int streams;
+      if (base::StringToInt(value, &streams)) {
+        globals->initial_max_spdy_concurrent_streams.set(streams);
+        continue;
+      }
+    }
+    LOG(DFATAL) << "Unrecognized spdy option: " << option;
+  }
+}
+
 }  // namespace
 
 class IOThread::LoggingNetworkChangeObserver
@@ -865,28 +960,12 @@
     if (trial)
       trial->Disable();
   } else {
-    if (command_line.HasSwitch(switches::kTrustedSpdyProxy)) {
-      globals_->trusted_spdy_proxy.set(
-          command_line.GetSwitchValueASCII(switches::kTrustedSpdyProxy));
+    std::string group = base::FieldTrialList::FindFullName(kSpdyFieldTrialName);
+    VariationParameters params;
+    if (!variations::GetVariationParams(kSpdyFieldTrialName, &params)) {
+      params.clear();
     }
-    if (command_line.HasSwitch(switches::kIgnoreUrlFetcherCertRequests))
-      net::URLFetcher::SetIgnoreCertificateRequests(true);
-
-    if (command_line.HasSwitch(switches::kUseSpdy)) {
-      std::string spdy_mode =
-          command_line.GetSwitchValueASCII(switches::kUseSpdy);
-      EnableSpdy(spdy_mode);
-    } else if (command_line.HasSwitch(switches::kEnableSpdy4)) {
-      globals_->next_protos = net::NextProtosSpdy4Http2();
-      globals_->use_alternate_protocols.set(true);
-    } else if (command_line.HasSwitch(switches::kEnableNpnHttpOnly)) {
-      globals_->next_protos = net::NextProtosHttpOnly();
-      globals_->use_alternate_protocols.set(false);
-    } else {
-      // No SPDY command-line flags have been specified. Examine trial groups.
-      ConfigureSpdyFromTrial(
-          base::FieldTrialList::FindFullName(kSpdyFieldTrialName), globals_);
-    }
+    ConfigureSpdyGlobals(command_line, group, params, globals_);
   }
 
   ConfigureTCPFastOpen(command_line);
@@ -930,87 +1009,92 @@
   }
 }
 
-void IOThread::ConfigureSpdyFromTrial(base::StringPiece spdy_trial_group,
-                                      Globals* globals) {
+// static
+void IOThread::ConfigureSpdyGlobals(
+    const base::CommandLine& command_line,
+    base::StringPiece spdy_trial_group,
+    const VariationParameters& spdy_trial_params,
+    IOThread::Globals* globals) {
+  if (command_line.HasSwitch(switches::kTrustedSpdyProxy)) {
+    globals->trusted_spdy_proxy.set(
+        command_line.GetSwitchValueASCII(switches::kTrustedSpdyProxy));
+  }
+  if (command_line.HasSwitch(switches::kIgnoreUrlFetcherCertRequests))
+    net::URLFetcher::SetIgnoreCertificateRequests(true);
+
+  if (command_line.HasSwitch(switches::kUseSpdy)) {
+    std::string spdy_mode =
+        command_line.GetSwitchValueASCII(switches::kUseSpdy);
+    ConfigureSpdyGlobalsFromUseSpdyArgument(spdy_mode, globals);
+    return;
+  }
+
+  globals->next_protos.clear();
+  globals->next_protos.push_back(net::kProtoHTTP11);
+  bool enable_quic = false;
+  globals->enable_quic.CopyToIfSet(&enable_quic);
+  if (enable_quic) {
+    globals->next_protos.push_back(net::kProtoQUIC1SPDY3);
+  }
+
+  if (command_line.HasSwitch(switches::kEnableSpdy4)) {
+    globals->next_protos.push_back(net::kProtoSPDY31);
+    globals->next_protos.push_back(net::kProtoSPDY4_14);
+    globals->next_protos.push_back(net::kProtoSPDY4);
+    globals->use_alternate_protocols.set(true);
+    return;
+  }
+  if (command_line.HasSwitch(switches::kEnableNpnHttpOnly)) {
+    globals->use_alternate_protocols.set(false);
+    return;
+  }
+
+  // No SPDY command-line flags have been specified. Examine trial groups.
   if (spdy_trial_group.starts_with(kSpdyFieldTrialHoldbackGroupNamePrefix)) {
-    // TODO(jgraettinger): Use net::NextProtosHttpOnly() instead?
     net::HttpStreamFactory::set_spdy_enabled(false);
-  } else if (spdy_trial_group.starts_with(
-                 kSpdyFieldTrialSpdy31GroupNamePrefix)) {
-    globals->next_protos = net::NextProtosSpdy31();
-    globals->use_alternate_protocols.set(true);
-  } else if (spdy_trial_group.starts_with(
-                 kSpdyFieldTrialSpdy4GroupNamePrefix)) {
-    globals->next_protos = net::NextProtosSpdy4Http2();
-    globals->use_alternate_protocols.set(true);
-  } else {
-    // By default, enable HTTP/2.
-    globals->next_protos = net::NextProtosSpdy4Http2();
-    globals->use_alternate_protocols.set(true);
+    return;
   }
-}
-
-void IOThread::EnableSpdy(const std::string& mode) {
-  static const char kOff[] = "off";
-  static const char kSSL[] = "ssl";
-  static const char kDisableSSL[] = "no-ssl";
-  static const char kDisablePing[] = "no-ping";
-  static const char kExclude[] = "exclude";  // Hosts to exclude
-  static const char kDisableCompression[] = "no-compress";
-  static const char kDisableAltProtocols[] = "no-alt-protocols";
-  static const char kForceAltProtocols[] = "force-alt-protocols";
-  static const char kSingleDomain[] = "single-domain";
-
-  static const char kInitialMaxConcurrentStreams[] = "init-max-streams";
-
-  std::vector<std::string> spdy_options;
-  base::SplitString(mode, ',', &spdy_options);
-
-  for (std::vector<std::string>::iterator it = spdy_options.begin();
-       it != spdy_options.end(); ++it) {
-    const std::string& element = *it;
-    std::vector<std::string> name_value;
-    base::SplitString(element, '=', &name_value);
-    const std::string& option =
-        name_value.size() > 0 ? name_value[0] : std::string();
-    const std::string value =
-        name_value.size() > 1 ? name_value[1] : std::string();
-
-    if (option == kOff) {
-      net::HttpStreamFactory::set_spdy_enabled(false);
-    } else if (option == kDisableSSL) {
-      globals_->spdy_default_protocol.set(net::kProtoSPDY31);
-      globals_->force_spdy_over_ssl.set(false);
-      globals_->force_spdy_always.set(true);
-    } else if (option == kSSL) {
-      globals_->spdy_default_protocol.set(net::kProtoSPDY31);
-      globals_->force_spdy_over_ssl.set(true);
-      globals_->force_spdy_always.set(true);
-    } else if (option == kDisablePing) {
-      globals_->enable_spdy_ping_based_connection_checking.set(false);
-    } else if (option == kExclude) {
-      globals_->forced_spdy_exclusions.insert(
-          net::HostPortPair::FromURL(GURL(value)));
-    } else if (option == kDisableCompression) {
-      globals_->enable_spdy_compression.set(false);
-    } else if (option == kDisableAltProtocols) {
-      globals_->use_alternate_protocols.set(false);
-    } else if (option == kForceAltProtocols) {
-      net::AlternateProtocolInfo pair(443, net::NPN_SPDY_3, 1);
-      net::HttpServerPropertiesImpl::ForceAlternateProtocol(pair);
-    } else if (option == kSingleDomain) {
-      DVLOG(1) << "FORCING SINGLE DOMAIN";
-      globals_->force_spdy_single_domain.set(true);
-    } else if (option == kInitialMaxConcurrentStreams) {
-      int streams;
-      if (base::StringToInt(value, &streams))
-        globals_->initial_max_spdy_concurrent_streams.set(streams);
-    } else if (option.empty() && it == spdy_options.begin()) {
-      continue;
-    } else {
-      LOG(DFATAL) << "Unrecognized spdy option: " << option;
+  if (spdy_trial_group.starts_with(kSpdyFieldTrialSpdy31GroupNamePrefix)) {
+    globals->next_protos.push_back(net::kProtoSPDY31);
+    globals->use_alternate_protocols.set(true);
+    return;
+  }
+  if (spdy_trial_group.starts_with(kSpdyFieldTrialSpdy4GroupNamePrefix)) {
+    globals->next_protos.push_back(net::kProtoSPDY31);
+    globals->next_protos.push_back(net::kProtoSPDY4_14);
+    globals->next_protos.push_back(net::kProtoSPDY4);
+    globals->use_alternate_protocols.set(true);
+    return;
+  }
+  if (spdy_trial_group.starts_with(kSpdyFieldTrialParametrizedPrefix)) {
+    bool spdy_enabled = false;
+    if (LowerCaseEqualsASCII(
+            GetVariationParam(spdy_trial_params, "enable_spdy31"), "true")) {
+      globals->next_protos.push_back(net::kProtoSPDY31);
+      spdy_enabled = true;
     }
+    if (LowerCaseEqualsASCII(
+            GetVariationParam(spdy_trial_params, "enable_http2_14"), "true")) {
+      globals->next_protos.push_back(net::kProtoSPDY4_14);
+      spdy_enabled = true;
+    }
+    if (LowerCaseEqualsASCII(
+            GetVariationParam(spdy_trial_params, "enable_http2"), "true")) {
+      globals->next_protos.push_back(net::kProtoSPDY4);
+      spdy_enabled = true;
+    }
+    // TODO(bnc): HttpStreamFactory::spdy_enabled_ is redundant with
+    // globals->next_protos, can it be eliminated?
+    net::HttpStreamFactory::set_spdy_enabled(spdy_enabled);
+    globals->use_alternate_protocols.set(true);
+    return;
   }
+
+  // By default, enable HTTP/2.
+  globals->next_protos.push_back(net::kProtoSPDY31);
+  globals->next_protos.push_back(net::kProtoSPDY4_14);
+  globals->next_protos.push_back(net::kProtoSPDY4);
+  globals->use_alternate_protocols.set(true);
 }
 
 // static
diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h
index 0da6e86..ab2926e8 100644
--- a/chrome/browser/io_thread.h
+++ b/chrome/browser/io_thread.h
@@ -271,27 +271,13 @@
   // Sets up SDCH based on field trials.
   void ConfigureSdch();
 
-  // Enable SPDY with the given mode, which may contain the following:
-  //
-  //   "off"                      : Disables SPDY support entirely.
-  //   "ssl"                      : Forces SPDY for all HTTPS requests.
-  //   "no-ssl"                   : Forces SPDY for all HTTP requests.
-  //   "no-ping"                  : Disables SPDY ping connection testing.
-  //   "exclude=<host>"           : Disables SPDY support for the host <host>.
-  //   "no-compress"              : Disables SPDY header compression.
-  //   "no-alt-protocols          : Disables alternate protocol support.
-  //   "force-alt-protocols       : Forces an alternate protocol of SPDY/3
-  //                                on port 443.
-  //   "single-domain"            : Forces all spdy traffic to a single domain.
-  //   "init-max-streams=<limit>" : Specifies the maximum number of concurrent
-  //                                streams for a SPDY session, unless the
-  //                                specifies a different value via SETTINGS.
-  void EnableSpdy(const std::string& mode);
-
-  // Configures available SPDY protocol versions from the given trial.
-  // Used only if no command-line configuration was present.
-  static void ConfigureSpdyFromTrial(base::StringPiece spdy_trial_group,
-                                     Globals* globals);
+  // Configures available SPDY protocol versions in |globals| based on the flags
+  // in |command_lin| as well as SPDY field trial group and parameters.  Must be
+  // called after ConfigureQuicGlobals.
+  static void ConfigureSpdyGlobals(const base::CommandLine& command_line,
+                                   base::StringPiece quic_trial_group,
+                                   const VariationParameters& quic_trial_params,
+                                   Globals* globals);
 
   // Global state must be initialized on the IO thread, then this
   // method must be invoked on the UI thread.
@@ -325,7 +311,8 @@
 #endif
   }
   // Configures QUIC options in |globals| based on the flags in |command_line|
-  // as well as the QUIC field trial group and parameters.
+  // as well as the QUIC field trial group and parameters.  Must be called
+  // before ConfigureSpdyGlobals.
   static void ConfigureQuicGlobals(
       const base::CommandLine& command_line,
       base::StringPiece quic_trial_group,
diff --git a/chrome/browser/io_thread_unittest.cc b/chrome/browser/io_thread_unittest.cc
index 33bde3d..7b527bad 100644
--- a/chrome/browser/io_thread_unittest.cc
+++ b/chrome/browser/io_thread_unittest.cc
@@ -37,16 +37,20 @@
                                    quic_trial_params, globals);
   }
 
+  static void ConfigureSpdyGlobals(
+      const base::CommandLine& command_line,
+      base::StringPiece spdy_trial_group,
+      const std::map<std::string, std::string>& spdy_trial_params,
+      IOThread::Globals* globals) {
+    IOThread::ConfigureSpdyGlobals(command_line, spdy_trial_group,
+                                   spdy_trial_params, globals);
+  }
+
   static void InitializeNetworkSessionParamsFromGlobals(
       const IOThread::Globals& globals,
       net::HttpNetworkSession::Params* params) {
     IOThread::InitializeNetworkSessionParamsFromGlobals(globals, params);
   }
-
-  static void ConfigureSpdyFromTrial(const std::string& trial_group,
-                                     IOThread::Globals* globals) {
-    IOThread::ConfigureSpdyFromTrial(trial_group, globals);
-  }
 };
 
 class IOThreadTest : public testing::Test {
@@ -60,6 +64,11 @@
                                        field_trial_params_, &globals_);
   }
 
+  void ConfigureSpdyGlobals() {
+    IOThreadPeer::ConfigureSpdyGlobals(command_line_, field_trial_group_,
+                                       field_trial_params_, &globals_);
+  }
+
   void InitializeNetworkSessionParams(net::HttpNetworkSession::Params* params) {
     IOThreadPeer::InitializeNetworkSessionParamsFromGlobals(globals_, params);
   }
@@ -83,16 +92,17 @@
 
 TEST_F(IOThreadTest, SpdyFieldTrialHoldbackEnabled) {
   net::HttpStreamFactory::set_spdy_enabled(true);
-  IOThreadPeer::ConfigureSpdyFromTrial("SpdyDisabled", &globals_);
+  field_trial_group_ = "SpdyDisabled";
+  ConfigureSpdyGlobals();
   EXPECT_FALSE(net::HttpStreamFactory::spdy_enabled());
 }
 
 TEST_F(IOThreadTest, SpdyFieldTrialSpdy31Enabled) {
   bool use_alternate_protocols = false;
-  IOThreadPeer::ConfigureSpdyFromTrial("Spdy31Enabled", &globals_);
+  field_trial_group_ = "Spdy31Enabled";
+  ConfigureSpdyGlobals();
   EXPECT_THAT(globals_.next_protos,
               ElementsAre(net::kProtoHTTP11,
-                          net::kProtoQUIC1SPDY3,
                           net::kProtoSPDY31));
   globals_.use_alternate_protocols.CopyToIfSet(&use_alternate_protocols);
   EXPECT_TRUE(use_alternate_protocols);
@@ -100,15 +110,92 @@
 
 TEST_F(IOThreadTest, SpdyFieldTrialSpdy4Enabled) {
   bool use_alternate_protocols = false;
-  IOThreadPeer::ConfigureSpdyFromTrial("Spdy4Enabled", &globals_);
-  EXPECT_THAT(
-      globals_.next_protos,
-      ElementsAre(net::kProtoHTTP11, net::kProtoQUIC1SPDY3, net::kProtoSPDY31,
-                  net::kProtoSPDY4_14, net::kProtoSPDY4));
+  field_trial_group_ = "Spdy4Enabled";
+  ConfigureSpdyGlobals();
+  EXPECT_THAT(globals_.next_protos,
+              ElementsAre(net::kProtoHTTP11, net::kProtoSPDY31,
+                          net::kProtoSPDY4_14, net::kProtoSPDY4));
   globals_.use_alternate_protocols.CopyToIfSet(&use_alternate_protocols);
   EXPECT_TRUE(use_alternate_protocols);
 }
 
+TEST_F(IOThreadTest, SpdyFieldTrialDefault) {
+  field_trial_group_ = "";
+  ConfigureSpdyGlobals();
+  EXPECT_THAT(globals_.next_protos,
+              ElementsAre(net::kProtoHTTP11, net::kProtoSPDY31,
+                          net::kProtoSPDY4_14, net::kProtoSPDY4));
+  bool use_alternate_protocols = false;
+  globals_.use_alternate_protocols.CopyToIfSet(&use_alternate_protocols);
+  EXPECT_TRUE(use_alternate_protocols);
+}
+
+TEST_F(IOThreadTest, SpdyFieldTrialParametrized) {
+  field_trial_params_["enable_spdy31"] = "false";
+  // Undefined parameter "enable_http2_14" should default to false.
+  field_trial_params_["enable_http2"] = "true";
+  field_trial_group_ = "ParametrizedHTTP2Only";
+  ConfigureSpdyGlobals();
+  EXPECT_THAT(globals_.next_protos,
+              ElementsAre(net::kProtoHTTP11, net::kProtoSPDY4));
+  bool use_alternate_protocols = false;
+  globals_.use_alternate_protocols.CopyToIfSet(&use_alternate_protocols);
+  EXPECT_TRUE(use_alternate_protocols);
+}
+
+TEST_F(IOThreadTest, SpdyCommandLineEnable) {
+  command_line_.AppendSwitch("enable-spdy4");
+  // Command line should overwrite field trial group.
+  field_trial_group_ = "SpdyDisabled";
+  ConfigureSpdyGlobals();
+  EXPECT_THAT(globals_.next_protos,
+              ElementsAre(net::kProtoHTTP11, net::kProtoSPDY31,
+                          net::kProtoSPDY4_14, net::kProtoSPDY4));
+  bool use_alternate_protocols = false;
+  globals_.use_alternate_protocols.CopyToIfSet(&use_alternate_protocols);
+  EXPECT_TRUE(use_alternate_protocols);
+}
+
+TEST_F(IOThreadTest, SpdyCommandLineDisable) {
+  command_line_.AppendSwitch("enable-npn-http");
+  // Command line should overwrite field trial group.
+  field_trial_group_ = "Spdy4Enabled";
+  ConfigureSpdyGlobals();
+  EXPECT_THAT(globals_.next_protos, ElementsAre(net::kProtoHTTP11));
+  bool use_alternate_protocols = true;
+  globals_.use_alternate_protocols.CopyToIfSet(&use_alternate_protocols);
+  EXPECT_FALSE(use_alternate_protocols);
+}
+
+TEST_F(IOThreadTest, SpdyCommandLineUseSpdyOff) {
+  command_line_.AppendSwitchASCII("use-spdy", "off");
+  // Command line should overwrite field trial group.
+  field_trial_group_ = "Spdy4Enabled";
+  ConfigureSpdyGlobals();
+  EXPECT_EQ(0u, globals_.next_protos.size());
+}
+
+TEST_F(IOThreadTest, SpdyCommandLineUseSpdySSL) {
+  command_line_.AppendSwitchASCII("use-spdy", "ssl");
+  // Command line should overwrite field trial group.
+  field_trial_group_ = "SpdyDisabled";
+  ConfigureSpdyGlobals();
+  bool force_spdy_over_ssl = false;
+  globals_.force_spdy_over_ssl.CopyToIfSet(&force_spdy_over_ssl);
+  EXPECT_TRUE(force_spdy_over_ssl);
+  bool force_spdy_always = false;
+  globals_.force_spdy_always.CopyToIfSet(&force_spdy_always);
+  EXPECT_TRUE(force_spdy_always);
+}
+
+TEST_F(IOThreadTest, SpdyCommandLineUseSpdyDisableAltProtocols) {
+  command_line_.AppendSwitchASCII("use-spdy", "no-alt-protocols");
+  ConfigureSpdyGlobals();
+  bool use_alternate_protocols = true;
+  globals_.use_alternate_protocols.CopyToIfSet(&use_alternate_protocols);
+  EXPECT_FALSE(use_alternate_protocols);
+}
+
 TEST_F(IOThreadTest, DisableQuicByDefault) {
   ConfigureQuicGlobals();
   net::HttpNetworkSession::Params params;
diff --git a/chrome/browser/media/chrome_webrtc_browsertest.cc b/chrome/browser/media/chrome_webrtc_browsertest.cc
index 1877260..37f59f5b0e 100644
--- a/chrome/browser/media/chrome_webrtc_browsertest.cc
+++ b/chrome/browser/media/chrome_webrtc_browsertest.cc
@@ -44,8 +44,10 @@
 #define MAYBE_RunsAudioVideoWebRTCCallInTwoTabs \
     DISABLED_RunsAudioVideoWebRTCCallInTwoTabs
 #else
+// TODO(pkasting): http://crbug.com/471132 Temporarily disabled this test due to
+// unexplained failure.
 #define MAYBE_RunsAudioVideoWebRTCCallInTwoTabs \
-    RunsAudioVideoWebRTCCallInTwoTabs
+    DISABLED_RunsAudioVideoWebRTCCallInTwoTabs
 #endif
 
 IN_PROC_BROWSER_TEST_F(WebRtcBrowserTest,
diff --git a/chrome/browser/media/media_capture_devices_dispatcher.cc b/chrome/browser/media/media_capture_devices_dispatcher.cc
index ef6127b..cba2d99 100644
--- a/chrome/browser/media/media_capture_devices_dispatcher.cc
+++ b/chrome/browser/media/media_capture_devices_dispatcher.cc
@@ -94,18 +94,16 @@
 // This is a short-term solution to grant camera and/or microphone access to
 // extensions:
 // 1. Virtual keyboard extension.
-// 2. Google Voice Search Hotword extension.
-// 3. Flutter gesture recognition extension.
-// 4. TODO(smus): Airbender experiment 1.
-// 5. TODO(smus): Airbender experiment 2.
-// 6. Hotwording component extension.
-// 7. XKB input method component extension.
-// 8. M17n/T13n/CJK input method component extension.
+// 2. Flutter gesture recognition extension.
+// 3. TODO(smus): Airbender experiment 1.
+// 4. TODO(smus): Airbender experiment 2.
+// 5. Hotwording component extension.
+// 6. XKB input method component extension.
+// 7. M17n/T13n/CJK input method component extension.
 // Once http://crbug.com/292856 is fixed, remove this whitelist.
 bool IsMediaRequestWhitelistedForExtension(
     const extensions::Extension* extension) {
   return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" ||
-      extension->id() == "bepbmhgboaologfdajaanbcjmnhjmhfn" ||
       extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" ||
       extension->id() == "clffjmdilanldobdnedchkdbofoimcgb" ||
       extension->id() == "nnckehldicaciogcbchegobnafnjkcne" ||
@@ -397,8 +395,15 @@
   }
 #endif
 
-  if (CheckAllowAllMediaStreamContentForOrigin(profile, security_origin))
+  ContentSettingsType contentSettingsType =
+      type == content::MEDIA_DEVICE_AUDIO_CAPTURE
+          ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
+          : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA;
+
+  if (CheckAllowAllMediaStreamContentForOrigin(
+          profile, security_origin, contentSettingsType)) {
     return true;
+  }
 
   const char* policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE
                                 ? prefs::kAudioCaptureAllowed
@@ -417,9 +422,7 @@
   if (profile->GetHostContentSettingsMap()->GetContentSetting(
           security_origin,
           security_origin,
-          type == content::MEDIA_DEVICE_AUDIO_CAPTURE
-              ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
-              : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
+          contentSettingsType,
           NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_ALLOW) {
     return true;
   }
@@ -438,8 +441,15 @@
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
 
-  if (CheckAllowAllMediaStreamContentForOrigin(profile, security_origin))
+  ContentSettingsType contentSettingsType =
+      type == content::MEDIA_DEVICE_AUDIO_CAPTURE
+          ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
+          : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA;
+
+  if (CheckAllowAllMediaStreamContentForOrigin(
+          profile, security_origin, contentSettingsType)) {
     return true;
+  }
 
   const char* policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE
                                 ? prefs::kAudioCaptureAllowed
@@ -458,9 +468,7 @@
   if (profile->GetHostContentSettingsMap()->GetContentSetting(
           security_origin,
           security_origin,
-          type == content::MEDIA_DEVICE_AUDIO_CAPTURE
-              ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
-              : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
+          contentSettingsType,
           NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_ALLOW) {
     return true;
   }
diff --git a/chrome/browser/media/media_stream_capture_indicator.cc b/chrome/browser/media/media_stream_capture_indicator.cc
index 642134df..978e0e8 100644
--- a/chrome/browser/media/media_stream_capture_indicator.cc
+++ b/chrome/browser/media/media_stream_capture_indicator.cc
@@ -57,7 +57,6 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   static const char* const kExtensionWhitelist[] = {
-    extension_misc::kHotwordExtensionId,
     extension_misc::kHotwordNewExtensionId,
   };
 
diff --git a/chrome/browser/media/media_stream_device_permissions.cc b/chrome/browser/media/media_stream_device_permissions.cc
index c8ff9a36..6ce1c7b 100644
--- a/chrome/browser/media/media_stream_device_permissions.cc
+++ b/chrome/browser/media/media_stream_device_permissions.cc
@@ -36,12 +36,12 @@
 }  // namespace
 
 bool CheckAllowAllMediaStreamContentForOrigin(Profile* profile,
-                                              const GURL& security_origin) {
-  // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the
-  // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and
-  // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA.
+                                              const GURL& security_origin,
+                                              ContentSettingsType type) {
+  DCHECK(type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
+         type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
   return profile->GetHostContentSettingsMap()->ShouldAllowAllContent(
-      security_origin, security_origin, CONTENT_SETTINGS_TYPE_MEDIASTREAM);
+      security_origin, security_origin, type);
 }
 
 MediaStreamDevicePolicy GetDevicePolicy(Profile* profile,
diff --git a/chrome/browser/media/media_stream_device_permissions.h b/chrome/browser/media/media_stream_device_permissions.h
index cd24479..f92609c3 100644
--- a/chrome/browser/media/media_stream_device_permissions.h
+++ b/chrome/browser/media/media_stream_device_permissions.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_MEDIA_MEDIA_STREAM_DEVICE_PERMISSIONS_H_
 #define CHROME_BROWSER_MEDIA_MEDIA_STREAM_DEVICE_PERMISSIONS_H_
 
+#include "components/content_settings/core/common/content_settings_types.h"
+
 class GURL;
 class Profile;
 
@@ -17,7 +19,8 @@
 // Returns true if security origin is from internal objects like
 // chrome://URLs, otherwise returns false.
 bool CheckAllowAllMediaStreamContentForOrigin(Profile* profile,
-                                              const GURL& security_origin);
+                                              const GURL& security_origin,
+                                              ContentSettingsType type);
 
 // Get the device policy for |security_origin| and |profile|.
 MediaStreamDevicePolicy GetDevicePolicy(Profile* profile,
diff --git a/chrome/browser/media/media_stream_devices_controller.cc b/chrome/browser/media/media_stream_devices_controller.cc
index cc371d5..fb6f381 100644
--- a/chrome/browser/media/media_stream_devices_controller.cc
+++ b/chrome/browser/media/media_stream_devices_controller.cc
@@ -500,8 +500,12 @@
 
 bool MediaStreamDevicesController::IsRequestAllowedByDefault() const {
   // The request from internal objects like chrome://URLs is always allowed.
-  if (CheckAllowAllMediaStreamContentForOrigin(profile_,
-                                               request_.security_origin)) {
+  if (CheckAllowAllMediaStreamContentForOrigin(
+          profile_, request_.security_origin,
+          CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC) &&
+      CheckAllowAllMediaStreamContentForOrigin(
+          profile_, request_.security_origin,
+          CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)) {
     return true;
   }
 
@@ -586,13 +590,12 @@
 
 bool MediaStreamDevicesController::IsDefaultMediaAccessBlocked() const {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the
-  // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and
-  // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA.
-  ContentSetting current_setting =
-      profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
-          CONTENT_SETTINGS_TYPE_MEDIASTREAM, NULL);
-  return (current_setting == CONTENT_SETTING_BLOCK);
+  return (profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
+              CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, NULL)
+          == CONTENT_SETTING_BLOCK &&
+          profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
+              CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, NULL)
+          == CONTENT_SETTING_BLOCK);
 }
 
 bool MediaStreamDevicesController::IsSchemeSecure() const {
diff --git a/chrome/browser/media/webrtc_browsertest_perf.cc b/chrome/browser/media/webrtc_browsertest_perf.cc
index d87ee56..f3883e3 100644
--- a/chrome/browser/media/webrtc_browsertest_perf.cc
+++ b/chrome/browser/media/webrtc_browsertest_perf.cc
@@ -110,15 +110,6 @@
   perf_test::PrintResult("video_resolution", modifier, "goog_frame_height_sent",
                          value, "pixels", false);
 
-  EXPECT_TRUE(pc_dict.GetString(
-      Statistic("googCaptureJitterMs", ssrc), &value));
-  perf_test::PrintResult(
-      "video_tx", modifier, "goog_capture_jitter_ms", value, "ms", false);
-  EXPECT_TRUE(pc_dict.GetString(
-      Statistic("googCaptureQueueDelayMsPerS", ssrc), &value));
-  perf_test::PrintResult(
-      "video_tx", modifier, "goog_capture_queue_delay_ms_per_s",
-       value, "ms/s", false);
   EXPECT_TRUE(pc_dict.GetString(Statistic("googAvgEncodeMs", ssrc), &value));
   perf_test::PrintResult(
       "video_tx", modifier, "goog_avg_encode_ms", value, "ms", false);
diff --git a/chrome/browser/media_galleries/fileapi/supported_image_type_validator.cc b/chrome/browser/media_galleries/fileapi/supported_image_type_validator.cc
index 460a563b..ac590cd 100644
--- a/chrome/browser/media_galleries/fileapi/supported_image_type_validator.cc
+++ b/chrome/browser/media_galleries/fileapi/supported_image_type_validator.cc
@@ -47,12 +47,15 @@
   return result.Pass();
 }
 
-class ImageDecoderDelegateAdapter : public ImageDecoder::Delegate {
+class ImageDecoderDelegateAdapter : public ImageDecoder::ImageRequest {
  public:
   ImageDecoderDelegateAdapter(
       scoped_ptr<std::string> data,
       const storage::CopyOrMoveFileValidator::ResultCallback& callback)
-      : data_(data.Pass()), callback_(callback) {
+      : ImageRequest(content::BrowserThread::GetMessageLoopProxyForThread(
+            BrowserThread::IO)),
+        data_(data.Pass()),
+        callback_(callback) {
     DCHECK(data_);
   }
 
@@ -60,14 +63,13 @@
     return *data_;
   }
 
-  // ImageDecoder::Delegate methods.
-  void OnImageDecoded(const ImageDecoder* /*decoder*/,
-                      const SkBitmap& /*decoded_image*/) override {
+  // ImageDecoder::ImageRequest methods.
+  void OnImageDecoded(const SkBitmap& /*decoded_image*/) override {
     callback_.Run(base::File::FILE_OK);
     delete this;
   }
 
-  void OnDecodeImageFailed(const ImageDecoder* /*decoder*/) override {
+  void OnDecodeImageFailed() override {
     callback_.Run(base::File::FILE_ERROR_SECURITY);
     delete this;
   }
@@ -127,8 +129,5 @@
   // |adapter| will delete itself after a completion message is received.
   ImageDecoderDelegateAdapter* adapter =
       new ImageDecoderDelegateAdapter(data.Pass(), callback_);
-  decoder_ = new ImageDecoder(adapter, adapter->data(),
-                              ImageDecoder::DEFAULT_CODEC);
-  decoder_->Start(content::BrowserThread::GetMessageLoopProxyForThread(
-      BrowserThread::IO));
+  ImageDecoder::Start(adapter, adapter->data());
 }
diff --git a/chrome/browser/media_galleries/fileapi/supported_image_type_validator.h b/chrome/browser/media_galleries/fileapi/supported_image_type_validator.h
index 28924e8..28be737 100644
--- a/chrome/browser/media_galleries/fileapi/supported_image_type_validator.h
+++ b/chrome/browser/media_galleries/fileapi/supported_image_type_validator.h
@@ -33,7 +33,6 @@
   void OnFileOpen(scoped_ptr<std::string> data);
 
   base::FilePath path_;
-  scoped_refptr<ImageDecoder> decoder_;
   storage::CopyOrMoveFileValidator::ResultCallback callback_;
   base::WeakPtrFactory<SupportedImageTypeValidator> weak_factory_;
 
diff --git a/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc b/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc
index b14461db..d0b1fd9 100644
--- a/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc
+++ b/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc
@@ -287,11 +287,25 @@
       storage_name, read_only);
 }
 
-// Opens |file_path| with |flags|.
-int OpenFileDescriptor(const char* file_path, const int flags) {
+// Opens |file_path| with |flags|. Returns the result as a pair.
+// first is file descriptor.
+// second is base::File::Error. This value is set as following.
+// - When it succeeds to open a file descriptor, base::File::FILE_OK is set.
+// - When |file_path| is a directory, base::File::FILE_ERROR_NOT_A_FILE is set.
+// - When |file_path| does not exist, base::File::FILE_ERROR_NOT_FOUND is set.
+// - For other error cases, base::File::FILE_ERROR_FAILED is set.
+std::pair<int, base::File::Error> OpenFileDescriptor(const char* file_path,
+                                                     const int flags) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
 
-  return open(file_path, flags);
+  if (base::DirectoryExists(base::FilePath(file_path)))
+    return std::make_pair(-1, base::File::FILE_ERROR_NOT_A_FILE);
+  int file_descriptor = open(file_path, flags);
+  if (file_descriptor >= 0)
+    return std::make_pair(file_descriptor, base::File::FILE_OK);
+  if (errno == ENOENT)
+    return std::make_pair(file_descriptor, base::File::FILE_ERROR_NOT_FOUND);
+  return std::make_pair(file_descriptor, base::File::FILE_ERROR_FAILED);
 }
 
 // Closes |file_descriptor| on file thread.
@@ -624,17 +638,20 @@
   DCHECK(!source_file_path.empty());
   DCHECK(!device_file_path.empty());
 
-  content::BrowserThread::PostTaskAndReplyWithResult(
-      content::BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&OpenFileDescriptor,
-                 source_file_path.value().c_str(),
-                 O_RDONLY),
-      base::Bind(&MTPDeviceDelegateImplLinux::CopyFileFromLocalInternal,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 device_file_path,
-                 success_callback,
-                 error_callback));
+  // Get file info of destination file path.
+  const GetFileInfoSuccessCallback success_callback_wrapper = base::Bind(
+      &MTPDeviceDelegateImplLinux::OnDidGetDestFileInfoToCopyFileFromLocal,
+      weak_ptr_factory_.GetWeakPtr(), error_callback);
+  const ErrorCallback error_callback_wrapper = base::Bind(
+      &MTPDeviceDelegateImplLinux::OnGetDestFileInfoErrorToCopyFileFromLocal,
+      weak_ptr_factory_.GetWeakPtr(), source_file_path, device_file_path,
+      success_callback, error_callback);
+  const base::Closure closure =
+      base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
+                 weak_ptr_factory_.GetWeakPtr(), device_file_path,
+                 success_callback_wrapper, error_callback_wrapper);
+  EnsureInitAndRunTask(PendingTaskInfo(
+      device_file_path, content::BrowserThread::IO, FROM_HERE, closure));
 }
 
 void MTPDeviceDelegateImplLinux::DeleteFile(
@@ -841,7 +858,6 @@
 
   if (source_file_info.is_directory) {
     error_callback.Run(base::File::FILE_ERROR_NOT_A_FILE);
-    PendingRequestDone();
     return;
   }
 
@@ -877,23 +893,21 @@
                   base::Bind(&FakeCopyFileProgressCallback),
                   success_callback_wrapper, error_callback);
   }
-
-  PendingRequestDone();
 }
 
-void MTPDeviceDelegateImplLinux::CopyFileFromLocalInternal(
+void MTPDeviceDelegateImplLinux::OnDidOpenFDToCopyFileFromLocal(
     const base::FilePath& device_file_path,
     const CopyFileFromLocalSuccessCallback& success_callback,
     const ErrorCallback& error_callback,
-    const int source_file_descriptor) {
+    const std::pair<int, base::File::Error>& open_fd_result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
-  if (source_file_descriptor < 0) {
-    error_callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
-    PendingRequestDone();
+  if (open_fd_result.second != base::File::FILE_OK) {
+    error_callback.Run(open_fd_result.second);
     return;
   }
 
+  const int source_file_descriptor = open_fd_result.first;
   uint32 parent_id;
   if (CachedPathToId(device_file_path.DirName(), &parent_id)) {
     CopyFileFromLocalSuccessCallback success_callback_wrapper =
@@ -918,10 +932,8 @@
         base::FilePath(), content::BrowserThread::UI, FROM_HERE, closure));
   } else {
     HandleCopyFileFromLocalError(error_callback, source_file_descriptor,
-                                 base::File::FILE_ERROR_INVALID_OPERATION);
+                                 base::File::FILE_ERROR_NOT_FOUND);
   }
-
-  PendingRequestDone();
 }
 
 void MTPDeviceDelegateImplLinux::DeleteFileInternal(
@@ -940,8 +952,6 @@
     else
       error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
   }
-
-  PendingRequestDone();
 }
 
 void MTPDeviceDelegateImplLinux::DeleteDirectoryInternal(
@@ -953,14 +963,12 @@
 
   if (!file_info.is_directory) {
     error_callback.Run(base::File::FILE_ERROR_NOT_A_DIRECTORY);
-    PendingRequestDone();
     return;
   }
 
   uint32 directory_id;
   if (!CachedPathToId(file_path, &directory_id)) {
     error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
-    PendingRequestDone();
     return;
   }
 
@@ -970,7 +978,6 @@
       file_id_to_node_map_.find(directory_id);
   if (it != file_id_to_node_map_.end() && it->second->HasChildren()) {
     error_callback.Run(base::File::FILE_ERROR_NOT_EMPTY);
-    PendingRequestDone();
     return;
   }
 
@@ -989,7 +996,6 @@
       1 /* max_size */, success_callback_wrapper, error_callback_wrapper);
   EnsureInitAndRunTask(PendingTaskInfo(
       base::FilePath(), content::BrowserThread::UI, FROM_HERE, closure));
-  PendingRequestDone();
 }
 
 void MTPDeviceDelegateImplLinux::OnDidReadDirectoryToDeleteDirectory(
@@ -1193,6 +1199,39 @@
   WriteDataIntoSnapshotFile(snapshot_file_info);
 }
 
+void MTPDeviceDelegateImplLinux::OnDidGetDestFileInfoToCopyFileFromLocal(
+    const ErrorCallback& error_callback,
+    const base::File::Info& file_info) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  if (file_info.is_directory)
+    error_callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
+  else
+    error_callback.Run(base::File::FILE_ERROR_FAILED);
+}
+
+void MTPDeviceDelegateImplLinux::OnGetDestFileInfoErrorToCopyFileFromLocal(
+    const base::FilePath& source_file_path,
+    const base::FilePath& device_file_path,
+    const CopyFileFromLocalSuccessCallback& success_callback,
+    const ErrorCallback& error_callback,
+    const base::File::Error error) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  if (error != base::File::FILE_ERROR_NOT_FOUND) {
+    error_callback.Run(error);
+    return;
+  }
+
+  content::BrowserThread::PostTaskAndReplyWithResult(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&OpenFileDescriptor, source_file_path.value().c_str(),
+                 O_RDONLY),
+      base::Bind(&MTPDeviceDelegateImplLinux::OnDidOpenFDToCopyFileFromLocal,
+                 weak_ptr_factory_.GetWeakPtr(), device_file_path,
+                 success_callback, error_callback));
+}
+
 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
     uint32 dir_id,
     const ReadDirectorySuccessCallback& success_callback,
diff --git a/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h b/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h
index 016993a..4f55f7f 100644
--- a/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h
+++ b/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h
@@ -148,11 +148,11 @@
       const MoveFileLocalSuccessCallback& success_callback,
       const ErrorCallback& error_callback,
       const base::File::Info& source_file_info);
-  virtual void CopyFileFromLocalInternal(
+  virtual void OnDidOpenFDToCopyFileFromLocal(
       const base::FilePath& device_file_path,
       const CopyFileFromLocalSuccessCallback& success_callback,
       const ErrorCallback& error_callback,
-      const int source_file_descriptor);
+      const std::pair<int, base::File::Error>& open_fd_result);
   virtual void DeleteFileInternal(
       const base::FilePath& file_path,
       const DeleteFileSuccessCallback& success_callback,
@@ -242,6 +242,21 @@
       scoped_ptr<SnapshotRequestInfo> snapshot_request_info,
       const base::File::Info& file_info);
 
+  // Called when GetFileInfo() for destination path succeeded for a
+  // CopyFileFromLocal operation.
+  void OnDidGetDestFileInfoToCopyFileFromLocal(
+      const ErrorCallback& error_callback,
+      const base::File::Info& file_info);
+
+  // Called when GetFileInfo() for destination path failed to copy file from
+  // local.
+  void OnGetDestFileInfoErrorToCopyFileFromLocal(
+      const base::FilePath& source_file_path,
+      const base::FilePath& device_file_path,
+      const CopyFileFromLocalSuccessCallback& success_callback,
+      const ErrorCallback& error_callback,
+      const base::File::Error error);
+
   // Called when ReadDirectory() succeeds.
   //
   // |dir_id| is the directory read.
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index f7bdca9..8a2dc89 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/metrics/chrome_stability_metrics_provider.h"
 #include "chrome/browser/metrics/drive_metrics_provider.h"
 #include "chrome/browser/metrics/omnibox_metrics_provider.h"
+#include "chrome/browser/metrics/time_ticks_experiment_win.h"
 #include "chrome/browser/ui/browser_otr_state.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
@@ -220,8 +221,10 @@
 }
 
 void ChromeMetricsServiceClient::OnLogUploadComplete() {
-  // Collect network stats after each UMA upload.
-  network_stats_uploader_.CollectAndReportNetworkStats();
+  // Collect time ticks stats after each UMA upload.
+#if defined(OS_WIN)
+  chrome::CollectTimeTicksStats();
+#endif
 }
 
 void ChromeMetricsServiceClient::StartGatheringMetrics(
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.h b/chrome/browser/metrics/chrome_metrics_service_client.h
index c240192..4d436e9 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.h
+++ b/chrome/browser/metrics/chrome_metrics_service_client.h
@@ -13,7 +13,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "chrome/browser/metrics/metrics_memory_details.h"
-#include "chrome/browser/metrics/network_stats_uploader.h"
 #include "components/metrics/metrics_service_client.h"
 #include "components/metrics/profiler/tracking_synchronizer_observer.h"
 #include "content/public/browser/notification_observer.h"
@@ -24,6 +23,7 @@
 class GoogleUpdateMetricsProviderWin;
 class PluginMetricsProvider;
 class PrefRegistrySimple;
+class PrefService;
 
 #if !defined(OS_CHROMEOS) && !defined(OS_IOS)
 class SigninStatusMetricsProvider;
@@ -140,8 +140,6 @@
   // that has been registered with MetricsService. On other platforms, is NULL.
   ChromeOSMetricsProvider* chromeos_metrics_provider_;
 
-  NetworkStatsUploader network_stats_uploader_;
-
   // Saved callback received from CollectFinalMetrics().
   base::Closure collect_final_metrics_done_callback_;
 
diff --git a/chrome/browser/metrics/network_stats_uploader.cc b/chrome/browser/metrics/network_stats_uploader.cc
deleted file mode 100644
index 6ab6671a..0000000
--- a/chrome/browser/metrics/network_stats_uploader.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/metrics/network_stats_uploader.h"
-
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/metrics/time_ticks_experiment_win.h"
-#include "chrome/browser/net/network_stats.h"
-#include "chrome/common/net/test_server_locations.h"
-
-#if !defined(OS_POSIX)
-#include "chrome/installer/util/browser_distribution.h"
-#endif
-
-NetworkStatsUploader::NetworkStatsUploader() {
-#if defined(OS_POSIX)
-  network_stats_server_ = chrome_common_net::kEchoTestServerLocation;
-#else
-  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
-  network_stats_server_ = dist->GetNetworkStatsServer();
-#endif
-}
-
-NetworkStatsUploader::~NetworkStatsUploader() {
-}
-
-void NetworkStatsUploader::CollectAndReportNetworkStats() {
-  IOThread* io_thread = g_browser_process->io_thread();
-  if (!io_thread)
-    return;
-
-  chrome_browser_net::CollectNetworkStats(network_stats_server_, io_thread);
-#if defined(OS_WIN)
-  chrome::CollectTimeTicksStats();
-#endif
-}
diff --git a/chrome/browser/metrics/network_stats_uploader.h b/chrome/browser/metrics/network_stats_uploader.h
deleted file mode 100644
index 1765611..0000000
--- a/chrome/browser/metrics/network_stats_uploader.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_METRICS_NETWORK_STATS_UPLOADER_H_
-#define CHROME_BROWSER_METRICS_NETWORK_STATS_UPLOADER_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-
-class PrefService;
-class PrefRegistrySimple;
-
-// NetworkStatsUploader implements the collection of various network stats,
-// which is done upon successful transmission of an UMA log.
-class NetworkStatsUploader {
- public:
-  NetworkStatsUploader();
-  ~NetworkStatsUploader();
-
-  // Collects and reports various network stats to external servers.
-  void CollectAndReportNetworkStats();
-
- private:
-  // The TCP/UDP echo server to collect network connectivity stats.
-  std::string network_stats_server_;
-
-  DISALLOW_COPY_AND_ASSIGN(NetworkStatsUploader);
-};
-
-#endif  // CHROME_BROWSER_METRICS_NETWORK_STATS_UPLOADER_H_
diff --git a/chrome/browser/net/certificate_error_reporter.cc b/chrome/browser/net/certificate_error_reporter.cc
index 02672068..65c1e05 100644
--- a/chrome/browser/net/certificate_error_reporter.cc
+++ b/chrome/browser/net/certificate_error_reporter.cc
@@ -22,9 +22,11 @@
 
 CertificateErrorReporter::CertificateErrorReporter(
     net::URLRequestContext* request_context,
-    const GURL& upload_url)
-    : request_context_(request_context), upload_url_(upload_url) {
-  DCHECK(!upload_url.is_empty());
+    const GURL& upload_url,
+    CookiesPreference cookies_preference)
+    : request_context_(request_context),
+      upload_url_(upload_url),
+      cookies_preference_(cookies_preference) {
 }
 
 CertificateErrorReporter::~CertificateErrorReporter() {
@@ -34,6 +36,7 @@
 void CertificateErrorReporter::SendReport(ReportType type,
                                           const std::string& hostname,
                                           const net::SSLInfo& ssl_info) {
+  DCHECK(!upload_url_.is_empty());
   CertLoggerRequest request;
   std::string out;
 
@@ -47,8 +50,7 @@
       // TODO(estark): Double-check that the user is opted in.
       // TODO(estark): Temporarily, since this is no upload endpoint, just
       // log the information.
-      request.SerializeToString(&out);
-      DVLOG(3) << "SSL report for " << hostname << ":\n" << out << "\n\n";
+      DVLOG(1) << "Would send certificate report for " << hostname;
       break;
     default:
       NOTREACHED();
@@ -76,8 +78,10 @@
     net::URLRequestContext* context) {
   scoped_ptr<net::URLRequest> request =
       context->CreateRequest(upload_url_, net::DEFAULT_PRIORITY, this);
-  request->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
-                        net::LOAD_DO_NOT_SAVE_COOKIES);
+  if (cookies_preference_ != SEND_COOKIES) {
+    request->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
+                          net::LOAD_DO_NOT_SAVE_COOKIES);
+  }
   return request.Pass();
 }
 
diff --git a/chrome/browser/net/certificate_error_reporter.h b/chrome/browser/net/certificate_error_reporter.h
index 5463d5a7..e14f765 100644
--- a/chrome/browser/net/certificate_error_reporter.h
+++ b/chrome/browser/net/certificate_error_reporter.h
@@ -36,15 +36,21 @@
     REPORT_TYPE_EXTENDED_REPORTING
   };
 
+  // Represents whether or not to send cookies along with reports sent
+  // to the server.
+  enum CookiesPreference { SEND_COOKIES, DO_NOT_SEND_COOKIES };
+
   // Create a certificate error reporter that will send certificate
   // error reports to |upload_url|, using |request_context| as the
-  // context for the reports.
+  // context for the reports. |cookies_preference| controls whether
+  // cookies will be sent along with the reports.
   CertificateErrorReporter(net::URLRequestContext* request_context,
-                           const GURL& upload_url);
+                           const GURL& upload_url,
+                           CookiesPreference cookies_preference);
 
   ~CertificateErrorReporter() override;
 
-  // Construct, serialize, and send a certificate reporter to the report
+  // Construct, serialize, and send a certificate report to the report
   // collection server containing the |ssl_info| associated with a
   // connection to |hostname|.
   virtual void SendReport(ReportType type,
@@ -79,6 +85,8 @@
   // Owns the contained requests.
   std::set<net::URLRequest*> inflight_requests_;
 
+  CookiesPreference cookies_preference_;
+
   DISALLOW_COPY_AND_ASSIGN(CertificateErrorReporter);
 };
 
diff --git a/chrome/browser/net/certificate_error_reporter_unittest.cc b/chrome/browser/net/certificate_error_reporter_unittest.cc
index 1647776d..843050d1 100644
--- a/chrome/browser/net/certificate_error_reporter_unittest.cc
+++ b/chrome/browser/net/certificate_error_reporter_unittest.cc
@@ -201,7 +201,8 @@
   network_delegate->set_expected_url(url);
   network_delegate->ExpectHostname(report_hostname);
 
-  CertificateErrorReporter reporter(context, url);
+  CertificateErrorReporter reporter(
+      context, url, CertificateErrorReporter::DO_NOT_SEND_COOKIES);
 
   EXPECT_EQ(request_sequence_number, network_delegate->num_requests());
 
@@ -235,7 +236,8 @@
   network_delegate()->ExpectHostname(kHostname);
   network_delegate()->ExpectHostname(kSecondRequestHostname);
 
-  CertificateErrorReporter reporter(context(), url);
+  CertificateErrorReporter reporter(
+      context(), url, CertificateErrorReporter::DO_NOT_SEND_COOKIES);
 
   EXPECT_EQ(0, network_delegate()->num_requests());
 
@@ -264,8 +266,8 @@
 
   EXPECT_EQ(0, network_delegate()->num_requests());
 
-  scoped_ptr<CertificateErrorReporter> reporter(
-      new CertificateErrorReporter(context(), url));
+  scoped_ptr<CertificateErrorReporter> reporter(new CertificateErrorReporter(
+      context(), url, CertificateErrorReporter::DO_NOT_SEND_COOKIES));
   reporter->SendReport(CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION,
                        kHostname, GetTestSSLInfo());
   reporter.reset();
diff --git a/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc b/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc
index 2759a97b..1c6dd48 100644
--- a/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc
+++ b/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc
@@ -25,7 +25,8 @@
     net::URLRequestContext* request_context)
     : certificate_reporter_(new CertificateErrorReporter(
           request_context,
-          GURL(kFraudulentCertificateUploadEndpoint))) {
+          GURL(kFraudulentCertificateUploadEndpoint),
+          CertificateErrorReporter::DO_NOT_SEND_COOKIES)) {
 }
 
 ChromeFraudulentCertificateReporter::ChromeFraudulentCertificateReporter(
diff --git a/chrome/browser/net/chrome_fraudulent_certificate_reporter_unittest.cc b/chrome/browser/net/chrome_fraudulent_certificate_reporter_unittest.cc
index 13aa5ef..f3ecf48 100644
--- a/chrome/browser/net/chrome_fraudulent_certificate_reporter_unittest.cc
+++ b/chrome/browser/net/chrome_fraudulent_certificate_reporter_unittest.cc
@@ -118,7 +118,10 @@
 class MockReporter : public CertificateErrorReporter {
  public:
   explicit MockReporter(net::URLRequestContext* request_context)
-      : CertificateErrorReporter(request_context, GURL("http://example.com")) {}
+      : CertificateErrorReporter(
+            request_context,
+            GURL("http://example.com"),
+            CertificateErrorReporter::DO_NOT_SEND_COOKIES) {}
 
   void SendReport(ReportType type,
                   const std::string& hostname,
diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc
index fbc7d30..7f87867 100644
--- a/chrome/browser/net/chrome_network_delegate.cc
+++ b/chrome/browser/net/chrome_network_delegate.cc
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/base_paths.h"
-#include "base/command_line.h"
 #include "base/debug/alias.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/debug/stack_trace.h"
@@ -39,7 +38,6 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/resource_request_info.h"
-#include "content/public/common/content_switches.h"
 #include "content/public/common/process_type.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
@@ -59,6 +57,7 @@
 #endif
 
 #if defined(OS_CHROMEOS)
+#include "base/command_line.h"
 #include "base/sys_info.h"
 #include "chrome/common/chrome_switches.h"
 #endif
@@ -292,9 +291,6 @@
       url_blacklist_manager_(NULL),
 #endif
       domain_reliability_monitor_(NULL),
-      experimental_web_platform_features_enabled_(
-          base::CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kEnableExperimentalWebPlatformFeatures)),
       first_request_(true) {
   DCHECK(enable_referrers);
   extensions_delegate_.reset(
@@ -705,10 +701,6 @@
   return privacy_mode;
 }
 
-bool ChromeNetworkDelegate::OnFirstPartyOnlyCookieExperimentEnabled() const {
-  return experimental_web_platform_features_enabled_;
-}
-
 bool ChromeNetworkDelegate::OnCancelURLRequestWithPolicyViolatingReferrerHeader(
     const net::URLRequest& request,
     const GURL& target_url,
diff --git a/chrome/browser/net/chrome_network_delegate.h b/chrome/browser/net/chrome_network_delegate.h
index 6265e52..68394ec2 100644
--- a/chrome/browser/net/chrome_network_delegate.h
+++ b/chrome/browser/net/chrome_network_delegate.h
@@ -176,7 +176,6 @@
   bool OnCanEnablePrivacyMode(
       const GURL& url,
       const GURL& first_party_for_cookies) const override;
-  bool OnFirstPartyOnlyCookieExperimentEnabled() const override;
   bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(
       const net::URLRequest& request,
       const GURL& target_url,
@@ -218,8 +217,6 @@
   // static anyway since it is based on a command-line flag.
   static bool g_never_throttle_requests_;
 
-  bool experimental_web_platform_features_enabled_;
-
   bool first_request_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeNetworkDelegate);
diff --git a/chrome/browser/net/chrome_network_delegate_unittest.cc b/chrome/browser/net/chrome_network_delegate_unittest.cc
index 3136bc7..dbf7040 100644
--- a/chrome/browser/net/chrome_network_delegate_unittest.cc
+++ b/chrome/browser/net/chrome_network_delegate_unittest.cc
@@ -25,36 +25,6 @@
 #include "chrome/browser/extensions/event_router_forwarder.h"
 #endif
 
-TEST(ChromeNetworkDelegateTest, DisableFirstPartyOnlyCookiesIffFlagDisabled) {
-  BooleanPrefMember pref_member_;
-  scoped_ptr<ChromeNetworkDelegate> delegate;
-
-#if defined(ENABLE_EXTENSIONS)
-  scoped_refptr<extensions::EventRouterForwarder> forwarder =
-      new extensions::EventRouterForwarder();
-  delegate.reset(new ChromeNetworkDelegate(forwarder.get(), &pref_member_));
-#else
-  delegate.reset(new ChromeNetworkDelegate(nullptr, &pref_member_));
-#endif
-  EXPECT_FALSE(delegate->FirstPartyOnlyCookieExperimentEnabled());
-}
-
-TEST(ChromeNetworkDelegateTest, EnableFirstPartyOnlyCookiesIffFlagEnabled) {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalWebPlatformFeatures);
-  BooleanPrefMember pref_member_;
-  scoped_ptr<ChromeNetworkDelegate> delegate;
-
-#if defined(ENABLE_EXTENSIONS)
-  scoped_refptr<extensions::EventRouterForwarder> forwarder =
-      new extensions::EventRouterForwarder();
-  delegate.reset(new ChromeNetworkDelegate(forwarder.get(), &pref_member_));
-#else
-  delegate.reset(new ChromeNetworkDelegate(nullptr, &pref_member_));
-#endif
-  EXPECT_TRUE(delegate->FirstPartyOnlyCookieExperimentEnabled());
-}
-
 #if defined(ENABLE_EXTENSIONS)
 class ChromeNetworkDelegateThrottlingTest : public testing::Test {
  protected:
diff --git a/chrome/browser/net/network_stats.cc b/chrome/browser/net/network_stats.cc
deleted file mode 100644
index 51df4c2..0000000
--- a/chrome/browser/net/network_stats.cc
+++ /dev/null
@@ -1,962 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/net/network_stats.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram.h"
-#include "base/profiler/scoped_tracker.h"
-#include "base/rand_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/time/time.h"
-#include "chrome/common/chrome_version_info.h"
-#include "content/public/browser/browser_thread.h"
-#include "net/base/load_flags.h"
-#include "net/base/net_errors.h"
-#include "net/base/network_change_notifier.h"
-#include "net/base/test_completion_callback.h"
-#include "net/dns/single_request_host_resolver.h"
-#include "net/proxy/proxy_service.h"
-#include "net/socket/client_socket_factory.h"
-#include "net/udp/datagram_client_socket.h"
-#include "url/gurl.h"
-
-using content::BrowserThread;
-
-namespace chrome_browser_net {
-
-// static
-uint32 NetworkStats::maximum_tests_ = 8;
-// static
-uint32 NetworkStats::maximum_sequential_packets_ = 21;
-// static
-uint32 NetworkStats::maximum_NAT_packets_ = 2;
-// static
-uint32 NetworkStats::maximum_NAT_idle_seconds_ = 300;
-// static
-bool NetworkStats::start_test_after_connect_ = true;
-
-// Specify the possible choices of probe packet sizes.
-const uint32 kProbePacketBytes[] = {100, 500, 1200};
-const uint32 kPacketSizeChoices = arraysize(kProbePacketBytes);
-
-// List of ports used for probing test.
-const uint16 kPorts[] = {443, 80};
-
-// Number of first few packets that are recorded in a packet-correlation
-// histogram, which shows exactly what sequence of packets were received.
-// We use this to deduce specific packet loss correlation.
-const uint32 kCorrelatedLossPacketCount = 6;
-
-// This specifies the maximum message (payload) size of one packet.
-const uint32 kMaxMessageSize = 1600;
-
-// This specifies the maximum udp receiver buffer size.
-const uint32 kMaxUdpReceiveBufferSize = 63000;
-
-// This specifies the maximum udp receiver buffer size.
-const uint32 kMaxUdpSendBufferSize = 4096;
-
-// This should match TestType except for the last one.
-const char* kTestName[] = {"TokenRequest", "StartPacket", "NonPacedPacket",
-                           "PacedPacket", "NATBind", "PacketSizeTest"};
-
-// Perform Pacing/Non-pacing test only if at least 2 packets are received
-// in the StartPacketTest.
-const uint32 kMinimumReceivedPacketsForPacingTest = 2;
-// Perform NAT binding test only if at least 10 packets are received.
-const uint32 kMinimumReceivedPacketsForNATTest = 10;
-
-// Maximum inter-packet pacing interval in microseconds.
-const uint32 kMaximumPacingMicros = 1000000;
-// Timeout value for getting the token.
-const uint32 kGetTokenTimeoutSeconds = 10;
-// Timeout value for StartPacket and NonPacedPacket if the client does not get
-// reply. For PacedPacket test, the timeout value is this number plus the total
-// pacing interval.
-const uint32 kReadDataTimeoutSeconds = 30;
-// This is the timeout for NAT without Idle periods.
-// For NAT test with idle periods, the timeout is the Idle period + this value.
-const uint32 kReadNATTimeoutSeconds = 10;
-// This is the timeout for PACKET_SIZE_TEST.
-const uint32 kReadPacketSizeTimeoutSeconds = 10;
-// This is the maxmium number of packets we would send for PACKET_SIZE_TEST.
-uint32 kMaximumPacketSizeTestPackets = 1;
-
-// These helper functions are similar to UMA_HISTOGRAM_XXX except that they do
-// not create a static histogram_pointer.
-void DynamicHistogramEnumeration(const std::string& name,
-                                 uint32 sample,
-                                 uint32 boundary_value) {
-  base::HistogramBase* histogram_pointer = base::LinearHistogram::FactoryGet(
-      name,
-      1,
-      boundary_value,
-      boundary_value + 1,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram_pointer->Add(sample);
-}
-
-void DynamicHistogramTimes(const std::string& name,
-                           const base::TimeDelta& sample) {
-  base::HistogramBase* histogram_pointer = base::Histogram::FactoryTimeGet(
-      name,
-      base::TimeDelta::FromMilliseconds(1),
-      base::TimeDelta::FromSeconds(30),
-      50,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram_pointer->AddTime(sample);
-}
-
-void DynamicHistogramCounts(const std::string& name,
-                            uint32 sample,
-                            uint32 min,
-                            uint32 max,
-                            uint32 bucket_count) {
-  base::HistogramBase* histogram_pointer = base::Histogram::FactoryGet(
-      name, min, max, bucket_count,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram_pointer->Add(sample);
-}
-
-NetworkStats::NetworkStats(net::ClientSocketFactory* socket_factory)
-    : socket_factory_(socket_factory),
-      histogram_port_(0),
-      has_proxy_server_(false),
-      probe_packet_bytes_(0),
-      bytes_for_packet_size_test_(0),
-      current_test_index_(0),
-      read_state_(READ_STATE_IDLE),
-      write_state_(WRITE_STATE_IDLE),
-      weak_factory_(this) {
-  ResetData();
-}
-
-NetworkStats::~NetworkStats() {}
-
-bool NetworkStats::Start(net::HostResolver* host_resolver,
-                         const net::HostPortPair& server_host_port_pair,
-                         uint16 histogram_port,
-                         bool has_proxy_server,
-                         uint32 probe_bytes,
-                         uint32 bytes_for_packet_size_test,
-                         const net::CompletionCallback& finished_callback) {
-  DCHECK(host_resolver);
-  histogram_port_ = histogram_port;
-  has_proxy_server_ = has_proxy_server;
-  probe_packet_bytes_ = probe_bytes;
-  bytes_for_packet_size_test_ = bytes_for_packet_size_test;
-  finished_callback_ = finished_callback;
-  test_sequence_.clear();
-  test_sequence_.push_back(TOKEN_REQUEST);
-
-  ResetData();
-
-  scoped_ptr<net::SingleRequestHostResolver> resolver(
-      new net::SingleRequestHostResolver(host_resolver));
-  net::HostResolver::RequestInfo request(server_host_port_pair);
-  int rv =
-      resolver->Resolve(request,
-                        net::DEFAULT_PRIORITY,
-                        &addresses_,
-                        base::Bind(base::IgnoreResult(&NetworkStats::DoConnect),
-                                   base::Unretained(this)),
-                        net::BoundNetLog());
-  if (rv == net::ERR_IO_PENDING) {
-    resolver_.swap(resolver);
-    return true;
-  }
-  return DoConnect(rv);
-}
-
-void NetworkStats::StartOneTest() {
-  if (test_sequence_[current_test_index_] == TOKEN_REQUEST) {
-    DCHECK_EQ(WRITE_STATE_IDLE, write_state_);
-    write_buffer_ = NULL;
-    SendHelloRequest();
-  } else {
-    SendProbeRequest();
-  }
-}
-
-void NetworkStats::ResetData() {
-  DCHECK_EQ(WRITE_STATE_IDLE, write_state_);
-  write_buffer_ = NULL;
-  packets_received_mask_.reset();
-  first_arrival_time_ = base::TimeTicks();
-  last_arrival_time_ = base::TimeTicks();
-
-  packet_rtt_.clear();
-  packet_rtt_.resize(maximum_sequential_packets_);
-  probe_request_time_ = base::TimeTicks();
-  // Note: inter_arrival_time_ should not be reset here because it is used in
-  // subsequent tests.
-}
-
-bool NetworkStats::DoConnect(int result) {
-  // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("436634 NetworkStats::DoConnect"));
-
-  if (result != net::OK) {
-    TestPhaseComplete(RESOLVE_FAILED, result);
-    return false;
-  }
-
-  scoped_ptr<net::DatagramClientSocket> udp_socket =
-      socket_factory_->CreateDatagramClientSocket(
-          net::DatagramSocket::DEFAULT_BIND,
-          net::RandIntCallback(),
-          NULL,
-          net::NetLog::Source());
-  DCHECK(udp_socket);
-  DCHECK(!socket_);
-  socket_ = udp_socket.Pass();
-
-  const net::IPEndPoint& endpoint = addresses_.front();
-  int rv = socket_->Connect(endpoint);
-  if (rv < 0) {
-    TestPhaseComplete(CONNECT_FAILED, rv);
-    return false;
-  }
-
-  socket_->SetSendBufferSize(kMaxUdpSendBufferSize);
-  socket_->SetReceiveBufferSize(kMaxUdpReceiveBufferSize);
-  return ConnectComplete(rv);
-}
-
-bool NetworkStats::ConnectComplete(int result) {
-  if (result < 0) {
-    TestPhaseComplete(CONNECT_FAILED, result);
-    return false;
-  }
-
-  if (start_test_after_connect_) {
-    // Reads data for all HelloReply and all subsequent probe tests.
-    if (ReadData() != net::ERR_IO_PENDING) {
-      TestPhaseComplete(READ_FAILED, result);
-      return false;
-    }
-    SendHelloRequest();
-  } else {
-    // For unittesting. Only run the callback, do not destroy it.
-    if (!finished_callback_.is_null())
-      finished_callback_.Run(result);
-  }
-  return true;
-}
-
-void NetworkStats::SendHelloRequest() {
-  StartReadDataTimer(kGetTokenTimeoutSeconds, current_test_index_);
-  ProbePacket probe_packet;
-  probe_message_.SetPacketHeader(ProbePacket_Type_HELLO_REQUEST, &probe_packet);
-  probe_packet.set_group_id(current_test_index_);
-  std::string output = probe_message_.MakeEncodedPacket(probe_packet);
-
-  int result = SendData(output);
-  if (result < 0 && result != net::ERR_IO_PENDING)
-    TestPhaseComplete(WRITE_FAILED, result);
-}
-
-void NetworkStats::SendProbeRequest() {
-  ResetData();
-  // Use default timeout except for the NAT bind test.
-  uint32 timeout_seconds = kReadDataTimeoutSeconds;
-  uint32 number_packets = maximum_sequential_packets_;
-  uint32 probe_bytes = probe_packet_bytes_;
-  pacing_interval_ = base::TimeDelta();
-  switch (test_sequence_[current_test_index_]) {
-    case START_PACKET_TEST:
-    case NON_PACED_PACKET_TEST:
-      break;
-    case PACED_PACKET_TEST: {
-      pacing_interval_ =
-          std::min(inter_arrival_time_,
-                   base::TimeDelta::FromMicroseconds(kMaximumPacingMicros));
-      timeout_seconds += pacing_interval_.InMicroseconds() *
-                         (maximum_sequential_packets_ - 1) / 1000000;
-      break;
-    }
-    case NAT_BIND_TEST: {
-      // Make sure no integer overflow.
-      DCHECK_LE(maximum_NAT_idle_seconds_, 4000U);
-      int nat_test_idle_seconds = base::RandInt(1, maximum_NAT_idle_seconds_);
-      pacing_interval_ = base::TimeDelta::FromSeconds(nat_test_idle_seconds);
-      timeout_seconds = nat_test_idle_seconds + kReadNATTimeoutSeconds;
-      number_packets = maximum_NAT_packets_;
-      break;
-    }
-    case PACKET_SIZE_TEST: {
-      number_packets = kMaximumPacketSizeTestPackets;
-      probe_bytes = bytes_for_packet_size_test_;
-      timeout_seconds = kReadPacketSizeTimeoutSeconds;
-      break;
-    }
-    default:
-      NOTREACHED();
-      return;
-  }
-  DVLOG(1) << "NetworkStat: Probe pacing " << pacing_interval_.InMicroseconds()
-           << " microseconds. Time out " << timeout_seconds << " seconds";
-  ProbePacket probe_packet;
-  probe_message_.GenerateProbeRequest(token_,
-                                      current_test_index_,
-                                      probe_bytes,
-                                      pacing_interval_.InMicroseconds(),
-                                      number_packets,
-                                      &probe_packet);
-  std::string output = probe_message_.MakeEncodedPacket(probe_packet);
-
-  StartReadDataTimer(timeout_seconds, current_test_index_);
-  probe_request_time_ = base::TimeTicks::Now();
-  int result = SendData(output);
-  if (result < 0 && result != net::ERR_IO_PENDING)
-    TestPhaseComplete(WRITE_FAILED, result);
-}
-
-int NetworkStats::ReadData() {
-  if (!socket_.get())
-    return 0;
-
-  if (read_state_ == READ_STATE_READ_PENDING)
-    return net::ERR_IO_PENDING;
-
-  int rv = 0;
-  while (true) {
-    DCHECK(!read_buffer_.get());
-    read_buffer_ = new net::IOBuffer(kMaxMessageSize);
-
-    rv = socket_->Read(
-        read_buffer_.get(),
-        kMaxMessageSize,
-        base::Bind(&NetworkStats::OnReadComplete, weak_factory_.GetWeakPtr()));
-    if (rv <= 0)
-      break;
-    if (ReadComplete(rv))
-      return rv;
-  }
-  if (rv == net::ERR_IO_PENDING)
-    read_state_ = READ_STATE_READ_PENDING;
-  return rv;
-}
-
-void NetworkStats::OnReadComplete(int result) {
-  DCHECK_NE(net::ERR_IO_PENDING, result);
-  DCHECK_EQ(READ_STATE_READ_PENDING, read_state_);
-
-  read_state_ = READ_STATE_IDLE;
-  if (!ReadComplete(result)) {
-    // Called ReadData() via PostDelayedTask() to avoid recursion. Added a delay
-    // of 1ms so that the time-out will fire before we have time to really hog
-    // the CPU too extensively (waiting for the time-out) in case of an infinite
-    // loop.
-    base::MessageLoop::current()->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(base::IgnoreResult(&NetworkStats::ReadData),
-                   weak_factory_.GetWeakPtr()),
-        base::TimeDelta::FromMilliseconds(1));
-  }
-}
-
-bool NetworkStats::ReadComplete(int result) {
-  DCHECK(socket_.get());
-  DCHECK_NE(net::ERR_IO_PENDING, result);
-  if (result < 0) {
-    // Something is wrong, finish the test.
-    read_buffer_ = NULL;
-    TestPhaseComplete(READ_FAILED, result);
-    return true;
-  }
-
-  std::string encoded_message(read_buffer_->data(),
-                              read_buffer_->data() + result);
-  read_buffer_ = NULL;
-  ProbePacket probe_packet;
-  if (!probe_message_.ParseInput(encoded_message, &probe_packet))
-    return false;
-  // Discard if the packet is for a different test.
-  if (probe_packet.group_id() != current_test_index_)
-    return false;
-
-  // Whether all packets in the current test have been received.
-  bool current_test_complete = false;
-  switch (probe_packet.header().type()) {
-    case ProbePacket_Type_HELLO_REPLY:
-      token_ = probe_packet.token();
-      if (current_test_index_ == 0)
-        test_sequence_.push_back(START_PACKET_TEST);
-      current_test_complete = true;
-      break;
-    case ProbePacket_Type_PROBE_REPLY:
-      current_test_complete = UpdateReception(probe_packet);
-      break;
-    default:
-      DVLOG(1) << "Received unexpected packet type: "
-               << probe_packet.header().type();
-  }
-
-  if (!current_test_complete) {
-    // All packets have not been received for the current test.
-    return false;
-  }
-  // All packets are received for the current test.
-  // Read completes if all tests are done (if TestPhaseComplete didn't start
-  // another test).
-  return TestPhaseComplete(SUCCESS, net::OK);
-}
-
-bool NetworkStats::UpdateReception(const ProbePacket& probe_packet) {
-  uint32 packet_index = probe_packet.packet_index();
-  if (packet_index >= packet_rtt_.size())
-    return false;
-  packets_received_mask_.set(packet_index);
-  TestType test_type = test_sequence_[current_test_index_];
-  uint32 received_packets = packets_received_mask_.count();
-
-  base::TimeTicks current_time = base::TimeTicks::Now();
-  last_arrival_time_ = current_time;
-  if (first_arrival_time_.is_null())
-    first_arrival_time_ = current_time;
-
-  // Need to do this after updating the last_arrival_time_ since NAT_BIND_TEST
-  // and PACKET_SIZE_TEST record the SendToLastRecvDelay.
-  if (test_type == NAT_BIND_TEST) {
-    return received_packets >= maximum_NAT_packets_;
-  }
-  if (test_type == PACKET_SIZE_TEST) {
-    return received_packets >= kMaximumPacketSizeTestPackets;
-  }
-
-  base::TimeDelta rtt =
-      current_time - probe_request_time_ -
-      base::TimeDelta::FromMicroseconds(std::max(
-          static_cast<int64>(0), probe_packet.server_processing_micros()));
-  base::TimeDelta min_rtt = base::TimeDelta::FromMicroseconds(1L);
-  packet_rtt_[packet_index] = (rtt >= min_rtt) ? rtt : min_rtt;
-
-  if (received_packets < maximum_sequential_packets_)
-    return false;
-  // All packets in the current test are received.
-  inter_arrival_time_ = (last_arrival_time_ - first_arrival_time_) /
-      std::max(1U, (received_packets - 1));
-  if (test_type == START_PACKET_TEST) {
-    test_sequence_.push_back(PACKET_SIZE_TEST);
-    test_sequence_.push_back(TOKEN_REQUEST);
-    // No need to add TOKEN_REQUEST here when all packets are received.
-    test_sequence_.push_back(base::RandInt(0, 1) ? PACED_PACKET_TEST
-                                                 : NON_PACED_PACKET_TEST);
-    test_sequence_.push_back(TOKEN_REQUEST);
-    test_sequence_.push_back(NAT_BIND_TEST);
-    test_sequence_.push_back(TOKEN_REQUEST);
-  }
-  return true;
-}
-
-int NetworkStats::SendData(const std::string& output) {
-  if (write_buffer_.get() || !socket_.get() ||
-      write_state_ == WRITE_STATE_WRITE_PENDING) {
-    return net::ERR_UNEXPECTED;
-  }
-  scoped_refptr<net::StringIOBuffer> buffer(new net::StringIOBuffer(output));
-  write_buffer_ = new net::DrainableIOBuffer(buffer.get(), buffer->size());
-
-  int bytes_written = socket_->Write(
-      write_buffer_.get(),
-      write_buffer_->BytesRemaining(),
-      base::Bind(&NetworkStats::OnWriteComplete, weak_factory_.GetWeakPtr()));
-  if (bytes_written < 0) {
-    if (bytes_written == net::ERR_IO_PENDING)
-      write_state_ = WRITE_STATE_WRITE_PENDING;
-    return bytes_written;
-  }
-  UpdateSendBuffer(bytes_written);
-  return net::OK;
-}
-
-void NetworkStats::OnWriteComplete(int result) {
-  DCHECK_NE(net::ERR_IO_PENDING, result);
-  DCHECK_EQ(WRITE_STATE_WRITE_PENDING, write_state_);
-  write_state_ = WRITE_STATE_IDLE;
-  if (result < 0 || !socket_.get() || write_buffer_.get() == NULL) {
-    TestPhaseComplete(WRITE_FAILED, result);
-    return;
-  }
-  UpdateSendBuffer(result);
-}
-
-void NetworkStats::UpdateSendBuffer(int bytes_sent) {
-  write_buffer_->DidConsume(bytes_sent);
-  DCHECK_EQ(write_buffer_->BytesRemaining(), 0);
-  DCHECK_EQ(WRITE_STATE_IDLE, write_state_);
-  write_buffer_ = NULL;
-}
-
-void NetworkStats::StartReadDataTimer(uint32 seconds, uint32 test_index) {
-  base::MessageLoop::current()->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&NetworkStats::OnReadDataTimeout,
-                 weak_factory_.GetWeakPtr(),
-                 test_index),
-      base::TimeDelta::FromSeconds(seconds));
-}
-
-void NetworkStats::OnReadDataTimeout(uint32 test_index) {
-  // If the current_test_index_ has changed since we set the timeout,
-  // the current test has been completed, so do nothing.
-  if (test_index != current_test_index_)
-    return;
-  // If test_type is TOKEN_REQUEST, it will do nothing but call
-  // TestPhaseComplete().
-  TestType test_type = test_sequence_[current_test_index_];
-
-  uint32 received_packets = packets_received_mask_.count();
-  if (received_packets >= 2) {
-    inter_arrival_time_ =
-        (last_arrival_time_ - first_arrival_time_) / (received_packets - 1);
-  }
-  // Add other tests if this is START_PACKET_TEST.
-  if (test_type == START_PACKET_TEST) {
-    if (received_packets >= kMinimumReceivedPacketsForPacingTest) {
-      test_sequence_.push_back(TOKEN_REQUEST);
-      test_sequence_.push_back(PACKET_SIZE_TEST);
-      test_sequence_.push_back(TOKEN_REQUEST);
-      test_sequence_.push_back(base::RandInt(0, 1) ? PACED_PACKET_TEST
-                                                   : NON_PACED_PACKET_TEST);
-    }
-    if (received_packets >= kMinimumReceivedPacketsForNATTest) {
-      test_sequence_.push_back(TOKEN_REQUEST);
-      test_sequence_.push_back(NAT_BIND_TEST);
-      test_sequence_.push_back(TOKEN_REQUEST);
-    }
-  }
-  TestPhaseComplete(READ_TIMED_OUT, net::ERR_FAILED);
-}
-
-bool NetworkStats::TestPhaseComplete(Status status, int result) {
-  // If there is no valid token, do nothing and delete self.
-  // This includes all connection error, name resolve error, etc.
-  if (write_state_ == WRITE_STATE_WRITE_PENDING) {
-    UMA_HISTOGRAM_BOOLEAN("NetConnectivity5.TestFailed.WritePending", true);
-  } else if (status == SUCCESS || status == READ_TIMED_OUT) {
-    TestType current_test = test_sequence_[current_test_index_];
-    DCHECK_LT(current_test, TEST_TYPE_MAX);
-    if (current_test != TOKEN_REQUEST) {
-      RecordHistograms(current_test);
-    } else if (current_test_index_ > 0) {
-      if (test_sequence_[current_test_index_ - 1] == NAT_BIND_TEST) {
-        // We record the NATTestReceivedHistograms after the succeeding
-        // TokenRequest.
-        RecordNATTestReceivedHistograms(status);
-      } else if (test_sequence_[current_test_index_ - 1] == PACKET_SIZE_TEST) {
-        // We record the PacketSizeTestReceivedHistograms after the succeeding
-        // TokenRequest.
-        RecordPacketSizeTestReceivedHistograms(status);
-      }
-    }
-
-    // Move to the next test.
-    current_test = GetNextTest();
-    if (current_test_index_ <= maximum_tests_ && current_test < TEST_TYPE_MAX) {
-      DVLOG(1) << "NetworkStat: Start Probe test: " << current_test;
-      base::MessageLoop::current()->PostTask(
-          FROM_HERE,
-          base::Bind(&NetworkStats::StartOneTest, weak_factory_.GetWeakPtr()));
-      return false;
-    }
-  }
-
-  // All tests are done.
-  DoFinishCallback(result);
-
-  // Close the socket so that there are no more IO operations.
-  if (socket_.get())
-    socket_->Close();
-
-  DVLOG(1) << "NetworkStat: schedule delete self at test index "
-           << current_test_index_;
-  delete this;
-  return true;
-}
-
-NetworkStats::TestType NetworkStats::GetNextTest() {
-  ++current_test_index_;
-  if (current_test_index_ >= test_sequence_.size())
-    return TEST_TYPE_MAX;
-  return test_sequence_[current_test_index_];
-}
-
-void NetworkStats::DoFinishCallback(int result) {
-  if (!finished_callback_.is_null()) {
-    net::CompletionCallback callback = finished_callback_;
-    finished_callback_.Reset();
-    callback.Run(result);
-  }
-}
-
-void NetworkStats::RecordHistograms(TestType test_type) {
-  switch (test_type) {
-    case START_PACKET_TEST:
-    case NON_PACED_PACKET_TEST:
-    case PACED_PACKET_TEST: {
-      RecordInterArrivalHistograms(test_type);
-      RecordPacketLossSeriesHistograms(test_type);
-      RecordPacketsReceivedHistograms(test_type);
-      // Only record RTT for these packet indices.
-      uint32 rtt_indices[] = {0, 1, 2, 9, 19};
-      for (uint32 i = 0; i < arraysize(rtt_indices); ++i) {
-        if (rtt_indices[i] < packet_rtt_.size())
-          RecordRTTHistograms(test_type, rtt_indices[i]);
-      }
-      RecordSendToLastRecvDelayHistograms(test_type);
-      return;
-    }
-    case NAT_BIND_TEST:
-      RecordSendToLastRecvDelayHistograms(test_type);
-      return;
-    case PACKET_SIZE_TEST:
-      // No need to record RTT for PacketSizeTest.
-      return;
-    default:
-      DVLOG(1) << "Unexpected test type " << test_type
-               << " in RecordHistograms.";
-  }
-}
-
-void NetworkStats::RecordInterArrivalHistograms(TestType test_type) {
-  DCHECK_NE(test_type, PACKET_SIZE_TEST);
-  std::string histogram_name =
-      base::StringPrintf("NetConnectivity5.%s.Sent%d.PacketDelay.%d.%dB",
-                         kTestName[test_type],
-                         maximum_sequential_packets_,
-                         histogram_port_,
-                         probe_packet_bytes_);
-  // Record the time normalized to 20 packet inter-arrivals.
-  DynamicHistogramTimes(histogram_name, inter_arrival_time_ * 20);
-}
-
-void NetworkStats::RecordPacketsReceivedHistograms(TestType test_type) {
-  DCHECK_NE(test_type, PACKET_SIZE_TEST);
-  const char* test_name = kTestName[test_type];
-  std::string histogram_prefix = base::StringPrintf(
-      "NetConnectivity5.%s.Sent%d.", test_name, maximum_sequential_packets_);
-  std::string histogram_suffix =
-      base::StringPrintf(".%d.%dB", histogram_port_, probe_packet_bytes_);
-  std::string name = histogram_prefix + "GotAPacket" + histogram_suffix;
-  base::HistogramBase* histogram_pointer = base::BooleanHistogram::FactoryGet(
-      name, base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram_pointer->Add(packets_received_mask_.any());
-
-  DynamicHistogramEnumeration(
-      histogram_prefix + "PacketsRecv" + histogram_suffix,
-      packets_received_mask_.count(),
-      maximum_sequential_packets_ + 1);
-
-  if (!packets_received_mask_.any())
-    return;
-
-  base::HistogramBase* received_nth_packet_histogram =
-      base::Histogram::FactoryGet(
-          histogram_prefix + "RecvNthPacket" + histogram_suffix,
-          1,
-          maximum_sequential_packets_ + 1,
-          maximum_sequential_packets_ + 2,
-          base::HistogramBase::kUmaTargetedHistogramFlag);
-
-  int count = 0;
-  for (size_t j = 0; j < maximum_sequential_packets_; ++j) {
-    int packet_number = j + 1;
-    if (packets_received_mask_.test(j)) {
-      received_nth_packet_histogram->Add(packet_number);
-      ++count;
-    }
-    std::string histogram_name =
-        base::StringPrintf("%sNumRecvFromFirst%02dPackets%s",
-                           histogram_prefix.c_str(),
-                           packet_number,
-                           histogram_suffix.c_str());
-    DynamicHistogramEnumeration(histogram_name, count, packet_number + 1);
-  }
-}
-
-void NetworkStats::RecordNATTestReceivedHistograms(Status status) {
-  const char* test_name = kTestName[NAT_BIND_TEST];
-  bool test_result = status == SUCCESS;
-  std::string middle_name = test_result ? "Connectivity.Success"
-                                        : "Connectivity.Failure";
-  // Record whether the HelloRequest got reply successfully.
-  std::string histogram_name =
-      base::StringPrintf("NetConnectivity5.%s.Sent%d.%s.%d.%dB",
-                         test_name,
-                         maximum_NAT_packets_,
-                         middle_name.c_str(),
-                         histogram_port_,
-                         probe_packet_bytes_);
-  uint32 bucket_count = std::min(maximum_NAT_idle_seconds_ + 2, 50U);
-  DynamicHistogramCounts(histogram_name,
-                         pacing_interval_.InSeconds(),
-                         1,
-                         maximum_NAT_idle_seconds_ + 1,
-                         bucket_count);
-
-  // Record the NAT bind result only if the HelloRequest successfully got the
-  // token and the first NAT test packet is received.
-  if (!test_result || !packets_received_mask_.test(0))
-    return;
-
-  middle_name = packets_received_mask_.test(1) ? "Bind.Success"
-                                               : "Bind.Failure";
-  histogram_name = base::StringPrintf("NetConnectivity5.%s.Sent%d.%s.%d.%dB",
-                                      test_name,
-                                      maximum_NAT_packets_,
-                                      middle_name.c_str(),
-                                      histogram_port_,
-                                      probe_packet_bytes_);
-  DynamicHistogramCounts(histogram_name,
-                         pacing_interval_.InSeconds(),
-                         1,
-                         maximum_NAT_idle_seconds_ + 1,
-                         bucket_count);
-}
-
-void NetworkStats::RecordPacketSizeTestReceivedHistograms(Status status) {
-  const char* test_name = kTestName[PACKET_SIZE_TEST];
-  bool test_result = (status == SUCCESS && packets_received_mask_.test(0));
-  std::string middle_name = test_result ? "Connectivity.Success"
-                                        : "Connectivity.Failure";
-  // Record whether the HelloRequest got reply successfully.
-  std::string histogram_name =
-      base::StringPrintf("NetConnectivity5.%s.%s.%d",
-                         test_name,
-                         middle_name.c_str(),
-                         histogram_port_);
-  base::HistogramBase* histogram_pointer = base::LinearHistogram::FactoryGet(
-      histogram_name, kProbePacketBytes[kPacketSizeChoices - 1],
-      ProbeMessage::kMaxProbePacketBytes, 60,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram_pointer->Add(bytes_for_packet_size_test_);
-}
-
-void NetworkStats::RecordPacketLossSeriesHistograms(TestType test_type) {
-  DCHECK_NE(test_type, PACKET_SIZE_TEST);
-  const char* test_name = kTestName[test_type];
-  // Build "NetConnectivity5.<TestName>.First6.SeriesRecv.<port>.<probe_size>"
-  // histogram name. Total 3(tests) x 12 histograms.
-  std::string series_acked_histogram_name =
-      base::StringPrintf("NetConnectivity5.%s.First6.SeriesRecv.%d.%dB",
-                         test_name,
-                         histogram_port_,
-                         probe_packet_bytes_);
-  uint32 histogram_boundary = 1 << kCorrelatedLossPacketCount;
-  uint32 correlated_packet_mask =
-      (histogram_boundary - 1) & packets_received_mask_.to_ulong();
-  DynamicHistogramEnumeration(
-      series_acked_histogram_name, correlated_packet_mask, histogram_boundary);
-
-  // If we are running without a proxy, we'll generate an extra histogram with
-  // the ".NoProxy" suffix.
-  if (!has_proxy_server_) {
-    series_acked_histogram_name.append(".NoProxy");
-    DynamicHistogramEnumeration(series_acked_histogram_name,
-                                correlated_packet_mask,
-                                histogram_boundary);
-  }
-}
-
-void NetworkStats::RecordRTTHistograms(TestType test_type, uint32 index) {
-  DCHECK_NE(test_type, PACKET_SIZE_TEST);
-  DCHECK_LT(index, packet_rtt_.size());
-
-  if (!packets_received_mask_.test(index))
-    return;  // Probe packet never received.
-
-  std::string rtt_histogram_name = base::StringPrintf(
-      "NetConnectivity5.%s.Sent%d.Success.RTT.Packet%02d.%d.%dB",
-      kTestName[test_type],
-      maximum_sequential_packets_,
-      index + 1,
-      histogram_port_,
-      probe_packet_bytes_);
-  DynamicHistogramTimes(rtt_histogram_name, packet_rtt_[index]);
-}
-
-void NetworkStats::RecordSendToLastRecvDelayHistograms(TestType test_type) {
-  DCHECK_NE(test_type, PACKET_SIZE_TEST);
-  if (packets_received_mask_.count() < 2)
-    return;  // Too few packets are received.
-  uint32 packets_sent = test_type == NAT_BIND_TEST
-      ? maximum_NAT_packets_ : maximum_sequential_packets_;
-  std::string histogram_name = base::StringPrintf(
-      "NetConnectivity5.%s.Sent%d.SendToLastRecvDelay.%d.%dB",
-      kTestName[test_type],
-      packets_sent,
-      histogram_port_,
-      probe_packet_bytes_);
-  base::TimeDelta send_to_last_recv_time =
-      std::max(last_arrival_time_ - probe_request_time_ -
-                   pacing_interval_ * (packets_sent - 1),
-               base::TimeDelta::FromMilliseconds(0));
-  DynamicHistogramTimes(histogram_name, send_to_last_recv_time);
-}
-
-// ProxyDetector methods and members.
-ProxyDetector::ProxyDetector(net::ProxyService* proxy_service,
-                             const net::HostPortPair& server_address,
-                             OnResolvedCallback callback)
-    : proxy_service_(proxy_service),
-      server_address_(server_address),
-      callback_(callback),
-      has_pending_proxy_resolution_(false) {}
-
-ProxyDetector::~ProxyDetector() {
-  CHECK(!has_pending_proxy_resolution_);
-}
-
-void ProxyDetector::StartResolveProxy() {
-  std::string url =
-      base::StringPrintf("https://%s", server_address_.ToString().c_str());
-  GURL gurl(url);
-
-  has_pending_proxy_resolution_ = true;
-  DCHECK(proxy_service_);
-  int rv = proxy_service_->ResolveProxy(
-      gurl,
-      net::LOAD_NORMAL,
-      &proxy_info_,
-      base::Bind(&ProxyDetector::OnResolveProxyComplete,
-                 base::Unretained(this)),
-      NULL,
-      NULL,
-      net::BoundNetLog());
-  if (rv != net::ERR_IO_PENDING)
-    OnResolveProxyComplete(rv);
-}
-
-void ProxyDetector::OnResolveProxyComplete(int result) {
-  has_pending_proxy_resolution_ = false;
-  bool has_proxy_server =
-      (result == net::OK && proxy_info_.proxy_server().is_valid() &&
-       !proxy_info_.proxy_server().is_direct());
-
-  OnResolvedCallback callback = callback_;
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE, base::Bind(callback, has_proxy_server));
-
-  // TODO(rtenneti): Will we leak if ProxyResolve is cancelled (or proxy
-  // resolution never completes).
-  delete this;
-}
-
-void CollectNetworkStats(const std::string& network_stats_server,
-                         IOThread* io_thread) {
-  if (network_stats_server.empty())
-    return;
-
-  // If we are not on IO Thread, then post a task to call CollectNetworkStats on
-  // IO Thread.
-  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
-    BrowserThread::PostTask(
-        BrowserThread::IO,
-        FROM_HERE,
-        base::Bind(&CollectNetworkStats, network_stats_server, io_thread));
-    return;
-  }
-
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
-  if (net::NetworkChangeNotifier::IsOffline()) {
-    return;
-  }
-
-  CR_DEFINE_STATIC_LOCAL(scoped_refptr<base::FieldTrial>, trial, ());
-  static bool collect_stats = false;
-
-  if (!trial.get()) {
-    // Set up a field trial to collect network stats for UDP.
-    const base::FieldTrial::Probability kDivisor = 1000;
-
-    // Enable the connectivity testing for 0.5% of the users in stable channel.
-    base::FieldTrial::Probability probability_per_group = kDivisor / 200;
-
-    chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
-    if (channel == chrome::VersionInfo::CHANNEL_CANARY) {
-      // Enable the connectivity testing for 50% of the users in canary channel.
-      probability_per_group = kDivisor / 2;
-    } else if (channel == chrome::VersionInfo::CHANNEL_DEV) {
-      // Enable the connectivity testing for 10% of the users in dev channel.
-      probability_per_group = kDivisor / 10;
-    } else if (channel == chrome::VersionInfo::CHANNEL_BETA) {
-      // Enable the connectivity testing for 1% of the users in beta channel.
-      probability_per_group = kDivisor / 100;
-    }
-
-    // After July 31, 2014 builds, it will always be in default group
-    // (disable_network_stats).
-    trial = base::FieldTrialList::FactoryGetFieldTrial(
-        "NetworkConnectivity", kDivisor, "disable_network_stats",
-        2014, 7, 31, base::FieldTrial::SESSION_RANDOMIZED, NULL);
-
-    // Add option to collect_stats for NetworkConnectivity.
-    int collect_stats_group =
-        trial->AppendGroup("collect_stats", probability_per_group);
-    if (trial->group() == collect_stats_group)
-      collect_stats = true;
-  }
-
-  if (!collect_stats)
-    return;
-
-  // Run test kMaxNumberOfTests times.
-  const size_t kMaxNumberOfTests = INT_MAX;
-  static size_t number_of_tests_done = 0;
-  if (number_of_tests_done > kMaxNumberOfTests)
-    return;
-  ++number_of_tests_done;
-
-  net::HostResolver* host_resolver = io_thread->globals()->host_resolver.get();
-  DCHECK(host_resolver);
-
-  uint32 port_index = base::RandInt(0, arraysize(kPorts) - 1);
-  uint16 histogram_port = kPorts[port_index];
-  net::HostPortPair server_address(network_stats_server, histogram_port);
-
-  net::ProxyService* proxy_service =
-      io_thread->globals()->system_proxy_service.get();
-  DCHECK(proxy_service);
-
-  ProxyDetector::OnResolvedCallback callback = base::Bind(
-      &StartNetworkStatsTest, host_resolver, server_address, histogram_port);
-
-  ProxyDetector* proxy_client =
-      new ProxyDetector(proxy_service, server_address, callback);
-  proxy_client->StartResolveProxy();
-}
-
-void StartNetworkStatsTest(net::HostResolver* host_resolver,
-                           const net::HostPortPair& server_address,
-                           uint16 histogram_port,
-                           bool has_proxy_server) {
-  int probe_choice = base::RandInt(0, kPacketSizeChoices - 1);
-
-  DCHECK_LE(ProbeMessage::kMaxProbePacketBytes, kMaxMessageSize);
-  // Pick a packet size between 1200 and kMaxProbePacketBytes bytes.
-  uint32 bytes_for_packet_size_test =
-      base::RandInt(kProbePacketBytes[kPacketSizeChoices - 1],
-                    ProbeMessage::kMaxProbePacketBytes);
-
-  // |udp_stats_client| is owned and deleted in the class NetworkStats.
-  NetworkStats* udp_stats_client =
-      new NetworkStats(net::ClientSocketFactory::GetDefaultFactory());
-  udp_stats_client->Start(host_resolver,
-                          server_address,
-                          histogram_port,
-                          has_proxy_server,
-                          kProbePacketBytes[probe_choice],
-                          bytes_for_packet_size_test,
-                          net::CompletionCallback());
-}
-
-}  // namespace chrome_browser_net
diff --git a/chrome/browser/net/network_stats.h b/chrome/browser/net/network_stats.h
deleted file mode 100644
index 4ce6c4e..0000000
--- a/chrome/browser/net/network_stats.h
+++ /dev/null
@@ -1,390 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_NET_NETWORK_STATS_H_
-#define CHROME_BROWSER_NET_NETWORK_STATS_H_
-
-#include <bitset>
-#include <string>
-#include <vector>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/metrics/histogram.h"
-#include "base/time/time.h"
-#include "chrome/browser/io_thread.h"
-#include "chrome/browser/net/probe_message.h"
-#include "net/base/address_list.h"
-#include "net/base/completion_callback.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/io_buffer.h"
-#include "net/proxy/proxy_info.h"
-
-namespace net {
-class ClientSocketFactory;
-class DatagramClientSocket;
-class HostResolver;
-class SingleRequestHostResolver;
-}
-
-namespace chrome_browser_net {
-
-// This class is used for live experiment of network connectivity (for UDP)
-// metrics. A small percentage of users participate in this experiment. All
-// users (who are in the experiment) must have enabled "UMA upload".
-// In the experiments, clients request the server to send some probing packets,
-// collect some stats, and send back the results via UMA reports.
-//
-// This class collects the following stats from users who have opted in.
-// a) RTT.
-// b) packet inter-arrival time.
-// c) packet losses for correlation and FEC experiments.
-// d) packet losses for NAT binding test after idling for a certain period.
-//
-// There are three tests in one experiment. Right before each test, a
-// HelloRequest is sent to get an updated token.
-// 1. |START_PACKET_TEST|: 21 packets are sent from the server to the client
-//    without pacing.
-// 2. |PACED_PACKET_TEST| or |NON_PACED_PACKET_TEST|: After the first test,
-//    21 packets are sent from the server to the client with or without pacing.
-//    If pacing, the pacing rate is computed from the first test.
-// 3. |NAT_BIND_TEST|: 2 packets are sent from the server to the client with
-//    a randomly generated delay of 1~300 seconds.
-// At the end of these tests, we send another HelloRequest to test whether
-// the network is still connected and has not changed (e.g. from Wifi to 3g).
-
-class NetworkStats {
- public:
-  enum Status {            // Used in UMA_HISTOGRAM_ENUMERATION.
-    SUCCESS,               // Successfully received bytes from the server.
-    SOCKET_CREATE_FAILED,  // Socket creation failed.
-    RESOLVE_FAILED,        // Host resolution failed.
-    CONNECT_FAILED,        // Connection to the server failed.
-    WRITE_FAILED,          // Sending a message to the server failed.
-    READ_TIMED_OUT,        // Reading the reply from the server timed out.
-    READ_FAILED,           // Reading the reply from the server failed.
-    STATUS_MAX,            // Bounding value.
-  };
-
-  enum ReadState {         // Used to track if |socket_| has a pending read.
-    READ_STATE_IDLE,
-    READ_STATE_READ_PENDING,
-  };
-
-  enum WriteState {        // Used to track if |socket_| has a pending write.
-    WRITE_STATE_IDLE,
-    WRITE_STATE_WRITE_PENDING,
-  };
-
-  // |TestType| specifies the possible tests we may run
-  // (except for the first and the last serving as boundaries).
-  enum TestType {
-    // The first one is for requesting token, not a probe test. Put it here
-    // because we use it as a symbol for sending the HelloRequest packet to
-    // acquire the token.
-    TOKEN_REQUEST,
-    START_PACKET_TEST,      // First packet loss test (no pacing).
-    NON_PACED_PACKET_TEST,  // Packet loss test with no pacing.
-    PACED_PACKET_TEST,      // Packet loss test with pacing.
-    // Test whether NAT binding expires after some idle period.
-    NAT_BIND_TEST,
-    PACKET_SIZE_TEST,
-    TEST_TYPE_MAX,
-  };
-
-  // Pointer |socket_factory| is NOT deleted by this class.
-  explicit NetworkStats(net::ClientSocketFactory* socket_factory);
-  // NetworkStats is deleted in TestPhaseComplete() when all tests are done.
-  ~NetworkStats();
-
-  // Start the client and connect to |server|.
-  // A client will request a token and then perform several tests.
-  // When the client finishes all tests, or when an error occurs causing the
-  // client to stop, |TestPhaseComplete| will be called with a net status code.
-  // |TestPhaseComplete| will collect histogram stats.
-  // Return true if successful in starting the client.
-  bool Start(net::HostResolver* host_resolver,
-             const net::HostPortPair& server,
-             uint16 histogram_port,
-             bool has_proxy_server,
-             uint32 probe_bytes,
-             uint32 bytes_for_packet_size_test,
-             const net::CompletionCallback& callback);
-
- private:
-  friend class NetworkStatsTest;
-
-  // Start the test specified by the current_test_index_. It also resets all
-  // the book keeping data, before starting the new test.
-  void StartOneTest();
-
-  // Reset all the counters and the collected stats.
-  void ResetData();
-
-  // Callback that is called when host resolution is completed.
-  void OnResolveComplete(int result);
-
-  // Called after host is resolved. Creates UDPClientSocket and connects to the
-  // server. If successfully connected, then calls ConnectComplete() to start
-  // the network connectivity tests. Returns |false| if there is any error.
-  bool DoConnect(int result);
-
-  // This method is called after socket connection is completed. It will start
-  // the process of sending packets to |server| by calling SendHelloPacket().
-  // Return false if connection is not established (result is less than 0).
-  bool ConnectComplete(int result);
-
-  // Send a HelloRequest packet which asks for a token from the server. If
-  // a token is received, it will will add |START_PACKET_TEST| to the test
-  // queue.
-  void SendHelloRequest();
-
-  // Send a ProbeRequest packet which requests the server to send a set
-  // of Probing packets.
-  void SendProbeRequest();
-
-  // Read and process the data. Called from OnReadComplete() or ReadData().
-  // This function calls TestPhaseComplete() if there is a significant network
-  // error or if all packets in the current test are received.
-  // Return true if TestPhaseComplete() is called otherwise return false.
-  bool ReadComplete(int result);
-
-  // Callbacks when an internal IO (Read or Write) is completed.
-  void OnReadComplete(int result);
-  void OnWriteComplete(int result);
-
-  // Read data from server until an error or IO blocking occurs or reading is
-  // complete. Return the result value from socket reading and 0 if |socket_|
-  // is Null.
-  int ReadData();
-
-  // Send data contained in |str| to server.
-  // Return a negative value if IO blocking occurs or there is an error.
-  // Otherwise return net::OK.
-  int SendData(const std::string& str);
-
-  // Update the send buffer (telling it that |bytes_sent| has been sent).
-  // And reset |write_buffer_|.
-  void UpdateSendBuffer(int bytes_sent);
-
-  // Start a timer (with value |milliseconds|) for responses from the probe
-  // servers.  |test_index| is the index of the test at vector |test_sequence_|
-  // and it is used as a parameter of the timer callback.
-  void StartReadDataTimer(uint32 milliseconds, uint32 test_index);
-
-  // Called when the StartReadDataTimer fires. |test_index| specifies
-  // the index of the test. If |current_test_index_| has changed to a
-  // different value, it indicates |test_index| has completed, then
-  // this method is a no-op.
-  void OnReadDataTimeout(uint32 test_index);
-
-  // Collect network connectivity stats. This is called when all the data from
-  // server is read or when there is a failure during connect/read/write. It
-  // will either start the next phase of the test, or it will self destruct
-  // at the end of this method. Returns true if a new test wasn't started and it
-  // was self destructed.
-  bool TestPhaseComplete(Status status, int result);
-
-  // This method is called from TestPhaseComplete() and calls
-  // |finished_callback_| callback to indicate that the test has finished.
-  void DoFinishCallback(int result);
-
-  // Update counters/metrics for the given |probe_packet|.
-  // Return true if all packets for the current test are received and
-  // false otherwise.
-  bool UpdateReception(const ProbePacket& probe_packet);
-
-  // Record all histograms for current test phase, which is assumed to be
-  // complete (i.e., we are no longer waiting for packets in this phase).
-  // |test_type| is the current test_type to be recorded. |status| is the
-  // status of the current test.
-  void RecordHistograms(TestType test_type);
-
-  // Collect the following network connectivity stats when
-  // kMaximumSequentialPackets (21) packets are sent from the server.
-  // a) Client received at least one packet.
-  // b) Client received the nth packet.
-  // c) The number of packets received for each subsequence of packets 1...n.
-  void RecordPacketsReceivedHistograms(TestType test_type);
-
-  // Collect the following network connectivity stats for the first
-  // kMaximumCorrelationPackets (6) packets in a test.
-  // Success/failure of each packet, to estimate reachability for users,
-  // and to estimate if there is a probabalistic dependency in packet loss when
-  // kMaximumCorrelationPackets packets are sent consecutively.
-  void RecordPacketLossSeriesHistograms(TestType test_type);
-
-  // Collect the average inter-arrival time (scaled up by 20 times because the
-  // minimum time value in a histogram is 1ms) of a sequence of probing packets.
-  void RecordInterArrivalHistograms(TestType test_type);
-
-  // Collect the RTT for the packet specified by the |index| in the current
-  // test.
-  void RecordRTTHistograms(TestType test_type, uint32 index);
-
-  // Collect whether the second packet in the NAT test is received for the
-  // given idle time.
-  void RecordNATTestReceivedHistograms(Status status);
-
-  // Collect whether we have the requested packet size was received or not in
-  // the PACKET_SIZE_TEST test.
-  void RecordPacketSizeTestReceivedHistograms(Status status);
-
-  // Record the time duration between sending the probe request and receiving
-  // the last probe packet excluding the pacing time requested by the client.
-  // This applies to both NAT bind test and paced/non-paced packet test.
-  void RecordSendToLastRecvDelayHistograms(TestType test_type);
-
-  // Return the next test type (internally increment |current_test_index_|)
-  // in |test_sequence_|;
-  TestType GetNextTest();
-
-  // These static variables are defined so that they can be changed in testing.
-  // Maximum number of tests in one activation of the experiment.
-  static uint32 maximum_tests_;
-  // Maximum number of packets for START/PACED/NON_PACED tests.
-  static uint32 maximum_sequential_packets_;
-  // Maximum number of packets for NAT binding test.
-  static uint32 maximum_NAT_packets_;
-  // Maximum time duration between the two packets for NAT Bind testing.
-  static uint32 maximum_NAT_idle_seconds_;
-  // Whether to start the probe test immediately after connect success.
-  // Used for unittest.
-  static bool start_test_after_connect_;
-
-  // The socket handler for this session.
-  scoped_ptr<net::DatagramClientSocket> socket_;
-
-  net::ClientSocketFactory* socket_factory_;
-
-  // The read buffer used to read data from the socket.
-  scoped_refptr<net::IOBuffer> read_buffer_;
-
-  // The write buffer used to write data to the socket.
-  scoped_refptr<net::DrainableIOBuffer> write_buffer_;
-
-  // Specify the port for which we are testing the network connectivity.
-  uint16 histogram_port_;
-
-  // Specify if there is a proxy server or not.
-  bool has_proxy_server_;
-
-  // HostResolver used to find the IP addresses.
-  scoped_ptr<net::SingleRequestHostResolver> resolver_;
-
-  // Addresses filled out by HostResolver after host resolution is completed.
-  net::AddressList addresses_;
-
-  // Callback to call when test is successefully finished or whenever
-  // there is an error (this will be used by unittests to check the result).
-  net::CompletionCallback finished_callback_;
-
-  // RTTs for each packet.
-  std::vector<base::TimeDelta> packet_rtt_;
-
-  // Time when sending probe_request, used for computing RTT.
-  base::TimeTicks probe_request_time_;
-
-  // Size of the probe packets requested to be sent from servers. We don't use
-  // |probe_packet_bytes_| during PACKET_SIZE_TEST.
-  uint32 probe_packet_bytes_;
-
-  // Size of the packet requested to be sent from servers for PACKET_SIZE_TEST.
-  uint32 bytes_for_packet_size_test_;
-
-  // bitmask indicating which packets are received.
-  std::bitset<21> packets_received_mask_;
-
-  // Arrival time of the first packet in the current test.
-  base::TimeTicks first_arrival_time_;
-  // Arrival time of the most recently received packet in the current test.
-  base::TimeTicks last_arrival_time_;
-  // Average time between two consecutive packets. It is updated when either all
-  // packets are received or timeout happens in the current test.
-  base::TimeDelta inter_arrival_time_;
-  // Target time duration for sending two consecutive packets at the server.
-  // It should be 0 for StartPacket test or NonPacedPacket test. For
-  // PacedPacket test, it is derived from the inter_arrival_time_ in the
-  // previous (StartPacket) test. For NATBind test, it is randomly generated
-  // between 1 second and |maximum_NAT_idle_seconds_| seconds.
-  base::TimeDelta pacing_interval_;
-  // A list of tests that will be performed in sequence.
-  std::vector<TestType> test_sequence_;
-  uint32 current_test_index_;  // Index of the current test.
-
-  ProbeMessage probe_message_;
-
-  // Token received from server for authentication.
-  ProbePacket_Token token_;
-
-  // The state variables to track pending reads/writes.
-  ReadState read_state_;
-  WriteState write_state_;
-
-  // We use this factory to create timeout tasks for socket's ReadData.
-  base::WeakPtrFactory<NetworkStats> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(NetworkStats);
-};
-
-class ProxyDetector {
- public:
-  // Used for the callback that is called from |OnResolveProxyComplete|.
-  typedef base::Callback<void(bool)> OnResolvedCallback;
-
-  // Construct a ProxyDetector object that finds out if access to
-  // |server_address| goes through a proxy server or not. Calls the |callback|
-  // after proxy resolution is completed by currying the proxy resolution
-  // status.
-  ProxyDetector(net::ProxyService* proxy_service,
-                const net::HostPortPair& server_address,
-                OnResolvedCallback callback);
-
-  // This method uses |proxy_service_| to resolve the proxy for
-  // |server_address_|.
-  void StartResolveProxy();
-
- private:
-  // This object is deleted from |OnResolveProxyComplete|.
-  ~ProxyDetector();
-
-  // Call the |callback_| by currying the proxy resolution status.
-  void OnResolveProxyComplete(int result);
-
-  // |proxy_service_| specifies the proxy service that is to be used to find
-  // if access to |server_address_| goes through proxy server or not.
-  net::ProxyService* proxy_service_;
-
-  // |server_address_| specifies the server host and port pair for which we are
-  // trying to see if access to it, goes through proxy or not.
-  net::HostPortPair server_address_;
-
-  // |callback_| will be called after proxy resolution is completed.
-  OnResolvedCallback callback_;
-
-  // |proxy_info_| holds proxy information returned by ResolveProxy.
-  net::ProxyInfo proxy_info_;
-
-  // Indicate if there is a pending a proxy resolution. We use this to assert
-  // that there is no in-progress proxy resolution request.
-  bool has_pending_proxy_resolution_;
-  DISALLOW_COPY_AND_ASSIGN(ProxyDetector);
-};
-
-// This collects the network connectivity stats for UDP protocol for small
-// percentage of users who are participating in the experiment (by enabling
-// "UMA upload"). This method gets called only if UMA upload to the
-// server has succeeded.
-void CollectNetworkStats(const std::string& network_stats_server_url,
-                         IOThread* io_thread);
-
-// This starts a series of tests randomly selected among one of the three
-// choices of probe packet sizes: 100 Bytes, 500 Bytes, 1200 Bytes.
-void StartNetworkStatsTest(net::HostResolver* host_resolver,
-                           const net::HostPortPair& server_address,
-                           uint16 histogram_port,
-                           bool has_proxy_server);
-
-}  // namespace chrome_browser_net
-
-#endif  // CHROME_BROWSER_NET_NETWORK_STATS_H_
diff --git a/chrome/browser/net/network_stats_unittest.cc b/chrome/browser/net/network_stats_unittest.cc
deleted file mode 100644
index 52c03f1e..0000000
--- a/chrome/browser/net/network_stats_unittest.cc
+++ /dev/null
@@ -1,410 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/basictypes.h"
-#include "base/message_loop/message_loop.h"
-#include "chrome/browser/net/network_stats.h"
-#include "net/base/net_errors.h"
-#include "net/base/network_change_notifier.h"
-#include "net/base/test_completion_callback.h"
-#include "net/dns/host_resolver.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/socket/socket_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-
-using std::string;
-
-namespace chrome_browser_net {
-
-class NetworkStatsTest : public PlatformTest {
- public:
-  NetworkStatsTest() {}
-
- protected:
-  void SetUp() override {
-    net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
-    base::MessageLoop::current()->RunUntilIdle();
-    mock_writes_.clear();
-    mock_reads_.clear();
-  }
-
-  void TearDown() override {
-    net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
-    // Empty the current queue.
-    base::MessageLoop::current()->RunUntilIdle();
-    PlatformTest::TearDown();
-  }
-
-  void CreateToken(uint64 timestamp_micros,
-                   const string& hash,
-                   ProbePacket_Token* token) {
-    token->set_timestamp_micros(timestamp_micros);
-    token->mutable_hash()->assign(hash);
-  }
-
-  // DeterministicMockData defines the exact sequence of the read/write
-  // operations (specified by the last parameter of MockRead/MockWrite).
-  // |io_mode_write| is the IO mode for writing only. Reading is always async.
-  void MakeDeterministicMockData(uint32 max_tests,
-                                 uint32 max_probe_packets,
-                                 uint32 probe_bytes,
-                                 net::IoMode io_mode_write) {
-    // Only allow 0 or 1 test because the test 2 in NetworkStats is random.
-    DCHECK_LT(max_tests, 2U);
-    outputs_.resize(10);
-    ProbePacket probe_packet;
-    ProbeMessage probe_message;
-    probe_message.SetPacketHeader(ProbePacket_Type_HELLO_REQUEST,
-                                  &probe_packet);
-    probe_packet.set_group_id(0);
-    outputs_[0] = probe_message.MakeEncodedPacket(probe_packet);
-    mock_writes_.clear();
-    mock_writes_.push_back(net::MockWrite(
-        io_mode_write, &outputs_[0][0], outputs_[0].size(), 0));
-    // Add one probe_request.
-    probe_packet = ProbePacket();  // Clear all content.
-    ProbePacket_Token token;
-    CreateToken(1L, "1010", &token);
-    probe_message.GenerateProbeRequest(
-        token, 1, probe_bytes, 0, max_probe_packets, &probe_packet);
-    outputs_[1] = probe_message.MakeEncodedPacket(probe_packet);
-    mock_writes_.push_back(net::MockWrite(
-        io_mode_write, &outputs_[1][0], outputs_[1].size(), 2));
-
-    inputs_.resize(10);
-    mock_reads_.clear();
-    // Add a hello reply.
-    probe_packet = ProbePacket();  // Clear all content.
-    probe_message.SetPacketHeader(ProbePacket_Type_HELLO_REPLY, &probe_packet);
-    probe_packet.set_group_id(0);
-    CreateToken(1L, "1010", probe_packet.mutable_token());
-    inputs_[0] = probe_message.MakeEncodedPacket(probe_packet);
-    mock_reads_.push_back(
-        net::MockRead(net::ASYNC, &inputs_[0][0], inputs_[0].size(), 1));
-
-    for (uint32 i = 0; i < max_probe_packets; ++i) {
-      // Add a probe reply.
-      probe_packet = ProbePacket();  // Clear all content.
-      probe_message.SetPacketHeader(ProbePacket_Type_PROBE_REPLY,
-                                    &probe_packet);
-      int padding_size = probe_bytes - probe_packet.ByteSize() - 8;
-      probe_packet.mutable_padding()->append(
-          std::string(std::max(0, padding_size), 0));
-      probe_packet.mutable_header()->set_checksum(0);
-      probe_packet.set_group_id(1);
-      probe_packet.set_packet_index(i);
-      inputs_[1 + i] = probe_message.MakeEncodedPacket(probe_packet);
-      mock_reads_.push_back(net::MockRead(
-          net::ASYNC, &inputs_[1 + i][0], inputs_[1 + i].size(), 3 + i));
-    }
-  }
-
-  // Test NetworkStats::Start(...) method.
-  void TestStart(bool has_proxy,
-                 uint32 max_tests,
-                 uint32 max_probe_packets,
-                 uint32 bytes,
-                 net::IoMode io_mode) {
-    net::DeterministicMockClientSocketFactory mock_socket_factory;
-    MakeDeterministicMockData(max_tests, max_probe_packets, bytes, io_mode);
-    net::DeterministicSocketData test_data(
-        &mock_reads_[0], mock_reads_.size(),
-        &mock_writes_[0], mock_writes_.size());
-    mock_socket_factory.AddSocketDataProvider(&test_data);
-    NetworkStats* udp_stats_client = new NetworkStats(&mock_socket_factory);
-    udp_stats_client->maximum_tests_ = max_tests;
-    udp_stats_client->maximum_sequential_packets_ = max_probe_packets;
-
-    net::TestCompletionCallback cb;
-    scoped_ptr<net::MockHostResolver> host_resolver(
-        new net::MockHostResolver());
-    net::HostPortPair host_port_pair;
-    EXPECT_TRUE(udp_stats_client->Start(host_resolver.get(),
-                                        host_port_pair,
-                                        9999,
-                                        has_proxy,
-                                        bytes,
-                                        bytes,
-                                        cb.callback()));
-    int num_packets_run = (max_tests + 1) * 2 + max_probe_packets - 1;
-    test_data.RunFor(num_packets_run);
-    int rv = cb.WaitForResult();
-    // Check there were no errors during connect/write/read to echo UDP server.
-    EXPECT_EQ(0, rv);
-  }
-
-  // Make one write and then |max_probe_packets| reads.
-  void MakeDelayedMockData(NetworkStats::TestType test_type,
-                           uint32 probe_bytes,
-                           uint32 pacing_interval_micros,
-                           uint32 max_probe_packets,
-                           net::IoMode io_mode) {
-    outputs_.resize(1);
-    ProbePacket probe_packet;
-    ProbeMessage probe_message;
-    mock_writes_.clear();
-    ProbePacket_Token token;
-    CreateToken(2L, "2a2b", &token);
-    switch (test_type) {
-      case NetworkStats::PACKET_SIZE_TEST:
-      case NetworkStats::NON_PACED_PACKET_TEST:
-        pacing_interval_micros = 0;
-        break;
-      case NetworkStats::NAT_BIND_TEST:
-        // For NAT_BIND_TEST, we always set this to 1000000 to avoid the
-        // randomness in NetworkStats::SendProbeRequest() and to match
-        // the value chosen in TestStartOneTest() below.
-        pacing_interval_micros = 1000000;
-        break;
-      default: {}  // Do nothing here.
-    }
-    probe_message.GenerateProbeRequest(token,
-                                       1,  // current_test_index_ = 1.
-                                       probe_bytes,
-                                       pacing_interval_micros,
-                                       max_probe_packets,
-                                       &probe_packet);
-    outputs_[0] = probe_message.MakeEncodedPacket(probe_packet);
-    mock_writes_.push_back(
-        net::MockWrite(io_mode, &outputs_[0][0], outputs_[0].size()));
-
-    inputs_.resize(max_probe_packets);
-    mock_reads_.clear();
-    for (uint32 i = 0; i < max_probe_packets; ++i) {
-      // Add a probe reply.
-      probe_packet = ProbePacket();  // Clear all content.
-      probe_message.SetPacketHeader(ProbePacket_Type_PROBE_REPLY,
-                                    &probe_packet);
-      int padding_size = probe_bytes - probe_packet.ByteSize() - 8;
-      probe_packet.mutable_padding()->append(
-          std::string(std::max(0, padding_size), 0));
-      probe_packet.mutable_header()->set_checksum(0);
-      probe_packet.set_group_id(1);
-      probe_packet.set_packet_index(i);
-      inputs_[i] = probe_message.MakeEncodedPacket(probe_packet);
-      mock_reads_.push_back(
-          net::MockRead(io_mode, &inputs_[i][0], inputs_[i].size()));
-    }
-  }
-
-  // Test NetworkStats::StartOneTest(...) method.
-  void TestStartOneTest(bool has_proxy,
-                        NetworkStats::TestType test_type,
-                        uint32 bytes,
-                        uint32 interval_micros,
-                        uint32 max_probe_packets,
-                        net::IoMode io_mode) {
-
-    net::MockClientSocketFactory mock_socket_factory;
-    MakeDelayedMockData(
-        test_type, bytes, interval_micros, max_probe_packets, io_mode);
-    net::DelayedSocketData test_data(1,
-                                     &mock_reads_[0],
-                                     mock_reads_.size(),
-                                     &mock_writes_[0],
-                                     mock_writes_.size());
-    mock_socket_factory.AddSocketDataProvider(&test_data);
-    NetworkStats* udp_stats_client = new NetworkStats(&mock_socket_factory);
-    udp_stats_client->maximum_tests_ = 1;  // Only do one probe at a time.
-    udp_stats_client->maximum_sequential_packets_ = max_probe_packets;
-    udp_stats_client->maximum_NAT_packets_ = max_probe_packets;
-    // For NAT_BIND_TEST, we always set this to 1 (second) to avoid the
-    // randomness in NetworkStats::SendProbeRequest().
-    udp_stats_client->maximum_NAT_idle_seconds_ = 1;
-    udp_stats_client->start_test_after_connect_ = false;
-    udp_stats_client->inter_arrival_time_ =
-        base::TimeDelta::FromMicroseconds(interval_micros);
-    CreateToken(2L, "2a2b", &udp_stats_client->token_);
-
-    net::TestCompletionCallback cb;
-    scoped_ptr<net::MockHostResolver> host_resolver(
-        new net::MockHostResolver());
-    net::HostPortPair host_port_pair;
-    EXPECT_TRUE(udp_stats_client->Start(host_resolver.get(),
-                                        host_port_pair,
-                                        9999,
-                                        has_proxy,
-                                        bytes,
-                                        bytes,
-                                        cb.callback()));
-    // Test need to be added after Start() because Start() will reset
-    // test_sequence_
-    udp_stats_client->test_sequence_.push_back(test_type);
-    udp_stats_client->current_test_index_ = 1;
-    // Wait for host resolving and check if there were no errors during
-    // connect/write/read to UDP server.
-    int rv = cb.WaitForResult();
-    EXPECT_EQ(0, rv);
-    udp_stats_client->ReadData();
-    udp_stats_client->StartOneTest();
-    rv = cb.WaitForResult();
-    EXPECT_EQ(0, rv);
-  }
-
-  base::MessageLoopForIO message_loop_;
-  std::vector<std::string> inputs_;
-  std::vector<std::string> outputs_;
-  std::vector<net::MockRead> mock_reads_;
-  std::vector<net::MockWrite> mock_writes_;
-};
-
-TEST_F(NetworkStatsTest, ProbeTest100BHasProxyGetToken) {
-  TestStart(true, 0, 1, 100, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest500BHasNoProxyGetTokenSync) {
-  TestStart(false, 0, 1, 500, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest100BHasNoProxyOneTest) {
-  TestStart(false, 1, 1, 100, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest100BHasNoProxyOneTestSync) {
-  TestStart(false, 1, 1, 100, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest100BHasProxyOneTest) {
-  TestStart(true, 1, 1, 100, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest100BHasProxyOneTestSync) {
-  TestStart(true, 1, 1, 100, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest500BHasProxyOneTest) {
-  TestStart(true, 1, 1, 500, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest500BHasNoProxyOneTestSync) {
-  TestStart(false, 1, 1, 500, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest500BHasNoProxyOneTest) {
-  TestStart(false, 1, 1, 500, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest500BHasProxyOneTestSync) {
-  TestStart(true, 1, 1, 500, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest1200BHasProxyOneTest) {
-  TestStart(true, 1, 1, 1200, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest1200BHasNoProxyOneTestSync) {
-  TestStart(false, 1, 1, 1200, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest1200BHasNoProxyOneTest) {
-  TestStart(false, 1, 1, 1200, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest1200BHasProxyOneTestSync) {
-  TestStart(true, 1, 1, 1200, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest100BHasNoProxyOneTestMultiPackets) {
-  TestStart(false, 1, 4, 100, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, ProbeTest1200BHasProxyOneTestMultiPacketsSync) {
-  TestStart(true, 1, 4, 1200, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, StartNonPacedTest100BHasProxy) {
-  TestStartOneTest(
-      true, NetworkStats::NON_PACED_PACKET_TEST, 100, 0, 1, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, StartNonPacedTest100BHasNoProxySync) {
-  TestStartOneTest(
-      false, NetworkStats::NON_PACED_PACKET_TEST, 100, 0, 1, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, StartNonPacedTest500BHasNoProxy) {
-  TestStartOneTest(
-      false, NetworkStats::NON_PACED_PACKET_TEST, 500, 3, 1, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, StartNonPacedTest1200BHasProxySync) {
-  TestStartOneTest(
-      true, NetworkStats::NON_PACED_PACKET_TEST, 1200, 1, 1, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, StartNonPacedTest500BHasNoProxyMulti) {
-  TestStartOneTest(
-      false, NetworkStats::NON_PACED_PACKET_TEST, 500, 2, 3, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, StartNonPacedTest1200BHasProxySyncMulti) {
-  TestStartOneTest(
-      true, NetworkStats::NON_PACED_PACKET_TEST, 1200, 1, 4, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, StartPacedTest100BHasProxy) {
-  TestStartOneTest(
-      true, NetworkStats::PACED_PACKET_TEST, 100, 0, 1, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, StartPacedTest100BHasNoProxySync) {
-  TestStartOneTest(
-      false, NetworkStats::PACED_PACKET_TEST, 100, 0, 1, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, StartPacedTest500BHasNoProxy) {
-  TestStartOneTest(
-      false, NetworkStats::PACED_PACKET_TEST, 500, 3, 1, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, StartPacedTest1200BHasProxySync) {
-  TestStartOneTest(
-      true, NetworkStats::PACED_PACKET_TEST, 1200, 1, 1, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, StartPacedTest500BHasNoProxyMulti) {
-  TestStartOneTest(
-      false, NetworkStats::PACED_PACKET_TEST, 500, 2, 3, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, StartPacedTest1200BHasProxySyncMulti) {
-  TestStartOneTest(
-      true, NetworkStats::PACED_PACKET_TEST, 1200, 1, 4, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, StartNATBindTest100BHasProxy) {
-  TestStartOneTest(true, NetworkStats::NAT_BIND_TEST, 100, 0, 1, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, StartNATBindTest100BHasNoProxySync) {
-  TestStartOneTest(
-      false, NetworkStats::NAT_BIND_TEST, 100, 3, 1, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, StartNATBindTest500BHasNoProxy) {
-  TestStartOneTest(false, NetworkStats::NAT_BIND_TEST, 500, 0, 2, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, StartNATBindTest1200BHasProxySync) {
-  TestStartOneTest(
-      true, NetworkStats::NAT_BIND_TEST, 1200, 3, 2, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, StartPacketSizeTest1500BHasProxy) {
-  TestStartOneTest(
-      true, NetworkStats::PACKET_SIZE_TEST, 1500, 0, 1, net::ASYNC);
-}
-
-TEST_F(NetworkStatsTest, StartPacketSizeTest1500HasNoProxySync) {
-  TestStartOneTest(
-      false, NetworkStats::PACKET_SIZE_TEST, 1500, 0, 1, net::SYNCHRONOUS);
-}
-
-TEST_F(NetworkStatsTest, StartPacketSizeTest1500BHasNoProxy) {
-  TestStartOneTest(
-      false, NetworkStats::PACKET_SIZE_TEST, 1500, 0, 1, net::ASYNC);
-}
-
-}  // namespace chrome_browser_net
diff --git a/chrome/browser/net/spdyproxy/OWNERS b/chrome/browser/net/spdyproxy/OWNERS
index 952891ee..ab34b0e 100644
--- a/chrome/browser/net/spdyproxy/OWNERS
+++ b/chrome/browser/net/spdyproxy/OWNERS
@@ -1,3 +1,4 @@
 bengr@chromium.org
 marq@chromium.org
 sclittle@chromium.org
+jeremyim@chromium.org
diff --git a/chrome/browser/password_manager/account_chooser_infobar_delegate_android.cc b/chrome/browser/password_manager/account_chooser_infobar_delegate_android.cc
index a4654ffc..face0fe8 100644
--- a/chrome/browser/password_manager/account_chooser_infobar_delegate_android.cc
+++ b/chrome/browser/password_manager/account_chooser_infobar_delegate_android.cc
@@ -7,7 +7,7 @@
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/ui/android/infobars/account_chooser_infobar.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 
 // static
 void AccountChooserInfoBarDelegateAndroid::Create(
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 79d900d4..4605e3a 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -31,12 +31,12 @@
 #include "components/password_manager/content/browser/content_password_manager_driver.h"
 #include "components/password_manager/content/browser/password_manager_internals_service_factory.h"
 #include "components/password_manager/content/common/credential_manager_messages.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
 #include "components/password_manager/core/browser/log_receiver.h"
 #include "components/password_manager/core/browser/password_form_manager.h"
 #include "components/password_manager/core/browser/password_manager_internals_service.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "components/password_manager/core/common/password_manager_switches.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/render_view_host.h"
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index 0a497986..6f192d3 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -15,9 +15,9 @@
 #include "components/autofill/content/common/autofill_messages.h"
 #include "components/password_manager/content/browser/password_manager_internals_service_factory.h"
 #include "components/password_manager/content/common/credential_manager_messages.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
 #include "components/password_manager/core/browser/log_receiver.h"
 #include "components/password_manager/core/browser/password_manager_internals_service.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "components/password_manager/core/common/password_manager_switches.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/password_manager/credential_android.cc b/chrome/browser/password_manager/credential_android.cc
index bc4ce1b0..291102b 100644
--- a/chrome/browser/password_manager/credential_android.cc
+++ b/chrome/browser/password_manager/credential_android.cc
@@ -5,7 +5,10 @@
 #include "chrome/browser/password_manager/credential_android.h"
 
 #include "base/android/jni_string.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/grit/generated_resources.h"
 #include "jni/Credential_jni.h"
+#include "ui/base/l10n/l10n_util.h"
 
 base::android::ScopedJavaLocalRef<jobject> CreateNativeCredential(
     JNIEnv* env,
@@ -14,10 +17,16 @@
     int type) {
   using base::android::ConvertUTF16ToJavaString;
   using base::android::ConvertUTF8ToJavaString;
+  std::string federation =
+      password_form.federation_url.is_empty()
+          ? ""
+          : l10n_util::GetStringFUTF8(
+                IDS_MANAGE_PASSWORDS_IDENTITY_PROVIDER,
+                base::ASCIIToUTF16(password_form.federation_url.host()));
   return Java_Credential_createCredential(
       env, ConvertUTF16ToJavaString(env, password_form.username_value).obj(),
-      ConvertUTF16ToJavaString(env, password_form.display_name).obj(), type,
-      position);
+      ConvertUTF16ToJavaString(env, password_form.display_name).obj(),
+      ConvertUTF8ToJavaString(env, federation).obj(), type, position);
 }
 
 base::android::ScopedJavaLocalRef<jobjectArray> CreateNativeCredentialArray(
diff --git a/chrome/browser/platform_util_unittest.cc b/chrome/browser/platform_util_unittest.cc
index 16a5edc..97c5b324 100644
--- a/chrome/browser/platform_util_unittest.cc
+++ b/chrome/browser/platform_util_unittest.cc
@@ -50,19 +50,12 @@
       ScopedVector<storage::FileSystemBackend>* additional_backends) override {
     storage::ExternalMountPoints* external_mount_points =
         content::BrowserContext::GetMountPoints(browser_context);
-    scoped_refptr<content::MockSpecialStoragePolicy> special_storage_policy =
-        new content::MockSpecialStoragePolicy();
 
     // New FileSystemBackend that uses our MockSpecialStoragePolicy.
     chromeos::FileSystemBackend* backend = new chromeos::FileSystemBackend(
-        nullptr, nullptr, nullptr, special_storage_policy,
-        external_mount_points,
+        nullptr, nullptr, nullptr, external_mount_points,
         storage::ExternalMountPoints::GetSystemInstance());
     additional_backends->push_back(backend);
-
-    // The only change we need to make is to add kFileManagerAppId as a file
-    // handler.
-    special_storage_policy->AddFileHandler(file_manager::kFileManagerAppId);
   }
 };
 
diff --git a/chrome/browser/plugins/plugin_infobar_delegates.cc b/chrome/browser/plugins/plugin_infobar_delegates.cc
index 15d7b048d..198777e 100644
--- a/chrome/browser/plugins/plugin_infobar_delegates.cc
+++ b/chrome/browser/plugins/plugin_infobar_delegates.cc
@@ -213,151 +213,23 @@
   // keep replacing infobar delegates infinitely).
   if ((message_ == message) || !infobar()->owner())
     return;
-  PluginInstallerInfoBarDelegate::Replace(
-      infobar(), installer(), plugin_metadata_->Clone(), false, message);
+  Replace(infobar(), installer(), plugin_metadata_->Clone(), message);
 }
 
-
-// PluginInstallerInfoBarDelegate ---------------------------------------------
-
-void PluginInstallerInfoBarDelegate::Create(
-    InfoBarService* infobar_service,
-    PluginInstaller* installer,
-    scoped_ptr<PluginMetadata> plugin_metadata,
-    const InstallCallback& callback) {
-  base::string16 name(plugin_metadata->name());
-#if defined(OS_WIN)
-  if (base::win::IsMetroProcess()) {
-    PluginMetroModeInfoBarDelegate::Create(
-        infobar_service, PluginMetroModeInfoBarDelegate::MISSING_PLUGIN, name);
-    return;
-  }
-#endif
-  infobar_service->AddInfoBar(infobar_service->CreateConfirmInfoBar(
-      scoped_ptr<ConfirmInfoBarDelegate>(new PluginInstallerInfoBarDelegate(
-          installer, plugin_metadata.Pass(), callback, true,
-          l10n_util::GetStringFUTF16(
-              (installer->state() == PluginInstaller::INSTALLER_STATE_IDLE)
-                  ? IDS_PLUGININSTALLER_INSTALLPLUGIN_PROMPT
-                  : IDS_PLUGIN_DOWNLOADING,
-              name)))));
-}
-
-void PluginInstallerInfoBarDelegate::Replace(
+// static
+void OutdatedPluginInfoBarDelegate::Replace(
     infobars::InfoBar* infobar,
     PluginInstaller* installer,
     scoped_ptr<PluginMetadata> plugin_metadata,
-    bool new_install,
     const base::string16& message) {
   DCHECK(infobar->owner());
   infobar->owner()->ReplaceInfoBar(
       infobar,
       infobar->owner()->CreateConfirmInfoBar(
-          scoped_ptr<ConfirmInfoBarDelegate>(new PluginInstallerInfoBarDelegate(
-              installer, plugin_metadata.Pass(),
-              PluginInstallerInfoBarDelegate::InstallCallback(), new_install,
-              message))));
+          scoped_ptr<ConfirmInfoBarDelegate>(new OutdatedPluginInfoBarDelegate(
+              installer, plugin_metadata.Pass(), message))));
 }
 
-PluginInstallerInfoBarDelegate::PluginInstallerInfoBarDelegate(
-    PluginInstaller* installer,
-    scoped_ptr<PluginMetadata> plugin_metadata,
-    const InstallCallback& callback,
-    bool new_install,
-    const base::string16& message)
-    : ConfirmInfoBarDelegate(),
-      WeakPluginInstallerObserver(installer),
-      plugin_metadata_(plugin_metadata.Pass()),
-      callback_(callback),
-      new_install_(new_install),
-      message_(message) {
-}
-
-PluginInstallerInfoBarDelegate::~PluginInstallerInfoBarDelegate() {
-}
-
-int PluginInstallerInfoBarDelegate::GetIconID() const {
-  return IDR_INFOBAR_PLUGIN_INSTALL;
-}
-
-base::string16 PluginInstallerInfoBarDelegate::GetMessageText() const {
-  return message_;
-}
-
-int PluginInstallerInfoBarDelegate::GetButtons() const {
-  return callback_.is_null() ? BUTTON_NONE : BUTTON_OK;
-}
-
-base::string16 PluginInstallerInfoBarDelegate::GetButtonLabel(
-    InfoBarButton button) const {
-  DCHECK_EQ(BUTTON_OK, button);
-  return l10n_util::GetStringUTF16(IDS_PLUGININSTALLER_INSTALLPLUGIN_BUTTON);
-}
-
-bool PluginInstallerInfoBarDelegate::Accept() {
-  callback_.Run(plugin_metadata_.get());
-  return false;
-}
-
-base::string16 PluginInstallerInfoBarDelegate::GetLinkText() const {
-  return l10n_util::GetStringUTF16(new_install_ ?
-      IDS_PLUGININSTALLER_PROBLEMSINSTALLING :
-      IDS_PLUGININSTALLER_PROBLEMSUPDATING);
-}
-
-bool PluginInstallerInfoBarDelegate::LinkClicked(
-    WindowOpenDisposition disposition) {
-  GURL url(plugin_metadata_->help_url());
-  if (url.is_empty()) {
-    url = GURL(
-        "https://www.google.com/support/chrome/bin/answer.py?answer=142064");
-  }
-  InfoBarService::WebContentsFromInfoBar(infobar())->OpenURL(
-      content::OpenURLParams(
-          url, content::Referrer(),
-          (disposition == CURRENT_TAB) ? NEW_FOREGROUND_TAB : disposition,
-          ui::PAGE_TRANSITION_LINK, false));
-  return false;
-}
-
-void PluginInstallerInfoBarDelegate::DownloadStarted() {
-  ReplaceWithInfoBar(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOADING,
-                                                plugin_metadata_->name()));
-}
-
-void PluginInstallerInfoBarDelegate::DownloadCancelled() {
-  ReplaceWithInfoBar(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_CANCELLED,
-                                                plugin_metadata_->name()));
-}
-
-void PluginInstallerInfoBarDelegate::DownloadError(const std::string& message) {
-  ReplaceWithInfoBar(l10n_util::GetStringFUTF16(IDS_PLUGIN_DOWNLOAD_ERROR_SHORT,
-                                                plugin_metadata_->name()));
-}
-
-void PluginInstallerInfoBarDelegate::DownloadFinished() {
-  ReplaceWithInfoBar(
-      l10n_util::GetStringFUTF16(
-          new_install_ ? IDS_PLUGIN_INSTALLING : IDS_PLUGIN_UPDATING,
-          plugin_metadata_->name()));
-}
-
-void PluginInstallerInfoBarDelegate::OnlyWeakObserversLeft() {
-  infobar()->RemoveSelf();
-}
-
-void PluginInstallerInfoBarDelegate::ReplaceWithInfoBar(
-    const base::string16& message) {
-  // Return early if the message doesn't change. This is important in case the
-  // PluginInstaller is still iterating over its observers (otherwise we would
-  // keep replacing infobar delegates infinitely).
-  if ((message_ == message) || !infobar()->owner())
-    return;
-  Replace(infobar(), installer(), plugin_metadata_->Clone(), new_install_,
-          message);
-}
-
-
 #if defined(OS_WIN)
 
 // PluginMetroModeInfoBarDelegate ---------------------------------------------
diff --git a/chrome/browser/plugins/plugin_infobar_delegates.h b/chrome/browser/plugins/plugin_infobar_delegates.h
index 5753031..09bf110 100644
--- a/chrome/browser/plugins/plugin_infobar_delegates.h
+++ b/chrome/browser/plugins/plugin_infobar_delegates.h
@@ -55,6 +55,13 @@
                      PluginInstaller* installer,
                      scoped_ptr<PluginMetadata> metadata);
 
+  // Replaces |infobar|, which must currently be owned, with an infobar asking
+  // the user to update a particular plugin.
+  static void Replace(infobars::InfoBar* infobar,
+                      PluginInstaller* installer,
+                      scoped_ptr<PluginMetadata> plugin_metadata,
+                      const base::string16& message);
+
  private:
   OutdatedPluginInfoBarDelegate(PluginInstaller* installer,
                                 scoped_ptr<PluginMetadata> metadata,
@@ -89,72 +96,6 @@
 
   DISALLOW_COPY_AND_ASSIGN(OutdatedPluginInfoBarDelegate);
 };
-
-// The main purpose for this class is to popup/close the infobar when there is
-// a missing plugin.
-class PluginInstallerInfoBarDelegate : public ConfirmInfoBarDelegate,
-                                       public WeakPluginInstallerObserver {
- public:
-  typedef base::Callback<void(const PluginMetadata*)> InstallCallback;
-
-  // Shows an infobar asking whether to install the plugin represented by
-  // |installer|. When the user accepts, |callback| is called.
-  // During installation of the plugin, the infobar will change to reflect the
-  // installation state.
-  static void Create(InfoBarService* infobar_service,
-                     PluginInstaller* installer,
-                     scoped_ptr<PluginMetadata> plugin_metadata,
-                     const InstallCallback& callback);
-
-  // Replaces |infobar|, which must currently be owned, with an infobar asking
-  // the user to install or update a particular plugin.
-  static void Replace(infobars::InfoBar* infobar,
-                      PluginInstaller* installer,
-                      scoped_ptr<PluginMetadata> plugin_metadata,
-                      bool new_install,
-                      const base::string16& message);
-
- private:
-  PluginInstallerInfoBarDelegate(PluginInstaller* installer,
-                                 scoped_ptr<PluginMetadata> metadata,
-                                 const InstallCallback& callback,
-                                 bool new_install,
-                                 const base::string16& message);
-  ~PluginInstallerInfoBarDelegate() override;
-
-  // ConfirmInfoBarDelegate:
-  int GetIconID() const override;
-  base::string16 GetMessageText() const override;
-  int GetButtons() const override;
-  base::string16 GetButtonLabel(InfoBarButton button) const override;
-  bool Accept() override;
-  base::string16 GetLinkText() const override;
-  bool LinkClicked(WindowOpenDisposition disposition) override;
-
-  // PluginInstallerObserver:
-  void DownloadStarted() override;
-  void DownloadError(const std::string& message) override;
-  void DownloadCancelled() override;
-  void DownloadFinished() override;
-
-  // WeakPluginInstallerObserver:
-  void OnlyWeakObserversLeft() override;
-
-  // Replaces this infobar with one showing |message|. The new infobar will
-  // not have any buttons (and not call the callback).
-  void ReplaceWithInfoBar(const base::string16& message);
-
-  scoped_ptr<PluginMetadata> plugin_metadata_;
-
-  InstallCallback callback_;
-
-  // True iff the plugin isn't installed yet.
-  bool new_install_;
-
-  base::string16 message_;
-
-  DISALLOW_COPY_AND_ASSIGN(PluginInstallerInfoBarDelegate);
-};
 #endif  // defined(ENABLE_PLUGIN_INSTALLATION)
 
 #if defined(OS_WIN)
diff --git a/chrome/browser/plugins/plugin_observer.cc b/chrome/browser/plugins/plugin_observer.cc
index 23ba08b..e3808c6 100644
--- a/chrome/browser/plugins/plugin_observer.cc
+++ b/chrome/browser/plugins/plugin_observer.cc
@@ -338,10 +338,6 @@
                         OnBlockedOutdatedPlugin)
     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NPAPINotSupported,
                         OnNPAPINotSupported)
-#if defined(ENABLE_PLUGIN_INSTALLATION)
-    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FindMissingPlugin,
-                        OnFindMissingPlugin)
-#endif
 
     IPC_MESSAGE_UNHANDLED(return false)
   IPC_END_MESSAGE_MAP()
@@ -389,43 +385,6 @@
 }
 
 #if defined(ENABLE_PLUGIN_INSTALLATION)
-void PluginObserver::OnFindMissingPlugin(int placeholder_id,
-                                         const std::string& mime_type) {
-  std::string lang = "en-US";  // Oh yes.
-  scoped_ptr<PluginMetadata> plugin_metadata;
-  PluginInstaller* installer = NULL;
-  bool found_plugin = PluginFinder::GetInstance()->FindPlugin(
-      mime_type, lang, &installer, &plugin_metadata);
-  if (!found_plugin) {
-    Send(new ChromeViewMsg_DidNotFindMissingPlugin(placeholder_id));
-    return;
-  }
-  DCHECK(installer);
-  DCHECK(plugin_metadata.get());
-
-  plugin_placeholders_[placeholder_id] =
-      new PluginPlaceholderHost(this, placeholder_id, plugin_metadata->name(),
-                                installer);
-  PluginInstallerInfoBarDelegate::Create(
-      InfoBarService::FromWebContents(web_contents()), installer,
-      plugin_metadata.Pass(),
-      base::Bind(&PluginObserver::InstallMissingPlugin,
-                 weak_ptr_factory_.GetWeakPtr(), installer));
-}
-
-void PluginObserver::InstallMissingPlugin(
-    PluginInstaller* installer,
-    const PluginMetadata* plugin_metadata) {
-  if (plugin_metadata->url_for_display()) {
-    installer->OpenDownloadURL(plugin_metadata->plugin_url(), web_contents());
-  } else {
-    TabModalConfirmDialog::Create(
-        new ConfirmInstallDialogDelegate(
-            web_contents(), installer, plugin_metadata->Clone()),
-        web_contents());
-  }
-}
-
 void PluginObserver::OnRemovePluginPlaceholderHost(int placeholder_id) {
   std::map<int, PluginPlaceholderHost*>::iterator it =
       plugin_placeholders_.find(placeholder_id);
diff --git a/chrome/browser/plugins/plugin_observer.h b/chrome/browser/plugins/plugin_observer.h
index a084bb1..1e46bd7 100644
--- a/chrome/browser/plugins/plugin_observer.h
+++ b/chrome/browser/plugins/plugin_observer.h
@@ -50,19 +50,12 @@
 
   class PluginPlaceholderHost;
 
-#if defined(ENABLE_PLUGIN_INSTALLATION)
-  void InstallMissingPlugin(PluginInstaller* installer,
-                            const PluginMetadata* plugin_metadata);
-#endif
-
   // Message handlers:
   void OnBlockedUnauthorizedPlugin(const base::string16& name,
                                    const std::string& identifier);
   void OnBlockedOutdatedPlugin(int placeholder_id,
                                const std::string& identifier);
 #if defined(ENABLE_PLUGIN_INSTALLATION)
-  void OnFindMissingPlugin(int placeholder_id, const std::string& mime_type);
-
   void OnRemovePluginPlaceholderHost(int placeholder_id);
 #endif
   void OnOpenAboutPlugins();
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index e2af8e27..f046efc 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -545,120 +545,95 @@
           base::Bind(&PopulatePolicyHandlerParameters),
           base::Bind(&GetChromePolicyDetails)));
   for (size_t i = 0; i < arraysize(kSimplePolicyMap); ++i) {
-    handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-        new SimplePolicyHandler(kSimplePolicyMap[i].policy_name,
-                                kSimplePolicyMap[i].preference_path,
-                                kSimplePolicyMap[i].value_type)));
+    handlers->AddHandler(make_scoped_ptr(new SimplePolicyHandler(
+        kSimplePolicyMap[i].policy_name, kSimplePolicyMap[i].preference_path,
+        kSimplePolicyMap[i].value_type)));
   }
 
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new AutofillPolicyHandler()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new DefaultSearchPolicyHandler()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new IncognitoModePolicyHandler()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new ManagedBookmarksPolicyHandler(chrome_schema)));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new ProxyPolicyHandler()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new URLBlacklistPolicyHandler()));
+  handlers->AddHandler(make_scoped_ptr(new AutofillPolicyHandler()));
+  handlers->AddHandler(make_scoped_ptr(new DefaultSearchPolicyHandler()));
+  handlers->AddHandler(make_scoped_ptr(new IncognitoModePolicyHandler()));
+  handlers->AddHandler(
+      make_scoped_ptr(new ManagedBookmarksPolicyHandler(chrome_schema)));
+  handlers->AddHandler(make_scoped_ptr(new ProxyPolicyHandler()));
+  handlers->AddHandler(make_scoped_ptr(new URLBlacklistPolicyHandler()));
 
 #if defined(OS_ANDROID)
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new ContextualSearchPolicyHandlerAndroid()));
+  handlers->AddHandler(
+      make_scoped_ptr(new ContextualSearchPolicyHandlerAndroid()));
 #endif
 
 #if !defined(OS_IOS)
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new FileSelectionDialogsPolicyHandler()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new JavascriptPolicyHandler()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new NetworkPredictionPolicyHandler()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new RestoreOnStartupPolicyHandler()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new browser_sync::SyncPolicyHandler()));
+  handlers->AddHandler(
+      make_scoped_ptr(new FileSelectionDialogsPolicyHandler()));
+  handlers->AddHandler(make_scoped_ptr(new JavascriptPolicyHandler()));
+  handlers->AddHandler(make_scoped_ptr(new NetworkPredictionPolicyHandler()));
+  handlers->AddHandler(make_scoped_ptr(new RestoreOnStartupPolicyHandler()));
+  handlers->AddHandler(make_scoped_ptr(new browser_sync::SyncPolicyHandler()));
 
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new StringMappingListPolicyHandler(
-          key::kEnableDeprecatedWebPlatformFeatures,
-          prefs::kEnableDeprecatedWebPlatformFeatures,
-          base::Bind(GetDeprecatedFeaturesMap))));
+  handlers->AddHandler(make_scoped_ptr(new StringMappingListPolicyHandler(
+      key::kEnableDeprecatedWebPlatformFeatures,
+      prefs::kEnableDeprecatedWebPlatformFeatures,
+      base::Bind(GetDeprecatedFeaturesMap))));
 #endif  // !defined(OS_IOS)
 
 #if defined(ENABLE_EXTENSIONS)
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new extensions::ExtensionListPolicyHandler(
+  handlers->AddHandler(
+      make_scoped_ptr(new extensions::ExtensionListPolicyHandler(
           key::kExtensionInstallWhitelist,
-          extensions::pref_names::kInstallAllowList,
-          false)));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new extensions::ExtensionListPolicyHandler(
+          extensions::pref_names::kInstallAllowList, false)));
+  handlers->AddHandler(
+      make_scoped_ptr(new extensions::ExtensionListPolicyHandler(
           key::kExtensionInstallBlacklist,
-          extensions::pref_names::kInstallDenyList,
-          true)));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
+          extensions::pref_names::kInstallDenyList, true)));
+  handlers->AddHandler(make_scoped_ptr(
       new extensions::ExtensionInstallForcelistPolicyHandler()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new extensions::ExtensionURLPatternListPolicyHandler(
+  handlers->AddHandler(
+      make_scoped_ptr(new extensions::ExtensionURLPatternListPolicyHandler(
           key::kExtensionInstallSources,
           extensions::pref_names::kAllowedInstallSites)));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new StringMappingListPolicyHandler(
-          key::kExtensionAllowedTypes,
-          extensions::pref_names::kAllowedTypes,
-          base::Bind(GetExtensionAllowedTypesMap))));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
+  handlers->AddHandler(make_scoped_ptr(new StringMappingListPolicyHandler(
+      key::kExtensionAllowedTypes, extensions::pref_names::kAllowedTypes,
+      base::Bind(GetExtensionAllowedTypesMap))));
+  handlers->AddHandler(make_scoped_ptr(
       new extensions::ExtensionSettingsPolicyHandler(chrome_schema)));
 #endif
 
 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) && !defined(OS_IOS)
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new DiskCacheDirPolicyHandler()));
+  handlers->AddHandler(make_scoped_ptr(new DiskCacheDirPolicyHandler()));
 
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new extensions::NativeMessagingHostListPolicyHandler(
+  handlers->AddHandler(
+      make_scoped_ptr(new extensions::NativeMessagingHostListPolicyHandler(
           key::kNativeMessagingWhitelist,
-          extensions::pref_names::kNativeMessagingWhitelist,
-          false)));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new extensions::NativeMessagingHostListPolicyHandler(
+          extensions::pref_names::kNativeMessagingWhitelist, false)));
+  handlers->AddHandler(
+      make_scoped_ptr(new extensions::NativeMessagingHostListPolicyHandler(
           key::kNativeMessagingBlacklist,
-          extensions::pref_names::kNativeMessagingBlacklist,
-          true)));
+          extensions::pref_names::kNativeMessagingBlacklist, true)));
 #endif  // !defined(OS_CHROMEOS) && !defined(OS_ANDROID) && !defined(OS_IOS)
 
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new DownloadDirPolicyHandler));
+  handlers->AddHandler(make_scoped_ptr(new DownloadDirPolicyHandler));
 
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new SimpleSchemaValidatingPolicyHandler(
-          key::kRegisteredProtocolHandlers,
-          prefs::kPolicyRegisteredProtocolHandlers,
-          chrome_schema,
-          SCHEMA_STRICT,
-          SimpleSchemaValidatingPolicyHandler::RECOMMENDED_ALLOWED,
-          SimpleSchemaValidatingPolicyHandler::MANDATORY_PROHIBITED)));
+  handlers->AddHandler(make_scoped_ptr(new SimpleSchemaValidatingPolicyHandler(
+      key::kRegisteredProtocolHandlers,
+      prefs::kPolicyRegisteredProtocolHandlers, chrome_schema, SCHEMA_STRICT,
+      SimpleSchemaValidatingPolicyHandler::RECOMMENDED_ALLOWED,
+      SimpleSchemaValidatingPolicyHandler::MANDATORY_PROHIBITED)));
 #endif
 
 #if defined(OS_CHROMEOS)
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new extensions::ExtensionListPolicyHandler(
+  handlers->AddHandler(
+      make_scoped_ptr(new extensions::ExtensionListPolicyHandler(
           key::kAttestationExtensionWhitelist,
-          prefs::kAttestationExtensionWhitelist,
-          false)));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
+          prefs::kAttestationExtensionWhitelist, false)));
+  handlers->AddHandler(make_scoped_ptr(
       NetworkConfigurationPolicyHandler::CreateForDevicePolicy()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
+  handlers->AddHandler(make_scoped_ptr(
       NetworkConfigurationPolicyHandler::CreateForUserPolicy()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new PinnedLauncherAppsPolicyHandler()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new ScreenMagnifierPolicyHandler()));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
+  handlers->AddHandler(make_scoped_ptr(new PinnedLauncherAppsPolicyHandler()));
+  handlers->AddHandler(make_scoped_ptr(new ScreenMagnifierPolicyHandler()));
+  handlers->AddHandler(make_scoped_ptr(
       new LoginScreenPowerManagementPolicyHandler(chrome_schema)));
 
   ScopedVector<ConfigurationPolicyHandler>
@@ -736,68 +711,44 @@
                                 INT_MAX,
                                 true));
 
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new IntRangePolicyHandler(key::kSAMLOfflineSigninTimeLimit,
-                                prefs::kSAMLOfflineSigninTimeLimit,
-                                -1,
-                                INT_MAX,
-                                true)));
-  handlers->AddHandler(
-      make_scoped_ptr<ConfigurationPolicyHandler>(new IntRangePolicyHandler(
-          key::kLidCloseAction,
-          prefs::kPowerLidClosedAction,
-          chromeos::PowerPolicyController::ACTION_SUSPEND,
-          chromeos::PowerPolicyController::ACTION_DO_NOTHING,
-          false)));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new IntPercentageToDoublePolicyHandler(
-          key::kPresentationScreenDimDelayScale,
-          prefs::kPowerPresentationScreenDimDelayFactor,
-          100,
-          INT_MAX,
-          true)));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new IntPercentageToDoublePolicyHandler(
-          key::kUserActivityScreenDimDelayScale,
-          prefs::kPowerUserActivityScreenDimDelayFactor,
-          100,
-          INT_MAX,
-          true)));
-  handlers->AddHandler(
-      make_scoped_ptr<ConfigurationPolicyHandler>(new IntRangePolicyHandler(
-          key::kUptimeLimit, prefs::kUptimeLimit, 3600, INT_MAX, true)));
-  handlers->AddHandler(
-      make_scoped_ptr<ConfigurationPolicyHandler>(new IntRangePolicyHandler(
-          key::kDeviceLoginScreenDefaultScreenMagnifierType,
-          NULL,
-          0,
-          ui::MAGNIFIER_FULL,
-          false)));
+  handlers->AddHandler(make_scoped_ptr(new IntRangePolicyHandler(
+      key::kSAMLOfflineSigninTimeLimit, prefs::kSAMLOfflineSigninTimeLimit, -1,
+      INT_MAX, true)));
+  handlers->AddHandler(make_scoped_ptr(new IntRangePolicyHandler(
+      key::kLidCloseAction, prefs::kPowerLidClosedAction,
+      chromeos::PowerPolicyController::ACTION_SUSPEND,
+      chromeos::PowerPolicyController::ACTION_DO_NOTHING, false)));
+  handlers->AddHandler(make_scoped_ptr(new IntPercentageToDoublePolicyHandler(
+      key::kPresentationScreenDimDelayScale,
+      prefs::kPowerPresentationScreenDimDelayFactor, 100, INT_MAX, true)));
+  handlers->AddHandler(make_scoped_ptr(new IntPercentageToDoublePolicyHandler(
+      key::kUserActivityScreenDimDelayScale,
+      prefs::kPowerUserActivityScreenDimDelayFactor, 100, INT_MAX, true)));
+  handlers->AddHandler(make_scoped_ptr(new IntRangePolicyHandler(
+      key::kUptimeLimit, prefs::kUptimeLimit, 3600, INT_MAX, true)));
+  handlers->AddHandler(make_scoped_ptr(new IntRangePolicyHandler(
+      key::kDeviceLoginScreenDefaultScreenMagnifierType, NULL, 0,
+      ui::MAGNIFIER_FULL, false)));
   // TODO(binjin): Remove LegacyPoliciesDeprecatingPolicyHandler for these two
   // policies once deprecation of legacy power management policies is done.
   // http://crbug.com/346229
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new LegacyPoliciesDeprecatingPolicyHandler(
+  handlers->AddHandler(
+      make_scoped_ptr(new LegacyPoliciesDeprecatingPolicyHandler(
           power_management_idle_legacy_policies.Pass(),
-          make_scoped_ptr<SchemaValidatingPolicyHandler>(
+          make_scoped_ptr(
               new PowerManagementIdleSettingsPolicyHandler(chrome_schema)))));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new LegacyPoliciesDeprecatingPolicyHandler(
+  handlers->AddHandler(
+      make_scoped_ptr(new LegacyPoliciesDeprecatingPolicyHandler(
           screen_lock_legacy_policies.Pass(),
-          make_scoped_ptr<SchemaValidatingPolicyHandler>(
-              new ScreenLockDelayPolicyHandler(chrome_schema)))));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new ExternalDataPolicyHandler(key::kUserAvatarImage)));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new ExternalDataPolicyHandler(key::kWallpaperImage)));
-  handlers->AddHandler(make_scoped_ptr<ConfigurationPolicyHandler>(
-      new SimpleSchemaValidatingPolicyHandler(
-          key::kSessionLocales,
-          NULL,
-          chrome_schema,
-          SCHEMA_STRICT,
-          SimpleSchemaValidatingPolicyHandler::RECOMMENDED_ALLOWED,
-          SimpleSchemaValidatingPolicyHandler::MANDATORY_PROHIBITED)));
+          make_scoped_ptr(new ScreenLockDelayPolicyHandler(chrome_schema)))));
+  handlers->AddHandler(
+      make_scoped_ptr(new ExternalDataPolicyHandler(key::kUserAvatarImage)));
+  handlers->AddHandler(
+      make_scoped_ptr(new ExternalDataPolicyHandler(key::kWallpaperImage)));
+  handlers->AddHandler(make_scoped_ptr(new SimpleSchemaValidatingPolicyHandler(
+      key::kSessionLocales, NULL, chrome_schema, SCHEMA_STRICT,
+      SimpleSchemaValidatingPolicyHandler::RECOMMENDED_ALLOWED,
+      SimpleSchemaValidatingPolicyHandler::MANDATORY_PROHIBITED)));
 #endif  // defined(OS_CHROMEOS)
 
   return handlers.Pass();
diff --git a/chrome/browser/prefs/leveldb_pref_store.cc b/chrome/browser/prefs/leveldb_pref_store.cc
index f397b08..c0af1038 100644
--- a/chrome/browser/prefs/leveldb_pref_store.cc
+++ b/chrome/browser/prefs/leveldb_pref_store.cc
@@ -15,6 +15,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
 
@@ -106,6 +107,7 @@
   DCHECK_EQ(0, reading_results->error);
   leveldb::Options options;
   options.create_if_missing = true;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   leveldb::DB* db;
   while (1) {
     leveldb::Status status =
diff --git a/chrome/browser/profiles/avatar_menu.h b/chrome/browser/profiles/avatar_menu.h
index a2ab430..b6bb047 100644
--- a/chrome/browser/profiles/avatar_menu.h
+++ b/chrome/browser/profiles/avatar_menu.h
@@ -66,9 +66,13 @@
     // Whether or not the current profile requires sign-in before use.
     bool signin_required;
 
-    // Whether or not the current profile is a supervised user
+    // Whether or not the current profile is a legacy supervised user profile
     // (see SupervisedUserService).
-    bool supervised;
+    bool legacy_supervised;
+
+    // Whether or not the profile is associated with a child account
+    // (see SupervisedUserService).
+    bool child_account;
 
     // The index in the menu of this profile, used by views to refer to
     // profiles.
diff --git a/chrome/browser/profiles/host_zoom_map_browsertest.cc b/chrome/browser/profiles/host_zoom_map_browsertest.cc
index c4f5730..84973b8 100644
--- a/chrome/browser/profiles/host_zoom_map_browsertest.cc
+++ b/chrome/browser/profiles/host_zoom_map_browsertest.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/memory/ref_counted.h"
@@ -30,6 +31,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/signin/core/common/profile_management_switches.h"
+#include "components/signin/core/common/signin_switches.h"
 #include "components/ui/zoom/page_zoom.h"
 #include "components/ui/zoom/zoom_event_manager.h"
 #include "content/public/test/test_utils.h"
@@ -238,8 +240,40 @@
                                 test_scheme, test_host));
 }
 
+IN_PROC_BROWSER_TEST_F(
+    HostZoomMapBrowserTest,
+    WebviewBasedSigninUsesDefaultStoragePartitionForEmbedder) {
+  GURL test_url = ConstructTestServerURL(chrome::kChromeUIChromeSigninURL);
+  std::string test_host(test_url.host());
+  std::string test_scheme(test_url.scheme());
+  ui_test_utils::NavigateToURL(browser(), test_url);
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents);
+
+  // For the webview based sign-in code, the sign in page uses the default host
+  // zoom map.
+  HostZoomMap* default_profile_host_zoom_map =
+      HostZoomMap::GetDefaultForBrowserContext(browser()->profile());
+  // Since ChromeOS still uses IFrame-based signin, we should expect the
+  // storage partition to be different if Webview signin is not enabled.
+  if (switches::IsEnableWebviewBasedSignin())
+    EXPECT_EQ(host_zoom_map, default_profile_host_zoom_map);
+  else
+    EXPECT_NE(host_zoom_map, default_profile_host_zoom_map);
+}
+
+class HostZoomMapIframeSigninBrowserTest : public HostZoomMapBrowserTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kEnableIframeBasedSignin);
+  }
+};
+
 // Regression test for crbug.com/435017.
-IN_PROC_BROWSER_TEST_F(HostZoomMapBrowserTest,
+IN_PROC_BROWSER_TEST_F(HostZoomMapIframeSigninBrowserTest,
                        EventsForNonDefaultStoragePartition) {
   ZoomLevelChangeObserver observer(browser()->profile());
   // TODO(wjmaclean): Make this test more general by implementing a way to
@@ -255,17 +289,14 @@
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
 
-  // Verify that our loaded page is using a HostZoomMap different from the
-  // one for the default StoragePartition.
+  // We are forcing non-webview based signin, so we expect the signin page to
+  // be in a different storage partition, and hence a different HostZoomMap.
   HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents);
 
-  // For the webview based sign-in code, the sign in page uses the default host
-  // zoom map.
-  if (!switches::IsEnableWebviewBasedSignin()) {
-    HostZoomMap* default_profile_host_zoom_map =
-        HostZoomMap::GetDefaultForBrowserContext(browser()->profile());
-    EXPECT_NE(host_zoom_map, default_profile_host_zoom_map);
-  }
+  EXPECT_FALSE(switches::IsEnableWebviewBasedSignin());
+  HostZoomMap* default_profile_host_zoom_map =
+      HostZoomMap::GetDefaultForBrowserContext(browser()->profile());
+  EXPECT_NE(host_zoom_map, default_profile_host_zoom_map);
 
   double new_zoom_level =
       host_zoom_map->GetZoomLevelForHostAndScheme(test_scheme, test_host) + 0.5;
diff --git a/chrome/browser/profiles/profile_downloader.cc b/chrome/browser/profiles/profile_downloader.cc
index 5fbb738c..2377772 100644
--- a/chrome/browser/profiles/profile_downloader.cc
+++ b/chrome/browser/profiles/profile_downloader.cc
@@ -193,7 +193,9 @@
 }
 
 ProfileDownloader::ProfileDownloader(ProfileDownloaderDelegate* delegate)
-    : OAuth2TokenService::Consumer("profile_downloader"),
+    : ImageRequest(
+          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)),
+      OAuth2TokenService::Consumer("profile_downloader"),
       delegate_(delegate),
       picture_status_(PICTURE_FAILED) {
   DCHECK(delegate_);
@@ -367,15 +369,10 @@
   }
 
   VLOG(1) << "Decoding the image...";
-  scoped_refptr<ImageDecoder> image_decoder = new ImageDecoder(
-      this, data, ImageDecoder::DEFAULT_CODEC);
-  scoped_refptr<base::MessageLoopProxy> task_runner =
-      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
-  image_decoder->Start(task_runner);
+  ImageDecoder::Start(this, data);
 }
 
-void ProfileDownloader::OnImageDecoded(const ImageDecoder* decoder,
-                                       const SkBitmap& decoded_image) {
+void ProfileDownloader::OnImageDecoded(const SkBitmap& decoded_image) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   int image_size = delegate_->GetDesiredImageSideLength();
   profile_picture_ = skia::ImageOperations::Resize(
@@ -387,7 +384,7 @@
   delegate_->OnProfileDownloadSuccess(this);
 }
 
-void ProfileDownloader::OnDecodeImageFailed(const ImageDecoder* decoder) {
+void ProfileDownloader::OnDecodeImageFailed() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   delegate_->OnProfileDownloadFailure(
       this, ProfileDownloaderDelegate::IMAGE_DECODE_FAILED);
diff --git a/chrome/browser/profiles/profile_downloader.h b/chrome/browser/profiles/profile_downloader.h
index aac6bb2..c21eb30 100644
--- a/chrome/browser/profiles/profile_downloader.h
+++ b/chrome/browser/profiles/profile_downloader.h
@@ -29,7 +29,7 @@
 // sandboxed process.
 class ProfileDownloader : public gaia::GaiaOAuthClient::Delegate,
                           public net::URLFetcherDelegate,
-                          public ImageDecoder::Delegate,
+                          public ImageDecoder::ImageRequest,
                           public OAuth2TokenService::Observer,
                           public OAuth2TokenService::Consumer {
  public:
@@ -94,10 +94,9 @@
   // Overriden from net::URLFetcherDelegate:
   void OnURLFetchComplete(const net::URLFetcher* source) override;
 
-  // Overriden from ImageDecoder::Delegate:
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override;
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override;
+  // Overriden from ImageDecoder::ImageRequest:
+  void OnImageDecoded(const SkBitmap& decoded_image) override;
+  void OnDecodeImageFailed() override;
 
   // Overriden from OAuth2TokenService::Observer:
   void OnRefreshTokenAvailable(const std::string& account_id) override;
diff --git a/chrome/browser/profiles/profile_list_desktop.cc b/chrome/browser/profiles/profile_list_desktop.cc
index 755794b..e1b863be 100644
--- a/chrome/browser/profiles/profile_list_desktop.cc
+++ b/chrome/browser/profiles/profile_list_desktop.cc
@@ -51,12 +51,14 @@
     item->name = profile_info_->GetNameOfProfileAtIndex(i);
     item->sync_state = profile_info_->GetUserNameOfProfileAtIndex(i);
     item->profile_path = profile_info_->GetPathOfProfileAtIndex(i);
-    item->supervised = profile_info_->ProfileIsSupervisedAtIndex(i);
+    item->legacy_supervised =
+        profile_info_->ProfileIsLegacySupervisedAtIndex(i);
+    item->child_account = profile_info_->ProfileIsChildAtIndex(i);
     item->signed_in = !item->sync_state.empty();
     if (!item->signed_in) {
       item->sync_state = l10n_util::GetStringUTF16(
-          item->supervised ? IDS_SUPERVISED_USER_AVATAR_LABEL :
-                             IDS_PROFILES_LOCAL_PROFILE_STATE);
+          item->legacy_supervised ? IDS_SUPERVISED_USER_AVATAR_LABEL :
+                                    IDS_PROFILES_LOCAL_PROFILE_STATE);
     }
     item->active = profile_info_->GetPathOfProfileAtIndex(i) ==
         active_profile_path_;
diff --git a/chrome/browser/profiles/profile_metrics.cc b/chrome/browser/profiles/profile_metrics.cc
index 2a40ed55a..0e42555 100644
--- a/chrome/browser/profiles/profile_metrics.cc
+++ b/chrome/browser/profiles/profile_metrics.cc
@@ -339,8 +339,12 @@
   DCHECK(metric < NUM_DELETE_PROFILE_METRICS);
   UMA_HISTOGRAM_ENUMERATION("Profile.DeleteProfileAction", metric,
                             NUM_DELETE_PROFILE_METRICS);
-  UMA_HISTOGRAM_ENUMERATION("Profile.NetUserCount", PROFILE_DELETED,
-                            NUM_PROFILE_NET_METRICS);
+  if (metric != DELETE_PROFILE_USER_MANAGER_SHOW_WARNING &&
+      metric != DELETE_PROFILE_SETTINGS_SHOW_WARNING) {
+    // If a user was actually deleted, update the net user count.
+    UMA_HISTOGRAM_ENUMERATION("Profile.NetUserCount", PROFILE_DELETED,
+                              NUM_PROFILE_NET_METRICS);
+  }
 }
 
 void ProfileMetrics::LogProfileOpenMethod(ProfileOpen metric) {
diff --git a/chrome/browser/profiles/profile_metrics.h b/chrome/browser/profiles/profile_metrics.h
index 7a9f844..a7fe1f40 100644
--- a/chrome/browser/profiles/profile_metrics.h
+++ b/chrome/browser/profiles/profile_metrics.h
@@ -49,8 +49,14 @@
   };
 
   enum ProfileDelete {
-    DELETE_PROFILE_SETTINGS = 0,  // Delete profile from settings page.
-    DELETE_PROFILE_USER_MANAGER,  // Delete profile from User Manager.
+    // Delete profile from settings page.
+    DELETE_PROFILE_SETTINGS = 0,
+    // Delete profile from User Manager.
+    DELETE_PROFILE_USER_MANAGER,
+    // Show the delete profile warning in the User Manager.
+    DELETE_PROFILE_USER_MANAGER_SHOW_WARNING,
+    // Show the delete profile warning in the Settings page.
+    DELETE_PROFILE_SETTINGS_SHOW_WARNING,
     NUM_DELETE_PROFILE_METRICS
   };
 
diff --git a/chrome/browser/renderer_context_menu/spelling_menu_observer.cc b/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
index 80dc097..7c37a83 100644
--- a/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
+++ b/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
@@ -37,6 +37,7 @@
     : proxy_(proxy),
       loading_frame_(0),
       succeeded_(false),
+      misspelling_hash_(0),
       client_(new SpellingServiceClient) {
   if (proxy_ && proxy_->GetBrowserContext()) {
     Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
@@ -67,6 +68,7 @@
 
   suggestions_ = params.dictionary_suggestions;
   misspelled_word_ = params.misspelled_word;
+  misspelling_hash_ = params.misspelling_hash;
 
   bool use_suggestions = SpellingServiceClient::IsAvailable(
       browser_context, SpellingServiceClient::SUGGEST);
@@ -256,6 +258,8 @@
       if (spellcheck) {
         if (spellcheck->GetMetrics())
           spellcheck->GetMetrics()->RecordReplacedWordStats(1);
+        spellcheck->GetFeedbackSender()->SelectedSuggestion(
+            misspelling_hash_, suggestion_index);
       }
     }
     return;
@@ -280,6 +284,7 @@
       if (spellcheck) {
         spellcheck->GetCustomDictionary()->AddWord(base::UTF16ToUTF8(
             misspelled_word_));
+        spellcheck->GetFeedbackSender()->AddedToDictionary(misspelling_hash_);
       }
     }
 #if defined(OS_MACOSX)
@@ -342,6 +347,17 @@
   }
 }
 
+void SpellingMenuObserver::OnMenuCancel() {
+  content::BrowserContext* browser_context = proxy_->GetBrowserContext();
+  if (!browser_context)
+    return;
+  SpellcheckService* spellcheck =
+      SpellcheckServiceFactory::GetForContext(browser_context);
+  if (!spellcheck)
+    return;
+  spellcheck->GetFeedbackSender()->IgnoredSuggestions(misspelling_hash_);
+}
+
 void SpellingMenuObserver::OnTextCheckComplete(
     SpellingServiceClient::ServiceType type,
     bool success,
diff --git a/chrome/browser/renderer_context_menu/spelling_menu_observer.h b/chrome/browser/renderer_context_menu/spelling_menu_observer.h
index e79b1e0..a29965d 100644
--- a/chrome/browser/renderer_context_menu/spelling_menu_observer.h
+++ b/chrome/browser/renderer_context_menu/spelling_menu_observer.h
@@ -48,6 +48,7 @@
   bool IsCommandIdChecked(int command_id) override;
   bool IsCommandIdEnabled(int command_id) override;
   void ExecuteCommand(int command_id) override;
+  void OnMenuCancel() override;
 
   // A callback function called when the Spelling service finishes checking a
   // misspelled word.
@@ -87,6 +88,10 @@
   // this word to the custom-word dictionary.
   base::string16 misspelled_word_;
 
+  // The hash identifier for the misspelled word. Used for collecting user
+  // feedback to spellcheck suggestions.
+  uint32 misspelling_hash_;
+
   // The string representing the result of this call. This string is a
   // suggestion when this call finished successfully. Otherwise it is error
   // text. Until we receive a response from the Spelling service, this string
diff --git a/chrome/browser/renderer_preferences_util.cc b/chrome/browser/renderer_preferences_util.cc
index 68b5f45..ce174ef 100644
--- a/chrome/browser/renderer_preferences_util.cc
+++ b/chrome/browser/renderer_preferences_util.cc
@@ -106,7 +106,7 @@
   }
 #endif
 
-#if defined(OS_LINUX) || defined(OS_ANDROID)
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_WIN)
   CR_DEFINE_STATIC_LOCAL(const gfx::FontRenderParams, params,
       (gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(true), NULL)));
   prefs->should_antialias_text = params.antialiasing;
diff --git a/chrome/browser/resources/app_list/audio_manager.js b/chrome/browser/resources/app_list/audio_manager.js
deleted file mode 100644
index aa6beb0..0000000
--- a/chrome/browser/resources/app_list/audio_manager.js
+++ /dev/null
@@ -1,107 +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.
-
-/**
- * @fileoverview The manager of audio streams.
- */
-
-cr.define('speech', function() {
-  'use strict';
-
-  /**
-   * The enum of the status of hotword audio recognition.
-   *
-   * @enum {number}
-   */
-  var AudioState = {
-    STOPPED: 0,
-    LISTENING: 1
-  };
-
-  /**
-   * @constructor
-   * @extends {cr.EventTarget}
-   */
-  function AudioManager() {
-    var audioContext = new window.AudioContext();
-    this.sampleRate = audioContext.sampleRate;
-    this.audioProc_ = null;
-    this.audioIn_ = null;
-    this.stream_ = null;
-    this.state = AudioState.STOPPED;
-  };
-
-  AudioManager.prototype.__proto__ = cr.EventTarget.prototype;
-
-  /**
-   * Called when the audio data arrives.
-   *
-   * @param {Event} audioEvent The audio event.
-   * @private
-   */
-  AudioManager.prototype.onAudioProcess_ = function(audioEvent) {
-    var data = audioEvent.inputBuffer.getChannelData(0);
-    var intData = new Int16Array(data.length);
-    for (var i = 0; i < data.length; ++i)
-      intData[i] = Math.round(data[i] * 32767);
-    var event = new Event('audio');
-    event.data = intData;
-    this.dispatchEvent(event);
-  };
-
-  /**
-   * Called when the audio stream is ready.
-   *
-   * @param {MediaStream} stream The media stream which is now available.
-   * @private
-   */
-  AudioManager.prototype.onAudioReady_ = function(stream) {
-    var audioContext = new window.AudioContext();
-    this.stream_ = stream;
-    this.audioIn_ = audioContext.createMediaStreamSource(stream);
-    this.audioProc_ = audioContext.createScriptProcessor(
-        4096 /* buffer size */, 1 /* channels */, 1 /* channels */);
-    this.audioProc_.onaudioprocess = this.onAudioProcess_.bind(this);
-
-    this.audioIn_.connect(this.audioProc_);
-    this.audioProc_.connect(audioContext.destination);
-    this.state = AudioState.LISTENING;
-  };
-
-  /**
-   * Starts the audio processing.
-   */
-  AudioManager.prototype.start = function() {
-    if (this.state == AudioState.LISTENING)
-      return;
-
-    navigator.webkitGetUserMedia(
-        {audio: true},
-        this.onAudioReady_.bind(this),
-        function(msg) { console.error('Failed to getUserMedia: ' + msg); });
-  };
-
-  /**
-   * Stops the audio processing.
-   */
-  AudioManager.prototype.stop = function() {
-    if (this.state != AudioState.LISTENING)
-      return;
-    this.audioProc_.disconnect();
-    this.audioIn_.disconnect();
-    var audioTracks = this.stream_.getAudioTracks();
-    for (var i = 0; i < audioTracks.length; ++i) {
-      audioTracks[i].stop();
-    }
-    this.audioProc_ = null;
-    this.audioIn_ = null;
-    this.stream_ = null;
-    this.state = AudioState.STOPPED;
-  };
-
-  return {
-    AudioManager: AudioManager,
-    AudioState: AudioState
-  };
-});
diff --git a/chrome/browser/resources/app_list/hotword_nacl.nmf b/chrome/browser/resources/app_list/hotword_nacl.nmf
deleted file mode 100644
index 2a3ae61a..0000000
--- a/chrome/browser/resources/app_list/hotword_nacl.nmf
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "program": {
-    "x86-64": { "url": "chrome://app-list/hotword_x86_64.nexe" },
-    "x86-32": { "url": "chrome://app-list/hotword_x86_32.nexe" },
-    "arm": {"url": "chrome://app-list/hotword_arm.nexe" }
-  }
-}
diff --git a/chrome/browser/resources/app_list/plugin_manager.js b/chrome/browser/resources/app_list/plugin_manager.js
deleted file mode 100644
index 6a025e0..0000000
--- a/chrome/browser/resources/app_list/plugin_manager.js
+++ /dev/null
@@ -1,168 +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.
-
-/**
- * @fileoverview The manager of offline hotword speech recognizer plugin.
- */
-
-cr.define('speech', function() {
-  'use strict';
-
-  /** The timeout milliseconds to load the model file. */
-  var MODEL_LOAD_TIMEOUT = 2000;
-
-  /**
-   * The type of the plugin state.
-   ** @enum {number}
-   */
-  var PluginState = {
-    UNINITIALIZED: 0,
-    LOADED: 1,
-    READY: 2,
-    RECOGNIZING: 3
-  };
-
-  /**
-   * The command names of the plugin.
-   * @enum {string}
-   */
-  var pluginCommands = {
-    SET_SAMPLING_RATE: 'h',
-    SET_CONFIG: 'm',
-    START_RECOGNIZING: 'r',
-    STOP_RECOGNIZING: 's'
-  };
-
-  /**
-   * The regexp pattern of the hotword recognition result.
-   */
-  var recognitionPattern = /^(HotwordFiredEvent:|hotword)/;
-
-  /**
-   * @constructor
-   */
-  function PluginManager(prefix, onReady, onRecognized, onError) {
-    this.state = PluginState.UNINITIALIZED;
-    this.onReady_ = onReady;
-    this.onRecognized_ = onRecognized;
-    this.onError_ = onError;
-    this.samplingRate_ = null;
-    this.config_ = null;
-    this.modelLoadTimeoutId_ = null;
-    var recognizer = $('recognizer');
-    if (!recognizer) {
-      recognizer = document.createElement('EMBED');
-      recognizer.id = 'recognizer';
-      recognizer.type = 'application/x-nacl';
-      recognizer.src = 'chrome://app-list/hotword_' + prefix + '.nmf';
-      recognizer.width = '1';
-      recognizer.height = '1';
-      document.body.appendChild(recognizer);
-    }
-    recognizer.addEventListener('error', onError);
-    recognizer.addEventListener('message', this.onMessage_.bind(this));
-    recognizer.addEventListener('load', this.onLoad_.bind(this));
-  };
-
-  /**
-   * The event handler of the plugin status.
-   *
-   * @param {Event} messageEvent the event object from the plugin.
-   * @private
-   */
-  PluginManager.prototype.onMessage_ = function(messageEvent) {
-    if (messageEvent.data == 'audio') {
-      var wasNotReady = this.state < PluginState.READY;
-      this.state = PluginState.RECOGNIZING;
-      if (wasNotReady) {
-        window.clearTimeout(this.modelLoadTimeoutId_);
-        this.modelLoadTimeoutId_ = null;
-        this.onReady_(this);
-      }
-    } else if (messageEvent.data == 'stopped' &&
-               this.state == PluginState.RECOGNIZING) {
-      this.state = PluginState.READY;
-    } else if (recognitionPattern.exec(messageEvent.data)) {
-      this.onRecognized_();
-    }
-  };
-
-  /**
-   * The event handler when the plugin is loaded.
-   *
-   * @private
-   */
-  PluginManager.prototype.onLoad_ = function() {
-    if (this.state == PluginState.UNINITIALIZED) {
-      this.state = PluginState.LOADED;
-      if (this.samplingRate_ && this.config_)
-        this.initialize_(this.samplingRate_, this.config_);
-      // Sets the timeout for initialization in case that NaCl module failed to
-      // respond during the initialization.
-      this.modelLoadTimeoutId_ = window.setTimeout(
-          this.onError_, MODEL_LOAD_TIMEOUT);
-    }
-  };
-
-  /**
-   * Sends the initialization messages to the plugin. This method is private.
-   * The initialization will happen from onLoad_ or scheduleInitialize.
-   *
-   * @param {number} samplingRate the sampling rate the plugin accepts.
-   * @param {string} config the url of the config file.
-   * @private
-   */
-  PluginManager.prototype.initialize_ = function(samplingRate, config) {
-    $('recognizer').postMessage(
-        pluginCommands.SET_SAMPLING_RATE + samplingRate);
-    $('recognizer').postMessage(pluginCommands.SET_CONFIG + config);
-  };
-
-  /**
-   * Initializes the plugin with the specified parameter, or schedules the
-   * initialization if the plugin is not ready.
-   *
-   * @param {number} samplingRate the sampling rate the plugin accepts.
-   * @param {string} config the url of the config file.
-   */
-  PluginManager.prototype.scheduleInitialize = function(samplingRate, config) {
-    if (this.state == PluginState.UNINITIALIZED) {
-      this.samplingRate_ = samplingRate;
-      this.config_ = config;
-    } else {
-      this.initialize_(samplingRate, config);
-    }
-  };
-
-  /**
-   * Asks the plugin to start recognizing the hotword.
-   */
-  PluginManager.prototype.startRecognizer = function() {
-    if (this.state == PluginState.READY)
-      $('recognizer').postMessage(pluginCommands.START_RECOGNIZING);
-  };
-
-  /**
-   * Asks the plugin to stop recognizing the hotword.
-   */
-  PluginManager.prototype.stopRecognizer = function() {
-    if (this.state == PluginState.RECOGNIZING)
-      $('recognizer').postMessage(pluginCommands.STOP_RECOGNIZING);
-  };
-
-  /**
-   * Sends the actual audio wave data.
-   *
-   * @param {cr.event.Event} event The event for the audio data.
-   */
-  PluginManager.prototype.sendAudioData = function(event) {
-    if (this.state == PluginState.RECOGNIZING)
-      $('recognizer').postMessage(event.data.buffer);
-  };
-
-  return {
-    PluginManager: PluginManager,
-    PluginState: PluginState,
-  };
-});
diff --git a/chrome/browser/resources/app_list/speech_manager.js b/chrome/browser/resources/app_list/speech_manager.js
deleted file mode 100644
index 74f4668..0000000
--- a/chrome/browser/resources/app_list/speech_manager.js
+++ /dev/null
@@ -1,312 +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.
-
-/**
- * @fileoverview The class to Manage both offline / online speech recognition.
- */
-
-<include src="plugin_manager.js">
-<include src="audio_manager.js">
-<include src="speech_recognition_manager.js">
-
-cr.define('speech', function() {
-  'use strict';
-
-  /**
-   * The state of speech recognition.
-   *
-   * @enum {string}
-   */
-  var SpeechState = {
-    READY: 'READY',
-    HOTWORD_RECOGNIZING: 'HOTWORD_RECOGNIZING',
-    RECOGNIZING: 'RECOGNIZING',
-    IN_SPEECH: 'IN_SPEECH',
-    STOPPING: 'STOPPING',
-    NETWORK_ERROR: 'NETWORK_ERROR'
-  };
-
-  /**
-   * The time to show the network error message in seconds.
-   *
-   * @const {number}
-   */
-  var SPEECH_ERROR_TIMEOUT = 3;
-
-  /**
-   * Checks the prefix for the hotword module based on the language. This is
-   * fragile if the file structure has changed.
-   */
-  function getHotwordPrefix() {
-    var prefix = navigator.language.toLowerCase();
-    if (prefix == 'en-gb')
-      return prefix;
-    var hyphen = prefix.indexOf('-');
-    if (hyphen >= 0)
-      prefix = prefix.substr(0, hyphen);
-    if (prefix == 'en')
-      prefix = '';
-    return prefix;
-  }
-
-  /**
-   * @constructor
-   */
-  function SpeechManager() {
-    this.audioManager_ = new speech.AudioManager();
-    this.audioManager_.addEventListener('audio', this.onAudioLevel_.bind(this));
-    this.speechRecognitionManager_ = new speech.SpeechRecognitionManager(this);
-    this.errorTimeoutId_ = null;
-  }
-
-  /**
-   * Updates the state.
-   *
-   * @param {SpeechState} newState The new state.
-   * @private
-   */
-  SpeechManager.prototype.setState_ = function(newState) {
-    if (this.state == newState)
-      return;
-
-    this.state = newState;
-    chrome.send('setSpeechRecognitionState', [this.state]);
-  };
-
-  /**
-   * Called with the mean audio level when audio data arrives.
-   *
-   * @param {cr.event.Event} event The event object for the audio data.
-   * @private
-   */
-  SpeechManager.prototype.onAudioLevel_ = function(event) {
-    var data = event.data;
-    var level = 0;
-    for (var i = 0; i < data.length; ++i)
-      level += Math.abs(data[i]);
-    level /= data.length;
-    chrome.send('speechSoundLevel', [level]);
-  };
-
-  /**
-   * Called when the hotword recognizer is ready.
-   *
-   * @param {PluginManager} pluginManager The hotword plugin manager which gets
-   *   ready.
-   * @private
-   */
-  SpeechManager.prototype.onHotwordRecognizerReady_ = function(pluginManager) {
-    this.pluginManager_ = pluginManager;
-    this.audioManager_.addEventListener(
-        'audio', pluginManager.sendAudioData.bind(pluginManager));
-    this.pluginManager_.startRecognizer();
-    this.audioManager_.start();
-    this.setState_(SpeechState.HOTWORD_RECOGNIZING);
-  };
-
-  /**
-   * Called when an error happens for loading the hotword recognizer.
-   *
-   * @private
-   */
-  SpeechManager.prototype.onHotwordRecognizerLoadError_ = function() {
-    this.setHotwordEnabled(false);
-    this.setState_(SpeechState.READY);
-  };
-
-  /**
-   * Called when the hotword is recognized.
-   *
-   * @private
-   */
-  SpeechManager.prototype.onHotwordRecognized_ = function() {
-    if (this.state != SpeechState.HOTWORD_RECOGNIZING)
-      return;
-    this.pluginManager_.stopRecognizer();
-    this.speechRecognitionManager_.start();
-  };
-
-  /**
-   * Called when the speech recognition has happened.
-   *
-   * @param {string} result The speech recognition result.
-   * @param {boolean} isFinal Whether the result is final or not.
-   */
-  SpeechManager.prototype.onSpeechRecognized = function(result, isFinal) {
-    chrome.send('speechResult', [result, isFinal]);
-    if (isFinal)
-      this.speechRecognitionManager_.stop();
-  };
-
-  /**
-   * Called when the speech recognition has started.
-   */
-  SpeechManager.prototype.onSpeechRecognitionStarted = function() {
-    this.setState_(SpeechState.RECOGNIZING);
-  };
-
-  /**
-   * Called when the speech recognition has ended.
-   */
-  SpeechManager.prototype.onSpeechRecognitionEnded = function() {
-    // Do not handle the speech recognition ends if it ends due to an error
-    // because an error message should be shown for a while.
-    // See onSpeechRecognitionError.
-    if (this.state == SpeechState.NETWORK_ERROR)
-      return;
-
-    // Restarts the hotword recognition.
-    if (this.state != SpeechState.STOPPING && this.pluginManager_) {
-      this.pluginManager_.startRecognizer();
-      this.audioManager_.start();
-      this.setState_(SpeechState.HOTWORD_RECOGNIZING);
-    } else {
-      this.audioManager_.stop();
-      this.setState_(SpeechState.READY);
-    }
-  };
-
-  /**
-   * Called when a speech has started.
-   */
-  SpeechManager.prototype.onSpeechStarted = function() {
-    if (this.state == SpeechState.RECOGNIZING)
-      this.setState_(SpeechState.IN_SPEECH);
-  };
-
-  /**
-   * Called when a speech has ended.
-   */
-  SpeechManager.prototype.onSpeechEnded = function() {
-    if (this.state == SpeechState.IN_SPEECH)
-      this.setState_(SpeechState.RECOGNIZING);
-  };
-
-  /**
-   * Called when the speech manager should recover from the error state.
-   *
-   * @private
-   */
-  SpeechManager.prototype.onSpeechRecognitionErrorTimeout_ = function() {
-    this.errorTimeoutId_ = null;
-    this.setState_(SpeechState.READY);
-    this.onSpeechRecognitionEnded();
-  };
-
-  /**
-   * Called when an error happened during the speech recognition.
-   *
-   * @param {SpeechRecognitionError} e The error object.
-   */
-  SpeechManager.prototype.onSpeechRecognitionError = function(e) {
-    if (e.error == 'network') {
-      this.setState_(SpeechState.NETWORK_ERROR);
-      this.errorTimeoutId_ = window.setTimeout(
-          this.onSpeechRecognitionErrorTimeout_.bind(this),
-          SPEECH_ERROR_TIMEOUT * 1000);
-    } else {
-      if (this.state != SpeechState.STOPPING)
-        this.setState_(SpeechState.READY);
-    }
-  };
-
-  /**
-   * Changes the availability of the hotword plugin.
-   *
-   * @param {boolean} enabled Whether enabled or not.
-   */
-  SpeechManager.prototype.setHotwordEnabled = function(enabled) {
-    var recognizer = $('recognizer');
-    if (enabled) {
-      if (recognizer)
-        return;
-      if (!this.naclArch)
-        return;
-
-      var prefix = getHotwordPrefix();
-      var pluginManager = new speech.PluginManager(
-          prefix,
-          this.onHotwordRecognizerReady_.bind(this),
-          this.onHotwordRecognized_.bind(this),
-          this.onHotwordRecognizerLoadError_.bind(this));
-      var modelUrl = 'chrome://app-list/_platform_specific/' + this.naclArch +
-          '_' + prefix + '/hotword.data';
-      pluginManager.scheduleInitialize(this.audioManager_.sampleRate, modelUrl);
-    } else {
-      if (!recognizer)
-        return;
-      document.body.removeChild(recognizer);
-      this.pluginManager_ = null;
-      if (this.state == SpeechState.HOTWORD_RECOGNIZING) {
-        this.audioManager_.stop();
-        this.setState_(SpeechState.READY);
-      }
-    }
-  };
-
-  /**
-   * Sets the NaCl architecture for the hotword module.
-   *
-   * @param {string} arch The architecture.
-   */
-  SpeechManager.prototype.setNaclArch = function(arch) {
-    this.naclArch = arch;
-  };
-
-  /**
-   * Called when the app-list bubble is shown.
-   *
-   * @param {boolean} hotwordEnabled Whether the hotword is enabled or not.
-   */
-  SpeechManager.prototype.onShown = function(hotwordEnabled) {
-    this.setHotwordEnabled(hotwordEnabled);
-
-    // No one sets the state if the content is initialized on shown but hotword
-    // is not enabled. Sets the state in such case.
-    if (!this.state && !hotwordEnabled)
-      this.setState_(SpeechState.READY);
-  };
-
-  /**
-   * Called when the app-list bubble is hidden.
-   */
-  SpeechManager.prototype.onHidden = function() {
-    this.setHotwordEnabled(false);
-
-    // SpeechRecognition is asynchronous.
-    this.audioManager_.stop();
-    if (this.state == SpeechState.RECOGNIZING ||
-        this.state == SpeechState.IN_SPEECH) {
-      this.setState_(SpeechState.STOPPING);
-      this.speechRecognitionManager_.stop();
-    } else {
-      this.setState_(SpeechState.READY);
-    }
-  };
-
-  /**
-   * Toggles the current state of speech recognition.
-   */
-  SpeechManager.prototype.toggleSpeechRecognition = function() {
-    if (this.state == SpeechState.NETWORK_ERROR) {
-      if (this.errorTimeoutId_)
-        window.clearTimeout(this.errorTimeoutId_);
-      this.onSpeechRecognitionErrorTimeout_();
-    } else if (this.state == SpeechState.RECOGNIZING ||
-        this.state == SpeechState.IN_SPEECH) {
-      this.audioManager_.stop();
-      this.speechRecognitionManager_.stop();
-    } else {
-      if (this.pluginManager_)
-        this.pluginManager_.stopRecognizer();
-      if (this.audioManager_.state == speech.AudioState.STOPPED)
-        this.audioManager_.start();
-      this.speechRecognitionManager_.start();
-    }
-  };
-
-  return {
-    SpeechManager: SpeechManager
-  };
-});
diff --git a/chrome/browser/resources/app_list/speech_recognition_manager.js b/chrome/browser/resources/app_list/speech_recognition_manager.js
deleted file mode 100644
index bffb383..0000000
--- a/chrome/browser/resources/app_list/speech_recognition_manager.js
+++ /dev/null
@@ -1,91 +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.
-
-/**
- * @fileoverview The manager of speech recognition.
- */
-
-cr.define('speech', function() {
-  'use strict';
-
-  /**
-   * @constructor
-   */
-  function SpeechRecognitionManager(delegate) {
-    this.isActive = true;
-    this.delegate_ = delegate;
-
-    this.recognizer_ = new window.webkitSpeechRecognition();
-    this.recognizer_.continuous = true;
-    this.recognizer_.interimResults = true;
-    this.recognizer_.lang = navigator.language;
-
-    this.recognizer_.onresult = this.onRecognizerResult_.bind(this);
-    if (this.delegate_) {
-      this.recognizer_.onstart =
-          this.delegate_.onSpeechRecognitionStarted.bind(this.delegate_);
-      this.recognizer_.onend =
-          this.delegate_.onSpeechRecognitionEnded.bind(this.delegate_);
-      this.recognizer_.onspeechstart =
-          this.delegate_.onSpeechStarted.bind(this.delegate_);
-      this.recognizer_.onspeechend =
-          this.delegate_.onSpeechEnded.bind(this.delegate_);
-      this.recognizer_.onerror = this.onRecognizerError_.bind(this);
-    }
-  }
-
-  /**
-   * Called when new speech recognition results arrive.
-   *
-   * @param {Event} speechEvent The event to contain the recognition results.
-   * @private
-   */
-  SpeechRecognitionManager.prototype.onRecognizerResult_ = function(
-      speechEvent) {
-    // Do not collect interim result for now.
-    var result = '';
-    var isFinal = false;
-    for (var i = 0; i < speechEvent.results.length; i++) {
-      if (speechEvent.results[i].isFinal)
-        isFinal = true;
-      result += speechEvent.results[i][0].transcript;
-    }
-    if (this.delegate_)
-      this.delegate_.onSpeechRecognized(result, isFinal);
-  };
-
-  /**
-   * Called when an error happens in speech recognition.
-   *
-   * @param {SpeechRecognitionError} error The error data.
-   * @private
-   */
-  SpeechRecognitionManager.prototype.onRecognizerError_ = function(error) {
-    if (error.error == 'language-not-supported') {
-      // Falls back to English and restart.
-      this.recognizer_.lang = 'en-US';
-      this.start();
-    } else {
-      this.delegate_.onSpeechRecognitionError(error);
-    }
-  };
-
-  /**
-   * Starts the speech recognition through webkitSpeechRecognition.
-   */
-  SpeechRecognitionManager.prototype.start = function() {
-    this.recognizer_.start();
-  };
-
-  /**
-   * Stops the ongoing speech recognition.
-   */
-  SpeechRecognitionManager.prototype.stop = function() {
-    this.recognizer_.abort();
-  };
-
-  return {
-    SpeechRecognitionManager: SpeechRecognitionManager
-  };
-});
diff --git a/chrome/browser/resources/app_list/start_page.js b/chrome/browser/resources/app_list/start_page.js
index 5138dba7..3b545b7 100644
--- a/chrome/browser/resources/app_list/start_page.js
+++ b/chrome/browser/resources/app_list/start_page.js
@@ -24,27 +24,9 @@
   }
 
   /**
-   * Invoked when the hotword plugin availability is changed.
-   *
-   * @param {boolean} enabled Whether the plugin is enabled or not.
-   */
-  function setHotwordEnabled(enabled) {
-  }
-
-  /**
-   * Sets the architecture of NaCl module to be loaded for hotword.
-   * @param {string} arch The architecture.
-   */
-  function setNaclArch(arch) {
-  }
-
-  /**
    * Invoked when the app-list bubble is shown.
-   *
-   * @param {boolean} hotwordEnabled Whether the hotword is enabled or not.
    */
-  function onAppListShown(hotwordEnabled, legacySpeechEnabled) {
-
+  function onAppListShown() {
     chrome.send('appListShown', [this.doodle != null]);
   }
 
@@ -122,27 +104,10 @@
     $('logo_container').appendChild(this.doodle);
   }
 
-  /**
-   * Invoked when the app-list bubble is hidden.
-   */
-  function onAppListHidden() {
-  }
-
-  /**
-   * Invoked when the user explicitly wants to toggle the speech recognition
-   * state.
-   */
-  function toggleSpeechRecognition() {
-  }
-
   return {
     initialize: initialize,
-    setHotwordEnabled: setHotwordEnabled,
-    setNaclArch: setNaclArch,
     onAppListDoodleUpdated: onAppListDoodleUpdated,
     onAppListShown: onAppListShown,
-    onAppListHidden: onAppListHidden,
-    toggleSpeechRecognition: toggleSpeechRecognition
   };
 });
 
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
index ebe865902..4d117e0 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -77,6 +77,56 @@
 Output.SPACE = ' ';
 
 /**
+ * Metadata about supported automation roles.
+ * @const {Object<string, {msgId: string, earcon: (string|undefined)}>}
+ * @private
+ */
+Output.ROLE_INFO_ = {
+  alert: {
+    msgId: 'aria_role_alert',
+    earcon: 'ALERT_NONMODAL',
+  },
+  button: {
+    msgId: 'tag_button',
+    earcon: 'BUTTON'
+  },
+  checkbox: {
+    msgId: 'input_type_checkbox'
+  },
+  heading: {
+    msgId: 'aria_role_heading',
+  },
+  link: {
+    msgId: 'tag_link',
+    earcon: 'LINK'
+  },
+  listItem: {
+    msgId: 'ARIA_ROLE_LISTITEM',
+    earcon: 'list_item'
+  },
+  menuListOption: {
+    msgId: 'aria_role_menuitem'
+  },
+  popUpButton: {
+    msgId: 'tag_button'
+  },
+  radioButton: {
+    msgId: 'input_type_radio'
+  },
+  textBox: {
+    msgId: 'input_type_text',
+    earcon: 'EDITABLE_TEXT'
+  },
+  textField: {
+    msgId: 'input_type_text',
+    earcon: 'EDITABLE_TEXT'
+  },
+  toolbar: {
+    msgId: 'aria_role_toolbar'
+  }
+};
+
+/**
  * Rules specifying format of AutomationNodes for output.
  * @type {!Object<string, Object<string, Object<string, string>>>}
  */
@@ -90,9 +140,6 @@
       speak: '!doNotInterrupt ' +
           '@aria_role_alert $name $earcon(ALERT_NONMODAL) $descendants'
     },
-    button: {
-      speak: '$name $earcon(BUTTON, @tag_button)'
-    },
     checkBox: {
       speak: '$if($checked, @describe_checkbox_checked($name), ' +
           '@describe_checkbox_unchecked($name)) ' +
@@ -116,7 +163,7 @@
       speak: '$name= $visited $earcon(LINK, @tag_link)='
     },
     list: {
-      enter: '$role'
+      enter: '@aria_role_list @list_with_items($parentChildCount)'
     },
     listItem: {
       enter: '$role'
@@ -150,15 +197,9 @@
     staticText: {
       speak: '$value'
     },
-    textBox: {
-      speak: '$name $value $earcon(EDITABLE_TEXT, @input_type_text)'
-    },
     tab: {
       speak: '@describe_tab($name)'
     },
-    textField: {
-      speak: '$name $value $earcon(EDITABLE_TEXT, @input_type_text) $protected'
-    },
     toolbar: {
       enter: '$name $role'
     },
@@ -425,10 +466,7 @@
       // All possible tokens based on prefix.
       if (prefix == '$') {
         options.annotation = token;
-        if (token == 'role') {
-          // Non-localized role and state obtained by default.
-          this.addToSpannable_(buff, node.role, options);
-        } else if (token == 'value') {
+        if (token == 'value') {
           var text = node.attributes.value;
           if (text !== undefined) {
             var offset = buff.getLength();
@@ -478,6 +516,26 @@
               new cursors.Cursor(leftmost, 0),
               new cursors.Cursor(rightmost, 0));
           this.range_(subrange, null, 'navigate', buff);
+        } else if (token == 'role') {
+          var msg = node.role;
+          var earconId = null;
+          var info = Output.ROLE_INFO_[node.role];
+          if (info) {
+            if (this.formatOptions_.braille)
+              msg = cvox.ChromeVox.msgs.getMsg(info.msgId + '_brl');
+            else
+              msg = cvox.ChromeVox.msgs.getMsg(info.msgId);
+            earconId = info.earcon;
+          } else {
+            console.error('Missing role info for ' + node.role);
+          }
+          if (earconId) {
+            options.annotation = new Output.Action(function() {
+              cvox.ChromeVox.earcons.playEarcon(
+                  cvox.AbstractEarcons[earconId]);
+            });
+          }
+          this.addToSpannable_(buff, msg, options);
         } else if (node.attributes[token]) {
           this.addToSpannable_(buff, node.attributes[token], options);
         } else if (node.state[token]) {
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
index d1409ec..96a7f372 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
@@ -13,7 +13,7 @@
   width: 522px;
 }
 
-.minute-maid #gaia-signin {
+.new-gaia-flow #gaia-signin {
   padding: 0 0 0;
   width: 522px;
 }
@@ -42,7 +42,7 @@
   }
 }
 
-.minute-maid #signin-right,
+.new-gaia-flow #signin-right,
 .no-right-panel #signin-right {
   display: none;
 }
@@ -87,6 +87,7 @@
 }
 
 html[dir=rtl] #back-button-item {
+  -webkit-transform: scaleX(-1);
   left: auto;
   right: 14px;
 }
@@ -132,8 +133,8 @@
   width: 1px;
 }
 
-.gaia-signin .minute-maid #step-logo,
-.gaia-signin .minute-maid #gaia-signin-divider,
+.gaia-signin .new-gaia-flow #step-logo,
+.gaia-signin .new-gaia-flow #gaia-signin-divider,
 .no-right-panel #gaia-signin-divider {
   display: none;
 }
@@ -176,7 +177,7 @@
   text-align: center;
 }
 
-.minute-maid #enterprise-info-container {
+.new-gaia-flow #enterprise-info-container {
   display: none;
 }
 
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
index f4139cbf..f6bbbe8 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -318,7 +318,7 @@
         params.localizedStrings = data.localizedStrings;
 
       if (this.isNewGaiaFlow) {
-        $('inner-container').classList.add('minute-maid');
+        $('inner-container').classList.add('new-gaia-flow');
         $('progress-dots').hidden = true;
         if (data.enterpriseDomain)
           params.enterpriseDomain = data.enterpriseDomain;
@@ -600,7 +600,8 @@
                       [credentials.gaiaId,
                        credentials.email,
                        credentials.password,
-                       credentials.authCode]);
+                       credentials.authCode,
+                       credentials.usingSAML]);
         }
       } else {
         chrome.send('completeLogin',
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index e2853ff..4482968 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -86,11 +86,7 @@
         <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_ICON_48" file="hotword_audio_verification/images/icon-48.png" type="BINDATA" />
         <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_ICON_128" file="hotword_audio_verification/images/icon-128.png" type="BINDATA" />
 
-        <!-- Hotword Helper extension -->
-        <include name="IDR_HOTWORD_HELPER_AUDIO_CLIENT_JS" file="hotword_helper/audio_client.js" type="BINDATA" />
-        <include name="IDR_HOTWORD_HELPER_MANAGER_JS" file="hotword_helper/manager.js" type="BINDATA" />
-
-        <!-- (Experimental) hotword triggering extension -->
+        <!-- Hotword extension -->
         <include name="IDR_HOTWORD_ALWAYS_ON_MANAGER_JS" file="hotword/always_on_manager.js" type="BINDATA" />
         <include name="IDR_HOTWORD_AUDIO_CLIENT_JS" file="hotword/audio_client.js" type="BINDATA" />
         <include name="IDR_HOTWORD_BASE_SESSION_MANAGER_JS" file="hotword/base_session_manager.js" type="BINDATA" />
@@ -178,6 +174,9 @@
         <include name="IDR_PDF_VIEWPORT_SCROLLER_JS" file="pdf/viewport_scroller.js" type="BINDATA" />
         <include name="IDR_PDF_PDF_SCRIPTING_API_JS" file="pdf/pdf_scripting_api.js" type="BINDATA" />
         <include name="IDR_PDF_CONTENT_SCRIPT_JS" file="pdf/content_script.js" type="BINDATA" />
+        <if expr="is_macosx">
+          <include name="IDR_PDF_SCROLLBARS_CSS" file="pdf/scrollbars_mac.css" type="BINDATA" />
+        </if>
 
         <include name="IDR_PDF_VIEWER_BOOKMARK_CSS" file="pdf/elements/viewer-bookmark/viewer-bookmark.css" type="BINDATA" />
         <include name="IDR_PDF_VIEWER_BOOKMARK_HTML" file="pdf/elements/viewer-bookmark/viewer-bookmark.html" type="BINDATA" />
diff --git a/chrome/browser/resources/contextual_search/header.svg b/chrome/browser/resources/contextual_search/header.svg
index 714a55d..3fb9aa35 100644
--- a/chrome/browser/resources/contextual_search/header.svg
+++ b/chrome/browser/resources/contextual_search/header.svg
@@ -1,64 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-	 viewBox="0 0 355.5 352.5" enable-background="new 0 0 355.5 352.5" xml:space="preserve">
+<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 156 99" enable-background="new 0 0 156 99" xml:space="preserve">
 <g>
-	<circle fill="#4788F4" cx="182.8" cy="176" r="163.9"/>
-	<circle fill="#3F87FF" cx="182.8" cy="176" r="163.9"/>
-	<g opacity="0.5">
-		<defs>
-			<path id="SVGID_1_" opacity="0.5" d="M286.1,17.8l-252,357.7c-9.3,69.8,187.6,72.8,238.1,43.7c47-27.2,125.7-73.5,124-158.1
-				C394.5,176.4,348.5,15.2,286.1,17.8z"/>
-		</defs>
-		<clipPath id="SVGID_2_">
-			<use xlink:href="#SVGID_1_"  overflow="visible"/>
-		</clipPath>
-		<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="303.0323" y1="-12.4325" x2="116.4693" y2="279.9136">
-			<stop  offset="0" style="stop-color:#4788F4"/>
-			<stop  offset="1" style="stop-color:#3360AD"/>
-		</linearGradient>
-		<circle clip-path="url(#SVGID_2_)" fill="url(#SVGID_3_)" cx="182.8" cy="176" r="163.9"/>
-	</g>
-	<circle fill="#9DBEF2" stroke="#29508F" stroke-width="15.5552" stroke-miterlimit="10" cx="182.6" cy="173.6" r="54.9"/>
-	<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="225.0402" y1="213.4908" x2="309.5199" y2="286.2732">
-		<stop  offset="0" style="stop-color:#1A1A1A"/>
-		<stop  offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
-	</linearGradient>
-	<polygon opacity="0.1" fill="url(#SVGID_4_)" points="460.4,342.1 296.8,350.5 144.1,223.3 186.5,230.3 219.1,214.8 236,185.1 
-		238.4,145.5 	"/>
-	
-		<line fill="none" stroke="#214175" stroke-width="15.5552" stroke-linecap="round" stroke-miterlimit="10" x1="224.5" y1="211.6" x2="268.6" y2="251.6"/>
-	<circle fill="none" stroke="#29508F" stroke-width="15.5552" stroke-miterlimit="10" cx="182.6" cy="173.6" r="54.9"/>
-	<path fill="#214175" d="M150.1,185.4c1.5,14,15.2,25,31.8,25c16.6,0,30.2-11,31.8-25H150.1z"/>
-	<path fill="none" stroke="#222222" stroke-width="15.5552" stroke-linecap="round" stroke-miterlimit="10" d="M239.8,198.8"/>
+	<rect x="48.2" y="55" fill="#51C1FF" width="108" height="26.7"/>
+	<path fill="#D4DDE1" d="M64,44c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-1c0-2.2,1.8-4,4-4h56c2.2,0,4,1.8,4,4V44z"/>
+	<path fill="#D4DDE1" d="M138,44c0,2.2-1.8,4-4,4H78c-2.2,0-4-1.8-4-4v-1c0-2.2,1.8-4,4-4h56c2.2,0,4,1.8,4,4V44z"/>
+	<path fill="#17F9FF" d="M145.9,69c0,2.2-1.8,4-4,4H62.2c-2.2,0-4-1.8-4-4v-1c0-2.2,1.8-4,4-4h79.7c2.2,0,4,1.8,4,4V69z"/>
+	<path fill="#D4DDE1" d="M37.8,69c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-1c0-2.2,1.8-4,4-4h29.8c2.2,0,4,1.8,4,4V69z"/>
+	<path fill="#D4DDE1" d="M79,95c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-1c0-2.2,1.8-4,4-4h71c2.2,0,4,1.8,4,4V95z"/>
+	<path fill="#D4DDE1" d="M119.1,95c0,2.2-1.8,4-4,4H93c-2.2,0-4-1.8-4-4v-1c0-2.2,1.8-4,4-4h22.1c2.2,0,4,1.8,4,4V95z"/>
 	<g>
-		<g>
-			<g>
-				<path fill="#214175" d="M157.3,152.5c-4.1,0-7.5,5.6-7.5,12.6c0,7,3.4,12.6,7.5,12.6c4.1,0,7.5-5.6,7.5-12.6
-					C164.8,158.2,161.5,152.5,157.3,152.5z M207.4,152.5c-4.1,0-7.5,5.6-7.5,12.6c0,7,3.4,12.6,7.5,12.6c4.1,0,7.5-5.6,7.5-12.6
-					C214.9,158.2,211.5,152.5,207.4,152.5z"/>
-			</g>
-		</g>
+		<path fill="#448AFF" d="M122.2,21c0-11-9-20-20-20s-20,9-20,20c0,4.8,1.7,9.1,4.5,12.6c0,0,0,0,0,0l15.5,20l15.9-20.4c0,0,0,0,0,0
+			C120.6,29.8,122.2,25.6,122.2,21z"/>
 	</g>
-	<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="97.7798" y1="99.6319" x2="182.315" y2="172.4622">
-		<stop  offset="0" style="stop-color:#1A1A1A"/>
-		<stop  offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
-	</linearGradient>
-	<polygon opacity="0.1" fill="url(#SVGID_5_)" points="339.5,221.1 171.9,245.8 13.2,113.5 119.9,45.7 	"/>
-	<g>
-		<path fill="#E8E8E8" d="M127.4,108.3c0,7.8-6.3,14.1-14.1,14.1H26.5c-7.8,0-14.1-6.3-14.1-14.1V60.5c0-7.8,6.3-14.1,14.1-14.1
-			h86.8c7.8,0,14.1,6.3,14.1,14.1V108.3z"/>
-	</g>
-	<path fill="#E8E8E8" d="M106.8,116.9c0,0-3.5,31.7,7.9,33.5c11.5,1.8-40.6,4.4-40.6-29.1s29.1-4.4,29.1-4.4"/>
-	<path opacity="0.2" fill="none" stroke="#F2F2F2" stroke-width="14.1051" stroke-linecap="round" d="M174.8,140.5
-		c0,0-19.4,3.1-22.7,19.2"/>
-	<path fill="#4788F4" d="M317.5,61"/>
-	
-		<line opacity="0.4" fill="none" stroke="#4788F4" stroke-width="6" stroke-linecap="round" stroke-miterlimit="10" x1="28.4" y1="67.4" x2="101.4" y2="67.4"/>
-	
-		<line opacity="0.4" fill="none" stroke="#4788F4" stroke-width="6" stroke-linecap="round" stroke-miterlimit="10" x1="28.4" y1="84.4" x2="91.4" y2="84.4"/>
-	
-		<line opacity="0.4" fill="none" stroke="#4788F4" stroke-width="6" stroke-linecap="round" stroke-miterlimit="10" x1="28.4" y1="101.4" x2="91.4" y2="101.4"/>
+	<path fill="#FFFFFF" d="M106.2,23.1l-0.9-0.7c-0.3-0.3-0.7-0.6-0.7-1.2c0-0.6,0.4-1.1,0.9-1.4c1.1-0.9,2.2-1.8,2.2-3.7
+		c0-1.8-1.1-2.8-1.7-3.3h1.5l1-1.2h-5.3c-3.8,0-5.6,2.4-5.6,5c0,2,1.5,4.1,4.3,4.1h0.7c-0.1,0.3-0.3,0.7-0.3,1.1
+		c0,0.9,0.3,1.2,0.8,1.7c-1.2,0.1-3.4,0.3-5,1.4c-1.5,0.9-2,2.2-2,3.2c0,2,1.8,3.8,5.6,3.8c4.6,0,6.8-2.6,6.8-5
+		C108.4,25.1,107.4,24.1,106.2,23.1z M99.7,15.3c0-1.9,1.1-2.7,2.3-2.7c2.2,0,3.4,3,3.4,4.7c0,2.2-1.8,2.7-2.5,2.7
+		C100.9,19.9,99.7,17.3,99.7,15.3z M102.8,30.7c-2.8,0-4.6-1.3-4.6-3.2c0-1.9,1.7-2.5,2.2-2.7c1.1-0.3,2.6-0.4,2.8-0.4h0.6
+		c2.1,1.5,2.9,2.1,2.9,3.4C106.8,29.4,105.1,30.7,102.8,30.7z"/>
 </g>
 </svg>
diff --git a/chrome/browser/resources/contextual_search/promo.css b/chrome/browser/resources/contextual_search/promo.css
index 098ab12..63fe3392 100644
--- a/chrome/browser/resources/contextual_search/promo.css
+++ b/chrome/browser/resources/contextual_search/promo.css
@@ -26,7 +26,6 @@
 }
 
 body {
-  color: #414141;
   font-family: 'Roboto2', sans-serif;
   margin: 0;
 }
@@ -39,7 +38,21 @@
   color: rgb(66, 133, 244);
 }
 
+#container {
+  /* NOTE(pedrosimonetti): There's an implicit extra top margin that is
+   * rendered natively (currently using 24dp). So, the total padding will
+   * be 38dp (24dp + 14dp). For more info, see SEARCH_BAR_HEIGHT_STATE_PROMO
+   * in ContextualSearchPanelBase.java.
+   *
+   * We're also setting the side and bottom paddings to ensure to make sure
+   * that when computing the height of the container all margins/paddings will
+   * be considered.
+   */
+  padding: 14px 16px 12px;
+}
+
 #button-container {
+  margin-top: 24px;
   text-align: end;
   width: 100%;
 }
@@ -52,21 +65,24 @@
 }
 
 #description {
+  color: #7E7E7E;
   font-size: 16px;
-  line-height: 1.4em;
-  margin: 24px 16px 4px 16px;
+  line-height: 1.38em;
+  margin: 12px 0 24px;
 }
 
 /* Some properties below can be overridden in landscape orientation. */
 #heading {
-  font-size: 24px;
-  letter-spacing: 1px;
-  margin-top: 0;
+  font-size: 23px;
+  margin: 20px 0 12px;
   text-align: center;
 }
-#header-image {
-  height: 100px;
-  margin: 0 auto 24px auto;
+.header-image {
+  background-image: url(header.svg);
+  background-repeat: no-repeat;
+  height: 98px;
+  margin: 0 auto 38px auto;
+  width: 156px;
 }
 .portrait {
   display: block;
@@ -78,10 +94,27 @@
 /* Landscape */
 @media screen and (orientation:landscape) {
   #heading {
-    padding-top: 16px;
+    margin-top: 0;
+    /* The heading text and description text should be aligned, therefore
+     * the left margin here will be equal to the header image width (156px)
+     * plus its right margin (24px). Therefore the total left should be
+     * 156px + 24px = 180px.
+     */
+    margin-left: 180px;
+    padding-top: 8px;
+    text-align: left;
   }
-  #header-image {
-    margin: 0 24px;
+  .header-image {
+    /* The header image is supposed to be vertically centered when the promo
+     * is in landscape mode. For now, we're forcefully moving the image 4
+     * pixels up to make it centered. A better approach would be using CSS
+     * flexbox to properly center it, but this will require changing the
+     * markup and styling of the whole promo, and it could be tricky coming
+     * up with a single markup that works in both portrait and lanscape modes.
+     */
+    margin: 0 24px 0 0;
+    position: relative;
+    top: -4px;
   }
   .portrait {
     display: none;
@@ -101,11 +134,12 @@
   display: inline-block;
   font-family: 'Roboto2', sans-serif;
   font-size: 14px;
-  margin: 14px 0;
+  margin: 6px 0;
   /* We use a slightly different top-bottom padding because Roboto has a
-     rendering bug which makes an extra padding to be rendered at the bottom of
-     text. */
-  padding: 17px 14px 13px 14px;
+   * rendering bug which makes an extra padding to be rendered at the bottom of
+   * text.
+   */
+  padding: 12px 16px 8px;
   white-space: nowrap;
 }
 
@@ -117,7 +151,6 @@
   background: rgb(66, 133, 244);
   background-clip: padding-box;
   border-radius: 3px;
-  margin-right: 24px;
 }
 
 #optin-button .caption {
diff --git a/chrome/browser/resources/contextual_search/promo.html b/chrome/browser/resources/contextual_search/promo.html
index 632b11da2..b5bbd1bc 100644
--- a/chrome/browser/resources/contextual_search/promo.html
+++ b/chrome/browser/resources/contextual_search/promo.html
@@ -14,17 +14,15 @@
 </head>
 <body>
   <div id="container">
-    <img id="header-image" class="portrait" src="header.svg">
-    <p id="heading" i18n-content="heading"></p>
-    <div>
-      <img id="header-image" class="landscape" src="header.svg">
-      <p id="description">
-        <span i18n-content="description-1"></span>
-        <a class="colored-link" href="#learn-more"
-            i18n-content="feature-name">
-        </a>
-        <span i18n-content="description-2"></span>
-      </p>
+    <div class="header-image portrait"></div>
+    <div id="heading" i18n-content="heading"></div>
+    <div class="header-image landscape"></div>
+    <div id="description">
+      <span i18n-content="description-1"></span>
+      <a class="colored-link" href="#learn-more"
+          i18n-content="feature-name">
+      </a>
+      <span i18n-content="description-2"></span>
     </div>
     <div id="button-container">
       <button id="optout-button">
diff --git a/chrome/browser/resources/contextual_search/promo.js b/chrome/browser/resources/contextual_search/promo.js
index 85c51c1..8e17d90 100644
--- a/chrome/browser/resources/contextual_search/promo.js
+++ b/chrome/browser/resources/contextual_search/promo.js
@@ -21,7 +21,7 @@
  */
 document.addEventListener('DOMContentLoaded', function(event) {
   if (config['hideHeader']) {
-    $('container').removeChild($('header-image'));
+    removeHeaderImages();
   }
   $('optin-button').addEventListener('click', function() {
     $('container').classList.add('hide');
@@ -42,3 +42,17 @@
 function getContentHeight() {
   return $('container').clientHeight;
 }
+
+/**
+ * Removes all header images from the promo.
+ */
+function removeHeaderImages() {
+  var images = document.querySelectorAll('.header-image');
+  for (var i = 0, length = images.length; i < length; i++) {
+    var image = images[i];
+    var parent = image.parentElement;
+    if (parent) {
+      parent.removeChild(image);
+    }
+  }
+}
diff --git a/chrome/browser/resources/extensions/extension_list.js b/chrome/browser/resources/extensions/extension_list.js
index 022f1bd..70fbb2f0 100644
--- a/chrome/browser/resources/extensions/extension_list.js
+++ b/chrome/browser/resources/extensions/extension_list.js
@@ -756,7 +756,7 @@
               dependentExtension.id;
           dependentList.appendChild(depNode);
         }, this);
-      });
+      }.bind(this));
 
       // The active views.
       this.updateVisibility_(row, '.active-views', extension.views.length > 0,
diff --git a/chrome/browser/resources/hotword_helper/audio_client.js b/chrome/browser/resources/hotword_helper/audio_client.js
deleted file mode 100644
index 3b0b46c..0000000
--- a/chrome/browser/resources/hotword_helper/audio_client.js
+++ /dev/null
@@ -1,387 +0,0 @@
-// Copyright (c) 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.
-
-'use strict';
-
-/**
- * @fileoverview This is the audio client content script injected into eligible
- *  Google.com and New tab pages for interaction between the Webpage and the
- *  Hotword extension.
- */
-
-
-
-(function() {
-  /**
-   * @constructor
-   */
-  var AudioClient = function() {
-    /** @private {Element} */
-    this.speechOverlay_ = null;
-
-    /** @private {number} */
-    this.checkSpeechUiRetries_ = 0;
-
-    /**
-     * Port used to communicate with the audio manager.
-     * @private {?Port}
-     */
-    this.port_ = null;
-
-    /**
-     * Keeps track of the effects of different commands. Used to verify that
-     * proper UIs are shown to the user.
-     * @private {Object<AudioClient.CommandToPage, Object>}
-     */
-    this.uiStatus_ = null;
-
-    /**
-     * Bound function used to handle commands sent from the page to this script.
-     * @private {Function}
-     */
-    this.handleCommandFromPageFunc_ = null;
-  };
-
-
-  /**
-   * Messages sent to the page to control the voice search UI.
-   * @enum {string}
-   */
-  AudioClient.CommandToPage = {
-    HOTWORD_VOICE_TRIGGER: 'vt',
-    HOTWORD_STARTED: 'hs',
-    HOTWORD_ENDED: 'hd',
-    HOTWORD_TIMEOUT: 'ht',
-    HOTWORD_ERROR: 'he'
-  };
-
-
-  /**
-   * Messages received from the page used to indicate voice search state.
-   * @enum {string}
-   */
-  AudioClient.CommandFromPage = {
-    SPEECH_START: 'ss',
-    SPEECH_END: 'se',
-    SPEECH_RESET: 'sr',
-    SHOWING_HOTWORD_START: 'shs',
-    SHOWING_ERROR_MESSAGE: 'sem',
-    SHOWING_TIMEOUT_MESSAGE: 'stm',
-    CLICKED_RESUME: 'hcc',
-    CLICKED_RESTART: 'hcr',
-    CLICKED_DEBUG: 'hcd'
-  };
-
-
-  /**
-   * Errors that are sent to the hotword extension.
-   * @enum {string}
-   */
-  AudioClient.Error = {
-    NO_SPEECH_UI: 'ac1',
-    NO_HOTWORD_STARTED_UI: 'ac2',
-    NO_HOTWORD_TIMEOUT_UI: 'ac3',
-    NO_HOTWORD_ERROR_UI: 'ac4'
-  };
-
-
-  /**
-   * @const {string}
-   * @private
-   */
-  AudioClient.HOTWORD_EXTENSION_ID_ = 'bepbmhgboaologfdajaanbcjmnhjmhfn';
-
-
-  /**
-   * Number of times to retry checking a transient error.
-   * @const {number}
-   * @private
-   */
-  AudioClient.MAX_RETRIES = 3;
-
-
-  /**
-   * Delay to wait in milliseconds before rechecking for any transient errors.
-   * @const {number}
-   * @private
-   */
-  AudioClient.RETRY_TIME_MS_ = 2000;
-
-
-  /**
-   * DOM ID for the speech UI overlay.
-   * @const {string}
-   * @private
-   */
-  AudioClient.SPEECH_UI_OVERLAY_ID_ = 'spch';
-
-
-  /**
-   * @const {string}
-   * @private
-   */
-  AudioClient.HELP_CENTER_URL_ =
-      'https://support.google.com/chrome/?p=ui_hotword_search';
-
-
-  /**
-   * @const {string}
-   * @private
-   */
-  AudioClient.CLIENT_PORT_NAME_ = 'chwcpn';
-
-  /**
-   * Existence of the Audio Client.
-   * @const {string}
-   * @private
-   */
-  AudioClient.EXISTS_ = 'chwace';
-
-
-  /**
-   * Checks for the presence of speech overlay UI DOM elements.
-   * @private
-   */
-  AudioClient.prototype.checkSpeechOverlayUi_ = function() {
-    if (!this.speechOverlay_) {
-      window.setTimeout(this.delayedCheckSpeechOverlayUi_.bind(this),
-                        AudioClient.RETRY_TIME_MS_);
-    } else {
-      this.checkSpeechUiRetries_ = 0;
-    }
-  };
-
-
-  /**
-   * Function called to check for the speech UI overlay after some time has
-   * passed since an initial check. Will either retry triggering the speech
-   * or sends an error message depending on the number of retries.
-   * @private
-   */
-  AudioClient.prototype.delayedCheckSpeechOverlayUi_ = function() {
-    this.speechOverlay_ = document.getElementById(
-        AudioClient.SPEECH_UI_OVERLAY_ID_);
-    if (!this.speechOverlay_) {
-      if (this.checkSpeechUiRetries_++ < AudioClient.MAX_RETRIES) {
-        this.sendCommandToPage_(AudioClient.CommandToPage.VOICE_TRIGGER);
-        this.checkSpeechOverlayUi_();
-      } else {
-        this.sendCommandToExtension_(AudioClient.Error.NO_SPEECH_UI);
-      }
-    } else {
-      this.checkSpeechUiRetries_ = 0;
-    }
-  };
-
-
-  /**
-   * Checks that the triggered UI is actually displayed.
-   * @param {AudioClient.CommandToPage} command Command that was send.
-   * @private
-   */
-  AudioClient.prototype.checkUi_ = function(command) {
-    this.uiStatus_[command].timeoutId =
-        window.setTimeout(this.failedCheckUi_.bind(this, command),
-                          AudioClient.RETRY_TIME_MS_);
-  };
-
-
-  /**
-   * Function called when the UI verification is not called in time. Will either
-   * retry the command or sends an error message, depending on the number of
-   * retries for the command.
-   * @param {AudioClient.CommandToPage} command Command that was sent.
-   * @private
-   */
-  AudioClient.prototype.failedCheckUi_ = function(command) {
-    if (this.uiStatus_[command].tries++ < AudioClient.MAX_RETRIES) {
-      this.sendCommandToPage_(command);
-      this.checkUi_(command);
-    } else {
-      this.sendCommandToExtension_(this.uiStatus_[command].error);
-    }
-  };
-
-
-  /**
-   * Confirm that an UI element has been shown.
-   * @param {AudioClient.CommandToPage} command UI to confirm.
-   * @private
-   */
-  AudioClient.prototype.verifyUi_ = function(command) {
-    if (this.uiStatus_[command].timeoutId) {
-      window.clearTimeout(this.uiStatus_[command].timeoutId);
-      this.uiStatus_[command].timeoutId = null;
-      this.uiStatus_[command].tries = 0;
-    }
-  };
-
-
-  /**
-   * Sends a command to the audio manager.
-   * @param {string} commandStr command to send to plugin.
-   * @private
-   */
-  AudioClient.prototype.sendCommandToExtension_ = function(commandStr) {
-    if (this.port_)
-      this.port_.postMessage({'cmd': commandStr});
-  };
-
-
-  /**
-   * Handles a message from the audio manager.
-   * @param {{cmd: string}} commandObj Command from the audio manager.
-   * @private
-   */
-  AudioClient.prototype.handleCommandFromExtension_ = function(commandObj) {
-    var command = commandObj['cmd'];
-    if (command) {
-      switch (command) {
-        case AudioClient.CommandToPage.HOTWORD_VOICE_TRIGGER:
-          this.sendCommandToPage_(command);
-          this.checkSpeechOverlayUi_();
-          break;
-        case AudioClient.CommandToPage.HOTWORD_STARTED:
-          this.sendCommandToPage_(command);
-          this.checkUi_(command);
-          break;
-        case AudioClient.CommandToPage.HOTWORD_ENDED:
-          this.sendCommandToPage_(command);
-          break;
-        case AudioClient.CommandToPage.HOTWORD_TIMEOUT:
-          this.sendCommandToPage_(command);
-          this.checkUi_(command);
-          break;
-        case AudioClient.CommandToPage.HOTWORD_ERROR:
-          this.sendCommandToPage_(command);
-          this.checkUi_(command);
-          break;
-      }
-    }
-  };
-
-
-  /**
-   * @param {AudioClient.CommandToPage} commandStr Command to send.
-   * @private
-   */
-  AudioClient.prototype.sendCommandToPage_ = function(commandStr) {
-    window.postMessage({'type': commandStr}, '*');
-  };
-
-
-  /**
-   * Handles a message from the html window.
-   * @param {!MessageEvent} messageEvent Message event from the window.
-   * @private
-   */
-  AudioClient.prototype.handleCommandFromPage_ = function(messageEvent) {
-    if (messageEvent.source == window && messageEvent.data.type) {
-      var command = messageEvent.data.type;
-      switch (command) {
-        case AudioClient.CommandFromPage.SPEECH_START:
-          this.speechActive_ = true;
-          this.sendCommandToExtension_(command);
-          break;
-        case AudioClient.CommandFromPage.SPEECH_END:
-          this.speechActive_ = false;
-          this.sendCommandToExtension_(command);
-          break;
-        case AudioClient.CommandFromPage.SPEECH_RESET:
-          this.speechActive_ = false;
-          this.sendCommandToExtension_(command);
-          break;
-        case 'SPEECH_RESET':  // Legacy, for embedded NTP.
-          this.speechActive_ = false;
-          this.sendCommandToExtension_(AudioClient.CommandFromPage.SPEECH_END);
-          break;
-        case AudioClient.CommandFromPage.CLICKED_RESUME:
-          this.sendCommandToExtension_(command);
-          break;
-        case AudioClient.CommandFromPage.CLICKED_RESTART:
-          this.sendCommandToExtension_(command);
-          break;
-        case AudioClient.CommandFromPage.CLICKED_DEBUG:
-          window.open(AudioClient.HELP_CENTER_URL_, '_blank');
-          break;
-        case AudioClient.CommandFromPage.SHOWING_HOTWORD_START:
-          this.verifyUi_(AudioClient.CommandToPage.HOTWORD_STARTED);
-          break;
-        case AudioClient.CommandFromPage.SHOWING_ERROR_MESSAGE:
-          this.verifyUi_(AudioClient.CommandToPage.HOTWORD_ERROR);
-          break;
-        case AudioClient.CommandFromPage.SHOWING_TIMEOUT_MESSAGE:
-          this.verifyUi_(AudioClient.CommandToPage.HOTWORD_TIMEOUT);
-          break;
-      }
-    }
-  };
-
-
-  /**
-   * Initialize the content script.
-   */
-  AudioClient.prototype.initialize = function() {
-    if (AudioClient.EXISTS_ in window)
-      return;
-    window[AudioClient.EXISTS_] = true;
-
-    // UI verification object.
-    this.uiStatus_ = {};
-    this.uiStatus_[AudioClient.CommandToPage.HOTWORD_STARTED] = {
-      timeoutId: null,
-      tries: 0,
-      error: AudioClient.Error.NO_HOTWORD_STARTED_UI
-    };
-    this.uiStatus_[AudioClient.CommandToPage.HOTWORD_TIMEOUT] = {
-      timeoutId: null,
-      tries: 0,
-      error: AudioClient.Error.NO_HOTWORD_TIMEOUT_UI
-    };
-    this.uiStatus_[AudioClient.CommandToPage.HOTWORD_ERROR] = {
-      timeoutId: null,
-      tries: 0,
-      error: AudioClient.Error.NO_HOTWORD_ERROR_UI
-    };
-
-    this.handleCommandFromPageFunc_ = this.handleCommandFromPage_.bind(this);
-    window.addEventListener('message', this.handleCommandFromPageFunc_, false);
-    this.initPort_();
-  };
-
-
-  /**
-   * Initialize the communications port with the audio manager. This
-   * function will be also be called again if the audio-manager
-   * disconnects for some reason (such as the extension
-   * background.html page being reloaded).
-   * @private
-   */
-  AudioClient.prototype.initPort_ = function() {
-    this.port_ = chrome.runtime.connect(
-        AudioClient.HOTWORD_EXTENSION_ID_,
-        {'name': AudioClient.CLIENT_PORT_NAME_});
-    // Note that this listen may have to be destroyed manually if AudioClient
-    // is ever destroyed on this tab.
-    this.port_.onDisconnect.addListener(
-        (function(e) {
-          if (this.handleCommandFromPageFunc_) {
-            window.removeEventListener(
-                'message', this.handleCommandFromPageFunc_, false);
-          }
-          delete window[AudioClient.EXISTS_];
-        }).bind(this));
-
-    // See note above.
-    this.port_.onMessage.addListener(
-        this.handleCommandFromExtension_.bind(this));
-
-    if (this.speechActive_)
-      this.sendCommandToExtension_(AudioClient.CommandFromPage.SPEECH_START);
-  };
-
-
-  // Initializes as soon as the code is ready, do not wait for the page.
-  new AudioClient().initialize();
-})();
diff --git a/chrome/browser/resources/hotword_helper/manager.js b/chrome/browser/resources/hotword_helper/manager.js
deleted file mode 100644
index 2810d63..0000000
--- a/chrome/browser/resources/hotword_helper/manager.js
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright (c) 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.
-
-'use strict';
-
-/**
- * @fileoverview This extension manages communications between Chrome,
- * Google.com pages and the Chrome Hotword extension.
- *
- * This helper extension is required due to the depoyment plan for Chrome M34:
- *
- *  - The hotword extension will be distributed as an externally loaded
- *      component extension.
- *  - Settings for enabling and disabling the hotword extension has moved to
- *      Chrome settings.
- *  - NewTab page is served via chrome://newtab/
- *
- */
-
-
-
-/** @constructor */
-var OptInManager = function() {};
-
-
-/**
- * @const {string}
- * @private
- */
-OptInManager.HOTWORD_EXTENSION_ID_ = 'bepbmhgboaologfdajaanbcjmnhjmhfn';
-
-
-/**
- * Test extension ID.
- * @const {string}
- * @private
- */
-OptInManager.TEST_EXTENSION_ID_ = 'cpfhkdbjfdgdebcjlifoldbijinjfifp';
-
-
-/**
- * Commands sent from the page to this content script.
- * @enum {string}
- */
-OptInManager.CommandFromPage = {
-  // User has explicitly clicked 'no'.
-  CLICKED_NO_OPTIN: 'hcno',
-  // User has opted in.
-  CLICKED_OPTIN: 'hco',
-  // Audio logging is opted in.
-  AUDIO_LOGGING_ON: 'alon',
-  // Audio logging is opted out.
-  AUDIO_LOGGING_OFF: 'aloff',
-  // User visited an eligible page.
-  PAGE_WAKEUP: 'wu'
-};
-
-
-/**
- * @param {Tab} tab Tab to inject.
- * @param {function(HotwordStatus)} sendResponse Callback function to respond
- *     to sender.
- * @param {HotwordStatus} hotwordStatus Status of the hotword extension.
- * @private
- */
-OptInManager.prototype.injectTab_ = function(
-    tab, sendResponse, hotwordStatus) {
-  var response = {'doNotShowOptinMessage': true};
-
-  if (!tab.incognito && hotwordStatus.available) {
-    if (!hotwordStatus.enabledSet)
-      response = hotwordStatus;
-    else if (hotwordStatus.enabled)
-      chrome.tabs.executeScript(tab.id, {'file': 'audio_client.js'});
-  }
-
-  try {
-    sendResponse(response);
-  } catch (err) {
-    // Suppress the exception thrown by sendResponse() when the page doesn't
-    // specify a response callback in the call to chrome.runtime.sendMessage().
-    // Unfortunately, there doesn't appear to be a way to detect one-way
-    // messages without explicitly saying in the message itself.  This message
-    // is defined as a constant in extensions/renderer/messaging_bindings.cc
-    if (err.message == 'Attempting to use a disconnected port object')
-      return;
-    throw err;
-  }
-};
-
-
-/**
- * Handles messages from the helper content script.
- * @param {*} request Message from the sender.
- * @param {MessageSender} sender Information about the sender.
- * @param {function(HotwordStatus)} sendResponse Callback function to respond
- *     to sender.
- * @return {boolean} Whether to maintain the port open to call sendResponse.
- * @private
- */
-OptInManager.prototype.handleMessage_ = function(
-    request, sender, sendResponse) {
-  switch (request.type) {
-    case OptInManager.CommandFromPage.PAGE_WAKEUP:
-      if (((sender.tab && this.isEligibleUrl(sender.tab.url)) ||
-          sender.id == OptInManager.HOTWORD_EXTENSION_ID_ ||
-          sender.id == OptInManager.TEST_EXTENSION_ID_) &&
-          chrome.hotwordPrivate && chrome.hotwordPrivate.getStatus) {
-        chrome.hotwordPrivate.getStatus(
-            this.injectTab_.bind(
-                this,
-                request.tab || sender.tab || {incognito: true},
-                sendResponse));
-        return true;
-      }
-      break;
-    case OptInManager.CommandFromPage.CLICKED_OPTIN:
-      if (chrome.hotwordPrivate && chrome.hotwordPrivate.setEnabled &&
-          chrome.hotwordPrivate.getStatus) {
-        chrome.hotwordPrivate.setEnabled(true);
-        chrome.hotwordPrivate.getStatus(
-            this.injectTab_.bind(this, sender.tab, sendResponse));
-        return true;
-      }
-      break;
-    // User has explicitly clicked 'no thanks'.
-    case OptInManager.CommandFromPage.CLICKED_NO_OPTIN:
-      if (chrome.hotwordPrivate && chrome.hotwordPrivate.setEnabled) {
-        chrome.hotwordPrivate.setEnabled(false);
-      }
-      break;
-    // Information regarding the audio logging preference was sent.
-    case OptInManager.CommandFromPage.AUDIO_LOGGING_ON:
-      if (chrome.hotwordPrivate &&
-          chrome.hotwordPrivate.setAudioLoggingEnabled) {
-        chrome.hotwordPrivate.setAudioLoggingEnabled(true);
-      }
-      break;
-    case OptInManager.CommandFromPage.AUDIO_LOGGING_OFF:
-      if (chrome.hotwordPrivate &&
-          chrome.hotwordPrivate.setAudioLoggingEnabled) {
-        chrome.hotwordPrivate.setAudioLoggingEnabled(false);
-      }
-      break;
-    default:
-      break;
-  }
-  return false;
-};
-
-/**
- * Helper function to test URLs as being valid for running the
- * hotwording extension. It's used by isEligibleUrl to make that
- * function clearer.
- * @param {string} url URL to check.
- * @param {string} base Base URL to compare against..
- * @return {boolean} True if url is an eligible hotword URL.
- */
-OptInManager.prototype.checkEligibleUrl = function(url, base) {
-  if (!url)
-    return false;
-
-  if (url === base ||
-      url === base + '/' ||
-      url.indexOf(base + '/_/chrome/newtab?') === 0 ||  // Appcache NTP.
-      url.indexOf(base + '/?') === 0 ||
-      url.indexOf(base + '/#') === 0 ||
-      url.indexOf(base + '/webhp') === 0 ||
-      url.indexOf(base + '/search') === 0) {
-    return true;
-  }
-  return false;
-
-};
-
-/**
- * Determines if a URL is eligible for hotwording. For now, the
- * valid pages are the Google HP and SERP (this will include the NTP).
- * @param {string} url URL to check.
- * @return {boolean} True if url is an eligible hotword URL.
- */
-OptInManager.prototype.isEligibleUrl = function(url) {
-  if (!url)
-    return false;
-
-  // More URLs will be added in the future so leaving this as an array.
-  var baseUrls = [
-    'chrome://newtab'
-  ];
-  var baseGoogleUrls = [
-    'https://www.google.',
-    'https://encrypted.google.'
-  ];
-  var tlds = [
-    'com',
-    'co.uk',
-    'de',
-    'fr',
-    'ru'
-  ];
-
-  // Check URLs which do not have locale-based TLDs first.
-  if (this.checkEligibleUrl(url, baseUrls[0]))
-    return true;
-
-  // Check URLs with each type of local-based TLD.
-  for (var i = 0; i < baseGoogleUrls.length; i++) {
-    for (var j = 0; j < tlds.length; j++) {
-      var base = baseGoogleUrls[i] + tlds[j];
-      if (this.checkEligibleUrl(url, base))
-        return true;
-    }
-  }
-  return false;
-};
-
-
-/**
- * Initializes the extension.
- */
-OptInManager.prototype.initialize = function() {
-  // TODO(rlp): Possibly remove the next line. It's proably not used, but
-  // leaving for now to be safe. We should remove it once all messsage
-  // relaying is removed form the content scripts.
-  chrome.runtime.onMessage.addListener(this.handleMessage_.bind(this));
-  chrome.runtime.onMessageExternal.addListener(
-      this.handleMessage_.bind(this));
-};
-
-
-new OptInManager().initialize();
diff --git a/chrome/browser/resources/hotword_helper/manifest.json b/chrome/browser/resources/hotword_helper/manifest.json
deleted file mode 100644
index 9439be3..0000000
--- a/chrome/browser/resources/hotword_helper/manifest.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  // Extension ID: dnhpdliibojhegemfjheidglijccjfmc
-  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDagiQy1VGkO2CHJSjVh7eU5GtuBuOlg2/cTZt7203AcevqpcDd+65S2/yd9KAELYcU6pK8nHVGYBMI6s0u+0RgXfIJ0eFOlTlgfAQWHvg8ovHtJlFJd1COrOkbntD9+s9Jobr3ldmow87aZF1bVHUY4khVP56cZe6adlVw2wK31QIDAQAB",
-
-  "name": "hotword helper",
-  "version": "0.0.2.0",
-  "manifest_version": 2,
-
-  "background": {
-    "scripts": ["manager.js"],
-    "persistent": false
-  },
-
-  "permissions": [
-    "*://*.google.com/*",
-    "*://*.google.ru/*",
-    "*://*.google.co.uk/*",
-    "*://*.google.fr/*",
-    "*://*.google.de/*",
-    "chrome://newtab/",
-    "hotwordPrivate",
-    "tabs"
-  ],
-
-  "externally_connectable": {
-    "matches": [
-      "*://*.google.com/*",
-      "*://*.google.ru/*",
-      "*://*.google.co.uk/*",
-      "*://*.google.fr/*",
-      "*://*.google.de/*",
-      "chrome://newtab/"
-    ],
-    "ids": [
-      // Test extension.
-      "cpfhkdbjfdgdebcjlifoldbijinjfifp"
-    ]
-  },
-
-  "minimum_chrome_version": "32"
-}
diff --git a/chrome/browser/resources/media_router/media_router_data.js b/chrome/browser/resources/media_router/media_router_data.js
new file mode 100644
index 0000000..c6844aa
--- /dev/null
+++ b/chrome/browser/resources/media_router/media_router_data.js
@@ -0,0 +1,163 @@
+// Copyright (c) 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.
+
+// Any strings used here will already be localized. Values such as castMode or
+// IDs will be defined elsewhere and determined later.
+cr.define('media_router', function() {
+  'use strict';
+
+  /**
+   * @enum {string}
+   */
+  var SinkStatus = {
+    IDLE: 'idle',
+    ACTIVE: 'active',
+    REQUEST_PENDING: 'request_pending'
+  };
+
+
+  /**
+   * @param {number} castMode The type of cast mode.
+   * @param {string} title The title of the cast mode.
+   * @param {string} description The description of the cast mode.
+   * @constructor
+   * @struct
+   */
+  var CastMode = function(castMode, title, description) {
+    /** @type {number} */
+    this.castMode = castMode;
+
+    /** @type {string} */
+    this.title = title;
+
+    /** @type {string} */
+    this.description = description;
+  };
+
+
+  /**
+   * @param {string} id The ID of this issue.
+   * @param {string} title The issue title.
+   * @param {string} message The issue message.
+   * @param {string} defaultActionText The button text of default action.
+   * @param {number} defaultActionType The type of default action.
+   * @param {?string} secondaryActionText The button text of optional action.
+   * @param {?number} secondaryActionType The type of optional action.
+   * @param {?string} mediaRouteId The route ID to which this issue
+   *                  pertains. If not set, this is a global issue.
+   * @param {boolean} isBlocking True if this issue blocks other UI.
+   * @param {?string} helpURL The URL to be opened if learn more is clicked.
+   * @constructor
+   * @struct
+   */
+  var Issue = function(id, title, message, defaultActionText,
+                       defaultActionType, secondaryActionText,
+                       secondaryActionType, mediaRouteId, isBlocking,
+                       helpURL) {
+    /** @type {string} */
+    this.id = id;
+
+    /** @type {string} */
+    this.title = title;
+
+    /** @type {string} */
+    this.message = message;
+
+    /** @type {string} */
+    this.defaultActionText = defaultActionText;
+
+    /** @type {number} */
+    this.defaultActionType = defaultActionType;
+
+    /** @type {?string} */
+    this.secondaryActionText = secondaryActionText;
+
+    /** @type {?number} */
+    this.secondaryActionType = secondaryActionType;
+
+    /** @type {?string} */
+    this.mediaRouteId = mediaRouteId;
+
+    /** @type {boolean} */
+    this.isBlocking = isBlocking;
+
+    /** @type {?string} */
+    this.helpURL = helpURL;
+  };
+
+
+  /**
+   * @param {string} id The media route ID.
+   * @param {string} sinkId The ID of the media sink running this route.
+   * @param {string} title The short description of this route.
+   * @param {?number} tabId The ID of the tab in which web app is running and
+   *                  accessing the route.
+   * @param {boolean} isLocal True if this is a locally created route.
+   * @constructor
+   * @struct
+   */
+  var Route = function(id, sinkId, title, tabId, isLocal) {
+    /** @type {string} */
+    this.id = id;
+
+    /** @type {string} */
+    this.sinkId = sinkId;
+
+    /** @type {string} */
+    this.title = title;
+
+    /** @type {?number} */
+    this.tabId = tabId;
+
+    /** @type {boolean} */
+    this.isLocal = isLocal;
+  };
+
+
+  /**
+   * @param {string} id The ID of the media sink.
+   * @param {string} name The name of the sink.
+   * @param {media_router.SinkStatus} status The readiness state of the sink.
+   * @param {!Array<number>} castModes Cast modes compatible with the sink.
+   * @constructor
+   * @struct
+   */
+  var Sink = function(id, name, status) {
+    /** @type {string} */
+    this.id = id;
+
+    /** @type {string} */
+    this.name = name;
+
+    /** @type {media_router.SinkStatus} */
+    this.status = status;
+
+    /** @type {!Array<number>} */
+    this.castModes = castModes;
+  };
+
+
+  /**
+   * @param {number} tabId The current tab ID.
+   * @param {string} domain The domain of the current tab.
+   * @constructor
+   * @struct
+   */
+  var TabInfo = function(tabId, domain) {
+    /** @type {number} */
+    this.tabId = tabId;
+
+    /** @type {string} */
+    this.domain = domain;
+  };
+
+  return {
+    SinkStatus: SinkStatus,
+    CastMode: CastMode,
+    Issue: Issue,
+    Route: Route,
+    Sink: Sink,
+    TabInfo: TabInfo,
+  };
+});
diff --git a/chrome/browser/resources/options/autofill_options.css b/chrome/browser/resources/options/autofill_options.css
index 41e7c57d..815c58c 100644
--- a/chrome/browser/resources/options/autofill_options.css
+++ b/chrome/browser/resources/options/autofill_options.css
@@ -12,6 +12,9 @@
 
 .autofill-list-item {
   -webkit-padding-start: 8px;
+  max-width: 50%;
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
 
 .autofill-list-item + .deemphasized {
diff --git a/chrome/browser/resources/options/autofill_options.js b/chrome/browser/resources/options/autofill_options.js
index ce8dd1fd..d9046928 100644
--- a/chrome/browser/resources/options/autofill_options.js
+++ b/chrome/browser/resources/options/autofill_options.js
@@ -175,34 +175,6 @@
     },
 
     /**
-     * For local Autofill data, this function causes the AutofillOptionsHandler
-     * to call showEditAddressOverlay(). For Wallet data, the user is
-     * redirected to the Wallet web interface.
-     * @param {Object} entry The relevant entry in data model.
-     * @private
-     */
-    loadAddressEditor_: function(entry) {
-      if (entry.isLocal)
-        chrome.send('loadAddressEditor', [entry.guid]);
-      else
-        window.open(loadTimeData.getString('manageWalletAddressesUrl'));
-    },
-
-    /**
-     * For local Autofill data, this function causes the AutofillOptionsHandler
-     * to call showEditCreditCardOverlay(). For Wallet data, the user is
-     * redirected to the Wallet web interface.
-     * @param {Object} entry The relevant entry in data model.
-     * @private
-     */
-    loadCreditCardEditor_: function(entry) {
-      if (entry.isLocal)
-        chrome.send('loadCreditCardEditor', [entry.guid]);
-      else
-        window.open(loadTimeData.getString('manageWalletPaymentMethodsUrl'));
-    },
-
-    /**
      * Shows the 'Edit address' overlay, using the data in |address| to fill the
      * input fields. |address| is a list with one item, an associative array
      * that contains the address data.
@@ -252,14 +224,6 @@
     AutofillOptions.getInstance().removeData_(guid, metricsAction);
   };
 
-  AutofillOptions.loadAddressEditor = function(entry) {
-    AutofillOptions.getInstance().loadAddressEditor_(entry);
-  };
-
-  AutofillOptions.loadCreditCardEditor = function(entry) {
-    AutofillOptions.getInstance().loadCreditCardEditor_(entry);
-  };
-
   AutofillOptions.editAddress = function(address) {
     AutofillOptions.getInstance().showEditAddressOverlay_(address);
   };
diff --git a/chrome/browser/resources/options/autofill_options_list.js b/chrome/browser/resources/options/autofill_options_list.js
index 46aff21..004f6213 100644
--- a/chrome/browser/resources/options/autofill_options_list.js
+++ b/chrome/browser/resources/options/autofill_options_list.js
@@ -90,11 +90,23 @@
       }
 
       // The 'Edit' button.
-      var guid = this.metadata_.guid;
+      var metadata = this.metadata_;
       var editButtonEl = AutofillEditProfileButton(
-          function() { AutofillOptions.loadAddressEditor(guid); });
+          AddressListItem.prototype.loadAddressEditor.bind(this));
       this.contentElement.appendChild(editButtonEl);
     },
+
+    /**
+     * For local Autofill data, this function causes the AutofillOptionsHandler
+     * to call showEditAddressOverlay(). For Wallet data, the user is
+     * redirected to the Wallet web interface.
+     */
+    loadAddressEditor: function() {
+      if (this.metadata_.isLocal)
+        chrome.send('loadAddressEditor', [this.metadata_.guid]);
+      else
+        window.open(loadTimeData.getString('manageWalletAddressesUrl'));
+    },
   };
 
   /**
@@ -151,10 +163,23 @@
       }
 
       // The 'Edit' button.
+      var metadata = this.metadata_;
       var editButtonEl = AutofillEditProfileButton(
-          function() { AutofillOptions.loadCreditCardEditor(guid); });
+          CreditCardListItem.prototype.loadCreditCardEditor.bind(this));
       this.contentElement.appendChild(editButtonEl);
     },
+
+    /**
+     * For local Autofill data, this function causes the AutofillOptionsHandler
+     * to call showEditCreditCardOverlay(). For Wallet data, the user is
+     * redirected to the Wallet web interface.
+     */
+    loadCreditCardEditor: function() {
+      if (this.metadata_.isLocal)
+        chrome.send('loadCreditCardEditor', [this.metadata_.guid]);
+      else
+        window.open(loadTimeData.getString('manageWalletPaymentMethodsUrl'));
+    },
   };
 
   /**
@@ -408,7 +433,7 @@
 
     /** @override */
     activateItemAtIndex: function(index) {
-      AutofillOptions.loadAddressEditor(this.dataModel.item(index));
+      this.getListItemByIndex(index).loadAddressEditor();
     },
 
     /**
@@ -442,7 +467,7 @@
 
     /** @override */
     activateItemAtIndex: function(index) {
-      AutofillOptions.loadCreditCardEditor(this.dataModel.item(index));
+      this.getListItemByIndex(index).loadCreditCardEditor();
     },
 
     /**
diff --git a/chrome/browser/resources/options/browser_options.html b/chrome/browser/resources/options/browser_options.html
index d40e318d..ab2d9408 100644
--- a/chrome/browser/resources/options/browser_options.html
+++ b/chrome/browser/resources/options/browser_options.html
@@ -470,7 +470,7 @@
                 pref="rappor.enabled"></span>
           </span>
         </label>
-      <div>
+      </div>
 </if>  <!-- _google_chrome -->
       <div class="checkbox">
         <label>
diff --git a/chrome/browser/resources/options/clear_browser_data_overlay.html b/chrome/browser/resources/options/clear_browser_data_overlay.html
index b018b03b..001bfdc8 100644
--- a/chrome/browser/resources/options/clear_browser_data_overlay.html
+++ b/chrome/browser/resources/options/clear_browser_data_overlay.html
@@ -74,8 +74,8 @@
       </div>
     </div>
     <div id="flash-storage-settings" class="flash-plugin-area">
-      <a target="_blank" i18n-content="flash_storage_settings"
-          i18n-values="href:flash_storage_url"></a>
+      <a target="_blank" i18n-content="flashStorageSettings"
+          i18n-values="href:flashStorageUrl"></a>
     </div>
   </div>
   <div class="action-area">
diff --git a/chrome/browser/resources/options/content_settings.html b/chrome/browser/resources/options/content_settings.html
index cb14de15..96e3c367 100644
--- a/chrome/browser/resources/options/content_settings.html
+++ b/chrome/browser/resources/options/content_settings.html
@@ -40,7 +40,7 @@
           <label>
             <input pref="profile.block_third_party_cookies" type="checkbox">
             <span>
-              <span i18n-content="cookies_block_3rd_party"></span>
+              <span i18n-content="cookiesBlock3rdParty"></span>
               <span class="controlled-setting-indicator"
                   pref="profile.block_third_party_cookies"></span>
           </label>
@@ -51,9 +51,9 @@
           <label>
             <input id="clear-cookies-on-exit"
                 pref="profile.clear_site_data_on_exit" type="checkbox">
-            <span i18n-content="cookies_lso_clear_when_close"
+            <span i18n-content="cookiesLsoClearWhenClose"
                 class="clear-plugin-lso-data-enabled"></span>
-            <span i18n-content="cookies_clear_when_close"
+            <span i18n-content="cookiesClearWhenClose"
                 class="clear-plugin-lso-data-disabled"></span>
           </label>
         </div>
@@ -61,7 +61,7 @@
           <button class="exceptions-list-button" contentType="cookies"
               i18n-content="manageExceptions"></button>
           <button id="show-cookies-button"
-              i18n-content="cookies_show_cookies"></button>
+              i18n-content="cookiesShowCookies"></button>
         </div>
      </div>
       <div class="experimental-website-settings" hidden>
@@ -70,7 +70,7 @@
           <button class="website-settings-permission-button"
               contentType="cookies" i18n-content="websitesManage"></button>
           <button id="show-cookies-button"
-              i18n-content="cookies_show_cookies"></button>
+              i18n-content="cookiesShowCookies"></button>
         </div>
       </div>
     </section>
@@ -168,7 +168,7 @@
         </div>
         <div class="settings-row">
           <button id="manage-handlers-button" contentType="handlers"
-              i18n-content="manage_handlers"></button>
+              i18n-content="manageHandlers"></button>
         </div>
       </div>
     </section>
@@ -600,7 +600,7 @@
     </section>
     <!-- MIDI system exclusive messages filter -->
     <section>
-      <h3 i18n-content="midi-sysex_header"></h3>
+      <h3 i18n-content="midiSysexHeader"></h3>
       <div>
         <div class="radio">
           <label>
@@ -628,7 +628,7 @@
     </section>
     <!-- Push messaging filter -->
     <section id="experimental-push-messaging-settings" hidden="true">
-      <h3 i18n-content="push-messaging_header"></h3>
+      <h3 i18n-content="pushMessagingHeader"></h3>
       <div>
         <div class="radio">
           <label>
@@ -656,7 +656,7 @@
     </section>
     <!-- Page zoom levels -->
     <section id="page-zoom-levels">
-      <h3 i18n-content="zoomlevels_header"></h3>
+      <h3 i18n-content="zoomlevelsHeader"></h3>
       <div>
         <div class="settings-row">
           <button class="exceptions-list-button" contentType="zoomlevels"
diff --git a/chrome/browser/resources/options/content_settings_exceptions_area.html b/chrome/browser/resources/options/content_settings_exceptions_area.html
index 45a1ccd7..3c3daef 100644
--- a/chrome/browser/resources/options/content_settings_exceptions_area.html
+++ b/chrome/browser/resources/options/content_settings_exceptions_area.html
@@ -17,20 +17,20 @@
     <div contentType="cookies">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
       <div class="flash-plugin-area">
-        <a i18n-values="href:flash_storage_url" target="_blank"
-            i18n-content="flash_storage_settings">
+        <a i18n-values="href:flashStorageUrl" target="_blank"
+            i18n-content="flashStorageSettings">
         </a>
       </div>
     </div>
     <div contentType="images">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
@@ -38,7 +38,7 @@
     <div contentType="javascript">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
@@ -46,7 +46,7 @@
     <div contentType="plugins">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
@@ -54,7 +54,7 @@
     <div contentType="popups">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
@@ -68,7 +68,7 @@
     <div contentType="fullscreen">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
@@ -76,7 +76,7 @@
     <div contentType="mouselock">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
@@ -84,7 +84,7 @@
     <div contentType="protectedContent">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
@@ -99,7 +99,7 @@
     <div contentType="media-stream">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
@@ -112,7 +112,7 @@
     <div contentType="ppapi-broker">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
@@ -123,7 +123,7 @@
     <div contentType="midi-sysex">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
@@ -131,7 +131,7 @@
     <div contentType="push-messaging">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
@@ -139,7 +139,7 @@
     <div contentType="zoomlevels">
       <list mode="normal"></list>
       <div>
-        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        <span class="otr-explanation" i18n-content="otrExceptionsExplanation">
         </span>
         <list mode="otr"></list>
       </div>
diff --git a/chrome/browser/resources/options/content_settings_exceptions_area.js b/chrome/browser/resources/options/content_settings_exceptions_area.js
index 74bd582..12004a5 100644
--- a/chrome/browser/resources/options/content_settings_exceptions_area.js
+++ b/chrome/browser/resources/options/content_settings_exceptions_area.js
@@ -189,7 +189,7 @@
       // icon of the app.
       if (controlledBy == 'HostedApp') {
         this.title =
-            loadTimeData.getString('set_by') + ' ' + this.dataItem.appName;
+            loadTimeData.getString('setBy') + ' ' + this.dataItem.appName;
         var button = this.querySelector('.row-delete-button');
         // Use the host app's favicon (16px, match bigger size).
         // See c/b/ui/webui/extensions/extension_icon_source.h
@@ -621,7 +621,10 @@
       this.title = loadTimeData.getString(type + 'TabTitle');
 
       var header = this.pageDiv.querySelector('h1');
-      header.textContent = loadTimeData.getString(type + '_header');
+      var camelCasedType = type.replace(/-([a-z])/g, function(g) {
+        return g[1].toUpperCase();
+      });
+      header.textContent = loadTimeData.getString(camelCasedType + 'Header');
 
       var divs = this.pageDiv.querySelectorAll('div[contentType]');
       for (var i = 0; i < divs.length; i++) {
diff --git a/chrome/browser/resources/options/manage_profile_overlay.js b/chrome/browser/resources/options/manage_profile_overlay.js
index 0be7f9f..5b3e41f 100644
--- a/chrome/browser/resources/options/manage_profile_overlay.js
+++ b/chrome/browser/resources/options/manage_profile_overlay.js
@@ -573,6 +573,7 @@
       // Because this dialog isn't useful when refreshing or as part of the
       // history, don't create a history entry for it when showing.
       PageManager.showPageByName('manageProfile', false);
+      chrome.send('logDeleteUserDialogShown');
     },
 
     /**
diff --git a/chrome/browser/resources/options/password_manager.js b/chrome/browser/resources/options/password_manager.js
index ed8e30a0..6e16ebac 100644
--- a/chrome/browser/resources/options/password_manager.js
+++ b/chrome/browser/resources/options/password_manager.js
@@ -180,7 +180,7 @@
               entry[1].toLowerCase().indexOf(query.toLowerCase()) >= 0) {
             // Keep the original index so we can delete correctly. See also
             // deleteItemAtIndex() in password_manager_list.js that uses this.
-            entry[3] = index;
+            entry[4] = index;
             return true;
           }
           return false;
@@ -214,7 +214,7 @@
         // index in the model, but each entry stores its original index, so
         // we can find the item using a linear search.
         for (var i = 0; i < model.length; ++i) {
-          if (model.item(i)[3] == index) {
+          if (model.item(i)[4] == index) {
             index = i;
             break;
           }
diff --git a/chrome/browser/resources/options/password_manager_list.css b/chrome/browser/resources/options/password_manager_list.css
index 8404784..ee17a12 100644
--- a/chrome/browser/resources/options/password_manager_list.css
+++ b/chrome/browser/resources/options/password_manager_list.css
@@ -34,12 +34,14 @@
 
 #saved-passwords-list .name {
   -webkit-box-flex: 1;
-  width: 20%;
+  width: 30%;
 }
 
-#saved-passwords-list .password {
+#saved-passwords-list .password,
+#saved-passwords-list .federation {
   -webkit-box-flex: 1;
   position: relative;
+  width: 30%;
 }
 
 #saved-passwords-list .password input[type='password'],
@@ -54,6 +56,7 @@
 
 #saved-passwords-list .url,
 #saved-passwords-list .name,
+#saved-passwords-list .federation,
 #password-exceptions-list .url {
   overflow: hidden;
   text-overflow: ellipsis;
diff --git a/chrome/browser/resources/options/password_manager_list.js b/chrome/browser/resources/options/password_manager_list.js
index 9578199..f8ed9e8 100644
--- a/chrome/browser/resources/options/password_manager_list.js
+++ b/chrome/browser/resources/options/password_manager_list.js
@@ -8,12 +8,19 @@
   /** @const */ var DeletableItem = options.DeletableItem;
   /** @const */ var List = cr.ui.List;
 
+  /** @const */ var URL_DATA_INDEX = 0;
+  /** @const */ var USERNAME_DATA_INDEX = 1;
+  /** @const */ var PASSWORD_DATA_INDEX = 2;
+  /** @const */ var FEDERATION_DATA_INDEX = 3;
+  /** @const */ var ORIGINAL_DATA_INDEX = 4;
+
   /**
    * Creates a new passwords list item.
    * @param {cr.ui.ArrayDataModel} dataModel The data model that contains this
    *     item.
-   * @param {Array} entry An array of the form [url, username, password]. When
-   *     the list has been filtered, a fourth element [index] may be present.
+   * @param {Array} entry An array of the form [url, username, password,
+   *     federation]. When the list has been filtered, a fifth element [index]
+   *     may be present.
    * @param {boolean} showPasswords If true, add a button to the element to
    *     allow the user to reveal the saved password.
    * @constructor
@@ -61,45 +68,53 @@
       usernameLabel.title = this.username;
       this.contentElement.appendChild(usernameLabel);
 
-      // The stored password.
-      var passwordInputDiv = this.ownerDocument.createElement('div');
-      passwordInputDiv.className = 'password';
+      if (this.federation) {
+        // The federation.
+        var federationDiv = this.ownerDocument.createElement('div');
+        federationDiv.className = 'federation';
+        federationDiv.textContent = this.federation;
+        this.contentElement.appendChild(federationDiv);
+      } else {
+        // The stored password.
+        var passwordInputDiv = this.ownerDocument.createElement('div');
+        passwordInputDiv.className = 'password';
 
-      // The password input field.
-      var passwordInput = this.ownerDocument.createElement('input');
-      passwordInput.type = 'password';
-      passwordInput.className = 'inactive-password';
-      passwordInput.readOnly = true;
-      passwordInput.value = this.showPasswords_ ? this.password : '********';
-      passwordInputDiv.appendChild(passwordInput);
-      var deletableItem = this;
-      passwordInput.addEventListener('focus', function() {
-        deletableItem.handleFocus();
-      });
-      this.passwordField = passwordInput;
-      this.setFocusable_(false);
-
-      // The show/hide button.
-      if (this.showPasswords_) {
-        var button = this.ownerDocument.createElement('button');
-        button.hidden = true;
-        button.className = 'list-inline-button custom-appearance';
-        button.textContent = loadTimeData.getString('passwordShowButton');
-        button.addEventListener('click', this.onClick_.bind(this), true);
-        button.addEventListener('mousedown', function(event) {
-          // Don't focus on this button by mousedown.
-          event.preventDefault();
-          // Don't handle list item selection. It causes focus change.
-          event.stopPropagation();
-        }, false);
-        button.addEventListener('focus', function() {
+        // The password input field.
+        var passwordInput = this.ownerDocument.createElement('input');
+        passwordInput.type = 'password';
+        passwordInput.className = 'inactive-password';
+        passwordInput.readOnly = true;
+        passwordInput.value = this.showPasswords_ ? this.password : '********';
+        passwordInputDiv.appendChild(passwordInput);
+        var deletableItem = this;
+        passwordInput.addEventListener('focus', function() {
           deletableItem.handleFocus();
         });
-        passwordInputDiv.appendChild(button);
-        this.passwordShowButton = button;
+        this.passwordField = passwordInput;
+        this.setFocusable_(false);
+
+        // The show/hide button.
+        if (this.showPasswords_) {
+          var button = this.ownerDocument.createElement('button');
+          button.hidden = true;
+          button.className = 'list-inline-button custom-appearance';
+          button.textContent = loadTimeData.getString('passwordShowButton');
+          button.addEventListener('click', this.onClick_.bind(this), true);
+          button.addEventListener('mousedown', function(event) {
+            // Don't focus on this button by mousedown.
+            event.preventDefault();
+            // Don't handle list item selection. It causes focus change.
+            event.stopPropagation();
+          }, false);
+          button.addEventListener('focus', function() {
+            deletableItem.handleFocus();
+          });
+          passwordInputDiv.appendChild(button);
+          this.passwordShowButton = button;
+        }
+        this.contentElement.appendChild(passwordInputDiv);
       }
 
-      this.contentElement.appendChild(passwordInputDiv);
     },
 
     /** @override */
@@ -161,7 +176,7 @@
      * @private
      */
     getOriginalIndex_: function() {
-      var index = this.dataItem[3];
+      var index = this.dataItem[ORIGINAL_DATA_INDEX];
       return index ? index : this.dataModel.indexOf(this.dataItem);
     },
 
@@ -184,10 +199,10 @@
      * @type {string}
      */
     get url() {
-      return this.dataItem[0];
+      return this.dataItem[URL_DATA_INDEX];
     },
     set url(url) {
-      this.dataItem[0] = url;
+      this.dataItem[URL_DATA_INDEX] = url;
     },
 
     /**
@@ -195,10 +210,10 @@
      * @type {string}
      */
     get username() {
-      return this.dataItem[1];
+      return this.dataItem[USERNAME_DATA_INDEX];
     },
     set username(username) {
-      this.dataItem[1] = username;
+      this.dataItem[USERNAME_DATA_INDEX] = username;
     },
 
     /**
@@ -206,10 +221,21 @@
      * @type {string}
      */
     get password() {
-      return this.dataItem[2];
+      return this.dataItem[PASSWORD_DATA_INDEX];
     },
     set password(password) {
-      this.dataItem[2] = password;
+      this.dataItem[PASSWORD_DATA_INDEX] = password;
+    },
+
+    /**
+     * Get and set the federation for the entry.
+     * @type {string}
+     */
+    get federation() {
+      return this.dataItem[FEDERATION_DATA_INDEX];
+    },
+    set federation(federation) {
+      this.dataItem[FEDERATION_DATA_INDEX] = federation;
     },
   };
 
@@ -318,9 +344,9 @@
     /** @override */
     deleteItemAtIndex: function(index) {
       var item = this.dataModel.item(index);
-      if (item && item.length > 3) {
-        // The fourth element, if present, is the original index to delete.
-        index = item[3];
+      if (item && item[ORIGINAL_DATA_INDEX] != undefined) {
+        // The fifth element, if present, is the original index to delete.
+        index = item[ORIGINAL_DATA_INDEX];
       }
       PasswordManager.removeSavedPassword(index);
     },
diff --git a/chrome/browser/resources/pdf/main.js b/chrome/browser/resources/pdf/main.js
index 07fa2e0..6900c6dc 100644
--- a/chrome/browser/resources/pdf/main.js
+++ b/chrome/browser/resources/pdf/main.js
@@ -42,6 +42,18 @@
 
   function generateStreamDetailsAndInitViewer() {
     var url = window.location.search.substring(1);
+
+    // Hack to enable custom scrollbars for print preview on non-retina mac
+    // displays. Remove after crbug.com/466039 is fixed.
+    if (url.indexOf(IS_MAC_PARAM) === 0) {
+      url = url.substring(IS_MAC_PARAM.length);
+      var link = document.createElement('link');
+      link.rel = 'stylesheet';
+      link.type = 'text/css';
+      link.href = 'scrollbars_mac.css';
+      document.getElementsByTagName('head')[0].appendChild(link);
+    }
+
     var streamDetails = {
       streamUrl: url,
       originalUrl: url,
diff --git a/chrome/browser/resources/pdf/pdf.js b/chrome/browser/resources/pdf/pdf.js
index c652ca4f..4fae231 100644
--- a/chrome/browser/resources/pdf/pdf.js
+++ b/chrome/browser/resources/pdf/pdf.js
@@ -313,13 +313,6 @@
           e.preventDefault();
         }
         return;
-      case 80:  // p key.
-        if (e.ctrlKey || e.metaKey) {
-          this.print_();
-          // Since we do the printing of the page.
-          e.preventDefault();
-        }
-        return;
       case 219:  // left bracket.
         if (e.ctrlKey)
           this.rotateCounterClockwise_();
diff --git a/chrome/browser/resources/pdf/pdf_scripting_api.js b/chrome/browser/resources/pdf/pdf_scripting_api.js
index 84b94b12..fa570aee 100644
--- a/chrome/browser/resources/pdf/pdf_scripting_api.js
+++ b/chrome/browser/resources/pdf/pdf_scripting_api.js
@@ -250,6 +250,8 @@
   },
 };
 
+var IS_MAC_PARAM = 'isMac&';
+
 /**
  * Creates a PDF viewer with a scripting interface. This is basically 1) an
  * iframe which is navigated to the PDF viewer extension and 2) a scripting
@@ -260,9 +262,11 @@
  */
 function PDFCreateOutOfProcessPlugin(src) {
   var iframe = window.document.createElement('iframe');
+  var isMac = cr.isMac ? IS_MAC_PARAM : '';
   iframe.setAttribute(
       'src',
-      'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html?' + src);
+      'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html?' +
+      isMac + src);
   // Prevent the frame from being tab-focusable.
   iframe.setAttribute('tabindex', '-1');
   var client = new PDFScriptingAPI(window);
diff --git a/chrome/browser/resources/pdf/scrollbars_mac.css b/chrome/browser/resources/pdf/scrollbars_mac.css
new file mode 100644
index 0000000..aaebda0
--- /dev/null
+++ b/chrome/browser/resources/pdf/scrollbars_mac.css
@@ -0,0 +1,33 @@
+/* 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. */
+
+/*
+ * Imitate mac scrollbars on non-retina displays to workaround crbug.com/466039.
+ */
+@media
+(-webkit-max-device-pixel-ratio: 1) {
+
+::-webkit-scrollbar {
+  height: 14px;
+  width: 14px;
+}
+
+::-webkit-scrollbar-track,
+::-webkit-scrollbar-corner {
+  background-color: rgb(242,242,242);
+}
+
+::-webkit-scrollbar-thumb:vertical,
+::-webkit-scrollbar-thumb:horizontal {
+  -webkit-border-radius: 7px;
+  background-color: rgb(198,198,198);
+  border: 3px solid rgb(242,242,242);
+}
+
+::-webkit-scrollbar-thumb:vertical:hover,
+::-webkit-scrollbar-thumb:horizontal:hover {
+  background-color: rgb(126,126,126);
+}
+
+}
diff --git a/chrome/browser/resources/pdf/viewport.js b/chrome/browser/resources/pdf/viewport.js
index 06428a9d..20c8460 100644
--- a/chrome/browser/resources/pdf/viewport.js
+++ b/chrome/browser/resources/pdf/viewport.js
@@ -242,6 +242,7 @@
    * @param {number} newZoom the zoom level to zoom to.
    */
   setZoom: function(newZoom) {
+    this.fittingType_ = Viewport.FittingType.NONE;
     newZoom = Math.max(Viewport.ZOOM_FACTOR_RANGE.min,
                        Math.min(newZoom, Viewport.ZOOM_FACTOR_RANGE.max));
     this.mightZoom_(function() {
diff --git a/chrome/browser/resources/security_warnings/extended_reporting.js b/chrome/browser/resources/security_warnings/extended_reporting.js
new file mode 100644
index 0000000..51b4b21
--- /dev/null
+++ b/chrome/browser/resources/security_warnings/extended_reporting.js
@@ -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.
+
+'use strict';
+
+// Other constants defined in security_interstitial_page.h.
+var SB_BOX_CHECKED = 'boxchecked';
+var SB_DISPLAY_CHECK_BOX = 'displaycheckbox';
+
+// This sets up the Extended Safe Browsing Reporting opt-in, either for
+// reporting malware or invalid certificate chains. Does nothing if the
+// interstitial type is not SAFEBROWSING or SSL.
+function setupExtendedReportingCheckbox() {
+  var interstitialType = loadTimeData.getString('type');
+  if (interstitialType != 'SAFEBROWSING' && interstitialType != 'SSL') {
+    return;
+  }
+
+  if (!loadTimeData.getBoolean(SB_DISPLAY_CHECK_BOX)) {
+    return;
+  }
+
+  $('opt-in-label').innerHTML = loadTimeData.getString('optInLink');
+  $('opt-in-checkbox').checked = loadTimeData.getBoolean(SB_BOX_CHECKED);
+  $('extended-reporting-opt-in').classList.remove('hidden');
+
+  var className = interstitialType == 'SAFEBROWSING' ?
+                  'safe-browsing-opt-in' :
+                  'ssl-opt-in';
+  $('extended-reporting-opt-in').classList.add(className);
+
+  $('body').classList.add('extended-reporting-has-checkbox');
+
+  $('opt-in-checkbox').addEventListener('click', function() {
+    sendCommand($('opt-in-checkbox').checked ?
+                CMD_DO_REPORT :
+                CMD_DONT_REPORT);
+  });
+}
diff --git a/chrome/browser/resources/security_warnings/interstitial_v2.css b/chrome/browser/resources/security_warnings/interstitial_v2.css
index ee9a19f3..ca6750b 100644
--- a/chrome/browser/resources/security_warnings/interstitial_v2.css
+++ b/chrome/browser/resources/security_warnings/interstitial_v2.css
@@ -146,7 +146,7 @@
   display: inline;
 }
 
-#malware-opt-in {
+#extended-reporting-opt-in {
   font-size: .875em;
   margin-top: 39px;
 }
@@ -224,7 +224,6 @@
 
 .styled-checkbox label {
   background: transparent;
-  border: white solid 1px;
   border-radius: 2px;
   height: 14px;
   left: 0;
@@ -236,9 +235,6 @@
 
 .styled-checkbox .checkbox-tick {
   background: transparent;
-  border: 2px solid white;
-  border-right-width: 0;
-  border-top-width: 0;
   content: '';
   height: 4px;
   left: 2px;
@@ -249,6 +245,26 @@
   width: 9px;
 }
 
+.safe-browsing-opt-in .styled-checkbox label {
+  border: white solid 1px;
+}
+
+.safe-browsing-opt-in .styled-checkbox .checkbox-tick {
+  border: white solid 1px;
+  border-right-width: 0;
+  border-top-width: 0;
+}
+
+.ssl-opt-in .styled-checkbox label {
+  border: #696969 solid 1px;
+}
+
+.ssl-opt-in .styled-checkbox .checkbox-tick {
+  border: #696969 solid 1px;
+  border-right-width: 0;
+  border-top-width: 0;
+}
+
 .styled-checkbox input[type=checkbox]:checked ~ .checkbox-tick {
   opacity: 1;
 }
@@ -304,7 +320,7 @@
     padding: 0 5%;
   }
 
-  #malware-opt-in {
+  #extended-reporting-opt-in {
     margin-top: 24px;
   }
 
@@ -442,7 +458,7 @@
 }
 
 @media (min-height: 400px) and (orientation:portrait) {
-  body:not(.safe-browsing-has-checkbox) .interstitial-wrapper {
+  body:not(.extended-reporting-has-checkbox) .interstitial-wrapper {
     margin-bottom: 145px;
   }
 }
@@ -476,7 +492,7 @@
 }
 
 @media (min-height: 500px) and (max-width: 414px) and (orientation: portrait) {
-  :not(.safe-browsing-has-checkbox) .interstitial-wrapper {
+  :not(.extended-reporting-has-checkbox) .interstitial-wrapper {
     margin-top: 96px;
   }
 }
@@ -558,7 +574,7 @@
     margin-top: 0;
   }
 
-  #malware-opt-in {
+  #extended-reporting-opt-in {
     margin-top: 0;
   }
 }
@@ -618,30 +634,30 @@
   }
 }
 
-/* Malware opt-in. No fixed nav. */
+/* Extended reporting opt-in. No fixed nav. */
 @media (max-height: 600px) and (orientation: portrait),
        (max-height: 360px) and (max-width: 680px) and (orientation: landscape) {
-  .safe-browsing-has-checkbox .interstitial-wrapper {
+  .extended-reporting-has-checkbox .interstitial-wrapper {
     display: flex;
     flex-direction: column;
     margin-bottom: 0;
   }
 
-  .safe-browsing-has-checkbox #details {
+  .extended-reporting-has-checkbox #details {
     flex: 1 1 auto;
     order: 0;
   }
 
-  .safe-browsing-has-checkbox #main-content {
+  .extended-reporting-has-checkbox #main-content {
     flex: 1 1 auto;
     order: 0;
   }
 
-  .safe-browsing-has-checkbox #malware-opt-in {
+  .extended-reporting-has-checkbox #extended-reporting-opt-in {
     margin-bottom: 8px;
   }
 
-  body.safe-browsing-has-checkbox .nav-wrapper {
+  body.extended-reporting-has-checkbox .nav-wrapper {
     flex: 0 1 auto;
     margin-top: 0;
     order: 1;
diff --git a/chrome/browser/resources/security_warnings/interstitial_v2.html b/chrome/browser/resources/security_warnings/interstitial_v2.html
index b06fe1f..a4b7ca9d 100644
--- a/chrome/browser/resources/security_warnings/interstitial_v2.html
+++ b/chrome/browser/resources/security_warnings/interstitial_v2.html
@@ -9,7 +9,7 @@
   <script src="../../../../ui/webui/resources/js/util.js"></script>
   <script src="captive_portal.js"></script>
   <script src="ssl.js"></script>
-  <script src="safe_browsing.js"></script>
+  <script src="extended_reporting.js"></script>
   <script src="interstitial_v2_mobile.js"></script>
   <script src="interstitial_v2.js"></script>
 </head>
@@ -25,7 +25,7 @@
           <div id="error-debugging-info" class="hidden"></div>
         </div>
       </div>
-      <div id="malware-opt-in" class="hidden">
+      <div id="extended-reporting-opt-in" class="hidden">
         <div class="styled-checkbox">
           <label>
             <input type="checkbox" id="opt-in-checkbox">
diff --git a/chrome/browser/resources/security_warnings/interstitial_v2.js b/chrome/browser/resources/security_warnings/interstitial_v2.js
index 925d0b9d..bd19272 100644
--- a/chrome/browser/resources/security_warnings/interstitial_v2.js
+++ b/chrome/browser/resources/security_warnings/interstitial_v2.js
@@ -173,7 +173,7 @@
   }
 
   preventDefaultOnPoundLinkClicks();
-  setupCheckbox();
+  setupExtendedReportingCheckbox();
   setupSSLDebuggingInfo();
   document.addEventListener('keypress', handleKeypress);
 }
diff --git a/chrome/browser/resources/security_warnings/safe_browsing.js b/chrome/browser/resources/security_warnings/safe_browsing.js
deleted file mode 100644
index 1fa4555..0000000
--- a/chrome/browser/resources/security_warnings/safe_browsing.js
+++ /dev/null
@@ -1,25 +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.
-
-// Other constants defined in safe_browsing_blocking_page.cc.
-var SB_BOX_CHECKED = 'boxchecked';
-var SB_DISPLAY_CHECK_BOX = 'displaycheckbox';
-
-// This sets up the Extended Safe Browsing Reporting opt-in.
-function setupCheckbox() {
-  if (loadTimeData.getString('type') != 'SAFEBROWSING' ||
-      !loadTimeData.getBoolean(SB_DISPLAY_CHECK_BOX)) {
-    return;
-  }
-
-  $('opt-in-label').innerHTML = loadTimeData.getString('optInLink');
-  $('opt-in-checkbox').checked = loadTimeData.getBoolean(SB_BOX_CHECKED);
-  $('malware-opt-in').classList.remove('hidden');
-  $('body').classList.add('safe-browsing-has-checkbox');
-
-  $('opt-in-checkbox').addEventListener('click', function() {
-    sendCommand(
-        $('opt-in-checkbox').checked ? CMD_DO_REPORT : CMD_DONT_REPORT);
-  });
-}
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.css b/chrome/browser/resources/settings/a11y_page/a11y_page.css
index df22ba5..2277ed3 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.css
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.css
@@ -17,3 +17,7 @@
   margin: 0;
   padding: 0 0 0.25em;
 }
+
+.more-a11y-link {
+  margin-bottom: 10px;
+}
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html
index 4e86ce3..ba4ab8e 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -13,6 +13,14 @@
     <paper-shadow layout vertical cross-fade>
       <cr-settings-page-header page="{{}}"></cr-settings-page-header>
 
+      <div class="more-a11y-link">
+        <a href="https://chrome.google.com/webstore/category/collection/accessibility"
+            target="_blank">
+          Add additional accessibility features
+        </a>
+      </div>
+
+<if expr="chromeos">
       <cr-settings-checkbox pref="{{prefs.settings.a11y.enable_menu}}"
           label="Show accessibility options in the system menu">
       </cr-settings-checkbox>
@@ -53,6 +61,8 @@
 
       <cr-settings-checkbox pref="{{prefs.settings.a11y.virtual_keyboard}}"
           label="Enable on-screen keyboard"></cr-settings-checkbox>
+</if>
+
     </paper-shadow>
   </template>
   <script src="a11y_page.js"></script>
diff --git a/chrome/browser/resources/settings/checkbox/checkbox.html b/chrome/browser/resources/settings/checkbox/checkbox.html
index fe56a01..e660fdb 100644
--- a/chrome/browser/resources/settings/checkbox/checkbox.html
+++ b/chrome/browser/resources/settings/checkbox/checkbox.html
@@ -1,11 +1,14 @@
 <link rel="import" href="chrome://resources/polymer/core-label/core-label.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_events/cr_events.html">
+<link rel="import" href="chrome://md-settings/pref_tracker/pref_tracker.html">
 
 <polymer-element name="cr-settings-checkbox">
   <template>
     <link rel="stylesheet" href="checkbox.css">
     <cr-events id="events"></cr-events>
+    <cr-settings-pref-tracker pref="{{pref}}"></cr-settings-pref-tracker>
+
     <core-label horizontal layout>
       <cr-checkbox id="checkbox" checked="{{pref.value}}"
           disabled="{{pref.disabled}}" for></cr-checkbox>
diff --git a/chrome/browser/resources/settings/pref_tracker/pref_tracker.html b/chrome/browser/resources/settings/pref_tracker/pref_tracker.html
new file mode 100644
index 0000000..91f4b3fd
--- /dev/null
+++ b/chrome/browser/resources/settings/pref_tracker/pref_tracker.html
@@ -0,0 +1,6 @@
+<link rel="import" href="chrome://resources/polymer/polymer/polymer.html">
+
+<polymer-element name="cr-settings-pref-tracker">
+  <script src="chrome://md-settings/prefs/prefs_types.js"></script>
+  <script src="pref_tracker.js"></script>
+</polymer-element>
diff --git a/chrome/browser/resources/settings/pref_tracker/pref_tracker.js b/chrome/browser/resources/settings/pref_tracker/pref_tracker.js
new file mode 100644
index 0000000..104b1862
--- /dev/null
+++ b/chrome/browser/resources/settings/pref_tracker/pref_tracker.js
@@ -0,0 +1,85 @@
+// 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
+ * `cr-settings-pref-tracker` is a utility element used to track the
+ * initialization of a specified preference and throw an error if the pref
+ * is not defined after prefs have all been fetched.
+ *
+ * Example:
+ *
+ *    <cr-settings-pref-tracker pref="{{prefs.settings.foo.bar}}">
+ *    </cr-settings-pref-tracker>
+ *
+ * @element cr-settings-pref-tracker
+ */
+(function() {
+
+  /**
+   * An array of all the tracker instances.
+   * @type {!Array<!CrSettingsPrefTrackerElement>}
+   */
+  var instances = [];
+
+  /**
+   * Validates all tracker instances.
+   * @private
+   */
+  var validateAll_ = function() {
+    instances.forEach(function(tracker) {
+      tracker.validate_();
+    });
+  };
+
+  document.addEventListener(CrSettingsPrefs.INITIALIZED, validateAll_);
+
+  Polymer('cr-settings-pref-tracker', {
+    publish: {
+      /**
+       * The Preference object being tracked.
+       *
+       * @attribute pref
+       * @type {Object}
+       * @default null
+       */
+      pref: null,
+    },
+
+    observe: {
+      pref: 'validate_',
+    },
+
+    /** @override */
+    ready: function() {
+      this.validate_();
+
+      instances.push(this);
+    },
+
+    /**
+     * Throws an error if prefs are initialized and the tracked pref is not
+     * found.
+     * @private
+     */
+    validate_: function() {
+      this.async(function() {
+        // Note that null == undefined.
+        if (CrSettingsPrefs.isInitialized && this.pref == null) {
+          // HACK ALERT: This is the best clue we have as to the pref key for
+          // this tracker. This value should not be relied upon anywhere or
+          // actually used besides for this error message.
+          var keyHint = '';
+          var parentPrefString = this.parentNode && this.parentNode.host &&
+              this.parentNode.host.getAttribute('pref');
+          if (parentPrefString) {
+            keyHint = parentPrefString.match(/{{([a-z._]+)}}/)[1];
+          }
+
+          throw new Error('Pref not found. Key Hint: ' + keyHint);
+        }
+      });
+    },
+  });
+})();
diff --git a/chrome/browser/resources/settings/prefs/prefs.html b/chrome/browser/resources/settings/prefs/prefs.html
index 3f82c40..a089264b 100644
--- a/chrome/browser/resources/settings/prefs/prefs.html
+++ b/chrome/browser/resources/settings/prefs/prefs.html
@@ -3,5 +3,6 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 
 <polymer-element name="cr-settings-prefs">
+  <script src="prefs_types.js"></script>
   <script src="prefs.js"></script>
 </polymer-element>
diff --git a/chrome/browser/resources/settings/prefs/prefs.js b/chrome/browser/resources/settings/prefs/prefs.js
index 11a42bd5..f7491d90 100644
--- a/chrome/browser/resources/settings/prefs/prefs.js
+++ b/chrome/browser/resources/settings/prefs/prefs.js
@@ -57,7 +57,7 @@
        * Object containing all preferences.
        *
        * @attribute settings
-       * @type CrSettingsPrefs.Settings
+       * @type {Object}
        * @default null
        */
       settings: null,
@@ -65,6 +65,7 @@
 
     /** @override */
     created: function() {
+      CrSettingsPrefs.isInitialized = false;
       this.settings = {};
       this.fetchSettings_();
     },
@@ -94,6 +95,8 @@
      */
     onPrefsFetched_: function(dict) {
       this.parsePrefDict_('', dict);
+      CrSettingsPrefs.isInitialized = true;
+      document.dispatchEvent(new Event(CrSettingsPrefs.INITIALIZED));
     },
 
     /**
diff --git a/chrome/browser/resources/settings/prefs/prefs_types.js b/chrome/browser/resources/settings/prefs/prefs_types.js
index ce4c93b..d757fc0 100644
--- a/chrome/browser/resources/settings/prefs/prefs_types.js
+++ b/chrome/browser/resources/settings/prefs/prefs_types.js
@@ -3,46 +3,21 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview Typedefs for CrSettingsPrefsElement.
+ * @fileoverview Types for CrSettingsPrefsElement.
  */
 
-var CrSettingsPrefs = {};
+if (CrSettingsPrefs === undefined) {
+  var CrSettingsPrefs = {};
 
-/**
- * @typedef {{
- *   a11y: CrSettingsPrefs.A11y,
- *   touchpad: CrSettingsPrefs.Touchpad,
- *   downloads: CrSettingsPrefs.Downloads,
- *   accessibility: boolean
- * }}
- */
-CrSettingsPrefs.Settings;
+  /**
+   * The type of the event fired when prefs have been fetched and initialized.
+   * @const {string}
+   */
+  CrSettingsPrefs.INITIALIZED = 'cr-settings-prefs-initialized';
 
-/**
- * @typedef {{
- *   enableMenu: boolean,
- *   largeCursorEnabled: boolean,
- *   highContrastEnabled: boolean,
- *   stickyKeysEnabled: boolean,
- *   screenMagnifier: boolean,
- *   autoclick: boolean,
- *   autoclickDelayMs: number,
- *   virtualKeyboard: boolean
- * }}
- */
-CrSettingsPrefs.A11y;
-
-/**
- * @typedef {{
- *   downloadLocation: string,
- *   promptForDownload: boolean
- * }}
- */
-CrSettingsPrefs.Downloads;
-
-/**
- * @typedef {{
- *   enableTapDragging: boolean
- * }}
- */
-CrSettingsPrefs.Touchpad;
+  /**
+   * Global boolean set to true when all settings have been initialized.
+   * @type {boolean}
+   */
+  CrSettingsPrefs.isInitialized = false;
+}
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 2f3aa18..55956b54 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -17,7 +17,9 @@
                  type="chrome_html" />
       <structure name="IDR_SETTINGS_A11Y_PAGE_HTML"
                  file="a11y_page/a11y_page.html"
-                 type="chrome_html" />
+                 type="chrome_html"
+                 flattenhtml="true"
+                 allowexternalscript="true" />
       <structure name="IDR_SETTINGS_A11Y_PAGE_CSS"
                  file="a11y_page/a11y_page.css"
                  type="chrome_html" />
@@ -99,6 +101,15 @@
       <structure name="IDR_SETTINGS_PREFS_HTML"
                  file="prefs/prefs.html"
                  type="chrome_html" />
+      <structure name="IDR_SETTINGS_PREFS_TYPES_JS"
+                 file="prefs/prefs_types.js"
+                 type="chrome_html" />
+      <structure name="IDR_SETTINGS_PREF_TRACKER_HTML"
+                 file="pref_tracker/pref_tracker.html"
+                 type="chrome_html" />
+      <structure name="IDR_SETTINGS_PREF_TRACKER_JS"
+                 file="pref_tracker/pref_tracker.js"
+                 type="chrome_html" />
       <structure name="IDR_SETTINGS_SETTINGS_HTML"
                  file="settings.html"
                  type="chrome_html" />
diff --git a/chrome/browser/safe_browsing/ping_manager.cc b/chrome/browser/safe_browsing/ping_manager.cc
index 0478341..947d8910 100644
--- a/chrome/browser/safe_browsing/ping_manager.cc
+++ b/chrome/browser/safe_browsing/ping_manager.cc
@@ -8,17 +8,27 @@
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "chrome/browser/net/certificate_error_reporter.h"
 #include "chrome/common/env_vars.h"
 #include "content/public/browser/browser_thread.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
+#include "net/ssl/ssl_info.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
 
+using chrome_browser_net::CertificateErrorReporter;
 using content::BrowserThread;
 
+namespace {
+// URL to upload invalid certificate chain reports
+// TODO(estark): Fill this in with the real URL when live.
+const char kExtendedReportingUploadUrl[] = "";
+}  // namespace
+
 // SafeBrowsingPingManager implementation ----------------------------------
 
 // static
@@ -34,7 +44,14 @@
     const SafeBrowsingProtocolConfig& config)
     : client_name_(config.client_name),
       request_context_getter_(request_context_getter),
-      url_prefix_(config.url_prefix) {
+      url_prefix_(config.url_prefix),
+      certificate_error_reporter_(
+          request_context_getter
+              ? new CertificateErrorReporter(
+                    request_context_getter->GetURLRequestContext(),
+                    GURL(kExtendedReportingUploadUrl),
+                    CertificateErrorReporter::SEND_COOKIES)
+              : nullptr) {
   DCHECK(!url_prefix_.empty());
 
   version_ = SafeBrowsingProtocolManagerHelper::Version();
@@ -95,6 +112,20 @@
   safebrowsing_reports_.insert(fetcher);
 }
 
+void SafeBrowsingPingManager::ReportInvalidCertificateChain(
+    const std::string& hostname,
+    const net::SSLInfo& ssl_info) {
+  DCHECK(certificate_error_reporter_);
+  certificate_error_reporter_->SendReport(
+      CertificateErrorReporter::REPORT_TYPE_EXTENDED_REPORTING, hostname,
+      ssl_info);
+}
+
+void SafeBrowsingPingManager::SetCertificateErrorReporterForTesting(
+    scoped_ptr<CertificateErrorReporter> certificate_error_reporter) {
+  certificate_error_reporter_ = certificate_error_reporter.Pass();
+}
+
 GURL SafeBrowsingPingManager::SafeBrowsingHitUrl(
     const GURL& malicious_url, const GURL& page_url,
     const GURL& referrer_url, bool is_subresource,
diff --git a/chrome/browser/safe_browsing/ping_manager.h b/chrome/browser/safe_browsing/ping_manager.h
index a33559b..28c536c 100644
--- a/chrome/browser/safe_browsing/ping_manager.h
+++ b/chrome/browser/safe_browsing/ping_manager.h
@@ -18,7 +18,12 @@
 #include "net/url_request/url_fetcher_delegate.h"
 #include "url/gurl.h"
 
+namespace chrome_browser_net {
+class CertificateErrorReporter;
+}
+
 namespace net {
+class SSLInfo;
 class URLRequestContextGetter;
 }  // namespace net
 
@@ -49,6 +54,14 @@
   // malware reports. |report| is the serialized report.
   void ReportMalwareDetails(const std::string& report);
 
+  // Users can opt-in on the SSL interstitial to send reports of invalid
+  // certificate chains.
+  void ReportInvalidCertificateChain(const std::string& hostname,
+                                     const net::SSLInfo& ssl_info);
+
+  void SetCertificateErrorReporterForTesting(scoped_ptr<
+      chrome_browser_net::CertificateErrorReporter> certificate_error_reporter);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(SafeBrowsingPingManagerTest,
                            TestSafeBrowsingHitUrl);
@@ -88,6 +101,10 @@
   // We add both "hit" and "detail" fetchers in this set.
   Reports safebrowsing_reports_;
 
+  // Sends reports of invalid SSL certificate chains.
+  scoped_ptr<chrome_browser_net::CertificateErrorReporter>
+      certificate_error_reporter_;
+
   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingPingManager);
 };
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
index 1fe998e..c04a8bc 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
@@ -68,20 +68,12 @@
 const char kLearnMorePhishingUrlV2[] =
     "https://www.google.com/transparencyreport/safebrowsing/";
 
-const char kPrivacyLinkHtml[] =
-    "<a id=\"privacy-link\" href=\"\" onclick=\"sendCommand(%d); "
-    "return false;\" onmousedown=\"return false;\">%s</a>";
-
 // After a malware interstitial where the user opted-in to the report
 // but clicked "proceed anyway", we delay the call to
 // MalwareDetails::FinishCollection() by this much time (in
 // milliseconds).
 const int64 kMalwareDetailsProceedDelayMilliSeconds = 3000;
 
-// Other constants used to communicate with the JavaScript.
-const char kBoxChecked[] = "boxchecked";
-const char kDisplayCheckBox[] = "displaycheckbox";
-
 // Constants for the Experience Sampling instrumentation.
 const char kEventNameMalware[] = "safebrowsing_interstitial_";
 const char kEventNameHarmful[] = "harmful_interstitial_";
@@ -167,15 +159,15 @@
 
   // This must be done after calculating |interstitial_reason_| above.
   // Use same prefix for UMA as for Rappor.
-  metrics_helper_.reset(new SecurityInterstitialMetricsHelper(
+  set_metrics_helper(new SecurityInterstitialMetricsHelper(
       web_contents, request_url(), GetMetricPrefix(), GetMetricPrefix(),
       SecurityInterstitialMetricsHelper::REPORT_RAPPOR,
       GetSamplingEventName()));
-  metrics_helper_->RecordUserDecision(SecurityInterstitialMetricsHelper::SHOW);
-  metrics_helper_->RecordUserInteraction(
+  metrics_helper()->RecordUserDecision(SecurityInterstitialMetricsHelper::SHOW);
+  metrics_helper()->RecordUserInteraction(
       SecurityInterstitialMetricsHelper::TOTAL_VISITS);
   if (IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled)) {
-    metrics_helper_->RecordUserDecision(
+    metrics_helper()->RecordUserDecision(
         SecurityInterstitialMetricsHelper::PROCEEDING_DISABLED);
   }
 
@@ -231,7 +223,7 @@
     }
     case CMD_OPEN_HELP_CENTER: {
       // User pressed "Learn more".
-      metrics_helper_->RecordUserInteraction(
+      metrics_helper()->RecordUserInteraction(
           SecurityInterstitialMetricsHelper::SHOW_LEARN_MORE);
       GURL learn_more_url(
           interstitial_reason_ == SB_REASON_PHISHING ?
@@ -248,24 +240,13 @@
     }
     case CMD_OPEN_REPORTING_PRIVACY: {
       // User pressed on the SB Extended Reporting "privacy policy" link.
-      metrics_helper_->RecordUserInteraction(
-          SecurityInterstitialMetricsHelper::SHOW_PRIVACY_POLICY);
-      GURL privacy_url(
-          l10n_util::GetStringUTF8(IDS_SAFE_BROWSING_PRIVACY_POLICY_URL));
-      privacy_url = google_util::AppendGoogleLocaleParam(
-          privacy_url, g_browser_process->GetApplicationLocale());
-      OpenURLParams params(privacy_url,
-                           Referrer(),
-                           CURRENT_TAB,
-                           ui::PAGE_TRANSITION_LINK,
-                           false);
-      web_contents()->OpenURL(params);
+      OpenExtendedReportingPrivacyPolicy();
       break;
     }
     case CMD_PROCEED: {
       // User pressed on the button to proceed.
       if (!IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled)) {
-        metrics_helper_->RecordUserDecision(
+        metrics_helper()->RecordUserDecision(
             SecurityInterstitialMetricsHelper::PROCEED);
         interstitial_page()->Proceed();
         // |this| has been deleted after Proceed() returns.
@@ -304,7 +285,7 @@
       size_t element_index = 0;
       const UnsafeResource& unsafe_resource = unsafe_resources_[element_index];
       std::string bad_url_spec = unsafe_resource.url.spec();
-      metrics_helper_->RecordUserInteraction(
+      metrics_helper()->RecordUserInteraction(
           SecurityInterstitialMetricsHelper::SHOW_DIAGNOSTIC);
       std::string diagnostic =
           base::StringPrintf(kSbDiagnosticUrl,
@@ -324,7 +305,7 @@
     }
     case CMD_SHOW_MORE_SECTION: {
       // User has opened up the hidden text.
-      metrics_helper_->RecordUserInteraction(
+      metrics_helper()->RecordUserInteraction(
           SecurityInterstitialMetricsHelper::SHOW_ADVANCED);
       break;
     }
@@ -339,14 +320,6 @@
       prefs, profile, web_contents());
 }
 
-void SafeBrowsingBlockingPage::SetReportingPreference(bool report) {
-  Profile* profile = Profile::FromBrowserContext(
-      web_contents()->GetBrowserContext());
-  PrefService* pref = profile->GetPrefs();
-  pref->SetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled, report);
-  UMA_HISTOGRAM_BOOLEAN("SB2.SetExtendedReportingEnabled", report);
-}
-
 void SafeBrowsingBlockingPage::OnProceed() {
   proceeded_ = true;
   // Send the malware details, if we opted to.
@@ -390,7 +363,7 @@
     return;
 
   if (!IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled)) {
-    metrics_helper_->RecordUserDecision(
+    metrics_helper()->RecordUserDecision(
         SecurityInterstitialMetricsHelper::DONT_PROCEED);
   }
 
@@ -431,20 +404,16 @@
 
   const bool enabled =
       IsPrefEnabled(prefs::kSafeBrowsingExtendedReportingEnabled);
-  UMA_HISTOGRAM_BOOLEAN("SB2.ExtendedReportingIsEnabled", enabled);
-  if (enabled) {
-    // Finish the malware details collection, send it over.
-    BrowserThread::PostDelayedTask(
-        BrowserThread::IO, FROM_HERE,
-        base::Bind(&MalwareDetails::FinishCollection, malware_details_.get()),
-        base::TimeDelta::FromMilliseconds(delay_ms));
-  }
-}
+  if (!enabled)
+    return;
 
-bool SafeBrowsingBlockingPage::IsPrefEnabled(const char* pref) {
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
-  return profile->GetPrefs()->GetBoolean(pref);
+  metrics_helper()->RecordUserInteraction(
+      SecurityInterstitialMetricsHelper::EXTENDED_REPORTING_IS_ENABLED);
+  // Finish the malware details collection, send it over.
+  BrowserThread::PostDelayedTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&MalwareDetails::FinishCollection, malware_details_.get()),
+      base::TimeDelta::FromMilliseconds(delay_ms));
 }
 
 // static
@@ -588,21 +557,19 @@
     base::DictionaryValue* load_time_data) {
   // Only show checkbox if !(HTTPS || incognito-mode).
   const bool show = CanShowMalwareDetailsOption();
-  load_time_data->SetBoolean(kDisplayCheckBox, show);
+  load_time_data->SetBoolean(interstitials::kDisplayCheckBox, show);
   if (!show)
     return;
 
   const std::string privacy_link = base::StringPrintf(
-      kPrivacyLinkHtml,
-      CMD_OPEN_REPORTING_PRIVACY,
-      l10n_util::GetStringUTF8(
-          IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE).c_str());
+      interstitials::kPrivacyLinkHtml, CMD_OPEN_REPORTING_PRIVACY,
+      l10n_util::GetStringUTF8(IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE).c_str());
   load_time_data->SetString(
-      "optInLink",
+      interstitials::kOptInLink,
       l10n_util::GetStringFUTF16(IDS_SAFE_BROWSING_MALWARE_REPORTING_AGREE,
                                  base::UTF8ToUTF16(privacy_link)));
   load_time_data->SetBoolean(
-      kBoxChecked,
+      interstitials::kBoxChecked,
       IsPrefEnabled(prefs::kSafeBrowsingExtendedReportingEnabled));
 }
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page.h b/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
index ea9704b..de5bbef 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
@@ -90,7 +90,7 @@
   friend class SafeBrowsingBlockingPageTest;
   FRIEND_TEST_ALL_PREFIXES(SafeBrowsingBlockingPageTest,
                            ProceedThenDontProceed);
-  void SetReportingPreference(bool report);
+
   void UpdateReportingPref();  // Used for the transition from old to new pref.
 
   // Don't instantiate this class directly, use ShowBlockingPage instead.
@@ -124,10 +124,6 @@
   // enabled, the report is scheduled to be sent on the |ui_manager_|.
   void FinishMalwareDetails(int64 delay_ms);
 
-  // Returns the boolean value of the given |pref| from the PrefService of the
-  // Profile associated with |web_contents_|.
-  bool IsPrefEnabled(const char* pref);
-
   // A list of SafeBrowsingUIManager::UnsafeResource for a tab that the user
   // should be warned about.  They are queued when displaying more than one
   // interstitial at a time.
@@ -193,8 +189,6 @@
   std::string GetMetricPrefix() const;
   std::string GetSamplingEventName() const;
 
-  scoped_ptr<SecurityInterstitialMetricsHelper> metrics_helper_;
-
   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPage);
 };
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
index ed8fad4..e79310f 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
@@ -690,7 +690,7 @@
   if (expect_malware_details)
     fake_malware_details->WaitForDOM();
 
-  EXPECT_EQ(VISIBLE, GetVisibility("malware-opt-in"));
+  EXPECT_EQ(VISIBLE, GetVisibility("extended-reporting-opt-in"));
   EXPECT_TRUE(Click("opt-in-checkbox"));
   EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
   AssertNoInterstitial(true);  // Assert the interstitial is gone
@@ -765,7 +765,7 @@
   ui_test_utils::NavigateToURL(browser(), url);
   ASSERT_TRUE(WaitForReady());
 
-  EXPECT_EQ(HIDDEN, GetVisibility("malware-opt-in"));
+  EXPECT_EQ(HIDDEN, GetVisibility("extended-reporting-opt-in"));
   EXPECT_EQ(HIDDEN, GetVisibility("opt-in-checkbox"));
   EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
   EXPECT_TRUE(Click("details-button"));
diff --git a/chrome/browser/safe_browsing/ui_manager.cc b/chrome/browser/safe_browsing/ui_manager.cc
index 45728d35..382d103 100644
--- a/chrome/browser/safe_browsing/ui_manager.cc
+++ b/chrome/browser/safe_browsing/ui_manager.cc
@@ -26,6 +26,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "net/ssl/ssl_info.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
 
@@ -213,6 +214,19 @@
                  threat_type, post_data));
 }
 
+void SafeBrowsingUIManager::ReportInvalidCertificateChain(
+    const std::string& hostname,
+    const net::SSLInfo& ssl_info,
+    const base::Closure& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  BrowserThread::PostTaskAndReply(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(
+          &SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread, this,
+          hostname, ssl_info),
+      callback);
+}
+
 void SafeBrowsingUIManager::AddObserver(Observer* observer) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   observer_list_.AddObserver(observer);
@@ -246,6 +260,20 @@
       threat_type, post_data);
 }
 
+void SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread(
+    const std::string& hostname,
+    const net::SSLInfo& ssl_info) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  // The service may delete the ping manager (i.e. when user disabling service,
+  // etc). This happens on the IO thread.
+  if (!sb_service_ || !sb_service_->ping_manager())
+    return;
+
+  sb_service_->ping_manager()->ReportInvalidCertificateChain(hostname,
+                                                             ssl_info);
+}
+
 // If the user had opted-in to send MalwareDetails, this gets called
 // when the report is ready.
 void SafeBrowsingUIManager::SendSerializedMalwareDetails(
@@ -305,4 +333,3 @@
   }
   return false;
 }
-
diff --git a/chrome/browser/safe_browsing/ui_manager.h b/chrome/browser/safe_browsing/ui_manager.h
index 543b1ab..42be145 100644
--- a/chrome/browser/safe_browsing/ui_manager.h
+++ b/chrome/browser/safe_browsing/ui_manager.h
@@ -24,7 +24,11 @@
 
 namespace base {
 class Thread;
-}
+}  // namespace base
+
+namespace net {
+class SSLInfo;
+}  // namespace net
 
 // Construction needs to happen on the main thread.
 class SafeBrowsingUIManager
@@ -124,6 +128,12 @@
                                      SBThreatType threat_type,
                                      const std::string& post_data);
 
+  // Report an invalid TLS/SSL certificate chain to the server. Can only
+  // be called on UI thread.
+  void ReportInvalidCertificateChain(const std::string& hostname,
+                                     const net::SSLInfo& ssl_info,
+                                     const base::Closure& callback);
+
   // Add and remove observers.  These methods must be invoked on the UI thread.
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* remove);
@@ -145,6 +155,10 @@
                                        SBThreatType threat_type,
                                        const std::string& post_data);
 
+  // Sends an invalid certificate chain report over the network.
+  void ReportInvalidCertificateChainOnIOThread(const std::string& hostname,
+                                               const net::SSLInfo& ssl_info);
+
   // Adds the given entry to the whitelist.  Called on the UI thread.
   void UpdateWhitelist(const UnsafeResource& resource);
 
diff --git a/chrome/browser/search/hotword_service.cc b/chrome/browser/search/hotword_service.cc
index 908a75b9..e688166 100644
--- a/chrome/browser/search/hotword_service.cc
+++ b/chrome/browser/search/hotword_service.cc
@@ -97,6 +97,9 @@
 // store.
 static const int kInstallRetryDelaySeconds = 5;
 
+// The extension id of the old hotword voice search trigger extension.
+const char kHotwordOldExtensionId[] = "bepbmhgboaologfdajaanbcjmnhjmhfn";
+
 // Enum describing the state of the hotword preference.
 // This is used for UMA stats -- do not reorder or delete items; only add to
 // the end.
@@ -132,25 +135,6 @@
   NUM_HOTWORD_ERROR_METRICS
 };
 
-void RecordExtensionAvailabilityMetrics(
-    ExtensionService* service,
-    const extensions::Extension* extension) {
-  HotwordExtensionAvailability availability_state = UNAVAILABLE;
-  if (extension) {
-    availability_state = AVAILABLE;
-  } else if (service->pending_extension_manager() &&
-             service->pending_extension_manager()->IsIdPending(
-                 extension_misc::kHotwordExtensionId)) {
-    availability_state = PENDING_DOWNLOAD;
-  } else if (!service->IsExtensionEnabled(
-      extension_misc::kHotwordExtensionId)) {
-    availability_state = DISABLED_EXTENSION;
-  }
-  UMA_HISTOGRAM_ENUMERATION("Hotword.HotwordExtensionAvailability",
-                            availability_state,
-                            NUM_HOTWORD_EXTENSION_AVAILABILITY_METRICS);
-}
-
 void RecordLoggingMetrics(Profile* profile) {
   // If the user is not opted in to hotword voice search, the audio logging
   // metric is not valid so it is not recorded.
@@ -189,8 +173,7 @@
   if (!prefs->HasPrefPath(prefs::kHotwordSearchEnabled) &&
       !prefs->HasPrefPath(prefs::kHotwordAlwaysOnSearchEnabled)) {
     enabled_state = UNSET;
-  } else if (service->IsExperimentalHotwordingEnabled() &&
-             service->IsAlwaysOnEnabled()) {
+  } else if (service->IsAlwaysOnEnabled()) {
     enabled_state = ALWAYS_ON_ENABLED;
   } else if (prefs->GetBoolean(prefs::kHotwordSearchEnabled)) {
     enabled_state = ENABLED;
@@ -298,11 +281,6 @@
 }
 
 // static
-bool HotwordService::IsExperimentalHotwordingEnabled() {
-  return true;
-}
-
-// static
 bool HotwordService::IsHotwordHardwareAvailable() {
 #if defined(OS_CHROMEOS)
   if (chromeos::CrasAudioHandler::IsInitialized()) {
@@ -353,28 +331,21 @@
       training_(false),
       weak_factory_(this) {
   extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
-  if (IsExperimentalHotwordingEnabled()) {
-    // Disable the old extension so it doesn't interfere with the new stuff.
-    DisableHotwordExtension(GetExtensionService(profile_));
-  } else {
-    if (!profile_->GetPrefs()->HasPrefPath(prefs::kHotwordSearchEnabled) &&
-        IsHotwordAllowed()) {
-      // If the preference has not been set the hotword extension should
-      // not be running. However, this should only be done if auto-install
-      // is enabled which is gated through the IsHotwordAllowed check.
-      DisableHotwordExtension(GetExtensionService(profile_));
-    }
+
+  // Disable the old extension so it doesn't interfere with the new stuff.
+  ExtensionService* extension_service = GetExtensionService(profile_);
+  if (extension_service) {
+    extension_service->DisableExtension(
+        kHotwordOldExtensionId,
+        extensions::Extension::DISABLE_USER_ACTION);
   }
+
   // This will be called during profile initialization which is a good time
   // to check the user's hotword state.
   RecordHotwordEnabledMetric(this, profile_);
 
   pref_registrar_.Init(profile_->GetPrefs());
   pref_registrar_.Add(
-      prefs::kHotwordSearchEnabled,
-      base::Bind(&HotwordService::OnHotwordSearchEnabledChanged,
-      base::Unretained(this)));
-  pref_registrar_.Add(
       prefs::kHotwordAlwaysOnSearchEnabled,
       base::Bind(&HotwordService::OnHotwordAlwaysOnSearchEnabledChanged,
       base::Unretained(this)));
@@ -396,8 +367,7 @@
       profile_, base::MessageLoop::current()->task_runner()));
 
   if (HotwordServiceFactory::IsAlwaysOnAvailable() &&
-      IsHotwordAllowed() &&
-      IsExperimentalHotwordingEnabled()) {
+      IsHotwordAllowed()) {
     // Show the hotword notification in 5 seconds if the experimental flag is
     // on, or in 10 minutes if not. We need to wait at least a few seconds
     // for the hotword extension to be installed.
@@ -477,9 +447,8 @@
     extensions::UninstallReason reason) {
   CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
-  if ((extension->id() != extension_misc::kHotwordExtensionId &&
-       extension->id() != extension_misc::kHotwordSharedModuleId) ||
-       profile_ != Profile::FromBrowserContext(browser_context) ||
+  if (extension->id() != extension_misc::kHotwordSharedModuleId ||
+      profile_ != Profile::FromBrowserContext(browser_context) ||
       !GetExtensionService(profile_))
     return;
 
@@ -493,10 +462,7 @@
 }
 
 std::string HotwordService::ReinstalledExtensionId() {
-  if (IsExperimentalHotwordingEnabled())
-    return extension_misc::kHotwordSharedModuleId;
-
-  return extension_misc::kHotwordExtensionId;
+  return extension_misc::kHotwordSharedModuleId;
 }
 
 void HotwordService::InstalledFromWebstoreCallback(
@@ -531,9 +497,8 @@
     const extensions::Extension* extension,
     bool is_update) {
 
-  if ((extension->id() != extension_misc::kHotwordExtensionId &&
-       extension->id() != extension_misc::kHotwordSharedModuleId) ||
-       profile_ != Profile::FromBrowserContext(browser_context))
+  if (extension->id() != extension_misc::kHotwordSharedModuleId ||
+      profile_ != Profile::FromBrowserContext(browser_context))
     return;
 
   // If the previous locale pref has never been set, set it now since
@@ -550,17 +515,6 @@
     MaybeReinstallHotwordExtension();
   else
     reinstall_pending_ = false;
-
-  // Now that the extension is installed, if the user has not selected
-  // the preference on, make sure it is turned off.
-  //
-  // Disabling the extension automatically on install should only occur
-  // if the user is in the field trial for auto-install which is gated
-  // by the IsHotwordAllowed check. The check for IsHotwordAllowed() here
-  // can be removed once it's known that few people have manually
-  // installed extension.
-  if (IsHotwordAllowed() && !IsSometimesOnEnabled())
-    DisableHotwordExtension(GetExtensionService(profile_));
 }
 
 bool HotwordService::MaybeReinstallHotwordExtension() {
@@ -643,7 +597,7 @@
   if (!extension)
     error_message_ = IDS_HOTWORD_GENERIC_ERROR_MESSAGE;
 
-  RecordExtensionAvailabilityMetrics(service, extension);
+  // TODO(amistry): Record availability of shared module in UMA.
   RecordLoggingMetrics(profile_);
 
   // Determine if NaCl is available.
@@ -710,21 +664,6 @@
       !HotwordServiceFactory::IsAlwaysOnAvailable();
 }
 
-void HotwordService::EnableHotwordExtension(
-    ExtensionService* extension_service) {
-  if (extension_service && !IsExperimentalHotwordingEnabled())
-    extension_service->EnableExtension(extension_misc::kHotwordExtensionId);
-}
-
-void HotwordService::DisableHotwordExtension(
-    ExtensionService* extension_service) {
-  if (extension_service) {
-    extension_service->DisableExtension(
-        extension_misc::kHotwordExtensionId,
-        extensions::Extension::DISABLE_USER_ACTION);
-  }
-}
-
 void HotwordService::SpeakerModelExistsComplete(bool exists) {
   if (exists) {
     profile_->GetPrefs()->
@@ -842,17 +781,6 @@
   }
 }
 
-void HotwordService::OnHotwordSearchEnabledChanged(
-    const std::string& pref_name) {
-  DCHECK_EQ(pref_name, std::string(prefs::kHotwordSearchEnabled));
-
-  ExtensionService* extension_service = GetExtensionService(profile_);
-  if (profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled))
-    EnableHotwordExtension(extension_service);
-  else
-    DisableHotwordExtension(extension_service);
-}
-
 void HotwordService::OnHotwordAlwaysOnSearchEnabledChanged(
     const std::string& pref_name) {
   // If the pref for always on has been changed in some way, that means that
@@ -912,10 +840,6 @@
 }
 
 void HotwordService::ActiveUserChanged() {
-  // Do nothing for old hotwording.
-  if (!IsExperimentalHotwordingEnabled())
-    return;
-
   // Don't bother notifying the extension if hotwording is completely off.
   if (!IsSometimesOnEnabled() && !IsAlwaysOnEnabled() && !IsTraining())
     return;
diff --git a/chrome/browser/search/hotword_service.h b/chrome/browser/search/hotword_service.h
index c4bf08bd..9b22c84 100644
--- a/chrome/browser/search/hotword_service.h
+++ b/chrome/browser/search/hotword_service.h
@@ -60,10 +60,6 @@
   // Returns true if the hotword supports the current system language.
   static bool DoesHotwordSupportLanguage(Profile* profile);
 
-  // Always returns true.
-  // TODO(amistry): Remove this.
-  static bool IsExperimentalHotwordingEnabled();
-
   // Returns true if hotwording hardware is available.
   static bool IsHotwordHardwareAvailable();
 
@@ -99,14 +95,6 @@
   // Returns whether google.com/NTP/launcher hotwording is enabled.
   bool IsSometimesOnEnabled();
 
-  // Control the state of the hotword extension.
-  void EnableHotwordExtension(ExtensionService* extension_service);
-  void DisableHotwordExtension(ExtensionService* extension_service);
-
-  // Handles enabling/disabling the hotword extension when the user
-  // turns it off via the settings menu.
-  void OnHotwordSearchEnabledChanged(const std::string& pref_name);
-
   // Handles enabling/disabling the hotword notification when the user
   // changes the always on search settings.
   void OnHotwordAlwaysOnSearchEnabledChanged(const std::string& pref_name);
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc
index 625ab096..9e2fdef 100644
--- a/chrome/browser/search/local_ntp_source.cc
+++ b/chrome/browser/search/local_ntp_source.cc
@@ -20,6 +20,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/search_engines/template_url_service.h"
+#include "components/variations/variations_associated_data.h"
 #include "grit/browser_resources.h"
 #include "grit/theme_resources.h"
 #include "net/url_request/url_request.h"
@@ -79,6 +80,11 @@
        SEARCH_ENGINE_GOOGLE);
 }
 
+// Returns whether icon NTP is enabled.
+bool IsIconNTPEnabled() {
+  return variations::GetVariationParamValue("IconNTP", "state") == "enabled";
+}
+
 // Returns whether we are in the Fast NTP experiment or not.
 bool IsLocalNTPFastEnabled() {
   return StartsWithASCII(base::FieldTrialList::FindFullName("LocalNTPFast"),
@@ -140,6 +146,7 @@
   config_data.Set("translatedStrings",
                   GetTranslatedStrings(is_google).release());
   config_data.SetBoolean("isGooglePage", is_google);
+  config_data.SetBoolean("useIcons", IsIconNTPEnabled());
 
   // Serialize the dictionary.
   std::string js_text;
diff --git a/chrome/browser/signin/about_signin_internals_factory.cc b/chrome/browser/signin/about_signin_internals_factory.cc
index 7174ffa..1236d9e 100644
--- a/chrome/browser/signin/about_signin_internals_factory.cc
+++ b/chrome/browser/signin/about_signin_internals_factory.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/signin/account_tracker_service_factory.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/common/pref_names.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -27,6 +28,7 @@
   DependsOn(AccountTrackerServiceFactory::GetInstance());
   DependsOn(ChromeSigninClientFactory::GetInstance());
   DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
+  DependsOn(SigninErrorControllerFactory::GetInstance());
   DependsOn(SigninManagerFactory::GetInstance());
 }
 
@@ -82,7 +84,8 @@
   AboutSigninInternals* service = new AboutSigninInternals(
       ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
       AccountTrackerServiceFactory::GetForProfile(profile),
-      SigninManagerFactory::GetForProfile(profile));
+      SigninManagerFactory::GetForProfile(profile),
+      SigninErrorControllerFactory::GetForProfile(profile));
   service->Initialize(ChromeSigninClientFactory::GetForProfile(profile));
   return service;
 }
diff --git a/chrome/browser/spellchecker/feedback.cc b/chrome/browser/spellchecker/feedback.cc
new file mode 100644
index 0000000..6b9687f
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 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.
+//
+// The |Feedback| object keeps track of each instance of user feedback in a map
+// |misspellings_|. This is a map from uint32 hashes to |Misspelling| objects.
+//
+// Each misspelling should be present in only one renderer process. The
+// |Feedback| objects keeps track of misspelling-renderer relationship in the
+// |renderers_| map of renderer process identifiers to a set of hashes.
+//
+// When the user adds a misspelling to their custom dictionary, all of the
+// |Misspelling| objects with the same misspelled string are updated. The
+// |Feedback| object facilitates efficient access to these misspellings through
+// a |text_| map of misspelled strings to a set of hashes.
+
+#include "chrome/browser/spellchecker/feedback.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "base/stl_util.h"
+
+namespace spellcheck {
+
+Feedback::Feedback() {
+}
+
+Feedback::~Feedback() {
+}
+
+Misspelling* Feedback::GetMisspelling(uint32 hash) {
+  HashMisspellingMap::iterator misspelling_it = misspellings_.find(hash);
+  if (misspelling_it == misspellings_.end())
+    return NULL;
+  return &misspelling_it->second;
+}
+
+void Feedback::FinalizeRemovedMisspellings(
+    int renderer_process_id,
+    const std::vector<uint32>& remaining_markers) {
+  RendererHashesMap::iterator renderer_it =
+      renderers_.find(renderer_process_id);
+  if (renderer_it == renderers_.end() || renderer_it->second.empty())
+    return;
+  HashCollection& renderer_hashes = renderer_it->second;
+  HashCollection remaining_hashes(remaining_markers.begin(),
+                                  remaining_markers.end());
+  std::vector<uint32> removed_hashes =
+      base::STLSetDifference<std::vector<uint32> >(renderer_hashes,
+                                                   remaining_hashes);
+  for (std::vector<uint32>::const_iterator hash_it = removed_hashes.begin();
+       hash_it != removed_hashes.end();
+       ++hash_it) {
+    HashMisspellingMap::iterator misspelling_it = misspellings_.find(*hash_it);
+    if (misspelling_it != misspellings_.end() &&
+        !misspelling_it->second.action.IsFinal()) {
+      misspelling_it->second.action.Finalize();
+    }
+  }
+}
+
+bool Feedback::RendererHasMisspellings(int renderer_process_id) const {
+  RendererHashesMap::const_iterator renderer_it =
+      renderers_.find(renderer_process_id);
+  return renderer_it != renderers_.end() && !renderer_it->second.empty();
+}
+
+std::vector<Misspelling> Feedback::GetMisspellingsInRenderer(
+    int renderer_process_id) const {
+  std::vector<Misspelling> misspellings_in_renderer;
+  RendererHashesMap::const_iterator renderer_it =
+      renderers_.find(renderer_process_id);
+  if (renderer_it == renderers_.end() || renderer_it->second.empty())
+    return misspellings_in_renderer;
+  const HashCollection& renderer_hashes = renderer_it->second;
+  for (HashCollection::const_iterator hash_it = renderer_hashes.begin();
+       hash_it != renderer_hashes.end();
+       ++hash_it) {
+    HashMisspellingMap::const_iterator misspelling_it =
+        misspellings_.find(*hash_it);
+    if (misspelling_it != misspellings_.end())
+      misspellings_in_renderer.push_back(misspelling_it->second);
+  }
+  return misspellings_in_renderer;
+}
+
+void Feedback::EraseFinalizedMisspellings(int renderer_process_id) {
+  RendererHashesMap::iterator renderer_it =
+      renderers_.find(renderer_process_id);
+  if (renderer_it == renderers_.end())
+    return;
+  HashCollection& renderer_hashes = renderer_it->second;
+  for (HashCollection::const_iterator hash_it = renderer_hashes.begin();
+       hash_it != renderer_hashes.end();) {
+    HashMisspellingMap::iterator misspelling_it = misspellings_.find(*hash_it);
+    HashCollection::iterator erasable_hash_it = hash_it;
+    ++hash_it;
+    if (misspelling_it == misspellings_.end())
+      continue;
+    const Misspelling& misspelling = misspelling_it->second;
+    if (!misspelling.action.IsFinal())
+      continue;
+    renderer_hashes.erase(erasable_hash_it);
+    text_[misspelling.GetMisspelledString()].erase(misspelling.hash);
+    misspellings_.erase(misspelling_it);
+  }
+  if (renderer_hashes.empty())
+    renderers_.erase(renderer_it);
+}
+
+bool Feedback::HasMisspelling(uint32 hash) const {
+  return !!misspellings_.count(hash);
+}
+
+void Feedback::AddMisspelling(int renderer_process_id,
+                              const Misspelling& misspelling) {
+  HashMisspellingMap::iterator misspelling_it =
+      misspellings_.find(misspelling.hash);
+  if (misspelling_it != misspellings_.end()) {
+    const Misspelling& existing_misspelling = misspelling_it->second;
+    text_[existing_misspelling.GetMisspelledString()].erase(misspelling.hash);
+    for (RendererHashesMap::iterator renderer_it = renderers_.begin();
+         renderer_it != renderers_.end();) {
+      HashCollection& renderer_hashes = renderer_it->second;
+      RendererHashesMap::iterator erasable_renderer_it = renderer_it;
+      ++renderer_it;
+      renderer_hashes.erase(misspelling.hash);
+      if (renderer_hashes.empty())
+        renderers_.erase(erasable_renderer_it);
+    }
+  }
+  misspellings_[misspelling.hash] = misspelling;
+  text_[misspelling.GetMisspelledString()].insert(misspelling.hash);
+  renderers_[renderer_process_id].insert(misspelling.hash);
+}
+
+bool Feedback::Empty() const {
+  return misspellings_.empty();
+}
+
+std::vector<int> Feedback::GetRendersWithMisspellings() const {
+  std::vector<int> renderers_with_misspellings;
+  for (RendererHashesMap::const_iterator renderer_it = renderers_.begin();
+       renderer_it != renderers_.end();
+       ++renderer_it) {
+    if (!renderer_it->second.empty())
+      renderers_with_misspellings.push_back(renderer_it->first);
+  }
+  return renderers_with_misspellings;
+}
+
+void Feedback::FinalizeAllMisspellings() {
+  for (HashMisspellingMap::iterator misspelling_it = misspellings_.begin();
+       misspelling_it != misspellings_.end();
+       ++misspelling_it) {
+    if (!misspelling_it->second.action.IsFinal())
+      misspelling_it->second.action.Finalize();
+  }
+}
+
+std::vector<Misspelling> Feedback::GetAllMisspellings() const {
+  std::vector<Misspelling> all_misspellings;
+  for (HashMisspellingMap::const_iterator misspelling_it =
+           misspellings_.begin();
+       misspelling_it != misspellings_.end();
+       ++misspelling_it) {
+    all_misspellings.push_back(misspelling_it->second);
+  }
+  return all_misspellings;
+}
+
+void Feedback::Clear() {
+  misspellings_.clear();
+  text_.clear();
+  renderers_.clear();
+}
+
+const std::set<uint32>& Feedback::FindMisspellings(
+    const base::string16& misspelled_text) const {
+  const TextHashesMap::const_iterator text_it = text_.find(misspelled_text);
+  return text_it == text_.end() ? empty_hash_collection_ : text_it->second;
+}
+
+}  // namespace spellcheck
diff --git a/chrome/browser/spellchecker/feedback.h b/chrome/browser/spellchecker/feedback.h
new file mode 100644
index 0000000..91a0abd
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback.h
@@ -0,0 +1,112 @@
+// Copyright (c) 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.
+//
+// An object to store user feedback to spellcheck suggestions from spelling
+// service.
+//
+// Stores feedback for the spelling service in |Misspelling| objects. Each
+// |Misspelling| object is identified by a |hash| and corresponds to a document
+// marker with the same |hash| identifier in the renderer.
+
+#ifndef CHROME_BROWSER_SPELLCHECKER_FEEDBACK_H_
+#define CHROME_BROWSER_SPELLCHECKER_FEEDBACK_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "chrome/browser/spellchecker/misspelling.h"
+
+namespace spellcheck {
+
+// Stores user feedback to spellcheck suggestions. Sample usage:
+//    Feedback feedback;
+//    feedback.AddMisspelling(renderer_process_id, Misspelling(
+//        base::ASCIIToUTF16("Helllo world"), 0, 6,
+//        std::vector<base::string16>(), GenerateRandomHash()));
+//    feedback.FinalizeRemovedMisspellings(renderer_process_id,
+//                                         std::vector<uint32>());
+//    ProcessFeedback(feedback.GetMisspellingsInRenderer(renderer_process_id));
+//    feedback.EraseFinalizedMisspellings(renderer_process_id);
+class Feedback {
+ public:
+  Feedback();
+  ~Feedback();
+
+  // Returns the misspelling identified by |hash|. Returns NULL if there's no
+  // misspelling identified by |hash|. Retains the ownership of the result. The
+  // caller should not modify the hash in the returned misspelling.
+  Misspelling* GetMisspelling(uint32 hash);
+
+  // Finalizes the user actions on misspellings that are removed from the
+  // renderer process with ID |renderer_process_id|.
+  void FinalizeRemovedMisspellings(
+      int renderer_process_id,
+      const std::vector<uint32>& remaining_markers);
+
+  // Returns true if the renderer with process ID |renderer_process_id| has
+  // misspellings.
+  bool RendererHasMisspellings(int renderer_process_id) const;
+
+  // Returns a copy of the misspellings in renderer with process ID
+  // |renderer_process_id|.
+  std::vector<Misspelling> GetMisspellingsInRenderer(
+      int renderer_process_id) const;
+
+  // Erases the misspellings with final user actions in the renderer with
+  // process ID |renderer_process_id|.
+  void EraseFinalizedMisspellings(int renderer_process_id);
+
+  // Returns true if there's a misspelling with |hash| identifier.
+  bool HasMisspelling(uint32 hash) const;
+
+  // Adds the |misspelling| to feedback data. If the |misspelling| has a
+  // duplicate hash, then replaces the existing misspelling with the same hash.
+  void AddMisspelling(int renderer_process_id, const Misspelling& misspelling);
+
+  // Returns true if there're no misspellings.
+  bool Empty() const;
+
+  // Returns a list of process identifiers for renderers that have misspellings.
+  std::vector<int> GetRendersWithMisspellings() const;
+
+  // Finalizes all misspellings.
+  void FinalizeAllMisspellings();
+
+  // Returns a copy of all misspellings.
+  std::vector<Misspelling> GetAllMisspellings() const;
+
+  // Removes all misspellings.
+  void Clear();
+
+  // Returns a list of all misspelling identifiers for |misspelled_text|.
+  const std::set<uint32>& FindMisspellings(
+      const base::string16& misspelled_text) const;
+
+ private:
+  typedef std::map<uint32, Misspelling> HashMisspellingMap;
+  typedef std::set<uint32> HashCollection;
+  typedef std::map<int, HashCollection> RendererHashesMap;
+  typedef std::map<base::string16, HashCollection> TextHashesMap;
+
+  // An empty hash collection to return when FindMisspellings() does not find
+  // misspellings.
+  const HashCollection empty_hash_collection_;
+
+  // A map of hashes that identify document markers to feedback data to be sent
+  // to spelling service.
+  HashMisspellingMap misspellings_;
+
+  // A map of renderer process ID to hashes that identify misspellings.
+  RendererHashesMap renderers_;
+
+  // A map of misspelled text to hashes that identify misspellings.
+  TextHashesMap text_;
+
+  DISALLOW_COPY_AND_ASSIGN(Feedback);
+};
+
+}  // namespace spellcheck
+
+#endif  // CHROME_BROWSER_SPELLCHECKER_FEEDBACK_H_
diff --git a/chrome/browser/spellchecker/feedback_sender.cc b/chrome/browser/spellchecker/feedback_sender.cc
new file mode 100644
index 0000000..ab83a50
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback_sender.cc
@@ -0,0 +1,424 @@
+// Copyright (c) 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.
+//
+// The |FeedbackSender| object stores the user feedback to spellcheck
+// suggestions in a |Feedback| object.
+//
+// When spelling service returns spellcheck results, these results first arrive
+// in |FeedbackSender| to assign hash identifiers for each
+// misspelling-suggestion pair. If the spelling service identifies the same
+// misspelling as already displayed to the user, then |FeedbackSender| reuses
+// the same hash identifiers to avoid duplication. It detects the duplicates by
+// comparing misspelling offsets in text. Spelling service can return duplicates
+// because we request spellcheck for whole paragraphs, as context around a
+// misspelled word is important to the spellcheck algorithm.
+//
+// All feedback is initially pending. When a user acts upon a misspelling such
+// that the misspelling is no longer displayed (red squiggly line goes away),
+// then the feedback for this misspelling is finalized. All finalized feedback
+// is erased after being sent to the spelling service. Pending feedback is kept
+// around for |kSessionHours| hours and then finalized even if user did not act
+// on the misspellings.
+//
+// |FeedbackSender| periodically requests a list of hashes of all remaining
+// misspellings in renderers. When a renderer responds with a list of hashes,
+// |FeedbackSender| uses the list to determine which misspellings are no longer
+// displayed to the user and sends the current state of user feedback to the
+// spelling service.
+
+#include "chrome/browser/spellchecker/feedback_sender.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "base/command_line.h"
+#include "base/hash.h"
+#include "base/json/json_writer.h"
+#include "base/metrics/field_trial.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "chrome/browser/spellchecker/word_trimmer.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/spellcheck_common.h"
+#include "chrome/common/spellcheck_marker.h"
+#include "chrome/common/spellcheck_messages.h"
+#include "content/public/browser/render_process_host.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace spellcheck {
+
+namespace {
+
+// The default URL where feedback data is sent.
+const char kFeedbackServiceURL[] = "https://www.googleapis.com/rpc";
+
+// The minimum number of seconds between sending batches of feedback.
+const int kMinIntervalSeconds = 5;
+
+// Returns a hash of |session_start|, the current timestamp, and
+// |suggestion_index|.
+uint32 BuildHash(const base::Time& session_start, size_t suggestion_index) {
+  return base::Hash(
+      base::StringPrintf("%" PRId64 "%" PRId64 "%" PRIuS,
+                         session_start.ToInternalValue(),
+                         base::Time::Now().ToInternalValue(),
+                         suggestion_index));
+}
+
+// Returns a pending feedback data structure for the spellcheck |result| and
+// |text|.
+Misspelling BuildFeedback(const SpellCheckResult& result,
+                          const base::string16& text) {
+  size_t start = result.location;
+  base::string16 context = TrimWords(&start,
+                               result.length,
+                               text,
+                               chrome::spellcheck_common::kContextWordCount);
+  return Misspelling(context,
+                     start,
+                     result.length,
+                     std::vector<base::string16>(1, result.replacement),
+                     result.hash);
+}
+
+// Builds suggestion info from |suggestions|. The caller owns the result.
+base::ListValue* BuildSuggestionInfo(
+    const std::vector<Misspelling>& suggestions,
+    bool is_first_feedback_batch) {
+  base::ListValue* list = new base::ListValue;
+  for (std::vector<Misspelling>::const_iterator suggestion_it =
+           suggestions.begin();
+       suggestion_it != suggestions.end();
+       ++suggestion_it) {
+    base::DictionaryValue* suggestion = suggestion_it->Serialize();
+    suggestion->SetBoolean("isFirstInSession", is_first_feedback_batch);
+    suggestion->SetBoolean("isAutoCorrection", false);
+    list->Append(suggestion);
+  }
+  return list;
+}
+
+// Builds feedback parameters from |suggestion_info|, |language|, and |country|.
+// Takes ownership of |suggestion_list|. The caller owns the result.
+base::DictionaryValue* BuildParams(base::ListValue* suggestion_info,
+                                   const std::string& language,
+                                   const std::string& country) {
+  base::DictionaryValue* params = new base::DictionaryValue;
+  params->Set("suggestionInfo", suggestion_info);
+  params->SetString("key", google_apis::GetAPIKey());
+  params->SetString("language", language);
+  params->SetString("originCountry", country);
+  params->SetString("clientName", "Chrome");
+  return params;
+}
+
+// Builds feedback data from |params|. Takes ownership of |params|. The caller
+// owns the result.
+base::Value* BuildFeedbackValue(base::DictionaryValue* params,
+                                const std::string& api_version) {
+  base::DictionaryValue* result = new base::DictionaryValue;
+  result->Set("params", params);
+  result->SetString("method", "spelling.feedback");
+  result->SetString("apiVersion", api_version);
+  return result;
+}
+
+// Returns true if the misspelling location is within text bounds.
+bool IsInBounds(int misspelling_location,
+                int misspelling_length,
+                size_t text_length) {
+  return misspelling_location >= 0 && misspelling_length > 0 &&
+         static_cast<size_t>(misspelling_location) < text_length &&
+         static_cast<size_t>(misspelling_location + misspelling_length) <=
+             text_length;
+}
+
+// Returns the feedback API version.
+std::string GetApiVersion() {
+  // This guard is temporary.
+  // TODO(rouslan): Remove the guard. http://crbug.com/247726
+  if (base::FieldTrialList::FindFullName(kFeedbackFieldTrialName) ==
+          kFeedbackFieldTrialEnabledGroupName &&
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableSpellingFeedbackFieldTrial)) {
+    return "v2-internal";
+  }
+  return "v2";
+}
+
+}  // namespace
+
+FeedbackSender::FeedbackSender(net::URLRequestContextGetter* request_context,
+                               const std::string& language,
+                               const std::string& country)
+    : request_context_(request_context),
+      api_version_(GetApiVersion()),
+      language_(language),
+      country_(country),
+      misspelling_counter_(0),
+      session_start_(base::Time::Now()),
+      feedback_service_url_(kFeedbackServiceURL) {
+  // The command-line switch is for testing and temporary.
+  // TODO(rouslan): Remove the command-line switch when testing is complete.
+  // http://crbug.com/247726
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSpellingServiceFeedbackUrl)) {
+    feedback_service_url_ =
+        GURL(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+            switches::kSpellingServiceFeedbackUrl));
+  }
+}
+
+FeedbackSender::~FeedbackSender() {
+}
+
+void FeedbackSender::SelectedSuggestion(uint32 hash, int suggestion_index) {
+  Misspelling* misspelling = feedback_.GetMisspelling(hash);
+  // GetMisspelling() returns null for flushed feedback. Feedback is flushed
+  // when the session expires every |kSessionHours| hours.
+  if (!misspelling)
+    return;
+  misspelling->action.type = SpellcheckAction::TYPE_SELECT;
+  misspelling->action.index = suggestion_index;
+  misspelling->timestamp = base::Time::Now();
+}
+
+void FeedbackSender::AddedToDictionary(uint32 hash) {
+  Misspelling* misspelling = feedback_.GetMisspelling(hash);
+  // GetMisspelling() returns null for flushed feedback. Feedback is flushed
+  // when the session expires every |kSessionHours| hours.
+  if (!misspelling)
+    return;
+  misspelling->action.type = SpellcheckAction::TYPE_ADD_TO_DICT;
+  misspelling->timestamp = base::Time::Now();
+  const std::set<uint32>& hashes =
+      feedback_.FindMisspellings(misspelling->GetMisspelledString());
+  for (std::set<uint32>::const_iterator hash_it = hashes.begin();
+       hash_it != hashes.end();
+       ++hash_it) {
+    Misspelling* duplicate_misspelling = feedback_.GetMisspelling(*hash_it);
+    if (!duplicate_misspelling || duplicate_misspelling->action.IsFinal())
+      continue;
+    duplicate_misspelling->action.type = SpellcheckAction::TYPE_ADD_TO_DICT;
+    duplicate_misspelling->timestamp = misspelling->timestamp;
+  }
+}
+
+void FeedbackSender::RecordInDictionary(uint32 hash) {
+  Misspelling* misspelling = feedback_.GetMisspelling(hash);
+  // GetMisspelling() returns null for flushed feedback. Feedback is flushed
+  // when the session expires every |kSessionHours| hours.
+  if (!misspelling)
+    return;
+  misspelling->action.type = SpellcheckAction::TYPE_IN_DICTIONARY;
+}
+
+void FeedbackSender::IgnoredSuggestions(uint32 hash) {
+  Misspelling* misspelling = feedback_.GetMisspelling(hash);
+  // GetMisspelling() returns null for flushed feedback. Feedback is flushed
+  // when the session expires every |kSessionHours| hours.
+  if (!misspelling)
+    return;
+  misspelling->action.type = SpellcheckAction::TYPE_PENDING_IGNORE;
+  misspelling->timestamp = base::Time::Now();
+}
+
+void FeedbackSender::ManuallyCorrected(uint32 hash,
+                                       const base::string16& correction) {
+  Misspelling* misspelling = feedback_.GetMisspelling(hash);
+  // GetMisspelling() returns null for flushed feedback. Feedback is flushed
+  // when the session expires every |kSessionHours| hours.
+  if (!misspelling)
+    return;
+  misspelling->action.type = SpellcheckAction::TYPE_MANUALLY_CORRECTED;
+  misspelling->action.value = correction;
+  misspelling->timestamp = base::Time::Now();
+}
+
+void FeedbackSender::OnReceiveDocumentMarkers(
+    int renderer_process_id,
+    const std::vector<uint32>& markers) {
+  if ((base::Time::Now() - session_start_).InHours() >=
+      chrome::spellcheck_common::kSessionHours) {
+    FlushFeedback();
+    return;
+  }
+
+  if (!feedback_.RendererHasMisspellings(renderer_process_id))
+    return;
+
+  feedback_.FinalizeRemovedMisspellings(renderer_process_id, markers);
+  SendFeedback(feedback_.GetMisspellingsInRenderer(renderer_process_id),
+               !renderers_sent_feedback_.count(renderer_process_id));
+  renderers_sent_feedback_.insert(renderer_process_id);
+  feedback_.EraseFinalizedMisspellings(renderer_process_id);
+}
+
+void FeedbackSender::OnSpellcheckResults(
+    int renderer_process_id,
+    const base::string16& text,
+    const std::vector<SpellCheckMarker>& markers,
+    std::vector<SpellCheckResult>* results) {
+  // Don't collect feedback if not going to send it.
+  if (!timer_.IsRunning())
+    return;
+
+  // Generate a map of marker offsets to marker hashes. This map helps to
+  // efficiently lookup feedback data based on the position of the misspelling
+  // in text.
+  typedef std::map<size_t, uint32> MarkerMap;
+  MarkerMap marker_map;
+  for (size_t i = 0; i < markers.size(); ++i)
+    marker_map[markers[i].offset] = markers[i].hash;
+
+  for (std::vector<SpellCheckResult>::iterator result_it = results->begin();
+       result_it != results->end();
+       ++result_it) {
+    if (!IsInBounds(result_it->location, result_it->length, text.length()))
+      continue;
+    MarkerMap::const_iterator marker_it = marker_map.find(result_it->location);
+    if (marker_it != marker_map.end() &&
+        feedback_.HasMisspelling(marker_it->second)) {
+      // If the renderer already has a marker for this spellcheck result, then
+      // set the hash of the spellcheck result to be the same as the marker.
+      result_it->hash = marker_it->second;
+    } else {
+      // If the renderer does not yet have a marker for this spellcheck result,
+      // then generate a new hash for the spellcheck result.
+      result_it->hash = BuildHash(session_start_, ++misspelling_counter_);
+    }
+    // Save the feedback data for the spellcheck result.
+    feedback_.AddMisspelling(renderer_process_id,
+                             BuildFeedback(*result_it, text));
+  }
+}
+
+void FeedbackSender::OnLanguageCountryChange(const std::string& language,
+                                             const std::string& country) {
+  FlushFeedback();
+  language_ = language;
+  country_ = country;
+}
+
+void FeedbackSender::StartFeedbackCollection() {
+  if (timer_.IsRunning())
+    return;
+
+  int interval_seconds = chrome::spellcheck_common::kFeedbackIntervalSeconds;
+  // This command-line switch is for testing and temporary.
+  // TODO(rouslan): Remove the command-line switch when testing is complete.
+  // http://crbug.com/247726
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSpellingServiceFeedbackIntervalSeconds)) {
+    base::StringToInt(
+        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+            switches::kSpellingServiceFeedbackIntervalSeconds),
+        &interval_seconds);
+    if (interval_seconds < kMinIntervalSeconds)
+      interval_seconds = kMinIntervalSeconds;
+    static const int kSessionSeconds =
+        chrome::spellcheck_common::kSessionHours * 60 * 60;
+    if (interval_seconds >  kSessionSeconds)
+      interval_seconds = kSessionSeconds;
+  }
+  timer_.Start(FROM_HERE,
+               base::TimeDelta::FromSeconds(interval_seconds),
+               this,
+               &FeedbackSender::RequestDocumentMarkers);
+}
+
+void FeedbackSender::StopFeedbackCollection() {
+  if (!timer_.IsRunning())
+    return;
+
+  FlushFeedback();
+  timer_.Stop();
+}
+
+void FeedbackSender::OnURLFetchComplete(const net::URLFetcher* source) {
+  for (ScopedVector<net::URLFetcher>::iterator sender_it = senders_.begin();
+       sender_it != senders_.end();
+       ++sender_it) {
+    if (*sender_it == source) {
+      senders_.erase(sender_it);
+      return;
+    }
+  }
+  delete source;
+}
+
+void FeedbackSender::RequestDocumentMarkers() {
+  // Request document markers from all the renderers that are still alive.
+  std::set<int> alive_renderers;
+  for (content::RenderProcessHost::iterator it(
+           content::RenderProcessHost::AllHostsIterator());
+       !it.IsAtEnd();
+       it.Advance()) {
+    alive_renderers.insert(it.GetCurrentValue()->GetID());
+    it.GetCurrentValue()->Send(new SpellCheckMsg_RequestDocumentMarkers());
+  }
+
+  // Asynchronously send out the feedback for all the renderers that are no
+  // longer alive.
+  std::vector<int> known_renderers = feedback_.GetRendersWithMisspellings();
+  std::sort(known_renderers.begin(), known_renderers.end());
+  std::vector<int> dead_renderers =
+      base::STLSetDifference<std::vector<int> >(known_renderers,
+                                                alive_renderers);
+  for (std::vector<int>::const_iterator it = dead_renderers.begin();
+       it != dead_renderers.end();
+       ++it) {
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(&FeedbackSender::OnReceiveDocumentMarkers,
+                   AsWeakPtr(),
+                   *it,
+                   std::vector<uint32>()));
+  }
+}
+
+void FeedbackSender::FlushFeedback() {
+  if (feedback_.Empty())
+    return;
+  feedback_.FinalizeAllMisspellings();
+  SendFeedback(feedback_.GetAllMisspellings(),
+               renderers_sent_feedback_.empty());
+  feedback_.Clear();
+  renderers_sent_feedback_.clear();
+  session_start_ = base::Time::Now();
+  timer_.Reset();
+}
+
+void FeedbackSender::SendFeedback(const std::vector<Misspelling>& feedback_data,
+                                  bool is_first_feedback_batch) {
+  scoped_ptr<base::Value> feedback_value(BuildFeedbackValue(
+      BuildParams(BuildSuggestionInfo(feedback_data, is_first_feedback_batch),
+                  language_,
+                  country_),
+      api_version_));
+  std::string feedback;
+  base::JSONWriter::Write(feedback_value.get(), &feedback);
+
+  // The tests use this identifier to mock the URL fetcher.
+  static const int kUrlFetcherId = 0;
+  net::URLFetcher* sender = net::URLFetcher::Create(
+      kUrlFetcherId, feedback_service_url_, net::URLFetcher::POST, this);
+  sender->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
+                       net::LOAD_DO_NOT_SAVE_COOKIES);
+  sender->SetUploadData("application/json", feedback);
+  senders_.push_back(sender);
+
+  // Request context is NULL in testing.
+  if (request_context_.get()) {
+    sender->SetRequestContext(request_context_.get());
+    sender->Start();
+  }
+}
+
+}  // namespace spellcheck
diff --git a/chrome/browser/spellchecker/feedback_sender.h b/chrome/browser/spellchecker/feedback_sender.h
new file mode 100644
index 0000000..80b23dd
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback_sender.h
@@ -0,0 +1,177 @@
+// Copyright (c) 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.
+//
+// An object to record and send user feedback to spelling service. The spelling
+// service uses the feedback to improve its suggestions.
+//
+// Assigns uint32 hash identifiers to spelling suggestions from spelling service
+// and stores these suggestions. Records user's actions on these suggestions.
+// Periodically sends batches of user feedback to the spelling service.
+
+#ifndef CHROME_BROWSER_SPELLCHECKER_FEEDBACK_SENDER_H_
+#define CHROME_BROWSER_SPELLCHECKER_FEEDBACK_SENDER_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/spellchecker/feedback.h"
+#include "chrome/browser/spellchecker/misspelling.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "url/gurl.h"
+
+class SpellCheckMarker;
+struct SpellCheckResult;
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+}
+
+namespace spellcheck {
+
+namespace {
+
+// Constants for the feedback field trial.
+const char kFeedbackFieldTrialName[] = "SpellingServiceFeedback";
+const char kFeedbackFieldTrialEnabledGroupName[] = "Enabled";
+
+}  // namespace
+
+// Stores and sends user feedback to the spelling service. Sample usage:
+//    FeedbackSender sender(profile.GetRequestContext(), language, country);
+//    sender.OnSpellcheckResults(spellcheck_results_from_spelling_service,
+//                               renderer_process_id,
+//                               spellchecked_text,
+//                               existing_hashes);
+//    sender.SelectedSuggestion(hash, suggestion_index);
+class FeedbackSender : public base::SupportsWeakPtr<FeedbackSender>,
+                       public net::URLFetcherDelegate {
+ public:
+  // Constructs a feedback sender. Keeps |request_context| in a scoped_refptr,
+  // because URLRequestContextGetter implements RefcountedThreadSafe.
+  FeedbackSender(net::URLRequestContextGetter* request_context,
+                 const std::string& language,
+                 const std::string& country);
+  ~FeedbackSender() override;
+
+  // Records that user selected suggestion |suggestion_index| for the
+  // misspelling identified by |hash|.
+  void SelectedSuggestion(uint32 hash, int suggestion_index);
+
+  // Records that user added the misspelling identified by |hash| to the
+  // dictionary.
+  void AddedToDictionary(uint32 hash);
+
+  // Records that user right-clicked on the misspelling identified by |hash|,
+  // but did not select any suggestion.
+  void IgnoredSuggestions(uint32 hash);
+
+  // Records that user did not choose any suggestion but manually corrected the
+  // misspelling identified by |hash| to string |correction|, which is not in
+  // the list of suggestions.
+  void ManuallyCorrected(uint32 hash, const base::string16& correction);
+
+  // Records that user has the misspelling in the custom dictionary. The user
+  // will never see the spellcheck suggestions for the misspelling.
+  void RecordInDictionary(uint32 hash);
+
+  // Receives document markers for renderer with process ID |render_process_id|
+  // when the renderer responds to a RequestDocumentMarkers() call. Finalizes
+  // feedback for the markers that are gone from the renderer. Sends feedback
+  // data for the renderer with process ID |renderer_process_id| to the spelling
+  // service. If the current session has expired, then refreshes the session
+  // start timestamp and sends out all of the feedback data.
+  void OnReceiveDocumentMarkers(int renderer_process_id,
+                                const std::vector<uint32>& markers);
+
+  // Generates feedback data based on spellcheck results. The new feedback data
+  // is pending. Sets hash identifiers for |results|. Called when spelling
+  // service client receives results from the spelling service. Does not take
+  // ownership of |results|.
+  void OnSpellcheckResults(int renderer_process_id,
+                           const base::string16& text,
+                           const std::vector<SpellCheckMarker>& markers,
+                           std::vector<SpellCheckResult>* results);
+
+  // Receives updated language and country code for feedback. Finalizes and
+  // sends out all of the feedback data.
+  void OnLanguageCountryChange(const std::string& language,
+                               const std::string& country);
+
+  // Starts collecting feedback, if it's not already being collected.
+  void StartFeedbackCollection();
+
+  // Sends out all previously collected data and stops collecting feedback, if
+  // it was being collected.
+  void StopFeedbackCollection();
+
+ private:
+  friend class FeedbackSenderTest;
+
+  // net::URLFetcherDelegate implementation. Takes ownership of |source|.
+  void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+  // Requests the document markers from all of the renderers to determine which
+  // feedback can be finalized. Finalizes feedback for renderers that are gone.
+  // Called periodically when |timer_| fires.
+  void RequestDocumentMarkers();
+
+  // Sends out all feedback data. Resets the session-start timestamp to now.
+  // Restarts the timer that requests markers from the renderers.
+  void FlushFeedback();
+
+  // Sends out the |feedback_data|.
+  void SendFeedback(const std::vector<Misspelling>& feedback_data,
+                    bool is_first_feedback_batch);
+
+  // URL request context for the feedback senders.
+  scoped_refptr<net::URLRequestContextGetter> request_context_;
+
+  // The feedback API version.
+  const std::string api_version_;
+
+  // The language of text. The string is a BCP 47 language tag.
+  std::string language_;
+
+  // The country of origin. The string is the ISO 3166-1 alpha-3 code.
+  std::string country_;
+
+  // Misspelling counter used to generate unique hash identifier for each
+  // misspelling.
+  size_t misspelling_counter_;
+
+  // Feedback data.
+  Feedback feedback_;
+
+  // A set of renderer process IDs for renderers that have sent out feedback in
+  // this session.
+  std::set<int> renderers_sent_feedback_;
+
+  // When the session started.
+  base::Time session_start_;
+
+  // The URL where the feedback data should be sent.
+  GURL feedback_service_url_;
+
+  // A timer to periodically request a list of document spelling markers from
+  // all of the renderers. The timer starts in StartFeedbackCollection() and
+  // stops in StopFeedbackCollection(). The timer stops and abandons its tasks
+  // on destruction.
+  base::RepeatingTimer<FeedbackSender> timer_;
+
+  // Feedback senders that need to stay alive for the duration of sending data.
+  // If a sender is destroyed before it finishes, then sending feedback will be
+  // canceled.
+  ScopedVector<net::URLFetcher> senders_;
+
+  DISALLOW_COPY_AND_ASSIGN(FeedbackSender);
+};
+
+}  // namespace spellcheck
+
+#endif  // CHROME_BROWSER_SPELLCHECKER_FEEDBACK_SENDER_H_
diff --git a/chrome/browser/spellchecker/feedback_sender_unittest.cc b/chrome/browser/spellchecker/feedback_sender_unittest.cc
new file mode 100644
index 0000000..5f6963d6
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback_sender_unittest.cc
@@ -0,0 +1,624 @@
+// Copyright (c) 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.
+//
+// Unit tests for |FeedbackSender| object.
+
+#include "chrome/browser/spellchecker/feedback_sender.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/json/json_reader.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/field_trial.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/spellcheck_common.h"
+#include "chrome/common/spellcheck_marker.h"
+#include "chrome/common/spellcheck_result.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/variations/entropy_provider.h"
+#include "content/public/test/test_browser_thread.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace spellcheck {
+
+namespace {
+
+const char kCountry[] = "USA";
+const char kLanguage[] = "en";
+const char kText[] = "Helllo world.";
+const int kMisspellingLength = 6;
+const int kMisspellingStart = 0;
+const int kRendererProcessId = 0;
+const int kUrlFetcherId = 0;
+
+// Builds a simple spellcheck result.
+SpellCheckResult BuildSpellCheckResult() {
+  return SpellCheckResult(SpellCheckResult::SPELLING,
+                          kMisspellingStart,
+                          kMisspellingLength,
+                          base::UTF8ToUTF16("Hello"));
+}
+
+// Returns the number of times that |needle| appears in |haystack| without
+// overlaps. For example, CountOccurences("bananana", "nana") returns 1.
+int CountOccurences(const std::string& haystack, const std::string& needle) {
+  int number_of_occurrences = 0;
+  for (size_t pos = haystack.find(needle);
+       pos != std::string::npos;
+       pos = haystack.find(needle, pos + needle.length())) {
+    ++number_of_occurrences;
+  }
+  return number_of_occurrences;
+}
+
+}  // namespace
+
+// A test fixture to help keep tests simple.
+class FeedbackSenderTest : public testing::Test {
+ public:
+  FeedbackSenderTest() : ui_thread_(content::BrowserThread::UI, &loop_) {
+    feedback_.reset(new FeedbackSender(NULL, kLanguage, kCountry));
+    feedback_->StartFeedbackCollection();
+  }
+
+  ~FeedbackSenderTest() override {}
+
+ protected:
+  // Appends the "--enable-spelling-service-feedback" switch to the
+  // command-line.
+  void AppendCommandLineSwitch() {
+    // The command-line switch is temporary.
+    // TODO(rouslan): Remove the command-line switch. http://crbug.com/247726
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kEnableSpellingFeedbackFieldTrial);
+    feedback_.reset(new FeedbackSender(NULL, kLanguage, kCountry));
+    feedback_->StartFeedbackCollection();
+  }
+
+  // Enables the "SpellingServiceFeedback.Enabled" field trial.
+  void EnableFieldTrial() {
+    // The field trial is temporary.
+    // TODO(rouslan): Remove the field trial. http://crbug.com/247726
+    field_trial_list_.reset(
+        new base::FieldTrialList(new metrics::SHA1EntropyProvider("foo")));
+    field_trial_ = base::FieldTrialList::CreateFieldTrial(
+        kFeedbackFieldTrialName, kFeedbackFieldTrialEnabledGroupName);
+    field_trial_->group();
+    feedback_.reset(new FeedbackSender(NULL, kLanguage, kCountry));
+    feedback_->StartFeedbackCollection();
+  }
+
+  uint32 AddPendingFeedback() {
+    std::vector<SpellCheckResult> results(1, BuildSpellCheckResult());
+    feedback_->OnSpellcheckResults(kRendererProcessId,
+                                   base::UTF8ToUTF16(kText),
+                                   std::vector<SpellCheckMarker>(),
+                                   &results);
+    return results[0].hash;
+  }
+
+  void ExpireSession() {
+    feedback_->session_start_ =
+        base::Time::Now() -
+        base::TimeDelta::FromHours(chrome::spellcheck_common::kSessionHours);
+  }
+
+  bool UploadDataContains(const std::string& data) const {
+    const net::TestURLFetcher* fetcher =
+        fetchers_.GetFetcherByID(kUrlFetcherId);
+    return fetcher && CountOccurences(fetcher->upload_data(), data) > 0;
+  }
+
+  bool UploadDataContains(const std::string& data,
+                          int number_of_occurrences) const {
+    const net::TestURLFetcher* fetcher =
+        fetchers_.GetFetcherByID(kUrlFetcherId);
+    return fetcher && CountOccurences(fetcher->upload_data(), data) ==
+                          number_of_occurrences;
+  }
+
+  // Returns true if the feedback sender would be uploading data now. The test
+  // does not open network connections.
+  bool IsUploadingData() const {
+    return !!fetchers_.GetFetcherByID(kUrlFetcherId);
+  }
+
+  void ClearUploadData() {
+    fetchers_.RemoveFetcherFromMap(kUrlFetcherId);
+  }
+
+  std::string GetUploadData() const {
+    const net::TestURLFetcher* fetcher =
+        fetchers_.GetFetcherByID(kUrlFetcherId);
+    return fetcher ? fetcher->upload_data() : std::string();
+  }
+
+  scoped_ptr<spellcheck::FeedbackSender> feedback_;
+
+ private:
+  TestingProfile profile_;
+  base::MessageLoop loop_;
+  content::TestBrowserThread ui_thread_;
+  scoped_ptr<base::FieldTrialList> field_trial_list_;
+  scoped_refptr<base::FieldTrial> field_trial_;
+  net::TestURLFetcherFactory fetchers_;
+};
+
+// Do not send data if there's no feedback.
+TEST_F(FeedbackSenderTest, NoFeedback) {
+  EXPECT_FALSE(IsUploadingData());
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_FALSE(IsUploadingData());
+}
+
+// Do not send data if not aware of which markers are still in the document.
+TEST_F(FeedbackSenderTest, NoDocumentMarkersReceived) {
+  EXPECT_FALSE(IsUploadingData());
+  uint32 hash = AddPendingFeedback();
+  EXPECT_FALSE(IsUploadingData());
+  static const int kSuggestionIndex = 1;
+  feedback_->SelectedSuggestion(hash, kSuggestionIndex);
+  EXPECT_FALSE(IsUploadingData());
+}
+
+// Send PENDING feedback message if the marker is still in the document, and the
+// user has not performed any action on it.
+TEST_F(FeedbackSenderTest, PendingFeedback) {
+  uint32 hash = AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>(1, hash));
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
+}
+
+// Send NO_ACTION feedback message if the marker has been removed from the
+// document.
+TEST_F(FeedbackSenderTest, NoActionFeedback) {
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
+}
+
+// Send SELECT feedback message if the user has selected a spelling suggestion.
+TEST_F(FeedbackSenderTest, SelectFeedback) {
+  uint32 hash = AddPendingFeedback();
+  static const int kSuggestionIndex = 0;
+  feedback_->SelectedSuggestion(hash, kSuggestionIndex);
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"SELECT\""));
+  EXPECT_TRUE(UploadDataContains("\"actionTargetIndex\":" + kSuggestionIndex));
+}
+
+// Send ADD_TO_DICT feedback message if the user has added the misspelled word
+// to the custom dictionary.
+TEST_F(FeedbackSenderTest, AddToDictFeedback) {
+  uint32 hash = AddPendingFeedback();
+  feedback_->AddedToDictionary(hash);
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"ADD_TO_DICT\""));
+}
+
+// Send IN_DICTIONARY feedback message if the user has the misspelled word in
+// the custom dictionary.
+TEST_F(FeedbackSenderTest, InDictionaryFeedback) {
+  uint32 hash = AddPendingFeedback();
+  feedback_->RecordInDictionary(hash);
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"IN_DICTIONARY\""));
+}
+
+// Send PENDING feedback message if the user saw the spelling suggestion, but
+// decided to not select it, and the marker is still in the document.
+TEST_F(FeedbackSenderTest, IgnoreFeedbackMarkerInDocument) {
+  uint32 hash = AddPendingFeedback();
+  feedback_->IgnoredSuggestions(hash);
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>(1, hash));
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
+}
+
+// Send IGNORE feedback message if the user saw the spelling suggestion, but
+// decided to not select it, and the marker is no longer in the document.
+TEST_F(FeedbackSenderTest, IgnoreFeedbackMarkerNotInDocument) {
+  uint32 hash = AddPendingFeedback();
+  feedback_->IgnoredSuggestions(hash);
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"IGNORE\""));
+}
+
+// Send MANUALLY_CORRECTED feedback message if the user manually corrected the
+// misspelled word.
+TEST_F(FeedbackSenderTest, ManuallyCorrectedFeedback) {
+  uint32 hash = AddPendingFeedback();
+  static const std::string kManualCorrection = "Howdy";
+  feedback_->ManuallyCorrected(hash, base::ASCIIToUTF16(kManualCorrection));
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"MANUALLY_CORRECTED\""));
+  EXPECT_TRUE(UploadDataContains("\"actionTargetValue\":\"" +
+                                 kManualCorrection + "\""));
+}
+
+// Send feedback messages in batch.
+TEST_F(FeedbackSenderTest, BatchFeedback) {
+  std::vector<SpellCheckResult> results;
+  results.push_back(SpellCheckResult(SpellCheckResult::SPELLING,
+                                     kMisspellingStart,
+                                     kMisspellingLength,
+                                     base::ASCIIToUTF16("Hello")));
+  static const int kSecondMisspellingStart = 7;
+  static const int kSecondMisspellingLength = 5;
+  results.push_back(SpellCheckResult(SpellCheckResult::SPELLING,
+                                     kSecondMisspellingStart,
+                                     kSecondMisspellingLength,
+                                     base::ASCIIToUTF16("world")));
+  feedback_->OnSpellcheckResults(kRendererProcessId,
+                                 base::UTF8ToUTF16(kText),
+                                 std::vector<SpellCheckMarker>(),
+                                 &results);
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\"", 2));
+}
+
+// Send a series of PENDING feedback messages and one final NO_ACTION feedback
+// message with the same hash identifier for a single misspelling.
+TEST_F(FeedbackSenderTest, SameHashFeedback) {
+  uint32 hash = AddPendingFeedback();
+  std::vector<uint32> remaining_markers(1, hash);
+
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
+  std::string hash_string = base::StringPrintf("\"suggestionId\":\"%u\"", hash);
+  EXPECT_TRUE(UploadDataContains(hash_string));
+  ClearUploadData();
+
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
+  EXPECT_TRUE(UploadDataContains(hash_string));
+  ClearUploadData();
+
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
+  EXPECT_TRUE(UploadDataContains(hash_string));
+  ClearUploadData();
+
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_FALSE(IsUploadingData());
+}
+
+// When a session expires:
+// 1) Pending feedback is finalized and sent to the server in the last message
+//    batch in the session.
+// 2) No feedback is sent until a spellcheck request happens.
+// 3) Existing markers get new hash identifiers.
+TEST_F(FeedbackSenderTest, SessionExpirationFeedback) {
+  std::vector<SpellCheckResult> results(
+      1,
+      SpellCheckResult(SpellCheckResult::SPELLING,
+                       kMisspellingStart,
+                       kMisspellingLength,
+                       base::ASCIIToUTF16("Hello")));
+  feedback_->OnSpellcheckResults(kRendererProcessId,
+                                 base::UTF8ToUTF16(kText),
+                                 std::vector<SpellCheckMarker>(),
+                                 &results);
+  uint32 original_hash = results[0].hash;
+  std::vector<uint32> remaining_markers(1, original_hash);
+
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
+  EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
+  std::string original_hash_string =
+      base::StringPrintf("\"suggestionId\":\"%u\"", original_hash);
+  EXPECT_TRUE(UploadDataContains(original_hash_string));
+  ClearUploadData();
+
+  ExpireSession();
+
+  // Last message batch in the current session has only finalized messages.
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
+  EXPECT_FALSE(UploadDataContains("\"actionType\":\"PENDING\""));
+  EXPECT_TRUE(UploadDataContains(original_hash_string));
+  ClearUploadData();
+
+  // The next session starts on the next spellchecker request. Until then,
+  // there's no more feedback sent.
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
+  EXPECT_FALSE(IsUploadingData());
+
+  // The first spellcheck request after session expiration creates different
+  // document marker hash identifiers.
+  std::vector<SpellCheckMarker> original_markers(
+      1, SpellCheckMarker(results[0].hash, results[0].location));
+  results[0] = SpellCheckResult(SpellCheckResult::SPELLING,
+                                kMisspellingStart,
+                                kMisspellingLength,
+                                base::ASCIIToUTF16("Hello"));
+  feedback_->OnSpellcheckResults(
+      kRendererProcessId, base::UTF8ToUTF16(kText), original_markers, &results);
+  uint32 updated_hash = results[0].hash;
+  EXPECT_NE(updated_hash, original_hash);
+  remaining_markers[0] = updated_hash;
+
+  // The first feedback message batch in session |i + 1| has the new document
+  // marker hash identifiers.
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
+  EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
+  EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
+  EXPECT_FALSE(UploadDataContains(original_hash_string));
+  std::string updated_hash_string =
+      base::StringPrintf("\"suggestionId\":\"%u\"", updated_hash);
+  EXPECT_TRUE(UploadDataContains(updated_hash_string));
+}
+
+// First message in session has an indicator.
+TEST_F(FeedbackSenderTest, FirstMessageInSessionIndicator) {
+  // Session 1, message 1
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":true"));
+
+  // Session 1, message 2
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
+
+  ExpireSession();
+
+  // Session 1, message 3 (last)
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
+
+  // Session 2, message 1
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":true"));
+
+  // Session 2, message 2
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
+}
+
+// Flush all feedback when the spellcheck language and country change.
+TEST_F(FeedbackSenderTest, OnLanguageCountryChange) {
+  AddPendingFeedback();
+  feedback_->OnLanguageCountryChange("pt", "BR");
+  EXPECT_TRUE(UploadDataContains("\"language\":\"en\""));
+  AddPendingFeedback();
+  feedback_->OnLanguageCountryChange("en", "US");
+  EXPECT_TRUE(UploadDataContains("\"language\":\"pt\""));
+}
+
+// The field names and types should correspond to the API.
+TEST_F(FeedbackSenderTest, FeedbackAPI) {
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  std::string actual_data = GetUploadData();
+  scoped_ptr<base::DictionaryValue> actual(
+      static_cast<base::DictionaryValue*>(base::JSONReader::Read(actual_data)));
+  actual->SetString("params.key", "TestDummyKey");
+  base::ListValue* suggestions = NULL;
+  actual->GetList("params.suggestionInfo", &suggestions);
+  base::DictionaryValue* suggestion = NULL;
+  suggestions->GetDictionary(0, &suggestion);
+  suggestion->SetString("suggestionId", "42");
+  suggestion->SetString("timestamp", "9001");
+  static const std::string expected_data =
+      "{\"apiVersion\":\"v2\","
+      "\"method\":\"spelling.feedback\","
+      "\"params\":"
+      "{\"clientName\":\"Chrome\","
+      "\"originCountry\":\"USA\","
+      "\"key\":\"TestDummyKey\","
+      "\"language\":\"en\","
+      "\"suggestionInfo\":[{"
+      "\"isAutoCorrection\":false,"
+      "\"isFirstInSession\":true,"
+      "\"misspelledLength\":6,"
+      "\"misspelledStart\":0,"
+      "\"originalText\":\"Helllo world\","
+      "\"suggestionId\":\"42\","
+      "\"suggestions\":[\"Hello\"],"
+      "\"timestamp\":\"9001\","
+      "\"userActions\":[{\"actionType\":\"NO_ACTION\"}]}]}}";
+  scoped_ptr<base::Value> expected(base::JSONReader::Read(expected_data));
+  EXPECT_TRUE(expected->Equals(actual.get()))
+      << "Expected data: " << expected_data
+      << "\nActual data:   " << actual_data;
+}
+
+// The default API version is "v2".
+TEST_F(FeedbackSenderTest, DefaultApiVersion) {
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2\""));
+  EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
+}
+
+// The API version should not change for field-trial participants that do not
+// append the command-line switch.
+TEST_F(FeedbackSenderTest, FieldTrialAloneHasSameApiVersion) {
+  EnableFieldTrial();
+
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+
+  EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2\""));
+  EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
+}
+
+// The API version should not change if the command-line switch is appended, but
+// the user is not participating in the field-trial.
+TEST_F(FeedbackSenderTest, CommandLineSwitchAloneHasSameApiVersion) {
+  AppendCommandLineSwitch();
+
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+
+  EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2\""));
+  EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
+}
+
+// The API version should be different for field-trial participants that also
+// append the command-line switch.
+TEST_F(FeedbackSenderTest, InternalApiVersion) {
+  AppendCommandLineSwitch();
+  EnableFieldTrial();
+
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+
+  EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2\""));
+  EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
+}
+
+// Duplicate spellcheck results should be matched to the existing markers.
+TEST_F(FeedbackSenderTest, MatchDupliateResultsWithExistingMarkers) {
+  uint32 hash = AddPendingFeedback();
+  std::vector<SpellCheckResult> results(
+      1,
+      SpellCheckResult(SpellCheckResult::SPELLING,
+                       kMisspellingStart,
+                       kMisspellingLength,
+                       base::ASCIIToUTF16("Hello")));
+  std::vector<SpellCheckMarker> markers(
+      1, SpellCheckMarker(hash, results[0].location));
+  EXPECT_EQ(static_cast<uint32>(0), results[0].hash);
+  feedback_->OnSpellcheckResults(
+      kRendererProcessId, base::UTF8ToUTF16(kText), markers, &results);
+  EXPECT_EQ(hash, results[0].hash);
+}
+
+// Adding a word to dictionary should trigger ADD_TO_DICT feedback for every
+// occurrence of that word.
+TEST_F(FeedbackSenderTest, MultipleAddToDictFeedback) {
+  std::vector<SpellCheckResult> results;
+  static const int kSentenceLength = 14;
+  static const int kNumberOfSentences = 2;
+  static const base::string16 kTextWithDuplicates =
+      base::ASCIIToUTF16("Helllo world. Helllo world.");
+  for (int i = 0; i < kNumberOfSentences; ++i) {
+    results.push_back(SpellCheckResult(SpellCheckResult::SPELLING,
+                                       kMisspellingStart + i * kSentenceLength,
+                                       kMisspellingLength,
+                                       base::ASCIIToUTF16("Hello")));
+  }
+  static const int kNumberOfRenderers = 2;
+  int last_renderer_process_id = -1;
+  for (int i = 0; i < kNumberOfRenderers; ++i) {
+    feedback_->OnSpellcheckResults(kRendererProcessId + i,
+                                   kTextWithDuplicates,
+                                   std::vector<SpellCheckMarker>(),
+                                   &results);
+    last_renderer_process_id = kRendererProcessId + i;
+  }
+  std::vector<uint32> remaining_markers;
+  for (size_t i = 0; i < results.size(); ++i)
+    remaining_markers.push_back(results[i].hash);
+  feedback_->OnReceiveDocumentMarkers(last_renderer_process_id,
+                                      remaining_markers);
+  EXPECT_TRUE(UploadDataContains("PENDING", 2));
+  EXPECT_FALSE(UploadDataContains("ADD_TO_DICT"));
+
+  feedback_->AddedToDictionary(results[0].hash);
+  feedback_->OnReceiveDocumentMarkers(last_renderer_process_id,
+                                      remaining_markers);
+  EXPECT_FALSE(UploadDataContains("PENDING"));
+  EXPECT_TRUE(UploadDataContains("ADD_TO_DICT", 2));
+}
+
+// ADD_TO_DICT feedback for multiple occurrences of a word should trigger only
+// for pending feedback.
+TEST_F(FeedbackSenderTest, AddToDictOnlyPending) {
+  AddPendingFeedback();
+  uint32 add_to_dict_hash = AddPendingFeedback();
+  uint32 select_hash = AddPendingFeedback();
+  feedback_->SelectedSuggestion(select_hash, 0);
+  feedback_->AddedToDictionary(add_to_dict_hash);
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(UploadDataContains("SELECT", 1));
+  EXPECT_TRUE(UploadDataContains("ADD_TO_DICT", 2));
+}
+
+// Spellcheck results that are out-of-bounds are not added to feedback.
+TEST_F(FeedbackSenderTest, IgnoreOutOfBounds) {
+  std::vector<SpellCheckResult> results;
+  results.push_back(SpellCheckResult(
+      SpellCheckResult::SPELLING, 0, 100, base::UTF8ToUTF16("Hello")));
+  results.push_back(SpellCheckResult(
+      SpellCheckResult::SPELLING, 100, 3, base::UTF8ToUTF16("world")));
+  results.push_back(SpellCheckResult(
+      SpellCheckResult::SPELLING, -1, 3, base::UTF8ToUTF16("how")));
+  results.push_back(SpellCheckResult(
+      SpellCheckResult::SPELLING, 0, 0, base::UTF8ToUTF16("are")));
+  results.push_back(SpellCheckResult(
+      SpellCheckResult::SPELLING, 2, -1, base::UTF8ToUTF16("you")));
+  feedback_->OnSpellcheckResults(kRendererProcessId,
+                                 base::UTF8ToUTF16(kText),
+                                 std::vector<SpellCheckMarker>(),
+                                 &results);
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_FALSE(IsUploadingData());
+}
+
+// FeedbackSender does not collect and upload feedback when instructed to stop.
+TEST_F(FeedbackSenderTest, CanStopFeedbackCollection) {
+  feedback_->StopFeedbackCollection();
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_FALSE(IsUploadingData());
+}
+
+// FeedbackSender resumes collecting and uploading feedback when instructed to
+// start after stopping.
+TEST_F(FeedbackSenderTest, CanResumeFeedbackCollection) {
+  feedback_->StopFeedbackCollection();
+  feedback_->StartFeedbackCollection();
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_TRUE(IsUploadingData());
+}
+
+// FeedbackSender does not collect data while being stopped and upload it later.
+TEST_F(FeedbackSenderTest, NoFeedbackCollectionWhenStopped) {
+  feedback_->StopFeedbackCollection();
+  AddPendingFeedback();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  feedback_->StartFeedbackCollection();
+  feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+                                      std::vector<uint32>());
+  EXPECT_FALSE(IsUploadingData());
+}
+
+}  // namespace spellcheck
diff --git a/chrome/browser/spellchecker/feedback_unittest.cc b/chrome/browser/spellchecker/feedback_unittest.cc
new file mode 100644
index 0000000..09d0af7
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback_unittest.cc
@@ -0,0 +1,259 @@
+// Copyright (c) 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.
+//
+// Unit tests for |Feedback| object.
+
+#include "chrome/browser/spellchecker/feedback.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::ASCIIToUTF16;
+
+namespace spellcheck {
+
+namespace {
+
+// Identifier for a renderer process.
+const int kRendererProcessId = 7;
+
+// Hash identifier for a misspelling.
+const uint32 kMisspellingHash = 42;
+
+}  // namespace
+
+// A test fixture to help keep the tests simple.
+class FeedbackTest : public testing::Test {
+ public:
+  FeedbackTest() {}
+  ~FeedbackTest() override {}
+
+ protected:
+  void AddMisspelling(int renderer_process_id, uint32 hash) {
+    Misspelling misspelling;
+    misspelling.hash = hash;
+    feedback_.AddMisspelling(renderer_process_id, misspelling);
+  }
+
+  spellcheck::Feedback feedback_;
+};
+
+// Should be able to retrieve misspelling after it's added.
+TEST_F(FeedbackTest, RetreiveMisspelling) {
+  EXPECT_EQ(NULL, feedback_.GetMisspelling(kMisspellingHash));
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  Misspelling* result = feedback_.GetMisspelling(kMisspellingHash);
+  EXPECT_NE(static_cast<Misspelling*>(NULL), result);
+  EXPECT_EQ(kMisspellingHash, result->hash);
+}
+
+// Removed misspellings should be finalized.
+TEST_F(FeedbackTest, FinalizeRemovedMisspellings) {
+  static const int kRemovedMisspellingHash = 1;
+  static const int kRemainingMisspellingHash = 2;
+  AddMisspelling(kRendererProcessId, kRemovedMisspellingHash);
+  AddMisspelling(kRendererProcessId, kRemainingMisspellingHash);
+  std::vector<uint32> remaining_markers(1, kRemainingMisspellingHash);
+  feedback_.FinalizeRemovedMisspellings(kRendererProcessId, remaining_markers);
+  Misspelling* removed_misspelling =
+      feedback_.GetMisspelling(kRemovedMisspellingHash);
+  EXPECT_NE(static_cast<Misspelling*>(NULL), removed_misspelling);
+  EXPECT_TRUE(removed_misspelling->action.IsFinal());
+  Misspelling* remaining_misspelling =
+      feedback_.GetMisspelling(kRemainingMisspellingHash);
+  EXPECT_NE(static_cast<Misspelling*>(NULL), remaining_misspelling);
+  EXPECT_FALSE(remaining_misspelling->action.IsFinal());
+}
+
+// Duplicate misspellings should not be finalized.
+TEST_F(FeedbackTest, DuplicateMisspellingFinalization) {
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  std::vector<uint32> remaining_markers(1, kMisspellingHash);
+  feedback_.FinalizeRemovedMisspellings(kRendererProcessId, remaining_markers);
+  std::vector<Misspelling> misspellings = feedback_.GetAllMisspellings();
+  EXPECT_EQ(static_cast<size_t>(1), misspellings.size());
+  EXPECT_FALSE(misspellings[0].action.IsFinal());
+}
+
+// Misspellings should be associated with a renderer.
+TEST_F(FeedbackTest, RendererHasMisspellings) {
+  EXPECT_FALSE(feedback_.RendererHasMisspellings(kRendererProcessId));
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  EXPECT_TRUE(feedback_.RendererHasMisspellings(kRendererProcessId));
+}
+
+// Should be able to retrieve misspellings in renderer.
+TEST_F(FeedbackTest, GetMisspellingsInRenderer) {
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  const std::vector<Misspelling>& renderer_with_misspellings =
+      feedback_.GetMisspellingsInRenderer(kRendererProcessId);
+  EXPECT_EQ(static_cast<size_t>(1), renderer_with_misspellings.size());
+  EXPECT_EQ(kMisspellingHash, renderer_with_misspellings[0].hash);
+  const std::vector<Misspelling>& renderer_without_misspellings =
+      feedback_.GetMisspellingsInRenderer(kRendererProcessId + 1);
+  EXPECT_EQ(static_cast<size_t>(0), renderer_without_misspellings.size());
+}
+
+// Finalized misspellings should be erased.
+TEST_F(FeedbackTest, EraseFinalizedMisspellings) {
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  feedback_.FinalizeRemovedMisspellings(kRendererProcessId,
+                                        std::vector<uint32>());
+  EXPECT_TRUE(feedback_.RendererHasMisspellings(kRendererProcessId));
+  feedback_.EraseFinalizedMisspellings(kRendererProcessId);
+  EXPECT_FALSE(feedback_.RendererHasMisspellings(kRendererProcessId));
+  EXPECT_TRUE(feedback_.GetMisspellingsInRenderer(kRendererProcessId).empty());
+}
+
+// Should be able to check for misspelling existence.
+TEST_F(FeedbackTest, HasMisspelling) {
+  EXPECT_FALSE(feedback_.HasMisspelling(kMisspellingHash));
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  EXPECT_TRUE(feedback_.HasMisspelling(kMisspellingHash));
+}
+
+// Should be able to check for feedback data presence.
+TEST_F(FeedbackTest, EmptyFeedback) {
+  EXPECT_TRUE(feedback_.Empty());
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  EXPECT_FALSE(feedback_.Empty());
+}
+
+// Should be able to retrieve a list of all renderers with misspellings.
+TEST_F(FeedbackTest, GetRendersWithMisspellings) {
+  EXPECT_TRUE(feedback_.GetRendersWithMisspellings().empty());
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  AddMisspelling(kRendererProcessId + 1, kMisspellingHash + 1);
+  std::vector<int> result = feedback_.GetRendersWithMisspellings();
+  EXPECT_EQ(static_cast<size_t>(2), result.size());
+  EXPECT_NE(result[0], result[1]);
+  EXPECT_TRUE(result[0] == kRendererProcessId ||
+              result[0] == kRendererProcessId + 1);
+  EXPECT_TRUE(result[1] == kRendererProcessId ||
+              result[1] == kRendererProcessId + 1);
+}
+
+// Should be able to finalize all misspellings.
+TEST_F(FeedbackTest, FinalizeAllMisspellings) {
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  AddMisspelling(kRendererProcessId + 1, kMisspellingHash + 1);
+  {
+    std::vector<Misspelling> pending = feedback_.GetAllMisspellings();
+    for (std::vector<Misspelling>::const_iterator it = pending.begin();
+         it != pending.end();
+         ++it) {
+      EXPECT_FALSE(it->action.IsFinal());
+    }
+  }
+  feedback_.FinalizeAllMisspellings();
+  {
+    std::vector<Misspelling> final = feedback_.GetAllMisspellings();
+    for (std::vector<Misspelling>::const_iterator it = final.begin();
+         it != final.end();
+         ++it) {
+      EXPECT_TRUE(it->action.IsFinal());
+    }
+  }
+}
+
+// Should be able to retrieve a copy of all misspellings.
+TEST_F(FeedbackTest, GetAllMisspellings) {
+  EXPECT_TRUE(feedback_.GetAllMisspellings().empty());
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  AddMisspelling(kRendererProcessId + 1, kMisspellingHash + 1);
+  const std::vector<Misspelling>& result = feedback_.GetAllMisspellings();
+  EXPECT_EQ(static_cast<size_t>(2), result.size());
+  EXPECT_NE(result[0].hash, result[1].hash);
+  EXPECT_TRUE(result[0].hash == kMisspellingHash ||
+              result[0].hash == kMisspellingHash + 1);
+  EXPECT_TRUE(result[1].hash == kMisspellingHash ||
+              result[1].hash == kMisspellingHash + 1);
+}
+
+// Should be able to clear all misspellings.
+TEST_F(FeedbackTest, ClearFeedback) {
+  AddMisspelling(kRendererProcessId, kMisspellingHash);
+  AddMisspelling(kRendererProcessId + 1, kMisspellingHash + 1);
+  EXPECT_FALSE(feedback_.Empty());
+  feedback_.Clear();
+  EXPECT_TRUE(feedback_.Empty());
+}
+
+// Should be able to find misspellings by misspelled word.
+TEST_F(FeedbackTest, FindMisspellingsByText) {
+  static const base::string16 kMisspelledText =
+      ASCIIToUTF16("Helllo world. Helllo world");
+  static const base::string16 kSuggestion = ASCIIToUTF16("Hello");
+  static const int kMisspellingStart = 0;
+  static const int kMisspellingLength = 6;
+  static const int kSentenceLength = 14;
+  static const int kNumberOfSentences = 2;
+  static const int kNumberOfRenderers = 2;
+  uint32 hash = kMisspellingHash;
+  for (int renderer_process_id = kRendererProcessId;
+       renderer_process_id < kRendererProcessId + kNumberOfRenderers;
+       ++renderer_process_id) {
+    for (int j = 0; j < kNumberOfSentences; ++j) {
+      feedback_.AddMisspelling(
+          renderer_process_id,
+          Misspelling(kMisspelledText,
+                      kMisspellingStart + j * kSentenceLength,
+                      kMisspellingLength,
+                      std::vector<base::string16>(1, kSuggestion),
+                      ++hash));
+    }
+  }
+
+  static const base::string16 kOtherMisspelledText =
+      ASCIIToUTF16("Somethign else");
+  static const base::string16 kOtherSuggestion = ASCIIToUTF16("Something");
+  static const int kOtherMisspellingStart = 0;
+  static const int kOtherMisspellingLength = 9;
+  feedback_.AddMisspelling(
+      kRendererProcessId,
+      Misspelling(kOtherMisspelledText,
+                  kOtherMisspellingStart,
+                  kOtherMisspellingLength,
+                  std::vector<base::string16>(1, kOtherSuggestion),
+                  hash + 1));
+
+  static const base::string16 kMisspelledWord = ASCIIToUTF16("Helllo");
+  const std::set<uint32>& misspellings =
+      feedback_.FindMisspellings(kMisspelledWord);
+  EXPECT_EQ(static_cast<size_t>(kNumberOfSentences * kNumberOfRenderers),
+            misspellings.size());
+
+  for (std::set<uint32>::const_iterator it = misspellings.begin();
+       it != misspellings.end();
+      ++it) {
+    Misspelling* misspelling = feedback_.GetMisspelling(*it);
+    EXPECT_NE(static_cast<Misspelling*>(NULL), misspelling);
+    EXPECT_TRUE(misspelling->hash >= kMisspellingHash &&
+                misspelling->hash <= hash);
+    EXPECT_EQ(kMisspelledWord, misspelling->GetMisspelledString());
+  }
+}
+
+// Should not be able to find misspellings by misspelled word after they have
+// been removed.
+TEST_F(FeedbackTest, CannotFindMisspellingsByTextAfterErased) {
+  static const base::string16 kMisspelledText = ASCIIToUTF16("Helllo world");
+  static const base::string16 kMisspelledWord = ASCIIToUTF16("Helllo");
+  static const base::string16 kSuggestion = ASCIIToUTF16("Hello");
+  static const int kMisspellingStart = 0;
+  static const int kMisspellingLength = 6;
+  feedback_.AddMisspelling(
+      kRendererProcessId,
+      Misspelling(kMisspelledText,
+                  kMisspellingStart,
+                  kMisspellingLength,
+                  std::vector<base::string16>(1, kSuggestion),
+                  kMisspellingHash));
+  feedback_.GetMisspelling(kMisspellingHash)->action.Finalize();
+  feedback_.EraseFinalizedMisspellings(kRendererProcessId);
+  EXPECT_TRUE(feedback_.FindMisspellings(kMisspelledWord).empty());
+}
+
+}  // namespace spellcheck
diff --git a/chrome/browser/spellchecker/misspelling.cc b/chrome/browser/spellchecker/misspelling.cc
new file mode 100644
index 0000000..e92a6f4
--- /dev/null
+++ b/chrome/browser/spellchecker/misspelling.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 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.
+//
+// The |Misspelling| object stores the misspelling, a spellcheck suggestion for
+// it, and user's action on it. The misspelling is stored as |context|,
+// |location|, and |length| instead of only misspelled text, because the
+// spellcheck algorithm uses the context.
+
+#include "chrome/browser/spellchecker/misspelling.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+
+namespace {
+
+// Builds a value from a list of spellcheck suggestions. The caller owns the
+// result.
+base::Value* BuildSuggestionsValue(const std::vector<base::string16>& list) {
+  base::ListValue* result = new base::ListValue;
+  result->AppendStrings(list);
+  return result;
+}
+
+// Builds a value from a spellcheck action. The caller owns the result.
+base::Value* BuildUserActionValue(const SpellcheckAction& action) {
+  base::ListValue* result = new base::ListValue;
+  result->Append(action.Serialize());
+  return result;
+}
+
+}  // namespace
+
+Misspelling::Misspelling()
+    : location(0), length(0), hash(0), timestamp(base::Time::Now()) {
+}
+
+Misspelling::Misspelling(const base::string16& context,
+                         size_t location,
+                         size_t length,
+                         const std::vector<base::string16>& suggestions,
+                         uint32 hash)
+    : context(context),
+      location(location),
+      length(length),
+      suggestions(suggestions),
+      hash(hash),
+      timestamp(base::Time::Now()) {
+}
+
+Misspelling::~Misspelling() {
+}
+
+base::DictionaryValue* Misspelling::Serialize() const {
+  base::DictionaryValue* result = new base::DictionaryValue;
+  result->SetString(
+      "timestamp",
+      base::Int64ToString(static_cast<long>(timestamp.ToJsTime())));
+  result->SetInteger("misspelledLength", length);
+  result->SetInteger("misspelledStart", location);
+  result->SetString("originalText", context);
+  result->SetString("suggestionId", base::UintToString(hash));
+  result->Set("suggestions", BuildSuggestionsValue(suggestions));
+  result->Set("userActions", BuildUserActionValue(action));
+  return result;
+}
+
+base::string16 Misspelling::GetMisspelledString() const {
+  // Feedback sender does not create Misspelling objects for spellcheck results
+  // that are out-of-bounds of checked text length.
+  if (location > context.length())
+    return base::string16();
+  return context.substr(location, length);
+}
diff --git a/chrome/browser/spellchecker/misspelling.h b/chrome/browser/spellchecker/misspelling.h
new file mode 100644
index 0000000..7db83f7
--- /dev/null
+++ b/chrome/browser/spellchecker/misspelling.h
@@ -0,0 +1,72 @@
+// Copyright (c) 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.
+//
+// An object to store user feedback to a single spellcheck suggestion.
+//
+// Stores the spellcheck suggestion, its uint32 hash identifier, and user's
+// feedback. The feedback is indirect, in the sense that we record user's
+// |action| instead of asking them how they feel about a spellcheck suggestion.
+// The object can serialize itself.
+
+#ifndef CHROME_BROWSER_SPELLCHECKER_MISSPELLING_H_
+#define CHROME_BROWSER_SPELLCHECKER_MISSPELLING_H_
+
+#include <vector>
+
+#include "base/time/time.h"
+#include "chrome/browser/spellchecker/spellcheck_action.h"
+
+// Stores user feedback to a spellcheck suggestion. Sample usage:
+//    Misspelling misspelling.
+//    misspelling.context = base::ASCIIToUTF16("Helllo world");
+//    misspelling.location = 0;
+//    misspelling.length = 6;
+//    misspelling.suggestions =
+//        std::vector<base::string16>(1, base::ASCIIToUTF16("Hello"));
+//    misspelling.hash = GenerateRandomHash();
+//    misspelling.action.type = SpellcheckAction::TYPE_SELECT;
+//    misspelling.action.index = 0;
+//    Process(misspelling.Serialize());
+class Misspelling {
+ public:
+  Misspelling();
+  Misspelling(const base::string16& context,
+              size_t location,
+              size_t length,
+              const std::vector<base::string16>& suggestions,
+              uint32 hash);
+  ~Misspelling();
+
+  // Serializes the data in this object into a dictionary value. The caller owns
+  // the result.
+  base::DictionaryValue* Serialize() const;
+
+  // Returns the substring of |context| that begins at |location| and contains
+  // |length| characters.
+  base::string16 GetMisspelledString() const;
+
+  // A several-word text snippet that immediately surrounds the misspelling.
+  base::string16 context;
+
+  // The number of characters between the beginning of |context| and the first
+  // misspelled character.
+  size_t location;
+
+  // The number of characters in the misspelling.
+  size_t length;
+
+  // Spelling suggestions.
+  std::vector<base::string16> suggestions;
+
+  // The hash that identifies the misspelling.
+  uint32 hash;
+
+  // User action.
+  SpellcheckAction action;
+
+  // The time when the user applied the action.
+  base::Time timestamp;
+};
+
+#endif  // CHROME_BROWSER_SPELLCHECKER_MISSPELLING_H_
diff --git a/chrome/browser/spellchecker/misspelling_unittest.cc b/chrome/browser/spellchecker/misspelling_unittest.cc
new file mode 100644
index 0000000..bfc1628
--- /dev/null
+++ b/chrome/browser/spellchecker/misspelling_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 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.
+//
+// Unit tests for |Misspelling| object.
+
+#include "chrome/browser/spellchecker/misspelling.h"
+
+#include "base/json/json_reader.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(MisspellingTest, SerializeTest) {
+  Misspelling misspelling;
+  misspelling.context = base::ASCIIToUTF16("How doe sit know");
+  misspelling.location = 4;
+  misspelling.length = 7;
+  misspelling.timestamp = base::Time::FromJsTime(42);
+  misspelling.hash = 9001;
+  misspelling.suggestions.push_back(base::ASCIIToUTF16("does it"));
+
+  scoped_ptr<base::Value> expected(base::JSONReader::Read(
+      "{\"originalText\": \"How doe sit know\","
+      "\"misspelledStart\": 4,"
+      "\"misspelledLength\": 7,"
+      "\"timestamp\": \"42\","
+      "\"suggestionId\":\"9001\","
+      "\"suggestions\": [\"does it\"],"
+      "\"userActions\": [{\"actionType\": \"PENDING\"}]}"));
+
+  scoped_ptr<base::DictionaryValue> serialized(misspelling.Serialize());
+  EXPECT_TRUE(serialized->Equals(expected.get()));
+}
+
+TEST(MisspellingTest, GetMisspelledStringTest) {
+  Misspelling misspelling;
+  misspelling.context = base::ASCIIToUTF16("How doe sit know");
+  misspelling.location = 4;
+  misspelling.length = 7;
+  EXPECT_EQ(base::ASCIIToUTF16("doe sit"), misspelling.GetMisspelledString());
+
+  misspelling.length = 0;
+  EXPECT_EQ(base::string16(), misspelling.GetMisspelledString());
+
+  misspelling.location = misspelling.context.length();
+  misspelling.length = 7;
+  EXPECT_EQ(base::string16(), misspelling.GetMisspelledString());
+
+  misspelling.location = misspelling.context.length() + 1;
+  EXPECT_EQ(base::string16(), misspelling.GetMisspelledString());
+}
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter.cc b/chrome/browser/spellchecker/spellcheck_message_filter.cc
index cc5f68e..30f7d5b 100644
--- a/chrome/browser/spellchecker/spellcheck_message_filter.cc
+++ b/chrome/browser/spellchecker/spellcheck_message_filter.cc
@@ -33,7 +33,8 @@
   // The message filter overrides the thread for these messages because they
   // access spellcheck data.
   if (message.type() == SpellCheckHostMsg_RequestDictionary::ID ||
-      message.type() == SpellCheckHostMsg_NotifyChecked::ID)
+      message.type() == SpellCheckHostMsg_NotifyChecked::ID ||
+      message.type() == SpellCheckHostMsg_RespondDocumentMarkers::ID)
     *thread = BrowserThread::UI;
 #if !defined(OS_MACOSX)
   if (message.type() == SpellCheckHostMsg_CallSpellingService::ID)
@@ -48,6 +49,8 @@
                         OnSpellCheckerRequestDictionary)
     IPC_MESSAGE_HANDLER(SpellCheckHostMsg_NotifyChecked,
                         OnNotifyChecked)
+    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RespondDocumentMarkers,
+                        OnRespondDocumentMarkers)
 #if !defined(OS_MACOSX)
     IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CallSpellingService,
                         OnCallSpellingService)
@@ -91,6 +94,17 @@
     spellcheck->GetMetrics()->RecordCheckedWordStats(word, misspelled);
 }
 
+void SpellCheckMessageFilter::OnRespondDocumentMarkers(
+    const std::vector<uint32>& markers) {
+  SpellcheckService* spellcheck = GetSpellcheckService();
+  // Spellcheck service may not be available for a renderer process that is
+  // shutting down.
+  if (!spellcheck)
+    return;
+  spellcheck->GetFeedbackSender()->OnReceiveDocumentMarkers(
+      render_process_id_, markers);
+}
+
 #if !defined(OS_MACOSX)
 void SpellCheckMessageFilter::OnCallSpellingService(
     int route_id,
@@ -122,16 +136,21 @@
   if (!spellcheck)
     return;
   std::vector<SpellCheckResult> results_copy = results;
+  spellcheck->GetFeedbackSender()->OnSpellcheckResults(
+      render_process_id_, text, markers, &results_copy);
 
-  // Erase custom dictionary words from the spellcheck results.
+  // Erase custom dictionary words from the spellcheck results and record
+  // in-dictionary feedback.
   std::vector<SpellCheckResult>::iterator write_iter;
   std::vector<SpellCheckResult>::iterator iter;
   std::string text_copy = base::UTF16ToUTF8(text);
   for (iter = write_iter = results_copy.begin();
        iter != results_copy.end();
        ++iter) {
-    if (!spellcheck->GetCustomDictionary()->HasWord(
+    if (spellcheck->GetCustomDictionary()->HasWord(
             text_copy.substr(iter->location, iter->length))) {
+      spellcheck->GetFeedbackSender()->RecordInDictionary(iter->hash);
+    } else {
       if (write_iter != iter)
         *write_iter = *iter;
       ++write_iter;
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter.h b/chrome/browser/spellchecker/spellcheck_message_filter.h
index 2f47f40..dbc8f9cc 100644
--- a/chrome/browser/spellchecker/spellcheck_message_filter.h
+++ b/chrome/browser/spellchecker/spellcheck_message_filter.h
@@ -32,6 +32,7 @@
 
   void OnSpellCheckerRequestDictionary();
   void OnNotifyChecked(const base::string16& word, bool misspelled);
+  void OnRespondDocumentMarkers(const std::vector<uint32>& markers);
 #if !defined(OS_MACOSX)
   void OnCallSpellingService(int route_id,
                              int identifier,
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc b/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc
index 9228cd3..0689342 100644
--- a/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc
+++ b/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc
@@ -166,6 +166,17 @@
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   remote_success_ = success;
   remote_results_ = results;
+
+  SpellcheckService* spellcheck_service =
+      SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
+  if (spellcheck_service) {
+    spellcheck_service->GetFeedbackSender()->OnSpellcheckResults(
+        render_process_id_,
+        text,
+        markers_,
+        &remote_results_);
+  }
+
   completion_barrier_.Run();
 }
 
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc b/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
index 3da4afd1..0e98efe 100644
--- a/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
+++ b/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
@@ -56,6 +56,7 @@
   static const uint32 kSpellcheckMessages[] = {
     SpellCheckHostMsg_RequestDictionary::ID,
     SpellCheckHostMsg_NotifyChecked::ID,
+    SpellCheckHostMsg_RespondDocumentMarkers::ID,
 #if !defined(OS_MACOSX)
     SpellCheckHostMsg_CallSpellingService::ID,
 #endif
diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index 3705ddfb..ffc2273 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -41,6 +41,15 @@
   PrefService* prefs = user_prefs::UserPrefs::Get(context);
   pref_change_registrar_.Init(prefs);
 
+  std::string language_code;
+  std::string country_code;
+  chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
+      prefs->GetString(prefs::kSpellCheckDictionary),
+      &language_code,
+      &country_code);
+  feedback_sender_.reset(new spellcheck::FeedbackSender(
+      context->GetRequestContext(), language_code, country_code));
+
   pref_change_registrar_.Add(
       prefs::kEnableAutoSpellCorrect,
       base::Bind(&SpellcheckService::OnEnableAutoSpellCorrectChanged,
@@ -187,6 +196,10 @@
   return hunspell_dictionary_.get();
 }
 
+spellcheck::FeedbackSender* SpellcheckService::GetFeedbackSender() {
+  return feedback_sender_.get();
+}
+
 bool SpellcheckService::LoadExternalDictionary(std::string language,
                                                std::string locale,
                                                std::string path,
@@ -282,6 +295,12 @@
       dictionary, context_->GetRequestContext(), this));
   hunspell_dictionary_->AddObserver(this);
   hunspell_dictionary_->Load();
+  std::string language_code;
+  std::string country_code;
+  chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
+      dictionary, &language_code, &country_code);
+  feedback_sender_->OnLanguageCountryChange(language_code, country_code);
+  UpdateFeedbackSenderState();
 }
 
 void SpellcheckService::OnUseSpellingServiceChanged() {
@@ -289,4 +308,14 @@
       prefs::kSpellCheckUseSpellingService);
   if (metrics_)
     metrics_->RecordSpellingServiceStats(enabled);
+  UpdateFeedbackSenderState();
+}
+
+void SpellcheckService::UpdateFeedbackSenderState() {
+  if (SpellingServiceClient::IsAvailable(
+          context_, SpellingServiceClient::SPELLCHECK)) {
+    feedback_sender_->StartFeedbackCollection();
+  } else {
+    feedback_sender_->StopFeedbackCollection();
+  }
 }
diff --git a/chrome/browser/spellchecker/spellcheck_service.h b/chrome/browser/spellchecker/spellcheck_service.h
index e310eb7..3fd004a 100644
--- a/chrome/browser/spellchecker/spellcheck_service.h
+++ b/chrome/browser/spellchecker/spellcheck_service.h
@@ -10,6 +10,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/prefs/pref_change_registrar.h"
+#include "chrome/browser/spellchecker/feedback_sender.h"
 #include "chrome/browser/spellchecker/spellcheck_custom_dictionary.h"
 #include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h"
 #include "chrome/common/spellcheck_common.h"
@@ -95,6 +96,9 @@
   // Returns the instance of the Hunspell dictionary.
   SpellcheckHunspellDictionary* GetHunspellDictionary();
 
+  // Returns the instance of the spelling service feedback sender.
+  spellcheck::FeedbackSender* GetFeedbackSender();
+
   // Load a dictionary from a given path. Format specifies how the dictionary
   // is stored. Return value is true if successful.
   bool LoadExternalDictionary(std::string language,
@@ -145,6 +149,10 @@
   // Notification handler for changes to prefs::kSpellCheckUseSpellingService.
   void OnUseSpellingServiceChanged();
 
+  // Enables the feedback sender if spelling server is available and enabled.
+  // Otherwise disables the feedback sender.
+  void UpdateFeedbackSenderState();
+
   PrefChangeRegistrar pref_change_registrar_;
   content::NotificationRegistrar registrar_;
 
@@ -157,6 +165,8 @@
 
   scoped_ptr<SpellcheckHunspellDictionary> hunspell_dictionary_;
 
+  scoped_ptr<spellcheck::FeedbackSender> feedback_sender_;
+
   base::WeakPtrFactory<SpellcheckService> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SpellcheckService);
diff --git a/chrome/browser/spellchecker/word_trimmer.cc b/chrome/browser/spellchecker/word_trimmer.cc
new file mode 100644
index 0000000..2465175
--- /dev/null
+++ b/chrome/browser/spellchecker/word_trimmer.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 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/spellchecker/word_trimmer.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/i18n/break_iterator.h"
+
+base::string16 TrimWords(size_t* start,
+                         size_t end,
+                         const base::string16& text,
+                         size_t keep) {
+  if (*start > text.length() || *start > end)
+    return text;
+  base::i18n::BreakIterator iter(text, base::i18n::BreakIterator::BREAK_WORD);
+  if (!iter.Init())
+    return text;
+  // A circular buffer of the last |keep + 1| words seen before position |start|
+  // in |text|.
+  std::vector<size_t> word_offset(keep + 1, 0);
+  size_t first = std::string::npos;
+  size_t last = std::string::npos;
+  while (iter.Advance()) {
+    if (iter.IsWord()) {
+      word_offset[keep] = iter.prev();
+      if ((*start >= iter.prev() && *start < iter.pos()) ||
+          (end > iter.prev() && end <= iter.pos())) {
+        if (first == std::string::npos)
+          first = word_offset[0];
+        last = iter.pos();
+      }
+      if (first == std::string::npos) {
+        std::rotate(word_offset.begin(),
+                    word_offset.begin() + 1,
+                    word_offset.end());
+      }
+      if (iter.prev() > end && keep) {
+        last = iter.pos();
+        keep--;
+      }
+    }
+  }
+  if (first == std::string::npos)
+    return text;
+  *start -= first;
+  return text.substr(first, last - first);
+}
diff --git a/chrome/browser/spellchecker/word_trimmer.h b/chrome/browser/spellchecker/word_trimmer.h
new file mode 100644
index 0000000..5b108d3
--- /dev/null
+++ b/chrome/browser/spellchecker/word_trimmer.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 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_SPELLCHECKER_WORD_TRIMMER_H_
+#define CHROME_BROWSER_SPELLCHECKER_WORD_TRIMMER_H_
+
+#include "base/strings/string16.h"
+
+// Trims |text| to contain only the range from |start| to |end| and |keep| words
+// on either side of the range. The |start| and |end| parameters are character
+// indexes into |text|. The |keep| parameter is the number of words to keep on
+// either side of the |start|-|end| range. The function updates |start| in
+// accordance with the trimming.
+//
+// Example:
+//
+//  size_t start = 14;
+//  size_t end = 23;
+//  base::string16 text =
+//      base::ASCIIToUTF16("one two three four five six seven eight");
+//  int keep = 2;
+//  base::string16 trimmed = TrimWords(&start, end, text, keep);
+//  DCHECK(trimmed == base::ASCIIToUTF16("two three four five six seven"));
+//  DCHECK(start == 10);
+//
+base::string16 TrimWords(size_t* start,
+                         size_t end,
+                         const base::string16& text,
+                         size_t keep);
+
+#endif  // CHROME_BROWSER_SPELLCHECKER_WORD_TRIMMER_H_
diff --git a/chrome/browser/spellchecker/word_trimmer_unittest.cc b/chrome/browser/spellchecker/word_trimmer_unittest.cc
new file mode 100644
index 0000000..35b8e6a6
--- /dev/null
+++ b/chrome/browser/spellchecker/word_trimmer_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2011 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/spellchecker/word_trimmer.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::ASCIIToUTF16;
+
+TEST(WordTrimmerTest, TrimWordsEmptyText) {
+  size_t start = 0;
+  size_t end = 0;
+  EXPECT_EQ(base::string16(), TrimWords(&start, end, base::string16(), 0));
+  EXPECT_EQ(0UL, start);
+}
+
+TEST(WordTrimmerTest, TrimWordsStart) {
+  size_t start = 0;
+  size_t end = 3;
+  EXPECT_EQ(ASCIIToUTF16("one two three"),
+            TrimWords(&start, end, ASCIIToUTF16("one two three four"), 2));
+  EXPECT_EQ(0UL, start);
+}
+
+TEST(WordTrimmerTest, TrimWordsEnd) {
+  size_t start = 14;
+  size_t end = 18;
+  EXPECT_EQ(ASCIIToUTF16("two three four"),
+            TrimWords(&start, end, ASCIIToUTF16("one two three four"), 2));
+  EXPECT_EQ(10UL, start);
+}
+
+TEST(WordTrimmerTest, TrimWordsMiddle) {
+  size_t start = 14;
+  size_t end = 23;
+  EXPECT_EQ(ASCIIToUTF16("two three four five six seven"), TrimWords(
+      &start, end, ASCIIToUTF16("one two three four five six seven eight"), 2));
+  EXPECT_EQ(10UL, start);
+}
+
+TEST(WordTrimmerTest, TrimWordsEmptyKeep) {
+  size_t start = 18;
+  size_t end = 18;
+  EXPECT_EQ(ASCIIToUTF16("two three four five six"), TrimWords(
+      &start, end, ASCIIToUTF16("one two three four five six seven eight"), 2));
+  EXPECT_EQ(14UL, start);
+}
+
+TEST(WordTrimmerTest, TrimWordsOutOfBounds) {
+  size_t start = 4;
+  size_t end = 5;
+  EXPECT_EQ(ASCIIToUTF16("one"), TrimWords(
+      &start, end, ASCIIToUTF16("one"), 2));
+  EXPECT_EQ(4UL, start);
+}
+
+TEST(WordTrimmerTest, TrimWordsInvalid) {
+  size_t start = 23;
+  size_t end = 14;
+  EXPECT_EQ(ASCIIToUTF16("one two three four five six seven eight"), TrimWords(
+      &start, end, ASCIIToUTF16("one two three four five six seven eight"), 2));
+  EXPECT_EQ(23UL, start);
+}
diff --git a/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc b/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc
index ec7402c..00bc1a9 100644
--- a/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc
+++ b/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/stringprintf.h"
+#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -23,6 +24,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+using chrome_browser_interstitials::IsInterstitialDisplayingText;
+using chrome_browser_interstitials::SecurityInterstitialIDNTest;
+
 namespace {
 // Partial text in the captive portal interstitial's main paragraph when the
 // login domain isn't displayed.
@@ -30,23 +34,6 @@
 const char kBrokenSSL[] = "https://broken.ssl";
 const char kWiFiSSID[] = "WiFiSSID";
 
-// Returns true if the interstitial contains |text| in its body.
-bool IsInterstitialDisplayingText(content::InterstitialPage* interstitial,
-                                  const std::string& text) {
-  // It's valid for |text| to contain "\'", but simply look for "'" instead
-  // since this function is used for searching host names and a predefined
-  // string.
-  DCHECK(text.find("\'") == std::string::npos);
-  std::string command = base::StringPrintf(
-      "var hasText = document.body.textContent.indexOf('%s') >= 0;"
-      "window.domAutomationController.send(hasText ? 1 : 0);",
-      text.c_str());
-  int result = 0;
-  EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
-      interstitial->GetMainFrame(), command, &result));
-  return result == 1;
-}
-
 enum ExpectWiFi {
   EXPECT_WIFI_NO,
   EXPECT_WIFI_YES
@@ -220,29 +207,25 @@
                    EXPECT_WIFI_YES, EXPECT_WIFI_SSID_NO, EXPECT_LOGIN_URL_NO);
 }
 
-class CaptivePortalBlockingPageIDNTest : public CaptivePortalBlockingPageTest {
-public:
-  // InProcessBrowserTest:
-  void SetUpOnMainThread() override {
-    // Clear AcceptLanguages to force punycode decoding.
-    browser()->profile()->GetPrefs()->SetString(prefs::kAcceptLanguages,
-                                                std::string());
+class CaptivePortalBlockingPageIDNTest : public SecurityInterstitialIDNTest {
+ protected:
+  // SecurityInterstitialIDNTest implementation
+  SecurityInterstitialPage* CreateInterstitial(
+      content::WebContents* contents,
+      const GURL& request_url) const override {
+    // Delegate is owned by the blocking page.
+    FakeConnectionInfoDelegate* delegate =
+        new FakeConnectionInfoDelegate(false, "");
+    // Blocking page is owned by the interstitial.
+    CaptivePortalBlockingPage* blocking_page = new CaptivePortalBlockingPage(
+        contents, GURL(kBrokenSSL), request_url, base::Callback<void(bool)>());
+    blocking_page->SetDelegateForTesting(delegate);
+    return blocking_page;
   }
 };
 
-// Same as CaptivePortalBlockingPageTest.WiredNetwork_LoginURL, except the login
-// domain is an IDN.
+// Test that an IDN login domain is decoded properly.
 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageIDNTest,
                        ShowLoginIDNIfPortalRedirectsDetectionURL) {
-  const char kHostname[] =
-      "xn--d1abbgf6aiiy.xn--p1ai";
-  const char kHostnameJSUnicode[] =
-      "\\u043f\\u0440\\u0435\\u0437\\u0438\\u0434\\u0435\\u043d\\u0442."
-      "\\u0440\\u0444";
-  std::string landing_url_spec =
-      base::StringPrintf("http://%s/landing_url", kHostname);
-  GURL landing_url(landing_url_spec);
-
-  TestInterstitial(false, "", landing_url, EXPECT_WIFI_NO, EXPECT_WIFI_SSID_NO,
-                   EXPECT_LOGIN_URL_YES, kHostnameJSUnicode);
+  EXPECT_TRUE(VerifyIDNDecoded());
 }
diff --git a/chrome/browser/ssl/ssl_blocking_page.cc b/chrome/browser/ssl/ssl_blocking_page.cc
index 64079cf..e6083f8 100644
--- a/chrome/browser/ssl/ssl_blocking_page.cc
+++ b/chrome/browser/ssl/ssl_blocking_page.cc
@@ -4,12 +4,16 @@
 
 #include "chrome/browser/ssl/ssl_blocking_page.h"
 
+#include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/build_time.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/i18n/rtl.h"
 #include "base/i18n/time_formatting.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram.h"
+#include "base/prefs/pref_service.h"
 #include "base/process/launch.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
@@ -20,11 +24,14 @@
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/interstitials/security_interstitial_metrics_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_preferences_util.h"
+#include "chrome/browser/safe_browsing/ui_manager.h"
 #include "chrome/browser/ssl/ssl_error_classification.h"
 #include "chrome/browser/ssl/ssl_error_info.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/google/core/browser/google_util.h"
@@ -221,13 +228,15 @@
 
 // Note that we always create a navigation entry with SSL errors.
 // No error happening loading a sub-resource triggers an interstitial so far.
-SSLBlockingPage::SSLBlockingPage(content::WebContents* web_contents,
-                                 int cert_error,
-                                 const net::SSLInfo& ssl_info,
-                                 const GURL& request_url,
-                                 int options_mask,
-                                 const base::Time& time_triggered,
-                                 const base::Callback<void(bool)>& callback)
+SSLBlockingPage::SSLBlockingPage(
+    content::WebContents* web_contents,
+    int cert_error,
+    const net::SSLInfo& ssl_info,
+    const GURL& request_url,
+    int options_mask,
+    const base::Time& time_triggered,
+    SafeBrowsingUIManager* safe_browsing_ui_manager,
+    const base::Callback<void(bool)>& callback)
     : SecurityInterstitialPage(web_contents, request_url),
       callback_(callback),
       cert_error_(cert_error),
@@ -237,7 +246,8 @@
       strict_enforcement_((options_mask & STRICT_ENFORCEMENT) != 0),
       expired_but_previously_allowed_(
           (options_mask & EXPIRED_BUT_PREVIOUSLY_ALLOWED) != 0),
-      time_triggered_(time_triggered) {
+      time_triggered_(time_triggered),
+      safe_browsing_ui_manager_(safe_browsing_ui_manager) {
   interstitial_reason_ =
       IsErrorDueToBadClock(time_triggered_, cert_error_) ?
       SSL_REASON_BAD_CLOCK : SSL_REASON_SSL;
@@ -245,15 +255,15 @@
   // We collapse the Rappor metric name to just "ssl" so we don't leak
   // the "overridable" bit.  We skip Rappor altogether for bad clocks.
   // This must be done after calculating |interstitial_reason_| above.
-  metrics_helper_.reset(new SecurityInterstitialMetricsHelper(
+  set_metrics_helper(new SecurityInterstitialMetricsHelper(
       web_contents, request_url, GetUmaHistogramPrefix(), kSSLRapporPrefix,
       (interstitial_reason_ == SSL_REASON_BAD_CLOCK
            ? SecurityInterstitialMetricsHelper::SKIP_RAPPOR
            : SecurityInterstitialMetricsHelper::REPORT_RAPPOR),
       GetSamplingEventName()));
 
-  metrics_helper_->RecordUserDecision(SecurityInterstitialMetricsHelper::SHOW);
-  metrics_helper_->RecordUserInteraction(
+  metrics_helper()->RecordUserDecision(SecurityInterstitialMetricsHelper::SHOW);
+  metrics_helper()->RecordUserInteraction(
       SecurityInterstitialMetricsHelper::TOTAL_VISITS);
 
   ssl_error_classification_.reset(new SSLErrorClassification(
@@ -285,7 +295,7 @@
   if (!callback_.is_null()) {
     // The page is closed without the user having chosen what to do, default to
     // deny.
-    metrics_helper_->RecordUserDecision(
+    metrics_helper()->RecordUserDecision(
         SecurityInterstitialMetricsHelper::DONT_PROCEED);
     RecordSSLExpirationPageEventState(
         expired_but_previously_allowed_, false, overridable_);
@@ -439,6 +449,34 @@
       &encoded_chain);
   load_time_data->SetString(
       "pem", JoinString(encoded_chain, std::string()));
+
+  PopulateExtendedReportingOption(load_time_data);
+}
+
+void SSLBlockingPage::PopulateExtendedReportingOption(
+    base::DictionaryValue* load_time_data) {
+  // Only show the checkbox if not off-the-record and if the
+  // command-line option is set.
+  const bool show = !web_contents()->GetBrowserContext()->IsOffTheRecord() &&
+                    base::CommandLine::ForCurrentProcess()->HasSwitch(
+                        switches::kEnableInvalidCertCollection);
+
+  load_time_data->SetBoolean(interstitials::kDisplayCheckBox, show);
+  if (!show)
+    return;
+
+  load_time_data->SetBoolean(
+      interstitials::kBoxChecked,
+      IsPrefEnabled(prefs::kSafeBrowsingExtendedReportingEnabled));
+
+  const std::string privacy_link = base::StringPrintf(
+      interstitials::kPrivacyLinkHtml, CMD_OPEN_REPORTING_PRIVACY,
+      l10n_util::GetStringUTF8(IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE).c_str());
+
+  load_time_data->SetString(
+      interstitials::kOptInLink,
+      l10n_util::GetStringFUTF16(IDS_SAFE_BROWSING_MALWARE_REPORTING_AGREE,
+                                 base::UTF8ToUTF16(privacy_link)));
 }
 
 void SSLBlockingPage::OverrideEntry(NavigationEntry* entry) {
@@ -453,9 +491,20 @@
   entry->GetSSL().security_bits = ssl_info_.security_bits;
 }
 
+void SSLBlockingPage::SetCertificateReportCallbackForTesting(
+    const base::Closure& callback) {
+  certificate_report_callback_for_testing_ = callback;
+}
+
 // This handles the commands sent from the interstitial JavaScript.
 // DO NOT reorder or change this logic without also changing the JavaScript!
 void SSLBlockingPage::CommandReceived(const std::string& command) {
+  if (command == "\"pageLoadComplete\"") {
+    // content::WaitForRenderFrameReady sends this message when the page
+    // load completes. Ignore it.
+    return;
+  }
+
   int cmd = 0;
   bool retval = base::StringToInt(command, &cmd);
   DCHECK(retval);
@@ -470,13 +519,21 @@
       }
       break;
     }
+    case CMD_DO_REPORT: {
+      SetReportingPreference(true);
+      break;
+    }
+    case CMD_DONT_REPORT: {
+      SetReportingPreference(false);
+      break;
+    }
     case CMD_SHOW_MORE_SECTION: {
-      metrics_helper_->RecordUserInteraction(
+      metrics_helper()->RecordUserInteraction(
           SecurityInterstitialMetricsHelper::SHOW_ADVANCED);
       break;
     }
     case CMD_OPEN_HELP_CENTER: {
-      metrics_helper_->RecordUserInteraction(
+      metrics_helper()->RecordUserInteraction(
           SecurityInterstitialMetricsHelper::SHOW_LEARN_MORE);
       content::NavigationController::LoadURLParams help_page_params(
           google_util::AppendGoogleLocaleParam(
@@ -485,25 +542,24 @@
       break;
     }
     case CMD_RELOAD: {
-      metrics_helper_->RecordUserInteraction(
+      metrics_helper()->RecordUserInteraction(
           SecurityInterstitialMetricsHelper::RELOAD);
       // The interstitial can't refresh itself.
       web_contents()->GetController().Reload(true);
       break;
     }
     case CMD_OPEN_DATE_SETTINGS: {
-      metrics_helper_->RecordUserInteraction(
+      metrics_helper()->RecordUserInteraction(
           SecurityInterstitialMetricsHelper::OPEN_TIME_SETTINGS);
       content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
                                        base::Bind(&LaunchDateAndTimeSettings));
       break;
     }
+    case CMD_OPEN_REPORTING_PRIVACY:
+      OpenExtendedReportingPrivacyPolicy();
+      break;
     case CMD_OPEN_DIAGNOSTIC:
       // Google doesn't currently have a transparency report for SSL.
-    case CMD_DO_REPORT:
-    case CMD_DONT_REPORT:
-    case CMD_OPEN_REPORTING_PRIVACY:
-      // Chrome doesn't currently do Extended Reporting for SSL.
       NOTREACHED() << "Unexpected command: " << command;
   }
 }
@@ -517,8 +573,13 @@
 }
 
 void SSLBlockingPage::OnProceed() {
-  metrics_helper_->RecordUserDecision(
+  metrics_helper()->RecordUserDecision(
       SecurityInterstitialMetricsHelper::PROCEED);
+
+  // Finish collecting information about invalid certificates, if the
+  // user opted in to.
+  FinishCertCollection();
+
   RecordSSLExpirationPageEventState(
       expired_but_previously_allowed_, true, overridable_);
   // Accepting the certificate resumes the loading of the page.
@@ -526,8 +587,13 @@
 }
 
 void SSLBlockingPage::OnDontProceed() {
-  metrics_helper_->RecordUserDecision(
+  metrics_helper()->RecordUserDecision(
       SecurityInterstitialMetricsHelper::DONT_PROCEED);
+
+  // Finish collecting information about invalid certificates, if the
+  // user opted in to.
+  FinishCertCollection();
+
   RecordSSLExpirationPageEventState(
       expired_but_previously_allowed_, false, overridable_);
   NotifyDenyCertificate();
@@ -575,6 +641,32 @@
   return event_name;
 }
 
+void SSLBlockingPage::FinishCertCollection() {
+  base::ScopedClosureRunner scoped_callback(
+      certificate_report_callback_for_testing_);
+
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableInvalidCertCollection) ||
+      web_contents()->GetBrowserContext()->IsOffTheRecord()) {
+    return;
+  }
+
+  const bool enabled =
+      IsPrefEnabled(prefs::kSafeBrowsingExtendedReportingEnabled);
+
+  if (!enabled)
+    return;
+
+  metrics_helper()->RecordUserInteraction(
+      SecurityInterstitialMetricsHelper::EXTENDED_REPORTING_IS_ENABLED);
+
+  if (certificate_report_callback_for_testing_.is_null())
+    scoped_callback.Reset(base::Bind(&base::DoNothing));
+
+  safe_browsing_ui_manager_->ReportInvalidCertificateChain(
+      request_url().host(), ssl_info_, scoped_callback.Release());
+}
+
 // static
 bool SSLBlockingPage::IsOptionsOverridable(int options_mask) {
   return (options_mask & SSLBlockingPage::OVERRIDABLE) &&
diff --git a/chrome/browser/ssl/ssl_blocking_page.h b/chrome/browser/ssl/ssl_blocking_page.h
index 4b9b7f1..1ce1ad8 100644
--- a/chrome/browser/ssl/ssl_blocking_page.h
+++ b/chrome/browser/ssl/ssl_blocking_page.h
@@ -12,7 +12,6 @@
 #include "base/strings/string16.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "base/time/time.h"
-#include "chrome/browser/interstitials/security_interstitial_metrics_helper.h"
 #include "chrome/browser/interstitials/security_interstitial_page.h"
 #include "net/ssl/ssl_info.h"
 #include "url/gurl.h"
@@ -23,6 +22,7 @@
 }
 #endif
 
+class SafeBrowsingUIManager;
 class SSLErrorClassification;
 
 // This class is responsible for showing/hiding the interstitial page that is
@@ -59,6 +59,7 @@
                   const GURL& request_url,
                   int options_mask,
                   const base::Time& time_triggered,
+                  SafeBrowsingUIManager* safe_browsing_ui_manager,
                   const base::Callback<void(bool)>& callback);
 
   // InterstitialPageDelegate method:
@@ -67,6 +68,10 @@
   // Returns true if |options_mask| refers to an overridable SSL error.
   static bool IsOptionsOverridable(int options_mask);
 
+  // Allows tests to be notified when an invalid cert chain report has
+  // been sent (or not sent).
+  void SetCertificateReportCallbackForTesting(const base::Closure& callback);
+
  protected:
   // InterstitialPageDelegate implementation.
   void CommandReceived(const std::string& command) override;
@@ -80,6 +85,8 @@
   void PopulateInterstitialStrings(
       base::DictionaryValue* load_time_data) override;
 
+  void PopulateExtendedReportingOption(base::DictionaryValue* load_time_data);
+
  private:
   void NotifyDenyCertificate();
   void NotifyAllowCertificate();
@@ -87,6 +94,10 @@
   std::string GetUmaHistogramPrefix() const;
   std::string GetSamplingEventName() const;
 
+  // Send a report about an invalid certificate to the server. Takes
+  // care of calling certificate_report_callback_for_testing_.
+  void FinishCertCollection();
+
   base::Callback<void(bool)> callback_;
 
   const int cert_error_;
@@ -106,11 +117,20 @@
   // expired?
   const bool expired_but_previously_allowed_;
   scoped_ptr<SSLErrorClassification> ssl_error_classification_;
-  scoped_ptr<SecurityInterstitialMetricsHelper> metrics_helper_;
+
   // The time at which the interstitial was triggered. The interstitial
   // calculates all times relative to this.
   const base::Time time_triggered_;
 
+  // For reporting invalid SSL certificates as part of Safe Browsing
+  // Extended Reporting.
+  SafeBrowsingUIManager* safe_browsing_ui_manager_;
+
+  // This callback is run when an extended reporting certificate chain
+  // report has been sent, or when it is decided that it should not be
+  // sent (for example, when in incognito mode).
+  base::Closure certificate_report_callback_for_testing_;
+
   // Which type of interstitial this is.
   enum SSLInterstitialReason {
     SSL_REASON_SSL,
diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc
index 581ebe48..ba69fe7 100644
--- a/chrome/browser/ssl/ssl_browser_tests.cc
+++ b/chrome/browser/ssl/ssl_browser_tests.cc
@@ -4,6 +4,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/callback.h"
 #include "base/command_line.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/string_util.h"
@@ -11,8 +12,13 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
+#include "chrome/browser/net/certificate_error_reporter.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/ping_manager.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/ssl/ssl_blocking_page.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -44,7 +50,10 @@
 #include "net/base/net_errors.h"
 #include "net/base/test_data_directory.h"
 #include "net/cert/cert_status_flags.h"
+#include "net/cert/x509_certificate.h"
+#include "net/ssl/ssl_info.h"
 #include "net/test/spawned_test_server/spawned_test_server.h"
+#include "net/url_request/url_request_context.h"
 
 #if defined(USE_NSS)
 #include "chrome/browser/net/nss_context.h"
@@ -53,6 +62,8 @@
 #endif  // defined(USE_NSS)
 
 using base::ASCIIToUTF16;
+using chrome_browser_interstitials::SecurityInterstitialIDNTest;
+using chrome_browser_net::CertificateErrorReporter;
 using content::InterstitialPage;
 using content::NavigationController;
 using content::NavigationEntry;
@@ -169,6 +180,48 @@
   AuthState::Check(*entry, expected_authentication_state);
 }
 
+namespace CertificateReporting {
+
+enum OptIn { EXTENDED_REPORTING_OPT_IN, EXTENDED_REPORTING_DO_NOT_OPT_IN };
+
+enum Proceed { SSL_INTERSTITIAL_PROCEED, SSL_INTERSTITIAL_DO_NOT_PROCEED };
+
+enum ExpectReport { CERT_REPORT_EXPECTED, CERT_REPORT_NOT_EXPECTED };
+
+// This class is used to test invalid certificate chain reporting when
+// the user opts in to do so on the interstitial.
+class MockReporter : public CertificateErrorReporter {
+ public:
+  explicit MockReporter(net::URLRequestContext* request_context,
+                        const GURL& upload_url,
+                        CookiesPreference cookies_preference)
+      : CertificateErrorReporter(request_context,
+                                 upload_url,
+                                 cookies_preference) {}
+
+  void SendReport(CertificateErrorReporter::ReportType type,
+                  const std::string& hostname,
+                  const net::SSLInfo& ssl_info) override {
+    EXPECT_EQ(CertificateErrorReporter::REPORT_TYPE_EXTENDED_REPORTING, type);
+    latest_hostname_reported_ = hostname;
+  }
+
+  const std::string& latest_hostname_reported() {
+    return latest_hostname_reported_;
+  }
+
+ private:
+  std::string latest_hostname_reported_;
+};
+
+void SetUpMockReporter(SafeBrowsingService* safe_browsing_service,
+                       MockReporter* reporter) {
+  safe_browsing_service->ping_manager()->SetCertificateErrorReporterForTesting(
+      scoped_ptr<CertificateErrorReporter>(reporter));
+}
+
+}  // namespace CertificateReporting
+
 }  // namespace
 
 class SSLUITest : public InProcessBrowserTest {
@@ -187,6 +240,24 @@
                             SSLOptions(SSLOptions::CERT_EXPIRED),
                             net::GetWebSocketTestDataDirectory()) {}
 
+  void SetUpOnMainThread() override {
+    // Set up the mock reporter to track the hostnames that reports get
+    // sent for. The request_context argument is NULL here
+    // because the MockReporter doesn't actually use a
+    // request_context. (In order to pass a real request_context, the
+    // reporter would have to be constructed on the IO thread.)
+    reporter_ = new CertificateReporting::MockReporter(
+        NULL, GURL("http://example.test"),
+        CertificateReporting::MockReporter::DO_NOT_SEND_COOKIES);
+    scoped_refptr<SafeBrowsingService> safe_browsing_service =
+        g_browser_process->safe_browsing_service();
+    ASSERT_TRUE(safe_browsing_service);
+    content::BrowserThread::PostTask(
+        content::BrowserThread::IO, FROM_HERE,
+        base::Bind(CertificateReporting::SetUpMockReporter,
+                   safe_browsing_service, reporter_));
+  }
+
   void SetUpCommandLine(base::CommandLine* command_line) override {
     // Browser will both run and display insecure content.
     command_line->AppendSwitch(switches::kAllowRunningInsecureContent);
@@ -351,6 +422,57 @@
         page_with_unsafe_worker_path);
   }
 
+  // Helper function for testing invalid certificate chain reporting.
+  void TestBrokenHTTPSReporting(
+      CertificateReporting::OptIn opt_in,
+      CertificateReporting::Proceed proceed,
+      CertificateReporting::ExpectReport expect_report,
+      Browser* browser) {
+    ASSERT_TRUE(https_server_expired_.Start());
+
+    // Opt in to sending reports for invalid certificate chains.
+    browser->profile()->GetPrefs()->SetBoolean(
+        prefs::kSafeBrowsingExtendedReportingEnabled,
+        opt_in == CertificateReporting::EXTENDED_REPORTING_OPT_IN);
+
+    ui_test_utils::NavigateToURL(browser, https_server_expired_.GetURL("/"));
+
+    WebContents* tab = browser->tab_strip_model()->GetActiveWebContents();
+    CheckAuthenticationBrokenState(tab, net::CERT_STATUS_DATE_INVALID,
+                                   AuthState::SHOWING_INTERSTITIAL);
+
+    // Set up a callback so that the test is notified when the report
+    // has been sent on the IO thread (or not sent).
+    base::RunLoop report_run_loop;
+    base::Closure report_callback = report_run_loop.QuitClosure();
+    SSLBlockingPage* interstitial_page = static_cast<SSLBlockingPage*>(
+        tab->GetInterstitialPage()->GetDelegateForTesting());
+    interstitial_page->SetCertificateReportCallbackForTesting(report_callback);
+
+    EXPECT_EQ(std::string(), reporter_->latest_hostname_reported());
+
+    // Leave the interstitial (either by proceeding or going back)
+    if (proceed == CertificateReporting::SSL_INTERSTITIAL_PROCEED) {
+      ProceedThroughInterstitial(tab);
+    } else {
+      // Click "Take me back"
+      InterstitialPage* interstitial_page = tab->GetInterstitialPage();
+      ASSERT_TRUE(interstitial_page);
+      interstitial_page->DontProceed();
+    }
+
+    // Wait until the report has been sent on the IO thread.
+    report_run_loop.Run();
+
+    if (expect_report == CertificateReporting::CERT_REPORT_EXPECTED) {
+      // Check that the mock reporter received a request to send a report.
+      EXPECT_EQ(https_server_expired_.GetURL("/").host(),
+                reporter_->latest_hostname_reported());
+    } else {
+      EXPECT_EQ(std::string(), reporter_->latest_hostname_reported());
+    }
+  }
+
   net::SpawnedTestServer https_server_;
   net::SpawnedTestServer https_server_expired_;
   net::SpawnedTestServer https_server_mismatched_;
@@ -358,6 +480,7 @@
 
  private:
   typedef net::SpawnedTestServer::SSLOptions SSLOptions;
+  CertificateReporting::MockReporter* reporter_;
 
   DISALLOW_COPY_AND_ASSIGN(SSLUITest);
 };
@@ -392,6 +515,17 @@
   }
 };
 
+class SSLUITestWithExtendedReporting : public SSLUITest {
+ public:
+  SSLUITestWithExtendedReporting() : SSLUITest() {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // Enable a checkbox on SSL interstitials that allows users to opt
+    // in to reporting invalid certificate chains.
+    command_line->AppendSwitch(switches::kEnableInvalidCertCollection);
+  }
+};
+
 // Visits a regular page over http.
 IN_PROC_BROWSER_TEST_F(SSLUITest, TestHTTP) {
   ASSERT_TRUE(test_server()->Start());
@@ -989,6 +1123,73 @@
                           AuthState::DISPLAYED_INSECURE_CONTENT);
 }
 
+// Test that when the checkbox is checked and the user proceeds through
+// the interstitial, the FraudulentCertificateReporter sees a request to
+// send a report.
+IN_PROC_BROWSER_TEST_F(SSLUITestWithExtendedReporting,
+                       TestBrokenHTTPSProceedWithReporting) {
+  TestBrokenHTTPSReporting(CertificateReporting::EXTENDED_REPORTING_OPT_IN,
+                           CertificateReporting::SSL_INTERSTITIAL_PROCEED,
+                           CertificateReporting::CERT_REPORT_EXPECTED,
+                           browser());
+}
+
+// Test that when the checkbox is checked and the user goes back (does
+// not proceed through the interstitial), the
+// FraudulentCertificateReporter sees a request to send a report.
+IN_PROC_BROWSER_TEST_F(SSLUITestWithExtendedReporting,
+                       TestBrokenHTTPSGoBackWithReporting) {
+  TestBrokenHTTPSReporting(
+      CertificateReporting::EXTENDED_REPORTING_OPT_IN,
+      CertificateReporting::SSL_INTERSTITIAL_DO_NOT_PROCEED,
+      CertificateReporting::CERT_REPORT_EXPECTED, browser());
+}
+
+// Test that when the checkbox is not checked and the user proceeds
+// through the interstitial, the FraudulentCertificateReporter does not
+// see a request to send a report.
+IN_PROC_BROWSER_TEST_F(SSLUITestWithExtendedReporting,
+                       TestBrokenHTTPSProceedWithNoReporting) {
+  TestBrokenHTTPSReporting(
+      CertificateReporting::EXTENDED_REPORTING_DO_NOT_OPT_IN,
+      CertificateReporting::SSL_INTERSTITIAL_PROCEED,
+      CertificateReporting::CERT_REPORT_NOT_EXPECTED, browser());
+}
+
+// Test that when the checkbox is not checked and the user does not proceed
+// through the interstitial, the FraudulentCertificateReporter does not
+// see a request to send a report.
+IN_PROC_BROWSER_TEST_F(SSLUITestWithExtendedReporting,
+                       TestBrokenHTTPSGoBackWithNoReporting) {
+  TestBrokenHTTPSReporting(
+      CertificateReporting::EXTENDED_REPORTING_DO_NOT_OPT_IN,
+      CertificateReporting::SSL_INTERSTITIAL_DO_NOT_PROCEED,
+      CertificateReporting::CERT_REPORT_NOT_EXPECTED, browser());
+}
+
+// Test that when the command-line switch for reporting invalid cert
+// chains is not enabled, reports don't get sent, even if the opt-in
+// preference is set. (i.e. if a user enables invalid cert collection in
+// chrome://flags, checks the box on an interstitial, and then disables
+// the flag in chrome://flags, reports shouldn't be sent on the next
+// interstitial).
+IN_PROC_BROWSER_TEST_F(SSLUITest, TestBrokenHTTPSNoReportingWithoutSwitch) {
+  TestBrokenHTTPSReporting(CertificateReporting::EXTENDED_REPORTING_OPT_IN,
+                           CertificateReporting::SSL_INTERSTITIAL_PROCEED,
+                           CertificateReporting::CERT_REPORT_NOT_EXPECTED,
+                           browser());
+}
+
+// Test that reports don't get sent in incognito mode even if the opt-in
+// preference is set and the command-line switch is enabled.
+IN_PROC_BROWSER_TEST_F(SSLUITestWithExtendedReporting,
+                       TestBrokenHTTPSNoReportingInIncognito) {
+  TestBrokenHTTPSReporting(CertificateReporting::EXTENDED_REPORTING_OPT_IN,
+                           CertificateReporting::SSL_INTERSTITIAL_PROCEED,
+                           CertificateReporting::CERT_REPORT_NOT_EXPECTED,
+                           CreateIncognitoBrowser());
+}
+
 // Visits a page that runs insecure content and tries to suppress the insecure
 // content warnings by randomizing location.hash.
 // Based on http://crbug.com/8706
@@ -1928,6 +2129,25 @@
   EXPECT_TRUE(tab->GetRenderWidgetHostView()->IsShowing());
 }
 
+class SSLBlockingPageIDNTest : public SecurityInterstitialIDNTest {
+ protected:
+  // SecurityInterstitialIDNTest implementation
+  SecurityInterstitialPage* CreateInterstitial(
+      content::WebContents* contents,
+      const GURL& request_url) const override {
+    net::SSLInfo ssl_info;
+    ssl_info.cert = new net::X509Certificate(
+        request_url.host(), "CA", base::Time::Max(), base::Time::Max());
+    return new SSLBlockingPage(
+        contents, net::ERR_CERT_CONTAINS_ERRORS, ssl_info, request_url, 0,
+        base::Time::NowFromSystemTime(), nullptr, base::Callback<void(bool)>());
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(SSLBlockingPageIDNTest, SSLBlockingPageDecodesIDN) {
+  EXPECT_TRUE(VerifyIDNDecoded());
+}
+
 // TODO(jcampan): more tests to do below.
 
 // Visit a page over https that contains a frame with a redirect.
diff --git a/chrome/browser/ssl/ssl_error_handler.cc b/chrome/browser/ssl/ssl_error_handler.cc
index 4667c860..6e0cd4d 100644
--- a/chrome/browser/ssl/ssl_error_handler.cc
+++ b/chrome/browser/ssl/ssl_error_handler.cc
@@ -9,6 +9,7 @@
 #include "base/metrics/histogram.h"
 #include "base/time/time.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/ui_manager.h"
 #include "chrome/browser/ssl/ssl_blocking_page.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
@@ -89,6 +90,7 @@
     const net::SSLInfo& ssl_info,
     const GURL& request_url,
     int options_mask,
+    SafeBrowsingUIManager* safe_browsing_ui_manager,
     const base::Callback<void(bool)>& callback) {
 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
   CaptivePortalTabHelper* captive_portal_tab_helper =
@@ -98,10 +100,10 @@
   }
 #endif
   DCHECK(!FromWebContents(web_contents));
-  web_contents->SetUserData(UserDataKey(),
-                            new SSLErrorHandler(web_contents, cert_error,
-                                                ssl_info, request_url,
-                                                options_mask, callback));
+  web_contents->SetUserData(
+      UserDataKey(),
+      new SSLErrorHandler(web_contents, cert_error, ssl_info, request_url,
+                          options_mask, safe_browsing_ui_manager, callback));
 
   SSLErrorHandler* error_handler =
       SSLErrorHandler::FromWebContents(web_contents);
@@ -121,19 +123,22 @@
   g_timer_started_callback = callback;
 }
 
-SSLErrorHandler::SSLErrorHandler(content::WebContents* web_contents,
-                                 int cert_error,
-                                 const net::SSLInfo& ssl_info,
-                                 const GURL& request_url,
-                                 int options_mask,
-                                 const base::Callback<void(bool)>& callback)
+SSLErrorHandler::SSLErrorHandler(
+    content::WebContents* web_contents,
+    int cert_error,
+    const net::SSLInfo& ssl_info,
+    const GURL& request_url,
+    int options_mask,
+    SafeBrowsingUIManager* safe_browsing_ui_manager,
+    const base::Callback<void(bool)>& callback)
     : content::WebContentsObserver(web_contents),
       web_contents_(web_contents),
       cert_error_(cert_error),
       ssl_info_(ssl_info),
       request_url_(request_url),
       options_mask_(options_mask),
-      callback_(callback) {
+      callback_(callback),
+      safe_browsing_ui_manager_(safe_browsing_ui_manager) {
 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
   Profile* profile = Profile::FromBrowserContext(
       web_contents->GetBrowserContext());
@@ -203,7 +208,7 @@
             SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE);
   (new SSLBlockingPage(web_contents_, cert_error_, ssl_info_, request_url_,
                        options_mask_, base::Time::NowFromSystemTime(),
-                       callback_))->Show();
+                       safe_browsing_ui_manager_, callback_))->Show();
   // Once an interstitial is displayed, no need to keep the handler around.
   // This is the equivalent of "delete this".
   web_contents_->RemoveUserData(UserDataKey());
diff --git a/chrome/browser/ssl/ssl_error_handler.h b/chrome/browser/ssl/ssl_error_handler.h
index 4679f4a..c069b47 100644
--- a/chrome/browser/ssl/ssl_error_handler.h
+++ b/chrome/browser/ssl/ssl_error_handler.h
@@ -23,6 +23,8 @@
 class WebContents;
 }
 
+class SafeBrowsingUIManager;
+
 // This class is responsible for deciding whether to show an SSL warning or a
 // captive portal error page. It makes this decision by delaying the display of
 // SSL interstitial for a few seconds (2 by default), and waiting for a captive
@@ -51,6 +53,7 @@
                              const net::SSLInfo& ssl_info,
                              const GURL& request_url,
                              int options_mask,
+                             SafeBrowsingUIManager* safe_browsing_ui_manager,
                              const base::Callback<void(bool)>& callback);
 
   static void SetInterstitialDelayTypeForTest(InterstitialDelayType delay);
@@ -65,6 +68,7 @@
                   const net::SSLInfo& ssl_info,
                   const GURL& request_url,
                   int options_mask,
+                  SafeBrowsingUIManager* safe_browsing_ui_manager,
                   const base::Callback<void(bool)>& callback);
 
   ~SSLErrorHandler() override;
@@ -108,6 +112,8 @@
   content::NotificationRegistrar registrar_;
   base::OneShotTimer<SSLErrorHandler> timer_;
 
+  SafeBrowsingUIManager* safe_browsing_ui_manager_;
+
   DISALLOW_COPY_AND_ASSIGN(SSLErrorHandler);
 };
 
diff --git a/chrome/browser/ssl/ssl_error_handler_unittest.cc b/chrome/browser/ssl/ssl_error_handler_unittest.cc
index 96ef738c..e93908e 100644
--- a/chrome/browser/ssl/ssl_error_handler_unittest.cc
+++ b/chrome/browser/ssl/ssl_error_handler_unittest.cc
@@ -29,12 +29,12 @@
                         ssl_info,
                         GURL(),
                         0,
+                        nullptr,
                         base::Callback<void(bool)>()),
         profile_(profile),
         captive_portal_checked_(false),
         ssl_interstitial_shown_(false),
-        captive_portal_interstitial_shown_(false) {
-  }
+        captive_portal_interstitial_shown_(false) {}
 
   ~TestSSLErrorHandler() override {
   }
diff --git a/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc b/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc
index 668fe4a..f6dc8b8 100644
--- a/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc
+++ b/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc
@@ -117,9 +117,9 @@
 }
 
 void PermissionRequestCreatorApiary::CreateExtensionUpdateRequest(
-    const std::string& extension_id,
+    const std::string& id,
     const SuccessCallback& callback) {
-  CreateRequest(kNamespaceUpdateRequest, extension_id, callback);
+  CreateRequest(kNamespaceUpdateRequest, id, callback);
 }
 
 GURL PermissionRequestCreatorApiary::GetApiUrl() const {
diff --git a/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.h b/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.h
index 9556d68..f232b06 100644
--- a/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.h
+++ b/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.h
@@ -42,7 +42,7 @@
   bool IsEnabled() const override;
   void CreateURLAccessRequest(const GURL& url_requested,
                               const SuccessCallback& callback) override;
-  void CreateExtensionUpdateRequest(const std::string& extension_id,
+  void CreateExtensionUpdateRequest(const std::string& id,
                                     const SuccessCallback& callback) override;
 
   void set_url_fetcher_id_for_testing(int id) { url_fetcher_id_ = id; }
diff --git a/chrome/browser/supervised_user/legacy/permission_request_creator_sync.cc b/chrome/browser/supervised_user/legacy/permission_request_creator_sync.cc
index 0bba1cee..e91d236 100644
--- a/chrome/browser/supervised_user/legacy/permission_request_creator_sync.cc
+++ b/chrome/browser/supervised_user/legacy/permission_request_creator_sync.cc
@@ -57,9 +57,9 @@
 }
 
 void PermissionRequestCreatorSync::CreateExtensionUpdateRequest(
-    const std::string& extension_id,
+    const std::string& id,
     const SuccessCallback& callback) {
-  CreateRequest(kSupervisedUserUpdateRequestKeyPrefix, extension_id, callback);
+  CreateRequest(kSupervisedUserUpdateRequestKeyPrefix, id, callback);
 }
 
 void PermissionRequestCreatorSync::CreateRequest(
diff --git a/chrome/browser/supervised_user/legacy/permission_request_creator_sync.h b/chrome/browser/supervised_user/legacy/permission_request_creator_sync.h
index 552dca0..0dcf1c49 100644
--- a/chrome/browser/supervised_user/legacy/permission_request_creator_sync.h
+++ b/chrome/browser/supervised_user/legacy/permission_request_creator_sync.h
@@ -31,7 +31,7 @@
   bool IsEnabled() const override;
   void CreateURLAccessRequest(const GURL& url_requested,
                               const SuccessCallback& callback) override;
-  void CreateExtensionUpdateRequest(const std::string& extension_id,
+  void CreateExtensionUpdateRequest(const std::string& id,
                                     const SuccessCallback& callback) override;
 
  private:
diff --git a/chrome/browser/supervised_user/permission_request_creator.h b/chrome/browser/supervised_user/permission_request_creator.h
index 549f0c9..de31ebd3 100644
--- a/chrome/browser/supervised_user/permission_request_creator.h
+++ b/chrome/browser/supervised_user/permission_request_creator.h
@@ -28,10 +28,10 @@
   virtual void CreateURLAccessRequest(const GURL& url_requested,
                                       const SuccessCallback& callback) = 0;
 
-  // Creates a request to re-enable the extension with the given |extension_id|,
-  // which was disabled due to a permission increase.
+  // Creates a request to re-enable the extension with the given |id| (composed
+  // of extension_id:version), which was disabled due to a permission increase.
   virtual void CreateExtensionUpdateRequest(
-      const std::string& extension_id,
+      const std::string& id,
       const SuccessCallback& callback) = 0;
 };
 
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index 102b9f5..d99f1da 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -9,6 +9,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/version.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
 #include "chrome/browser/profiles/profile.h"
@@ -86,10 +87,10 @@
 }
 
 void CreateExtensionUpdateRequest(
-    const std::string& extension_id,
+    const std::string& id,
     PermissionRequestCreator* creator,
     const SupervisedUserService::SuccessCallback& callback) {
-  creator->CreateExtensionUpdateRequest(extension_id, callback);
+  creator->CreateExtensionUpdateRequest(id, callback);
 }
 
 #if defined(ENABLE_EXTENSIONS)
@@ -657,9 +658,11 @@
 
 void SupervisedUserService::AddExtensionUpdateRequest(
     const std::string& extension_id,
+    const base::Version& version,
     const SuccessCallback& callback) {
+  std::string id = extension_id + ":" + version.GetString();
   AddPermissionRequestInternal(
-      base::Bind(CreateExtensionUpdateRequest, extension_id),
+      base::Bind(CreateExtensionUpdateRequest, id),
       callback, 0);
 }
 
diff --git a/chrome/browser/supervised_user/supervised_user_service.h b/chrome/browser/supervised_user/supervised_user_service.h
index 67573528..08ada06 100644
--- a/chrome/browser/supervised_user/supervised_user_service.h
+++ b/chrome/browser/supervised_user/supervised_user_service.h
@@ -43,6 +43,7 @@
 
 namespace base {
 class FilePath;
+class Version;
 }
 
 namespace content {
@@ -120,6 +121,7 @@
 
   // Adds an update request for the given WebStore item (App/Extension).
   void AddExtensionUpdateRequest(const std::string& extension_id,
+                                 const base::Version& version,
                                  const SuccessCallback& callback);
 
   // Returns the email address of the custodian.
diff --git a/chrome/browser/supervised_user/supervised_user_service_unittest.cc b/chrome/browser/supervised_user/supervised_user_service_unittest.cc
index fa416420..83778b3 100644
--- a/chrome/browser/supervised_user/supervised_user_service_unittest.cc
+++ b/chrome/browser/supervised_user/supervised_user_service_unittest.cc
@@ -256,7 +256,7 @@
     callbacks_.push_back(callback);
   }
 
-  void CreateExtensionUpdateRequest(const std::string& extension_id,
+  void CreateExtensionUpdateRequest(const std::string& id,
                                     const SuccessCallback& callback) override {
     FAIL();
   }
diff --git a/chrome/browser/supervised_user/supervised_user_settings_service.cc b/chrome/browser/supervised_user/supervised_user_settings_service.cc
index 55ff2f4..69c0acf 100644
--- a/chrome/browser/supervised_user/supervised_user_settings_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_settings_service.cc
@@ -50,7 +50,10 @@
 }  // namespace
 
 SupervisedUserSettingsService::SupervisedUserSettingsService()
-    : active_(false), local_settings_(new base::DictionaryValue) {}
+    : active_(false),
+      initialization_failed_(false),
+      local_settings_(new base::DictionaryValue) {
+}
 
 SupervisedUserSettingsService::~SupervisedUserSettingsService() {}
 
@@ -97,7 +100,9 @@
 }
 
 bool SupervisedUserSettingsService::IsReady() {
-  return store_->IsInitializationComplete();
+  // Initialization cannot be complete but have failed at the same time.
+  DCHECK(!(store_->IsInitializationComplete() && initialization_failed_));
+  return initialization_failed_ || store_->IsInitializationComplete();
 }
 
 void SupervisedUserSettingsService::Clear() {
@@ -299,9 +304,16 @@
 }
 
 void SupervisedUserSettingsService::OnInitializationCompleted(bool success) {
+  if (!success) {
+    // If this happens, it means the profile directory was not found. There is
+    // not much we can do, but the whole profile will probably be useless
+    // anyway. Just mark initialization as failed and continue otherwise,
+    // because subscribers might still expect to be called back.
+    initialization_failed_ = true;
+  }
+
   // TODO(bauerb): Temporary CHECK while investigating https://crbug.com/425785.
   // Remove (or change back to DCHECK) once the bug is fixed.
-  CHECK(success);
   CHECK(IsReady());
   InformSubscribers();
 }
@@ -354,7 +366,7 @@
 
 scoped_ptr<base::DictionaryValue> SupervisedUserSettingsService::GetSettings() {
   DCHECK(IsReady());
-  if (!active_)
+  if (!active_ || initialization_failed_)
     return scoped_ptr<base::DictionaryValue>();
 
   scoped_ptr<base::DictionaryValue> settings(local_settings_->DeepCopy());
diff --git a/chrome/browser/supervised_user/supervised_user_settings_service.h b/chrome/browser/supervised_user/supervised_user_settings_service.h
index 29bd306..7a7041ef3 100644
--- a/chrome/browser/supervised_user/supervised_user_settings_service.h
+++ b/chrome/browser/supervised_user/supervised_user_settings_service.h
@@ -152,6 +152,8 @@
 
   bool active_;
 
+  bool initialization_failed_;
+
   // A set of local settings that are fixed and not configured remotely.
   scoped_ptr<base::DictionaryValue> local_settings_;
 
diff --git a/chrome/browser/sync/glue/autofill_wallet_data_type_controller.cc b/chrome/browser/sync/glue/autofill_wallet_data_type_controller.cc
index 5050de97..00ec1a8 100644
--- a/chrome/browser/sync/glue/autofill_wallet_data_type_controller.cc
+++ b/chrome/browser/sync/glue/autofill_wallet_data_type_controller.cc
@@ -5,10 +5,14 @@
 #include "chrome/browser/sync/glue/autofill_wallet_data_type_controller.h"
 
 #include "base/bind.h"
+#include "base/prefs/pref_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h"
 #include "chrome/browser/sync/profile_sync_components_factory.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/webdata/web_data_service_factory.h"
+#include "chrome/common/pref_names.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "sync/api/sync_error.h"
@@ -27,6 +31,12 @@
           profile_sync_factory),
       profile_(profile),
       callback_registered_(false) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  pref_registrar_.Init(profile->GetPrefs());
+  pref_registrar_.Add(
+      autofill::prefs::kAutofillWalletSyncExperimentEnabled,
+      base::Bind(&AutofillWalletDataTypeController::OnSyncExperimentPrefChanged,
+                 base::Unretained(this)));
 }
 
 AutofillWalletDataTypeController::~AutofillWalletDataTypeController() {
@@ -75,8 +85,40 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 }
 
+bool AutofillWalletDataTypeController::ReadyForStart() const {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  return profile_->GetPrefs()->GetBoolean(
+      autofill::prefs::kAutofillWalletSyncExperimentEnabled);
+}
+
 void AutofillWalletDataTypeController::WebDatabaseLoaded() {
   OnModelLoaded();
 }
 
+void AutofillWalletDataTypeController::OnSyncExperimentPrefChanged() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  if (!profile_->GetPrefs()->GetBoolean(
+          autofill::prefs::kAutofillWalletSyncExperimentEnabled)) {
+    // If autofill wallet sync is disabled, post a task to the backend thread to
+    // stop the datatype.
+    if (state() != NOT_RUNNING && state() != STOPPING) {
+      syncer::SyncError error(FROM_HERE,
+                              syncer::SyncError::DATATYPE_POLICY_ERROR,
+                              "Wallet syncing is disabled by policy.",
+                              syncer::AUTOFILL_WALLET_DATA);
+      PostTaskOnBackendThread(
+          FROM_HERE,
+          base::Bind(&DataTypeController::OnSingleDataTypeUnrecoverableError,
+                     this,
+                     error));
+    }
+  } else {
+    // The experiment was just enabled. Trigger a reconfiguration. This will do
+    // nothing if the type isn't preferred.
+    ProfileSyncService* sync_service =
+        ProfileSyncServiceFactory::GetForProfile(profile_);
+    sync_service->ReenableDatatype(type());
+  }
+}
+
 }  // namespace browser_sync
diff --git a/chrome/browser/sync/glue/autofill_wallet_data_type_controller.h b/chrome/browser/sync/glue/autofill_wallet_data_type_controller.h
index e5381dec..a652f6d 100644
--- a/chrome/browser/sync/glue/autofill_wallet_data_type_controller.h
+++ b/chrome/browser/sync/glue/autofill_wallet_data_type_controller.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_SYNC_GLUE_AUTOFILL_WALLET_DATA_TYPE_CONTROLLER_H_
 
 #include "base/basictypes.h"
+#include "base/prefs/pref_change_registrar.h"
 #include "components/sync_driver/non_ui_data_type_controller.h"
 
 class Profile;
@@ -33,12 +34,19 @@
                                const base::Closure& task) override;
   bool StartModels() override;
   void StopModels() override;
+  bool ReadyForStart() const override;
 
   void WebDatabaseLoaded();
 
+  // Callback for changes to kAutofillWalletSyncExperimentEnabled.
+  void OnSyncExperimentPrefChanged();
+
   Profile* const profile_;
   bool callback_registered_;
 
+  // Registrar for listening to kAutofillWalletSyncExperimentEnabled status.
+  PrefChangeRegistrar pref_registrar_;
+
   DISALLOW_COPY_AND_ASSIGN(AutofillWalletDataTypeController);
 };
 
diff --git a/chrome/browser/sync/profile_sync_components_factory_impl.cc b/chrome/browser/sync/profile_sync_components_factory_impl.cc
index 1d051477..fbde84be 100644
--- a/chrome/browser/sync/profile_sync_components_factory_impl.cc
+++ b/chrome/browser/sync/profile_sync_components_factory_impl.cc
@@ -229,11 +229,9 @@
         new AutofillProfileDataTypeController(this, profile_));
   }
 
-  if (profile_->GetPrefs()->GetBoolean(
-           autofill::prefs::kAutofillWalletSyncExperimentEnabled) &&
-      !disabled_types.Has(syncer::AUTOFILL_WALLET_DATA)) {
-    // The feature can be enabled by sync experiment *or* command line flag,
-    // and additionally the sync type must be enabled.
+  // Autofill wallet sync is enabled by default, but behind a syncer experiment
+  // enforced by the datatype controller. Register unless explicitly disabled.
+  if (!disabled_types.Has(syncer::AUTOFILL_WALLET_DATA)) {
     pss->RegisterDataTypeController(
         new browser_sync::AutofillWalletDataTypeController(this, profile_));
   }
@@ -641,7 +639,7 @@
 
 scoped_ptr<syncer::AttachmentService>
 ProfileSyncComponentsFactoryImpl::CreateAttachmentService(
-    scoped_ptr<syncer::AttachmentStore> attachment_store,
+    scoped_ptr<syncer::AttachmentStoreForSync> attachment_store,
     const syncer::UserShare& user_share,
     const std::string& store_birthday,
     syncer::ModelType model_type,
diff --git a/chrome/browser/sync/profile_sync_components_factory_impl.h b/chrome/browser/sync/profile_sync_components_factory_impl.h
index ca8037a..0014e0a 100644
--- a/chrome/browser/sync/profile_sync_components_factory_impl.h
+++ b/chrome/browser/sync/profile_sync_components_factory_impl.h
@@ -66,7 +66,7 @@
   base::WeakPtr<syncer::SyncableService> GetSyncableServiceForType(
       syncer::ModelType type) override;
   scoped_ptr<syncer::AttachmentService> CreateAttachmentService(
-      scoped_ptr<syncer::AttachmentStore> attachment_store,
+      scoped_ptr<syncer::AttachmentStoreForSync> attachment_store,
       const syncer::UserShare& user_share,
       const std::string& store_birthday,
       syncer::ModelType model_type,
diff --git a/chrome/browser/sync/profile_sync_components_factory_impl_unittest.cc b/chrome/browser/sync/profile_sync_components_factory_impl_unittest.cc
index d0ff9afe..a484b44a 100644
--- a/chrome/browser/sync/profile_sync_components_factory_impl_unittest.cc
+++ b/chrome/browser/sync/profile_sync_components_factory_impl_unittest.cc
@@ -49,6 +49,7 @@
     datatypes.push_back(syncer::APP_SETTINGS);
     datatypes.push_back(syncer::AUTOFILL);
     datatypes.push_back(syncer::AUTOFILL_PROFILE);
+    datatypes.push_back(syncer::AUTOFILL_WALLET_DATA);
     datatypes.push_back(syncer::BOOKMARKS);
     datatypes.push_back(syncer::DEVICE_INFO);
 #if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_CHROMEOS)
diff --git a/chrome/browser/sync/profile_sync_components_factory_mock.cc b/chrome/browser/sync/profile_sync_components_factory_mock.cc
index c6181acf..30f57a0 100644
--- a/chrome/browser/sync/profile_sync_components_factory_mock.cc
+++ b/chrome/browser/sync/profile_sync_components_factory_mock.cc
@@ -37,7 +37,7 @@
 
 scoped_ptr<syncer::AttachmentService>
 ProfileSyncComponentsFactoryMock::CreateAttachmentService(
-    scoped_ptr<syncer::AttachmentStore> attachment_store,
+    scoped_ptr<syncer::AttachmentStoreForSync> attachment_store,
     const syncer::UserShare& user_share,
     const std::string& store_birthday,
     syncer::ModelType model_type,
diff --git a/chrome/browser/sync/profile_sync_components_factory_mock.h b/chrome/browser/sync/profile_sync_components_factory_mock.h
index ae0a1a1..3e106962 100644
--- a/chrome/browser/sync/profile_sync_components_factory_mock.h
+++ b/chrome/browser/sync/profile_sync_components_factory_mock.h
@@ -52,7 +52,7 @@
   MOCK_METHOD1(GetSyncableServiceForType,
                base::WeakPtr<syncer::SyncableService>(syncer::ModelType));
   virtual scoped_ptr<syncer::AttachmentService> CreateAttachmentService(
-      scoped_ptr<syncer::AttachmentStore> attachment_store,
+      scoped_ptr<syncer::AttachmentStoreForSync> attachment_store,
       const syncer::UserShare& user_share,
       const std::string& store_birthday,
       syncer::ModelType model_type,
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index 7ea11bc4..b85d6b6 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -958,12 +958,6 @@
   unrecoverable_error_location_ = tracked_objects::Location();
 }
 
-void ProfileSyncService::RegisterNewDataType(syncer::ModelType data_type) {
-  if (directory_data_type_controllers_.count(data_type) > 0)
-    return;
-  NOTREACHED();
-}
-
 // An invariant has been violated.  Transition to an error state where we try
 // to do as little work as possible, to avoid further corruption or crashes.
 void ProfileSyncService::OnUnrecoverableError(
@@ -1001,7 +995,8 @@
 }
 
 void ProfileSyncService::ReenableDatatype(syncer::ModelType type) {
-  DCHECK(backend_initialized_);
+  if (!backend_initialized_)
+    return;
   directory_data_type_manager_->ReenableType(type);
 }
 
@@ -1162,51 +1157,6 @@
   profile()->GetPrefs()->SetBoolean(
       autofill::prefs::kAutofillWalletSyncExperimentEnabled,
       experiments.wallet_sync_enabled);
-
-  // If this is a first time sync for a client, this will be called before
-  // OnBackendInitialized() to ensure the new datatypes are available at sync
-  // setup. As a result, the migrator won't exist yet. This is fine because for
-  // first time sync cases we're only concerned with making the datatype
-  // available.
-  if (migrator_.get() &&
-      migrator_->state() != browser_sync::BackendMigrator::IDLE) {
-    DVLOG(1) << "Dropping OnExperimentsChanged due to migrator busy.";
-    return;
-  }
-
-  const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
-  syncer::ModelTypeSet to_add;
-  const syncer::ModelTypeSet to_register =
-      Difference(to_add, registered_types);
-  DVLOG(2) << "OnExperimentsChanged called with types: "
-           << syncer::ModelTypeSetToString(to_add);
-  DVLOG(2) << "Enabling types: " << syncer::ModelTypeSetToString(to_register);
-
-  for (syncer::ModelTypeSet::Iterator it = to_register.First();
-       it.Good(); it.Inc()) {
-    // Received notice to enable experimental type. Check if the type is
-    // registered, and if not register a new datatype controller.
-    RegisterNewDataType(it.Get());
-  }
-
-  // Check if the user has "Keep Everything Synced" enabled. If so, we want
-  // to turn on all experimental types if they're not already on. Otherwise we
-  // leave them off.
-  // Note: if any types are already registered, we don't turn them on. This
-  // covers the case where we're already in the process of reconfiguring
-  // to turn an experimental type on.
-  if (sync_prefs_.HasKeepEverythingSynced()) {
-    // Mark all data types as preferred.
-    sync_prefs_.SetPreferredDataTypes(registered_types, registered_types);
-
-    // Only automatically turn on types if we have already finished set up.
-    // Otherwise, just leave the experimental types on by default.
-    if (!to_register.Empty() && HasSyncSetupCompleted() && migrator_) {
-      DVLOG(1) << "Dynamically enabling new datatypes: "
-               << syncer::ModelTypeSetToString(to_register);
-      OnMigrationNeededForTypes(to_register);
-    }
-  }
 }
 
 void ProfileSyncService::UpdateAuthErrorState(const AuthError& error) {
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index aa1f4608..0ca40fe 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -384,7 +384,7 @@
   // Fills state_map with a map of current data types that are possible to
   // sync, as well as their states.
   void GetDataTypeControllerStates(
-    sync_driver::DataTypeController::StateMap* state_map) const;
+      sync_driver::DataTypeController::StateMap* state_map) const;
 
   // Disables sync for user. Use ShowLoginDialog to enable.
   virtual void DisableForUser();
@@ -916,14 +916,6 @@
   // backend to start, one of SYNC, BACKUP or ROLLBACK.
   virtual void StartUpSlowBackendComponents(BackendMode mode);
 
-  // About-flags experiment names for datatypes that aren't enabled by default
-  // yet.
-  static std::string GetExperimentNameForDataType(
-      syncer::ModelType data_type);
-
-  // Create and register a new datatype controller.
-  void RegisterNewDataType(syncer::ModelType data_type);
-
   // Collects preferred sync data types from |preference_providers_|.
   syncer::ModelTypeSet GetDataTypesFromPreferenceProviders() const;
 
diff --git a/chrome/browser/sync/profile_sync_service_android.cc b/chrome/browser/sync/profile_sync_service_android.cc
index 039878f8..c7350a8c 100644
--- a/chrome/browser/sync/profile_sync_service_android.cc
+++ b/chrome/browser/sync/profile_sync_service_android.cc
@@ -64,6 +64,7 @@
   EXPERIMENTS = 1 << 12,
   SUPERVISED_USER_SETTING = 1 << 13,
   SUPERVISED_USER_WHITELIST = 1 << 14,
+  AUTOFILL_WALLET = 1 << 15,
 };
 
 }  // namespace
@@ -461,6 +462,9 @@
   if (types.Has(syncer::AUTOFILL_PROFILE)) {
     model_type_selection |= AUTOFILL_PROFILE;
   }
+  if (types.Has(syncer::AUTOFILL_WALLET_DATA)) {
+    model_type_selection |= AUTOFILL_WALLET;
+  }
   if (types.Has(syncer::PASSWORDS)) {
     model_type_selection |= PASSWORD;
   }
diff --git a/chrome/browser/sync/test/integration/migration_test.cc b/chrome/browser/sync/test/integration/migration_test.cc
index f97f399..4d7e1aa8 100644
--- a/chrome/browser/sync/test/integration/migration_test.cc
+++ b/chrome/browser/sync/test/integration/migration_test.cc
@@ -100,6 +100,10 @@
     preferred_data_types.Remove(syncer::SUPERVISED_USER_SETTINGS);
     preferred_data_types.Remove(syncer::SUPERVISED_USER_WHITELISTS);
 
+    // Autofill wallet will be unready during this test, so we should not
+    // request that it be migrated.
+    preferred_data_types.Remove(syncer::AUTOFILL_WALLET_DATA);
+
     // Make sure all clients have the same preferred data types.
     for (int i = 1; i < num_clients(); ++i) {
       const syncer::ModelTypeSet other_preferred_data_types =
@@ -296,9 +300,11 @@
 
 #if defined(OS_WIN) || defined(OS_MACOSX)
 // http://crbug.com/403778
-#define MAYBE_AllTypesIndividuallyTriggerNotification DISABLED_AllTypesIndividuallyTriggerNotification
+#define MAYBE_AllTypesIndividuallyTriggerNotification \
+    DISABLED_AllTypesIndividuallyTriggerNotification
 #else
-#define MAYBE_AllTypesIndividuallyTriggerNotification AllTypesIndividuallyTriggerNotification
+#define MAYBE_AllTypesIndividuallyTriggerNotification \
+    AllTypesIndividuallyTriggerNotification
 #endif
 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
                        MAYBE_AllTypesIndividuallyTriggerNotification) {
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
index 53081eef..672e6d1 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -6,13 +6,17 @@
 #include "base/command_line.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/browser/sync/test/integration/autofill_helper.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
+#include "content/public/browser/notification_service.h"
 #include "sync/internal_api/public/base/model_type.h"
 #include "sync/test/fake_server/fake_server_entity.h"
 #include "sync/test/fake_server/unique_client_entity.h"
@@ -27,6 +31,8 @@
 const char kWalletSyncEnabledPreferencesContents[] =
     "{\"autofill\": { \"wallet_import_sync_experiment_enabled\": true } }";
 
+const char kWalletSyncExperimentTag[] = "wallet_sync";
+
 }  // namespace
 
 class SingleClientWalletSyncTest : public SyncTest {
@@ -34,25 +40,131 @@
   SingleClientWalletSyncTest() : SyncTest(SINGLE_CLIENT) {}
   ~SingleClientWalletSyncTest() override {}
 
+  void TriggerSyncCycle() {
+    // Note: we use the experiments type here as we want to be able to trigger a
+    // sync cycle even when wallet is not enabled yet.
+    const syncer::ModelTypeSet kExperimentsType(syncer::EXPERIMENTS);
+    content::NotificationService::current()->Notify(
+        chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
+        content::Source<Profile>(GetProfile(0)),
+        content::Details<const syncer::ModelTypeSet>(&kExperimentsType));
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(SingleClientWalletSyncTest);
 };
 
+// Checker that will wait until an asynchronous Wallet datatype enable event
+// happens, or times out.
+class WalletEnabledChecker : public SingleClientStatusChangeChecker {
+ public:
+  WalletEnabledChecker()
+      : SingleClientStatusChangeChecker(
+            sync_datatype_helper::test()->GetSyncService(0)) {}
+  ~WalletEnabledChecker() override {}
+
+  // SingleClientStatusChangeChecker overrides.
+  bool IsExitConditionSatisfied() override {
+    return service()->GetActiveDataTypes().Has(syncer::AUTOFILL_WALLET_DATA);
+  }
+  std::string GetDebugMessage() const override {
+    return "Waiting for wallet enable event.";
+  }
+};
+
+// Checker that will wait until an asynchronous Wallet datatype disable event
+// happens, or times out
+class WalletDisabledChecker : public SingleClientStatusChangeChecker {
+ public:
+  WalletDisabledChecker()
+      : SingleClientStatusChangeChecker(
+            sync_datatype_helper::test()->GetSyncService(0)) {}
+  ~WalletDisabledChecker() override {}
+
+  // SingleClientStatusChangeChecker overrides.
+  bool IsExitConditionSatisfied() override {
+    return !service()->GetActiveDataTypes().Has(syncer::AUTOFILL_WALLET_DATA);
+  }
+  std::string GetDebugMessage() const override {
+    return "Waiting for wallet disable event.";
+  }
+};
+
 IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest, DisabledByDefault) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed";
   // The type should not be enabled without the experiment enabled.
-  ASSERT_FALSE(GetClient(0)->IsTypePreferred(syncer::AUTOFILL_WALLET_DATA));
+  ASSERT_FALSE(GetClient(0)->service()->GetActiveDataTypes().Has(
+      syncer::AUTOFILL_WALLET_DATA));
 }
 
 IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest, EnabledViaPreference) {
   SetPreexistingPreferencesFileContents(kWalletSyncEnabledPreferencesContents);
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed";
   // The type should not be enabled without the experiment enabled.
-  ASSERT_TRUE(GetClient(0)->IsTypePreferred(syncer::AUTOFILL_WALLET_DATA));
+  ASSERT_TRUE(GetClient(0)->service()->GetActiveDataTypes().Has(
+      syncer::AUTOFILL_WALLET_DATA));
   // TODO(pvalenzuela): Assert that the local root node for AUTOFILL_WALLET_DATA
   // exists.
 }
 
+// Tests that an experiment received at sync startup time (during sign-in)
+// enables the wallet datatype.
+IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest,
+                       EnabledViaExperimentStartup) {
+  sync_pb::EntitySpecifics experiment_entity;
+  sync_pb::ExperimentsSpecifics* experiment_specifics =
+      experiment_entity.mutable_experiments();
+  experiment_specifics->mutable_wallet_sync()->set_enabled(true);
+  GetFakeServer()->InjectEntity(
+      fake_server::UniqueClientEntity::CreateForInjection(
+          syncer::EXPERIMENTS,
+          kWalletSyncExperimentTag,
+          experiment_entity));
+
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed";
+  ASSERT_TRUE(GetClient(0)->service()->GetActiveDataTypes().Has(
+      syncer::AUTOFILL_WALLET_DATA));
+}
+
+// Tests receiving an enable experiment at runtime, followed by a disabled
+// experiment, and verifies the datatype is enabled/disabled as necessary.
+IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest,
+                       EnabledDisabledViaExperiment) {
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed";
+  ASSERT_FALSE(GetClient(0)->service()->GetActiveDataTypes().
+      Has(syncer::AUTOFILL_WALLET_DATA));
+
+  sync_pb::EntitySpecifics experiment_entity;
+  sync_pb::ExperimentsSpecifics* experiment_specifics =
+      experiment_entity.mutable_experiments();
+
+  // First enable the experiment.
+  experiment_specifics->mutable_wallet_sync()->set_enabled(true);
+  GetFakeServer()->InjectEntity(
+      fake_server::UniqueClientEntity::CreateForInjection(
+          syncer::EXPERIMENTS, kWalletSyncExperimentTag, experiment_entity));
+  TriggerSyncCycle();
+
+  WalletEnabledChecker enabled_checker;
+  enabled_checker.Wait();
+  ASSERT_FALSE(enabled_checker.TimedOut());
+  ASSERT_TRUE(GetClient(0)->service()->GetActiveDataTypes().Has(
+      syncer::AUTOFILL_WALLET_DATA));
+
+  // Then disable the experiment.
+  experiment_specifics->mutable_wallet_sync()->set_enabled(false);
+  GetFakeServer()->InjectEntity(
+      fake_server::UniqueClientEntity::CreateForInjection(
+          syncer::EXPERIMENTS, kWalletSyncExperimentTag, experiment_entity));
+  TriggerSyncCycle();
+
+  WalletDisabledChecker disable_checker;
+  disable_checker.Wait();
+  ASSERT_FALSE(disable_checker.TimedOut());
+  ASSERT_FALSE(GetClient(0)->service()->GetActiveDataTypes().
+      Has(syncer::AUTOFILL_WALLET_DATA));
+}
+
 IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest, Download) {
   SetPreexistingPreferencesFileContents(kWalletSyncEnabledPreferencesContents);
 
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
index 22cd926e..5d31cf49 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
@@ -34,6 +34,7 @@
 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
 #include "google_apis/drive/drive_api_parser.h"
 #include "storage/common/fileapi/file_system_util.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/env.h"
 #include "third_party/leveldatabase/src/include/leveldb/status.h"
@@ -214,6 +215,7 @@
   leveldb::Options options;
   options.max_open_files = 0;  // Use minimum.
   options.create_if_missing = true;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   if (env_override)
     options.env = env_override;
   leveldb::DB* db = nullptr;
diff --git a/chrome/browser/sync_file_system/local/local_file_change_tracker.cc b/chrome/browser/sync_file_system/local/local_file_change_tracker.cc
index 694b05a9..ba63633c 100644
--- a/chrome/browser/sync_file_system/local/local_file_change_tracker.cc
+++ b/chrome/browser/sync_file_system/local/local_file_change_tracker.cc
@@ -16,6 +16,7 @@
 #include "storage/browser/fileapi/file_system_file_util.h"
 #include "storage/browser/fileapi/file_system_operation_context.h"
 #include "storage/common/fileapi/file_system_util.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/env.h"
@@ -480,6 +481,7 @@
   leveldb::Options options;
   options.max_open_files = 0;  // Use minimum.
   options.create_if_missing = true;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   if (env_override_)
     options.env = env_override_;
   leveldb::DB* db;
diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc
index 66a8407..6ec678b 100644
--- a/chrome/browser/task_manager/task_manager_browsertest.cc
+++ b/chrome/browser/task_manager/task_manager_browsertest.cc
@@ -778,8 +778,8 @@
 
 // Checks that task manager counts a worker thread JS heap size.
 // http://crbug.com/241066
-// Flaky, http://crbug.com/259368
-IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, DISABLED_WebWorkerJSHeapMemory) {
+IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, WebWorkerJSHeapMemory) {
+  ShowTaskManager();
   ui_test_utils::NavigateToURL(browser(), GetTestURL());
   const int extra_timeout_ms = 500;
   size_t minimal_heap_size = 2 * 1024 * 1024 * sizeof(void*);
@@ -787,7 +787,7 @@
       "var blob = new Blob([\n"
       "    'mem = new Array(%lu);',\n"
       "    'for (var i = 0; i < mem.length; i += 16) mem[i] = i;',\n"
-      "    'postMessage();']);\n"
+      "    'postMessage(i);']);\n"
       "blobURL = window.URL.createObjectURL(blob);\n"
       "worker = new Worker(blobURL);\n"
       "// Give the task manager few seconds to poll for JS heap sizes.\n"
@@ -795,7 +795,7 @@
       "    this,\n"
       "    function () { window.domAutomationController.send(true); },\n"
       "    %d);\n"
-      "worker.postMessage();\n",
+      "worker.postMessage('go');\n",
       static_cast<unsigned long>(minimal_heap_size),
       GetUpdateTimeMs() + extra_timeout_ms);
   bool ok;
@@ -803,7 +803,9 @@
       browser()->tab_strip_model()->GetActiveWebContents(), test_js, &ok));
   ASSERT_TRUE(ok);
 
-  int resource_index = TaskManager::GetInstance()->model()->ResourceCount() - 1;
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchTab("title1.html")));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  int resource_index = FindResourceIndex(MatchTab("title1.html"));
   size_t result;
 
   ASSERT_TRUE(model()->GetV8Memory(resource_index, &result));
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 092c7d1..2bc64da 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -149,11 +149,6 @@
                     ".",
                     "//chrome")
   }
-  if (enable_task_manager) {
-    sources += rebase_path(gypi_values.chrome_browser_ui_task_manager_sources,
-                           ".",
-                           "//chrome")
-  }
   if (enable_nacl) {
     sources +=
         rebase_path(gypi_values.chrome_browser_ui_nacl_sources, ".", "//chrome")
diff --git a/chrome/browser/ui/android/infobars/account_chooser_infobar.cc b/chrome/browser/ui/android/infobars/account_chooser_infobar.cc
index 68d42e9..1f9b05c5 100644
--- a/chrome/browser/ui/android/infobars/account_chooser_infobar.cc
+++ b/chrome/browser/ui/android/infobars/account_chooser_infobar.cc
@@ -7,7 +7,7 @@
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/password_manager/account_chooser_infobar_delegate_android.h"
 #include "chrome/browser/password_manager/credential_android.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "jni/AccountChooserInfoBar_jni.h"
 
 namespace {
diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.cc b/chrome/browser/ui/app_list/app_list_view_delegate.cc
index 3e9377e3..c16306c 100644
--- a/chrome/browser/ui/app_list/app_list_view_delegate.cc
+++ b/chrome/browser/ui/app_list/app_list_view_delegate.cc
@@ -660,8 +660,7 @@
   // should cause a search to happen for 'Ok Google', not two hotword triggers).
   // To get around this, always stop the session when switching to speech
   // recognition.
-  if (HotwordService::IsExperimentalHotwordingEnabled() &&
-      service && service->HotwordEnabled()) {
+  if (service && service->HotwordEnabled()) {
     HotwordService* hotword_service =
         HotwordServiceFactory::GetForProfile(profile_);
     if (hotword_service)
@@ -699,7 +698,6 @@
   // speech recognition has stopped. Do not request hotwording after the app
   // list has already closed.
   if (new_state == app_list::SPEECH_RECOGNITION_READY &&
-      HotwordService::IsExperimentalHotwordingEnabled() &&
       service && service->HotwordEnabled() &&
       controller_->GetAppListWindow()) {
     HotwordService* hotword_service =
diff --git a/chrome/browser/ui/app_list/search/common/url_icon_source.cc b/chrome/browser/ui/app_list/search/common/url_icon_source.cc
index acb2d28..3dccf53 100644
--- a/chrome/browser/ui/app_list/search/common/url_icon_source.cc
+++ b/chrome/browser/ui/app_list/search/common/url_icon_source.cc
@@ -17,13 +17,14 @@
 
 namespace app_list {
 
-UrlIconSource::UrlIconSource(
-    const IconLoadedCallback& icon_loaded_callback,
-    net::URLRequestContextGetter* context_getter,
-    const GURL& icon_url,
-    int icon_size,
-    int default_icon_resource_id)
-    : icon_loaded_callback_(icon_loaded_callback),
+UrlIconSource::UrlIconSource(const IconLoadedCallback& icon_loaded_callback,
+                             net::URLRequestContextGetter* context_getter,
+                             const GURL& icon_url,
+                             int icon_size,
+                             int default_icon_resource_id)
+    : ImageRequest(
+          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)),
+      icon_loaded_callback_(icon_loaded_callback),
       context_getter_(context_getter),
       icon_url_(icon_url),
       icon_size_(icon_size),
@@ -33,8 +34,6 @@
 }
 
 UrlIconSource::~UrlIconSource() {
-  if (image_decoder_.get())
-    image_decoder_->set_delegate(NULL);
 }
 
 void UrlIconSource::StartIconFetch() {
@@ -72,14 +71,10 @@
   std::string unsafe_icon_data;
   fetcher->GetResponseAsString(&unsafe_icon_data);
 
-  image_decoder_ =
-      new ImageDecoder(this, unsafe_icon_data, ImageDecoder::DEFAULT_CODEC);
-  image_decoder_->Start(
-      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI));
+  ImageDecoder::Start(this, unsafe_icon_data);
 }
 
-void UrlIconSource::OnImageDecoded(const ImageDecoder* decoder,
-                                   const SkBitmap& decoded_image) {
+void UrlIconSource::OnImageDecoded(const SkBitmap& decoded_image) {
   icon_ = gfx::ImageSkiaOperations::CreateResizedImage(
       gfx::ImageSkia::CreateFrom1xBitmap(decoded_image),
       skia::ImageOperations::RESIZE_BEST,
@@ -88,8 +83,7 @@
   icon_loaded_callback_.Run();
 }
 
-void UrlIconSource::OnDecodeImageFailed(
-    const ImageDecoder* decoder) {
+void UrlIconSource::OnDecodeImageFailed() {
   // Failed to decode image. Do nothing and just use the default icon.
 }
 
diff --git a/chrome/browser/ui/app_list/search/common/url_icon_source.h b/chrome/browser/ui/app_list/search/common/url_icon_source.h
index 6d910be..63c74f57 100644
--- a/chrome/browser/ui/app_list/search/common/url_icon_source.h
+++ b/chrome/browser/ui/app_list/search/common/url_icon_source.h
@@ -26,7 +26,7 @@
 // fetched, the default icon (specified by it's resource id) is shown.
 class UrlIconSource : public gfx::ImageSkiaSource,
                       public net::URLFetcherDelegate,
-                      public ImageDecoder::Delegate {
+                      public ImageDecoder::ImageRequest {
  public:
   typedef base::Closure IconLoadedCallback;
 
@@ -50,10 +50,9 @@
   // net::URLFetcherDelegate overrides:
   void OnURLFetchComplete(const net::URLFetcher* source) override;
 
-  // ImageDecoder::Delegate overrides:
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override;
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override;
+  // ImageDecoder::ImageRequest overrides:
+  void OnImageDecoded(const SkBitmap& decoded_image) override;
+  void OnDecodeImageFailed() override;
 
   IconLoadedCallback icon_loaded_callback_;
   net::URLRequestContextGetter* context_getter_;
@@ -64,8 +63,6 @@
   bool icon_fetch_attempted_;
   scoped_ptr<net::URLFetcher> icon_fetcher_;
 
-  scoped_refptr<ImageDecoder> image_decoder_;
-
   gfx::ImageSkia icon_;
 
   DISALLOW_COPY_AND_ASSIGN(UrlIconSource);
diff --git a/chrome/browser/ui/app_list/search/suggestions/suggestions_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/suggestions/suggestions_search_provider_unittest.cc
index 1486a06..470fa250 100644
--- a/chrome/browser/ui/app_list/search/suggestions/suggestions_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/suggestions/suggestions_search_provider_unittest.cc
@@ -10,7 +10,6 @@
 
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/metrics/field_trial.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/sync/profile_sync_service_mock.h"
 #include "chrome/browser/ui/app_list/app_list_test_util.h"
@@ -18,8 +17,6 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/suggestions/proto/suggestions.pb.h"
 #include "components/suggestions/suggestions_store.h"
-#include "components/variations/entropy_provider.h"
-#include "components/variations/variations_associated_data.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/app_list/search_result.h"
 
@@ -57,7 +54,6 @@
   // AppListTestBase overrides:
   void SetUp() override {
     AppListTestBase::SetUp();
-    EnableSuggestionsService();
 
     profile_.reset(ProfileSyncServiceMock::MakeSignedInTestingProfile());
     suggestions_search_.reset(
@@ -69,23 +65,6 @@
 
   }
 
-  // Enables the SuggestionsService field trial.
-  void EnableSuggestionsService() {
-    // Clear the existing |field_trial_list_| to avoid firing a DCHECK.
-    field_trial_list_.reset(NULL);
-    field_trial_list_.reset(
-        new base::FieldTrialList(new metrics::SHA1EntropyProvider("foo")));
-
-    variations::testing::ClearAllVariationParams();
-    std::map<std::string, std::string> params;
-    params["state"] = "enabled";
-    variations::AssociateVariationParams("ChromeSuggestions", "Group1",
-                                         params);
-    field_trial_ = base::FieldTrialList::CreateFieldTrial(
-        "ChromeSuggestions", "Group1");
-    field_trial_->group();
-  }
-
   void TearDown() override {
     // Making sure suggestions are cleared between tests.
     suggestions_store_->ClearSuggestions();
@@ -121,8 +100,6 @@
 
   // Helpers.
   ProfileSyncServiceMock* mock_pss_;
-  scoped_ptr<base::FieldTrialList> field_trial_list_;
-  scoped_refptr<base::FieldTrial> field_trial_;
 
   DISALLOW_COPY_AND_ASSIGN(SuggestionsSearchProviderTest);
 };
diff --git a/chrome/browser/ui/app_list/start_page_service.cc b/chrome/browser/ui/app_list/start_page_service.cc
index 8af8b47..dc7e805 100644
--- a/chrome/browser/ui/app_list/start_page_service.cc
+++ b/chrome/browser/ui/app_list/start_page_service.cc
@@ -281,7 +281,7 @@
 StartPageService::StartPageService(Profile* profile)
     : profile_(profile),
       profile_destroy_observer_(new ProfileDestroyObserver(this)),
-      state_(app_list::SPEECH_RECOGNITION_OFF),
+      state_(app_list::SPEECH_RECOGNITION_READY),
       speech_button_toggled_manually_(false),
       speech_result_obtained_(false),
       webui_finished_loading_(false),
@@ -290,12 +290,6 @@
       microphone_available_(true),
       search_engine_is_google_(false),
       weak_factory_(this) {
-  // If experimental hotwording is enabled, then we're always "ready".
-  // Transitioning into the "hotword recognizing" state is handled by the
-  // hotword extension.
-  if (HotwordService::IsExperimentalHotwordingEnabled()) {
-    state_ = app_list::SPEECH_RECOGNITION_READY;
-  }
   if (switches::IsExperimentalAppListEnabled()) {
     TemplateURLService* template_url_service =
         TemplateURLServiceFactory::GetForProfile(profile_);
@@ -369,14 +363,8 @@
   } else if (contents_->IsCrashed()) {
     LoadStartPageURL();
   } else if (contents_->GetWebUI()) {
-    // If experimental hotwording is enabled, don't initialize the web speech
-    // API, which is not used with
-    // experimental hotwording.
     contents_->GetWebUI()->CallJavascriptFunction(
-        "appList.startPage.onAppListShown",
-        base::FundamentalValue(HotwordEnabled()),
-        base::FundamentalValue(
-            !HotwordService::IsExperimentalHotwordingEnabled()));
+        "appList.startPage.onAppListShown");
   }
 
 #if defined(OS_CHROMEOS)
@@ -385,15 +373,10 @@
 }
 
 void StartPageService::AppListHidden() {
-  if (contents_->GetWebUI()) {
-    contents_->GetWebUI()->CallJavascriptFunction(
-        "appList.startPage.onAppListHidden");
-  }
   if (!app_list::switches::IsExperimentalAppListEnabled())
     UnloadContents();
 
-  if (HotwordService::IsExperimentalHotwordingEnabled() &&
-      speech_recognizer_) {
+  if (speech_recognizer_) {
     speech_recognizer_->Stop();
     speech_recognizer_.reset();
 
@@ -414,58 +397,33 @@
   DCHECK(contents_);
   speech_button_toggled_manually_ = true;
 
-  // Speech recognition under V2 hotwording does not depend in any way on the
-  // start page web contents. Do this code path first to make this explicit and
-  // easier to identify what code needs to be deleted when V2 hotwording is
-  // stable.
-  if (HotwordService::IsExperimentalHotwordingEnabled()) {
-    if (!speech_recognizer_) {
-      std::string profile_locale;
+  if (!speech_recognizer_) {
+    std::string profile_locale;
 #if defined(OS_CHROMEOS)
-      profile_locale = profile_->GetPrefs()->GetString(
-          prefs::kApplicationLocale);
+    profile_locale = profile_->GetPrefs()->GetString(
+        prefs::kApplicationLocale);
 #endif
-      if (profile_locale.empty())
-        profile_locale = g_browser_process->GetApplicationLocale();
+    if (profile_locale.empty())
+      profile_locale = g_browser_process->GetApplicationLocale();
 
-      speech_recognizer_.reset(
-          new SpeechRecognizer(weak_factory_.GetWeakPtr(),
-                               profile_->GetRequestContext(),
-                               profile_locale));
-    }
-
-    speech_recognizer_->Start(preamble);
-    return;
+    speech_recognizer_.reset(
+        new SpeechRecognizer(weak_factory_.GetWeakPtr(),
+                             profile_->GetRequestContext(),
+                             profile_locale));
   }
 
-  if (!contents_->GetWebUI())
-    return;
-
-  if (!webui_finished_loading_) {
-    pending_webui_callbacks_.push_back(
-        base::Bind(&StartPageService::ToggleSpeechRecognition,
-                   base::Unretained(this),
-                   preamble));
-    return;
-  }
-
-  contents_->GetWebUI()->CallJavascriptFunction(
-      "appList.startPage.toggleSpeechRecognition");
+  speech_recognizer_->Start(preamble);
 }
 
 bool StartPageService::HotwordEnabled() {
 // Voice input for the launcher is unsupported on non-ChromeOS platforms.
 // TODO(amistry): Make speech input, and hotwording, work on non-ChromeOS.
 #if defined(OS_CHROMEOS)
-  if (HotwordService::IsExperimentalHotwordingEnabled()) {
-    HotwordService* service = HotwordServiceFactory::GetForProfile(profile_);
-    return state_ != SPEECH_RECOGNITION_OFF &&
-        service &&
-        (service->IsSometimesOnEnabled() || service->IsAlwaysOnEnabled()) &&
-        service->IsServiceAvailable();
-  }
-  return HotwordServiceFactory::IsServiceAvailable(profile_) &&
-      profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled);
+  HotwordService* service = HotwordServiceFactory::GetForProfile(profile_);
+  return state_ != SPEECH_RECOGNITION_OFF &&
+      service &&
+      (service->IsSometimesOnEnabled() || service->IsAlwaysOnEnabled()) &&
+      service->IsServiceAvailable();
 #else
   return false;
 #endif
@@ -515,8 +473,7 @@
   if (state_ == new_state)
     return;
 
-  if (HotwordService::IsExperimentalHotwordingEnabled() &&
-      (new_state == SPEECH_RECOGNITION_READY ||
+  if ((new_state == SPEECH_RECOGNITION_READY ||
        new_state == SPEECH_RECOGNITION_OFF) &&
       speech_recognizer_) {
     speech_recognizer_->Stop();
@@ -543,15 +500,13 @@
 
 void StartPageService::GetSpeechAuthParameters(std::string* auth_scope,
                                                std::string* auth_token) {
-  if (HotwordService::IsExperimentalHotwordingEnabled()) {
-    HotwordService* service = HotwordServiceFactory::GetForProfile(profile_);
-    if (service &&
-        service->IsOptedIntoAudioLogging() &&
-        service->IsAlwaysOnEnabled() &&
-        !speech_auth_helper_->GetToken().empty()) {
-      *auth_scope = speech_auth_helper_->GetScope();
-      *auth_token = speech_auth_helper_->GetToken();
-    }
+  HotwordService* service = HotwordServiceFactory::GetForProfile(profile_);
+  if (service &&
+      service->IsOptedIntoAudioLogging() &&
+      service->IsAlwaysOnEnabled() &&
+      !speech_auth_helper_->GetToken().empty()) {
+    *auth_scope = speech_auth_helper_->GetScope();
+    *auth_token = speech_auth_helper_->GetToken();
   }
 }
 
diff --git a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc b/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
index 2b325d4..2fb7881d 100644
--- a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
@@ -61,6 +61,10 @@
     service->Init();
 }
 
+void AppListServiceAsh::OnProfileWillBeRemoved(
+    const base::FilePath& profile_path) {
+}
+
 base::FilePath AppListServiceAsh::GetProfilePath(
     const base::FilePath& user_data_dir) {
   return ChromeLauncherController::instance()->profile()->GetPath();
diff --git a/chrome/browser/ui/ash/app_list/app_list_service_ash.h b/chrome/browser/ui/ash/app_list/app_list_service_ash.h
index a8c42e9c..6dcc302 100644
--- a/chrome/browser/ui/ash/app_list/app_list_service_ash.h
+++ b/chrome/browser/ui/ash/app_list/app_list_service_ash.h
@@ -21,6 +21,12 @@
   // AppListService overrides:
   void Init(Profile* initial_profile) override;
 
+  // ProfileInfoCacheObserver overrides:
+  // On ChromeOS this should never happen. On other platforms, there is always a
+  // Non-ash AppListService that is responsible for handling this.
+  // TODO(calamity): Ash shouldn't observe the ProfileInfoCache at all.
+  void OnProfileWillBeRemoved(const base::FilePath& profile_path) override;
+
  private:
   friend struct DefaultSingletonTraits<AppListServiceAsh>;
 
diff --git a/chrome/browser/ui/autofill/card_unmask_prompt_controller_impl.cc b/chrome/browser/ui/autofill/card_unmask_prompt_controller_impl.cc
index 76d0a4f8..c63d92e 100644
--- a/chrome/browser/ui/autofill/card_unmask_prompt_controller_impl.cc
+++ b/chrome/browser/ui/autofill/card_unmask_prompt_controller_impl.cc
@@ -211,10 +211,12 @@
 }
 
 bool CardUnmaskPromptControllerImpl::CanStoreLocally() const {
-  // TODO(estade): Always return false for Linux. See
-  // https://codereview.chromium.org/1012223002/
+#if defined(ENABLE_SAVE_WALLET_CARDS_LOCALLY)
   return !Profile::FromBrowserContext(web_contents_->GetBrowserContext())
               ->IsOffTheRecord();
+#else
+  return false;
+#endif
 }
 
 bool CardUnmaskPromptControllerImpl::GetStoreLocallyStartState() const {
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 48fb86c..55db7f5 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -2446,7 +2446,7 @@
   const extensions::Extension* extension =
       extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
           extension_id, extensions::ExtensionRegistry::EVERYTHING);
-  return extension && extension->is_hosted_app();
+  return extension && extension->from_bookmark();
 }
 
 bool Browser::SupportsWindowFeatureImpl(WindowFeature feature,
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index e38be5b56..75777311 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -49,7 +49,6 @@
 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
 #include "chrome/browser/ui/tabs/pinned_tab_codec.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/pref_names.h"
@@ -210,19 +209,6 @@
   chrome::ShowAppMenu(browser);
 }
 
-#if !defined(OS_MACOSX)
-// Used by ShouldLocationBarForXXX. Performs a navigation and then checks that
-// the location bar visibility is as expcted.
-void NavigateAndCheckForLocationBar(Browser* browser,
-                                    const std::string& url_string,
-                                    bool expected_visibility) {
-  GURL url(url_string);
-  ui_test_utils::NavigateToURL(browser, url);
-  EXPECT_EQ(expected_visibility,
-      browser->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR));
-}
-#endif  // !defined(OS_MACOSX)
-
 // Displays "INTERSTITIAL" while the interstitial is attached.
 // (InterstitialPage can be used in a test directly, but there would be no way
 // to visually tell if it is showing or not.)
@@ -1400,204 +1386,8 @@
 
   DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
 }
-
-// Check that the location bar is shown correctly for HTTP bookmark apps.
-IN_PROC_BROWSER_TEST_F(BrowserTest, ShouldShowLocationBarForHTTPBookmarkApp) {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableNewBookmarkApps);
-  ASSERT_TRUE(test_server()->Start());
-
-  // Load a http bookmark app.
-  const Extension* http_bookmark_app = InstallExtensionWithSourceAndFlags(
-      test_data_dir_.AppendASCII("app/"),
-      1,
-      extensions::Manifest::INTERNAL,
-      extensions::Extension::FROM_BOOKMARK);
-  ASSERT_TRUE(http_bookmark_app);
-
-  // Launch it in a window.
-  WebContents* app_window = OpenApplication(AppLaunchParams(
-      browser()->profile(), http_bookmark_app,
-      extensions::LAUNCH_CONTAINER_WINDOW, NEW_WINDOW,
-      extensions::SOURCE_TEST));
-  ASSERT_TRUE(app_window);
-
-  // Find the new browser.
-  Browser* http_app_browser = NULL;
-  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
-    std::string app_id =
-        web_app::GetExtensionIdFromApplicationName((*it)->app_name());
-    if (*it == browser()) {
-      continue;
-    } else if (app_id == http_bookmark_app->id()) {
-      http_app_browser = *it;
-    }
-  }
-  ASSERT_TRUE(http_app_browser);
-  ASSERT_TRUE(http_app_browser != browser());
-
-  // Navigate to the app's launch page; the location bar should be hidden.
-  NavigateAndCheckForLocationBar(
-      http_app_browser, "http://www.example.com/empty.html", false);
-
-  // Navigate to another page on the same origin; the location bar should still
-  // hidden.
-  NavigateAndCheckForLocationBar(
-      http_app_browser, "http://www.example.com/blah", false);
-
-  // Navigate to the https version of the site; the location bar should
-  // be hidden for both browsers.
-  NavigateAndCheckForLocationBar(
-      http_app_browser, "https://www.example.com/blah", false);
-
-  // Navigate to different origin; the location bar should now be visible.
-  NavigateAndCheckForLocationBar(
-      http_app_browser, "http://www.foo.com/blah", true);
-
-  // Navigate back to the app's origin; the location bar should now be hidden.
-  NavigateAndCheckForLocationBar(
-      http_app_browser, "http://www.example.com/blah", false);
-}
-
-// Check that the location bar is shown correctly for HTTPS bookmark apps.
-IN_PROC_BROWSER_TEST_F(BrowserTest, ShouldShowLocationBarForHTTPSBookmarkApp) {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableNewBookmarkApps);
-  ASSERT_TRUE(test_server()->Start());
-
-  // Load a https bookmark app.
-  const Extension* https_bookmark_app = InstallExtensionWithSourceAndFlags(
-      test_data_dir_.AppendASCII("https_app/"),
-      1,
-      extensions::Manifest::INTERNAL,
-      extensions::Extension::FROM_BOOKMARK);
-  ASSERT_TRUE(https_bookmark_app);
-
-  // Launch it in a window.
-  WebContents* app_window = OpenApplication(AppLaunchParams(
-      browser()->profile(), https_bookmark_app,
-      extensions::LAUNCH_CONTAINER_WINDOW, NEW_WINDOW,
-      extensions::SOURCE_TEST));
-  ASSERT_TRUE(app_window);
-
-  // Find the new browser.
-  Browser* https_app_browser = NULL;
-  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
-    std::string app_id =
-        web_app::GetExtensionIdFromApplicationName((*it)->app_name());
-    if (*it == browser()) {
-      continue;
-    } else if (app_id == https_bookmark_app->id()) {
-      https_app_browser = *it;
-    }
-  }
-  ASSERT_TRUE(https_app_browser);
-  ASSERT_TRUE(https_app_browser != browser());
-
-  // Navigate to the app's launch page; the location bar should be hidden.
-  NavigateAndCheckForLocationBar(
-      https_app_browser, "https://www.example.com/empty.html", false);
-
-  // Navigate to another page on the same origin; the location bar should still
-  // hidden.
-  NavigateAndCheckForLocationBar(
-      https_app_browser, "https://www.example.com/blah", false);
-
-  // Navigate to the http version of the site; the location bar should
-  // be visible for the https version as it is now on a less secure version
-  // of its host.
-  NavigateAndCheckForLocationBar(
-      https_app_browser, "http://www.example.com/blah", true);
-
-  // Navigate to different origin; the location bar should now be visible.
-  NavigateAndCheckForLocationBar(
-      https_app_browser, "http://www.foo.com/blah", true);
-
-  // Navigate back to the app's origin; the location bar should now be hidden.
-  NavigateAndCheckForLocationBar(
-      https_app_browser, "https://www.example.com/blah", false);
-}
 #endif  // !defined(OS_MACOSX)
 
-// Open a normal browser window, a hosted app window, a legacy packaged app
-// window and a dev tools window, and check that the web app frame feature is
-// supported correctly.
-IN_PROC_BROWSER_TEST_F(BrowserTest, ShouldUseWebAppFrame) {
-  ASSERT_TRUE(test_server()->Start());
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableWebAppFrame);
-
-  // Load a hosted app.
-  host_resolver()->AddRule("www.example.com", "127.0.0.1");
-  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));
-  const Extension* hosted_app = GetExtension();
-
-  // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
-  WebContents* hosted_app_window = OpenApplication(AppLaunchParams(
-      browser()->profile(), hosted_app, extensions::LAUNCH_CONTAINER_WINDOW,
-      NEW_WINDOW, extensions::SOURCE_UNTRACKED));
-  ASSERT_TRUE(hosted_app_window);
-
-  //  Load a packaged app.
-  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("packaged_app/")));
-  const Extension* packaged_app = nullptr;
-  extensions::ExtensionRegistry* registry =
-      extensions::ExtensionRegistry::Get(browser()->profile());
-  for (const scoped_refptr<const extensions::Extension>& extension :
-       registry->enabled_extensions()) {
-    if (extension->name() == "Packaged App Test")
-      packaged_app = extension.get();
-  }
-  ASSERT_TRUE(packaged_app);
-
-  // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
-  WebContents* packaged_app_window = OpenApplication(AppLaunchParams(
-      browser()->profile(), packaged_app, extensions::LAUNCH_CONTAINER_WINDOW,
-      NEW_WINDOW, extensions::SOURCE_UNTRACKED));
-  ASSERT_TRUE(packaged_app_window);
-
-  DevToolsWindow* devtools_window =
-      DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), false);
-
-  // The launch should have created a new app browser and a dev tools browser.
-  ASSERT_EQ(4u, chrome::GetBrowserCount(browser()->profile(),
-                                        browser()->host_desktop_type()));
-
-  // Find the new browsers.
-  Browser* hosted_app_browser = NULL;
-  Browser* packaged_app_browser = NULL;
-  Browser* dev_tools_browser = NULL;
-  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
-    if (*it == browser()) {
-      continue;
-    } else if ((*it)->app_name() == DevToolsWindow::kDevToolsApp) {
-      dev_tools_browser = *it;
-    } else if ((*it)->tab_strip_model()->GetActiveWebContents() ==
-               hosted_app_window) {
-      hosted_app_browser = *it;
-    } else {
-      packaged_app_browser = *it;
-    }
-  }
-  ASSERT_TRUE(dev_tools_browser);
-  ASSERT_TRUE(hosted_app_browser);
-  ASSERT_TRUE(hosted_app_browser != browser());
-  ASSERT_TRUE(packaged_app_browser);
-  ASSERT_TRUE(packaged_app_browser != browser());
-  ASSERT_TRUE(packaged_app_browser != hosted_app_browser);
-
-  EXPECT_FALSE(browser()->SupportsWindowFeature(Browser::FEATURE_WEBAPPFRAME));
-  EXPECT_FALSE(
-      dev_tools_browser->SupportsWindowFeature(Browser::FEATURE_WEBAPPFRAME));
-  EXPECT_EQ(
-      browser()->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH,
-      hosted_app_browser->SupportsWindowFeature(Browser::FEATURE_WEBAPPFRAME));
-  EXPECT_FALSE(packaged_app_browser->SupportsWindowFeature(
-      Browser::FEATURE_WEBAPPFRAME));
-
-  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
-}
-
 // Tests that the CLD (Compact Language Detection) works properly.
 IN_PROC_BROWSER_TEST_F(BrowserTest, PageLanguageDetection) {
   scoped_ptr<test::CldDataHarness> cld_data_harness =
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 0d9df2a..a7b5229 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -570,7 +570,6 @@
     case IDC_ENCODING_WINDOWS1252:
     case IDC_ENCODING_GBK:
     case IDC_ENCODING_GB18030:
-    case IDC_ENCODING_BIG5HKSCS:
     case IDC_ENCODING_BIG5:
     case IDC_ENCODING_KOREAN:
     case IDC_ENCODING_SHIFTJIS:
@@ -585,6 +584,7 @@
     case IDC_ENCODING_WINDOWS1251:
     case IDC_ENCODING_KOI8R:
     case IDC_ENCODING_KOI8U:
+    case IDC_ENCODING_IBM866:
     case IDC_ENCODING_ISO88597:
     case IDC_ENCODING_WINDOWS1253:
     case IDC_ENCODING_ISO88594:
@@ -919,7 +919,6 @@
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_WINDOWS1252, true);
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_GBK, true);
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_GB18030, true);
-  command_updater_.UpdateCommandEnabled(IDC_ENCODING_BIG5HKSCS, true);
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_BIG5, true);
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_THAI, true);
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_KOREAN, true);
@@ -934,6 +933,7 @@
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_WINDOWS1251, true);
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_KOI8R, true);
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_KOI8U, true);
+  command_updater_.UpdateCommandEnabled(IDC_ENCODING_IBM866, true);
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_ISO88597, true);
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_WINDOWS1253, true);
   command_updater_.UpdateCommandEnabled(IDC_ENCODING_ISO88594, true);
diff --git a/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.mm b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.mm
index f809da5..b437f6b 100644
--- a/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.mm
+++ b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.mm
@@ -398,7 +398,7 @@
     // will be changed when another window becomes main. Otherwise, restore the
     // Chrome menu.
     for (NSWindow* w : [NSApp windows]) {
-      if ([w canBecomeMainWindow] && ![w isEqual:window])
+      if ([w canBecomeMainWindow] && ![w isEqual:window] && [w isOnActiveSpace])
         return;
     }
 
diff --git a/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.h b/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.h
index b03818f..06934de 100644
--- a/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.h
+++ b/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.h
@@ -55,6 +55,8 @@
 - (id)initWithBridge:(autofill::CardUnmaskPromptViewBridge*)bridge;
 
 - (void)setProgressOverlayText:(const base::string16&)text;
+- (void)setRetriableErrorMessage:(const base::string16&)text;
+- (void)setPermanentErrorMessage:(const base::string16&)text;
 
 @end
 
diff --git a/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm b/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm
index ba5e6c62..f8d9a81 100644
--- a/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm
+++ b/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm
@@ -6,17 +6,20 @@
 #include "base/message_loop/message_loop.h"
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/browser/ui/autofill/autofill_dialog_models.h"
+#include "chrome/browser/ui/autofill/autofill_dialog_types.h"
 #include "chrome/browser/ui/autofill/card_unmask_prompt_controller.h"
+#include "chrome/browser/ui/chrome_style.h"
 #import "chrome/browser/ui/cocoa/autofill/autofill_tooltip_controller.h"
 #include "chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.h"
 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h"
-#include "chrome/browser/ui/chrome_style.h"
 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h"
 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_window.h"
 #import "chrome/browser/ui/cocoa/key_equivalent_constants.h"
+#import "chrome/browser/ui/cocoa/l10n_util.h"
 #include "grit/generated_resources.h"
 #include "grit/theme_resources.h"
+#include "skia/ext/skia_utils_mac.h"
 #include "ui/base/cocoa/window_size_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -25,8 +28,12 @@
 const CGFloat kButtonGap = 6.0f;
 const CGFloat kDialogContentMinWidth = 210.0f;
 const CGFloat kCvcInputWidth = 64.0f;
+const SkColor kPermanentErrorTextColor = SK_ColorWHITE;
+const SkColor kPermanentErrorBackgroundColor = SkColorSetRGB(0xd3, 0x2f, 0x2f);
 const ui::ResourceBundle::FontStyle kProgressFontStyle =
     chrome_style::kTitleFontStyle;
+const ui::ResourceBundle::FontStyle kErrorFontStyle =
+    chrome_style::kTextFontStyle;
 
 }  // namespace
 
@@ -84,6 +91,14 @@
         base::TimeDelta::FromSeconds(1));
   } else {
     [view_controller_ setProgressOverlayText:base::string16()];
+
+    if (allow_retry) {
+      // TODO(bondd): Views version never hides |errorLabel_|. When Views
+      // decides when to hide it then do the same thing here.
+      [view_controller_ setRetriableErrorMessage:error_message];
+    } else {
+      [view_controller_ setPermanentErrorMessage:error_message];
+    }
   }
 }
 
@@ -107,14 +122,22 @@
 #pragma mark CardUnmaskPromptViewCocoa
 
 @implementation CardUnmaskPromptViewCocoa {
+  base::scoped_nsobject<NSBox> permanentErrorBox_;
+  base::scoped_nsobject<NSView> inputRowView_;
+  base::scoped_nsobject<NSView> storageView_;
+
+  base::scoped_nsobject<NSTextField> titleLabel_;
+  base::scoped_nsobject<NSTextField> permanentErrorLabel_;
+  base::scoped_nsobject<NSTextField> instructionsLabel_;
   base::scoped_nsobject<NSTextField> cvcInput_;
   base::scoped_nsobject<NSPopUpButton> monthPopup_;
   base::scoped_nsobject<NSPopUpButton> yearPopup_;
+  base::scoped_nsobject<NSButton> cancelButton_;
   base::scoped_nsobject<NSButton> verifyButton_;
   base::scoped_nsobject<NSButton> storageCheckbox_;
   base::scoped_nsobject<AutofillTooltipController> storageTooltip_;
-  base::scoped_nsobject<NSView> inputRow_;
-  base::scoped_nsobject<NSTextField> progressOverlayText_;
+  base::scoped_nsobject<NSTextField> errorLabel_;
+  base::scoped_nsobject<NSTextField> progressOverlayLabel_;
 
   int monthPopupDefaultIndex_;
   int yearPopupDefaultIndex_;
@@ -168,35 +191,75 @@
         constrained_window::GetAttributedLabelString(
             SysUTF16ToNSString(text), kProgressFontStyle, NSCenterTextAlignment,
             NSLineBreakByWordWrapping);
-    [progressOverlayText_ setAttributedStringValue:attributedString];
+    [progressOverlayLabel_ setAttributedStringValue:attributedString];
   }
 
-  [progressOverlayText_ setHidden:text.empty()];
-  [inputRow_ setHidden:!text.empty()];
+  [progressOverlayLabel_ setHidden:text.empty()];
+  [inputRowView_ setHidden:!text.empty()];
+  [storageView_ setHidden:!text.empty()];
   [self updateVerifyButtonEnabled];
 }
 
-- (void)updateVerifyButtonEnabled {
-  autofill::CardUnmaskPromptController* controller = bridge_->GetController();
-  DCHECK(controller);
+- (void)setInputsEnabled:(BOOL)enabled {
+  [cvcInput_ setEnabled:enabled];
+  [monthPopup_ setEnabled:enabled];
+  [yearPopup_ setEnabled:enabled];
+  [storageCheckbox_ setEnabled:enabled];
+}
 
-  BOOL enable =
-      ![inputRow_ isHidden] &&
-      controller->InputCvcIsValid(
-          base::SysNSStringToUTF16([cvcInput_ stringValue])) &&
-      (!monthPopup_ ||
-       [monthPopup_ indexOfSelectedItem] != monthPopupDefaultIndex_) &&
-      (!yearPopup_ ||
-       [yearPopup_ indexOfSelectedItem] != yearPopupDefaultIndex_);
+- (void)setRetriableErrorMessage:(const base::string16&)text {
+  NSAttributedString* attributedString =
+      constrained_window::GetAttributedLabelString(
+          SysUTF16ToNSString(text), kErrorFontStyle, NSNaturalTextAlignment,
+          NSLineBreakByWordWrapping);
+  [errorLabel_ setAttributedStringValue:attributedString];
+  [self performLayoutAndDisplay:YES];
+}
+
+- (void)setPermanentErrorMessage:(const base::string16&)text {
+  if (!text.empty()) {
+    if (!permanentErrorBox_) {
+      permanentErrorBox_.reset([[NSBox alloc] initWithFrame:NSZeroRect]);
+      [permanentErrorBox_ setBoxType:NSBoxCustom];
+      [permanentErrorBox_ setBorderType:NSNoBorder];
+      [permanentErrorBox_ setTitlePosition:NSNoTitle];
+      [permanentErrorBox_ setFillColor:gfx::SkColorToCalibratedNSColor(
+                                           kPermanentErrorBackgroundColor)];
+
+      permanentErrorLabel_.reset([constrained_window::CreateLabel() retain]);
+      [permanentErrorLabel_ setAutoresizingMask:NSViewWidthSizable];
+      [permanentErrorLabel_ setTextColor:gfx::SkColorToCalibratedNSColor(
+                                             kPermanentErrorTextColor)];
+
+      [permanentErrorBox_ addSubview:permanentErrorLabel_];
+      [[self view] addSubview:permanentErrorBox_];
+    }
+
+    NSAttributedString* attributedString =
+        constrained_window::GetAttributedLabelString(
+            SysUTF16ToNSString(text), kErrorFontStyle, NSNaturalTextAlignment,
+            NSLineBreakByWordWrapping);
+    [permanentErrorLabel_ setAttributedStringValue:attributedString];
+  }
+
+  [permanentErrorBox_ setHidden:text.empty()];
+  [self setInputsEnabled:NO];
+  [self updateVerifyButtonEnabled];
+  [self setRetriableErrorMessage:base::string16()];
+}
+
+- (void)updateVerifyButtonEnabled {
+  BOOL enable = ![inputRowView_ isHidden] &&
+                ![[permanentErrorLabel_ stringValue] length] &&
+                bridge_->GetController()->InputCvcIsValid(
+                    base::SysNSStringToUTF16([cvcInput_ stringValue])) &&
+                [self expirationDateIsValid];
 
   [verifyButton_ setEnabled:enable];
 }
 
 - (void)onVerify:(id)sender {
-  autofill::CardUnmaskPromptController* controller = bridge_->GetController();
-  DCHECK(controller);
-
-  controller->OnUnmaskResponse(
+  bridge_->GetController()->OnUnmaskResponse(
       base::SysNSStringToUTF16([cvcInput_ stringValue]),
       base::SysNSStringToUTF16([monthPopup_ titleOfSelectedItem]),
       base::SysNSStringToUTF16([yearPopup_ titleOfSelectedItem]),
@@ -207,7 +270,25 @@
   bridge_->PerformClose();
 }
 
+- (BOOL)expirationDateIsValid {
+  if (!bridge_->GetController()->ShouldRequestExpirationDate())
+    return true;
+
+  return bridge_->GetController()->InputExpirationIsValid(
+      base::SysNSStringToUTF16([monthPopup_ titleOfSelectedItem]),
+      base::SysNSStringToUTF16([yearPopup_ titleOfSelectedItem]));
+}
+
 - (void)onExpirationDateChanged:(id)sender {
+  if ([self expirationDateIsValid]) {
+    [self setRetriableErrorMessage:base::string16()];
+  } else if ([monthPopup_ indexOfSelectedItem] != monthPopupDefaultIndex_ &&
+             [yearPopup_ indexOfSelectedItem] != yearPopupDefaultIndex_) {
+    [self setRetriableErrorMessage:
+              l10n_util::GetStringUTF16(
+                  IDS_AUTOFILL_CARD_UNMASK_INVALID_EXPIRATION_DATE)];
+  }
+
   [self updateVerifyButtonEnabled];
 }
 
@@ -220,7 +301,7 @@
         (autofill::CardUnmaskPromptController*)controller {
   base::scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:NSZeroRect]);
 
-  // "Store card on this device" checkbox.
+  // Add "Store card on this device" checkbox.
   storageCheckbox_.reset([[NSButton alloc] initWithFrame:NSZeroRect]);
   [storageCheckbox_ setButtonType:NSSwitchButton];
   [storageCheckbox_
@@ -232,7 +313,7 @@
   [storageCheckbox_ sizeToFit];
   [view addSubview:storageCheckbox_];
 
-  // "?" icon with tooltip.
+  // Add "?" icon with tooltip.
   storageTooltip_.reset([[AutofillTooltipController alloc]
       initWithArrowLocation:info_bubble::kTopRight]);
   [storageTooltip_ setImage:ui::ResourceBundle::GetSharedInstance()
@@ -242,15 +323,99 @@
       setMessage:base::SysUTF16ToNSString(l10n_util::GetStringUTF16(
                      IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_TOOLTIP))];
   [view addSubview:[storageTooltip_ view]];
-  [[storageTooltip_ view]
-      setFrameOrigin:NSMakePoint(NSMaxX([storageCheckbox_ frame]) + kButtonGap,
-                                 0)];
+  [[storageTooltip_ view] setFrameOrigin:
+      NSMakePoint(NSMaxX([storageCheckbox_ frame]) + kButtonGap, 0)];
 
   [CardUnmaskPromptViewCocoa sizeToFitView:view];
   [CardUnmaskPromptViewCocoa verticallyCenterSubviewsInView:view];
   return view;
 }
 
+// +---------------------------------------------------------------------------+
+// | titleLabel_        (Single line.)                                         |
+// |---------------------------------------------------------------------------|
+// | permanentErrorBox_ (Multiline, may be hidden.)                            |
+// |---------------------------------------------------------------------------|
+// | instructionsLabel_ (Multiline.)                                           |
+// |---------------------------------------------------------------------------|
+// | monthPopup_ yearPopup_ cvcInput_ cvcImage                                 |
+// |     (All enclosed in inputRowView_. Month and year may be hidden.)        |
+// |---------------------------------------------------------------------------|
+// | errorLabel_ (Multiline. Always takes up space for one line even if        |
+// |                 empty.)                                                   |
+// |---------------------------------------------------------------------------|
+// |                                                         [Cancel] [Verify] |
+// |---------------------------------------------------------------------------|
+// | storageCheckbox_ storageTooltip_                                          |
+// |     (Both enclosed in storageView_. May be hidden but still taking up     |
+// |         layout space. Will all be nil if !CanStoreLocally()).             |
+// +---------------------------------------------------------------------------+
+- (void)performLayoutAndDisplay:(BOOL)display {
+  // Calculate dialog content width.
+  CGFloat contentWidth =
+      std::max(NSWidth([titleLabel_ frame]), NSWidth([inputRowView_ frame]));
+  contentWidth = std::max(contentWidth, NSWidth([storageView_ frame]));
+  contentWidth = std::max(contentWidth, kDialogContentMinWidth);
+
+  [storageView_
+      setFrameOrigin:NSMakePoint(0, chrome_style::kClientBottomPadding)];
+
+  CGFloat verifyMinY =
+      storageView_ ? NSMaxY([storageView_ frame]) + chrome_style::kRowPadding
+                   : chrome_style::kClientBottomPadding;
+  [verifyButton_ setFrameOrigin:
+      NSMakePoint(contentWidth - NSWidth([verifyButton_ frame]), verifyMinY)];
+
+  [cancelButton_
+      setFrameOrigin:NSMakePoint(NSMinX([verifyButton_ frame]) - kButtonGap -
+                                     NSWidth([cancelButton_ frame]),
+                                 NSMinY([verifyButton_ frame]))];
+
+  [errorLabel_ setFrame:NSMakeRect(0, NSMaxY([cancelButton_ frame]) +
+                                          chrome_style::kRowPadding,
+                                   contentWidth, 0)];
+  cocoa_l10n_util::WrapOrSizeToFit(errorLabel_);
+
+  [inputRowView_ setFrameOrigin:NSMakePoint(0, NSMaxY([errorLabel_ frame]) +
+                                                   chrome_style::kRowPadding)];
+
+  [instructionsLabel_ setFrame:NSMakeRect(0, NSMaxY([inputRowView_ frame]) +
+                                                 chrome_style::kRowPadding,
+                                          contentWidth, 0)];
+  cocoa_l10n_util::WrapOrSizeToFit(instructionsLabel_);
+
+  // Layout permanent error box.
+  CGFloat minY = NSMaxY([instructionsLabel_ frame]) + chrome_style::kRowPadding;
+  if (permanentErrorBox_ && ![permanentErrorBox_ isHidden]) {
+    [permanentErrorBox_ setFrame:NSMakeRect(0, minY, contentWidth, 0)];
+    cocoa_l10n_util::WrapOrSizeToFit(permanentErrorLabel_);
+    [permanentErrorBox_ sizeToFit];
+    minY = NSMaxY([permanentErrorBox_ frame]) + chrome_style::kRowPadding;
+  }
+
+  [titleLabel_ setFrameOrigin:NSMakePoint(0, minY)];
+
+  // Center progressOverlayLabel_ vertically within inputRowView_ frame.
+  CGFloat progressHeight = ui::ResourceBundle::GetSharedInstance()
+                               .GetFont(kProgressFontStyle)
+                               .GetHeight();
+  [progressOverlayLabel_
+      setFrame:NSMakeRect(0, ceil(NSMidY([inputRowView_ frame]) -
+                                  progressHeight / 2.0),
+                          contentWidth, progressHeight)];
+
+  // Set dialog size.
+  [[self view]
+      setFrameSize:NSMakeSize(
+                       contentWidth + chrome_style::kHorizontalPadding * 2.0,
+                       NSMaxY([titleLabel_ frame]) +
+                           chrome_style::kTitleTopPadding)];
+
+  NSRect frameRect =
+      [[[self view] window] frameRectForContentRect:[[self view] frame]];
+  [[[self view] window] setFrame:frameRect display:display];
+}
+
 - (void)loadView {
   autofill::CardUnmaskPromptController* controller = bridge_->GetController();
   DCHECK(controller);
@@ -263,40 +428,45 @@
   [mainView
       setContentViewMargins:NSMakeSize(chrome_style::kHorizontalPadding, 0)];
 
-  inputRow_.reset([[NSView alloc] initWithFrame:NSZeroRect]);
-  [mainView addSubview:inputRow_];
+  inputRowView_.reset([[NSView alloc] initWithFrame:NSZeroRect]);
+  [mainView addSubview:inputRowView_];
 
-  base::scoped_nsobject<NSView> storageView(
-      [self createStorageViewWithController:controller]);
-  [mainView addSubview:storageView];
+  if (controller->CanStoreLocally()) {
+    storageView_ = [self createStorageViewWithController:controller];
+    [mainView addSubview:storageView_];
+  }
 
-  // Title label.
-  NSTextField* title = constrained_window::CreateLabel();
+  progressOverlayLabel_.reset([constrained_window::CreateLabel() retain]);
+  [progressOverlayLabel_ setHidden:YES];
+  [mainView addSubview:progressOverlayLabel_];
+
+  // Add title label.
+  titleLabel_.reset([constrained_window::CreateLabel() retain]);
   NSAttributedString* titleString =
       constrained_window::GetAttributedLabelString(
           SysUTF16ToNSString(controller->GetWindowTitle()),
           chrome_style::kTitleFontStyle, NSNaturalTextAlignment,
           NSLineBreakByWordWrapping);
-  [title setAttributedStringValue:titleString];
-  [title sizeToFit];
-  [mainView addSubview:title];
+  [titleLabel_ setAttributedStringValue:titleString];
+  [titleLabel_ sizeToFit];
+  [mainView addSubview:titleLabel_];
 
-  // Instructions label.
-  NSTextField* instructions = constrained_window::CreateLabel();
+  // Add instructions label.
+  instructionsLabel_.reset([constrained_window::CreateLabel() retain]);
   NSAttributedString* instructionsString =
       constrained_window::GetAttributedLabelString(
           SysUTF16ToNSString(controller->GetInstructionsMessage()),
           chrome_style::kTextFontStyle, NSNaturalTextAlignment,
           NSLineBreakByWordWrapping);
-  [instructions setAttributedStringValue:instructionsString];
-  [mainView addSubview:instructions];
+  [instructionsLabel_ setAttributedStringValue:instructionsString];
+  [mainView addSubview:instructionsLabel_];
 
-  // Expiration date.
+  // Add expiration date.
   base::scoped_nsobject<NSView> expirationView;
   if (controller->ShouldRequestExpirationDate()) {
     expirationView.reset([[NSView alloc] initWithFrame:NSZeroRect]);
 
-    // Month.
+    // Add expiration month.
     autofill::MonthComboboxModel monthModel;
     monthPopupDefaultIndex_ = monthModel.GetDefaultIndex();
     monthPopup_.reset(
@@ -305,7 +475,7 @@
     [monthPopup_ setAction:@selector(onExpirationDateChanged:)];
     [expirationView addSubview:monthPopup_];
 
-    // Year.
+    // Add expiration year.
     autofill::YearComboboxModel yearModel;
     yearPopupDefaultIndex_ = yearModel.GetDefaultIndex();
     yearPopup_.reset(
@@ -322,10 +492,10 @@
         NSUnionRect([monthPopup_ frame], [yearPopup_ frame]);
     expirationFrame.size.width += kButtonGap;
     [expirationView setFrame:expirationFrame];
-    [inputRow_ addSubview:expirationView];
+    [inputRowView_ addSubview:expirationView];
   }
 
-  // CVC text input.
+  // Add CVC text input.
   cvcInput_.reset([[NSTextField alloc] initWithFrame:NSZeroRect]);
   [[cvcInput_ cell]
       setPlaceholderString:l10n_util::GetNSString(
@@ -335,9 +505,9 @@
   [cvcInput_ sizeToFit];
   [cvcInput_ setFrame:NSMakeRect(NSMaxX([expirationView frame]), 0,
                                  kCvcInputWidth, NSHeight([cvcInput_ frame]))];
-  [inputRow_ addSubview:cvcInput_];
+  [inputRowView_ addSubview:cvcInput_];
 
-  // CVC image.
+  // Add CVC image.
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   NSImage* cvcImage =
       rb.GetNativeImageNamed(controller->GetCvcImageRid()).ToNSImage();
@@ -347,19 +517,25 @@
   [cvcImageView setFrameSize:[cvcImage size]];
   [cvcImageView
       setFrameOrigin:NSMakePoint(NSMaxX([cvcInput_ frame]) + kButtonGap, 0)];
-  [inputRow_ addSubview:cvcImageView];
+  [inputRowView_ addSubview:cvcImageView];
 
-  // Cancel button.
-  base::scoped_nsobject<NSButton> cancelButton(
+  // Add error message label.
+  errorLabel_.reset([constrained_window::CreateLabel() retain]);
+  [errorLabel_
+      setTextColor:gfx::SkColorToCalibratedNSColor(autofill::kWarningColor)];
+  [mainView addSubview:errorLabel_];
+
+  // Add cancel button.
+  cancelButton_.reset(
       [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]);
-  [cancelButton setTitle:l10n_util::GetNSStringWithFixup(IDS_CANCEL)];
-  [cancelButton setKeyEquivalent:kKeyEquivalentEscape];
-  [cancelButton setTarget:self];
-  [cancelButton setAction:@selector(onCancel:)];
-  [cancelButton sizeToFit];
-  [mainView addSubview:cancelButton];
+  [cancelButton_ setTitle:l10n_util::GetNSStringWithFixup(IDS_CANCEL)];
+  [cancelButton_ setKeyEquivalent:kKeyEquivalentEscape];
+  [cancelButton_ setTarget:self];
+  [cancelButton_ setAction:@selector(onCancel:)];
+  [cancelButton_ sizeToFit];
+  [mainView addSubview:cancelButton_];
 
-  // Verify button.
+  // Add verify button.
   verifyButton_.reset(
       [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]);
   // TODO(bondd): use l10n string.
@@ -371,66 +547,12 @@
   [self updateVerifyButtonEnabled];
   [mainView addSubview:verifyButton_];
 
-  // Layout inputRow_.
-  [CardUnmaskPromptViewCocoa sizeToFitView:inputRow_];
-  [CardUnmaskPromptViewCocoa verticallyCenterSubviewsInView:inputRow_];
-
-  // Calculate dialog content width.
-  CGFloat contentWidth =
-      std::max(NSWidth([title frame]), NSWidth([inputRow_ frame]));
-  contentWidth = std::max(contentWidth, NSWidth([storageView frame]));
-  contentWidth = std::max(contentWidth, kDialogContentMinWidth);
-
-  // Layout mainView contents, starting at the bottom and moving up.
-
-  [storageView
-      setFrameOrigin:NSMakePoint(0, chrome_style::kClientBottomPadding)];
-
-  // Verify and Cancel buttons.
-  [verifyButton_
-      setFrameOrigin:NSMakePoint(contentWidth - NSWidth([verifyButton_ frame]),
-                                 NSMaxY([storageView frame]) +
-                                     chrome_style::kRowPadding)];
-
-  [cancelButton
-      setFrameOrigin:NSMakePoint(NSMinX([verifyButton_ frame]) - kButtonGap -
-                                     NSWidth([cancelButton frame]),
-                                 NSMinY([verifyButton_ frame]))];
-
-  // Input row.
-  [inputRow_ setFrameOrigin:NSMakePoint(0, NSMaxY([cancelButton frame]) +
-                                               chrome_style::kRowPadding)];
-
-  // Instruction label.
-  [instructions setFrameOrigin:NSMakePoint(0, NSMaxY([inputRow_ frame]) +
-                                                  chrome_style::kRowPadding)];
-  NSSize instructionsSize = [[instructions cell]
-      cellSizeForBounds:NSMakeRect(0, 0, contentWidth, CGFLOAT_MAX)];
-  [instructions setFrameSize:instructionsSize];
-
-  // Title label.
-  [title setFrameOrigin:NSMakePoint(0, NSMaxY([instructions frame]) +
-                                           chrome_style::kRowPadding)];
-
-  // Dialog size.
-  [mainView
-      setFrameSize:NSMakeSize(
-                       contentWidth + [mainView contentViewMargins].width * 2.0,
-                       NSMaxY([title frame]) + chrome_style::kTitleTopPadding)];
-
-  // Add progress overlay.
-  progressOverlayText_.reset([constrained_window::CreateLabel() retain]);
-  CGFloat progressHeight = ui::ResourceBundle::GetSharedInstance()
-                               .GetFont(kProgressFontStyle)
-                               .GetHeight();
-  // Center the text vertically within inputRow_ frame.
-  [progressOverlayText_ setFrame:NSMakeRect(0, ceil(NSMidY([inputRow_ frame]) -
-                                                    progressHeight / 2.0),
-                                            contentWidth, progressHeight)];
-  [progressOverlayText_ setHidden:YES];
-  [mainView addSubview:progressOverlayText_];
+  // Layout inputRowView_.
+  [CardUnmaskPromptViewCocoa sizeToFitView:inputRowView_];
+  [CardUnmaskPromptViewCocoa verticallyCenterSubviewsInView:inputRowView_];
 
   [self setView:mainView];
+  [self performLayoutAndDisplay:NO];
 }
 
 @end
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.mm
index 1da08e0..0cc3fa0 100644
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.mm
+++ b/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.mm
@@ -20,12 +20,15 @@
     id<ConstrainedWindowSheet> sheet)
     : delegate_(delegate) {
   DCHECK(sheet);
-  extensions::GuestViewBase* guest_view =
-      extensions::GuestViewBase::FromWebContents(web_contents);
-  // For embedded WebContents, use the embedder's WebContents for constrained
-  // window.
-  web_contents = guest_view && guest_view->embedder_web_contents() ?
-                    guest_view->embedder_web_contents() : web_contents;
+
+  // |web_contents| may be embedded within a chain of nested GuestViews. If it
+  // is, follow the chain of embedders to the outermost WebContents and use it.
+  while (extensions::GuestViewBase* guest_view =
+             extensions::GuestViewBase::FromWebContents(web_contents)) {
+    if (!guest_view->embedder_web_contents())
+      break;
+    web_contents = guest_view->embedder_web_contents();
+  }
 
   auto manager = WebContentsModalDialogManager::FromWebContents(web_contents);
   scoped_ptr<SingleWebContentsDialogManagerCocoa> native_manager(
diff --git a/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa.mm b/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa.mm
index 65937f8..189962f6 100644
--- a/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa.mm
+++ b/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa.mm
@@ -210,7 +210,7 @@
 - (void)initializeBlockedPluginsList;
 - (void)initializeTitle;
 - (void)initializeRadioGroup;
-- (void)initializePopupList;
+- (void)initializeItemList;
 - (void)initializeGeoLists;
 - (void)initializeMediaMenus;
 - (void)initializeMIDISysExLists;
@@ -384,16 +384,20 @@
 }
 
 - (void)initializeBlockedPluginsList {
-  NSString* label = base::SysUTF16ToNSString(
-      contentSettingBubbleModel_->bubble_content().plugin_names);
-  [blockedResourcesField_ setStringValue:label];
+  // Hide the empty label at the top of the dialog.
+  int delta =
+      NSMinY([titleLabel_ frame]) - NSMinY([blockedResourcesField_ frame]);
+  [blockedResourcesField_ removeFromSuperview];
+  NSRect frame = [[self window] frame];
+  frame.size.height -= delta;
+  [[self window] setFrame:frame display:NO];
 }
 
-- (void)initializePopupList {
+- (void)initializeItemList {
   // I didn't put the buttons into a NSMatrix because then they are only one
   // entity in the key view loop. This way, one can tab through all of them.
-  const ContentSettingBubbleModel::PopupItems& popupItems =
-      contentSettingBubbleModel_->bubble_content().popup_items;
+  const ContentSettingBubbleModel::ListItems& listItems =
+      contentSettingBubbleModel_->bubble_content().list_items;
 
   // Get the pre-resize frame of the radio group. Its origin is where the
   // popup list should go.
@@ -403,37 +407,36 @@
   // themselves when the window is enlarged.
   // Heading and radio box are already 1 * kLinkOuterPadding apart in the nib,
   // so only 1 * kLinkOuterPadding more is needed.
-  int delta = popupItems.size() * kLinkLineHeight - kLinkPadding +
-              kLinkOuterPadding;
+  int delta =
+      listItems.size() * kLinkLineHeight - kLinkPadding + kLinkOuterPadding;
   NSSize deltaSize = NSMakeSize(0, delta);
   deltaSize = [[[self window] contentView] convertSize:deltaSize toView:nil];
   NSRect windowFrame = [[self window] frame];
   windowFrame.size.height += deltaSize.height;
   [[self window] setFrame:windowFrame display:NO];
 
-  // Create popup list.
+  // Create item list.
   int topLinkY = NSMaxY(radioFrame) + delta - kLinkHeight;
   int row = 0;
-  for (std::vector<ContentSettingBubbleModel::PopupItem>::const_iterator
-       it(popupItems.begin()); it != popupItems.end(); ++it, ++row) {
-    NSImage* image = it->image.AsNSImage();
-
-    std::string title(it->title);
-    // The popup may not have committed a load yet, in which case it won't
-    // have a URL or title.
-    if (title.empty())
-      title = l10n_util::GetStringUTF8(IDS_TAB_LOADING_TITLE);
-
-    NSRect linkFrame =
-        NSMakeRect(NSMinX(radioFrame), topLinkY - kLinkLineHeight * row,
-                   200, kLinkHeight);
-    NSButton* button = [self
-        hyperlinkButtonWithFrame:linkFrame
-                           title:base::SysUTF8ToNSString(title)
-                            icon:image
-                  referenceFrame:radioFrame];
-    [[self bubble] addSubview:button];
-    popupLinks_[button] = row;
+  for (const ContentSettingBubbleModel::ListItem& listItem : listItems) {
+    NSImage* image = listItem.image.AsNSImage();
+    NSRect frame = NSMakeRect(
+        NSMinX(radioFrame), topLinkY - kLinkLineHeight * row, 200, kLinkHeight);
+    if (listItem.has_link) {
+      NSButton* button =
+          [self hyperlinkButtonWithFrame:frame
+                                   title:base::SysUTF8ToNSString(listItem.title)
+                                    icon:image
+                          referenceFrame:radioFrame];
+      [[self bubble] addSubview:button];
+      popupLinks_[button] = row++;
+    } else {
+      NSTextField* label =
+          LabelWithFrame(base::SysUTF8ToNSString(listItem.title), frame);
+      SetControlSize(label, NSSmallControlSize);
+      [[self bubble] addSubview:label];
+      row++;
+    }
   }
 }
 
@@ -531,12 +534,13 @@
   CGFloat maxMenuWidth = 0;
   CGFloat maxMenuHeight = 0;
   NSRect radioFrame = [allowBlockRadioGroup_ frame];
-  for (ContentSettingBubbleModel::MediaMenuMap::const_iterator it(
-       media_menus.begin()); it != media_menus.end(); ++it) {
+  for (const std::pair<content::MediaStreamType,
+                       ContentSettingBubbleModel::MediaMenu>& map_entry :
+       media_menus) {
     // |labelFrame| will be resized later on in this function.
     NSRect labelFrame = NSMakeRect(NSMinX(radioFrame), 0, 0, 0);
-    NSTextField* label =
-        LabelWithFrame(base::SysUTF8ToNSString(it->second.label), labelFrame);
+    NSTextField* label = LabelWithFrame(
+        base::SysUTF8ToNSString(map_entry.second.label), labelFrame);
     SetControlSize(label, NSSmallControlSize);
     NSCell* cell = [label cell];
     [cell setAlignment:NSRightTextAlignment];
@@ -553,15 +557,14 @@
     // Store the |label| and |button| into MediaMenuParts struct and build
     // the popup menu from the menu model.
     content_setting_bubble::MediaMenuParts* menuParts =
-        new content_setting_bubble::MediaMenuParts(it->first, label);
+        new content_setting_bubble::MediaMenuParts(map_entry.first, label);
     menuParts->model.reset(new ContentSettingMediaMenuModel(
-        it->first, contentSettingBubbleModel_.get(),
+        map_entry.first, contentSettingBubbleModel_.get(),
         ContentSettingMediaMenuModel::MenuLabelChangedCallback()));
     mediaMenus_[button] = menuParts;
-    CGFloat width = BuildPopUpMenuFromModel(button,
-                                            menuParts->model.get(),
-                                            it->second.selected_device.name,
-                                            it->second.disabled);
+    CGFloat width = BuildPopUpMenuFromModel(
+        button, menuParts->model.get(), map_entry.second.selected_device.name,
+        map_entry.second.disabled);
     maxMenuWidth = std::max(maxMenuWidth, width);
 
     [[self bubble] addSubview:button
@@ -593,20 +596,20 @@
   // Resize and reposition the media menus layout.
   CGFloat topMenuY = NSMinY(radioFrame) - kMediaMenuVerticalPadding;
   maxMenuWidth = std::max(maxMenuWidth, kMinMediaMenuButtonWidth);
-  for (content_setting_bubble::MediaMenuPartsMap::const_iterator i =
-       mediaMenus_.begin(); i != mediaMenus_.end(); ++i) {
-    NSRect labelFrame = [i->second->label frame];
+  for (const std::pair<NSPopUpButton*, content_setting_bubble::MediaMenuParts*>&
+           map_entry : mediaMenus_) {
+    NSRect labelFrame = [map_entry.second->label frame];
     // Align the label text with the button text.
     labelFrame.origin.y =
         topMenuY + (maxMenuHeight - labelFrame.size.height) / 2 + 1;
     labelFrame.size.width = maxLabelWidth;
-    [i->second->label setFrame:labelFrame];
-    NSRect menuFrame = [i->first frame];
+    [map_entry.second->label setFrame:labelFrame];
+    NSRect menuFrame = [map_entry.first frame];
     menuFrame.origin.y = topMenuY;
     menuFrame.origin.x = NSMinX(radioFrame) + maxLabelWidth;
     menuFrame.size.width = maxMenuWidth;
     menuFrame.size.height = maxMenuHeight;
-    [i->first setFrame:menuFrame];
+    [map_entry.first setFrame:menuFrame];
     topMenuY -= (maxMenuHeight + kMediaMenuElementVerticalPadding);
   }
 }
@@ -748,8 +751,9 @@
   if (allowBlockRadioGroup_)  // not bound in cookie bubble xib
     [self initializeRadioGroup];
 
-  if (type == CONTENT_SETTINGS_TYPE_POPUPS)
-    [self initializePopupList];
+  if (type == CONTENT_SETTINGS_TYPE_POPUPS ||
+      type == CONTENT_SETTINGS_TYPE_PLUGINS)
+    [self initializeItemList];
   if (type == CONTENT_SETTINGS_TYPE_GEOLOCATION)
     [self initializeGeoLists];
   if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM)
@@ -769,7 +773,7 @@
 - (void)popupLinkClicked:(id)sender {
   content_setting_bubble::PopupLinks::iterator i(popupLinks_.find(sender));
   DCHECK(i != popupLinks_.end());
-  contentSettingBubbleModel_->OnPopupClicked(i->second);
+  contentSettingBubbleModel_->OnListItemClicked(i->second);
 }
 
 - (void)clearGeolocationForCurrentHost:(id)sender {
diff --git a/chrome/browser/ui/cocoa/passwords/credential_item_view.h b/chrome/browser/ui/cocoa/passwords/credential_item_view.h
index c032759d..8bb1b4f 100644
--- a/chrome/browser/ui/cocoa/passwords/credential_item_view.h
+++ b/chrome/browser/ui/cocoa/passwords/credential_item_view.h
@@ -9,7 +9,7 @@
 
 #import "base/mac/scoped_nsobject.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 
 @class CredentialItemView;
 class GURL;
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_account_chooser_view_controller.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_account_chooser_view_controller.mm
index 89eb767..456f3ee 100644
--- a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_account_chooser_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_account_chooser_view_controller.mm
@@ -13,7 +13,7 @@
 #include "chrome/browser/ui/passwords/account_chooser_more_combobox_model.h"
 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "ui/base/l10n/l10n_util.h"
 
 @implementation CredentialItemCell {
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.mm
index fd1a36b7..8f9560d 100644
--- a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.mm
@@ -7,9 +7,11 @@
 #import "chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.h"
 
 #include "base/strings/sys_string_conversions.h"
+#include "chrome/browser/profiles/profile.h"
 #import "chrome/browser/ui/cocoa/bubble_combobox.h"
 #import "chrome/browser/ui/cocoa/passwords/manage_password_item_view_controller.h"
 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
+#include "chrome/browser/ui/passwords/password_bubble_experiment.h"
 #include "chrome/browser/ui/passwords/save_password_refusal_combobox_model.h"
 #include "chrome/grit/generated_resources.h"
 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
@@ -101,25 +103,24 @@
                 action:@selector(onSaveClicked:)] retain]);
 
   // Nope combobox.
-  SavePasswordRefusalComboboxModel comboboxModel;
+  SavePasswordRefusalComboboxModel comboboxModel(
+      password_bubble_experiment::ShouldShowNeverForThisSiteDefault(
+          model_->GetProfile()->GetPrefs()));
   nopeButton_.reset([[BubbleCombobox alloc] initWithFrame:NSZeroRect
                                                 pullsDown:YES
                                                     model:&comboboxModel]);
   NSMenuItem* nopeItem =
-      [nopeButton_ itemAtIndex:SavePasswordRefusalComboboxModel::INDEX_NOPE];
+      [nopeButton_ itemAtIndex:comboboxModel.index_nope()];
   [nopeItem setTarget:self];
   [nopeItem setAction:@selector(onNopeClicked:)];
   NSMenuItem* neverItem = [nopeButton_
-      itemAtIndex:SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE];
+      itemAtIndex:comboboxModel.index_never()];
   [neverItem setTarget:self];
   [neverItem setAction:@selector(onNeverForThisSiteClicked:)];
 
-  // Insert a dummy row at the top and set its title to the desired title.
-  // Must be done in two steps to prevent NSPopUpButton from removing
-  // duplicates.
+  // Insert a dummy row at the top and select the right item.
   [nopeButton_ insertItemWithTitle:@"" atIndex:0];
-  [[nopeButton_ itemAtIndex:0]
-      setTitle:l10n_util::GetNSString(IDS_PASSWORD_MANAGER_CANCEL_BUTTON)];
+  [nopeButton_ setTitle:base::SysUTF16ToNSString(comboboxModel.GetItemAt(0))];
 
   [nopeButton_ sizeToFit];
   [view addSubview:nopeButton_.get()];
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller_unittest.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller_unittest.mm
index 3671cf2..6410d48 100644
--- a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller_unittest.mm
@@ -52,7 +52,7 @@
 class ManagePasswordsBubblePendingViewControllerTest
     : public ManagePasswordsControllerTest {
  public:
-  ManagePasswordsBubblePendingViewControllerTest() : controller_(nil) {}
+  ManagePasswordsBubblePendingViewControllerTest() : combobox_model_(false) {}
 
   void SetUp() override {
     ManagePasswordsControllerTest::SetUp();
@@ -74,9 +74,18 @@
     return controller_.get();
   }
 
+  int index_nope() const {
+    return combobox_model_.index_nope();
+  }
+
+  int index_never() const {
+    return combobox_model_.index_never();
+  }
+
  private:
   base::scoped_nsobject<ManagePasswordsBubblePendingViewController> controller_;
   base::scoped_nsobject<ManagePasswordsBubblePendingViewTestDelegate> delegate_;
+  SavePasswordRefusalComboboxModel combobox_model_;
 };
 
 TEST_F(ManagePasswordsBubblePendingViewControllerTest,
@@ -91,7 +100,7 @@
 TEST_F(ManagePasswordsBubblePendingViewControllerTest,
        ShouldNopeAndDismissWhenNopeClicked) {
   BubbleCombobox* nopeButton = [controller() nopeButton];
-  ClickMenuItem(nopeButton, SavePasswordRefusalComboboxModel::INDEX_NOPE);
+  ClickMenuItem(nopeButton, index_nope());
 
   EXPECT_TRUE([delegate() dismissed]);
   EXPECT_FALSE(ui_controller()->saved_password());
@@ -101,8 +110,7 @@
 TEST_F(ManagePasswordsBubblePendingViewControllerTest,
        ShouldNeverSaveAndDismissWhenNeverSaveClickedWithoutAnyBestMatches) {
   BubbleCombobox* nopeButton = [controller() nopeButton];
-  ClickMenuItem(nopeButton,
-                SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE);
+  ClickMenuItem(nopeButton, index_never());
 
   EXPECT_TRUE([delegate() dismissed]);
   EXPECT_FALSE(ui_controller()->saved_password());
@@ -121,8 +129,7 @@
   EXPECT_FALSE(model()->local_credentials().empty());
 
   BubbleCombobox* nopeButton = [controller() nopeButton];
-  ClickMenuItem(nopeButton,
-                SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE);
+  ClickMenuItem(nopeButton, index_never());
 
   EXPECT_TRUE([delegate() neverSave]);
   EXPECT_FALSE(ui_controller()->saved_password());
diff --git a/chrome/browser/ui/cocoa/profiles/profile_menu_controller.mm b/chrome/browser/ui/cocoa/profiles/profile_menu_controller.mm
index 20cafe11..437bed15 100644
--- a/chrome/browser/ui/cocoa/profiles/profile_menu_controller.mm
+++ b/chrome/browser/ui/cocoa/profiles/profile_menu_controller.mm
@@ -173,14 +173,14 @@
       avatarMenu_->GetActiveProfileIndex());
   if ([menuItem action] == @selector(switchToProfileFromDock:) ||
       [menuItem action] == @selector(switchToProfileFromMenu:)) {
-    if (!itemData.supervised)
+    if (!itemData.legacy_supervised)
       return YES;
 
     return [menuItem tag] == static_cast<NSInteger>(itemData.menu_index);
   }
 
   if ([menuItem action] == @selector(newProfile:))
-    return !itemData.supervised;
+    return !itemData.legacy_supervised;
 
   return YES;
 }
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index 8154cb2a..8b37d7dc 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -36,6 +36,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "grit/components_strings.h"
+#include "grit/theme_resources.h"
 #include "net/base/net_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -79,10 +80,9 @@
     const std::string& device_id,
     const content::MediaStreamDevices& devices) {
   DCHECK(!devices.empty());
-  for (content::MediaStreamDevices::const_iterator it = devices.begin();
-       it != devices.end(); ++it) {
-    if (it->id == device_id)
-      return *(it);
+  for (const content::MediaStreamDevice& device : devices) {
+    if (device.id == device_id)
+      return device;
   }
 
   // A device with the |device_id| was not found. It is likely that the device
@@ -114,11 +114,6 @@
         TabSpecificContentSettings::FromWebContents(web_contents());
   }
 
-  if (content_type() == CONTENT_SETTINGS_TYPE_PLUGINS && content_settings &&
-      content_settings->IsContentBlocked(content_type())) {
-    set_plugin_names(content_settings->GetBlockedPluginNames());
-  }
-
   static const ContentSettingsTypeIdEntry kBlockedTitleIDs[] = {
     {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_TITLE},
     {CONTENT_SETTINGS_TYPE_IMAGES, IDS_BLOCKED_IMAGES_TITLE},
@@ -425,8 +420,7 @@
  public:
   ContentSettingCookiesBubbleModel(Delegate* delegate,
                                    WebContents* web_contents,
-                                   Profile* profile,
-                                   ContentSettingsType content_type);
+                                   Profile* profile);
 
   ~ContentSettingCookiesBubbleModel() override;
 
@@ -437,11 +431,11 @@
 ContentSettingCookiesBubbleModel::ContentSettingCookiesBubbleModel(
     Delegate* delegate,
     WebContents* web_contents,
-    Profile* profile,
-    ContentSettingsType content_type)
-    : ContentSettingSingleRadioGroup(
-          delegate, web_contents, profile, content_type) {
-  DCHECK_EQ(CONTENT_SETTINGS_TYPE_COOKIES, content_type);
+    Profile* profile)
+    : ContentSettingSingleRadioGroup(delegate,
+                                     web_contents,
+                                     profile,
+                                     CONTENT_SETTINGS_TYPE_COOKIES) {
   set_custom_link_enabled(true);
 }
 
@@ -469,8 +463,7 @@
  public:
   ContentSettingPluginBubbleModel(Delegate* delegate,
                                   WebContents* web_contents,
-                                  Profile* profile,
-                                  ContentSettingsType content_type);
+                                  Profile* profile);
 
   ~ContentSettingPluginBubbleModel() override;
 
@@ -481,11 +474,11 @@
 ContentSettingPluginBubbleModel::ContentSettingPluginBubbleModel(
     Delegate* delegate,
     WebContents* web_contents,
-    Profile* profile,
-    ContentSettingsType content_type)
-    : ContentSettingSingleRadioGroup(
-          delegate, web_contents, profile, content_type) {
-  DCHECK_EQ(content_type, CONTENT_SETTINGS_TYPE_PLUGINS);
+    Profile* profile)
+    : ContentSettingSingleRadioGroup(delegate,
+                                     web_contents,
+                                     profile,
+                                     CONTENT_SETTINGS_TYPE_PLUGINS) {
   // Disable the "Run all plugins this time" link if the setting is managed and
   // can't be controlled by the user or if the user already clicked on the link
   // and ran all plugins.
@@ -493,6 +486,21 @@
                           web_contents &&
                           TabSpecificContentSettings::FromWebContents(
                               web_contents)->load_plugins_link_enabled());
+  // Build blocked plugin list.
+  if (web_contents) {
+    TabSpecificContentSettings* content_settings =
+        TabSpecificContentSettings::FromWebContents(web_contents);
+
+    const std::vector<base::string16>& blocked_plugins =
+        content_settings->blocked_plugin_names();
+    for (const base::string16& blocked_plugin : blocked_plugins) {
+      ListItem plugin_item(
+          ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+              IDR_BLOCKED_PLUGINS),
+          base::UTF16ToUTF8(blocked_plugin), false, 0);
+      add_list_item(plugin_item);
+    }
+  }
 }
 
 ContentSettingPluginBubbleModel::~ContentSettingPluginBubbleModel() {
@@ -523,50 +531,47 @@
  public:
   ContentSettingPopupBubbleModel(Delegate* delegate,
                                  WebContents* web_contents,
-                                 Profile* profile,
-                                 ContentSettingsType content_type);
+                                 Profile* profile);
   ~ContentSettingPopupBubbleModel() override {}
 
  private:
-  void SetPopups();
-  void OnPopupClicked(int index) override;
+  void OnListItemClicked(int index) override;
+
+  int32 item_id_from_item_index(int index) const {
+    return bubble_content().list_items[index].item_id;
+  }
 };
 
 ContentSettingPopupBubbleModel::ContentSettingPopupBubbleModel(
     Delegate* delegate,
     WebContents* web_contents,
-    Profile* profile,
-    ContentSettingsType content_type)
-    : ContentSettingSingleRadioGroup(
-        delegate, web_contents, profile, content_type) {
-  SetPopups();
-}
-
-
-void ContentSettingPopupBubbleModel::SetPopups() {
-  std::map<int32, GURL> blocked_popups =
-      PopupBlockerTabHelper::FromWebContents(web_contents())
-          ->GetBlockedPopupRequests();
-  for (std::map<int32, GURL>::const_iterator iter = blocked_popups.begin();
-       iter != blocked_popups.end();
-       ++iter) {
-    std::string title(iter->second.spec());
-    // The popup may not have a valid URL.
-    if (title.empty())
-      title = l10n_util::GetStringUTF8(IDS_TAB_LOADING_TITLE);
-    PopupItem popup_item(
-        ui::ResourceBundle::GetSharedInstance().GetImageNamed(
-            IDR_DEFAULT_FAVICON),
-        title,
-        iter->first);
-    add_popup(popup_item);
+    Profile* profile)
+    : ContentSettingSingleRadioGroup(delegate,
+                                     web_contents,
+                                     profile,
+                                     CONTENT_SETTINGS_TYPE_POPUPS) {
+  if (web_contents) {
+    // Build blocked popup list.
+    std::map<int32, GURL> blocked_popups =
+        PopupBlockerTabHelper::FromWebContents(web_contents)
+            ->GetBlockedPopupRequests();
+    for (const std::pair<int32, GURL>& blocked_popup : blocked_popups) {
+      std::string title(blocked_popup.second.spec());
+      // The popup may not have a valid URL.
+      if (title.empty())
+        title = l10n_util::GetStringUTF8(IDS_TAB_LOADING_TITLE);
+      ListItem popup_item(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+                              IDR_DEFAULT_FAVICON),
+                          title, true, blocked_popup.first);
+      add_list_item(popup_item);
+    }
   }
 }
 
-void ContentSettingPopupBubbleModel::OnPopupClicked(int index) {
+void ContentSettingPopupBubbleModel::OnListItemClicked(int index) {
   if (web_contents()) {
-    PopupBlockerTabHelper::FromWebContents(web_contents())->
-        ShowBlockedPopup(bubble_content().popup_items[index].popup_id);
+    PopupBlockerTabHelper::FromWebContents(web_contents())
+        ->ShowBlockedPopup(item_id_from_item_index(index));
   }
 }
 
@@ -640,10 +645,12 @@
   if (!web_contents())
     return;
 
-  for (MediaMenuMap::const_iterator it = bubble_content().media_menus.begin();
-      it != bubble_content().media_menus.end(); ++it) {
-    if (it->second.selected_device.id != it->second.default_device.id) {
-      UpdateDefaultDeviceForType(it->first, it->second.selected_device.id);
+  for (const std::pair<content::MediaStreamType, MediaMenu>& media_menu :
+       bubble_content().media_menus) {
+    if (media_menu.second.selected_device.id !=
+        media_menu.second.default_device.id) {
+      UpdateDefaultDeviceForType(media_menu.first,
+                                 media_menu.second.selected_device.id);
     }
   }
 
@@ -952,13 +959,11 @@
   HostContentSettingsMap* settings_map =
       profile()->GetHostContentSettingsMap();
 
-  for (ContentSettingsUsagesState::StateMap::const_iterator it =
-       state_map.begin(); it != state_map.end(); ++it) {
+  for (const std::pair<GURL, ContentSetting>& map_entry : state_map) {
     settings_map->SetContentSetting(
-        ContentSettingsPattern::FromURLNoWildcard(it->first),
+        ContentSettingsPattern::FromURLNoWildcard(map_entry.first),
         ContentSettingsPattern::FromURLNoWildcard(embedder_url),
-        CONTENT_SETTINGS_TYPE_GEOLOCATION,
-        std::string(),
+        CONTENT_SETTINGS_TYPE_GEOLOCATION, std::string(),
         CONTENT_SETTING_DEFAULT);
   }
 }
@@ -968,8 +973,7 @@
  public:
   ContentSettingMixedScriptBubbleModel(Delegate* delegate,
                                        WebContents* web_contents,
-                                       Profile* profile,
-                                       ContentSettingsType content_type);
+                                       Profile* profile);
 
   ~ContentSettingMixedScriptBubbleModel() override {}
 
@@ -980,11 +984,11 @@
 ContentSettingMixedScriptBubbleModel::ContentSettingMixedScriptBubbleModel(
     Delegate* delegate,
     WebContents* web_contents,
-    Profile* profile,
-    ContentSettingsType content_type)
-    : ContentSettingTitleLinkAndCustomModel(
-        delegate, web_contents, profile, content_type) {
-  DCHECK_EQ(content_type, CONTENT_SETTINGS_TYPE_MIXEDSCRIPT);
+    Profile* profile)
+    : ContentSettingTitleLinkAndCustomModel(delegate,
+                                            web_contents,
+                                            profile,
+                                            CONTENT_SETTINGS_TYPE_MIXEDSCRIPT) {
   content_settings::RecordMixedScriptAction(
       content_settings::MIXED_SCRIPT_ACTION_DISPLAYED_BUBBLE);
   set_custom_link_enabled(true);
@@ -1004,16 +1008,15 @@
     Delegate* delegate,
     WebContents* web_contents,
     Profile* profile,
-    ProtocolHandlerRegistry* registry,
-    ContentSettingsType content_type)
-    : ContentSettingTitleAndLinkModel(
-          delegate, web_contents, profile, content_type),
+    ProtocolHandlerRegistry* registry)
+    : ContentSettingTitleAndLinkModel(delegate,
+                                      web_contents,
+                                      profile,
+                                      CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS),
       selected_item_(0),
       registry_(registry),
       pending_handler_(ProtocolHandler::EmptyProtocolHandler()),
       previous_handler_(ProtocolHandler::EmptyProtocolHandler()) {
-  DCHECK_EQ(CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS, content_type);
-
   TabSpecificContentSettings* content_settings =
       TabSpecificContentSettings::FromWebContents(web_contents);
   pending_handler_ = content_settings->pending_protocol_handler();
@@ -1136,8 +1139,7 @@
  public:
   ContentSettingMidiSysExBubbleModel(Delegate* delegate,
                                      WebContents* web_contents,
-                                     Profile* profile,
-                                     ContentSettingsType content_type);
+                                     Profile* profile);
   ~ContentSettingMidiSysExBubbleModel() override {}
 
  private:
@@ -1149,11 +1151,11 @@
 ContentSettingMidiSysExBubbleModel::ContentSettingMidiSysExBubbleModel(
     Delegate* delegate,
     WebContents* web_contents,
-    Profile* profile,
-    ContentSettingsType content_type)
-    : ContentSettingTitleAndLinkModel(
-        delegate, web_contents, profile, content_type) {
-  DCHECK_EQ(CONTENT_SETTINGS_TYPE_MIDI_SYSEX, content_type);
+    Profile* profile)
+    : ContentSettingTitleAndLinkModel(delegate,
+                                      web_contents,
+                                      profile,
+                                      CONTENT_SETTINGS_TYPE_MIDI_SYSEX) {
   SetDomainsAndCustomLink();
 }
 
@@ -1207,13 +1209,11 @@
   HostContentSettingsMap* settings_map =
       profile()->GetHostContentSettingsMap();
 
-  for (ContentSettingsUsagesState::StateMap::const_iterator it =
-       state_map.begin(); it != state_map.end(); ++it) {
+  for (const std::pair<GURL, ContentSetting>& map_entry : state_map) {
     settings_map->SetContentSetting(
-        ContentSettingsPattern::FromURLNoWildcard(it->first),
+        ContentSettingsPattern::FromURLNoWildcard(map_entry.first),
         ContentSettingsPattern::FromURLNoWildcard(embedder_url),
-        CONTENT_SETTINGS_TYPE_MIDI_SYSEX,
-        std::string(),
+        CONTENT_SETTINGS_TYPE_MIDI_SYSEX, std::string(),
         CONTENT_SETTING_DEFAULT);
   }
 }
@@ -1226,12 +1226,11 @@
         Profile* profile,
         ContentSettingsType content_type) {
   if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
-    return new ContentSettingCookiesBubbleModel(delegate, web_contents, profile,
-                                                content_type);
+    return new ContentSettingCookiesBubbleModel(delegate, web_contents,
+                                                profile);
   }
   if (content_type == CONTENT_SETTINGS_TYPE_POPUPS) {
-    return new ContentSettingPopupBubbleModel(delegate, web_contents, profile,
-                                              content_type);
+    return new ContentSettingPopupBubbleModel(delegate, web_contents, profile);
   }
   if (content_type == CONTENT_SETTINGS_TYPE_GEOLOCATION) {
     return new ContentSettingDomainListBubbleModel(delegate, web_contents,
@@ -1242,22 +1241,21 @@
                                                     profile);
   }
   if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) {
-    return new ContentSettingPluginBubbleModel(delegate, web_contents, profile,
-                                               content_type);
+    return new ContentSettingPluginBubbleModel(delegate, web_contents, profile);
   }
   if (content_type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT) {
     return new ContentSettingMixedScriptBubbleModel(delegate, web_contents,
-                                                    profile, content_type);
+                                                    profile);
   }
   if (content_type == CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS) {
     ProtocolHandlerRegistry* registry =
         ProtocolHandlerRegistryFactory::GetForBrowserContext(profile);
     return new ContentSettingRPHBubbleModel(delegate, web_contents, profile,
-                                            registry, content_type);
+                                            registry);
   }
   if (content_type == CONTENT_SETTINGS_TYPE_MIDI_SYSEX) {
     return new ContentSettingMidiSysExBubbleModel(delegate, web_contents,
-                                                  profile, content_type);
+                                                  profile);
   }
   return new ContentSettingSingleRadioGroup(delegate, web_contents, profile,
                                             content_type);
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.h b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
index 81ccd9e39..b6b9f7b1 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
@@ -34,15 +34,19 @@
  public:
   typedef ContentSettingBubbleModelDelegate Delegate;
 
-  struct PopupItem {
-    PopupItem(const gfx::Image& image, const std::string& title, int32 popup_id)
-        : image(image), title(title), popup_id(popup_id) {}
+  struct ListItem {
+    ListItem(const gfx::Image& image,
+             const std::string& title,
+             bool has_link,
+             int32 item_id)
+        : image(image), title(title), has_link(has_link), item_id(item_id) {}
 
     gfx::Image image;
     std::string title;
-    int32 popup_id;
+    bool has_link;
+    int32 item_id;
   };
-  typedef std::vector<PopupItem> PopupItems;
+  typedef std::vector<ListItem> ListItems;
 
   typedef std::vector<std::string> RadioItems;
   struct RadioGroup {
@@ -79,8 +83,7 @@
     ~BubbleContent();
 
     std::string title;
-    base::string16 plugin_names;
-    PopupItems popup_items;
+    ListItems list_items;
     RadioGroup radio_group;
     bool radio_group_enabled;
     std::vector<DomainList> domain_lists;
@@ -112,7 +115,7 @@
                const content::NotificationDetails& details) override;
 
   virtual void OnRadioClicked(int radio_index) {}
-  virtual void OnPopupClicked(int index) {}
+  virtual void OnListItemClicked(int index) {}
   virtual void OnCustomLinkClicked() {}
   virtual void OnManageLinkClicked() {}
   virtual void OnLearnMoreLinkClicked() {}
@@ -133,11 +136,8 @@
   Profile* profile() const { return profile_; }
 
   void set_title(const std::string& title) { bubble_content_.title = title; }
-  void set_plugin_names(const base::string16& plugin_names) {
-    bubble_content_.plugin_names = plugin_names;
-  }
-  void add_popup(const PopupItem& popup) {
-    bubble_content_.popup_items.push_back(popup);
+  void add_list_item(const ListItem& item) {
+    bubble_content_.list_items.push_back(item);
   }
   void set_radio_group(const RadioGroup& radio_group) {
     bubble_content_.radio_group = radio_group;
@@ -213,8 +213,7 @@
   ContentSettingRPHBubbleModel(Delegate* delegate,
                                content::WebContents* web_contents,
                                Profile* profile,
-                               ProtocolHandlerRegistry* registry,
-                               ContentSettingsType content_type);
+                               ProtocolHandlerRegistry* registry);
 
   void OnRadioClicked(int radio_index) override;
   void OnDoneClicked() override;
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
index 0b747901..df3602d 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -47,7 +47,7 @@
         content_setting_bubble_model->bubble_content();
     EXPECT_TRUE(bubble_content.title.empty());
     EXPECT_TRUE(bubble_content.radio_group.radio_items.empty());
-    EXPECT_TRUE(bubble_content.popup_items.empty());
+    EXPECT_TRUE(bubble_content.list_items.empty());
     EXPECT_EQ(expected_domains, bubble_content.domain_lists.size());
     EXPECT_NE(expect_clear_link || expect_reload_hint,
               bubble_content.custom_link.empty());
@@ -96,7 +96,6 @@
       content_setting_bubble_model->bubble_content();
   std::string title = bubble_content.title;
   EXPECT_FALSE(title.empty());
-  EXPECT_TRUE(bubble_content.plugin_names.empty());
   ASSERT_EQ(2U, bubble_content.radio_group.radio_items.size());
   std::string radio1 = bubble_content.radio_group.radio_items[0];
   std::string radio2 = bubble_content.radio_group.radio_items[1];
@@ -742,9 +741,9 @@
   const ContentSettingBubbleModel::BubbleContent& bubble_content =
       content_setting_bubble_model->bubble_content();
   EXPECT_FALSE(bubble_content.title.empty());
-  EXPECT_FALSE(bubble_content.plugin_names.empty());
-  EXPECT_NE(base::string16::npos,
-            bubble_content.plugin_names.find(plugin_name));
+  ASSERT_EQ(1U, bubble_content.list_items.size());
+  EXPECT_EQ(plugin_name,
+            base::ASCIIToUTF16(bubble_content.list_items[0].title));
   EXPECT_EQ(2U, bubble_content.radio_group.radio_items.size());
   EXPECT_FALSE(bubble_content.custom_link.empty());
   EXPECT_TRUE(bubble_content.custom_link_enabled);
@@ -853,14 +852,13 @@
           "mailto", GURL("http://www.toplevel.example/")));
 
   ContentSettingRPHBubbleModel content_setting_bubble_model(
-          NULL, web_contents(), profile(), NULL,
-          CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS);
+      NULL, web_contents(), profile(), NULL);
 
   const ContentSettingBubbleModel::BubbleContent& bubble_content =
       content_setting_bubble_model.bubble_content();
   EXPECT_FALSE(bubble_content.title.empty());
   EXPECT_FALSE(bubble_content.radio_group.radio_items.empty());
-  EXPECT_TRUE(bubble_content.popup_items.empty());
+  EXPECT_TRUE(bubble_content.list_items.empty());
   EXPECT_TRUE(bubble_content.domain_lists.empty());
   EXPECT_TRUE(bubble_content.custom_link.empty());
   EXPECT_FALSE(bubble_content.custom_link_enabled);
@@ -907,8 +905,7 @@
   content_settings->set_pending_protocol_handler(test_handler);
 
   ContentSettingRPHBubbleModel content_setting_bubble_model(
-          NULL, web_contents(), profile(), &registry,
-          CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS);
+      NULL, web_contents(), profile(), &registry);
 
   {
     ProtocolHandler handler = registry.GetHandlerFor("mailto");
diff --git a/chrome/browser/ui/extensions/bookmark_app_browsertest.cc b/chrome/browser/ui/extensions/bookmark_app_browsertest.cc
new file mode 100644
index 0000000..be61abf
--- /dev/null
+++ b/chrome/browser/ui/extensions/bookmark_app_browsertest.cc
@@ -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.
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/devtools/devtools_window_testing.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_iterator.h"
+#include "chrome/browser/ui/extensions/app_launch_params.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_set.h"
+
+using content::WebContents;
+using extensions::Extension;
+
+typedef ExtensionBrowserTest BookmarkAppTest;
+
+namespace {
+
+// Used by ShouldLocationBarForXXX. Performs a navigation and then checks that
+// the location bar visibility is as expcted.
+void NavigateAndCheckForLocationBar(Browser* browser,
+                                    const std::string& url_string,
+                                    bool expected_visibility) {
+  GURL url(url_string);
+  ui_test_utils::NavigateToURL(browser, url);
+  EXPECT_EQ(expected_visibility,
+      browser->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR));
+}
+
+}  // namespace
+
+// Check that the location bar is shown correctly for HTTP bookmark apps.
+IN_PROC_BROWSER_TEST_F(BookmarkAppTest,
+                       ShouldShowLocationBarForHTTPBookmarkApp) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kEnableNewBookmarkApps);
+  ASSERT_TRUE(test_server()->Start());
+
+  // Load a http bookmark app.
+  const Extension* http_bookmark_app = InstallExtensionWithSourceAndFlags(
+      test_data_dir_.AppendASCII("app/"),
+      1,
+      extensions::Manifest::INTERNAL,
+      extensions::Extension::FROM_BOOKMARK);
+  ASSERT_TRUE(http_bookmark_app);
+
+  // Launch it in a window.
+  WebContents* app_window = OpenApplication(AppLaunchParams(
+      browser()->profile(), http_bookmark_app,
+      extensions::LAUNCH_CONTAINER_WINDOW, NEW_WINDOW,
+      extensions::SOURCE_TEST));
+  ASSERT_TRUE(app_window);
+
+  // Find the new browser.
+  Browser* http_app_browser = NULL;
+  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
+    std::string app_id =
+        web_app::GetExtensionIdFromApplicationName((*it)->app_name());
+    if (*it == browser()) {
+      continue;
+    } else if (app_id == http_bookmark_app->id()) {
+      http_app_browser = *it;
+    }
+  }
+  ASSERT_TRUE(http_app_browser);
+  ASSERT_TRUE(http_app_browser != browser());
+
+  // Navigate to the app's launch page; the location bar should be hidden.
+  NavigateAndCheckForLocationBar(
+      http_app_browser, "http://www.example.com/empty.html", false);
+
+  // Navigate to another page on the same origin; the location bar should still
+  // hidden.
+  NavigateAndCheckForLocationBar(
+      http_app_browser, "http://www.example.com/blah", false);
+
+  // Navigate to the https version of the site; the location bar should
+  // be hidden for both browsers.
+  NavigateAndCheckForLocationBar(
+      http_app_browser, "https://www.example.com/blah", false);
+
+  // Navigate to different origin; the location bar should now be visible.
+  NavigateAndCheckForLocationBar(
+      http_app_browser, "http://www.foo.com/blah", true);
+
+  // Navigate back to the app's origin; the location bar should now be hidden.
+  NavigateAndCheckForLocationBar(
+      http_app_browser, "http://www.example.com/blah", false);
+}
+
+// Check that the location bar is shown correctly for HTTPS bookmark apps.
+IN_PROC_BROWSER_TEST_F(BookmarkAppTest,
+                       ShouldShowLocationBarForHTTPSBookmarkApp) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kEnableNewBookmarkApps);
+  ASSERT_TRUE(test_server()->Start());
+
+  // Load a https bookmark app.
+  const Extension* https_bookmark_app = InstallExtensionWithSourceAndFlags(
+      test_data_dir_.AppendASCII("https_app/"),
+      1,
+      extensions::Manifest::INTERNAL,
+      extensions::Extension::FROM_BOOKMARK);
+  ASSERT_TRUE(https_bookmark_app);
+
+  // Launch it in a window.
+  WebContents* app_window = OpenApplication(AppLaunchParams(
+      browser()->profile(), https_bookmark_app,
+      extensions::LAUNCH_CONTAINER_WINDOW, NEW_WINDOW,
+      extensions::SOURCE_TEST));
+  ASSERT_TRUE(app_window);
+
+  // Find the new browser.
+  Browser* https_app_browser = NULL;
+  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
+    std::string app_id =
+        web_app::GetExtensionIdFromApplicationName((*it)->app_name());
+    if (*it == browser()) {
+      continue;
+    } else if (app_id == https_bookmark_app->id()) {
+      https_app_browser = *it;
+    }
+  }
+  ASSERT_TRUE(https_app_browser);
+  ASSERT_TRUE(https_app_browser != browser());
+
+  // Navigate to the app's launch page; the location bar should be hidden.
+  NavigateAndCheckForLocationBar(
+      https_app_browser, "https://www.example.com/empty.html", false);
+
+  // Navigate to another page on the same origin; the location bar should still
+  // hidden.
+  NavigateAndCheckForLocationBar(
+      https_app_browser, "https://www.example.com/blah", false);
+
+  // Navigate to the http version of the site; the location bar should
+  // be visible for the https version as it is now on a less secure version
+  // of its host.
+  NavigateAndCheckForLocationBar(
+      https_app_browser, "http://www.example.com/blah", true);
+
+  // Navigate to different origin; the location bar should now be visible.
+  NavigateAndCheckForLocationBar(
+      https_app_browser, "http://www.foo.com/blah", true);
+
+  // Navigate back to the app's origin; the location bar should now be hidden.
+  NavigateAndCheckForLocationBar(
+      https_app_browser, "https://www.example.com/blah", false);
+}
+
+// Open a normal browser window, a hosted app window, a legacy packaged app
+// window and a dev tools window, and check that the web app frame feature is
+// supported correctly.
+IN_PROC_BROWSER_TEST_F(BookmarkAppTest, ShouldUseWebAppFrame) {
+  ASSERT_TRUE(test_server()->Start());
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kEnableWebAppFrame);
+
+  // Load a hosted app.
+   const Extension* bookmark_app = InstallExtensionWithSourceAndFlags(
+      test_data_dir_.AppendASCII("app/"),
+      1,
+      extensions::Manifest::INTERNAL,
+      extensions::Extension::FROM_BOOKMARK);
+  ASSERT_TRUE(bookmark_app);
+
+  // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
+  WebContents* bookmark_app_window = OpenApplication(AppLaunchParams(
+      browser()->profile(), bookmark_app, extensions::LAUNCH_CONTAINER_WINDOW,
+      NEW_WINDOW, extensions::SOURCE_UNTRACKED));
+  ASSERT_TRUE(bookmark_app_window);
+
+  //  Load a packaged app.
+  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("packaged_app/")));
+  const Extension* packaged_app = nullptr;
+  extensions::ExtensionRegistry* registry =
+      extensions::ExtensionRegistry::Get(browser()->profile());
+  for (const scoped_refptr<const extensions::Extension>& extension :
+       registry->enabled_extensions()) {
+    if (extension->name() == "Packaged App Test")
+      packaged_app = extension.get();
+  }
+  ASSERT_TRUE(packaged_app);
+
+  // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
+  WebContents* packaged_app_window = OpenApplication(AppLaunchParams(
+      browser()->profile(), packaged_app, extensions::LAUNCH_CONTAINER_WINDOW,
+      NEW_WINDOW, extensions::SOURCE_UNTRACKED));
+  ASSERT_TRUE(packaged_app_window);
+
+  DevToolsWindow* devtools_window =
+      DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), false);
+
+  // The launch should have created a new app browser and a dev tools browser.
+  ASSERT_EQ(4u, chrome::GetBrowserCount(browser()->profile(),
+                                        browser()->host_desktop_type()));
+
+  // Find the new browsers.
+  Browser* bookmark_app_browser = NULL;
+  Browser* packaged_app_browser = NULL;
+  Browser* dev_tools_browser = NULL;
+  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
+    if (*it == browser()) {
+      continue;
+    } else if ((*it)->app_name() == DevToolsWindow::kDevToolsApp) {
+      dev_tools_browser = *it;
+    } else if ((*it)->tab_strip_model()->GetActiveWebContents() ==
+               bookmark_app_window) {
+      bookmark_app_browser = *it;
+    } else {
+      packaged_app_browser = *it;
+    }
+  }
+  ASSERT_TRUE(dev_tools_browser);
+  ASSERT_TRUE(bookmark_app_browser);
+  ASSERT_TRUE(bookmark_app_browser != browser());
+  ASSERT_TRUE(packaged_app_browser);
+  ASSERT_TRUE(packaged_app_browser != browser());
+  ASSERT_TRUE(packaged_app_browser != bookmark_app_browser);
+
+  EXPECT_FALSE(browser()->SupportsWindowFeature(Browser::FEATURE_WEBAPPFRAME));
+  EXPECT_FALSE(
+      dev_tools_browser->SupportsWindowFeature(Browser::FEATURE_WEBAPPFRAME));
+  EXPECT_EQ(browser()->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH,
+            bookmark_app_browser->SupportsWindowFeature(
+                Browser::FEATURE_WEBAPPFRAME));
+  EXPECT_FALSE(packaged_app_browser->SupportsWindowFeature(
+      Browser::FEATURE_WEBAPPFRAME));
+
+  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
+}
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
index 5233f82c..b87b261 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
@@ -15,8 +15,8 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/feedback/feedback_data.h"
 #include "components/feedback/feedback_util.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
 #include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "components/password_manager/core/common/password_manager_ui.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_switches.h"
@@ -273,7 +273,6 @@
     const autofill::PasswordForm& password_form,
     password_manager::CredentialType credential_type) {
   dismissal_reason_ = metrics_util::CLICKED_CREDENTIAL;
-  RecordExperimentStatistics(web_contents(), dismissal_reason_);
   ManagePasswordsUIController* manage_passwords_ui_controller =
       ManagePasswordsUIController::FromWebContents(web_contents());
   manage_passwords_ui_controller->ChooseCredential(password_form,
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
index 4b57e04..f3fb0fe2 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
@@ -10,8 +10,8 @@
 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/password_manager/core/common/password_manager_ui.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/ui/passwords/manage_passwords_state.cc b/chrome/browser/ui/passwords/manage_passwords_state.cc
index 113b46a..457b6ec 100644
--- a/chrome/browser/ui/passwords/manage_passwords_state.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_state.cc
@@ -4,10 +4,10 @@
 
 #include "chrome/browser/ui/passwords/manage_passwords_state.h"
 
-#include "components/password_manager/content/common/credential_manager_types.h"
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
 #include "components/password_manager/core/browser/password_form_manager.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 
 using password_manager::PasswordFormManager;
 using autofill::PasswordFormMap;
diff --git a/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc
index 359c88c58..3a12071 100644
--- a/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc
@@ -5,9 +5,9 @@
 #include "chrome/browser/ui/passwords/manage_passwords_state.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
 #include "components/password_manager/core/browser/password_form_manager.h"
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ui/passwords/manage_passwords_test.h b/chrome/browser/ui/passwords/manage_passwords_test.h
index 4630aff5..f9ef9f53 100644
--- a/chrome/browser/ui/passwords/manage_passwords_test.h
+++ b/chrome/browser/ui/passwords/manage_passwords_test.h
@@ -9,7 +9,7 @@
 #include "base/test/histogram_tester.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index bbc7c14e..5fcb462 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -18,9 +18,9 @@
 #include "chrome/browser/ui/passwords/password_bubble_experiment.h"
 #include "chrome/browser/ui/tab_dialogs.h"
 #include "chrome/common/url_constants.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
 #include "components/password_manager/core/browser/password_form_manager.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "content/public/browser/navigation_details.h"
 
 #if defined(OS_ANDROID)
@@ -332,10 +332,7 @@
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
   if (!browser || browser->toolbar_model()->input_in_progress())
     return;
-  if (state() == password_manager::ui::PENDING_PASSWORD_STATE &&
-      !password_bubble_experiment::ShouldShowBubble(
-          browser->profile()->GetPrefs()))
-    return;
+
   CommandUpdater* updater = browser->command_controller()->command_updater();
   updater->ExecuteCommand(IDC_MANAGE_PASSWORDS_FOR_PAGE);
 #endif
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.cc
index d71daace..b69799d 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.cc
@@ -4,8 +4,8 @@
 
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h"
 
-#include "components/password_manager/content/common/credential_manager_types.h"
 #include "components/password_manager/core/browser/password_form_manager.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "content/public/browser/web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index f5511fd1..0b8dd60f 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -14,10 +14,10 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
 #include "components/password_manager/core/browser/password_form_manager.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/credential_manager_types.h"
 #include "components/password_manager/core/common/password_manager_ui.h"
 #include "content/public/browser/navigation_details.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/ui/passwords/password_bubble_experiment.cc b/chrome/browser/ui/passwords/password_bubble_experiment.cc
index 276a038..8afabfca 100644
--- a/chrome/browser/ui/passwords/password_bubble_experiment.cc
+++ b/chrome/browser/ui/passwords/password_bubble_experiment.cc
@@ -6,9 +6,7 @@
 
 #include "base/metrics/field_trial.h"
 #include "base/prefs/pref_service.h"
-#include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/time/time.h"
 #include "chrome/common/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/variations/variations_associated_data.h"
@@ -16,190 +14,47 @@
 namespace password_bubble_experiment {
 namespace {
 
-bool IsNegativeEvent(password_manager::metrics_util::UIDismissalReason reason) {
-  return (reason == password_manager::metrics_util::NO_DIRECT_INTERACTION ||
-          reason == password_manager::metrics_util::CLICKED_NOPE ||
-          reason == password_manager::metrics_util::CLICKED_NEVER);
-}
-
-// "TimeSpan" experiment -----------------------------------------------------
-
-bool ExtractTimeSpanParams(base::TimeDelta* time_delta, int* nopes_limit) {
-  std::map<std::string, std::string> params;
-  bool retrieved = variations::GetVariationParams(kExperimentName, &params);
-  if (!retrieved)
-    return false;
-  int days = 0;
-  if (!base::StringToInt(params[kParamTimeSpan], &days) ||
-      !base::StringToInt(params[kParamTimeSpanNopeThreshold], nopes_limit))
-    return false;
-  *time_delta = base::TimeDelta::FromDays(days);
-  return true;
-}
-
-bool OverwriteTimeSpanPrefsIfNeeded(PrefService* prefs,
-                                    base::TimeDelta time_span) {
-  base::Time beginning = base::Time::FromInternalValue(
-      prefs->GetInt64(prefs::kPasswordBubbleTimeStamp));
-  base::Time now = base::Time::Now();
-  if (beginning + time_span < now) {
-    prefs->SetInt64(prefs::kPasswordBubbleTimeStamp, now.ToInternalValue());
-    prefs->SetInteger(prefs::kPasswordBubbleNopesCount, 0);
-    return true;
-  }
-  return false;
-}
-
-// If user dismisses the bubble >= kParamTimeSpanNopeThreshold times during
-// kParamTimeSpan days then the bubble isn't shown until the end of this time
-// span.
-bool ShouldShowBubbleTimeSpanExperiment(PrefService* prefs) {
-  base::TimeDelta time_span;
-  int nopes_limit = 0;
-  if (!ExtractTimeSpanParams(&time_span, &nopes_limit)) {
-    VLOG(2) << "Can't read parameters for "
-        << kExperimentName  << " experiment";
-    return true;
-  }
-  // Check if the new time span has started.
-  if (OverwriteTimeSpanPrefsIfNeeded(prefs, time_span))
-    return true;
-  int current_nopes = prefs->GetInteger(prefs::kPasswordBubbleNopesCount);
-  return current_nopes < nopes_limit;
-}
-
-// Increase the "Nope" counter in prefs and start a new time span if needed.
-void UpdateTimeSpanPrefs(
-    PrefService* prefs,
-    password_manager::metrics_util::UIDismissalReason reason) {
-  if (!IsNegativeEvent(reason))
-    return;
-  base::TimeDelta time_span;
-  int nopes_limit = 0;
-  if (!ExtractTimeSpanParams(&time_span, &nopes_limit)) {
-    VLOG(2) << "Can't read parameters for "
-        << kExperimentName << " experiment";
-    return;
-  }
-  OverwriteTimeSpanPrefsIfNeeded(prefs, time_span);
-  int current_nopes = prefs->GetInteger(prefs::kPasswordBubbleNopesCount);
-  prefs->SetInteger(prefs::kPasswordBubbleNopesCount, current_nopes + 1);
-}
-
-// "Probability" experiment --------------------------------------------------
-
-bool ExtractProbabilityParams(unsigned* history_length, unsigned* saves) {
-  std::map<std::string, std::string> params;
-  bool retrieved = variations::GetVariationParams(kExperimentName, &params);
-  if (!retrieved)
-    return false;
-  return base::StringToUint(params[kParamProbabilityInteractionsCount],
-                            history_length) &&
-      base::StringToUint(params[kParamProbabilityFakeSaves], saves);
-}
-
-std::vector<int> ReadInteractionHistory(PrefService* prefs) {
-  std::vector<int> interactions;
-  const base::ListValue* list =
-      prefs->GetList(prefs::kPasswordBubbleLastInteractions);
-  if (!list)
-    return interactions;
-  for (const base::Value* value : *list) {
-    int out_value;
-    if (value->GetAsInteger(&out_value))
-      interactions.push_back(out_value);
-  }
-  return interactions;
-}
-
-// We keep the history of last kParamProbabilityInteractionsCount interactions
-// with the bubble. We implicitly add kParamProbabilityFakeSaves "Save" clicks.
-// If there are x "Save" clicks among those kParamProbabilityInteractionsCount
-// then the bubble is shown with probability (x + kParamProbabilityFakeSaves)/
-// (kParamProbabilityInteractionsCount + kParamProbabilityFakeSaves).
-bool ShouldShowBubbleProbabilityExperiment(PrefService* prefs) {
-  unsigned history_length = 0, fake_saves = 0;
-  if (!ExtractProbabilityParams(&history_length, &fake_saves)) {
-    VLOG(2) << "Can't read parameters for "
-        << kExperimentName << " experiment";
-    return true;
-  }
-  std::vector<int> interactions = ReadInteractionHistory(prefs);
-  unsigned real_saves =
-      std::count(interactions.begin(), interactions.end(),
-                 password_manager::metrics_util::CLICKED_SAVE);
-  return (interactions.size() + fake_saves) * base::RandDouble() <=
-      real_saves + fake_saves;
-}
-
-void UpdateProbabilityPrefs(
-    PrefService* prefs,
-    password_manager::metrics_util::UIDismissalReason reason) {
-  if (!IsNegativeEvent(reason) &&
-      reason != password_manager::metrics_util::CLICKED_SAVE)
-    return;
-  unsigned history_length = 0, fake_saves = 0;
-  if (!ExtractProbabilityParams(&history_length, &fake_saves)) {
-    VLOG(2) << "Can't read parameters for "
-        << kExperimentName << " experiment";
-    return;
-  }
-  std::vector<int> interactions = ReadInteractionHistory(prefs);
-  interactions.push_back(reason);
-  size_t history_beginning = interactions.size() > history_length ?
-      interactions.size() - history_length : 0;
-  base::ListValue value;
-  for (size_t i = history_beginning; i < interactions.size(); ++i)
-    value.AppendInteger(interactions[i]);
-  prefs->Set(prefs::kPasswordBubbleLastInteractions, value);
+// Return the number of consecutive times the user clicked "Not now" in the
+// "Save password" bubble.
+int GetCurrentNopesCount(PrefService* prefs) {
+  return prefs->GetInteger(prefs::kPasswordBubbleNopesCount);
 }
 
 } // namespace
 
 const char kExperimentName[] = "PasswordBubbleAlgorithm";
-const char kGroupTimeSpanBased[] = "TimeSpan";
-const char kGroupProbabilityBased[] = "Probability";
-const char kParamProbabilityFakeSaves[] = "saves_count";
-const char kParamProbabilityInteractionsCount[] = "last_interactions_count";
-const char kParamTimeSpan[] = "time_span";
-const char kParamTimeSpanNopeThreshold[] = "nope_threshold";
+const char kParamNopeThreshold[] = "consecutive_nope_threshold";
 
 void RegisterPrefs(user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterInt64Pref(
-      prefs::kPasswordBubbleTimeStamp,
-      0,
-      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
   registry->RegisterIntegerPref(
       prefs::kPasswordBubbleNopesCount,
       0,
       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
-  registry->RegisterListPref(
-      prefs::kPasswordBubbleLastInteractions,
-      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
 }
 
-bool ShouldShowBubble(PrefService* prefs) {
+bool ShouldShowNeverForThisSiteDefault(PrefService* prefs) {
   if (!base::FieldTrialList::TrialExists(kExperimentName))
-    return true;
-  std::string group_name =
-      base::FieldTrialList::FindFullName(kExperimentName);
+    return false;
+  std::string param =
+      variations::GetVariationParamValue(kExperimentName, kParamNopeThreshold);
 
-  if (group_name == kGroupTimeSpanBased) {
-    return ShouldShowBubbleTimeSpanExperiment(prefs);
-  }
-  if (group_name == kGroupProbabilityBased) {
-    return ShouldShowBubbleProbabilityExperiment(prefs);
-  }
-
-  // The "Show Always" should be the default case.
-  return true;
+  int threshold = 0;
+  return base::StringToInt(param, &threshold) &&
+      GetCurrentNopesCount(prefs) >= threshold;
 }
 
 void RecordBubbleClosed(
     PrefService* prefs,
     password_manager::metrics_util::UIDismissalReason reason) {
-  UpdateTimeSpanPrefs(prefs, reason);
-  UpdateProbabilityPrefs(prefs, reason);
+  // Clicking "Never" doesn't change the experiment state.
+  if (reason == password_manager::metrics_util::CLICKED_NEVER)
+    return;
+  int nopes_count = GetCurrentNopesCount(prefs);
+  if (reason == password_manager::metrics_util::CLICKED_NOPE)
+    nopes_count++;
+  else
+    nopes_count = 0;
+  prefs->SetInteger(prefs::kPasswordBubbleNopesCount, nopes_count);
 }
 
 }  // namespace password_bubble_experiment
diff --git a/chrome/browser/ui/passwords/password_bubble_experiment.h b/chrome/browser/ui/passwords/password_bubble_experiment.h
index 9c4d302..eca144bd 100644
--- a/chrome/browser/ui/passwords/password_bubble_experiment.h
+++ b/chrome/browser/ui/passwords/password_bubble_experiment.h
@@ -15,15 +15,15 @@
 class PrefService;
 
 // These functions handle the algorithms according to which the "Save password?"
-// bubble is shown to user.
+// bubble shows "No thanks" or "Never for this site" button default.
 namespace password_bubble_experiment {
 
 void RegisterPrefs(user_prefs::PrefRegistrySyncable* registry);
 
-// The decision is made based on the "PasswordBubbleAlgorithm" finch experiment.
-// The default value is true.
+// Returns true if "Never for this site" should be the default negative option.
+// Otherwise it's "No thanks". The default value is false.
 // It should be called before showing the "Save Password?" dialog.
-bool ShouldShowBubble(PrefService* prefs);
+bool ShouldShowNeverForThisSiteDefault(PrefService* prefs);
 
 // Should be called when user dismisses the "Save Password?" dialog. It stores
 // the statistics about interactions with the bubble.
@@ -34,23 +34,9 @@
 // The name of the finch experiment controlling the algorithm.
 extern const char kExperimentName[];
 
-// The group name for the time based algorithm.
-extern const char kGroupTimeSpanBased[];
-
-// The group name for the probability algorithm.
-extern const char kGroupProbabilityBased[];
-
-// For "Probability" group. The additional "Saves" to be added to the model.
-extern const char kParamProbabilityFakeSaves[];
-
-// For "Probability" group. The interaction history length.
-extern const char kParamProbabilityInteractionsCount[];
-
-// For "TimeSpan" group. The time span until the nope counter is zeroed.
-extern const char kParamTimeSpan[];
-
-// For "TimeSpan" group. The nopes threshold.
-extern const char kParamTimeSpanNopeThreshold[];
+// The name of the finch parameter. It signifies the consecutive nopes
+// threshold after which the user sees "Never for this site" button by default.
+extern const char kParamNopeThreshold[];
 
 }  // namespace password_bubble_experiment
 
diff --git a/chrome/browser/ui/passwords/password_bubble_experiment_unittest.cc b/chrome/browser/ui/passwords/password_bubble_experiment_unittest.cc
index 96265b5..e6084b950 100644
--- a/chrome/browser/ui/passwords/password_bubble_experiment_unittest.cc
+++ b/chrome/browser/ui/passwords/password_bubble_experiment_unittest.cc
@@ -16,38 +16,19 @@
 
 namespace {
 
-const int kTimeSpanDays = 2;
-const int kTimeSpanThreshold = 3;
-const int kProbabilityFakeSaves = 0;
-const int kProbabilityHistory = 10;
+const char kGroupName[] = "SomeGroupName";
+const int kNopeThreshold = 10;
 
-void SetupTimeSpanExperiment() {
+void SetupExperiment() {
   ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
       password_bubble_experiment::kExperimentName,
-      password_bubble_experiment::kGroupTimeSpanBased));
+      kGroupName));
   std::map<std::string, std::string> params;
-  params[password_bubble_experiment::kParamTimeSpan] =
-      base::IntToString(kTimeSpanDays);
-  params[password_bubble_experiment::kParamTimeSpanNopeThreshold] =
-      base::IntToString(kTimeSpanThreshold);
+  params[password_bubble_experiment::kParamNopeThreshold] =
+      base::IntToString(kNopeThreshold);
   ASSERT_TRUE(variations::AssociateVariationParams(
       password_bubble_experiment::kExperimentName,
-      password_bubble_experiment::kGroupTimeSpanBased,
-      params));
-}
-
-void SetupProbabilityExperiment() {
-  ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
-      password_bubble_experiment::kExperimentName,
-      password_bubble_experiment::kGroupProbabilityBased));
-  std::map<std::string, std::string> params;
-  params[password_bubble_experiment::kParamProbabilityFakeSaves] =
-      base::IntToString(kProbabilityFakeSaves);
-  params[password_bubble_experiment::kParamProbabilityInteractionsCount] =
-      base::IntToString(kProbabilityHistory);
-  ASSERT_TRUE(variations::AssociateVariationParams(
-      password_bubble_experiment::kExperimentName,
-      password_bubble_experiment::kGroupProbabilityBased,
+      kGroupName,
       params));
 }
 
@@ -61,6 +42,9 @@
 
     field_trial_list_.reset(new base::FieldTrialList(
         new metrics::SHA1EntropyProvider("foo")));
+  }
+
+  void TearDown() override {
     variations::testing::ClearAllVariationParams();
   }
 
@@ -72,68 +56,61 @@
   scoped_ptr<base::FieldTrialList> field_trial_list_;
 };
 
-TEST_F(PasswordBubbleExperimentTest, TimeSpan) {
-  SetupTimeSpanExperiment();
+TEST_F(PasswordBubbleExperimentTest, NoExperiment) {
+  EXPECT_FALSE(
+      password_bubble_experiment::ShouldShowNeverForThisSiteDefault(prefs()));
+  for (int i = 0; i <= kNopeThreshold; ++i) {
+    password_bubble_experiment::RecordBubbleClosed(
+        prefs(), password_manager::metrics_util::CLICKED_NOPE);
+    EXPECT_FALSE(
+        password_bubble_experiment::ShouldShowNeverForThisSiteDefault(prefs()));
+  }
+}
 
-  EXPECT_TRUE(password_bubble_experiment::ShouldShowBubble(prefs()));
-  // Don't save password enough times.
-  for (int i = 0; i < kTimeSpanThreshold; ++i) {
-    password_manager::metrics_util::UIDismissalReason reason = i % 2 ?
-        password_manager::metrics_util::NO_DIRECT_INTERACTION :
+TEST_F(PasswordBubbleExperimentTest, WithExperiment) {
+  SetupExperiment();
+
+  // Repeatedly click "Never". It shouldn't affect the state.
+  for (int i = 0; i < kNopeThreshold; ++i) {
+    password_manager::metrics_util::UIDismissalReason reason =
+        password_manager::metrics_util::CLICKED_NEVER;
+    password_bubble_experiment::RecordBubbleClosed(prefs(), reason);
+  }
+  EXPECT_FALSE(
+      password_bubble_experiment::ShouldShowNeverForThisSiteDefault(prefs()));
+  // Repeatedly refuse to save password, for |kNopeThreshold|-1 times.
+  for (int i = 0; i < kNopeThreshold - 1; ++i) {
+    password_manager::metrics_util::UIDismissalReason reason =
         password_manager::metrics_util::CLICKED_NOPE;
     password_bubble_experiment::RecordBubbleClosed(prefs(), reason);
   }
-  EXPECT_FALSE(password_bubble_experiment::ShouldShowBubble(prefs()));
+  EXPECT_FALSE(
+      password_bubble_experiment::ShouldShowNeverForThisSiteDefault(prefs()));
 
-  // Save password many times. It doesn't bring the bubble back while the time
-  // span isn't over.
-  for (int i = 0; i < 2*kTimeSpanThreshold; ++i) {
-    password_bubble_experiment::RecordBubbleClosed(
-        prefs(),
-        password_manager::metrics_util::CLICKED_SAVE);
-  }
-  EXPECT_FALSE(password_bubble_experiment::ShouldShowBubble(prefs()));
-}
-
-TEST_F(PasswordBubbleExperimentTest, TimeSpanOver) {
-  SetupTimeSpanExperiment();
-
-  base::Time past_interval =
-      base::Time::Now() - base::TimeDelta::FromDays(kTimeSpanDays + 1);
-  prefs()->SetInt64(prefs::kPasswordBubbleTimeStamp,
-                    past_interval.ToInternalValue());
-  prefs()->SetInteger(prefs::kPasswordBubbleNopesCount, kTimeSpanThreshold);
-  // The time span is over. The bubble should be shown.
-  EXPECT_TRUE(password_bubble_experiment::ShouldShowBubble(prefs()));
-  EXPECT_EQ(0, prefs()->GetInteger(prefs::kPasswordBubbleNopesCount));
-
-  // Set the old time span again and record "Nope". The counter restarts from 0.
-  prefs()->SetInt64(prefs::kPasswordBubbleTimeStamp,
-                    past_interval.ToInternalValue());
+  // Refuse to save once more to make Never the default button.
   password_bubble_experiment::RecordBubbleClosed(
       prefs(), password_manager::metrics_util::CLICKED_NOPE);
-  EXPECT_TRUE(password_bubble_experiment::ShouldShowBubble(prefs()));
-  EXPECT_EQ(1, prefs()->GetInteger(prefs::kPasswordBubbleNopesCount));
-}
+  EXPECT_TRUE(
+      password_bubble_experiment::ShouldShowNeverForThisSiteDefault(prefs()));
+  password_bubble_experiment::RecordBubbleClosed(
+      prefs(), password_manager::metrics_util::CLICKED_SAVE);
+  EXPECT_FALSE(
+      password_bubble_experiment::ShouldShowNeverForThisSiteDefault(prefs()));
 
-TEST_F(PasswordBubbleExperimentTest, Probability) {
-  SetupProbabilityExperiment();
-
-  EXPECT_TRUE(password_bubble_experiment::ShouldShowBubble(prefs()));
-  // Don't save password enough times.
-  for (int i = 0; i < kProbabilityHistory; ++i) {
-    password_manager::metrics_util::UIDismissalReason reason = i % 2 ?
-        password_manager::metrics_util::NO_DIRECT_INTERACTION :
+  // Repeatedly refuse to save password, for |kNopeThreshold| times.
+  for (int i = 0; i < kNopeThreshold; ++i) {
+    password_manager::metrics_util::UIDismissalReason reason =
         password_manager::metrics_util::CLICKED_NOPE;
     password_bubble_experiment::RecordBubbleClosed(prefs(), reason);
   }
-  EXPECT_FALSE(password_bubble_experiment::ShouldShowBubble(prefs()));
-
-  // Save password enough times.
-  for (int i = 0; i < kProbabilityHistory; ++i) {
-    password_bubble_experiment::RecordBubbleClosed(
-        prefs(),
-        password_manager::metrics_util::CLICKED_SAVE);
+  EXPECT_TRUE(
+      password_bubble_experiment::ShouldShowNeverForThisSiteDefault(prefs()));
+  // Repeatedly click "Never". It shouldn't affect the state.
+  for (int i = 0; i < kNopeThreshold; ++i) {
+    password_manager::metrics_util::UIDismissalReason reason =
+        password_manager::metrics_util::CLICKED_NEVER;
+    password_bubble_experiment::RecordBubbleClosed(prefs(), reason);
   }
-  EXPECT_TRUE(password_bubble_experiment::ShouldShowBubble(prefs()));
+  EXPECT_TRUE(
+      password_bubble_experiment::ShouldShowNeverForThisSiteDefault(prefs()));
 }
diff --git a/chrome/browser/ui/passwords/save_password_refusal_combobox_model.cc b/chrome/browser/ui/passwords/save_password_refusal_combobox_model.cc
index 8d397ee..f826de73 100644
--- a/chrome/browser/ui/passwords/save_password_refusal_combobox_model.cc
+++ b/chrome/browser/ui/passwords/save_password_refusal_combobox_model.cc
@@ -7,12 +7,16 @@
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
-SavePasswordRefusalComboboxModel::SavePasswordRefusalComboboxModel() {
+SavePasswordRefusalComboboxModel::SavePasswordRefusalComboboxModel(
+    bool never_is_default)
+    : never_is_default_(never_is_default) {
 #if !defined(OS_ANDROID)
   items_.push_back(
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_CANCEL_BUTTON));
   items_.push_back(
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_BLACKLIST_BUTTON));
+  if (never_is_default)
+    swap(items_[0], items_[1]);
 #endif
 }
 
diff --git a/chrome/browser/ui/passwords/save_password_refusal_combobox_model.h b/chrome/browser/ui/passwords/save_password_refusal_combobox_model.h
index 6a92878..49c59cd 100644
--- a/chrome/browser/ui/passwords/save_password_refusal_combobox_model.h
+++ b/chrome/browser/ui/passwords/save_password_refusal_combobox_model.h
@@ -12,19 +12,26 @@
 
 class SavePasswordRefusalComboboxModel : public ui::ComboboxModel {
  public:
-  enum { INDEX_NOPE = 0, INDEX_NEVER_FOR_THIS_SITE = 1 };
-
-  SavePasswordRefusalComboboxModel();
+  explicit SavePasswordRefusalComboboxModel(bool never_is_default);
   ~SavePasswordRefusalComboboxModel() override;
 
- private:
+  int index_nope() const {
+    return never_is_default_ ? 1 : 0;
+  }
+
+  int index_never() const {
+    return never_is_default_ ? 0 : 1;
+  }
+
   // Overridden from ui::ComboboxModel:
   int GetItemCount() const override;
   base::string16 GetItemAt(int index) override;
   bool IsItemSeparatorAt(int index) override;
   int GetDefaultIndex() const override;
 
+ private:
   std::vector<base::string16> items_;
+  const bool never_is_default_;
 
   DISALLOW_COPY_AND_ASSIGN(SavePasswordRefusalComboboxModel);
 };
diff --git a/chrome/browser/ui/toolbar/encoding_menu_controller.cc b/chrome/browser/ui/toolbar/encoding_menu_controller.cc
index 8d9089c..0f4a821 100644
--- a/chrome/browser/ui/toolbar/encoding_menu_controller.cc
+++ b/chrome/browser/ui/toolbar/encoding_menu_controller.cc
@@ -22,7 +22,6 @@
     IDC_ENCODING_GBK,
     IDC_ENCODING_GB18030,
     IDC_ENCODING_BIG5,
-    IDC_ENCODING_BIG5HKSCS,
     IDC_ENCODING_KOREAN,
     IDC_ENCODING_SHIFTJIS,
     IDC_ENCODING_ISO2022JP,
@@ -52,6 +51,7 @@
     IDC_ENCODING_WINDOWS1255,
     IDC_ENCODING_WINDOWS1258,
     IDC_ENCODING_ISO88598I,
+    IDC_ENCODING_IBM866,
 };
 
 bool EncodingMenuController::DoesCommandBelongToEncodingMenu(int id) {
diff --git a/chrome/browser/ui/views/app_list/win/app_list_service_win.cc b/chrome/browser/ui/views/app_list/win/app_list_service_win.cc
index ba5d155..57772f6 100644
--- a/chrome/browser/ui/views/app_list/win/app_list_service_win.cc
+++ b/chrome/browser/ui/views/app_list/win/app_list_service_win.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/shell_integration.h"
+#include "chrome/browser/ui/app_list/app_list_util.h"
 #include "chrome/browser/ui/ash/app_list/app_list_service_ash.h"
 #include "chrome/browser/ui/views/app_list/win/activation_tracker_win.h"
 #include "chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.h"
@@ -343,6 +344,12 @@
     case chrome::VersionInfo::CHANNEL_DEV:
     case chrome::VersionInfo::CHANNEL_BETA:
     case chrome::VersionInfo::CHANNEL_STABLE:
+      // Except on Canary, don't bother scheduling an app launcher warmup when
+      // it's not already enabled. Always schedule on Canary while collecting
+      // profiler data (see comment above).
+      if (!IsAppLauncherEnabled())
+        return;
+
       // Profiler instrumentations are not enabled.
       break;
   }
diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
index d1b4f670..4bdb7af 100644
--- a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
@@ -685,8 +685,6 @@
     paint.setStyle(SkPaint::kStroke_Style);
     canvas->DrawPath(arrow, paint);
   }
-
-  PaintChildren(canvas, views::CullSet());
 }
 
 void AutofillDialogViews::OverlayView::OnNativeThemeChanged(
@@ -763,12 +761,7 @@
 void AutofillDialogViews::NotificationArea::PaintChildren(
     gfx::Canvas* canvas,
     const views::CullSet& cull_set) {
-}
-
-void AutofillDialogViews::NotificationArea::OnPaint(gfx::Canvas* canvas) {
-  views::View::OnPaint(canvas);
   views::View::PaintChildren(canvas, views::CullSet());
-
   if (HasArrow()) {
     DrawArrow(
         canvas,
@@ -1002,11 +995,6 @@
   return kSuggestedButtonClassName;
 }
 
-void AutofillDialogViews::SuggestedButton::PaintChildren(
-    gfx::Canvas* canvas,
-    const views::CullSet& cull_set) {
-}
-
 void AutofillDialogViews::SuggestedButton::OnPaint(gfx::Canvas* canvas) {
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   const gfx::Insets insets = GetInsets();
diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.h b/chrome/browser/ui/views/autofill/autofill_dialog_views.h
index 6a10bf3..d4c8f662 100644
--- a/chrome/browser/ui/views/autofill/autofill_dialog_views.h
+++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.h
@@ -263,7 +263,6 @@
     const char* GetClassName() const override;
     void PaintChildren(gfx::Canvas* canvas,
                        const views::CullSet& cull_set) override;
-    void OnPaint(gfx::Canvas* canvas) override;
 
     void set_arrow_centering_anchor(
         const base::WeakPtr<views::View>& arrow_centering_anchor) {
@@ -345,8 +344,6 @@
     // views::MenuButton implementation.
     gfx::Size GetPreferredSize() const override;
     const char* GetClassName() const override;
-    void PaintChildren(gfx::Canvas* canvas,
-                       const views::CullSet& cull_set) override;
     void OnPaint(gfx::Canvas* canvas) override;
 
    private:
diff --git a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc
index dd3a8698..4a5f96e1 100644
--- a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc
+++ b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc
@@ -19,11 +19,13 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/combobox/combobox.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/throbber.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_client_view.h"
@@ -51,13 +53,17 @@
     : controller_(controller),
       main_contents_(nullptr),
       permanent_error_label_(nullptr),
+      input_row_(nullptr),
       cvc_input_(nullptr),
       month_input_(nullptr),
       year_input_(nullptr),
       error_label_(nullptr),
+      storage_row_(nullptr),
       storage_checkbox_(nullptr),
       progress_overlay_(nullptr),
+      progress_throbber_(nullptr),
       progress_label_(nullptr),
+      overlay_animation_(this),
       weak_ptr_factory_(this) {
 }
 
@@ -78,7 +84,10 @@
 
 void CardUnmaskPromptViews::DisableAndWaitForVerification() {
   SetInputsEnabled(false);
+  progress_overlay_->SetOpacity(0.0);
   progress_overlay_->SetVisible(true);
+  progress_throbber_->Start();
+  overlay_animation_.Show();
   GetDialogClientView()->UpdateDialogButtons();
   Layout();
 }
@@ -86,9 +95,11 @@
 void CardUnmaskPromptViews::GotVerificationResult(
     const base::string16& error_message,
     bool allow_retry) {
+  progress_throbber_->Stop();
   if (error_message.empty()) {
     progress_label_->SetText(l10n_util::GetStringUTF16(
         IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_SUCCESS));
+    progress_throbber_->SetChecked(true);
     base::MessageLoop::current()->PostDelayedTask(
         FROM_HERE, base::Bind(&CardUnmaskPromptViews::ClosePrompt,
                               weak_ptr_factory_.GetWeakPtr()),
@@ -96,6 +107,8 @@
   } else {
     // TODO(estade): it's somewhat jarring when the error comes back too
     // quickly.
+    overlay_animation_.Reset();
+    storage_row_->SetOpacity(1.0);
     progress_overlay_->SetVisible(false);
 
     if (allow_retry) {
@@ -159,25 +172,25 @@
     return nullptr;
 
   // Local storage checkbox and (?) tooltip.
-  views::View* storage_row = new views::View();
+  storage_row_ = new FadeOutView();
   views::BoxLayout* storage_row_layout = new views::BoxLayout(
       views::BoxLayout::kHorizontal, kEdgePadding, kEdgePadding, 0);
-  storage_row->SetLayoutManager(storage_row_layout);
-  storage_row->SetBorder(
+  storage_row_->SetLayoutManager(storage_row_layout);
+  storage_row_->SetBorder(
       views::Border::CreateSolidSidedBorder(1, 0, 0, 0, kSubtleBorderColor));
-  storage_row->set_background(
+  storage_row_->set_background(
       views::Background::CreateSolidBackground(kShadingColor));
 
   storage_checkbox_ = new views::Checkbox(l10n_util::GetStringUTF16(
       IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_CHECKBOX));
   storage_checkbox_->SetChecked(controller_->GetStoreLocallyStartState());
-  storage_row->AddChildView(storage_checkbox_);
+  storage_row_->AddChildView(storage_checkbox_);
   storage_row_layout->SetFlexForView(storage_checkbox_, 1);
 
-  storage_row->AddChildView(new TooltipIcon(l10n_util::GetStringUTF16(
+  storage_row_->AddChildView(new TooltipIcon(l10n_util::GetStringUTF16(
       IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_TOOLTIP)));
 
-  return storage_row;
+  return storage_row_;
 }
 
 gfx::Size CardUnmaskPromptViews::GetPreferredSize() const {
@@ -189,9 +202,16 @@
 }
 
 void CardUnmaskPromptViews::Layout() {
-  for (int i = 0; i < child_count(); ++i) {
-    child_at(i)->SetBoundsRect(GetContentsBounds());
-  }
+  gfx::Rect contents_bounds = GetContentsBounds();
+  main_contents_->SetBoundsRect(contents_bounds);
+
+  // The progress overlay extends from the top of the input row
+  // to the bottom of the content area.
+  gfx::RectF input_rect = input_row_->GetContentsBounds();
+  View::ConvertRectToTarget(input_row_, this, &input_rect);
+  input_rect.set_height(contents_bounds.height());
+  contents_bounds.Intersect(gfx::ToNearestRect(input_rect));
+  progress_overlay_->SetBoundsRect(contents_bounds);
 }
 
 int CardUnmaskPromptViews::GetHeightForWidth(int width) const {
@@ -205,9 +225,9 @@
 void CardUnmaskPromptViews::OnNativeThemeChanged(const ui::NativeTheme* theme) {
   SkColor bg_color =
       theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground);
-  bg_color = SkColorSetA(bg_color, 0xDD);
   progress_overlay_->set_background(
       views::Background::CreateSolidBackground(bg_color));
+  progress_label_->SetBackgroundColor(bg_color);
 }
 
 ui::ModalType CardUnmaskPromptViews::GetModalType() const {
@@ -303,6 +323,12 @@
   GetDialogClientView()->UpdateDialogButtons();
 }
 
+void CardUnmaskPromptViews::AnimationProgressed(
+    const gfx::Animation* animation) {
+  progress_overlay_->SetOpacity(animation->GetCurrentValue());
+  storage_row_->SetOpacity(1.0 - animation->GetCurrentValue());
+}
+
 void CardUnmaskPromptViews::InitIfNecessary() {
   if (has_children())
     return;
@@ -338,33 +364,33 @@
   instructions->SetBorder(views::Border::CreateEmptyBorder(0, 0, 15, 0));
   controls_container->AddChildView(instructions);
 
-  views::View* input_row = new views::View();
-  input_row->SetLayoutManager(
+  input_row_ = new views::View();
+  input_row_->SetLayoutManager(
       new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 5));
-  controls_container->AddChildView(input_row);
+  controls_container->AddChildView(input_row_);
 
   if (controller_->ShouldRequestExpirationDate()) {
     month_input_ = new views::Combobox(&month_combobox_model_);
     month_input_->set_listener(this);
-    input_row->AddChildView(month_input_);
-    input_row->AddChildView(new views::Label(l10n_util::GetStringUTF16(
+    input_row_->AddChildView(month_input_);
+    input_row_->AddChildView(new views::Label(l10n_util::GetStringUTF16(
         IDS_AUTOFILL_CARD_UNMASK_EXPIRATION_DATE_SEPARATOR)));
     year_input_ = new views::Combobox(&year_combobox_model_);
     year_input_->set_listener(this);
-    input_row->AddChildView(year_input_);
-    input_row->AddChildView(new views::Label(base::ASCIIToUTF16("    ")));
+    input_row_->AddChildView(year_input_);
+    input_row_->AddChildView(new views::Label(base::ASCIIToUTF16("    ")));
   }
 
   cvc_input_ = new DecoratedTextfield(
       base::string16(),
       l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC), this);
   cvc_input_->set_default_width_in_chars(8);
-  input_row->AddChildView(cvc_input_);
+  input_row_->AddChildView(cvc_input_);
 
   views::ImageView* cvc_image = new views::ImageView();
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   cvc_image->SetImage(rb.GetImageSkiaNamed(controller_->GetCvcImageRid()));
-  input_row->AddChildView(cvc_image);
+  input_row_->AddChildView(cvc_image);
 
   // Reserve vertical space for the error label, assuming it's one line.
   error_label_ = new views::Label(base::ASCIIToUTF16(" "));
@@ -373,20 +399,26 @@
   error_label_->SetBorder(views::Border::CreateEmptyBorder(3, 0, 5, 0));
   controls_container->AddChildView(error_label_);
 
-  progress_overlay_ = new views::View();
+  progress_overlay_ = new FadeOutView();
+  progress_overlay_->set_fade_everything(true);
   views::BoxLayout* progress_layout =
-      new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
-  progress_layout->set_main_axis_alignment(
-      views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
+      new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 5);
   progress_layout->set_cross_axis_alignment(
       views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
+  progress_layout->set_main_axis_alignment(
+      views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
   progress_overlay_->SetLayoutManager(progress_layout);
 
   progress_overlay_->SetVisible(false);
   AddChildView(progress_overlay_);
 
+  progress_throbber_ = new views::CheckmarkThrobber();
+  progress_overlay_->AddChildView(progress_throbber_);
+
   progress_label_ = new views::Label(l10n_util::GetStringUTF16(
       IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_IN_PROGRESS));
+  // Material blue. TODO(estade): find an appropriate place for this color.
+  progress_label_->SetEnabledColor(SkColorSetRGB(0x42, 0x85, 0xF4));
   progress_overlay_->AddChildView(progress_label_);
 }
 
@@ -403,4 +435,35 @@
   GetWidget()->Close();
 }
 
+CardUnmaskPromptViews::FadeOutView::FadeOutView()
+    : fade_everything_(false), opacity_(1.0) {
+}
+CardUnmaskPromptViews::FadeOutView::~FadeOutView() {
+}
+
+void CardUnmaskPromptViews::FadeOutView::PaintChildren(
+    gfx::Canvas* canvas,
+    const views::CullSet& cull_set) {
+  if (opacity_ > 0.99)
+    return views::View::PaintChildren(canvas, cull_set);
+
+  canvas->SaveLayerAlpha(0xff * opacity_);
+  views::View::PaintChildren(canvas, cull_set);
+  canvas->Restore();
+}
+
+void CardUnmaskPromptViews::FadeOutView::OnPaint(gfx::Canvas* canvas) {
+  if (!fade_everything_ || opacity_ > 0.99)
+    return views::View::OnPaint(canvas);
+
+  canvas->SaveLayerAlpha(0xff * opacity_);
+  views::View::OnPaint(canvas);
+  canvas->Restore();
+}
+
+void CardUnmaskPromptViews::FadeOutView::SetOpacity(double opacity) {
+  opacity_ = opacity;
+  SchedulePaint();
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h
index fa1f442..115c4160 100644
--- a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h
+++ b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h
@@ -7,6 +7,8 @@
 
 #include "chrome/browser/ui/autofill/autofill_dialog_models.h"
 #include "chrome/browser/ui/autofill/card_unmask_prompt_view.h"
+#include "ui/gfx/animation/animation_delegate.h"
+#include "ui/gfx/animation/slide_animation.h"
 #include "ui/views/controls/combobox/combobox_listener.h"
 #include "ui/views/controls/textfield/textfield_controller.h"
 #include "ui/views/window/dialog_delegate.h"
@@ -14,6 +16,7 @@
 namespace views {
 class Label;
 class Checkbox;
+class CheckmarkThrobber;
 }
 
 namespace autofill {
@@ -23,7 +26,8 @@
 class CardUnmaskPromptViews : public CardUnmaskPromptView,
                               views::ComboboxListener,
                               views::DialogDelegateView,
-                              views::TextfieldController {
+                              views::TextfieldController,
+                              gfx::AnimationDelegate {
  public:
   explicit CardUnmaskPromptViews(CardUnmaskPromptController* controller);
 
@@ -63,9 +67,40 @@
   // views::ComboboxListener
   void OnPerformAction(views::Combobox* combobox) override;
 
+  // gfx::AnimationDelegate
+  void AnimationProgressed(const gfx::Animation* animation) override;
+
  private:
   friend class CardUnmaskPromptViewTesterViews;
 
+  // A view that allows changing the opacity of its contents.
+  class FadeOutView : public views::View {
+   public:
+    FadeOutView();
+    ~FadeOutView() override;
+
+    // views::View
+    void PaintChildren(gfx::Canvas* canvas,
+                       const views::CullSet& cull_set) override;
+    void OnPaint(gfx::Canvas* canvas) override;
+
+    void set_fade_everything(bool fade_everything) {
+      fade_everything_ = fade_everything;
+    }
+    void SetOpacity(double opacity);
+
+   private:
+    // Controls whether the background and border are faded out as well. Default
+    // is false, meaning only children are faded.
+    bool fade_everything_;
+
+    // On a scale of 0-1, how much to fade out the contents of this view. 0 is
+    // totally invisible, 1 is totally visible.
+    double opacity_;
+
+    DISALLOW_COPY_AND_ASSIGN(FadeOutView);
+  };
+
   void InitIfNecessary();
   void SetRetriableErrorMessage(const base::string16& message);
   bool ExpirationDateIsValid() const;
@@ -79,6 +114,9 @@
   // The error label for permanent errors (where the user can't retry).
   views::Label* permanent_error_label_;
 
+  // Holds the cvc and expiration inputs.
+  views::View* input_row_;
+
   DecoratedTextfield* cvc_input_;
 
   // These will be null when expiration date is not required.
@@ -91,11 +129,15 @@
   // The error label for most errors, which lives beneath the inputs.
   views::Label* error_label_;
 
+  FadeOutView* storage_row_;
   views::Checkbox* storage_checkbox_;
 
-  views::View* progress_overlay_;
+  FadeOutView* progress_overlay_;
+  views::CheckmarkThrobber* progress_throbber_;
   views::Label* progress_label_;
 
+  gfx::SlideAnimation overlay_animation_;
+
   base::WeakPtrFactory<CardUnmaskPromptViews> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CardUnmaskPromptViews);
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc
index 47bd73a0..7d18728 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.cc
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -211,52 +211,38 @@
     bubble_content_empty = false;
   }
 
-  if (!bubble_content.plugin_names.empty()) {
-    const int kPluginsColumnSetId = 4;
-    layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
-    views::ColumnSet* plugins_column_set =
-        layout->AddColumnSet(kPluginsColumnSetId);
-    plugins_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1,
-                                  GridLayout::USE_PREF, 0, 0);
-    plugins_column_set->AddPaddingColumn(
+  // Layout for the item list (blocked plugins and popups).
+  if (!bubble_content.list_items.empty()) {
+    const int kItemListColumnSetId = 2;
+    views::ColumnSet* item_list_column_set =
+        layout->AddColumnSet(kItemListColumnSetId);
+    item_list_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0,
+                                    GridLayout::USE_PREF, 0, 0);
+    item_list_column_set->AddPaddingColumn(
         0, views::kRelatedControlHorizontalSpacing);
-    plugins_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1,
-                                  GridLayout::USE_PREF, 0, 0);
+    item_list_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1,
+                                    GridLayout::USE_PREF, 0, 0);
 
-    views::Label* plugin_names_label =
-        new views::Label(bubble_content.plugin_names);
-    plugin_names_label->SetMultiLine(true);
-    plugin_names_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    layout->StartRow(0, kPluginsColumnSetId);
-    layout->AddView(plugin_names_label);
-    bubble_content_empty = false;
-  }
-
-  if (content_setting_bubble_model_->content_type() ==
-      CONTENT_SETTINGS_TYPE_POPUPS) {
-    const int kPopupColumnSetId = 2;
-    views::ColumnSet* popup_column_set =
-        layout->AddColumnSet(kPopupColumnSetId);
-    popup_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0,
-                                GridLayout::USE_PREF, 0, 0);
-    popup_column_set->AddPaddingColumn(
-        0, views::kRelatedControlHorizontalSpacing);
-    popup_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1,
-                                GridLayout::USE_PREF, 0, 0);
-
-    for (std::vector<ContentSettingBubbleModel::PopupItem>::const_iterator
-         i(bubble_content.popup_items.begin());
-         i != bubble_content.popup_items.end(); ++i) {
+    int row = 0;
+    for (const ContentSettingBubbleModel::ListItem& list_item :
+         bubble_content.list_items) {
       if (!bubble_content_empty)
         layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
-      layout->StartRow(0, kPopupColumnSetId);
-
-      views::Link* link = new views::Link(base::UTF8ToUTF16(i->title));
-      link->set_listener(this);
-      link->SetElideBehavior(gfx::ELIDE_MIDDLE);
-      popup_links_[link] = i - bubble_content.popup_items.begin();
-      layout->AddView(new Favicon(i->image, this, link));
-      layout->AddView(link);
+      layout->StartRow(0, kItemListColumnSetId);
+      if (list_item.has_link) {
+        views::Link* link = new views::Link(base::UTF8ToUTF16(list_item.title));
+        link->set_listener(this);
+        link->SetElideBehavior(gfx::ELIDE_MIDDLE);
+        list_item_links_[link] = row;
+        layout->AddView(new Favicon(list_item.image, this, link));
+        layout->AddView(link);
+      } else {
+        views::Label* label =
+            new views::Label(base::UTF8ToUTF16(list_item.title));
+        layout->AddView(new Favicon(list_item.image, this, NULL));
+        layout->AddView(label);
+      }
+      row++;
       bubble_content_empty = false;
     }
   }
@@ -295,7 +281,7 @@
   // Layout code for the media device menus.
   if (content_setting_bubble_model_->content_type() ==
       CONTENT_SETTINGS_TYPE_MEDIASTREAM) {
-    const int kMediaMenuColumnSetId = 2;
+    const int kMediaMenuColumnSetId = 4;
     views::ColumnSet* menu_column_set =
         layout->AddColumnSet(kMediaMenuColumnSetId);
     menu_column_set->AddPaddingColumn(0, views::kCheckboxIndent);
@@ -463,9 +449,9 @@
     return;
   }
 
-  PopupLinks::const_iterator i(popup_links_.find(source));
-  DCHECK(i != popup_links_.end());
-  content_setting_bubble_model_->OnPopupClicked(i->second);
+  ListItemLinks::const_iterator i(list_item_links_.find(source));
+  DCHECK(i != list_item_links_.end());
+  content_setting_bubble_model_->OnListItemClicked(i->second);
 }
 
 void ContentSettingBubbleContents::OnMenuButtonClicked(
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.h b/chrome/browser/ui/views/content_setting_bubble_contents.h
index 30ad0885..62968a4 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.h
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.h
@@ -68,7 +68,7 @@
   class Favicon;
   struct MediaMenuParts;
 
-  typedef std::map<views::Link*, int> PopupLinks;
+  typedef std::map<views::Link*, int> ListItemLinks;
   typedef std::map<views::MenuButton*, MediaMenuParts*> MediaMenuPartsMap;
 
   // content::WebContentsObserver:
@@ -97,7 +97,7 @@
 
   // Some of our controls, so we can tell what's been clicked when we get a
   // message.
-  PopupLinks popup_links_;
+  ListItemLinks list_item_links_;
   typedef std::vector<views::RadioButton*> RadioGroup;
   RadioGroup radio_group_;
   views::Link* custom_link_;
diff --git a/chrome/browser/ui/views/find_bar_view.cc b/chrome/browser/ui/views/find_bar_view.cc
index 57373ea..c64082d 100644
--- a/chrome/browser/ui/views/find_bar_view.cc
+++ b/chrome/browser/ui/views/find_bar_view.cc
@@ -22,6 +22,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "grit/theme_resources.h"
 #include "third_party/skia/include/core/SkPaint.h"
+#include "ui/base/ime/text_input_flags.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/theme_provider.h"
@@ -88,6 +89,7 @@
   find_text_->set_default_width_in_chars(kDefaultCharWidth);
   find_text_->set_controller(this);
   find_text_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FIND));
+  find_text_->SetTextInputFlags(ui::TEXT_INPUT_FLAG_AUTOCORRECT_OFF);
   // The find bar textfield has a background image instead of a border.
   find_text_->SetBorder(views::Border::NullBorder());
   AddChildView(find_text_);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index 0e7cbcc..41afcab 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -70,12 +70,12 @@
   if (!is_visible)
     return;
 
-  // The first time UpdateAvatarInfo() is called the window is not visible so
-  // DrawTaskBarDecoration() has no effect. Therefore we need to call it again
-  // once the window is visible.
+  // The first time UpdateOldAvatarButton() is called the window is not visible
+  // so DrawTaskBarDecoration() has no effect. Therefore we need to call it
+  // again once the window is visible.
   if (!browser_view_->IsRegularOrGuestSession() ||
       !switches::IsNewAvatarMenu()) {
-    UpdateAvatarInfo();
+    UpdateOldAvatarButton();
   }
 
   // Make sure the task bar icon is correctly updated call
@@ -175,12 +175,12 @@
 
 void BrowserNonClientFrameView::UpdateAvatar() {
   if (browser_view()->IsRegularOrGuestSession() && switches::IsNewAvatarMenu())
-    UpdateNewStyleAvatar();
+    UpdateNewAvatarButtonImpl();
   else
-    UpdateAvatarInfo();
+    UpdateOldAvatarButton();
 }
 
-void BrowserNonClientFrameView::UpdateAvatarInfo() {
+void BrowserNonClientFrameView::UpdateOldAvatarButton() {
   if (browser_view_->ShouldShowAvatar()) {
     if (!avatar_button_) {
 #if defined(ENABLE_SUPERVISED_USERS)
@@ -237,7 +237,7 @@
     avatar_button_->SetAvatarIcon(avatar, is_rectangle);
 }
 
-void BrowserNonClientFrameView::UpdateNewStyleAvatarInfo(
+void BrowserNonClientFrameView::UpdateNewAvatarButton(
     views::ButtonListener* listener,
     const NewAvatarButton::AvatarButtonStyle style) {
   DCHECK(switches::IsNewAvatarMenu());
@@ -277,13 +277,7 @@
   UpdateTaskbarDecoration();
   // Profile avatars are only displayed in the old UI or incognito.
   if (browser_view()->IsOffTheRecord() || !switches::IsNewAvatarMenu())
-    UpdateAvatarInfo();
-}
-
-void BrowserNonClientFrameView::OnProfileNameChanged(
-    const base::FilePath& profile_path,
-    const base::string16& old_profile_name) {
-  UpdateAvatar();
+    UpdateOldAvatarButton();
 }
 
 void BrowserNonClientFrameView::UpdateTaskbarDecoration() {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
index 7a552db4..6b70655a 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
@@ -80,22 +80,22 @@
 
   // Updates the avatar button using the old or new UI based on the BrowserView
   // type, and the presence of the --enable-new-avatar-menu flag. Calls either
-  // UpdateAvatarInfo() or UpdateNewStyleAvatar() accordingly.
+  // UpdateOldAvatarButton() or UpdateNewAvatarButtonImpl() accordingly.
   void UpdateAvatar();
 
   // Updates the title and icon of the old avatar button.
-  void UpdateAvatarInfo();
+  void UpdateOldAvatarButton();
 
   // Updates the avatar button displayed in the caption area by calling
-  // UpdateNewStyleAvatarInfo() with an implementation specific |listener|
+  // UpdateNewAvatarButton() with an implementation specific |listener|
   // and button |style|.
-  virtual void UpdateNewStyleAvatar() = 0;
+  virtual void UpdateNewAvatarButtonImpl() = 0;
 
   // Updates the title of the avatar button displayed in the caption area.
   // The button uses |style| to match the browser window style and notifies
   // |listener| when it is clicked.
-  void UpdateNewStyleAvatarInfo(views::ButtonListener* listener,
-                                const NewAvatarButton::AvatarButtonStyle style);
+  void UpdateNewAvatarButton(views::ButtonListener* listener,
+                             const NewAvatarButton::AvatarButtonStyle style);
 
  private:
   // Overriden from ProfileInfoCacheObserver.
@@ -103,8 +103,6 @@
   void OnProfileWasRemoved(const base::FilePath& profile_path,
                            const base::string16& profile_name) override;
   void OnProfileAvatarChanged(const base::FilePath& profile_path) override;
-  void OnProfileNameChanged(const base::FilePath& profile_path,
-                            const base::string16& old_profile_name) override;
 
   // Draws a taskbar icon if avatars are enabled, erases it otherwise.
   void UpdateTaskbarDecoration();
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 378448f..9e8e776 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -468,8 +468,8 @@
 // BrowserNonClientFrameViewAsh, protected:
 
 // BrowserNonClientFrameView:
-void BrowserNonClientFrameViewAsh::UpdateNewStyleAvatar() {
-  UpdateNewStyleAvatarInfo(this, NewAvatarButton::NATIVE_BUTTON);
+void BrowserNonClientFrameViewAsh::UpdateNewAvatarButtonImpl() {
+  UpdateNewAvatarButton(this, NewAvatarButton::NATIVE_BUTTON);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
index c0ea724..8b8132e2 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -79,7 +79,7 @@
 
  protected:
   // BrowserNonClientFrameView:
-  void UpdateNewStyleAvatar() override;
+  void UpdateNewAvatarButtonImpl() override;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest, WindowHeader);
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index 7d70388..5868fa9 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -300,8 +300,8 @@
 }
 
 // BrowserNonClientFrameView:
-void GlassBrowserFrameView::UpdateNewStyleAvatar() {
-  UpdateNewStyleAvatarInfo(this, NewAvatarButton::NATIVE_BUTTON);
+void GlassBrowserFrameView::UpdateNewAvatarButtonImpl() {
+  UpdateNewAvatarButton(this, NewAvatarButton::NATIVE_BUTTON);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.h b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
index 54db3f9..7d29eb9 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
@@ -48,7 +48,7 @@
                              const ui::Event& event) override;
 
   // BrowserNonClientFrameView:
-  void UpdateNewStyleAvatar() override;
+  void UpdateNewAvatarButtonImpl() override;
 
  private:
   // views::NonClientFrameView:
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
index 2a6f984..13d84417 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -485,8 +485,8 @@
          platform_observer_->IsUsingSystemTheme();
 }
 
-void OpaqueBrowserFrameView::UpdateNewStyleAvatar() {
-  UpdateNewStyleAvatarInfo(this, NewAvatarButton::THEMED_BUTTON);
+void OpaqueBrowserFrameView::UpdateNewAvatarButtonImpl() {
+  UpdateNewAvatarButton(this, NewAvatarButton::THEMED_BUTTON);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
index 1a355b0..4e3ef389 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
@@ -98,7 +98,7 @@
 
   // BrowserNonClientFrameView:
   bool ShouldPaintAsThemed() const override;
-  void UpdateNewStyleAvatar() override;
+  void UpdateNewAvatarButtonImpl() override;
 
  private:
   // views::NonClientFrameView:
diff --git a/chrome/browser/ui/views/frame/web_app_left_header_view_ash_unittest.cc b/chrome/browser/ui/views/frame/web_app_left_header_view_ash_unittest.cc
index ca90496..03a1117 100644
--- a/chrome/browser/ui/views/frame/web_app_left_header_view_ash_unittest.cc
+++ b/chrome/browser/ui/views/frame/web_app_left_header_view_ash_unittest.cc
@@ -88,7 +88,7 @@
     scoped_refptr<extensions::Extension> extension(
         extensions::Extension::Create(
             base::FilePath(), extensions::Manifest::UNPACKED, manifest,
-            extensions::Extension::NO_FLAGS, "abc", &error));
+            extensions::Extension::FROM_BOOKMARK, "abc", &error));
 
     ASSERT_TRUE(extension.get()) << error;
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index 3e6e2054..61f394a 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -370,12 +370,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 // OmniboxPopupContentsView, protected:
 
-void OmniboxPopupContentsView::PaintResultViews(gfx::Canvas* canvas) {
-  canvas->DrawColor(result_view_at(0)->GetColor(
-      OmniboxResultView::NORMAL, OmniboxResultView::BACKGROUND));
-  View::PaintChildren(canvas, views::CullSet());
-}
-
 int OmniboxPopupContentsView::CalculatePopupHeight() {
   DCHECK_GE(static_cast<size_t>(child_count()), model_->result().size());
   int popup_height = 0;
@@ -409,19 +403,6 @@
 }
 
 void OmniboxPopupContentsView::OnPaint(gfx::Canvas* canvas) {
-  gfx::Rect contents_bounds = GetContentsBounds();
-  contents_bounds.set_height(
-      contents_bounds.height() - bottom_shadow_->height() + kBorderInterior);
-
-  gfx::Path path;
-  MakeContentsPath(&path, contents_bounds);
-  canvas->Save();
-  canvas->sk_canvas()->clipPath(path,
-                                SkRegion::kIntersect_Op,
-                                true /* doAntialias */);
-  PaintResultViews(canvas);
-  canvas->Restore();
-
   // Top border.
   canvas->FillRect(
       gfx::Rect(0, 0, width(), views::NonClientFrameView::kClientEdgeThickness),
@@ -435,7 +416,18 @@
 
 void OmniboxPopupContentsView::PaintChildren(gfx::Canvas* canvas,
                                              const views::CullSet& cull_set) {
-  // We paint our children inside OnPaint().
+  gfx::Rect contents_bounds = GetContentsBounds();
+  contents_bounds.Inset(0, views::NonClientFrameView::kClientEdgeThickness, 0,
+                        bottom_shadow_->height() - kBorderInterior);
+
+  canvas->Save();
+  canvas->sk_canvas()->clipRect(gfx::RectToSkRect(contents_bounds),
+                                SkRegion::kIntersect_Op,
+                                true /* doAntialias */);
+  canvas->DrawColor(result_view_at(0)->GetColor(OmniboxResultView::NORMAL,
+                                                OmniboxResultView::BACKGROUND));
+  View::PaintChildren(canvas, cull_set);
+  canvas->Restore();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -456,17 +448,6 @@
   return model_->result().match_at(index);
 }
 
-void OmniboxPopupContentsView::MakeContentsPath(
-    gfx::Path* path,
-    const gfx::Rect& bounding_rect) {
-  SkRect rect;
-  rect.set(SkIntToScalar(bounding_rect.x()),
-           SkIntToScalar(bounding_rect.y()),
-           SkIntToScalar(bounding_rect.right()),
-           SkIntToScalar(bounding_rect.bottom()));
-  path->addRect(rect);
-}
-
 size_t OmniboxPopupContentsView::GetIndexForPoint(
     const gfx::Point& point) {
   if (!HitTestPoint(point))
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
index 855135c..e655ffa 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
@@ -81,8 +81,6 @@
 
   LocationBarView* location_bar_view() { return location_bar_view_; }
 
-  virtual void PaintResultViews(gfx::Canvas* canvas);
-
   // Calculates the height needed to show all the results in the model.
   virtual int CalculatePopupHeight();
   virtual OmniboxResultView* CreateResultView(int model_index,
@@ -94,10 +92,6 @@
   // views::View:
   const char* GetClassName() const override;
   void OnPaint(gfx::Canvas* canvas) override;
-  // This method should not be triggered directly as we paint our children
-  // in an un-conventional way inside OnPaint. We use a separate canvas to
-  // paint the children. Hence we override this method to a no-op so that
-  // the view hierarchy does not "accidentally" trigger this.
   void PaintChildren(gfx::Canvas* canvas,
                      const views::CullSet& cull_set) override;
 
@@ -113,10 +107,6 @@
   // Returns the match at the specified index within the popup model.
   const AutocompleteMatch& GetMatchAtIndex(size_t index) const;
 
-  // Fill a path for the contents' roundrect. |bounding_rect| is the rect that
-  // bounds the path.
-  void MakeContentsPath(gfx::Path* path, const gfx::Rect& bounding_rect);
-
   // Find the index of the match under the given |point|, specified in window
   // coordinates. Returns OmniboxPopupModel::kNoMatch if there isn't a match at
   // the specified point.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index 911c26a..283f9e4 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -197,8 +197,8 @@
      gfx::NORMAL_BASELINE},
 };
 
-const TextStyle& GetTextStyle(size_t type) {
-  if (type > arraysize(kTextStyles))
+const TextStyle& GetTextStyle(int type) {
+  if (type < 1 || static_cast<size_t>(type) > arraysize(kTextStyles))
     type = 1;
   // Subtract one because the types are one based (not zero based).
   return kTextStyles[type - 1];
@@ -699,13 +699,16 @@
       if (match_.answer) {
         base::string16 text;
         description_rendertext_ = CreateRenderText(text);
-        for (const SuggestionAnswer::TextField& textfield :
+        for (const SuggestionAnswer::TextField& text_field :
              match_.answer->second_line().text_fields())
-          AppendAnswerText(textfield);
-        if (match_.answer->second_line().additional_text())
-          AppendAnswerText(*match_.answer->second_line().additional_text());
-        if (match_.answer->second_line().status_text())
-          AppendAnswerText(*match_.answer->second_line().status_text());
+          AppendAnswerText(text_field.text(), text_field.type());
+        const base::char16 space(' ');
+        const auto* text_field = match_.answer->second_line().additional_text();
+        if (text_field)
+          AppendAnswerText(space + text_field->text(), text_field->type());
+        text_field = match_.answer->second_line().status_text();
+        if (text_field)
+          AppendAnswerText(space + text_field->text(), text_field->type());
       } else if (!match_.description.empty()) {
         description_rendertext_ = CreateClassifiedRenderText(
             match_.description, match_.description_class, true);
@@ -755,12 +758,12 @@
                   GetTextHeight() + (kMinimumTextVerticalPadding * 2));
 }
 
-void OmniboxResultView::AppendAnswerText(
-    const SuggestionAnswer::TextField& text_field) {
+void OmniboxResultView::AppendAnswerText(const base::string16& text,
+                                         int text_type) {
   int offset = description_rendertext_->text().length();
-  gfx::Range range(offset, offset + text_field.text().length());
-  description_rendertext_->AppendText(text_field.text());
-  const TextStyle& text_style = GetTextStyle(text_field.type());
+  gfx::Range range(offset, offset + text.length());
+  description_rendertext_->AppendText(text);
+  const TextStyle& text_style = GetTextStyle(text_type);
   // TODO(dschuyler): follow up on the problem of different font sizes within
   // one RenderText.
   description_rendertext_->SetFontList(
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.h b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
index 1df1b2c..984183d 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
@@ -10,7 +10,6 @@
 #include "base/gtest_prod_util.h"
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service.h"
 #include "components/omnibox/autocomplete_match.h"
-#include "components/omnibox/suggestion_answer.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/animation/animation_delegate.h"
 #include "ui/gfx/animation/slide_animation.h"
@@ -160,7 +159,11 @@
   int GetAnswerLineHeight() const;
   int GetContentLineHeight() const;
 
-  void AppendAnswerText(const SuggestionAnswer::TextField& text_field);
+  // Adds |text| to |description_rendertext_|.  |text_type| is an index into the
+  // kTextStyles constant defined in the .cc file and is used to style the text,
+  // including setting the font size, color, and baseline style.  See the
+  // TextStyle struct in the .cc file for more.
+  void AppendAnswerText(const base::string16& text, int text_type);
 
   static int default_icon_size_;
 
diff --git a/chrome/browser/ui/views/passwords/credentials_item_view.h b/chrome/browser/ui/views/passwords/credentials_item_view.h
index 4291513..67491a8 100644
--- a/chrome/browser/ui/views/passwords/credentials_item_view.h
+++ b/chrome/browser/ui/views/passwords/credentials_item_view.h
@@ -8,7 +8,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/passwords/account_avatar_fetcher.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "ui/views/controls/button/label_button.h"
 
 namespace autofill {
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
index e805f128..b1bede3a 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
+#include "chrome/browser/ui/passwords/password_bubble_experiment.h"
 #include "chrome/browser/ui/passwords/save_password_refusal_combobox_model.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/passwords/credentials_item_view.h"
@@ -397,7 +398,9 @@
   save_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
       ui::ResourceBundle::SmallFont));
 
-  combobox_model_.reset(new SavePasswordRefusalComboboxModel());
+  combobox_model_.reset(new SavePasswordRefusalComboboxModel(
+      password_bubble_experiment::ShouldShowNeverForThisSiteDefault(
+          parent_->model()->GetProfile()->GetPrefs())));
   refuse_combobox_.reset(new views::Combobox(combobox_model_.get()));
   refuse_combobox_->set_listener(this);
   refuse_combobox_->SetStyle(views::Combobox::STYLE_ACTION);
@@ -438,14 +441,13 @@
 void ManagePasswordsBubbleView::PendingView::OnPerformAction(
     views::Combobox* source) {
   DCHECK_EQ(source, refuse_combobox_);
-  switch (refuse_combobox_->selected_index()) {
-    case SavePasswordRefusalComboboxModel::INDEX_NOPE:
-      parent_->model()->OnNopeClicked();
-      parent_->Close();
-      break;
-    case SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE:
-      parent_->NotifyNeverForThisSiteClicked();
-      break;
+  if (source->selected_index() == combobox_model_->index_nope()) {
+    parent_->model()->OnNopeClicked();
+    parent_->Close();
+  } else if (source->selected_index() == combobox_model_->index_never()) {
+    parent_->NotifyNeverForThisSiteClicked();
+  } else {
+    NOTREACHED();
   }
 }
 
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
index e349973..4d9e45f 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -1279,9 +1279,7 @@
   views::View* view = new views::View();
   views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth);
 
-  int num_avatars_to_show = avatars_to_show.size();
-  for (int i = 0; i < num_avatars_to_show; ++i) {
-    const size_t index = avatars_to_show[i];
+  for (size_t index : avatars_to_show) {
     const AvatarMenu::Item& item = avatar_menu_->GetItemAt(index);
     const int kSmallImageSide = 32;
 
@@ -1293,9 +1291,11 @@
         item.profile_path, &item_icon, &is_rectangle);
 
     base::string16 title = item.name;
-    if (item.supervised) {
+    if (item.legacy_supervised) {
       title = l10n_util::GetStringFUTF16(IDS_SUPERVISED_USER_NEW_AVATAR_LABEL,
                                          title);
+    } else if (item.child_account) {
+      title = l10n_util::GetStringFUTF16(IDS_CHILD_AVATAR_LABEL, title);
     }
 
     gfx::Image image = profiles::GetSizedAvatarIcon(
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector.cc b/chrome/browser/ui/views/ssl_client_certificate_selector.cc
index 72f4a498..8222614d 100644
--- a/chrome/browser/ui/views/ssl_client_certificate_selector.cc
+++ b/chrome/browser/ui/views/ssl_client_certificate_selector.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/web_modal/popup_manager.h"
@@ -30,7 +29,6 @@
       SSLClientAuthObserver(web_contents->GetBrowserContext(),
                             cert_request_info,
                             delegate.Pass()) {
-  DVLOG(1) << __FUNCTION__;
 }
 
 SSLClientCertificateSelector::~SSLClientCertificateSelector() {
@@ -44,19 +42,15 @@
 }
 
 void SSLClientCertificateSelector::OnCertSelectedByNotification() {
-  DVLOG(1) << __FUNCTION__;
   GetWidget()->Close();
 }
 
 bool SSLClientCertificateSelector::Cancel() {
-  DVLOG(1) << __FUNCTION__;
-  StopObserving();
   CertificateSelected(nullptr);
   return true;
 }
 
 bool SSLClientCertificateSelector::Accept() {
-  DVLOG(1) << __FUNCTION__;
   scoped_refptr<net::X509Certificate> cert = GetSelectedCert();
   if (cert.get()) {
     // Remove the observer before we try unlocking, otherwise we might act on a
@@ -88,7 +82,6 @@
 }
 
 void SSLClientCertificateSelector::Unlocked(net::X509Certificate* cert) {
-  DVLOG(1) << __FUNCTION__;
   CertificateSelected(cert);
   GetWidget()->Close();
 }
@@ -99,7 +92,6 @@
     content::WebContents* contents,
     net::SSLCertRequestInfo* cert_request_info,
     scoped_ptr<content::ClientCertificateDelegate> delegate) {
-  DVLOG(1) << __FUNCTION__ << " " << contents;
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   // Not all WebContentses can show modal dialogs.
diff --git a/chrome/browser/ui/webui/app_list/start_page_handler.cc b/chrome/browser/ui/webui/app_list/start_page_handler.cc
index d8234cc..72b10e6 100644
--- a/chrome/browser/ui/webui/app_list/start_page_handler.cc
+++ b/chrome/browser/ui/webui/app_list/start_page_handler.cc
@@ -12,7 +12,6 @@
 #include "base/values.h"
 #include "base/version.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search/hotword_service.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_service.h"
 #include "chrome/browser/ui/app_list/start_page_service.h"
@@ -27,7 +26,6 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_icon_set.h"
 #include "ui/app_list/app_list_switches.h"
-#include "ui/app_list/speech_ui_model_observer.h"
 #include "ui/events/event_constants.h"
 
 namespace app_list {
@@ -46,13 +44,9 @@
   DOODLE_ACTION_LAST,
 };
 
-#if defined(OS_CHROMEOS)
-const char kOldHotwordExtensionVersionString[] = "0.1.1.5023";
-#endif
-
 }  // namespace
 
-StartPageHandler::StartPageHandler() : extension_registry_observer_(this) {
+StartPageHandler::StartPageHandler() {
 }
 
 StartPageHandler::~StartPageHandler() {
@@ -71,67 +65,8 @@
   web_ui()->RegisterMessageCallback(
       "launchApp",
       base::Bind(&StartPageHandler::HandleLaunchApp, base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "speechResult",
-      base::Bind(&StartPageHandler::HandleSpeechResult,
-                 base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "speechSoundLevel",
-      base::Bind(&StartPageHandler::HandleSpeechSoundLevel,
-                 base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "setSpeechRecognitionState",
-      base::Bind(&StartPageHandler::HandleSpeechRecognition,
-                 base::Unretained(this)));
 }
 
-void StartPageHandler::OnExtensionLoaded(
-    content::BrowserContext* browser_context,
-    const extensions::Extension* extension) {
-#if defined(OS_CHROMEOS)
-  DCHECK_EQ(Profile::FromWebUI(web_ui()),
-            Profile::FromBrowserContext(browser_context));
-  if (extension->id() == extension_misc::kHotwordExtensionId)
-    OnHotwordEnabledChanged();
-#endif
-}
-
-void StartPageHandler::OnExtensionUnloaded(
-    content::BrowserContext* browser_context,
-    const extensions::Extension* extension,
-    extensions::UnloadedExtensionInfo::Reason reason) {
-#if defined(OS_CHROMEOS)
-  DCHECK_EQ(Profile::FromWebUI(web_ui()),
-            Profile::FromBrowserContext(browser_context));
-  if (extension->id() == extension_misc::kHotwordExtensionId)
-    OnHotwordEnabledChanged();
-#endif
-}
-
-#if defined(OS_CHROMEOS)
-void StartPageHandler::OnHotwordEnabledChanged() {
-  // If the hotword extension is new enough, we should use the new
-  // hotwordPrivate API to provide the feature.
-  // TODO(mukai): remove this after everything gets stable.
-  Profile* profile = Profile::FromWebUI(web_ui());
-
-  extensions::ExtensionRegistry* registry =
-      extensions::ExtensionRegistry::Get(profile);
-  const extensions::Extension* hotword_extension =
-      registry->GetExtensionById(extension_misc::kHotwordExtensionId,
-                                 extensions::ExtensionRegistry::ENABLED);
-  if (hotword_extension &&
-      hotword_extension->version()->CompareTo(
-          base::Version(kOldHotwordExtensionVersionString)) <= 0 &&
-      !HotwordService::IsExperimentalHotwordingEnabled()) {
-    StartPageService* service = StartPageService::Get(profile);
-    web_ui()->CallJavascriptFunction(
-        "appList.startPage.setHotwordEnabled",
-        base::FundamentalValue(service && service->HotwordEnabled()));
-  }
-}
-#endif
-
 void StartPageHandler::HandleAppListShown(const base::ListValue* args) {
   bool doodle_shown = false;
   if (args->GetBoolean(0, &doodle_shown) && doodle_shown) {
@@ -152,45 +87,6 @@
     return;
 
   service->WebUILoaded();
-
-#if defined(OS_CHROMEOS)
-  if (app_list::switches::IsVoiceSearchEnabled() &&
-      HotwordService::DoesHotwordSupportLanguage(profile)) {
-    OnHotwordEnabledChanged();
-    pref_change_registrar_.Init(profile->GetPrefs());
-    pref_change_registrar_.RemoveAll();
-    pref_change_registrar_.Add(
-        prefs::kHotwordSearchEnabled,
-        base::Bind(&StartPageHandler::OnHotwordEnabledChanged,
-                   base::Unretained(this)));
-
-    extension_registry_observer_.RemoveAll();
-    extension_registry_observer_.Add(
-        extensions::ExtensionRegistry::Get(profile));
-  }
-
-  extensions::ExtensionRegistry* registry =
-      extensions::ExtensionRegistry::Get(profile);
-  const extensions::Extension* hotword_extension =
-      registry->GetExtensionById(extension_misc::kHotwordExtensionId,
-                                 extensions::ExtensionRegistry::ENABLED);
-  if (hotword_extension &&
-      hotword_extension->version()->CompareTo(
-          base::Version(kOldHotwordExtensionVersionString)) <= 0) {
-    web_ui()->CallJavascriptFunction(
-        "appList.startPage.setNaclArch",
-        base::StringValue(update_client::UpdateQueryParams::GetNaclArch()));
-  }
-#endif
-
-  // If v2 hotwording is enabled, don't tell the start page that the app list is
-  // being shown. V2 hotwording doesn't use the start page for anything.
-  if (!app_list::switches::IsExperimentalAppListEnabled() &&
-      !HotwordService::IsExperimentalHotwordingEnabled()) {
-    web_ui()->CallJavascriptFunction(
-        "appList.startPage.onAppListShown",
-        base::FundamentalValue(service->HotwordEnabled()));
-  }
 }
 
 void StartPageHandler::HandleLaunchApp(const base::ListValue* args) {
@@ -216,48 +112,4 @@
                           ui::EF_NONE);
 }
 
-void StartPageHandler::HandleSpeechResult(const base::ListValue* args) {
-  base::string16 query;
-  bool is_final = false;
-  CHECK(args->GetString(0, &query));
-  CHECK(args->GetBoolean(1, &is_final));
-
-  StartPageService::Get(Profile::FromWebUI(web_ui()))->OnSpeechResult(
-      query, is_final);
-}
-
-void StartPageHandler::HandleSpeechSoundLevel(const base::ListValue* args) {
-  double level;
-  CHECK(args->GetDouble(0, &level));
-
-  StartPageService* service =
-      StartPageService::Get(Profile::FromWebUI(web_ui()));
-  if (service)
-    service->OnSpeechSoundLevelChanged(static_cast<int16>(level));
-}
-
-void StartPageHandler::HandleSpeechRecognition(const base::ListValue* args) {
-  std::string state_string;
-  CHECK(args->GetString(0, &state_string));
-
-  SpeechRecognitionState new_state = SPEECH_RECOGNITION_OFF;
-  if (state_string == "READY")
-    new_state = SPEECH_RECOGNITION_READY;
-  else if (state_string == "HOTWORD_RECOGNIZING")
-    new_state = SPEECH_RECOGNITION_HOTWORD_LISTENING;
-  else if (state_string == "RECOGNIZING")
-    new_state = SPEECH_RECOGNITION_RECOGNIZING;
-  else if (state_string == "IN_SPEECH")
-    new_state = SPEECH_RECOGNITION_IN_SPEECH;
-  else if (state_string == "STOPPING")
-    new_state = SPEECH_RECOGNITION_STOPPING;
-  else if (state_string == "NETWORK_ERROR")
-    new_state = SPEECH_RECOGNITION_NETWORK_ERROR;
-
-  StartPageService* service =
-      StartPageService::Get(Profile::FromWebUI(web_ui()));
-  if (service)
-    service->OnSpeechRecognitionStateChanged(new_state);
-}
-
 }  // namespace app_list
diff --git a/chrome/browser/ui/webui/app_list/start_page_handler.h b/chrome/browser/ui/webui/app_list/start_page_handler.h
index f0a5fa6..25293dd46 100644
--- a/chrome/browser/ui/webui/app_list/start_page_handler.h
+++ b/chrome/browser/ui/webui/app_list/start_page_handler.h
@@ -8,23 +8,16 @@
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/prefs/pref_change_registrar.h"
-#include "base/scoped_observer.h"
 #include "content/public/browser/web_ui_message_handler.h"
-#include "extensions/browser/extension_registry_observer.h"
 
 namespace base {
 class ListValue;
 }
 
-namespace extensions {
-class ExtensionRegistry;
-}
-
 namespace app_list {
 
 // Handler for the app launcher start page.
-class StartPageHandler : public content::WebUIMessageHandler,
-                         public extensions::ExtensionRegistryObserver {
+class StartPageHandler : public content::WebUIMessageHandler {
  public:
   StartPageHandler();
   ~StartPageHandler() override;
@@ -33,34 +26,14 @@
   // content::WebUIMessageHandler overrides:
   void RegisterMessages() override;
 
-  // extensions::ExtensionRegistryObserver implementation.
-  void OnExtensionLoaded(content::BrowserContext* browser_context,
-                         const extensions::Extension* extension) override;
-  void OnExtensionUnloaded(
-      content::BrowserContext* browser_context,
-      const extensions::Extension* extension,
-      extensions::UnloadedExtensionInfo::Reason reason) override;
-
-#if defined(OS_CHROMEOS)
-  // Called when the pref has been changed.
-  void OnHotwordEnabledChanged();
-#endif
-
   // JS callbacks.
   void HandleAppListShown(const base::ListValue* args);
   void HandleDoodleClicked(const base::ListValue* args);
   void HandleInitialize(const base::ListValue* args);
   void HandleLaunchApp(const base::ListValue* args);
-  void HandleSpeechResult(const base::ListValue* args);
-  void HandleSpeechSoundLevel(const base::ListValue* args);
-  void HandleSpeechRecognition(const base::ListValue* args);
 
   PrefChangeRegistrar pref_change_registrar_;
 
-  ScopedObserver<extensions::ExtensionRegistry,
-                 extensions::ExtensionRegistryObserver>
-      extension_registry_observer_;
-
   DISALLOW_COPY_AND_ASSIGN(StartPageHandler);
 };
 
diff --git a/chrome/browser/ui/webui/app_list/start_page_ui.cc b/chrome/browser/ui/webui/app_list/start_page_ui.cc
index 40f1e1e99..2695981 100644
--- a/chrome/browser/ui/webui/app_list/start_page_ui.cc
+++ b/chrome/browser/ui/webui/app_list/start_page_ui.cc
@@ -22,48 +22,6 @@
 #include "grit/browser_resources.h"
 
 namespace app_list {
-namespace {
-#if defined(OS_CHROMEOS)
-const char* kHotwordFilePrefixes[] = {
-  "hotword_",
-  "_platform_specific/",
-};
-
-void LoadModelData(const base::FilePath& base_dir,
-                   const std::string& path,
-                   const content::WebUIDataSource::GotDataCallback& callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
-  // Will be owned by |callback|.
-  scoped_refptr<base::RefCountedString> data(new base::RefCountedString());
-  base::ReadFileToString(base_dir.AppendASCII(path), &(data->data()));
-  callback.Run(data.get());
-}
-
-bool HandleHotwordFilesResourceFilter(
-    Profile* profile,
-    const std::string& path,
-    const content::WebUIDataSource::GotDataCallback& callback) {
-  ExtensionService* service =
-      extensions::ExtensionSystem::Get(profile)->extension_service();
-  const extensions::Extension* extension =
-      service->GetExtensionById(extension_misc::kHotwordExtensionId, false);
-  if (!extension)
-    return false;
-
-  for (size_t i = 0; i < arraysize(kHotwordFilePrefixes); ++i) {
-    if (path.find(kHotwordFilePrefixes[i]) == 0) {
-      content::BrowserThread::PostTask(
-          content::BrowserThread::FILE,
-          FROM_HERE,
-          base::Bind(&LoadModelData, extension->path(), path, callback));
-      return true;
-    }
-  }
-
-  return false;
-}
-#endif  // OS_CHROMEOS
-}  // namespace
 
 StartPageUI::StartPageUI(content::WebUI* web_ui)
     : content::WebUIController(web_ui) {
@@ -81,16 +39,8 @@
 
   source->AddResourcePath("start_page.css", IDR_APP_LIST_START_PAGE_CSS);
   source->AddResourcePath("start_page.js", IDR_APP_LIST_START_PAGE_JS);
-  source->AddResourcePath("hotword_nacl.nmf", IDR_APP_LIST_HOTWORD_NACL_NMF);
   source->SetDefaultResource(IDR_APP_LIST_START_PAGE_HTML);
 
-#if defined(OS_CHROMEOS)
-  source->OverrideContentSecurityPolicyObjectSrc("object-src 'self' data:;");
-  if (base::SysInfo::IsRunningOnChromeOS())
-    source->SetRequestFilter(base::Bind(&HandleHotwordFilesResourceFilter,
-                                        Profile::FromWebUI(web_ui())));
-#endif
-
   content::WebUIDataSource::Add(Profile::FromWebUI(web_ui()), source.release());
 }
 
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 18ea11e..69ad96c 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -36,7 +36,6 @@
 #include "chrome/browser/ui/webui/interstitials/interstitial_ui.h"
 #include "chrome/browser/ui/webui/invalidations_ui.h"
 #include "chrome/browser/ui/webui/local_state/local_state_ui.h"
-#include "chrome/browser/ui/webui/md_settings_ui.h"
 #include "chrome/browser/ui/webui/memory_internals/memory_internals_ui.h"
 #include "chrome/browser/ui/webui/net_internals/net_internals_ui.h"
 #include "chrome/browser/ui/webui/omnibox/omnibox_ui.h"
@@ -45,6 +44,7 @@
 #include "chrome/browser/ui/webui/plugins_ui.h"
 #include "chrome/browser/ui/webui/predictors/predictors_ui.h"
 #include "chrome/browser/ui/webui/profiler_ui.h"
+#include "chrome/browser/ui/webui/settings/md_settings_ui.h"
 #include "chrome/browser/ui/webui/signin/inline_login_ui.h"
 #include "chrome/browser/ui/webui/signin/profile_signin_confirmation_ui.h"
 #include "chrome/browser/ui/webui/signin/user_manager_ui.h"
diff --git a/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h
index b1b6e8b4..359126e 100644
--- a/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h
@@ -129,47 +129,12 @@
         base::Bind(method, base::Unretained(static_cast<T*>(this))));
   }
 
-  template<typename T>
-  void AddCallback(const std::string& name, void (T::*method)()) {
-    base::Callback<void()> callback =
+  template<typename T, typename... Args>
+  void AddCallback(const std::string& name, void (T::*method)(Args...)) {
+    base::Callback<void(Args...)> callback =
         base::Bind(method, base::Unretained(static_cast<T*>(this)));
     web_ui()->RegisterMessageCallback(
-        name, base::Bind(&::login::CallbackWrapper0, callback));
-  }
-
-  template<typename T, typename A1>
-  void AddCallback(const std::string& name, void (T::*method)(A1 arg1)) {
-    base::Callback<void(A1)> callback =
-        base::Bind(method, base::Unretained(static_cast<T*>(this)));
-    web_ui()->RegisterMessageCallback(
-        name, base::Bind(&::login::CallbackWrapper1<A1>, callback));
-  }
-
-  template<typename T, typename A1, typename A2>
-  void AddCallback(const std::string& name,
-                   void (T::*method)(A1 arg1, A2 arg2)) {
-    base::Callback<void(A1, A2)> callback =
-        base::Bind(method, base::Unretained(static_cast<T*>(this)));
-    web_ui()->RegisterMessageCallback(
-        name, base::Bind(&::login::CallbackWrapper2<A1, A2>, callback));
-  }
-
-  template<typename T, typename A1, typename A2, typename A3>
-  void AddCallback(const std::string& name,
-                   void (T::*method)(A1 arg1, A2 arg2, A3 arg3)) {
-    base::Callback<void(A1, A2, A3)> callback =
-        base::Bind(method, base::Unretained(static_cast<T*>(this)));
-    web_ui()->RegisterMessageCallback(
-        name, base::Bind(&::login::CallbackWrapper3<A1, A2, A3>, callback));
-  }
-
-  template<typename T, typename A1, typename A2, typename A3, typename A4>
-  void AddCallback(const std::string& name,
-                   void (T::*method)(A1 arg1, A2 arg2, A3 arg3, A4 arg4)) {
-    base::Callback<void(A1, A2, A3, A4)> callback =
-        base::Bind(method, base::Unretained(static_cast<T*>(this)));
-    web_ui()->RegisterMessageCallback(
-        name, base::Bind(&::login::CallbackWrapper4<A1, A2, A3, A4>, callback));
+        name, base::Bind(&::login::CallbackWrapper<Args...>, callback));
   }
 
   template <typename Method>
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index d98dce5ad..47af3d4 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -402,7 +402,8 @@
     const std::string& gaia_id,
     const std::string& email,
     const std::string& password,
-    const std::string& auth_code) {
+    const std::string& auth_code,
+    bool using_saml) {
   if (!Delegate())
     return;
 
@@ -416,6 +417,9 @@
   user_context.SetGaiaID(gaia_id);
   user_context.SetKey(Key(password));
   user_context.SetAuthCode(auth_code);
+  user_context.SetAuthFlow(using_saml
+                               ? UserContext::AUTH_FLOW_GAIA_WITH_SAML
+                               : UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML);
   Delegate()->CompleteLogin(user_context);
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
index 8af7c07a..f06086a 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -103,7 +103,8 @@
   void HandleCompleteAuthentication(const std::string& gaia_id,
                                     const std::string& email,
                                     const std::string& password,
-                                    const std::string& auth_code);
+                                    const std::string& auth_code,
+                                    bool using_saml);
   void HandleCompleteAuthenticationAuthCodeOnly(const std::string& auth_code);
   void HandleCompleteLogin(const std::string& gaia_id,
                            const std::string& typed_email,
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 0caf2cb8..3d721d2 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -47,6 +47,7 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/io_thread.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/browser/signin/easy_unlock_service.h"
 #include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
@@ -491,6 +492,8 @@
               &SigninScreenHandler::HandleCancelConsumerManagementEnrollment);
   AddCallback("getTouchViewState",
               &SigninScreenHandler::HandleGetTouchViewState);
+  AddCallback("logRemoveUserWarningShown",
+              &SigninScreenHandler::HandleLogRemoveUserWarningShown);
 
   // This message is sent by the kiosk app menu, but is handled here
   // so we can tell the delegate to launch the app.
@@ -1271,6 +1274,11 @@
   }
 }
 
+void SigninScreenHandler::HandleLogRemoveUserWarningShown() {
+  ProfileMetrics::LogProfileDeleteUser(
+      ProfileMetrics::DELETE_PROFILE_USER_MANAGER_SHOW_WARNING);
+}
+
 bool SigninScreenHandler::AllWhitelistedUsersPresent() {
   CrosSettings* cros_settings = CrosSettings::Get();
   bool allow_new_user = false;
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index 0fb8224..0133936 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -359,6 +359,7 @@
                                              const std::string& locale);
   void HandleCancelConsumerManagementEnrollment();
   void HandleGetTouchViewState();
+  void HandleLogRemoveUserWarningShown();
 
   // Sends the list of |keyboard_layouts| available for the |locale| that is
   // currently selected for the public session identified by |user_id|.
diff --git a/chrome/browser/ui/webui/devtools_ui.cc b/chrome/browser/ui/webui/devtools_ui.cc
index fe8a671..1f61307 100644
--- a/chrome/browser/ui/webui/devtools_ui.cc
+++ b/chrome/browser/ui/webui/devtools_ui.cc
@@ -76,6 +76,8 @@
     return "image/png";
   } else if (EndsWith(filename, ".gif", false)) {
     return "image/gif";
+  } else if (EndsWith(filename, ".svg", false)) {
+    return "image/svg+xml";
   } else if (EndsWith(filename, ".manifest", false)) {
     return "text/cache-manifest";
   }
diff --git a/chrome/browser/ui/webui/fallback_icon_source.cc b/chrome/browser/ui/webui/fallback_icon_source.cc
index 5ca4746..e568b9d 100644
--- a/chrome/browser/ui/webui/fallback_icon_source.cc
+++ b/chrome/browser/ui/webui/fallback_icon_source.cc
@@ -26,8 +26,7 @@
 #else
   font_list.push_back(l10n_util::GetStringUTF8(IDS_SANS_SERIF_FONT_FAMILY));
 #endif
-  fallback_icon_service_.reset(
-      new favicon_base::FallbackIconService(font_list));
+  fallback_icon_service_.reset(new FallbackIconService(font_list));
 }
 
 FallbackIconSource::~FallbackIconSource() {
diff --git a/chrome/browser/ui/webui/fallback_icon_source.h b/chrome/browser/ui/webui/fallback_icon_source.h
index ba4843e..acc1d6b8 100644
--- a/chrome/browser/ui/webui/fallback_icon_source.h
+++ b/chrome/browser/ui/webui/fallback_icon_source.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_UI_WEBUI_FALLBACK_ICON_SOURCE_H_
 
 #include "base/memory/scoped_ptr.h"
-#include "components/favicon_base/fallback_icon_service.h"
+#include "components/favicon/core/fallback_icon_service.h"
 #include "content/public/browser/url_data_source.h"
 
 // FallbackIconSource services explicit chrome:// requests for fallback icons.
@@ -61,7 +61,7 @@
   void SendDefaultResponse(
       const content::URLDataSource::GotDataCallback& callback);
 
-  scoped_ptr<favicon_base::FallbackIconService> fallback_icon_service_;
+  scoped_ptr<FallbackIconService> fallback_icon_service_;
 
   DISALLOW_COPY_AND_ASSIGN(FallbackIconSource);
 };
diff --git a/chrome/browser/ui/webui/inspect_ui.cc b/chrome/browser/ui/webui/inspect_ui.cc
index 77ac218..f54bc34 100644
--- a/chrome/browser/ui/webui/inspect_ui.cc
+++ b/chrome/browser/ui/webui/inspect_ui.cc
@@ -313,14 +313,12 @@
   }
 }
 
-static void NoOp(DevToolsTargetImpl*) {}
-
 void InspectUI::Open(const std::string& source_id,
                      const std::string& browser_id,
                      const std::string& url) {
   DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
   if (handler)
-    handler->Open(browser_id, url, base::Bind(&NoOp));
+    handler->Open(browser_id, url);
 }
 
 void InspectUI::InspectBrowserWithCustomFrontend(
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index 5148eda..31a98bef 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -94,13 +94,9 @@
     options_mask |= SSLBlockingPage::OVERRIDABLE;
   if (strict_enforcement)
     options_mask |= SSLBlockingPage::STRICT_ENFORCEMENT;
-  return new SSLBlockingPage(web_contents,
-                              cert_error,
-                              ssl_info,
-                              request_url,
-                              options_mask,
-                              time_triggered_,
-                              base::Callback<void(bool)>());
+  return new SSLBlockingPage(web_contents, cert_error, ssl_info, request_url,
+                             options_mask, time_triggered_, nullptr,
+                             base::Callback<void(bool)>());
 }
 
 SafeBrowsingBlockingPage* CreateSafeBrowsingBlockingPage(
diff --git a/chrome/browser/ui/webui/large_icon_source.cc b/chrome/browser/ui/webui/large_icon_source.cc
index 4dc270e..e28d342c 100644
--- a/chrome/browser/ui/webui/large_icon_source.cc
+++ b/chrome/browser/ui/webui/large_icon_source.cc
@@ -51,8 +51,7 @@
 #else
   font_list.push_back(l10n_util::GetStringUTF8(IDS_SANS_SERIF_FONT_FAMILY));
 #endif
-  fallback_icon_service_.reset(
-      new favicon_base::FallbackIconService(font_list));
+  fallback_icon_service_.reset(new FallbackIconService(font_list));
 }
 
 LargeIconSource::~LargeIconSource() {
diff --git a/chrome/browser/ui/webui/large_icon_source.h b/chrome/browser/ui/webui/large_icon_source.h
index 7d5da6b..df3fad62 100644
--- a/chrome/browser/ui/webui/large_icon_source.h
+++ b/chrome/browser/ui/webui/large_icon_source.h
@@ -7,7 +7,7 @@
 
 #include "base/memory/scoped_ptr.h"
 #include "base/task/cancelable_task_tracker.h"
-#include "components/favicon_base/fallback_icon_service.h"
+#include "components/favicon/core/fallback_icon_service.h"
 #include "components/favicon_base/favicon_types.h"
 #include "content/public/browser/url_data_source.h"
 
@@ -73,7 +73,7 @@
 
   base::CancelableTaskTracker cancelable_task_tracker_;
 
-  scoped_ptr<favicon_base::FallbackIconService> fallback_icon_service_;
+  scoped_ptr<FallbackIconService> fallback_icon_service_;
 
   DISALLOW_COPY_AND_ASSIGN(LargeIconSource);
 };
diff --git a/chrome/browser/ui/webui/options/autofill_options_handler.cc b/chrome/browser/ui/webui/options/autofill_options_handler.cc
index 0b38cdd7..76948064 100644
--- a/chrome/browser/ui/webui/options/autofill_options_handler.cc
+++ b/chrome/browser/ui/webui/options/autofill_options_handler.cc
@@ -16,6 +16,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "chrome/browser/autofill/options_util.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
@@ -375,10 +376,10 @@
           Profile::FromWebUI(web_ui()));
   if (service)
     observer_.Add(service);
-  localized_strings->SetBoolean(
-      "autofillWalletIntegrationAvailable",
-      service && service->IsSyncEnabledAndLoggedIn() &&
-          personal_data_->IsExperimentalWalletIntegrationEnabled());
+
+  localized_strings->SetBoolean("autofillWalletIntegrationAvailable",
+                                autofill::WalletIntegrationAvailableForProfile(
+                                    Profile::FromWebUI(web_ui())));
 }
 
 void AutofillOptionsHandler::InitializeHandler() {
@@ -443,12 +444,10 @@
 }
 
 void AutofillOptionsHandler::OnStateChanged() {
-  ProfileSyncService* service =
-      ProfileSyncServiceFactory::GetInstance()->GetForProfile(
-          Profile::FromWebUI(web_ui()));
   web_ui()->CallJavascriptFunction(
       "AutofillOptions.walletIntegrationAvailableStateChanged",
-      base::FundamentalValue(service && service->IsSyncEnabledAndLoggedIn()));
+      base::FundamentalValue(autofill::WalletIntegrationAvailableForProfile(
+          Profile::FromWebUI(web_ui()))));
 }
 
 void AutofillOptionsHandler::SetAddressOverlayStrings(
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc
index c4b4acd..22a9b25 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.cc
+++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -1783,31 +1783,25 @@
     bool always_on = false;
     SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile);
     bool authenticated = signin && signin->IsAuthenticated();
-    if (HotwordService::IsExperimentalHotwordingEnabled()) {
-      if (HotwordServiceFactory::IsAlwaysOnAvailable() &&
-          authenticated) {
-        function_name = "BrowserOptions.showHotwordAlwaysOnSection";
-        always_on = true;
-        // Show the retrain link if always-on is enabled.
-        if (profile->GetPrefs()->GetBoolean(
-                prefs::kHotwordAlwaysOnSearchEnabled)) {
-          web_ui()->CallJavascriptFunction(
-              "BrowserOptions.setHotwordRetrainLinkVisible",
-              base::FundamentalValue(true));
-        }
-      } else {
-        function_name = "BrowserOptions.showHotwordNoDspSection";
+    if (HotwordServiceFactory::IsAlwaysOnAvailable() && authenticated) {
+      function_name = "BrowserOptions.showHotwordAlwaysOnSection";
+      always_on = true;
+      // Show the retrain link if always-on is enabled.
+      if (profile->GetPrefs()->GetBoolean(
+              prefs::kHotwordAlwaysOnSearchEnabled)) {
+        web_ui()->CallJavascriptFunction(
+            "BrowserOptions.setHotwordRetrainLinkVisible",
+            base::FundamentalValue(true));
       }
     } else {
-      function_name = "BrowserOptions.showHotwordSection";
+      function_name = "BrowserOptions.showHotwordNoDspSection";
     }
 
     // Audio history should be displayed if it's enabled regardless of the
     // hotword error state if the user is signed in. If the user is not signed
     // in, audio history is meaningless. This is only displayed if always-on
     // hotwording is available.
-    if (HotwordService::IsExperimentalHotwordingEnabled() &&
-        authenticated && always_on) {
+    if (authenticated && always_on) {
       std::string user_display_name = signin->GetAuthenticatedUsername();
       DCHECK(!user_display_name.empty());
       base::string16 audio_history_state =
diff --git a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
index 300965fa..2af44e0f 100644
--- a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
@@ -77,7 +77,9 @@
 }  // namespace
 
 ChangePictureOptionsHandler::ChangePictureOptionsHandler()
-    : previous_image_url_(url::kAboutBlankURL),
+    : ImageRequest(
+          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)),
+      previous_image_url_(url::kAboutBlankURL),
       previous_image_index_(user_manager::User::USER_IMAGE_INVALID) {
   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
       content::NotificationService::AllSources());
@@ -98,8 +100,6 @@
   CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
   if (select_file_dialog_.get())
     select_file_dialog_->ListenerDestroyed();
-  if (image_decoder_.get())
-    image_decoder_->set_delegate(NULL);
 }
 
 void ChangePictureOptionsHandler::GetLocalizedValues(
@@ -240,13 +240,8 @@
   user_photo_ = gfx::ImageSkia();
   user_photo_data_url_ = image_url;
 
-  if (image_decoder_.get())
-    image_decoder_->set_delegate(NULL);
-  image_decoder_ = new ImageDecoder(this, raw_data,
-                                    ImageDecoder::DEFAULT_CODEC);
-  scoped_refptr<base::MessageLoopProxy> task_runner =
-      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
-  image_decoder_->Start(task_runner);
+  ImageDecoder::Cancel(this);
+  ImageDecoder::Start(this, raw_data);
 }
 
 void ChangePictureOptionsHandler::HandlePageInitialized(
@@ -370,7 +365,6 @@
   } else if (image_type == "camera") {
     // Camera image is selected.
     if (user_photo_.isNull()) {
-      DCHECK(image_decoder_.get());
       waiting_for_camera_photo = true;
       VLOG(1) << "Still waiting for camera image to decode";
     } else {
@@ -396,8 +390,8 @@
   }
 
   // Ignore the result of the previous decoding if it's no longer needed.
-  if (!waiting_for_camera_photo && image_decoder_.get())
-    image_decoder_->set_delegate(NULL);
+  if (!waiting_for_camera_photo)
+    ImageDecoder::Cancel(this);
 }
 
 void ChangePictureOptionsHandler::FileSelected(const base::FilePath& path,
@@ -458,16 +452,12 @@
 }
 
 void ChangePictureOptionsHandler::OnImageDecoded(
-    const ImageDecoder* decoder,
     const SkBitmap& decoded_image) {
-  DCHECK_EQ(image_decoder_.get(), decoder);
-  image_decoder_ = NULL;
   user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
   SetImageFromCamera(user_photo_);
 }
 
-void ChangePictureOptionsHandler::OnDecodeImageFailed(
-    const ImageDecoder* decoder) {
+void ChangePictureOptionsHandler::OnDecodeImageFailed() {
   NOTREACHED() << "Failed to decode PNG image from WebUI";
 }
 
diff --git a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h
index d0282e2..80ca836 100644
--- a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h
+++ b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h
@@ -31,7 +31,7 @@
 class ChangePictureOptionsHandler : public ::options::OptionsPageUIHandler,
                                     public ui::SelectFileDialog::Listener,
                                     public content::NotificationObserver,
-                                    public ImageDecoder::Delegate,
+                                    public ImageDecoder::ImageRequest,
                                     public CameraPresenceNotifier::Observer {
  public:
   ChangePictureOptionsHandler();
@@ -113,10 +113,9 @@
   // Returns handle to browser window or NULL if it can't be found.
   gfx::NativeWindow GetBrowserWindow() const;
 
-  // Overriden from ImageDecoder::Delegate:
-  void OnImageDecoded(const ImageDecoder* decoder,
-                      const SkBitmap& decoded_image) override;
-  void OnDecodeImageFailed(const ImageDecoder* decoder) override;
+  // Overriden from ImageDecoder::ImageRequest:
+  void OnImageDecoded(const SkBitmap& decoded_image) override;
+  void OnDecodeImageFailed() override;
 
   // Returns user related to current WebUI. If this user doesn't exist,
   // returns active user.
@@ -139,10 +138,6 @@
 
   content::NotificationRegistrar registrar_;
 
-  // Last ImageDecoder instance used to decode an image blob received by
-  // HandlePhotoTaken.
-  scoped_refptr<ImageDecoder> image_decoder_;
-
   DISALLOW_COPY_AND_ASSIGN(ChangePictureOptionsHandler);
 };
 
diff --git a/chrome/browser/ui/webui/options/content_settings_handler.cc b/chrome/browser/ui/webui/options/content_settings_handler.cc
index 2e5ee06..562ea7cf 100644
--- a/chrome/browser/ui/webui/options/content_settings_handler.cc
+++ b/chrome/browser/ui/webui/options/content_settings_handler.cc
@@ -109,9 +109,12 @@
   {CONTENT_SETTINGS_TYPE_FULLSCREEN, "fullscreen"},
   {CONTENT_SETTINGS_TYPE_MOUSELOCK, "mouselock"},
   {CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS, "register-protocol-handler"},
+  // The MEDIASTREAM content setting is deprecated, but the settings for
+  // microphone and camera still live in the part of UI labeled "media-stream".
+  // TODO(msramek): Clean this up once we have a new UI for media.
   {CONTENT_SETTINGS_TYPE_MEDIASTREAM, "media-stream"},
-  {CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, "media-stream-mic"},
-  {CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, "media-stream-camera"},
+  {CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, "media-stream"},
+  {CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, "media-stream"},
   {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, "ppapi-broker"},
   {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, "multiple-automatic-downloads"},
   {CONTENT_SETTINGS_TYPE_MIDI_SYSEX, "midi-sysex"},
@@ -255,10 +258,11 @@
       last_flash_refresh_request_id(0),
       show_flash_default_link(false),
       show_flash_exceptions_link(false),
-      default_setting(CONTENT_SETTING_DEFAULT),
+      default_audio_setting(CONTENT_SETTING_DEFAULT),
+      default_video_setting(CONTENT_SETTING_DEFAULT),
       policy_disable_audio(false),
       policy_disable_video(false),
-      default_setting_initialized(false),
+      default_settings_initialized(false),
       exceptions_initialized(false) {
 }
 
@@ -275,78 +279,77 @@
     base::DictionaryValue* localized_strings) {
   DCHECK(localized_strings);
 
-  // TODO(dhnishi): Standardize to lowerCamelCase.
   static OptionsStringResource resources[] = {
     {"allowException", IDS_EXCEPTIONS_ALLOW_BUTTON},
     {"blockException", IDS_EXCEPTIONS_BLOCK_BUTTON},
     {"sessionException", IDS_EXCEPTIONS_SESSION_ONLY_BUTTON},
     {"detectException", IDS_EXCEPTIONS_DETECT_IMPORTANT_CONTENT_BUTTON},
     {"askException", IDS_EXCEPTIONS_ASK_BUTTON},
-    {"otr_exceptions_explanation", IDS_EXCEPTIONS_OTR_LABEL},
+    {"otrExceptionsExplanation", IDS_EXCEPTIONS_OTR_LABEL},
     {"addNewExceptionInstructions", IDS_EXCEPTIONS_ADD_NEW_INSTRUCTIONS},
     {"manageExceptions", IDS_EXCEPTIONS_MANAGE},
-    {"manage_handlers", IDS_HANDLERS_MANAGE},
+    {"manageHandlers", IDS_HANDLERS_MANAGE},
     {"exceptionPatternHeader", IDS_EXCEPTIONS_PATTERN_HEADER},
     {"exceptionBehaviorHeader", IDS_EXCEPTIONS_ACTION_HEADER},
     {"exceptionZoomHeader", IDS_EXCEPTIONS_ZOOM_HEADER},
     {"embeddedOnHost", IDS_EXCEPTIONS_GEOLOCATION_EMBEDDED_ON_HOST},
     // Cookies filter.
     {"cookiesTabLabel", IDS_COOKIES_TAB_LABEL},
-    {"cookies_header", IDS_COOKIES_HEADER},
+    {"cookiesHeader", IDS_COOKIES_HEADER},
     {"cookiesAllow", IDS_COOKIES_ALLOW_RADIO},
     {"cookiesBlock", IDS_COOKIES_BLOCK_RADIO},
     {"cookiesSession", IDS_COOKIES_SESSION_ONLY_RADIO},
-    {"cookies_block_3rd_party", IDS_COOKIES_BLOCK_3RDPARTY_CHKBOX},
-    {"cookies_clear_when_close", IDS_COOKIES_CLEAR_WHEN_CLOSE_CHKBOX},
-    {"cookies_lso_clear_when_close", IDS_COOKIES_LSO_CLEAR_WHEN_CLOSE_CHKBOX},
-    {"cookies_show_cookies", IDS_COOKIES_SHOW_COOKIES_BUTTON},
-    {"flash_storage_settings", IDS_FLASH_STORAGE_SETTINGS},
-    {"flash_storage_url", IDS_FLASH_STORAGE_URL},
+    {"cookiesBlock3rdParty", IDS_COOKIES_BLOCK_3RDPARTY_CHKBOX},
+    {"cookiesClearWhenClose", IDS_COOKIES_CLEAR_WHEN_CLOSE_CHKBOX},
+    {"cookiesLsoClearWhenClose", IDS_COOKIES_LSO_CLEAR_WHEN_CLOSE_CHKBOX},
+    {"cookiesShowCookies", IDS_COOKIES_SHOW_COOKIES_BUTTON},
+    {"flashStorageSettings", IDS_FLASH_STORAGE_SETTINGS},
+    {"flashStorageUrl", IDS_FLASH_STORAGE_URL},
 #if defined(ENABLE_GOOGLE_NOW)
     {"googleGeolocationAccessEnable",
      IDS_GEOLOCATION_GOOGLE_ACCESS_ENABLE_CHKBOX},
 #endif
     // Image filter.
     {"imagesTabLabel", IDS_IMAGES_TAB_LABEL},
-    {"images_header", IDS_IMAGES_HEADER},
+    {"imagesHeader", IDS_IMAGES_HEADER},
     {"imagesAllow", IDS_IMAGES_LOAD_RADIO},
     {"imagesBlock", IDS_IMAGES_NOLOAD_RADIO},
     // JavaScript filter.
     {"javascriptTabLabel", IDS_JAVASCRIPT_TAB_LABEL},
-    {"javascript_header", IDS_JAVASCRIPT_HEADER},
+    {"javascriptHeader", IDS_JAVASCRIPT_HEADER},
     {"javascriptAllow", IDS_JS_ALLOW_RADIO},
     {"javascriptBlock", IDS_JS_DONOTALLOW_RADIO},
     // Plugins filter.
     {"pluginsTabLabel", IDS_PLUGIN_TAB_LABEL},
-    {"plugins_header", IDS_PLUGIN_HEADER},
+    {"pluginsHeader", IDS_PLUGIN_HEADER},
     {"pluginsAllow", IDS_PLUGIN_ALLOW_RADIO},
     {"pluginsDetect", IDS_PLUGIN_DETECT_RADIO},
     {"pluginsBlock", IDS_PLUGIN_BLOCK_RADIO},
     {"manageIndividualPlugins", IDS_PLUGIN_MANAGE_INDIVIDUAL},
     // Pop-ups filter.
     {"popupsTabLabel", IDS_POPUP_TAB_LABEL},
-    {"popups_header", IDS_POPUP_HEADER},
+    {"popupsHeader", IDS_POPUP_HEADER},
     {"popupsAllow", IDS_POPUP_ALLOW_RADIO},
     {"popupsBlock", IDS_POPUP_BLOCK_RADIO},
     // Location filter.
     {"locationTabLabel", IDS_GEOLOCATION_TAB_LABEL},
-    {"location_header", IDS_GEOLOCATION_HEADER},
+    {"locationHeader", IDS_GEOLOCATION_HEADER},
     {"locationAllow", IDS_GEOLOCATION_ALLOW_RADIO},
     {"locationAsk", IDS_GEOLOCATION_ASK_RADIO},
     {"locationBlock", IDS_GEOLOCATION_BLOCK_RADIO},
-    {"set_by", IDS_GEOLOCATION_SET_BY_HOVER},
+    {"setBy", IDS_GEOLOCATION_SET_BY_HOVER},
     // Notifications filter.
     {"notificationsTabLabel", IDS_NOTIFICATIONS_TAB_LABEL},
-    {"notifications_header", IDS_NOTIFICATIONS_HEADER},
+    {"notificationsHeader", IDS_NOTIFICATIONS_HEADER},
     {"notificationsAllow", IDS_NOTIFICATIONS_ALLOW_RADIO},
     {"notificationsAsk", IDS_NOTIFICATIONS_ASK_RADIO},
     {"notificationsBlock", IDS_NOTIFICATIONS_BLOCK_RADIO},
     // Fullscreen filter.
     {"fullscreenTabLabel", IDS_FULLSCREEN_TAB_LABEL},
-    {"fullscreen_header", IDS_FULLSCREEN_HEADER},
+    {"fullscreenHeader", IDS_FULLSCREEN_HEADER},
     // Mouse Lock filter.
     {"mouselockTabLabel", IDS_MOUSE_LOCK_TAB_LABEL},
-    {"mouselock_header", IDS_MOUSE_LOCK_HEADER},
+    {"mouselockHeader", IDS_MOUSE_LOCK_HEADER},
     {"mouselockAllow", IDS_MOUSE_LOCK_ALLOW_RADIO},
     {"mouselockAsk", IDS_MOUSE_LOCK_ASK_RADIO},
     {"mouselockBlock", IDS_MOUSE_LOCK_BLOCK_RADIO},
@@ -355,11 +358,11 @@
     {"protectedContentTabLabel", IDS_PROTECTED_CONTENT_TAB_LABEL},
     {"protectedContentInfo", IDS_PROTECTED_CONTENT_INFO},
     {"protectedContentEnable", IDS_PROTECTED_CONTENT_ENABLE},
-    {"protectedContent_header", IDS_PROTECTED_CONTENT_HEADER},
+    {"protectedContentHeader", IDS_PROTECTED_CONTENT_HEADER},
 #endif  // defined(OS_CHROMEOS) || defined(OS_WIN)
     // Media stream capture device filter.
     {"mediaStreamTabLabel", IDS_MEDIA_STREAM_TAB_LABEL},
-    {"media-stream_header", IDS_MEDIA_STREAM_HEADER},
+    {"mediaStreamHeader", IDS_MEDIA_STREAM_HEADER},
     {"mediaStreamAsk", IDS_MEDIA_STREAM_ASK_RADIO},
     {"mediaStreamBlock", IDS_MEDIA_STREAM_BLOCK_RADIO},
     {"mediaStreamAudioAsk", IDS_MEDIA_STREAM_ASK_AUDIO_ONLY_RADIO},
@@ -378,28 +381,28 @@
     {"mediaPepperFlashGlobalPrivacyURL", IDS_FLASH_GLOBAL_PRIVACY_URL},
     {"mediaPepperFlashWebsitePrivacyURL", IDS_FLASH_WEBSITE_PRIVACY_URL},
     // PPAPI broker filter.
-    {"ppapi-broker_header", IDS_PPAPI_BROKER_HEADER},
+    {"ppapiBrokerHeader", IDS_PPAPI_BROKER_HEADER},
     {"ppapiBrokerTabLabel", IDS_PPAPI_BROKER_TAB_LABEL},
     {"ppapiBrokerAllow", IDS_PPAPI_BROKER_ALLOW_RADIO},
     {"ppapiBrokerAsk", IDS_PPAPI_BROKER_ASK_RADIO},
     {"ppapiBrokerBlock", IDS_PPAPI_BROKER_BLOCK_RADIO},
     // Multiple automatic downloads
     {"multipleAutomaticDownloadsTabLabel", IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL},
-    {"multiple-automatic-downloads_header", IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL},
+    {"multipleAutomaticDownloadsHeader", IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL},
     {"multipleAutomaticDownloadsAllow", IDS_AUTOMATIC_DOWNLOADS_ALLOW_RADIO},
     {"multipleAutomaticDownloadsAsk", IDS_AUTOMATIC_DOWNLOADS_ASK_RADIO},
     {"multipleAutomaticDownloadsBlock", IDS_AUTOMATIC_DOWNLOADS_BLOCK_RADIO},
     // MIDI system exclusive messages
-    {"midi-sysex_header", IDS_MIDI_SYSEX_TAB_LABEL},
+    {"midiSysexHeader", IDS_MIDI_SYSEX_TAB_LABEL},
     {"midiSysExAllow", IDS_MIDI_SYSEX_ALLOW_RADIO},
     {"midiSysExAsk", IDS_MIDI_SYSEX_ASK_RADIO},
     {"midiSysExBlock", IDS_MIDI_SYSEX_BLOCK_RADIO},
     // Push messaging strings
-    {"push-messaging_header", IDS_PUSH_MESSAGES_TAB_LABEL},
+    {"pushMessagingHeader", IDS_PUSH_MESSAGES_TAB_LABEL},
     {"pushMessagingAllow", IDS_PUSH_MESSSAGING_ALLOW_RADIO},
     {"pushMessagingAsk", IDS_PUSH_MESSSAGING_ASK_RADIO},
     {"pushMessagingBlock", IDS_PUSH_MESSSAGING_BLOCK_RADIO},
-    {"zoomlevels_header", IDS_ZOOMLEVELS_HEADER_AND_TAB_LABEL},
+    {"zoomlevelsHeader", IDS_ZOOMLEVELS_HEADER_AND_TAB_LABEL},
     {"zoomLevelsManage", IDS_ZOOMLEVELS_MANAGE_BUTTON},
   };
 
@@ -599,11 +602,9 @@
 
 void ContentSettingsHandler::UpdateSettingDefaultFromModel(
     ContentSettingsType type) {
-  Profile* profile = Profile::FromWebUI(web_ui());
   std::string provider_id;
   ContentSetting default_setting =
-      profile->GetHostContentSettingsMap()->GetDefaultContentSetting(
-          type, &provider_id);
+      GetContentSettingsMap()->GetDefaultContentSetting(type, &provider_id);
 
   // For Plugins, display the obsolete ASK setting as BLOCK.
   if (type == ContentSettingsType::CONTENT_SETTINGS_TYPE_PLUGINS &&
@@ -630,10 +631,13 @@
 
   media_settings_.policy_disable_audio = audio_disabled;
   media_settings_.policy_disable_video = video_disabled;
-  media_settings_.default_setting =
+  media_settings_.default_audio_setting =
       GetContentSettingsMap()->GetDefaultContentSetting(
-          CONTENT_SETTINGS_TYPE_MEDIASTREAM, NULL);
-  media_settings_.default_setting_initialized = true;
+          CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, NULL);
+  media_settings_.default_video_setting =
+      GetContentSettingsMap()->GetDefaultContentSetting(
+          CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, NULL);
+  media_settings_.default_settings_initialized = true;
   UpdateFlashMediaLinksVisibility();
 
   base::DictionaryValue media_ui_settings;
@@ -723,10 +727,12 @@
       UpdateNotificationExceptionsView();
       break;
     case CONTENT_SETTINGS_TYPE_MEDIASTREAM:
-      UpdateMediaSettingsView();
+      // The content settings type CONTENT_SETTINGS_TYPE_MEDIASSTREAM
+      // is deprecated.
       break;
     case CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
     case CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
+      UpdateMediaSettingsView();
       UpdateMediaExceptionsView();
       break;
     case CONTENT_SETTINGS_TYPE_MIXEDSCRIPT:
@@ -982,7 +988,11 @@
   web_ui()->CallJavascriptFunction("ContentSettings.setExceptions",
                                    type_string, media_exceptions);
 
-  UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_MEDIASTREAM);
+  // TODO(msramek): We currently don't have a UI to show separate default
+  // settings for microphone and camera. However, SetContentFilter always sets
+  // both defaults to the same value, so it doesn't matter which one we pick
+  // to show in the UI. Makes sure to update both when we have the new media UI.
+  UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC);
 }
 
 void ContentSettingsHandler::UpdateMIDISysExExceptionsView() {
@@ -1340,9 +1350,20 @@
     profile = profile->GetOriginalProfile();
 #endif
 
-
   HostContentSettingsMap* map = profile->GetHostContentSettingsMap();
-  map->SetDefaultContentSetting(content_type, default_setting);
+
+  // MEDIASTREAM is deprecated and the two separate settings MEDIASTREAM_CAMERA
+  // and MEDIASTREAM_MIC should be used instead. However, we still only have
+  // one pair of radio buttons that sets both settings.
+  // TODO(msramek): Clean this up once we have the new UI for media.
+  if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM) {
+    map->SetDefaultContentSetting(
+        CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, default_setting);
+    map->SetDefaultContentSetting(
+        CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, default_setting);
+  } else {
+    map->SetDefaultContentSetting(content_type, default_setting);
+  }
 
   switch (content_type) {
     case CONTENT_SETTINGS_TYPE_COOKIES:
@@ -1543,7 +1564,7 @@
 
 void ContentSettingsHandler::UpdateFlashMediaLinksVisibility() {
   if (!media_settings_.flash_settings_initialized ||
-      !media_settings_.default_setting_initialized ||
+      !media_settings_.default_settings_initialized ||
       !media_settings_.exceptions_initialized) {
     return;
   }
@@ -1566,8 +1587,10 @@
     // settings.
     if (!(media_settings_.policy_disable_audio &&
           media_settings_.policy_disable_video) &&
-        media_settings_.flash_default_setting !=
-            media_settings_.default_setting) {
+        ((media_settings_.flash_default_setting !=
+          media_settings_.default_audio_setting) ||
+         (media_settings_.flash_default_setting !=
+          media_settings_.default_video_setting))) {
       ShowFlashMediaLink(DEFAULT_SETTING, true);
     }
   }
@@ -1575,9 +1598,11 @@
     // If audio or video capture is disabled by policy, we skip comparison of
     // exceptions for audio or video capture, respectively.
     if (!PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
-            media_settings_.default_setting,
+            media_settings_.default_audio_setting,
+            media_settings_.default_video_setting,
             media_settings_.exceptions,
             media_settings_.flash_default_setting,
+            media_settings_.flash_default_setting,
             media_settings_.flash_exceptions,
             media_settings_.policy_disable_audio,
             media_settings_.policy_disable_video)) {
diff --git a/chrome/browser/ui/webui/options/content_settings_handler.h b/chrome/browser/ui/webui/options/content_settings_handler.h
index 2abb4d1..7ae81cb 100644
--- a/chrome/browser/ui/webui/options/content_settings_handler.h
+++ b/chrome/browser/ui/webui/options/content_settings_handler.h
@@ -78,10 +78,11 @@
     bool show_flash_exceptions_link;
 
     // Cached Chrome media settings.
-    ContentSetting default_setting;
+    ContentSetting default_audio_setting;
+    ContentSetting default_video_setting;
     bool policy_disable_audio;
     bool policy_disable_video;
-    bool default_setting_initialized;
+    bool default_settings_initialized;
     MediaExceptions exceptions;
     bool exceptions_initialized;
   };
diff --git a/chrome/browser/ui/webui/options/manage_profile_handler.cc b/chrome/browser/ui/webui/options/manage_profile_handler.cc
index c3424c9d..691ddb4 100644
--- a/chrome/browser/ui/webui/options/manage_profile_handler.cc
+++ b/chrome/browser/ui/webui/options/manage_profile_handler.cc
@@ -64,6 +64,11 @@
   return base::GetValueAsFilePath(*file_path_value, profile_file_path);
 }
 
+void HandleLogDeleteUserDialogShown(const base::ListValue* args) {
+  ProfileMetrics::LogProfileDeleteUser(
+      ProfileMetrics::DELETE_PROFILE_SETTINGS_SHOW_WARNING);
+}
+
 }  // namespace
 
 ManageProfileHandler::ManageProfileHandler()
@@ -185,6 +190,8 @@
       "showDisconnectManagedProfileDialog",
       base::Bind(&ManageProfileHandler::ShowDisconnectManagedProfileDialog,
                  base::Unretained(this)));
+  web_ui()->RegisterMessageCallback("logDeleteUserDialogShown",
+      base::Bind(&HandleLogDeleteUserDialogShown));
 }
 
 void ManageProfileHandler::Uninitialize() {
diff --git a/chrome/browser/ui/webui/options/password_manager_handler.cc b/chrome/browser/ui/webui/options/password_manager_handler.cc
index 6631844..4a1a8b576 100644
--- a/chrome/browser/ui/webui/options/password_manager_handler.cc
+++ b/chrome/browser/ui/webui/options/password_manager_handler.cc
@@ -202,6 +202,12 @@
       entry->AppendString(
           base::string16(password_list[i]->password_value.length(), ' '));
     }
+    const GURL& federation_url = password_list[i]->federation_url;
+    if (!federation_url.is_empty()) {
+      entry->AppendString(l10n_util::GetStringFUTF16(
+          IDS_PASSWORDS_VIA_FEDERATION,
+          base::UTF8ToUTF16(federation_url.host())));
+    }
     entries.Append(entry);
   }
 
diff --git a/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.cc b/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.cc
index e3e4d25..7f05fcd 100644
--- a/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.cc
+++ b/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.cc
@@ -96,9 +96,11 @@
 
 // static
 bool PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
-    ContentSetting default_setting_1,
+    ContentSetting default_audio_setting_1,
+    ContentSetting default_video_setting_1,
     const MediaExceptions& exceptions_1,
-    ContentSetting default_setting_2,
+    ContentSetting default_audio_setting_2,
+    ContentSetting default_video_setting_2,
     const MediaExceptions& exceptions_2,
     bool ignore_audio_setting,
     bool ignore_video_setting) {
@@ -106,11 +108,11 @@
   MediaExceptions::const_iterator iter_2 = exceptions_2.begin();
 
   MediaException default_exception_1(ContentSettingsPattern(),
-                                     default_setting_1,
-                                     default_setting_1);
+                                     default_audio_setting_1,
+                                     default_video_setting_1);
   MediaException default_exception_2(ContentSettingsPattern(),
-                                     default_setting_2,
-                                     default_setting_2);
+                                     default_audio_setting_2,
+                                     default_video_setting_2);
 
   while (iter_1 != exceptions_1.end() && iter_2 != exceptions_2.end()) {
     int compare_result = CompareMediaException(*iter_1, *iter_2);
diff --git a/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.h b/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.h
index 7d4b361..7cbe68d 100644
--- a/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.h
+++ b/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.h
@@ -53,9 +53,11 @@
   // |ignore_audio_setting| and |ignore_video_setting| specify whether to skip
   // comparison of the |audio_setting| and |video_setting| field of
   // MediaException, respectively.
-  static bool AreMediaExceptionsEqual(ContentSetting default_setting_1,
+  static bool AreMediaExceptionsEqual(ContentSetting default_audio_setting_1,
+                                      ContentSetting default_video_setting_1,
                                       const MediaExceptions& exceptions_1,
-                                      ContentSetting default_setting_2,
+                                      ContentSetting default_audio_setting_2,
+                                      ContentSetting default_video_setting_2,
                                       const MediaExceptions& exceptions_2,
                                       bool ignore_audio_setting,
                                       bool ignore_video_setting);
diff --git a/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils_unittest.cc b/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils_unittest.cc
index ce9295c..8ca94f37 100644
--- a/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils_unittest.cc
+++ b/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils_unittest.cc
@@ -68,7 +68,9 @@
     // true when they are different.
     EXPECT_TRUE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
         CONTENT_SETTING_BLOCK,
+        CONTENT_SETTING_ASK,
         MediaExceptions(),
+        CONTENT_SETTING_BLOCK,
         CONTENT_SETTING_ASK,
         MediaExceptions(),
         false,
@@ -92,23 +94,29 @@
     // the result, because it has the same settings as |default_setting_2|.
     EXPECT_TRUE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
         CONTENT_SETTING_ALLOW,
+        CONTENT_SETTING_ALLOW,
         ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
         CONTENT_SETTING_ASK,
+        CONTENT_SETTING_ASK,
         ConvertAndSort(exceptions_2, arraysize(exceptions_2)),
         false,
         false));
     EXPECT_TRUE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
         CONTENT_SETTING_ASK,
+        CONTENT_SETTING_ASK,
         ConvertAndSort(exceptions_2, arraysize(exceptions_2)),
         CONTENT_SETTING_ALLOW,
+        CONTENT_SETTING_ALLOW,
         ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
         false,
         false));
     // Changing |default_setting_2| should change the result.
     EXPECT_FALSE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
         CONTENT_SETTING_ALLOW,
+        CONTENT_SETTING_ALLOW,
         ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
         CONTENT_SETTING_ALLOW,
+        CONTENT_SETTING_ALLOW,
         ConvertAndSort(exceptions_2, arraysize(exceptions_2)),
         false,
         false));
@@ -131,15 +139,19 @@
 
     EXPECT_TRUE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
         CONTENT_SETTING_ALLOW,
+        CONTENT_SETTING_ALLOW,
         ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
         CONTENT_SETTING_ASK,
+        CONTENT_SETTING_ASK,
         ConvertAndSort(exceptions_2, arraysize(exceptions_2)),
         false,
         false));
     EXPECT_FALSE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
         CONTENT_SETTING_ALLOW,
+        CONTENT_SETTING_ALLOW,
         ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
         CONTENT_SETTING_ALLOW,
+        CONTENT_SETTING_ALLOW,
         ConvertAndSort(exceptions_2, arraysize(exceptions_2)),
         false,
         false));
@@ -159,15 +171,19 @@
     // Test that |ignore_video_setting| works.
     EXPECT_TRUE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
         CONTENT_SETTING_ASK,
+        CONTENT_SETTING_ASK,
         ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
         CONTENT_SETTING_ASK,
+        CONTENT_SETTING_BLOCK,
         ConvertAndSort(exceptions_2, arraysize(exceptions_2)),
         false,
         true));
     EXPECT_FALSE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
         CONTENT_SETTING_ASK,
+        CONTENT_SETTING_ASK,
         ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
         CONTENT_SETTING_ASK,
+        CONTENT_SETTING_ASK,
         ConvertAndSort(exceptions_2, arraysize(exceptions_2)),
         false,
         false));
@@ -186,16 +202,20 @@
 
     // Test that |ignore_audio_setting| works.
     EXPECT_TRUE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
+        CONTENT_SETTING_BLOCK,
         CONTENT_SETTING_ASK,
         ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
         CONTENT_SETTING_ASK,
+        CONTENT_SETTING_ASK,
         ConvertAndSort(exceptions_2, arraysize(exceptions_2)),
         true,
         false));
     EXPECT_FALSE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
         CONTENT_SETTING_ASK,
+        CONTENT_SETTING_ASK,
         ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
         CONTENT_SETTING_ASK,
+        CONTENT_SETTING_ASK,
         ConvertAndSort(exceptions_2, arraysize(exceptions_2)),
         false,
         false));
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
index e6753e4..3236e3ec 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
@@ -108,6 +108,7 @@
 void ExtensionPrinterHandler::StartPrint(
     const std::string& destination_id,
     const std::string& capability,
+    const base::string16& job_title,
     const std::string& ticket_json,
     const gfx::Size& page_size,
     const scoped_refptr<base::RefCountedMemory>& print_data,
@@ -115,6 +116,7 @@
   scoped_ptr<extensions::PrinterProviderPrintJob> print_job(
       new extensions::PrinterProviderPrintJob());
   print_job->printer_id = destination_id;
+  print_job->job_title = job_title;
   print_job->ticket_json = ticket_json;
 
   cloud_devices::CloudDeviceDescription printer_description;
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler.h b/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
index b236407..9995184 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
 #include "chrome/browser/ui/webui/print_preview/printer_handler.h"
 #include "extensions/browser/api/printer_provider/printer_provider_api.h"
 
@@ -59,6 +60,7 @@
   // TODO(tbarzic): It might make sense to have the strings in a single struct.
   void StartPrint(const std::string& destination_id,
                   const std::string& capability,
+                  const base::string16& job_title,
                   const std::string& ticket_json,
                   const gfx::Size& page_size,
                   const scoped_refptr<base::RefCountedMemory>& print_data,
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
index b87fa84..35517fa1 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
@@ -12,6 +12,8 @@
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/run_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/local_discovery/pwg_raster_converter.h"
 #include "chrome/browser/ui/webui/print_preview/extension_printer_handler.h"
@@ -512,10 +514,11 @@
   scoped_refptr<base::RefCountedString> print_data(
       new base::RefCountedString());
   print_data->data() = "print data, PDF";
+  base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPdfSupportedPrinter, kEmptyPrintTicket, gfx::Size(100, 100),
-      print_data,
+      kPrinterId, kPdfSupportedPrinter, title, kEmptyPrintTicket,
+      gfx::Size(100, 100), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(0u, call_count);
@@ -527,6 +530,7 @@
   ASSERT_TRUE(print_job);
 
   EXPECT_EQ(kPrinterId, print_job->printer_id);
+  EXPECT_EQ(title, print_job->job_title);
   EXPECT_EQ(kEmptyPrintTicket, print_job->ticket_json);
   EXPECT_EQ(kContentTypePDF, print_job->content_type);
   EXPECT_TRUE(print_job->document_path.empty());
@@ -549,10 +553,11 @@
   scoped_refptr<base::RefCountedString> print_data(
       new base::RefCountedString());
   print_data->data() = "print data, PDF";
+  base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPdfSupportedPrinter, kEmptyPrintTicket, gfx::Size(100, 100),
-      print_data,
+      kPrinterId, kPdfSupportedPrinter, title, kEmptyPrintTicket,
+      gfx::Size(100, 100), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(0u, call_count);
@@ -575,9 +580,10 @@
   scoped_refptr<base::RefCountedString> print_data(
       new base::RefCountedString());
   print_data->data() = "print data, PDF";
+  base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kAllContentTypesSupportedPrinter, kEmptyPrintTicket,
+      kPrinterId, kAllContentTypesSupportedPrinter, title, kEmptyPrintTicket,
       gfx::Size(100, 100), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
@@ -591,6 +597,7 @@
   ASSERT_TRUE(print_job);
 
   EXPECT_EQ(kPrinterId, print_job->printer_id);
+  EXPECT_EQ(title, print_job->job_title);
   EXPECT_EQ(kEmptyPrintTicket, print_job->ticket_json);
   EXPECT_EQ(kContentTypePDF, print_job->content_type);
   EXPECT_TRUE(print_job->document_path.empty());
@@ -613,10 +620,11 @@
   scoped_refptr<base::RefCountedString> print_data(
       new base::RefCountedString());
   print_data->data() = "print data, PDF";
+  base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, kEmptyPrintTicket,
-      gfx::Size(100, 50), print_data,
+      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
+      kEmptyPrintTicket, gfx::Size(100, 50), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(0u, call_count);
@@ -642,6 +650,7 @@
   ASSERT_TRUE(print_job);
 
   EXPECT_EQ(kPrinterId, print_job->printer_id);
+  EXPECT_EQ(title, print_job->job_title);
   EXPECT_EQ(kEmptyPrintTicket, print_job->ticket_json);
   EXPECT_EQ(kContentTypePWG, print_job->content_type);
   EXPECT_FALSE(print_job->document_bytes);
@@ -665,9 +674,10 @@
   scoped_refptr<base::RefCountedString> print_data(
       new base::RefCountedString());
   print_data->data() = "print data, PDF";
+  base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPWGRasterOnlyPrinter, kPrintTicketWithDuplex,
+      kPrinterId, kPWGRasterOnlyPrinter, title, kPrintTicketWithDuplex,
       gfx::Size(100, 50), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
@@ -694,6 +704,7 @@
   ASSERT_TRUE(print_job);
 
   EXPECT_EQ(kPrinterId, print_job->printer_id);
+  EXPECT_EQ(title, print_job->job_title);
   EXPECT_EQ(kPrintTicketWithDuplex, print_job->ticket_json);
   EXPECT_EQ(kContentTypePWG, print_job->content_type);
   EXPECT_FALSE(print_job->document_bytes);
@@ -717,10 +728,11 @@
   scoped_refptr<base::RefCountedString> print_data(
       new base::RefCountedString());
   print_data->data() = "print data, PDF";
+  base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, kEmptyPrintTicket,
-      gfx::Size(100, 50), print_data,
+      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
+      kEmptyPrintTicket, gfx::Size(100, 50), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(0u, call_count);
@@ -746,10 +758,11 @@
   scoped_refptr<base::RefCountedString> print_data(
       new base::RefCountedString());
   print_data->data() = "print data, PDF";
+  base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, "{}" /* ticket */,
-      gfx::Size(100, 100), print_data,
+      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
+      "{}" /* ticket */, gfx::Size(100, 100), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(1u, call_count);
@@ -768,10 +781,11 @@
   scoped_refptr<base::RefCountedString> print_data(
       new base::RefCountedString());
   print_data->data() = "print data, PDF";
+  base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, kEmptyPrintTicket,
-      gfx::Size(100, 100), print_data,
+      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
+      kEmptyPrintTicket, gfx::Size(100, 100), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(1u, call_count);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 30327e9c..f9c6252 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -915,9 +915,10 @@
 
     EnsureExtensionPrinterHandlerSet();
     extension_printer_handler_->StartPrint(
-        destination_id, capabilities, print_ticket, gfx::Size(width, height),
-        data, base::Bind(&PrintPreviewHandler::OnExtensionPrintResult,
-                         base::Unretained(this)));
+        destination_id, capabilities, title, print_ticket,
+        gfx::Size(width, height), data,
+        base::Bind(&PrintPreviewHandler::OnExtensionPrintResult,
+                   base::Unretained(this)));
     return;
   }
 
diff --git a/chrome/browser/ui/webui/print_preview/printer_handler.h b/chrome/browser/ui/webui/print_preview/printer_handler.h
index d51c22e..7882e49 100644
--- a/chrome/browser/ui/webui/print_preview/printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/printer_handler.h
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
 
 namespace base {
 class DictionaryValue;
@@ -61,6 +62,7 @@
   // Starts a print request.
   // |destination_id|: The printer to which print job should be sent.
   // |capability|: Capability reported by the printer.
+  // |job_title|: The  title used for print job.
   // |ticket_json|: The print job ticket as JSON string.
   // |page_size|: The document page size.
   // |print_data|: The document bytes to print.
@@ -69,6 +71,7 @@
   virtual void StartPrint(
       const std::string& destination_id,
       const std::string& capability,
+      const base::string16& job_title,
       const std::string& ticket_json,
       const gfx::Size& page_size,
       const scoped_refptr<base::RefCountedMemory>& print_data,
diff --git a/chrome/browser/ui/webui/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
similarity index 96%
rename from chrome/browser/ui/webui/md_settings_ui.cc
rename to chrome/browser/ui/webui/settings/md_settings_ui.cc
index 2f7e40a..65f9908 100644
--- a/chrome/browser/ui/webui/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/md_settings_ui.h"
+#include "chrome/browser/ui/webui/settings/md_settings_ui.h"
 
 #include <string>
 
diff --git a/chrome/browser/ui/webui/md_settings_ui.h b/chrome/browser/ui/webui/settings/md_settings_ui.h
similarity index 80%
rename from chrome/browser/ui/webui/md_settings_ui.h
rename to chrome/browser/ui/webui/settings/md_settings_ui.h
index 8ad796b7..d2160dc 100644
--- a/chrome/browser/ui/webui/md_settings_ui.h
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_MD_SETTINGS_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_MD_SETTINGS_UI_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_UI_H_
 
 #include "chrome/browser/ui/webui/options/core_options_handler.h"
 #include "chrome/browser/ui/webui/options/options_ui.h"
@@ -25,4 +25,4 @@
   DISALLOW_COPY_AND_ASSIGN(MdSettingsUI);
 };
 
-#endif  // CHROME_BROWSER_UI_WEBUI_MD_SETTINGS_UI_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_UI_H_
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
index 8a12f07..95a25f43 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -75,6 +75,8 @@
 const char kJsApiUserManagerLaunchUser[] = "launchUser";
 const char kJsApiUserManagerRemoveUser[] = "removeUser";
 const char kJsApiUserManagerAttemptUnlock[] = "attemptUnlock";
+const char kJsApiUserManagerLogRemoveUserWarningShown[] =
+    "logRemoveUserWarningShown";
 
 const size_t kAvatarIconSize = 180;
 
@@ -212,6 +214,11 @@
     chrome::ShowMemory(target_browser);
 }
 
+void HandleLogRemoveUserWarningShown(const base::ListValue* args) {
+  ProfileMetrics::LogProfileDeleteUser(
+      ProfileMetrics::DELETE_PROFILE_USER_MANAGER_SHOW_WARNING);
+}
+
 }  // namespace
 
 // ProfileUpdateObserver ------------------------------------------------------
@@ -594,6 +601,8 @@
   web_ui()->RegisterMessageCallback(kJsApiUserManagerAttemptUnlock,
       base::Bind(&UserManagerScreenHandler::HandleAttemptUnlock,
                  base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(kJsApiUserManagerLogRemoveUserWarningShown,
+      base::Bind(&HandleLogRemoveUserWarningShown));
 
   const content::WebUI::MessageCallback& kDoNothingCallback =
       base::Bind(&HandleAndDoNothing);
diff --git a/chrome/browser/ui/webui/uber/uber_ui.cc b/chrome/browser/ui/webui/uber/uber_ui.cc
index d4baf30..8b0d516 100644
--- a/chrome/browser/ui/webui/uber/uber_ui.cc
+++ b/chrome/browser/ui/webui/uber/uber_ui.cc
@@ -5,9 +5,6 @@
 #include "chrome/browser/ui/webui/uber/uber_ui.h"
 
 #include "base/stl_util.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
 #include "chrome/browser/ui/webui/extensions/extensions_ui.h"
 #include "chrome/browser/ui/webui/options/options_ui.h"
@@ -16,6 +13,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_source.h"
@@ -59,9 +57,10 @@
 }
 
 // Determines whether the user has an active extension of the given type.
-bool HasExtensionType(Profile* profile, const std::string& extension_type) {
+bool HasExtensionType(content::BrowserContext* browser_context,
+                      const std::string& extension_type) {
   const extensions::ExtensionSet& extension_set =
-      extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
+      extensions::ExtensionRegistry::Get(browser_context)->enabled_extensions();
   for (extensions::ExtensionSet::const_iterator iter = extension_set.begin();
        iter != extension_set.end(); ++iter) {
     const extensions::URLOverrides::URLOverrideMap& map =
@@ -73,7 +72,8 @@
   return false;
 }
 
-content::WebUIDataSource* CreateUberFrameHTMLSource(Profile* profile) {
+content::WebUIDataSource* CreateUberFrameHTMLSource(
+    content::BrowserContext* browser_context) {
   content::WebUIDataSource* source =
       content::WebUIDataSource::Create(chrome::kChromeUIUberFrameHost);
 
@@ -106,7 +106,7 @@
   source->AddLocalizedString("settingsDisplayName", IDS_SETTINGS_TITLE);
   source->AddString("settingsGroup", settings_group);
   bool overridesHistory =
-      HasExtensionType(profile, chrome::kChromeUIHistoryHost);
+      HasExtensionType(browser_context, chrome::kChromeUIHistoryHost);
   source->AddString("overridesHistory", overridesHistory ? "yes" : "no");
   source->DisableDenyXFrameOptions();
   source->OverrideContentSecurityPolicyFrameSrc("frame-src chrome:;");
@@ -114,11 +114,21 @@
   return source;
 }
 
+void UpdateHistoryNavigation(content::WebUI* web_ui) {
+  bool overrides_history =
+      HasExtensionType(web_ui->GetWebContents()->GetBrowserContext(),
+                       chrome::kChromeUIHistoryHost);
+  web_ui->CallJavascriptFunction(
+      "uber_frame.setNavigationOverride",
+      base::StringValue(chrome::kChromeUIHistoryHost),
+      base::StringValue(overrides_history ? "yes" : "no"));
+}
+
 }  // namespace
 
 UberUI::UberUI(content::WebUI* web_ui) : WebUIController(web_ui) {
-  Profile* profile = Profile::FromWebUI(web_ui);
-  content::WebUIDataSource::Add(profile, CreateUberHTMLSource());
+  content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
+                                CreateUberHTMLSource());
 
   RegisterSubpage(chrome::kChromeUIExtensionsFrameURL,
                   chrome::kChromeUIExtensionsHost);
@@ -186,44 +196,36 @@
 
 // UberFrameUI
 
-UberFrameUI::UberFrameUI(content::WebUI* web_ui) : WebUIController(web_ui) {
-  Profile* profile = Profile::FromWebUI(web_ui);
-  content::WebUIDataSource::Add(profile, CreateUberFrameHTMLSource(profile));
+UberFrameUI::UberFrameUI(content::WebUI* web_ui)
+    : WebUIController(web_ui),
+      extension_registry_observer_(this) {
+  content::BrowserContext* browser_context =
+      web_ui->GetWebContents()->GetBrowserContext();
+  content::WebUIDataSource::Add(browser_context,
+                                CreateUberFrameHTMLSource(browser_context));
 
   // Register as an observer for when extensions are loaded and unloaded.
-  registrar_.Add(this,
-                 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
-                 content::Source<Profile>(profile));
-  registrar_.Add(this,
-                 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
-                 content::Source<Profile>(profile));
+  extension_registry_observer_.Add(
+      extensions::ExtensionRegistry::Get(browser_context));
 }
 
 UberFrameUI::~UberFrameUI() {
 }
 
-void UberFrameUI::Observe(int type,
-                          const content::NotificationSource& source,
-                          const content::NotificationDetails& details) {
-  switch (type) {
-    // We listen for notifications that indicate an extension has been loaded
-    // (i.e., has been installed and/or enabled) or unloaded (i.e., has been
-    // uninstalled and/or disabled). If one of these events has occurred, then
-    // we must update the behavior of the History navigation element so that
-    // it opens the history extension if one is installed and enabled or
-    // opens the default history page if one is uninstalled or disabled.
-    case extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED:
-    case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
-      Profile* profile = Profile::FromWebUI(web_ui());
-      bool overrides_history =
-          HasExtensionType(profile, chrome::kChromeUIHistoryHost);
-      web_ui()->CallJavascriptFunction(
-          "uber_frame.setNavigationOverride",
-          base::StringValue(chrome::kChromeUIHistoryHost),
-          base::StringValue(overrides_history ? "yes" : "no"));
-      break;
-    }
-    default:
-      NOTREACHED();
-  }
+void UberFrameUI::OnExtensionLoaded(content::BrowserContext* browser_context,
+                                    const extensions::Extension* extension) {
+  // We listen for notifications that indicate an extension has been loaded
+  // (i.e., has been installed and/or enabled) or unloaded (i.e., has been
+  // uninstalled and/or disabled). If one of these events has occurred, then
+  // we must update the behavior of the History navigation element so that
+  // it opens the history extension if one is installed and enabled or
+  // opens the default history page if one is uninstalled or disabled.
+  UpdateHistoryNavigation(web_ui());
+}
+
+void UberFrameUI::OnExtensionUnloaded(
+    content::BrowserContext* browser_context,
+    const extensions::Extension* extension,
+    extensions::UnloadedExtensionInfo::Reason reason) {
+  UpdateHistoryNavigation(web_ui());
 }
diff --git a/chrome/browser/ui/webui/uber/uber_ui.h b/chrome/browser/ui/webui/uber/uber_ui.h
index bbb3c70e..ca04098 100644
--- a/chrome/browser/ui/webui/uber/uber_ui.h
+++ b/chrome/browser/ui/webui/uber/uber_ui.h
@@ -7,12 +7,18 @@
 
 #include <string>
 
-#include "base/memory/scoped_vector.h"
+#include "base/scoped_observer.h"
 #include "base/values.h"
-#include "chrome/browser/profiles/profile.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/web_ui_controller.h"
+#include "extensions/browser/extension_registry_observer.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace extensions {
+class ExtensionRegistry;
+}
 
 // The WebUI class for the uber page (chrome://chrome). It manages the UI for
 // the uber page (navigation bar and so forth) as well as WebUI objects for
@@ -47,19 +53,24 @@
   DISALLOW_COPY_AND_ASSIGN(UberUI);
 };
 
-class UberFrameUI : public content::NotificationObserver,
-                    public content::WebUIController {
+class UberFrameUI : public content::WebUIController,
+                    public extensions::ExtensionRegistryObserver {
  public:
   explicit UberFrameUI(content::WebUI* web_ui);
   ~UberFrameUI() override;
 
  private:
-  // content::NotificationObserver implementation.
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
+  // extensions::ExtensionRegistryObserver implementation.
+  void OnExtensionLoaded(content::BrowserContext* browser_context,
+                         const extensions::Extension* extension) override;
+  void OnExtensionUnloaded(
+      content::BrowserContext* browser_context,
+      const extensions::Extension* extension,
+      extensions::UnloadedExtensionInfo::Reason reason) override;
 
-  content::NotificationRegistrar registrar_;
+  ScopedObserver<extensions::ExtensionRegistry,
+                 extensions::ExtensionRegistryObserver>
+      extension_registry_observer_;
 
   DISALLOW_COPY_AND_ASSIGN(UberFrameUI);
 };
diff --git a/chrome/browser/ui/webui/uber/uber_ui_browsertest.cc b/chrome/browser/ui/webui/uber/uber_ui_browsertest.cc
new file mode 100644
index 0000000..6ec293c
--- /dev/null
+++ b/chrome/browser/ui/webui/uber/uber_ui_browsertest.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 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 <string>
+
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+
+class UberUIBrowserTest : public WebUIBrowserTest {};
+
+IN_PROC_BROWSER_TEST_F(UberUIBrowserTest, HistoryOverride) {
+  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIUberFrameURL));
+  content::WebContents* tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Inject script. This script will be called when Extension is loaded.
+  const std::string inject_script =
+      "var is_called_override = false;"
+      "var uber_frame = {};"
+      "uber_frame.setNavigationOverride = function(controls, override) { "
+      "is_called_override = true; };";
+
+  ASSERT_TRUE(content::ExecuteScript(tab, inject_script));
+
+  scoped_refptr<const extensions::Extension> extension =
+      extensions::ExtensionBuilder()
+          .SetManifest(extensions::DictionaryBuilder()
+                           .Set("name", "History Override")
+                           .Set("version", "1")
+                           .Set("manifest_version", 2)
+                           .Set("permission",
+                                extensions::ListBuilder().Append("history")))
+          .Build();
+
+  ExtensionService* service = extensions::ExtensionSystem::Get(
+                                  browser()->profile())->extension_service();
+  // Load extension. UberUI overrides history navigation.
+  // In this test, injected script will be called instead.
+  service->AddExtension(extension.get());
+
+  bool called_override;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+      tab, "domAutomationController.send(is_called_override);",
+      &called_override));
+  EXPECT_TRUE(called_override);
+}
diff --git a/chrome/browser/ui/webui/voice_search_ui.cc b/chrome/browser/ui/webui/voice_search_ui.cc
index 3ddf0e94..6731445 100644
--- a/chrome/browser/ui/webui/voice_search_ui.cc
+++ b/chrome/browser/ui/webui/voice_search_ui.cc
@@ -168,12 +168,9 @@
     AddHotwordInfo(list.get());
     AddAppListInfo(list.get());
 
-    std::string extension_id = extension_misc::kHotwordExtensionId;
-    HotwordService* hotword_service =
-        HotwordServiceFactory::GetForProfile(profile_);
-    if (hotword_service && hotword_service->IsExperimentalHotwordingEnabled())
-      extension_id = extension_misc::kHotwordNewExtensionId;
-    AddExtensionInfo(extension_id, "Extension", list.get());
+    AddExtensionInfo(extension_misc::kHotwordNewExtensionId,
+                     "Extension",
+                     list.get());
 
     AddExtensionInfo(extension_misc::kHotwordSharedModuleId,
                      "Shared Module",
@@ -326,11 +323,6 @@
         hotword_internal::kHotwordFieldTrialName);
     AddPair(list, "Field trial", group);
 
-    std::string new_hotwording_enabled = "No";
-    if (hotword_service && hotword_service->IsExperimentalHotwordingEnabled())
-      new_hotwording_enabled = "Yes";
-    AddPair(list, "New Hotwording Enabled", new_hotwording_enabled);
-
     AddLineBreak(list);
   }
 
diff --git a/chrome/browser/web_applications/update_shortcut_worker_win.cc b/chrome/browser/web_applications/update_shortcut_worker_win.cc
index a1f5985..39d6ec0 100644
--- a/chrome/browser/web_applications/update_shortcut_worker_win.cc
+++ b/chrome/browser/web_applications/update_shortcut_worker_win.cc
@@ -192,7 +192,7 @@
 
   base::FilePath icon_file =
       web_app::internals::GetIconFilePath(web_app_path, shortcut_info_.title);
-  web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info_.favicon);
+  web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info_.favicon, true);
 
   // Update existing shortcuts' description, icon and app id.
   CheckExistingShortcuts();
diff --git a/chrome/browser/web_applications/web_app_win.cc b/chrome/browser/web_applications/web_app_win.cc
index 10bad71..45fdf5e9 100644
--- a/chrome/browser/web_applications/web_app_win.cc
+++ b/chrome/browser/web_applications/web_app_win.cc
@@ -183,7 +183,8 @@
   // Generates file name to use with persisted ico and shortcut file.
   base::FilePath icon_file =
       web_app::internals::GetIconFilePath(web_app_path, shortcut_info.title);
-  if (!web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon)) {
+  if (!web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon,
+                                            false)) {
     return false;
   }
 
@@ -357,7 +358,7 @@
     return;
 
   ui::win::SetAppIconForWindow(icon_file.value(), hwnd);
-  web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon);
+  web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon, true);
 }
 
 void OnShortcutInfoLoadedForSetRelaunchDetails(
@@ -543,7 +544,7 @@
   } else {
     internals::CheckAndSaveIcon(
         internals::GetIconFilePath(web_app_dir, shortcut_info.title),
-        shortcut_info.favicon);
+        shortcut_info.favicon, true);
   }
   return web_app_dir_shortcut;
 }
@@ -564,23 +565,22 @@
 
 namespace internals {
 
-// Saves |image| to |icon_file| if the file is outdated and refresh shell's
-// icon cache to ensure correct icon is displayed. Returns true if icon_file
-// is up to date or successfully updated.
 bool CheckAndSaveIcon(const base::FilePath& icon_file,
-                      const gfx::ImageFamily& image) {
-  if (ShouldUpdateIcon(icon_file, image)) {
-    if (SaveIconWithCheckSum(icon_file, image)) {
-      // Refresh shell's icon cache. This call is quite disruptive as user would
-      // see explorer rebuilding the icon cache. It would be great that we find
-      // a better way to achieve this.
-      SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT,
-                     NULL, NULL);
-    } else {
-      return false;
-    }
-  }
+                      const gfx::ImageFamily& image,
+                      bool refresh_shell_icon_cache) {
+  if (!ShouldUpdateIcon(icon_file, image))
+    return true;
 
+  if (!SaveIconWithCheckSum(icon_file, image))
+    return false;
+
+  if (refresh_shell_icon_cache) {
+    // Refresh shell's icon cache. This call is quite disruptive as user would
+    // see explorer rebuilding the icon cache. It would be great that we find
+    // a better way to achieve this.
+    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, NULL,
+                   NULL);
+  }
   return true;
 }
 
@@ -674,7 +674,7 @@
 
   // Update the icon if necessary.
   base::FilePath icon_file = GetIconFilePath(web_app_path, shortcut_info.title);
-  CheckAndSaveIcon(icon_file, shortcut_info.favicon);
+  CheckAndSaveIcon(icon_file, shortcut_info.favicon, true);
 }
 
 void DeletePlatformShortcuts(const base::FilePath& web_app_path,
diff --git a/chrome/browser/web_applications/web_app_win.h b/chrome/browser/web_applications/web_app_win.h
index b86f91a..7dba046 100644
--- a/chrome/browser/web_applications/web_app_win.h
+++ b/chrome/browser/web_applications/web_app_win.h
@@ -37,8 +37,15 @@
 
 namespace internals {
 
+// Saves |image| to |icon_file| if the file is outdated. Returns true if
+// icon_file is up to date or successfully updated.
+// If |refresh_shell_icon_cache| is true, the shell's icon cache will be
+// refreshed, ensuring the correct icon is displayed, but causing a flicker.
+// Refreshing the icon cache is not necessary on shortcut creation as the shell
+// will be notified when the shortcut is created.
 bool CheckAndSaveIcon(const base::FilePath& icon_file,
-                      const gfx::ImageFamily& image);
+                      const gfx::ImageFamily& image,
+                      bool refresh_shell_icon_cache);
 
 base::FilePath GetIconFilePath(const base::FilePath& web_app_path,
                                const base::string16& title);
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 0e5b5fc..47188f1 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -277,6 +277,7 @@
         {
           # A library containing the actual code for the app mode app, shared
           # by unit tests.
+          # GN: //chrome/common:app_mode_app_support
           'target_name': 'app_mode_app_support',
           'type': 'static_library',
           'variables': { 'enable_wexit_time_destructors': 1, },
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index acaf49d2..061e1f18 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -192,6 +192,8 @@
       'browser/app_mode/app_mode_utils.h',
       'browser/autofill/android/personal_data_manager_android.cc',
       'browser/autofill/android/personal_data_manager_android.h',
+      'browser/autofill/options_util.cc',
+      'browser/autofill/options_util.h',
       'browser/autofill/personal_data_manager_factory.cc',
       'browser/autofill/personal_data_manager_factory.h',
       'browser/autofill/risk_util.cc',
@@ -249,6 +251,8 @@
       'browser/browsing_data/cookies_tree_model.h',
       'browser/browsing_data/local_data_container.cc',
       'browser/browsing_data/local_data_container.h',
+      'browser/browsing_data/storage_partition_http_cache_data_remover.cc',
+      'browser/browsing_data/storage_partition_http_cache_data_remover.h',
       'browser/caps/generate_state_json.h',
       'browser/caps/generate_state_json.cc',
       'browser/character_encoding.cc',
@@ -1731,8 +1735,6 @@
       'browser/metrics/metrics_reporting_state.h',
       'browser/metrics/metrics_services_manager.cc',
       'browser/metrics/metrics_services_manager.h',
-      'browser/metrics/network_stats_uploader.cc',
-      'browser/metrics/network_stats_uploader.h',
       'browser/metrics/omnibox_metrics_provider.cc',
       'browser/metrics/omnibox_metrics_provider.h',
       'browser/metrics/perf_provider_chromeos.cc',
@@ -1812,8 +1814,6 @@
       'browser/net/net_log_temp_file.h',
       'browser/net/net_pref_observer.cc',
       'browser/net/net_pref_observer.h',
-      'browser/net/network_stats.cc',
-      'browser/net/network_stats.h',
       'browser/net/preconnect.cc',
       'browser/net/preconnect.h',
       'browser/net/prediction_options.cc',
@@ -2568,6 +2568,12 @@
       'browser/signin/signin_tracker_factory.h',
     ],
     'chrome_browser_spellchecker_sources': [
+      'browser/spellchecker/feedback.cc',
+      'browser/spellchecker/feedback.h',
+      'browser/spellchecker/feedback_sender.cc',
+      'browser/spellchecker/feedback_sender.h',
+      'browser/spellchecker/misspelling.cc',
+      'browser/spellchecker/misspelling.h',
       'browser/spellchecker/spellcheck_action.cc',
       'browser/spellchecker/spellcheck_action.h',
       'browser/spellchecker/spellcheck_custom_dictionary.cc',
@@ -2589,6 +2595,8 @@
       'browser/spellchecker/spellcheck_service.h',
       'browser/spellchecker/spelling_service_client.cc',
       'browser/spellchecker/spelling_service_client.h',
+      'browser/spellchecker/word_trimmer.cc',
+      'browser/spellchecker/word_trimmer.h',
     ],
     'chrome_browser_ssl_sources': [
       'browser/ssl/chrome_ssl_host_state_delegate.cc',
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index cc1736d..fb99fb3 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -599,6 +599,8 @@
         'browser/chromeos/login/signin/oauth2_login_verifier.h',
         'browser/chromeos/login/signin/oauth2_token_fetcher.cc',
         'browser/chromeos/login/signin/oauth2_token_fetcher.h',
+        'browser/chromeos/login/signin/token_handler_util.cc',
+        'browser/chromeos/login/signin/token_handler_util.h',
         'browser/chromeos/login/signin_screen_controller.cc',
         'browser/chromeos/login/signin_screen_controller.h',
         'browser/chromeos/login/signin_specifics.cc',
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index cba2cee..0e66f77 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -392,6 +392,8 @@
       'browser/extensions/api/sessions/sessions_api.h',
       'browser/extensions/api/settings_overrides/settings_overrides_api.cc',
       'browser/extensions/api/settings_overrides/settings_overrides_api.h',
+      'browser/extensions/api/settings_private/settings_private_api.cc',
+      'browser/extensions/api/settings_private/settings_private_api.h',
       'browser/extensions/api/signed_in_devices/id_mapping_helper.cc',
       'browser/extensions/api/signed_in_devices/id_mapping_helper.h',
       'browser/extensions/api/signed_in_devices/signed_in_devices_api.cc',
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 6eafb63..85692c6 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -717,6 +717,8 @@
       'browser/ui/cocoa/tabs/tab_view.mm',
       'browser/ui/cocoa/tabs/tab_window_controller.h',
       'browser/ui/cocoa/tabs/tab_window_controller.mm',
+      'browser/ui/cocoa/task_manager_mac.h',
+      'browser/ui/cocoa/task_manager_mac.mm',
       'browser/ui/cocoa/themed_window.h',
       'browser/ui/cocoa/themed_window.mm',
       'browser/ui/cocoa/toolbar/back_forward_menu_controller.h',
@@ -1673,8 +1675,6 @@
       'browser/ui/webui/identity_internals_ui.h',
       'browser/ui/webui/inspect_ui.cc',
       'browser/ui/webui/inspect_ui.h',
-      'browser/ui/webui/md_settings_ui.cc',
-      'browser/ui/webui/md_settings_ui.h',
       'browser/ui/webui/ntp/app_launcher_handler.cc',
       'browser/ui/webui/ntp/app_launcher_handler.h',
       'browser/ui/webui/ntp/app_resource_cache_factory.cc',
@@ -1827,6 +1827,8 @@
       'browser/ui/webui/quota_internals/quota_internals_types.h',
       'browser/ui/webui/quota_internals/quota_internals_ui.cc',
       'browser/ui/webui/quota_internals/quota_internals_ui.h',
+      'browser/ui/webui/settings/md_settings_ui.cc',
+      'browser/ui/webui/settings/md_settings_ui.h',
       'browser/ui/webui/signin/inline_login_handler.cc',
       'browser/ui/webui/signin/inline_login_handler.h',
       'browser/ui/webui/signin/inline_login_ui.cc',
@@ -2291,6 +2293,7 @@
       'browser/ui/views/tabs/window_finder_chromeos.cc',
       'browser/ui/views/tabs/window_finder_mac.mm',
       'browser/ui/views/tabs/window_finder_win.cc',
+      'browser/ui/views/task_manager_view.cc',
       'browser/ui/views/theme_image_mapper.cc',
       'browser/ui/views/theme_image_mapper.h',
       'browser/ui/views/toolbar/back_button.cc',
@@ -2662,11 +2665,6 @@
       'browser/ui/webui/local_discovery/local_discovery_ui_handler.cc',
       'browser/ui/webui/local_discovery/local_discovery_ui_handler.h',
     ],
-    'chrome_browser_ui_task_manager_sources': [
-      'browser/ui/cocoa/task_manager_mac.h',
-      'browser/ui/cocoa/task_manager_mac.mm',
-      'browser/ui/views/task_manager_view.cc',
-    ],
     'chrome_browser_ui_toolbar_model_sources': [
       'browser/ui/android/toolbar/toolbar_model_android.cc',
       'browser/ui/android/toolbar/toolbar_model_android.h',
@@ -2807,9 +2805,6 @@
         ['enable_one_click_signin==1', {
           'sources': [ '<@(chrome_browser_ui_one_click_signin_sources)' ]
         }],
-        ['enable_task_manager==1', {
-          'sources': [ '<@(chrome_browser_ui_task_manager_sources)' ],
-        }],
         ['disable_nacl==0', {
           'sources': [ '<@(chrome_browser_ui_nacl_sources)' ],
           'dependencies': [
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index 54ae50f..9c44174 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -67,8 +67,6 @@
       'common/localized_error.h',
       'common/logging_chrome.cc',
       'common/logging_chrome.h',
-      'common/mac/app_mode_common.h',
-      'common/mac/app_mode_common.mm',
       'common/mac/app_shim_launch.h',
       'common/mac/app_shim_messages.h',
       'common/mac/cfbundle_blocker.h',
@@ -380,6 +378,7 @@
         }],
         ['OS=="mac"', {
           'sources': [ '<@(chrome_common_mac_sources)' ],
+          'dependencies': [ 'app_mode_app_support' ],
         }],
         ['OS != "ios"', {
           'dependencies': [
diff --git a/chrome/chrome_repack_chrome_100_percent.gypi b/chrome/chrome_repack_chrome_100_percent.gypi
index c585684..ded84b0b 100644
--- a/chrome/chrome_repack_chrome_100_percent.gypi
+++ b/chrome/chrome_repack_chrome_100_percent.gypi
@@ -15,6 +15,7 @@
     'conditions': [
       ['OS != "ios"', {
         'pak_inputs': [
+          '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources_100_percent.pak',
           '<(SHARED_INTERMEDIATE_DIR)/content/app/resources/content_resources_100_percent.pak',
         ],
       }],
diff --git a/chrome/chrome_repack_chrome_200_percent.gypi b/chrome/chrome_repack_chrome_200_percent.gypi
index 2aff0e5b..5257327 100644
--- a/chrome/chrome_repack_chrome_200_percent.gypi
+++ b/chrome/chrome_repack_chrome_200_percent.gypi
@@ -15,6 +15,7 @@
     'conditions': [
       ['OS != "ios"', {
         'pak_inputs': [
+          '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources_200_percent.pak',
           '<(SHARED_INTERMEDIATE_DIR)/content/app/resources/content_resources_200_percent.pak',
         ],
       }],
diff --git a/chrome/chrome_repack_resources.gypi b/chrome/chrome_repack_resources.gypi
index 1cc88e5..26a88e6 100644
--- a/chrome/chrome_repack_resources.gypi
+++ b/chrome/chrome_repack_resources.gypi
@@ -29,7 +29,7 @@
       }],
       ['OS != "ios"', {
         'pak_inputs': [
-          '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources.pak',
+          '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources_100_percent.pak',
           '<(SHARED_INTERMEDIATE_DIR)/content/browser/tracing/tracing_resources.pak',
           '<(SHARED_INTERMEDIATE_DIR)/content/content_resources.pak',
         ],
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 4043c24..13dab894 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -299,6 +299,8 @@
       'browser/importer/importer_unittest_utils.cc',
       'browser/importer/importer_unittest_utils.h',
       'browser/infobars/infobars_browsertest.cc',
+      'browser/interstitials/security_interstitial_page_test_utils.cc',
+      'browser/interstitials/security_interstitial_page_test_utils.h',
       'browser/invalidation/profile_invalidation_provider_factory_browsertest.cc',
       'browser/lifetime/browser_close_manager_browsertest.cc',
       'browser/loadtimes_extension_bindings_browsertest.cc',
@@ -475,6 +477,7 @@
       'browser/ui/cocoa/view_id_util_browsertest.mm',
       'browser/ui/content_settings/content_setting_bubble_model_browsertest.cc',
       'browser/ui/exclusive_access/fullscreen_controller_browsertest.cc',
+      'browser/ui/extensions/bookmark_app_browsertest.cc',
       'browser/ui/find_bar/find_bar_host_browsertest.cc',
       'browser/ui/global_error/global_error_service_browsertest.cc',
       'browser/ui/location_bar/location_bar_browsertest.cc',
@@ -526,6 +529,7 @@
       'browser/ui/webui/print_preview/print_preview_ui_browsertest.cc',
       'browser/ui/webui/signin/inline_login_ui_browsertest.cc',
       'browser/ui/webui/signin/user_manager_ui_browsertest.cc',
+      'browser/ui/webui/uber/uber_ui_browsertest.cc',
       'browser/ui/webui/web_ui_test_handler.cc',
       'browser/ui/webui/web_ui_test_handler.h',
       'browser/ui/webui/webui_webview_browsertest.cc',
@@ -1039,6 +1043,7 @@
       'browser/notifications/message_center_notifications_browsertest.cc',
     ],
     'chrome_interactive_ui_test_chromeos_sources': [
+      '../ash/accelerators/accelerator_interactive_uitest_chromeos.cc',
       'browser/chromeos/accessibility/magnification_controller_browsertest.cc',
       'browser/chromeos/accessibility/speech_monitor.cc',
       'browser/chromeos/accessibility/speech_monitor.h',
@@ -2922,6 +2927,26 @@
           'includes': [ '../build/java_apk.gypi' ],
         },
         {
+          # GN: //chrome/android:chrome_shell_unit_tests
+          'target_name': 'chrome_shell_unit_tests',
+          'type': 'none',
+          'dependencies': [
+            'chrome_java',
+            '../base/base.gyp:base',
+            '../base/base.gyp:base_java_test_support',
+            '../testing/android/junit/junit_test.gyp:junit_test_support',
+          ],
+          'variables': {
+            'main_class': 'org.chromium.testing.local.JunitTestMain',
+            'src_paths': [
+              'android/junit/',
+            ],
+          },
+          'includes': [
+            '../build/host_jar.gypi',
+          ],
+        },
+        {
           # GN: //chrome/test/chromedriver/test/webview_shell:chromedriver_webview_shell_apk
           'target_name': 'chromedriver_webview_shell_apk',
           'type': 'none',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 567ed97..b81b86e4 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -59,6 +59,7 @@
       'browser/chrome_elf_init_unittest_win.cc',
       'browser/chrome_process_singleton_win_unittest.cc',
       'browser/command_updater_unittest.cc',
+      'browser/component_updater/test/chrome_component_updater_configurator_unittest.cc',
       'browser/component_updater/test/cld_component_installer_unittest.cc',
       'browser/component_updater/test/component_updater_service_unittest.cc',
       'browser/component_updater/test/supervised_user_whitelist_installer_unittest.cc',
@@ -156,7 +157,6 @@
       'browser/net/evicted_domain_cookie_counter_unittest.cc',
       'browser/net/net_error_tab_helper_unittest.cc',
       'browser/net/net_log_temp_file_unittest.cc',
-      'browser/net/network_stats_unittest.cc',
       'browser/net/predictor_unittest.cc',
       'browser/net/pref_proxy_config_tracker_impl_unittest.cc',
       'browser/net/probe_message_unittest.cc',
@@ -618,6 +618,9 @@
       'browser/ui/views/sync/one_click_signin_bubble_view_unittest.cc',
     ],
     'chrome_unit_tests_spellchecker_sources': [
+      'browser/spellchecker/feedback_sender_unittest.cc',
+      'browser/spellchecker/feedback_unittest.cc',
+      'browser/spellchecker/misspelling_unittest.cc',
       'browser/spellchecker/spellcheck_action_unittest.cc',
       'browser/spellchecker/spellcheck_custom_dictionary_unittest.cc',
       'browser/spellchecker/spellcheck_host_metrics_unittest.cc',
@@ -626,6 +629,7 @@
       'browser/spellchecker/spellcheck_platform_mac_unittest.cc',
       'browser/spellchecker/spellcheck_service_unittest.cc',
       'browser/spellchecker/spelling_service_client_unittest.cc',
+      'browser/spellchecker/word_trimmer_unittest.cc',
       'renderer/spellchecker/custom_dictionary_engine_unittest.cc',
       'renderer/spellchecker/spellcheck_provider_hunspell_unittest.cc',
       'renderer/spellchecker/spellcheck_provider_mac_unittest.cc',
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index eaf9767..b287530 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -135,6 +135,7 @@
   if (is_mac) {
     sources +=
         rebase_path(gypi_values.chrome_common_mac_sources, ".", "//chrome")
+    deps += [ ":app_mode_app_support" ]
   }
 
   if (enable_nacl) {
@@ -319,3 +320,18 @@
     ]
   }
 }
+
+# GN version: chrome/chrome.gyp:app_mode_app_support
+static_library("app_mode_app_support") {
+  sources = [
+    "mac/app_mode_chrome_locator.h",
+    "mac/app_mode_chrome_locator.mm",
+    "mac/app_mode_common.h",
+    "mac/app_mode_common.mm",
+  ]
+
+  deps = [
+    ":constants",
+    "//base",
+  ]
+}
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 193dc25..1938051 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -246,6 +246,9 @@
 const char kDisableDeviceDiscoveryNotifications[] =
     "disable-device-discovery-notifications";
 
+// Disables the dinosaur easter egg on the offline interstitial.
+const char kDisableDinosaurEasterEgg[] = "disable-dinosaur-easter-egg";
+
 // Disables Domain Reliability Monitoring.
 const char kDisableDomainReliability[]      = "disable-domain-reliability";
 
@@ -262,6 +265,9 @@
 const char kDisableExtensionsHttpThrottling[] =
     "disable-extensions-http-throttling";
 
+// Disables large icons on the New Tab page.
+const char kDisableIconNtp[]                = "disable-icon-ntp";
+
 // Don't resolve hostnames to IPv6 addresses. This can be used when debugging
 // issues relating to IPv6, but shouldn't otherwise be needed. Be sure to file
 // bugs if something isn't working properly in the presence of IPv6. This flag
@@ -454,6 +460,12 @@
 // crbug.com/142458 .
 const char kEnableFastUnload[]         = "enable-fast-unload";
 
+// Enables large icons on the New Tab page.
+const char kEnableIconNtp[]                 = "enable-icon-ntp";
+
+// Enable opt-in for the collection of invalid TLS/SSL certificate chains.
+const char kEnableInvalidCertCollection[] = "enable-invalid-cert-collection";
+
 // Enables IPv6 support, even if probes suggest that it may not be fully
 // supported. Some probes may require internet connections, and this flag will
 // allow support independent of application testing. This flag overrides
@@ -1076,6 +1088,25 @@
 #if defined(ENABLE_SPELLCHECK)
 // Enables auto correction for misspelled words.
 const char kEnableSpellingAutoCorrect[]     = "enable-spelling-auto-correct";
+
+// Enables participation in the field trial for user feedback to spelling
+// service.
+const char kEnableSpellingFeedbackFieldTrial[] =
+    "enable-spelling-feedback-field-trial";
+
+// Specifies the URL where spelling service feedback data will be sent instead
+// of the default URL. This switch is for temporary testing only.
+// TODO(rouslan): Remove this flag when feedback testing is complete. Revisit by
+// August 2013.
+const char kSpellingServiceFeedbackUrl[] = "spelling-service-feedback-url";
+
+// Specifies the number of seconds between sending batches of feedback to
+// spelling service. The default is 30 minutes. The minimum is 5 seconds. This
+// switch is for temporary testing only.
+// TODO(rouslan): Remove this flag when feedback testing is complete. Revisit by
+// August 2013.
+const char kSpellingServiceFeedbackIntervalSeconds[] =
+    "spelling-service-feedback-interval-seconds";
 #endif
 
 // Specifies the maximum SSL/TLS version ("ssl3", "tls1", "tls1.1", or
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index feb28fe6..8b4033c3 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -75,10 +75,12 @@
 extern const char kDisableComponentUpdate[];
 extern const char kDisableDefaultApps[];
 extern const char kDisableDeviceDiscoveryNotifications[];
+extern const char kDisableDinosaurEasterEgg[];
 extern const char kDisableDomainReliability[];
 extern const char kDisableExtensionsFileAccessCheck[];
 extern const char kDisableExtensionsHttpThrottling[];
 extern const char kDisableExtensions[];
+extern const char kDisableIconNtp[];
 extern const char kDisableIPv6[];
 extern const char kDisableJavaScriptHarmonyShipping[];
 extern const char kDisableMinimizeOnSecondLauncherItemClick[];
@@ -133,6 +135,8 @@
 extern const char kEnableExtensionActivityLogging[];
 extern const char kEnableExtensionActivityLogTesting[];
 extern const char kEnableFastUnload[];
+extern const char kEnableIconNtp[];
+extern const char kEnableInvalidCertCollection[];
 extern const char kEnableIPv6[];
 extern const char kEnableLinkableEphemeralApps[];
 extern const char kEnableMaterialDesignSettings[];
@@ -301,6 +305,9 @@
 extern const char kSpeculativeResourcePrefetchingLearning[];
 #if defined(ENABLE_SPELLCHECK)
 extern const char kEnableSpellingAutoCorrect[];
+extern const char kEnableSpellingFeedbackFieldTrial[];
+extern const char kSpellingServiceFeedbackUrl[];
+extern const char kSpellingServiceFeedbackIntervalSeconds[];
 #endif
 extern const char kSSLVersionMax[];
 extern const char kSSLVersionMin[];
diff --git a/chrome/common/chrome_utility_messages.h b/chrome/common/chrome_utility_messages.h
index 80ea64c..52ff842 100644
--- a/chrome/common/chrome_utility_messages.h
+++ b/chrome/common/chrome_utility_messages.h
@@ -129,14 +129,16 @@
                      std::string /* JSON to parse */)
 
 // Tell the utility process to decode the given image data.
-IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_DecodeImage,
+IPC_MESSAGE_CONTROL3(ChromeUtilityMsg_DecodeImage,
                      std::vector<unsigned char> /* encoded image contents */,
-                     bool /* shrink image if needed for IPC msg limit */)
+                     bool /* shrink image if needed for IPC msg limit */,
+                     int /* delegate id */)
 
 // Tell the utility process to decode the given JPEG image data with a robust
 // libjpeg codec.
-IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_RobustJPEGDecodeImage,
-                     std::vector<unsigned char>)  // encoded image contents
+IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_RobustJPEGDecodeImage,
+                     std::vector<unsigned char> /* encoded image contents*/,
+                     int /* delegate id */)
 
 // Tell the utility process to patch the given |input_file| using |patch_file|
 // and place the output in |output_file|. The patch should use the bsdiff
@@ -234,11 +236,13 @@
                      std::string /* error_message, if any */)
 
 // Reply when the utility process has succeeded in decoding the image.
-IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_DecodeImage_Succeeded,
-                     SkBitmap)  // decoded image
+IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_DecodeImage_Succeeded,
+                     SkBitmap /* decoded image */,
+                     int /* delegate id */)
 
 // Reply when an error occurred decoding the image.
-IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_DecodeImage_Failed)
+IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_DecodeImage_Failed,
+                     int /* delegate id */)
 
 // Reply when a file has been patched.
 IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_PatchFile_Finished, int /* result */)
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 48638b4..a2aaef6 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -619,6 +619,18 @@
     "dependencies": ["permission:sessions"],
     "contexts": ["blessed_extension"]
   },
+  "settingsPrivate": [{
+    "dependencies": ["permission:settingsPrivate"],
+    "contexts": ["blessed_extension"]
+  }, {
+    "channel": "trunk",
+    "contexts": ["webui"],
+    "matches": [
+      "chrome://md-settings/*",
+      "chrome://settings/*",
+      "chrome://settings-frame/*"
+    ]
+  }],
   "signedInDevices": {
     "dependencies": ["permission:signedInDevices"],
     "contexts": ["blessed_extension"]
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 67750c6..5ae35649 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -494,8 +494,6 @@
     "channel": "stable",
     "extension_types": ["extension", "platform_app"],
     "whitelist": [
-      "8CE39F175D076AD6BDF7CBF418F2409558B367E3",  // hotword_helper
-      "09FDCB5851B8F3378DB630D06E316076E89C95A6",  // hotword nacl
       "62CCAAD339E6451BBF97C4BBDF758E934A05AD0B",  // hotword component
       "B6356EFF4047BC5F868C7D91868B6F5C01951A8A"   // hotword_audio_verification
     ]
@@ -862,6 +860,12 @@
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app"]
   },
+  "settingsPrivate": {
+    "channel": "trunk",
+    "extension_types": ["extension", "platform_app"],
+    "platforms": ["chromeos", "mac", "win", "linux"],
+    "whitelist": []
+  },
   "signedInDevices": {
     "channel": "dev",
     "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
diff --git a/chrome/common/extensions/api/appview_tag.idl b/chrome/common/extensions/api/appview_tag.idl
index 4bc62935..efa6b613 100644
--- a/chrome/common/extensions/api/appview_tag.idl
+++ b/chrome/common/extensions/api/appview_tag.idl
@@ -14,7 +14,7 @@
   // request.
   dictionary EmbedRequest {
     // The ID of the app that sent the embedding request.
-    string embedderId;
+    DOMString embedderId;
 
     // Optional developer specified data that the app to be embedded can use
     // when making an embedding decision.
diff --git a/chrome/common/extensions/api/chrome_web_view_internal.json b/chrome/common/extensions/api/chrome_web_view_internal.json
index 69729c5..33e282c 100644
--- a/chrome/common/extensions/api/chrome_web_view_internal.json
+++ b/chrome/common/extensions/api/chrome_web_view_internal.json
@@ -288,6 +288,25 @@
         "type": "function",
         "nodoc": true,
         "$ref": "contextMenusInternal.onClicked"
+      },
+      {
+        "name": "onShow",
+        "type": "function",
+        "description": "Fired when context menu is about to be shown. Provides the ability to cancel the context menu by calling <code>event.preventDefault()</code> from this handler.",
+        "nodoc": true,
+        "parameters": [
+          {
+            "name": "event",
+            "type": "object",
+            "properties": {
+              "preventDefault": {
+                "type": "function",
+                "parameters": [
+                ]
+              }
+            }
+          }
+        ]
       }
     ]
   }
diff --git a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
index 92124d2..260742fe0 100644
--- a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
+++ b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
@@ -11,8 +11,11 @@
 #include "base/values.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "extensions/common/error_utils.h"
+#include "extensions/common/install_warning.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_constants.h"
+#include "extensions/common/manifest_handlers/permissions_parser.h"
+#include "extensions/common/permissions/api_permission.h"
 #include "extensions/common/url_pattern.h"
 #include "url/url_constants.h"
 
@@ -115,11 +118,12 @@
 // static
 FileBrowserHandler::List*
 FileBrowserHandler::GetHandlers(const extensions::Extension* extension) {
-  FileBrowserHandlerInfo* info = static_cast<FileBrowserHandlerInfo*>(
+  FileBrowserHandlerInfo* const info = static_cast<FileBrowserHandlerInfo*>(
       extension->GetManifestData(keys::kFileBrowserHandlers));
-  if (info)
-    return &info->file_browser_handlers;
-  return NULL;
+  if (!info)
+    return nullptr;
+
+  return &info->file_browser_handlers;
 }
 
 FileBrowserHandlerParser::FileBrowserHandlerParser() {
@@ -128,6 +132,7 @@
 FileBrowserHandlerParser::~FileBrowserHandlerParser() {
 }
 
+#if defined(OS_CHROMEOS)
 namespace {
 
 FileBrowserHandler* LoadFileBrowserHandler(
@@ -265,25 +270,43 @@
 }
 
 }  // namespace
+#endif
 
 bool FileBrowserHandlerParser::Parse(extensions::Extension* extension,
                                      base::string16* error) {
-  const base::ListValue* file_browser_handlers_value = NULL;
-  if (!extension->manifest()->GetList(keys::kFileBrowserHandlers,
-                                      &file_browser_handlers_value)) {
+#if !defined(OS_CHROMEOS)
+  return true;
+#else
+  const base::Value* file_browser_handlers_value = nullptr;
+  if (!extension->manifest()->Get(keys::kFileBrowserHandlers,
+                                  &file_browser_handlers_value)) {
+    return true;
+  }
+
+  if (!extensions::PermissionsParser::HasAPIPermission(
+          extension, extensions::APIPermission::ID::kFileBrowserHandler)) {
+    extension->AddInstallWarning(extensions::InstallWarning(
+        errors::kInvalidFileBrowserHandlerMissingPermission));
+    return true;
+  }
+
+  const base::ListValue* file_browser_handlers_list_value = nullptr;
+  if (!file_browser_handlers_value->GetAsList(
+          &file_browser_handlers_list_value)) {
     *error = base::ASCIIToUTF16(errors::kInvalidFileBrowserHandler);
     return false;
   }
+
   scoped_ptr<FileBrowserHandlerInfo> info(new FileBrowserHandlerInfo);
   if (!LoadFileBrowserHandlers(extension->id(),
-                               file_browser_handlers_value,
-                               &info->file_browser_handlers,
-                               error)) {
+                               file_browser_handlers_list_value,
+                               &info->file_browser_handlers, error)) {
     return false;  // Failed to parse file browser actions definition.
   }
 
   extension->SetManifestData(keys::kFileBrowserHandlers, info.release());
   return true;
+#endif
 }
 
 const std::vector<std::string> FileBrowserHandlerParser::Keys() const {
diff --git a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc
index ba36d5e..18f6aba 100644
--- a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc
+++ b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc
@@ -24,50 +24,105 @@
 class FileBrowserHandlerManifestTest : public ChromeManifestTest {
 };
 
+#if !defined(OS_CHROMEOS)
+TEST_F(FileBrowserHandlerManifestTest, PermissionNotAllowedOnNonChromeOS) {
+  RunTestcase(
+      Testcase("filebrowser_valid.json",
+               "'fileBrowserHandler' is not allowed for specified platform."),
+      EXPECT_TYPE_WARNING);
+}
+#else
+
+TEST_F(FileBrowserHandlerManifestTest, PermissionAllowed) {
+  RunTestcase(Testcase("filebrowser_valid.json"), EXPECT_TYPE_SUCCESS);
+}
+
+TEST_F(FileBrowserHandlerManifestTest, GetHandlersRequiresPermission) {
+  extensions::DictionaryBuilder bad_manifest_builder;
+  bad_manifest_builder.Set("name", "Foo")
+      .Set("version", "1.0.0")
+      .Set("manifest_version", 2)
+      .Set("file_browser_handlers",
+           extensions::ListBuilder().Append(
+               extensions::DictionaryBuilder()
+                   .Set("id", "open")
+                   .Set("default_title", "open")
+                   .Set("file_filters", extensions::ListBuilder()
+                                            .Append("filesystem:*.txt")
+                                            .Append("filesystem:*.html"))));
+  scoped_ptr<base::DictionaryValue> bad_manifest_value(
+      bad_manifest_builder.Build());
+
+  // Create a good manifest by extending the bad one with the missing
+  // permission.
+  extensions::DictionaryBuilder good_manifest_builder(
+      *make_scoped_ptr(bad_manifest_value->DeepCopy()).get());
+  good_manifest_builder.Set(
+      "permissions", extensions::ListBuilder().Append("fileBrowserHandler"));
+
+  extensions::ExtensionBuilder bad_app_builder;
+  bad_app_builder.SetManifest(bad_manifest_value.Pass());
+  scoped_refptr<extensions::Extension> bad_app(bad_app_builder.Build());
+  EXPECT_FALSE(FileBrowserHandler::GetHandlers(bad_app.get()));
+
+  extensions::ExtensionBuilder good_app_builder;
+  good_app_builder.SetManifest(good_manifest_builder);
+  scoped_refptr<extensions::Extension> good_app(good_app_builder.Build());
+  EXPECT_TRUE(FileBrowserHandler::GetHandlers(good_app.get()));
+}
+
 TEST_F(FileBrowserHandlerManifestTest, InvalidFileBrowserHandlers) {
   Testcase testcases[] = {
-    Testcase("filebrowser_invalid_access_permission.json",
-             extensions::ErrorUtils::FormatErrorMessage(
-                 errors::kInvalidFileAccessValue, base::IntToString(1))),
-    Testcase("filebrowser_invalid_access_permission_list.json",
-             errors::kInvalidFileAccessList),
-    Testcase("filebrowser_invalid_empty_access_permission_list.json",
-             errors::kInvalidFileAccessList),
-    Testcase("filebrowser_invalid_actions_1.json",
-             errors::kInvalidFileBrowserHandler),
-    Testcase("filebrowser_invalid_actions_2.json",
-             errors::kInvalidFileBrowserHandler),
-    Testcase("filebrowser_invalid_action_id.json",
-             errors::kInvalidPageActionId),
-    Testcase("filebrowser_invalid_action_title.json",
-             errors::kInvalidPageActionDefaultTitle),
-    Testcase("filebrowser_invalid_file_filters_1.json",
-             errors::kInvalidFileFiltersList),
-    Testcase("filebrowser_invalid_file_filters_2.json",
-             extensions::ErrorUtils::FormatErrorMessage(
-                errors::kInvalidFileFilterValue, base::IntToString(0))),
-    Testcase("filebrowser_invalid_file_filters_url.json",
-             extensions::ErrorUtils::FormatErrorMessage(
-                errors::kInvalidURLPatternError, "http:*.html"))
-  };
+      Testcase("filebrowser_invalid_access_permission.json",
+               extensions::ErrorUtils::FormatErrorMessage(
+                   errors::kInvalidFileAccessValue, base::IntToString(1))),
+      Testcase("filebrowser_invalid_access_permission_list.json",
+               errors::kInvalidFileAccessList),
+      Testcase("filebrowser_invalid_empty_access_permission_list.json",
+               errors::kInvalidFileAccessList),
+      Testcase("filebrowser_invalid_value.json",
+               errors::kInvalidFileBrowserHandler),
+      Testcase("filebrowser_invalid_actions_1.json",
+               errors::kInvalidFileBrowserHandler),
+      Testcase("filebrowser_invalid_actions_2.json",
+               errors::kInvalidFileBrowserHandler),
+      Testcase("filebrowser_invalid_action_id.json",
+               errors::kInvalidPageActionId),
+      Testcase("filebrowser_invalid_action_title.json",
+               errors::kInvalidPageActionDefaultTitle),
+      Testcase("filebrowser_invalid_file_filters_1.json",
+               errors::kInvalidFileFiltersList),
+      Testcase("filebrowser_invalid_file_filters_2.json",
+               extensions::ErrorUtils::FormatErrorMessage(
+                   errors::kInvalidFileFilterValue, base::IntToString(0))),
+      Testcase("filebrowser_invalid_file_filters_url.json",
+               extensions::ErrorUtils::FormatErrorMessage(
+                   errors::kInvalidURLPatternError, "http:*.html"))};
   RunTestcases(testcases, arraysize(testcases), EXPECT_TYPE_ERROR);
+  RunTestcase(Testcase("filebrowser_missing_permission.json",
+                       errors::kInvalidFileBrowserHandlerMissingPermission),
+              EXPECT_TYPE_WARNING);
 }
 
 TEST_F(FileBrowserHandlerManifestTest, ValidFileBrowserHandler) {
   scoped_refptr<const Extension> extension =
       ExtensionBuilder()
-      .SetManifest(DictionaryBuilder()
+          .SetManifest(
+               DictionaryBuilder()
                    .Set("name", "file browser handler test")
                    .Set("version", "1.0.0")
                    .Set("manifest_version", 2)
-                   .Set("file_browser_handlers", ListBuilder()
-                       .Append(DictionaryBuilder()
-                           .Set("id", "ExtremelyCoolAction")
-                           .Set("default_title", "Be Amazed")
-                           .Set("default_icon", "icon.png")
-                           .Set("file_filters", ListBuilder()
-                               .Append("filesystem:*.txt")))))
-      .Build();
+                   .Set("permissions",
+                        extensions::ListBuilder().Append("fileBrowserHandler"))
+                   .Set("file_browser_handlers",
+                        ListBuilder().Append(
+                            DictionaryBuilder()
+                                .Set("id", "ExtremelyCoolAction")
+                                .Set("default_title", "Be Amazed")
+                                .Set("default_icon", "icon.png")
+                                .Set("file_filters", ListBuilder().Append(
+                                                         "filesystem:*.txt")))))
+          .Build();
 
   ASSERT_TRUE(extension.get());
   FileBrowserHandler::List* handlers =
@@ -91,19 +146,23 @@
 TEST_F(FileBrowserHandlerManifestTest, ValidFileBrowserHandlerMIMETypes) {
   scoped_refptr<const Extension> extension =
       ExtensionBuilder()
-      .SetID(extension_misc::kQuickOfficeExtensionId)
-      .SetManifest(DictionaryBuilder()
+          .SetID(extension_misc::kQuickOfficeExtensionId)
+          .SetManifest(
+               DictionaryBuilder()
                    .Set("name", "file browser handler test")
                    .Set("version", "1.0.0")
                    .Set("manifest_version", 2)
-                   .Set("file_browser_handlers", ListBuilder()
-                       .Append(DictionaryBuilder()
-                           .Set("id", "ID")
-                           .Set("default_title", "Default title")
-                           .Set("default_icon", "icon.png")
-                           .Set("file_filters", ListBuilder()
-                               .Append("filesystem:*.txt")))))
-      .Build();
+                   .Set("permissions",
+                        extensions::ListBuilder().Append("fileBrowserHandler"))
+                   .Set("file_browser_handlers",
+                        ListBuilder().Append(
+                            DictionaryBuilder()
+                                .Set("id", "ID")
+                                .Set("default_title", "Default title")
+                                .Set("default_icon", "icon.png")
+                                .Set("file_filters", ListBuilder().Append(
+                                                         "filesystem:*.txt")))))
+          .Build();
 
   ASSERT_TRUE(extension.get());
   FileBrowserHandler::List* handlers =
@@ -121,20 +180,24 @@
 TEST_F(FileBrowserHandlerManifestTest, ValidFileBrowserHandlerWithCreate) {
   scoped_refptr<const Extension> extension =
       ExtensionBuilder()
-      .SetManifest(DictionaryBuilder()
+          .SetManifest(
+               DictionaryBuilder()
                    .Set("name", "file browser handler test create")
                    .Set("version", "1.0.0")
                    .Set("manifest_version", 2)
-                   .Set("file_browser_handlers", ListBuilder()
-                       .Append(DictionaryBuilder()
-                           .Set("id", "ID")
-                           .Set("default_title", "Default title")
-                           .Set("default_icon", "icon.png")
-                           .Set("file_filters", ListBuilder()
-                               .Append("filesystem:*.txt"))
-                           .Set("file_access", ListBuilder()
-                               .Append("create")))))
-      .Build();
+                   .Set("permissions",
+                        extensions::ListBuilder().Append("fileBrowserHandler"))
+                   .Set("file_browser_handlers",
+                        ListBuilder().Append(
+                            DictionaryBuilder()
+                                .Set("id", "ID")
+                                .Set("default_title", "Default title")
+                                .Set("default_icon", "icon.png")
+                                .Set("file_filters",
+                                     ListBuilder().Append("filesystem:*.txt"))
+                                .Set("file_access",
+                                     ListBuilder().Append("create")))))
+          .Build();
 
   ASSERT_TRUE(extension.get());
   FileBrowserHandler::List* handlers =
@@ -149,5 +212,6 @@
   EXPECT_FALSE(action->CanRead());
   EXPECT_FALSE(action->CanWrite());
 }
+#endif
 
 }  // namespace
diff --git a/chrome/common/extensions/api/platform_keys_internal.idl b/chrome/common/extensions/api/platform_keys_internal.idl
index d3338b4f..48719e2c 100644
--- a/chrome/common/extensions/api/platform_keys_internal.idl
+++ b/chrome/common/extensions/api/platform_keys_internal.idl
@@ -4,7 +4,7 @@
 
 // Internal API for to implement the platformKeys and enterprise.platformKeys
 // APIs.
-[ implemented_in = "chrome/browser/extensions/api/platform_keys/platform_keys_api.h" ]
+[implemented_in = "chrome/browser/extensions/api/platform_keys/platform_keys_api.h"]
 namespace platformKeysInternal {
   callback SelectCallback = void (platformKeys.Match[] certs);
 
@@ -48,9 +48,14 @@
                      ArrayBuffer data,
                      SignCallback callback);
 
-    // Calls back <code>callback</code> with details about the key certified by
-    // <code>certificate</code>.
+    // Checks whether <code>certificate</code> certifies a key that allows usage
+    // of the WebCrypto algorithm <code>algorithmName</code>. If so, calls back
+    // <code>callback</code> with the key info and a WebCrypto
+    // <code>KeyAlgorithm</code> dictionary describing the key's algorithm. The
+    // <code>name</code> property will equal <code>algorithmName</code>.
+    // Otherwise, calls back with an error.
     static void getPublicKey(ArrayBuffer certificate,
+                             DOMString algorithmName,
                              GetPublicKeyCallback callback);
   };
 };
diff --git a/chrome/common/extensions/api/schemas.gypi b/chrome/common/extensions/api/schemas.gypi
index 8bada51..624c3f9 100644
--- a/chrome/common/extensions/api/schemas.gypi
+++ b/chrome/common/extensions/api/schemas.gypi
@@ -68,6 +68,7 @@
       'reading_list_private.json',
       'screenlock_private.idl',
       'sessions.json',
+      'settings_private.idl',
       'signed_in_devices.idl',
       'streams_private.idl',
       'sync_file_system.idl',
diff --git a/chrome/common/extensions/api/settings_private.idl b/chrome/common/extensions/api/settings_private.idl
new file mode 100644
index 0000000..80f2d0b
--- /dev/null
+++ b/chrome/common/extensions/api/settings_private.idl
@@ -0,0 +1,70 @@
+// Copyright (c) 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.
+
+// Use the <code>chrome.settingsPrivate</code> API to get or set preferences
+// from the settings UI.
+namespace settingsPrivate {
+  // Type of a pref.
+  enum PrefType { BOOLEAN, NUMBER, STRING, URL };
+
+  // Source of a pref (device, user).
+  enum PrefSource { DEVICE, USER };
+
+  dictionary PrefObject {
+    // The key for the pref.
+    DOMString key;
+
+    // The type of the pref (e.g., boolean, string, etc.).
+    PrefType type;
+
+    // The current value of the pref.
+    any value;
+
+    // The source of the pref (e.g., device, user).
+    PrefSource source;
+  };
+
+  callback OnPrefSetCallback = void (boolean success);
+  callback GetAllPrefsCallback = void (PrefObject[] prefs);
+  callback GetPrefCallback = void (PrefObject pref);
+
+  interface Functions {
+    // Sets a boolean settings value.
+    // |name|: The name of the pref.
+    // |value|: The new value of the pref.
+    static void setBooleanPref(DOMString name, boolean value,
+        optional OnPrefSetCallback callback);
+
+    // Sets a number settings value.
+    // |name|: The name of the pref.
+    // |value|: The new value of the pref.
+    static void setNumericPref(DOMString name, double value,
+        optional OnPrefSetCallback callback);
+
+    // Sets a string settings value.
+    // |name|: The name of the pref.
+    // |value|: The new value of the pref.
+    static void setStringPref(DOMString name, DOMString value,
+        optional OnPrefSetCallback callback);
+
+    // Sets a URL settings value.
+    // |name|: The name of the pref.
+    // |value|: The new value of the pref.
+    static void setURLPref(DOMString name, DOMString value,
+        optional OnPrefSetCallback callback);
+
+    // Gets an array of all the prefs.
+    static void getAllPrefs(GetAllPrefsCallback callback);
+
+    // Gets the value of a specific pref.
+    static void getPref(DOMString name, GetPrefCallback callback);
+  };
+
+  interface Events {
+    // Fired when a set of prefs has changed.
+    //
+    // |callback|: Callback fired with a list of prefs that changed.
+    static void onPrefsChanged(GetAllPrefsCallback callback);
+  };
+};
diff --git a/chrome/common/extensions/api/webview_tag.json b/chrome/common/extensions/api/webview_tag.json
index 80eee0d..53f69c6e 100644
--- a/chrome/common/extensions/api/webview_tag.json
+++ b/chrome/common/extensions/api/webview_tag.json
@@ -251,6 +251,21 @@
         ]
       },
       {
+        "id": "FullscreenPermissionRequest",
+        "type": "object",
+        "description": "The type of <code>request</code> object which accompanies a <code>fullscreen</code> <a href=\"#event-permissionrequest\">permissionrequest</a> DOM event.<p>",
+        "properties": {
+          "origin": {
+            "description": "The origin of the frame inside the <code>webview</code> that initiated the fullscreen request.",
+            "type": "string"
+          }
+        },
+        "functions": [
+          { "name": "allow", "description": "Allow the permission request." },
+          { "name": "deny", "description": "Deny the permission request." }
+        ]
+      },
+      {
         "id": "LoadPluginPermissionRequest",
         "type": "object",
         "description": "The type of <code>request</code> object which accompanies a <code>loadplugin</code> <a href=\"#event-permissionrequest\">permissionrequest</a> DOM event.<p>",
@@ -829,7 +844,7 @@
             "name": "permission",
             "description": "The type of permission being requested.",
             "type": "string",
-            "enum": ["media", "geolocation", "pointerLock", "download", "loadplugin", "filesystem"]
+            "enum": ["media", "geolocation", "pointerLock", "download", "loadplugin", "filesystem", "fullscreen"]
           },
           {
             "name": "requestId",
@@ -840,7 +855,7 @@
             "name": "request",
             "type": "object",
             "properties": {},
-            "description": "An object which holds details of the requested permission. Depending on the type of permission requested, this may be a $(ref:webviewTag.MediaPermissionRequest), $(ref:webviewTag.GeolocationPermissionRequest), $(ref:webviewTag.PointerLockPermissionRequest), $(ref:webviewTag.DownloadPermissionRequest), or $(ref:webviewTag.LoadPluginPermissionRequest)."
+            "description": "An object which holds details of the requested permission. Depending on the type of permission requested, this may be a $(ref:webviewTag.MediaPermissionRequest), $(ref:webviewTag.GeolocationPermissionRequest), $(ref:webviewTag.PointerLockPermissionRequest), $(ref:webviewTag.DownloadPermissionRequest), $(ref:webviewTag.LoadPluginPermissionRequest), or $(ref:webviewTag.FullscreenPermissionRequest)."
           }
         ]
       },
diff --git a/chrome/common/extensions/chrome_utility_extensions_messages.h b/chrome/common/extensions/chrome_utility_extensions_messages.h
index 2d9c8f42..6613e478 100644
--- a/chrome/common/extensions/chrome_utility_extensions_messages.h
+++ b/chrome/common/extensions/chrome_utility_extensions_messages.h
@@ -79,11 +79,6 @@
                      base::FilePath /* zip_file */,
                      base::FilePath /* dir */)
 
-// Tell the utility process to decode the given image data, which is base64
-// encoded.
-IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_DecodeImageBase64,
-                     std::string)  // base64 encoded image contents
-
 #if defined(OS_WIN)
 // Tell the utility process to parse the iTunes preference XML file contents
 // and return the path to the iTunes directory.
diff --git a/chrome/common/extensions/docs/server2/api_data_source.py b/chrome/common/extensions/docs/server2/api_data_source.py
index c28984c..98408a3e 100644
--- a/chrome/common/extensions/docs/server2/api_data_source.py
+++ b/chrome/common/extensions/docs/server2/api_data_source.py
@@ -11,6 +11,7 @@
 from future import Future, All
 from jsc_view import CreateJSCView, GetEventByNameFromEvents
 from platform_util import GetPlatforms
+from third_party.json_schema_compiler.model import UnixName
 
 
 class APIDataSource(DataSource):
@@ -87,7 +88,7 @@
   def GetRefreshPaths(self):
     tasks = []
     for platform in GetPlatforms():
-      tasks += ['%s/%s' % (platform, api)
+      tasks += ['%s/%s' % (platform, UnixName(api))
                 for api in
                     self._platform_bundle.GetAPIModels(platform).GetNames()]
     return tasks
diff --git a/chrome/common/extensions/docs/server2/app.yaml b/chrome/common/extensions/docs/server2/app.yaml
index ac922a7..451eb3e 100644
--- a/chrome/common/extensions/docs/server2/app.yaml
+++ b/chrome/common/extensions/docs/server2/app.yaml
@@ -1,5 +1,5 @@
 application: chrome-apps-doc
-version: 3-48-0
+version: 3-48-2
 runtime: python27
 api_version: 1
 threadsafe: false
diff --git a/chrome/common/extensions/docs/server2/cron.yaml b/chrome/common/extensions/docs/server2/cron.yaml
index 7c8d833..504c11b 100644
--- a/chrome/common/extensions/docs/server2/cron.yaml
+++ b/chrome/common/extensions/docs/server2/cron.yaml
@@ -2,4 +2,4 @@
 - description: Repopulates all cached data.
   url: /_cron
   schedule: every 180 minutes
-  target: 3-48-0
+  target: 3-48-2
diff --git a/chrome/common/extensions/docs/server2/queue.yaml b/chrome/common/extensions/docs/server2/queue.yaml
index fc80220c..3a95d696 100644
--- a/chrome/common/extensions/docs/server2/queue.yaml
+++ b/chrome/common/extensions/docs/server2/queue.yaml
@@ -1,5 +1,5 @@
 queue:
   - name: default
-    rate: 7/s
-    bucket_size: 7
-    max_concurrent_requests: 7
+    rate: 4/s
+    bucket_size: 4
+    max_concurrent_requests: 4
diff --git a/chrome/common/extensions/docs/templates/articles/nativeMessaging.html b/chrome/common/extensions/docs/templates/articles/nativeMessaging.html
index 4ff66deb..29c2291a 100644
--- a/chrome/common/extensions/docs/templates/articles/nativeMessaging.html
+++ b/chrome/common/extensions/docs/templates/articles/nativeMessaging.html
@@ -210,7 +210,7 @@
     <li>
       Is the manifest file in the correct format? In particular, is the JSON
       syntax correct and do the values match the definition of a
-      <a href="native-messaging-host-manifest">native messaging host manifest</a>?
+      <a href="#native-messaging-host-manifest">native messaging host manifest</a>?
     <li>
       Does the file specified in <code>path</code> exist? On Windows, paths
       may be relative, but on OS X and Linux, the paths must be absolute.
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index 6828801..23a88df 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -44,7 +44,6 @@
 const char kCroshBuiltinAppId[] = "nkoccljplnhpfnfiajclkommnmllphnl";
 const char kHotwordAudioVerificationAppId[] =
     "abjokfonkihficiokmkfboogholifghn";
-const char kHotwordExtensionId[] = "bepbmhgboaologfdajaanbcjmnhjmhfn";
 const char kHotwordNewExtensionId[] = "nbpagnldghgfoolbancepceaanlmhfmd";
 const char kHotwordSharedModuleId[] = "lccekmodgklaepjeofjdjpbminllajkg";
 const char kSettingsAppId[] = "ennkphjdgehloodpbhlhldgbnhmacadg";
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index 699ce8df..e97794ab 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -76,9 +76,6 @@
 // The extension id of the hotword audio verification dialogue app.
 extern const char kHotwordAudioVerificationAppId[];
 
-// The extension id of the hotword voice search trigger extension.
-extern const char kHotwordExtensionId[];
-
 // The extension id of the new (experimental) hotword extension.
 extern const char kHotwordNewExtensionId[];
 
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
index 2a8b42c4..29c5936 100644
--- a/chrome/common/extensions/permissions/chrome_api_permissions.cc
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -333,6 +333,11 @@
        "firstRunPrivate",
        APIPermissionInfo::kFlagCannotBeOptional},
       {APIPermission::kInlineInstallPrivate, "inlineInstallPrivate"},
+      {APIPermission::kSettingsPrivate,
+       "settingsPrivate",
+       APIPermissionInfo::kFlagCannotBeOptional,
+       IDS_EXTENSION_PROMPT_WARNING_SETTINGS_PRIVATE,
+       PermissionMessage::kSettingsPrivate},
 
       // Full url access permissions.
       {APIPermission::kDebugger,
diff --git a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
index 8cfe9e7..da8d65a 100644
--- a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
+++ b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
@@ -454,9 +454,7 @@
       {IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT,
        {APIPermission::kManagement},
        {}},
-      {IDS_EXTENSION_PROMPT_WARNING_MDNS,
-       {APIPermission::kMDns},
-       {}},
+      {IDS_EXTENSION_PROMPT_WARNING_MDNS, {APIPermission::kMDns}, {}},
       {IDS_EXTENSION_PROMPT_WARNING_NATIVE_MESSAGING,
        {APIPermission::kNativeMessaging},
        {}},
@@ -496,6 +494,9 @@
       {IDS_EXTENSION_PROMPT_WARNING_MUSIC_MANAGER_PRIVATE,
        {APIPermission::kMusicManagerPrivate},
        {}},
+      {IDS_EXTENSION_PROMPT_WARNING_SETTINGS_PRIVATE,
+       {APIPermission::kSettingsPrivate},
+       {}},
 
       // Platform-app permission messages.
 
diff --git a/chrome/common/localized_error.cc b/chrome/common/localized_error.cc
index 8ee9b36..cd7e5ef0 100644
--- a/chrome/common/localized_error.cc
+++ b/chrome/common/localized_error.cc
@@ -28,6 +28,11 @@
 #include "base/win/windows_version.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "base/command_line.h"
+#include "chrome/common/chrome_switches.h"
+#endif
+
 #if defined(ENABLE_EXTENSIONS)
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_icon_set.h"
@@ -568,6 +573,18 @@
       error_code == chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET) {
     error_strings->SetString("primaryParagraph",
         l10n_util::GetStringUTF16(options.summary_resource_id));
+
+#if defined(OS_CHROMEOS)
+    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+    // Check if easter egg should be disabled.
+    if (command_line->HasSwitch(switches::kDisableDinosaurEasterEgg)) {
+      // The prescence of this string disables the easter egg. Acts as a flag.
+      error_strings->SetString("disabledEasterEgg",
+          l10n_util::GetStringUTF16(IDS_ERRORPAGE_FUN_DISABLED));
+    }
+#endif
+
   } else {
     // Set summary message in the details.
     summary->SetString("msg",
diff --git a/chrome/common/pepper_flash.cc b/chrome/common/pepper_flash.cc
index b21d4d12..da1c00d 100644
--- a/chrome/common/pepper_flash.cc
+++ b/chrome/common/pepper_flash.cc
@@ -117,14 +117,10 @@
   if (os != kPepperFlashOperatingSystem)
     return false;
 
-// On Win64, PepperFlash manifests have "ia32" instead of "x64" so skip the
-// architecture check. TODO(wfh): remove this when crbug.com/458894 is fixed.
-#if !defined(OS_WIN) || !defined(ARCH_CPU_X86_64)
   std::string arch;
   manifest.GetStringASCII("x-ppapi-arch", &arch);
   if (arch != kPepperFlashArch)
     return false;
-#endif
 
   *version_out = version;
   return true;
diff --git a/chrome/common/ppapi_utils.cc b/chrome/common/ppapi_utils.cc
index 0b7f1631..0baad10a 100644
--- a/chrome/common/ppapi_utils.cc
+++ b/chrome/common/ppapi_utils.cc
@@ -17,7 +17,6 @@
 #include "ppapi/c/dev/ppb_gles_chromium_texture_mapping_dev.h"
 #include "ppapi/c/dev/ppb_ime_input_event_dev.h"
 #include "ppapi/c/dev/ppb_memory_dev.h"
-#include "ppapi/c/dev/ppb_messaging_deprecated.h"
 #include "ppapi/c/dev/ppb_opengles2ext_dev.h"
 #include "ppapi/c/dev/ppb_printing_dev.h"
 #include "ppapi/c/dev/ppb_scrollbar_dev.h"
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index fca754c..ae6f5c4 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2271,16 +2271,9 @@
 // their profile's Easy Unlock preferences.
 const char kEasyUnlockLocalStateUserPrefs[] = "easy_unlock.user_prefs";
 
-// The beginning of time span when we count user's "Nope" for the password
-// bubble.
-const char kPasswordBubbleTimeStamp[] = "password_bubble.timestamp";
-
 // The count of user's "Nope" for the password bubble.
 const char kPasswordBubbleNopesCount[] = "password_bubble.nopes";
 
-// Last user's interaction with the password bubble.
-const char kPasswordBubbleLastInteractions[] = "password_bubble.interactions";
-
 // Boolean that indicates whether elevation is needed to recover Chrome upgrade.
 const char kRecoveryComponentNeedsElevation[] =
     "recovery_component.needs_elevation";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 442f2080..d304380 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -816,9 +816,7 @@
 extern const char kEasyUnlockLocalStateTpmKeys[];
 extern const char kEasyUnlockLocalStateUserPrefs[];
 
-extern const char kPasswordBubbleTimeStamp[];
 extern const char kPasswordBubbleNopesCount[];
-extern const char kPasswordBubbleLastInteractions[];
 
 extern const char kRecoveryComponentNeedsElevation[];
 
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index ab5c73d..f75359e 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -476,13 +476,6 @@
 #endif
 
 #if defined(ENABLE_PLUGIN_INSTALLATION)
-// Tells the browser to search for a plugin that can handle the given MIME
-// type. The result will be sent asynchronously to the routing ID
-// |placeholder_id|.
-IPC_MESSAGE_ROUTED2(ChromeViewHostMsg_FindMissingPlugin,
-                    int /* placeholder_id */,
-                    std::string /* mime_type */)
-
 // Notifies the browser that a missing plugin placeholder has been removed, so
 // the corresponding PluginPlaceholderHost can be deleted.
 IPC_MESSAGE_ROUTED1(ChromeViewHostMsg_RemovePluginPlaceholderHost,
diff --git a/chrome/common/safe_browsing/csd.proto b/chrome/common/safe_browsing/csd.proto
index ae0e7b9..e5c4e27 100644
--- a/chrome/common/safe_browsing/csd.proto
+++ b/chrome/common/safe_browsing/csd.proto
@@ -383,7 +383,7 @@
       optional string variations_seed_signature = 1;
     }
     message ScriptRequestIncident {
-      optional string script_digest = 1;
+      optional bytes script_digest = 1;
       optional string inclusion_origin = 2;
     }
     optional int64 incident_time_msec = 1;
diff --git a/chrome/common/safe_browsing/zip_analyzer.cc b/chrome/common/safe_browsing/zip_analyzer.cc
index 6f1d5e2..718cc28d 100644
--- a/chrome/common/safe_browsing/zip_analyzer.cc
+++ b/chrome/common/safe_browsing/zip_analyzer.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/common/safe_browsing/zip_analyzer.h"
 
+#include "base/i18n/streaming_utf8_validator.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
@@ -58,7 +59,9 @@
     zip::ZipReader* reader,
     base::File* temp_file,
     ClientDownloadRequest_ArchivedBinary* archived_binary) {
-  archived_binary->set_file_basename(file_path.BaseName().AsUTF8Unsafe());
+  std::string file_basename(file_path.BaseName().AsUTF8Unsafe());
+  if (base::StreamingUtf8Validator::Validate(file_basename))
+    archived_binary->set_file_basename(file_basename);
   archived_binary->set_download_type(
       download_protection_util::GetDownloadType(file_path));
   archived_binary->set_length(reader->current_entry_info()->original_size());
diff --git a/chrome/common/spellcheck_messages.h b/chrome/common/spellcheck_messages.h
index 6d415d1..c8b02fb0 100644
--- a/chrome/common/spellcheck_messages.h
+++ b/chrome/common/spellcheck_messages.h
@@ -55,6 +55,15 @@
 IPC_MESSAGE_CONTROL1(SpellCheckMsg_EnableAutoSpellCorrect,
                      bool /* enable */)
 
+// Request a list of all document markers in the renderer for spelling service
+// feedback.
+IPC_MESSAGE_CONTROL0(SpellCheckMsg_RequestDocumentMarkers)
+
+// Send a list of document markers in the renderer to the spelling service
+// feedback sender.
+IPC_MESSAGE_CONTROL1(SpellCheckHostMsg_RespondDocumentMarkers,
+                     std::vector<uint32> /* document marker identifiers */)
+
 #if !defined(OS_MACOSX)
 // Sends text-check results from the Spelling service when the service finishes
 // checking text received by a SpellCheckHostMsg_CallSpellingService message.
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 42557859..acd1678 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -255,10 +255,6 @@
 extern const char kChromeUIPrintHost[];
 #endif  // ENABLE_PRINT_PREVIEW
 
-#if defined(OS_ANDROID)
-extern const char kChromeUIWelcomeHost[];
-#endif
-
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
 extern const char kChromeUILinuxProxyConfigHost[];
 extern const char kChromeUISandboxHost[];
diff --git a/chrome/installer/mini_installer.gyp b/chrome/installer/mini_installer.gyp
index 9c3716a..fdbd85d0 100644
--- a/chrome/installer/mini_installer.gyp
+++ b/chrome/installer/mini_installer.gyp
@@ -134,6 +134,7 @@
                   'BasicRuntimeChecks': '0',
                   'BufferSecurityCheck': 'false',
                   'ExceptionHandling': '0',
+                  'WholeProgramOptimization': 'false',
                 },
                 'VCLinkerTool': {
                   'SubSystem': '2',     # Set /SUBSYSTEM:WINDOWS
diff --git a/chrome/installer/mini_installer.gypi b/chrome/installer/mini_installer.gypi
index 57eaf03..b1e5a2b 100644
--- a/chrome/installer/mini_installer.gypi
+++ b/chrome/installer/mini_installer.gypi
@@ -223,15 +223,15 @@
       'message': 'Create installer archive',
     },
   ],
-  # TODO(mark):  <(branding_dir) should be defined by the
-  # global condition block at the bottom of the file, but
-  # this doesn't work due to the following issue:
-  #
-  #   http://code.google.com/p/gyp/issues/detail?id=22
-  #
-  # Remove this block once the above issue is fixed.
   'conditions': [
-    [ 'branding == "Chrome"', {
+    # TODO(mark):  <(branding_dir) should be defined by the
+    # global condition block at the bottom of the file, but
+    # this doesn't work due to the following issue:
+    #
+    #   http://code.google.com/p/gyp/issues/detail?id=22
+    #
+    # Remove this block once the above issue is fixed.
+    ['branding == "Chrome"', {
       'variables': {
          'branding_dir': '../app/theme/google_chrome',
       },
@@ -240,5 +240,9 @@
          'branding_dir': '../app/theme/chromium',
       },
     }],
+    ['OS=="win" and buildtype=="Official"', {
+      # Optimize for size when doing an official build.
+      'optimize' :'size',
+    }],
   ],
 }
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index e4ea3df..be1dcf7fc 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -890,9 +890,9 @@
           break;
         }
 
-        scoped_ptr<content::PluginInstanceThrottler> throttler =
-            PluginInstanceThrottler::Create(power_saver_enabled);
+        scoped_ptr<content::PluginInstanceThrottler> throttler;
         if (power_saver_enabled) {
+          throttler = PluginInstanceThrottler::Create();
           // PluginPreroller manages its own lifetime.
           new PluginPreroller(
               render_frame, frame, params, info, identifier, group_name,
diff --git a/chrome/renderer/media/chrome_key_systems.cc b/chrome/renderer/media/chrome_key_systems.cc
index 5307481..1b9f424 100644
--- a/chrome/renderer/media/chrome_key_systems.cc
+++ b/chrome/renderer/media/chrome_key_systems.cc
@@ -75,22 +75,22 @@
   KeySystemInfo info;
   info.key_system = kExternalClearKeyKeySystem;
 
-  info.supported_codecs = media::EME_CODEC_WEBM_ALL;
   info.supported_init_data_types =
-      media::EME_INIT_DATA_TYPE_WEBM | media::EME_INIT_DATA_TYPE_KEYIDS;
+    media::kInitDataTypeMaskWebM | media::kInitDataTypeMaskKeyIds;
+  info.supported_codecs = media::EME_CODEC_WEBM_ALL;
 #if defined(USE_PROPRIETARY_CODECS)
+  info.supported_init_data_types |= media::kInitDataTypeMaskCenc;
   info.supported_codecs |= media::EME_CODEC_MP4_ALL;
-  info.supported_init_data_types |= media::EME_INIT_DATA_TYPE_CENC;
 #endif  // defined(USE_PROPRIETARY_CODECS)
 
+  info.max_audio_robustness = media::EmeRobustness::EMPTY;
+  info.max_video_robustness = media::EmeRobustness::EMPTY;
+
   // Persistent sessions are faked.
   info.persistent_license_support = media::EME_SESSION_TYPE_SUPPORTED;
   info.persistent_release_message_support =
       media::EME_SESSION_TYPE_NOT_SUPPORTED;
-  // TODO(sandersd): Using ALWAYS_ENABLED prevents "not-allowed" from
-  // succeeding. Change this to REQUESTABLE once the state can be blocked.
-  // http://crbug.com/457482
-  info.persistent_state_support = media::EME_FEATURE_ALWAYS_ENABLED;
+  info.persistent_state_support = media::EME_FEATURE_REQUESTABLE;
   info.distinctive_identifier_support = media::EME_FEATURE_NOT_SUPPORTED;
 
   info.pepper_type = kExternalClearKeyPepperType;
@@ -193,24 +193,21 @@
   cdm::AddWidevineWithCodecs(
       cdm::WIDEVINE, supported_codecs,
 #if defined(OS_CHROMEOS)
-      // Persistent licenses are supported if remote attestation succeeds.
-      media::EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION,
-      media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent release message.
-      // TODO(sandersd): Using ALWAYS_ENABLED prevents "not-allowed" from
-      // succeeding. Change this to REQUESTABLE once the state can be blocked.
-      // http://crbug.com/457482
-      media::EME_FEATURE_ALWAYS_ENABLED,  // Persistent state.
-      // A distinctive identifier will be available if remote attestation
-      // succeeds.
-      media::EME_FEATURE_REQUESTABLE_WITH_PERMISSION,
+      media::EmeRobustness::HW_SECURE_ALL,     // Maximum audio robustness.
+      media::EmeRobustness::HW_SECURE_ALL,     // Maximim video robustness.
+      // persistent-license.
+      media::EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER,
+      media::EME_SESSION_TYPE_NOT_SUPPORTED,   // persistent-release-message.
+      media::EME_FEATURE_REQUESTABLE,          // Persistent state.
+      // Distinctive identifier.
+      media::EME_FEATURE_REQUESTABLE_WITH_IDENTIFIER,
 #else   // (Desktop)
-      media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent license.
-      media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent release message.
-      // TODO(sandersd): Using ALWAYS_ENABLED prevents "not-allowed" from
-      // succeeding. Change this to REQUESTABLE once the state can be blocked.
-      // http://crbug.com/457482
-      media::EME_FEATURE_ALWAYS_ENABLED,  // Persistent state.
-      media::EME_FEATURE_NOT_SUPPORTED,   // Distinctive identifier.
+      media::EmeRobustness::SW_SECURE_CRYPTO,  // Maximum audio robustness.
+      media::EmeRobustness::SW_SECURE_DECODE,  // Maximum video robustness.
+      media::EME_SESSION_TYPE_NOT_SUPPORTED,   // persistent-license.
+      media::EME_SESSION_TYPE_NOT_SUPPORTED,   // persistent-release-message.
+      media::EME_FEATURE_REQUESTABLE,          // Persistent state.
+      media::EME_FEATURE_NOT_SUPPORTED,        // Distinctive identifier.
 #endif  // defined(OS_CHROMEOS)
       concrete_key_systems);
 }
diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.cc b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
index 6bb6bd2e..c1788a3 100644
--- a/chrome/renderer/plugins/chrome_plugin_placeholder.cc
+++ b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
@@ -98,12 +98,8 @@
           IDR_BLOCKED_PLUGIN_HTML));
 
   base::DictionaryValue values;
-#if defined(ENABLE_PLUGIN_INSTALLATION)
-  values.SetString("message", l10n_util::GetStringUTF8(IDS_PLUGIN_SEARCHING));
-#else
   values.SetString("message",
                    l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED));
-#endif
 
   std::string html_data = webui::GetI18nTemplateHtml(template_html, &values);
 
@@ -111,12 +107,6 @@
   ChromePluginPlaceholder* missing_plugin = new ChromePluginPlaceholder(
       render_frame, frame, params, html_data, params.mimeType);
   missing_plugin->set_allow_loading(true);
-#if defined(ENABLE_PLUGIN_INSTALLATION)
-  RenderThread::Get()->Send(
-      new ChromeViewHostMsg_FindMissingPlugin(missing_plugin->routing_id(),
-                                              missing_plugin->CreateRoutingId(),
-                                              params.mimeType.utf8()));
-#endif
   return missing_plugin;
 }
 
diff --git a/chrome/renderer/resources/default_100_percent/offline/100-disabled.png b/chrome/renderer/resources/default_100_percent/offline/100-disabled.png
new file mode 100644
index 0000000..526075e6
--- /dev/null
+++ b/chrome/renderer/resources/default_100_percent/offline/100-disabled.png
Binary files differ
diff --git a/chrome/renderer/resources/default_200_percent/offline/200-disabled.png b/chrome/renderer/resources/default_200_percent/offline/200-disabled.png
new file mode 100644
index 0000000..ea2b90e
--- /dev/null
+++ b/chrome/renderer/resources/default_200_percent/offline/200-disabled.png
Binary files differ
diff --git a/chrome/renderer/resources/extensions/platform_keys/get_public_key.js b/chrome/renderer/resources/extensions/platform_keys/get_public_key.js
index d88fc608..af4a858 100644
--- a/chrome/renderer/resources/extensions/platform_keys/get_public_key.js
+++ b/chrome/renderer/resources/extensions/platform_keys/get_public_key.js
@@ -53,14 +53,15 @@
 
 function getPublicKey(cert, importParams, callback) {
   importParams = normalizeImportParams(importParams);
-  // TODO(pneubeck): pass importParams.name to the internal getPublicKey and
-  // verify on the C++ side that the requested algorithm is compatible with the
-  // given SubjectPublicKeyInfo of the certificate.
-  // https://crbug.com/466584
-  internalAPI.getPublicKey(cert, function(publicKey, algorithm) {
-    var combinedAlgorithm = combineAlgorithms(algorithm, importParams);
-    callback(publicKey, combinedAlgorithm);
-  });
+  internalAPI.getPublicKey(
+      cert, importParams.name, function(publicKey, algorithm) {
+        if (chrome.runtime.lastError) {
+          callback();
+          return;
+        }
+        var combinedAlgorithm = combineAlgorithms(algorithm, importParams);
+        callback(publicKey, combinedAlgorithm);
+      });
 }
 
 exports.getPublicKey = getPublicKey;
diff --git a/chrome/renderer/resources/extensions/platform_keys_custom_bindings.js b/chrome/renderer/resources/extensions/platform_keys_custom_bindings.js
index 9320e02..1f30c1f 100644
--- a/chrome/renderer/resources/extensions/platform_keys_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/platform_keys_custom_bindings.js
@@ -49,6 +49,10 @@
   apiFunctions.setHandleRequest(
       'getKeyPair', function(cert, params, callback) {
         getPublicKey(cert, params, function(publicKey, algorithm) {
+          if (chrome.runtime.lastError) {
+            callback();
+            return;
+          }
           callback(createPublicKey(publicKey, algorithm),
                    createPrivateKey(publicKey, algorithm));
         });
diff --git a/chrome/renderer/resources/extensions/web_view/chrome_web_view.js b/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
index a91a239..a2ae0687 100644
--- a/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
+++ b/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
@@ -12,18 +12,8 @@
 var EventBindings = require('event_bindings');
 var idGeneratorNatives = requireNative('id_generator');
 var Utils = require('utils');
-var WebViewEvents = require('webViewEvents').WebViewEvents;
 var WebViewImpl = require('webView').WebViewImpl;
 
-var CHROME_WEB_VIEW_EVENTS = {
-  'contextmenushown': {
-    evt: CreateEvent('chromeWebViewInternal.contextmenu'),
-    cancelable: true,
-    fields: ['items'],
-    handler: 'handleContextMenu'
-  }
-};
-
 // This is the only "webViewInternal.onClicked" named event for this renderer.
 //
 // Since we need an event per <webview>, we define events with suffix
@@ -33,6 +23,9 @@
 // it to the subEvent's listeners. This way
 // <webview>.contextMenus.onClicked behave as a regular chrome Event type.
 var ContextMenusEvent = CreateEvent('chromeWebViewInternal.onClicked');
+// See comment above.
+var ContextMenusHandlerEvent =
+    CreateEvent('chromeWebViewInternal.onContextMenuShow');
 
 // -----------------------------------------------------------------------------
 // ContextMenusOnClickedEvent object.
@@ -58,6 +51,41 @@
 
 ContextMenusOnClickedEvent.prototype.__proto__ = EventBindings.Event.prototype;
 
+function ContextMenusOnContextMenuEvent(webViewImpl,
+                                        opt_eventName,
+                                        opt_argSchemas,
+                                        opt_eventOptions,
+                                        opt_webViewInstanceId) {
+  var subEventName = GetUniqueSubEventName(opt_eventName);
+  EventBindings.Event.call(this,
+                           subEventName,
+                           opt_argSchemas,
+                           opt_eventOptions,
+                           opt_webViewInstanceId);
+  var defaultPrevented = false;
+  ContextMenusHandlerEvent.addListener(function(e) {
+    var defaultPrevented = false;
+    var event = {
+      'preventDefault': function() { defaultPrevented = true; }
+    };
+
+    // Re-dispatch to subEvent's listeners.
+    $Function.apply(this.dispatch, this, [event]);
+
+    if (!defaultPrevented) {
+      // TODO(lazyboy): Remove |items| parameter completely from
+      // ChromeWebView.showContextMenu as we don't do anything useful with it
+      // currently.
+      var items = [];
+      ChromeWebView.showContextMenu(
+          webViewImpl.guest.getId(), e.requestId, items);
+    }
+  }.bind(this), {instanceId: opt_webViewInstanceId || 0});
+}
+
+ContextMenusOnContextMenuEvent.prototype.__proto__ =
+    EventBindings.Event.prototype;
+
 // -----------------------------------------------------------------------------
 // WebViewContextMenusImpl object.
 
@@ -93,6 +121,15 @@
 // -----------------------------------------------------------------------------
 
 WebViewImpl.prototype.maybeSetupContextMenus = function() {
+  if (!this.contextMenusOnContextMenuEvent_) {
+    var eventName = 'chromeWebViewInternal.onContextMenuShow';
+    // TODO(lazyboy): Find event by name instead of events[1].
+    var eventSchema = ChromeWebViewSchema.events[1];
+    var eventOptions = {supportsListeners: true};
+    this.contextMenusOnContextMenuEvent_ = new ContextMenusOnContextMenuEvent(
+        this, eventName, eventSchema, eventOptions, this.viewInstanceId);
+  }
+
   var createContextMenus = function() {
     return function() {
       if (this.contextMenus_) {
@@ -117,17 +154,27 @@
           return this.contextMenusOnClickedEvent_;
         }.bind(this);
       }.bind(this);
-      Object.defineProperty(
+      $Object.defineProperty(
           this.contextMenus_,
           'onClicked',
           {get: getOnClickedEvent(), enumerable: true});
-
+      $Object.defineProperty(
+          this.contextMenus_,
+          'onShow',
+          {
+            get: function() {
+              return this.contextMenusOnContextMenuEvent_;
+            }.bind(this),
+            enumerable: true
+          });
       return this.contextMenus_;
     }.bind(this);
   }.bind(this);
 
   // Expose <webview>.contextMenus object.
-  Object.defineProperty(
+  // TODO(lazyboy): Add documentation for contextMenus:
+  // http://crbug.com/470979.
+  $Object.defineProperty(
       this.element,
       'contextMenus',
       {
@@ -136,52 +183,6 @@
       });
 };
 
-WebViewEvents.prototype.handleContextMenu = function(event, eventName) {
-  var webViewEvent = this.makeDomEvent(event, eventName);
-  var requestId = event.requestId;
-  // Construct the event.menu object.
-  var actionTaken = false;
-  var validateCall = function() {
-    var ERROR_MSG_CONTEXT_MENU_ACTION_ALREADY_TAKEN = '<webview>: ' +
-        'An action has already been taken for this "contextmenu" event.';
-
-    if (actionTaken) {
-      throw new Error(ERROR_MSG_CONTEXT_MENU_ACTION_ALREADY_TAKEN);
-    }
-    actionTaken = true;
-  };
-  var menu = {
-    show: function(items) {
-      validateCall();
-      // TODO(lazyboy): WebViewShowContextFunction doesn't do anything useful
-      // with |items|, implement.
-      ChromeWebView.showContextMenu(this.view.guest.getId(), requestId, items);
-    }.bind(this)
-  };
-  webViewEvent.menu = menu;
-  var element = this.view.element;
-  var defaultPrevented = !element.dispatchEvent(webViewEvent);
-  if (actionTaken) {
-    return;
-  }
-  if (!defaultPrevented) {
-    actionTaken = true;
-    // The default action is equivalent to just showing the context menu as is.
-    ChromeWebView.showContextMenu(
-        this.view.guest.getId(), requestId, undefined);
-
-    // TODO(lazyboy): Figure out a way to show warning message only when
-    // listeners are registered for this event.
-  } //  else we will ignore showing the context menu completely.
-};
-
 function GetUniqueSubEventName(eventName) {
   return eventName + '/' + idGeneratorNatives.GetNextId();
 }
-
-// Exposes |CHROME_WEB_VIEW_EVENTS| when the ChromeWebView API is available.
-(function() {
-  for (var eventName in CHROME_WEB_VIEW_EVENTS) {
-    WebViewEvents.EVENTS[eventName] = CHROME_WEB_VIEW_EVENTS[eventName];
-  }
-})();
diff --git a/chrome/renderer/resources/neterror.css b/chrome/renderer/resources/neterror.css
index e9f9ac4..e62327eb 100644
--- a/chrome/renderer/resources/neterror.css
+++ b/chrome/renderer/resources/neterror.css
@@ -58,6 +58,13 @@
   position: relative;
 }
 
+.icon-disabled {
+  content: -webkit-image-set(
+      url(default_100_percent/offline/100-disabled.png) 1x,
+      url(default_200_percent/offline/200-disabled.png) 2x);
+  width: 112px;
+}
+
 .error-code {
   display: block;
 }
@@ -74,6 +81,10 @@
   text-align: start;
 }
 
+.hidden {
+  display: none;
+}
+
 #suggestion {
   margin-top: 15px;
 }
@@ -140,8 +151,46 @@
   color: #696969;
 }
 
-.hidden {
-  display: none;
+.snackbar {
+  background: #323232;
+  border-radius: 2px;
+  bottom: 24px;
+  box-sizing: border-box;
+  color: #fff;
+  font-size: .87em;
+  left: 24px;
+  max-width: 568px;
+  min-width: 288px;
+  opacity: 0;
+  padding: 16px 24px 12px;
+  position: fixed;
+  transform: translateY(90px);
+  will-change: opacity, transform;
+  z-index: 999;
+}
+
+.snackbar-show {
+  -webkit-animation:
+    show-snackbar .25s cubic-bezier(0.0, 0.0, 0.2, 1) forwards,
+    hide-snackbar .25s cubic-bezier(0.4, 0.0, 1, 1) forwards 5s;
+}
+
+@-webkit-keyframes show-snackbar {
+  100% {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+@-webkit-keyframes hide-snackbar {
+  0% {
+    opacity: 1;
+    transform: translateY(0);
+  }
+  100% {
+    opacity: 0;
+    transform: translateY(90px);
+  }
 }
 
 .suggestions {
@@ -326,6 +375,13 @@
   .suggested-right > #control-buttons {
     float: none;
   }
+
+  .snackbar {
+    left: 0;
+    bottom: 0;
+    width: 100%;
+    border-radius: 0;
+  }
 }
 
 @media (max-height: 350px) {
diff --git a/chrome/renderer/resources/offline.js b/chrome/renderer/resources/offline.js
index 75404cc..5fabe44 100644
--- a/chrome/renderer/resources/offline.js
+++ b/chrome/renderer/resources/offline.js
@@ -61,7 +61,12 @@
   // Images.
   this.images = {};
   this.imagesLoaded = 0;
-  this.loadImages();
+
+  if (this.isDisabled()) {
+    this.setupDisabledRunner();
+  } else {
+    this.loadImages();
+  }
 }
 window['Runner'] = Runner;
 
@@ -135,6 +140,8 @@
   CONTAINER: 'runner-container',
   CRASHED: 'crashed',
   ICON: 'icon-offline',
+  SNACKBAR: 'snackbar',
+  SNACKBAR_SHOW: 'snackbar-show',
   TOUCH_CONTROLLER: 'controller'
 };
 
@@ -210,6 +217,32 @@
 
 Runner.prototype = {
   /**
+   * Whether the easter egg has been disabled. CrOS enterprise enrolled devices.
+   * @return {boolean}
+   */
+  isDisabled: function() {
+    return loadTimeData && loadTimeData.valueExists('disabledEasterEgg');
+  },
+
+  /**
+   * For disabled instances, set up a snackbar with the disabled message.
+   */
+  setupDisabledRunner: function() {
+    this.containerEl = document.createElement('div');
+    this.containerEl.className = Runner.classes.SNACKBAR;
+    this.containerEl.textContent = loadTimeData.getValue('disabledEasterEgg');
+    this.outerContainerEl.appendChild(this.containerEl);
+
+    // Show notification when the activation key is pressed.
+    document.addEventListener(Runner.events.KEYDOWN, function(e) {
+      if (Runner.keycodes.JUMP[e.keyCode]) {
+        this.containerEl.classList.add(Runner.classes.SNACKBAR_SHOW);
+        document.querySelector('.icon').classList.add('icon-disabled');
+      }
+    }.bind(this));
+  },
+
+  /**
    * Setting individual settings for debugging.
    * @param {string} setting
    * @param {*} value
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index 21bf0b6..7cb5d7d4 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -6,7 +6,6 @@
 
 #include "base/i18n/rtl.h"
 #include "base/json/string_escape.h"
-#include "base/metrics/field_trial.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -17,6 +16,7 @@
 #include "chrome/grit/renderer_resources.h"
 #include "chrome/renderer/searchbox/searchbox.h"
 #include "components/crx_file/id_util.h"
+#include "components/variations/variations_associated_data.h"
 #include "content/public/renderer/render_view.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
@@ -64,8 +64,7 @@
 
 // Returns whether we should use large icons on NTP.
 bool IsIconNTPEnabled() {
-  return StartsWithASCII(base::FieldTrialList::FindFullName("IconNTP"),
-                         "Enabled", true);
+  return variations::GetVariationParamValue("IconNTP", "state") == "enabled";
 }
 
 // Converts string16 to V8 String.
diff --git a/chrome/renderer/spellchecker/spellcheck.cc b/chrome/renderer/spellchecker/spellcheck.cc
index 8d3a0491..e8e283a0 100644
--- a/chrome/renderer/spellchecker/spellcheck.cc
+++ b/chrome/renderer/spellchecker/spellcheck.cc
@@ -45,6 +45,29 @@
   return true;
 }
 
+class DocumentMarkersCollector : public content::RenderViewVisitor {
+ public:
+  DocumentMarkersCollector() {}
+  ~DocumentMarkersCollector() override {}
+  const std::vector<uint32>& markers() const { return markers_; }
+  bool Visit(content::RenderView* render_view) override;
+
+ private:
+  std::vector<uint32> markers_;
+  DISALLOW_COPY_AND_ASSIGN(DocumentMarkersCollector);
+};
+
+bool DocumentMarkersCollector::Visit(content::RenderView* render_view) {
+  if (!render_view || !render_view->GetWebView())
+    return true;
+  WebVector<uint32> markers;
+  render_view->GetWebView()->spellingMarkers(&markers);
+  for (size_t i = 0; i < markers.size(); ++i)
+    markers_.push_back(markers[i]);
+  // Visit all render views.
+  return true;
+}
+
 class DocumentMarkersRemover : public content::RenderViewVisitor {
  public:
   explicit DocumentMarkersRemover(const std::vector<std::string>& words);
@@ -124,6 +147,8 @@
     IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableAutoSpellCorrect,
                         OnEnableAutoSpellCorrect)
     IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableSpellCheck, OnEnableSpellCheck)
+    IPC_MESSAGE_HANDLER(SpellCheckMsg_RequestDocumentMarkers,
+                        OnRequestDocumentMarkers)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -162,6 +187,13 @@
   content::RenderView::ForEach(&updater);
 }
 
+void SpellCheck::OnRequestDocumentMarkers() {
+  DocumentMarkersCollector collector;
+  content::RenderView::ForEach(&collector);
+  content::RenderThread::Get()->Send(
+      new SpellCheckHostMsg_RespondDocumentMarkers(collector.markers()));
+}
+
 // TODO(groby): Make sure we always have a spelling engine, even before Init()
 // is called.
 void SpellCheck::Init(base::File file,
diff --git a/chrome/renderer/spellchecker/spellcheck.h b/chrome/renderer/spellchecker/spellcheck.h
index 472b219..23f58f26 100644
--- a/chrome/renderer/spellchecker/spellcheck.h
+++ b/chrome/renderer/spellchecker/spellcheck.h
@@ -127,6 +127,7 @@
       const std::vector<std::string>& words_removed);
   void OnEnableAutoSpellCorrect(bool enable);
   void OnEnableSpellCheck(bool enable);
+  void OnRequestDocumentMarkers();
 
 #if !defined (OS_MACOSX)
   // Posts delayed spellcheck task and clear it if any.
diff --git a/chrome/test/chromedriver/VERSION b/chrome/test/chromedriver/VERSION
index 123a39a8..e3d06964 100644
--- a/chrome/test/chromedriver/VERSION
+++ b/chrome/test/chromedriver/VERSION
@@ -1 +1 @@
-2.14
+2.15
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py
index 0b5a624..bc657125 100644
--- a/chrome/test/chromedriver/client/chromedriver.py
+++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -384,3 +384,6 @@
         'upload_throughput': conditions['upload_throughput'],
         'offline': conditions['offline']
     }
+
+  def DeleteNetworkConditions(self):
+    self.ExecuteCommand(Command.DELETE_NETWORK_CONDITIONS)
diff --git a/chrome/test/chromedriver/client/command_executor.py b/chrome/test/chromedriver/client/command_executor.py
index f3d6465b..fd4a464 100644
--- a/chrome/test/chromedriver/client/command_executor.py
+++ b/chrome/test/chromedriver/client/command_executor.py
@@ -99,6 +99,8 @@
       _Method.GET, '/session/:sessionId/chromium/network_conditions')
   SET_NETWORK_CONDITIONS = (
       _Method.POST, '/session/:sessionId/chromium/network_conditions')
+  DELETE_NETWORK_CONDITIONS = (
+      _Method.DELETE, '/session/:sessionId/chromium/network_conditions')
   GET_STATUS = (_Method.GET, '/session/:sessionId/application_cache/status')
   IS_BROWSER_ONLINE = (_Method.GET, '/session/:sessionId/browser_connection')
   SET_BROWSER_ONLINE = (_Method.POST, '/session/:sessionId/browser_connection')
diff --git a/chrome/test/chromedriver/server/http_handler.cc b/chrome/test/chromedriver/server/http_handler.cc
index 3ae9299..65f05ee9 100644
--- a/chrome/test/chromedriver/server/http_handler.cc
+++ b/chrome/test/chromedriver/server/http_handler.cc
@@ -380,6 +380,11 @@
           "session/:sessionId/chromium/network_conditions",
           WrapToCommand("SetNetworkConditions",
                         base::Bind(&ExecuteSetNetworkConditions))),
+      CommandMapping(
+          kDelete,
+          "session/:sessionId/chromium/network_conditions",
+          WrapToCommand("DeleteNetworkConditions",
+                        base::Bind(&ExecuteDeleteNetworkConditions))),
       CommandMapping(kGet,
                      "session/:sessionId/application_cache/status",
                      base::Bind(&ExecuteGetStatus)),
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 57e1990..8ed2651 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -800,6 +800,10 @@
     self.assertEquals('0'.zfill(int(10e6)), lots_of_data)
 
   def testEmulateNetworkConditions(self):
+    # Network conditions must be set before it can be retrieved.
+    self.assertRaises(chromedriver.UnknownError,
+                      self._driver.GetNetworkConditions)
+
     # DSL: 2Mbps throughput, 5ms RTT
     latency = 5
     throughput = 2048 * 1024
@@ -811,6 +815,11 @@
     self.assertEquals(throughput, network['upload_throughput']);
     self.assertEquals(False, network['offline']);
 
+    # Network Conditions again cannot be retrieved after they've been deleted.
+    self._driver.DeleteNetworkConditions()
+    self.assertRaises(chromedriver.UnknownError,
+                      self._driver.GetNetworkConditions)
+
   def testEmulateNetworkConditionsName(self):
     # DSL: 2Mbps throughput, 5ms RTT
     #latency = 5
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index 000cec03..075f8b1143 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -930,6 +930,29 @@
       *session->overridden_network_conditions);
 }
 
+Status ExecuteDeleteNetworkConditions(
+    Session* session,
+    WebView* web_view,
+    const base::DictionaryValue& params,
+    scoped_ptr<base::Value>* value) {
+  // Chrome does not have any command to stop overriding network conditions, so
+  // we just override the network conditions with the "No throttling" preset.
+  NetworkConditions network_conditions;
+  // Get conditions from preset list.
+  Status status = FindPresetNetwork("No throttling", &network_conditions);
+  if (status.IsError())
+    return status;
+
+  status = web_view->OverrideNetworkConditions(network_conditions);
+  if (status.IsError())
+    return status;
+
+  // After we've successfully overridden the network conditions with
+  // "No throttling", we can delete them from |session|.
+  session->overridden_network_conditions.reset();
+  return status;
+}
+
 Status ExecuteTakeHeapSnapshot(
     Session* session,
     WebView* web_view,
diff --git a/chrome/test/chromedriver/window_commands.h b/chrome/test/chromedriver/window_commands.h
index 67d9ff88..ec4a68c 100644
--- a/chrome/test/chromedriver/window_commands.h
+++ b/chrome/test/chromedriver/window_commands.h
@@ -296,6 +296,12 @@
     const base::DictionaryValue& params,
     scoped_ptr<base::Value>* value);
 
+Status ExecuteDeleteNetworkConditions(
+    Session* session,
+    WebView* web_view,
+    const base::DictionaryValue& params,
+    scoped_ptr<base::Value>* value);
+
 Status ExecuteTakeHeapSnapshot(
     Session* session,
     WebView* web_view,
diff --git a/chrome/test/data/android/about.html b/chrome/test/data/android/about.html
new file mode 100644
index 0000000..b33ddfe
--- /dev/null
+++ b/chrome/test/data/android/about.html
@@ -0,0 +1,11 @@
+<html>
+
+<head>
+<title>About</title>
+</head>
+
+<body>
+  Hello, World!
+</body>
+
+</html>
diff --git a/chrome/test/data/autofill/heuristics/input/bug_464002.html b/chrome/test/data/autofill/heuristics/input/bug_464002.html
new file mode 100644
index 0000000..f49b202
--- /dev/null
+++ b/chrome/test/data/autofill/heuristics/input/bug_464002.html
@@ -0,0 +1,244 @@
+<form id="paymentMethodForm" name="paymentMethodForm" method="post" class="styled_forms  modalForms">
+  <p class="size18 float-left">Debit / Credit Card Information</p>
+  <p class="float-right PadTop3 botMar0">
+  <span class="redtxt font12" aria-hidden="true">*=Required</span>
+  </p>
+  <div class="ClearNew botMar0"></div>
+  <div class="errorMsg box botMar20" id="errorPageDiv" style="display: none;">
+    <p class="botMar0" id="errorUL"></p>
+  </div>
+  <input type="hidden" name="selectedMethodName" id="selectedMethodName" value="Add Credit/Debit Card">
+
+  <div class="row-seamless botMar0 styled_forms  modalForms">
+
+    <div class="tipPad  safariComp botMar15" style="overflow:hidden">
+      <div class=" right MarRight5 float-left w150">
+        <label class="rel top0">Method<span style="visibility:hidden" class="colorRed" aria-hidden="true">*</span></label>
+      </div>
+
+      <div class=" MarTop4  ie7MarTop0  helpDiv "><a href="#tipcr10" aria-describedby="tipcr10" class="lnk-help tooltips" title="" onclick="return false"><img src="/olam/images/brand30/global/icn-help.png" alt="Help "></a>
+        <span class="tips" id="tipcr10">We accept the following forms of payment:<br><br>
+          <strong>ATM debit cards</strong><br>
+          STAR, Pulse, ACCEL, NYCE
+          <br><br>
+          <strong>Debit / credit cards</strong><br>
+          Visa, MasterCard, American Express, Discover Network, JCB, Diners Club, AT&amp;T Universal
+          <br><img src="/OLAM_PROD_CMS/English/images/brand30/olam_creditCards_PinlessCards.gif" height="110px;" alt="Pinless Cards" border="0"></span></div>
+
+      <div class="float-left">
+        <span class="statictxt top4px">
+          Debit / credit card
+        </span>
+      </div>
+      <div class="ClearNew"></div>
+    </div>
+
+    <div class="tipPad safariComp botMar20">
+      <div class=" right MarRight5 float-left w150">
+        <label class="rel top0 " for="paymentMethodForm.newCreditCardCustomerName">Name on card<span class="colorRed" aria-hidden="true">*</span></label>
+      </div>
+      <div class="MarTop3  ie7MarTop0  helpDiv">&nbsp;</div>
+      <div id="divNameOnCard" class="MarBot0 PadRight2 float-left"><!-- for error case  MarBot0 PadRight2 error float-left -->
+        <div class="emailInput">
+          <span class="textInput-wrapper"><span class=""><input type="text" name="paymentMethodForm.newCreditCardCustomerName" maxlength="25" size="35" value="" onblur="validateNameOnCard(this.value, true)" class="popupinput text inline-error" id="paymentMethodForm.newCreditCardCustomerName" aria-required="true" aria-invalid="false"></span></span>
+        </div>
+        <div style="display: none;" id="" tabindex="0" class="focusable EmailAlertsDiv"><span style="display:block;height:0;width:0;color:transparent;z-index:-1;position:absolute" role="presentation">alert</span>
+          <div class="email-error-top-image"></div>
+          <div style="display:block;" class="email-error-mid-image" id="EmailAlert1">
+            <p class="rederr-Msg font11imp lineHeight15  PadTop3imp" id="pNameOnCardErrorMessage"></p>
+          </div>
+          <!--
+            <div class="email-error-mid-image" style="display:none;" id="EmailAlertMidPassDiv1">&nbsp;</div>
+            <div class="email-error-mid-image" style="display:none;" id="EmailAlertMidPassDiv2">&nbsp;</div>
+            <div style="display:none;" class="email-error-mid-image" id="EmailAlert2">
+            <p class="rederr-Msg">Caps Lock on</p>
+            </div> -->
+            <div class="email-error-bot-image"></div>
+        </div>
+      </div>
+      <div class="ClearNew"></div>
+    </div>
+
+    <div class="tipPad safariComp botMar20">
+      <div class=" right MarRight5 float-left w150">
+        <label class="rel top0 " for="paymentMethodForm.cardNumber">Card number<span class="colorRed" aria-hidden="true">*</span></label>
+      </div>
+      <div class=" MarTop3  ie7MarTop0  helpDiv ">&nbsp;</div>
+      <div id="divCardNumber" class="MarBot0 PadRight2 float-left error"><!-- for error case  MarBot0 PadRight2 error float-left -->
+        <div class="emailInput">
+          <span class="textInput-wrapper"><span class=""><input type="text" name="paymentMethodForm.cardNumber" maxlength="19" size="35" value="" onkeypress="return validatePmtNumericKeyPress(event)" onblur="validateCardNumber(this.value, true)" class="popupinput inline-error text" id="paymentMethodForm.cardNumber" aria-required="true" aria-invalid="true"></span></span>
+        </div>
+        <div style="" id="" tabindex="0" class="focusable EmailAlertsDiv"><span style="display:block;height:0;width:0;color:transparent;z-index:-1;position:absolute" role="presentation">alert</span>
+          <div class="email-error-top-image"></div>
+          <div style="display:block;" class="email-error-mid-image" id="EmailAlert1">
+            <p class="rederr-Msg font11imp lineHeight15  PadTop3imp" id="pCardNumberErrorMessage">Enter card number</p>
+          </div>
+          <!--
+            <div class="email-error-mid-image" style="display:none;" id="EmailAlertMidPassDiv1">&nbsp;</div>
+            <div class="email-error-mid-image" style="display:none;" id="EmailAlertMidPassDiv2">&nbsp;</div>
+            <div style="display:none;" class="email-error-mid-image" id="EmailAlert2">
+            <p class="rederr-Msg">Caps Lock on</p>
+            </div> -->
+            <div class="email-error-bot-image"></div>
+        </div>
+      </div>
+      <div class="ClearNew"></div>
+    </div>
+
+    <div class="botMar30">
+      <div class="float-left">
+        <div class="tipPad  safariComp botMar20">
+          <div class=" right MarRight5 float-left w150">
+            <label class="rel top0" for="paymentMethodForm.cardVerificationNumber">Security code<span class="colorRed" aria-hidden="true">*</span></label>
+          </div>
+          <div class=" MarTop3 ie7MarTop0 helpDiv ">&nbsp; </div>
+          <div id="divCVV" class="MarBot0 PadRight2 float-left error"><!-- for error case  MarBot0 PadRight2 error float-left -->
+            <div class="emailInput">
+              <span class="textInput-wrapper"><span class=""><input type="text" name="paymentMethodForm.cardVerificationNumber" maxlength="4" size="35" value="" onkeypress="return validatePmtNumericKeyPress(event)" onblur="validateCardVerificationValue(this.value, true)" class="popupinput inline-error text" id="paymentMethodForm.cardVerificationNumber" aria-required="true" aria-invalid="true"></span></span>
+            </div>
+            <div id="" style="" tabindex="0" class="focusable EmailAlertsDiv"><span style="display:block;height:0;width:0;color:transparent;z-index:-1;position:absolute" role="presentation">alert</span>
+              <div class="email-error-top-image"></div>
+              <div id="EmailAlert1" class="email-error-mid-image" style="display:block;">
+                <p class="rederr-Msg font11imp lineHeight15  PadTop3imp" id="pCVVErrorMessage">Enter security code</p>
+              </div>
+              <!--
+                <div id="EmailAlertMidPassDiv1" style="display:none;" class="email-error-mid-image">&nbsp;</div>
+                <div id="EmailAlertMidPassDiv2" style="display:none;" class="email-error-mid-image">&nbsp;</div>
+                <div id="EmailAlert2" class="email-error-mid-image" style="display:none;">
+                <p class="rederr-Msg">Caps Lock on</p>
+                </div> -->
+                <div class="email-error-bot-image"></div>
+            </div>
+          </div>
+          <div class="ClearNew"></div>
+        </div>
+        <div class="tipPad safariComp botMar20">
+          <div class=" right MarRight5 float-left w150">
+            <label class="rel top0 ">Card expiration date<span class="colorRed" aria-hidden="true">*</span></label>
+          </div>
+          <div class=" MarTop3  ie7MarTop0  helpDiv ">&nbsp;</div>
+          <div class="Login_styled_forms fixSelect float-left">
+
+
+            <div id="divExpirationMonth" class="MarBot0 float-left w138"><!-- class=" MarBot0 PadRight2 error float-left w138" -->
+              <div id="divExpirationMonthSelect" class=""><!--for error case class="error-selector" -->
+                <div class="selector" id="uniform-paymentMethodForm.expirationMonth"><span style="width: 75px;">04</span><select name="paymentMethodForm.expirationMonth" onblur="validateExpirationMonth(this.value, true)" class="inline-error jojoFix" id="paymentMethodForm.expirationMonth" title="Select month" aria-required="true" style="opacity: 0; width: 120px; height: 32px; top: 0px; left: -2px;"><!-- onchange="validateExpirationMonth(this.value, true) to make the consitent -->
+                    <option value="" selected="selected">Select month</option>
+                    <option value="01">01</option>
+                    <option value="02">02</option>
+                    <option value="03">03</option>
+                    <option value="04">04</option>
+                    <option value="05">05</option>
+                    <option value="06">06</option>
+                    <option value="07">07</option>
+                    <option value="08">08</option>
+                    <option value="09">09</option>
+                    <option value="10">10</option>
+                    <option value="11">11</option>
+                    <option value="12">12</option></select></div>
+              </div>
+              <div id="divExpirationMonthErrorMessage" style="display:none;" tabindex="0" class="focusable"><span style="display:block;height:0;width:0;color:transparent;z-index:-1;position:absolute" role="presentation">alert</span>
+                <p class="rederr-text bold PadLeft7 MarTop5 botMar0 font11imp w100 PadTop3imp" id="pExpirationMonthErrorMessage"></p>
+              </div>
+              <div class="ClearNew"></div>
+            </div>
+
+            <div id="divExpirationYear" class=" MarBot0 float-left w138">
+              <div id="divExpirationYearSelect" class="">
+                <div class="selector" id="uniform-paymentMethodForm.expirationYear"><span style="width: 65px;">2018</span><select name="paymentMethodForm.expirationYear" onblur="validateExpirationYear(this.value, true)" class="inline-error jojoFix" id="paymentMethodForm.expirationYear" title="Select year" aria-required="true" style="opacity: 0; width: 110px; height: 32px; top: 0px; left: -2px;"><!-- onchange="validateExpirationYear(this.value, true) to make the consitent -->
+                    <option value="" selected="selected">Select year</option>
+                    <option value="2015">2015</option>
+                    <option value="2016">2016</option>
+                    <option value="2017">2017</option>
+                    <option value="2018">2018</option>
+                    <option value="2019">2019</option>
+                    <option value="2020">2020</option>
+                    <option value="2021">2021</option>
+                    <option value="2022">2022</option>
+                    <option value="2023">2023</option>
+                    <option value="2024">2024</option>
+                    <option value="2025">2025</option>
+                    <option value="2026">2026</option>
+                    <option value="2027">2027</option>
+                    <option value="2028">2028</option>
+                    <option value="2029">2029</option></select></div>
+              </div>
+
+              <div id="divExpirationYearErrorMessage" style="display:none;" tabindex="0" class="focusable"><span style="display:block;height:0;width:0;color:transparent;z-index:-1;position:absolute" role="presentation">alert</span>
+                <p class="rederr-text bold PadLeft7 MarTop5 botMar0 w100 font11imp PadTop3imp" id="pExpirationYearErrorMessage"></p>
+              </div>
+              <div class="ClearNew"></div>
+            </div>
+            <div class="ClearNew"></div>
+          </div>
+          <div class="ClearNew"></div>
+        </div>
+        <div class="tipPad safariComp">
+          <div class=" right MarRight5 float-left w150">
+            <label class="rel top0" for="paymentMethodForm.zip">Card billing ZIP Code<span class="colorRed" aria-hidden="true">*</span></label>
+          </div>
+
+          <div class=" MarTop3  ie7MarTop0  helpDiv "><!-- &nbsp; --><a href="#tip52" id="atip52" aria-describedby="tip52" class="lnk-help tooltips" title=""><img src="/olam/images/brand30/global/icn-help.png" alt="Help"></a>
+            <span class="tips" id="tip52">Enter the ZIP Code of the credit cardholder's billing address.</span></div>
+
+
+          <div id="divCardZipCode" class=" MarBot0 PadRight2 float-left"> <!--for error case  class=" MarBot0 PadRight2 error float-left"  -->
+            <div class="emailInput">
+              <span class="textInput-wrapper"><span><input type="text" name="paymentMethodForm.zip" maxlength="9" size="35" value="" onblur="validateCardZipCode(this.value, true)" class="popupinput inline-error text" id="paymentMethodForm.zip" aria-required="true"></span></span>
+            </div>
+            <div style="display:none;" id="" tabindex="0" class="focusable EmailAlertsDiv"><span style="display:block;height:0;width:0;color:transparent;z-index:-1;position:absolute" role="presentation">alert</span>
+              <div class="email-error-top-image"></div>
+              <div style="display:block;" class="email-error-mid-image" id="EmailAlert1">
+                <p class="rederr-Msg font11imp lineHeight15  PadTop3imp" id="pCardZipCodeErrorMessage"></p>
+              </div>
+              <!--
+                <div class="email-error-mid-image" style="display:none;" id="EmailAlertMidPassDiv1">&nbsp;</div>
+                <div class="email-error-mid-image" style="display:none;" id="EmailAlertMidPassDiv2">&nbsp;</div>
+                <div style="display:none;" class="email-error-mid-image" id="EmailAlert2">
+                <p class="rederr-Msg">Caps Lock on</p>
+                </div> -->
+                <div class="email-error-bot-image"></div>
+            </div>
+          </div>
+          <div class="ClearNew"></div>
+        </div>
+      </div>
+      <div id="divCardImage" class="float-left focusable MarTopNeg15" tabindex="0"><!-- for error case class="float-left MarTopNeg15" -->
+        <img class=" " border="0" src="/olam/English/brand30/img_card.png" alt="Code is 3 digits on the back, to the right. It's the 4 digits above account number for American Express.">
+      </div>
+      <div class="ClearNew"></div>
+    </div>
+  </div>
+  <div style="display: none;" id="paymentMethodFormCustCode">
+    <div class="row-seamless ">
+      <div class="botMarOCDPA0 TopDotBorder PadTop18">
+        <h3 class="font17">For Business, Corporate, or Purchasing Cardholders Only</h3>
+      </div>
+    </div>
+
+    <div class="row-seamless botMar15 styled_forms  modalForms">
+      <div class="botMar0 Padbot15"></div>
+      <div class="tipPad safariComp ">
+        <div class=" right MarRight5 float-left w253">
+          <label class="rel top0" for="paymentMethodForm.custCode">Customer code</label>
+        </div>
+
+        <div class=" MarTop3  ie7MarTop0  helpDiv ">&nbsp;<a href="#tip53" id="atip53" class="lnk-help tooltips" aria-describedby="tip53"><img src="/olam/images/brand30/global/icn-help.png" alt="Help"></a>
+          <span class="tips" id="tip53">Up to 17 characters of optional information that eligible business, purchasing, and corporate cardholders use to identify transactions on their Visa, MasterCard, or American Express statements.</span></div>
+
+        <span class="textInput-wrapper"><span><input type="text" name="paymentMethodForm.custCode" maxlength="17" size="35" value="" onkeydown="return validateAlphaNumericKeyDown(event)" class="popupinput text" id="paymentMethodForm.custCode"></span></span>
+      </div>
+    </div>
+  </div>
+  <input type="hidden" name="paymentMethodForm.storePaymentProfileInd" value="N">
+
+  <div class="btnInsideRowSeam botMar5" style="overflow:hidden">
+    <div class="btnLt">
+      <a href="#" title="Cancel" class="textLink" onclick="cancelMethod(&quot;Are you sure you want to cancel?&quot;);parent.enableNextBtn();">Cancel</a>
+    </div>
+    <div class="btnRt PadRight1 PadBot3">
+      <a role="button" title="" class="button primary small" href="javascript:perfValidationBeforeSubmit('/pmt/addNewPayment.myworld','paymentMethodForm','payCommonActionForm')">Continue</a>
+    </div>
+    <div class="ClearNew"></div>
+  </div>
+</form>
diff --git a/chrome/test/data/autofill/heuristics/input/bug_469012.html b/chrome/test/data/autofill/heuristics/input/bug_469012.html
new file mode 100644
index 0000000..3830bb4
--- /dev/null
+++ b/chrome/test/data/autofill/heuristics/input/bug_469012.html
@@ -0,0 +1,453 @@
+<form action="/ticket/addcard.do" method="post" id="payment_new">
+  <div class="dmethod">
+    <input type="hidden" name="newcard" value="1">
+    <input type="hidden" name="dest" value="/tmloggedin.do">
+
+    <div class="center"><img src="/styles/tmus/amex_parity_mini.gif" alt="creditcards">
+
+
+      <img src="/styles/tmus/visa_parity_mini.gif" alt="creditcards">
+      <img src="/styles/tmus/mc_parity_mini.gif" alt="creditcards">
+      <img src="/styles/tmus/diners_parity_mini.gif" alt="creditcards">
+      <img src="/styles/tmus/discover_network_parity_mini.gif" alt="creditcards">
+    </div>
+
+    <label>Card Issuer</label>
+    <select name="cardissuer">
+      <option value="">Select Card Type</option>
+      <option value="AMEX">American Express</option>
+      <option value="VISA">VISA</option>
+      <option value="MC">MasterCard</option>
+      <option value="DISCOVER">Discover Network</option>
+      <option value="DINERS">Diners Club</option>
+    </select><br>
+    <label>Your cardnumber</label>
+    <input name="cardnumber" type="text" pattern="[0-9]*" placeholder="Your 16 digit number">
+    <fieldset class="expiration">
+      <label><strong id="new_credit_card_expire_heading">Expiration Date</strong>
+      </label>
+      <select id="expire_month" name="expire_month">
+        <option value="">Month</option>
+        <option value="1">1</option>
+        <option value="2">2</option>
+        <option value="3">3</option>
+        <option value="4">4</option>
+        <option value="5">5</option>
+        <option value="6">6</option>
+        <option value="7">7</option>
+        <option value="8">8</option>
+        <option value="9">9</option>
+        <option value="10">10</option>
+        <option value="11">11</option>
+        <option value="12">12</option>
+      </select>
+
+      <select id="expire_year" name="expire_year">
+        <option value="">Year</option>
+        <option value="2015">2015</option>
+        <option value="2016">2016</option>
+        <option value="2017">2017</option>
+        <option value="2018">2018</option>
+        <option value="2019">2019</option>
+        <option value="2020">2020</option>
+        <option value="2021">2021</option>
+        <option value="2022">2022</option>
+        <option value="2023">2023</option>
+        <option value="2024">2024</option>
+        <option value="2025">2025</option>
+        <option value="2026">2026</option>
+        <option value="2027">2027</option>
+        <option value="2028">2028</option>
+        <option value="2029">2029</option>
+      </select>
+
+    </fieldset>
+  </div>
+
+  <div class="dmethod">
+    <span class="info" title="= Fields marked with an * are required">* = Required Text</span>
+
+    <div class="smalltext">Enter the credit card billing address and name exactly as it appears on your credit card statement.
+    </div>
+
+    <div class="billInfo">
+      <fieldset>
+
+        <div class="formline">
+          <label for="first_name" id="address_form_label_first_name">First Name <span class="requiredText">*</span> </label>
+          <input type="text" name="firstname" id="first_name" size="35" maxlength="80">
+
+        </div>
+        <div class="formline">
+          <label for="last_name" id="address_form_label_last_name">Last Name <span class="requiredText">*</span> </label>
+          <input type="text" name="lastname" id="last_name" size="35" maxlength="80">
+        </div>
+        <div class="formline">
+          <label for="address1">Address
+            <span class="requiredText">*</span> </label>
+          <input type="text" size="35" maxlength="30" name="address1" id="address1"></div>
+        <div class="formline"> <span id="unit_title_span"><label for="unit">Unit #</label></span>
+
+          <span id="unit_input_span"><input type="text" size="10" name="unit" id="unit"></span> <span id="unit_optional_span" class="txt11">(Optional)</span>
+        </div>
+        <div class="formline">
+          <label for="address2"><span id="address2_title_span">Address Line 2</span></label>
+          <input type="text" size="35" name="address2" id="address2">
+        </div>
+        <div class="formline">
+          <label for="city"><span id="city_title_span">City                    </span>&nbsp;<span class="requiredText">*</span> </label>
+          <input type="text" name="city" id="city" size="35">
+          <span class="info">Military Address? Enter APO or FPO.</span>
+        </div>
+
+        <div class="formline">
+          <label for="state_id"><span id="state_title_span">State                    </span> <span class="requiredText">*</span> </label>
+          <span id="state_input_span">
+            <select name="state" id="state_id">
+              <option value="">-- Select State
+              --
+              </option>
+              <option value="AL">Alabama</option>
+              <option value="AK">Alaska</option>
+              <option value="AS">American Samoa</option>
+              <option value="AZ">Arizona</option>
+              <option value="AR">Arkansas</option>
+              <option value="AA">Armed Forces Americas</option>
+              <option value="AE">Armed Forces Other</option>
+              <option value="AP">Armed Forces Pacific</option>
+              <option value="CA">California</option>
+              <option value="CO">Colorado</option>
+              <option value="CT">Connecticut</option>
+              <option value="DE">Delaware</option>
+              <option value="DC">District Of Columbia</option>
+              <option value="FM">Federated States Of Micronesia</option>
+              <option value="FL">Florida</option>
+              <option value="GA">Georgia</option>
+              <option value="GU">Guam</option>
+              <option value="HI">Hawaii</option>
+              <option value="ID">Idaho</option>
+              <option value="IL">Illinois</option>
+              <option value="IN">Indiana</option>
+              <option value="IA">Iowa</option>
+              <option value="KS">Kansas</option>
+              <option value="KY">Kentucky</option>
+              <option value="LA">Louisiana</option>
+              <option value="ME">Maine</option>
+              <option value="MH">Marshall Islands</option>
+              <option value="MD">Maryland</option>
+              <option value="MA">Massachusetts</option>
+              <option value="MI">Michigan</option>
+              <option value="MN">Minnesota</option>
+              <option value="MS">Mississippi</option>
+              <option value="MO">Missouri</option>
+              <option value="MT">Montana</option>
+              <option value="NE">Nebraska</option>
+              <option value="NV">Nevada</option>
+              <option value="NH">New Hampshire</option>
+              <option value="NJ">New Jersey</option>
+              <option value="NM">New Mexico</option>
+              <option value="NY">New York</option>
+              <option value="NC">North Carolina</option>
+              <option value="ND">North Dakota</option>
+              <option value="MP">Northern Mariana Islands</option>
+              <option value="OH">Ohio</option>
+              <option value="OK">Oklahoma</option>
+              <option value="OR">Oregon</option>
+              <option value="PW">Palau</option>
+              <option value="PA">Pennsylvania</option>
+              <option value="PR">Puerto Rico</option>
+              <option value="RI">Rhode Island</option>
+              <option value="SC">South Carolina</option>
+              <option value="SD">South Dakota</option>
+              <option value="TN">Tennessee</option>
+              <option value="TX">Texas</option>
+              <option value="UT">Utah</option>
+              <option value="VT">Vermont</option>
+              <option value="VI">Virgin Islands</option>
+              <option value="VA">Virginia</option>
+              <option value="WA">Washington</option>
+              <option value="WV">West Virginia</option>
+              <option value="WI">Wisconsin</option>
+              <option value="WY">Wyoming</option>
+        </div>
+        <div class="formline">
+          <label for="postal_code"><span id="zip_title_span">ZIP Code</span><span class="requiredText">*</span>
+          </label>
+
+          <input type="text" name="zip" id="postal_code" size="35" autocapitalize="off" autocomplete="off">
+        </div>
+        <div class="formline">
+          <label for="country">Country
+            <span class="requiredText">*</span>
+          </label>
+          <select name="country" id="countrylist">
+            <option value="ad">Andorra</option>
+            <option value="ae">United Arab Emirates</option>
+            <option value="af">Afghanistan</option>
+            <option value="ag">Antigua and Barbuda</option>
+            <option value="ai">Anguilla</option>
+            <option value="al">Albania</option>
+            <option value="am">Armenia</option>
+            <option value="an">Netherlands Antilles</option>
+            <option value="ao">Angola</option>
+            <option value="ap">Asia/Pacific Region</option>
+            <option value="aq">Antarctica</option>
+            <option value="ar">Argentina</option>
+            <option value="as">American Samoa</option>
+            <option value="at">Austria</option>
+            <option value="au">Australia</option>
+
+            <option value="aw">Aruba</option>
+            <option value="ax">Aland Islands</option>
+            <option value="az">Azerbaijan</option>
+            <option value="ba">Bosnia and Herzegovina</option>
+            <option value="bb">Barbados</option>
+            <option value="bd">Bangladesh</option>
+            <option value="be">Belgium</option>
+            <option value="bf">Burkina Faso</option>
+            <option value="bg">Bulgaria</option>
+            <option value="bh">Bahrain</option>
+            <option value="bi">Burundi</option>
+            <option value="bj">Benin</option>
+            <option value="bm">Bermuda</option>
+            <option value="bn">Brunei Darussalam</option>
+            <option value="bo">Bolivia</option>
+            <option value="br">Brazil</option>
+            <option value="bs">Bahamas</option>
+            <option value="bt">Bhutan</option>
+            <option value="bv">Bouvet Island</option>
+            <option value="bw">Botswana</option>
+            <option value="by">Belarus</option>
+            <option value="bz">Belize</option>
+            <option value="ca">Canada</option>
+            <option value="cc">Cocos (Keeling) Islands</option>
+            <option value="cd">Congo The Democratic Republic of the</option>
+            <option value="cf">Central African Republic</option>
+            <option value="cg">Congo</option>
+            <option value="ch">Switzerland</option>
+            <option value="ci">Cote d'Ivoire</option>
+            <option value="ck">Cook Islands</option>
+            <option value="cl">Chile</option>
+            <option value="cm">Cameroon</option>
+            <option value="cn">China</option>
+            <option value="co">Colombia</option>
+            <option value="cr">Costa Rica</option>
+            <option value="cu">Cuba</option>
+            <option value="cv">Cape Verde</option>
+            <option value="cx">Christmas Island</option>
+            <option value="cy">Cyprus</option>
+            <option value="cz">Czech Republic</option>
+            <option value="de">Germany</option>
+            <option value="dj">Djibouti</option>
+            <option value="dk">Denmark</option>
+            <option value="dm">Dominica</option>
+            <option value="do">Dominican Republic</option>
+            <option value="dz">Algeria</option>
+            <option value="ec">Ecuador</option>
+            <option value="ee">Estonia</option>
+            <option value="eg">Egypt</option>
+            <option value="eh">Western Sahara</option>
+            <option value="er">Eritrea</option>
+            <option value="es">Spain</option>
+            <option value="et">Ethiopia</option>
+            <option value="eu">Europe</option>
+            <option value="fi">Finland</option>
+            <option value="fj">Fiji</option>
+            <option value="fk">Falkland Islands (Malvinas)</option>
+            <option value="fm">Micronesia Federated States of</option>
+            <option value="fo">Faroe Islands</option>
+            <option value="fr">France</option>
+            <option value="ga">Gabon</option>
+            <option value="gb">United Kingdom</option>
+            <option value="gd">Grenada</option>
+            <option value="ge">Georgia</option>
+            <option value="gf">French Guiana</option>
+            <option value="gg">Guernsey</option>
+            <option value="gh">Ghana</option>
+            <option value="gi">Gibraltar</option>
+            <option value="gl">Greenland</option>
+            <option value="gm">Gambia</option>
+            <option value="gn">Guinea</option>
+            <option value="gp">Guadeloupe</option>
+            <option value="gq">Equatorial Guinea</option>
+            <option value="gr">Greece</option>
+            <option value="gs">South Georgia and the South Sandwich Islands</option>
+            <option value="gt">Guatemala</option>
+            <option value="gu">Guam</option>
+            <option value="gw">Guinea-Bissau</option>
+            <option value="gy">Guyana</option>
+            <option value="hk">Hong Kong</option>
+            <option value="hm">Heard Island and McDonald Islands</option>
+            <option value="hn">Honduras</option>
+            <option value="hr">Croatia</option>
+            <option value="ht">Haiti</option>
+            <option value="hu">Hungary</option>
+            <option value="id">Indonesia</option>
+            <option value="ie">Ireland</option>
+            <option value="il">Israel</option>
+            <option value="im">Isle of Man</option>
+            <option value="in">India</option>
+            <option value="io">British Indian Ocean Territory</option>
+            <option value="iq">Iraq</option>
+            <option value="ir">Iran Islamic Republic of</option>
+            <option value="is">Iceland</option>
+            <option value="it">Italy</option>
+            <option value="je">Jersey</option>
+            <option value="jm">Jamaica</option>
+            <option value="jo">Jordan</option>
+            <option value="jp">Japan</option>
+            <option value="ke">Kenya</option>
+            <option value="kg">Kyrgyzstan</option>
+            <option value="kh">Cambodia</option>
+            <option value="ki">Kiribati</option>
+            <option value="km">Comoros</option>
+            <option value="kn">Saint Kitts and Nevis</option>
+            <option value="kp">Korea Democratic People's Republic of</option>
+            <option value="kr">Korea Republic of</option>
+            <option value="kw">Kuwait</option>
+            <option value="ky">Cayman Islands</option>
+            <option value="kz">Kazakhstan</option>
+            <option value="la">Lao People's Democratic Republic</option>
+            <option value="lb">Lebanon</option>
+            <option value="lc">Saint Lucia</option>
+            <option value="li">Liechtenstein</option>
+            <option value="lk">Sri Lanka</option>
+            <option value="lr">Liberia</option>
+            <option value="ls">Lesotho</option>
+            <option value="lt">Lithuania</option>
+            <option value="lu">Luxembourg</option>
+            <option value="lv">Latvia</option>
+            <option value="ly">Libyan Arab Jamahiriya</option>
+            <option value="ma">Morocco</option>
+            <option value="mc">Monaco</option>
+            <option value="md">Moldova Republic of</option>
+            <option value="me">Montenegro</option>
+            <option value="mg">Madagascar</option>
+            <option value="mh">Marshall Islands</option>
+            <option value="mk">Macedonia</option>
+            <option value="ml">Mali</option>
+            <option value="mm">Myanmar</option>
+            <option value="mn">Mongolia</option>
+            <option value="mo">Macao</option>
+            <option value="mp">Northern Mariana Islands</option>
+            <option value="mq">Martinique</option>
+            <option value="mr">Mauritania</option>
+            <option value="ms">Montserrat</option>
+            <option value="mt">Malta</option>
+            <option value="mu">Mauritius</option>
+            <option value="mv">Maldives</option>
+            <option value="mw">Malawi</option>
+            <option value="mx">Mexico</option>
+            <option value="my">Malaysia</option>
+            <option value="mz">Mozambique</option>
+            <option value="na">Namibia</option>
+            <option value="nc">New Caledonia</option>
+            <option value="ne">Niger</option>
+            <option value="nf">Norfolk Island</option>
+            <option value="ng">Nigeria</option>
+            <option value="ni">Nicaragua</option>
+            <option value="nl">Netherlands</option>
+            <option value="no">Norway</option>
+            <option value="np">Nepal</option>
+            <option value="nr">Nauru</option>
+            <option value="nu">Niue</option>
+            <option value="nz">New Zealand</option>
+            <option value="om">Oman</option>
+            <option value="pa">Panama</option>
+            <option value="pe">Peru</option>
+            <option value="pf">French Polynesia</option>
+            <option value="pg">Papua New Guinea</option>
+            <option value="ph">Philippines</option>
+            <option value="pk">Pakistan</option>
+            <option value="pl">Poland</option>
+            <option value="pm">Saint Pierre and Miquelon</option>
+            <option value="pn">Pitcairn</option>
+            <option value="pr">Puerto Rico</option>
+            <option value="ps">Palestinian Territory</option>
+            <option value="pt">Portugal</option>
+            <option value="pw">Palau</option>
+            <option value="py">Paraguay</option>
+            <option value="qa">Qatar</option>
+            <option value="re">Reunion</option>
+            <option value="ro">Romania</option>
+            <option value="rs">Serbia</option>
+            <option value="ru">Russian Federation</option>
+            <option value="rw">Rwanda</option>
+            <option value="sa">Saudi Arabia</option>
+            <option value="sb">Solomon Islands</option>
+            <option value="sc">Seychelles</option>
+            <option value="sd">Sudan</option>
+            <option value="se">Sweden</option>
+            <option value="sg">Singapore</option>
+            <option value="sh">Saint Helena</option>
+            <option value="si">Slovenia</option>
+            <option value="sj">Svalbard and Jan Mayen</option>
+            <option value="sk">Slovakia</option>
+            <option value="sl">Sierra Leone</option>
+            <option value="sm">San Marino</option>
+            <option value="sn">Senegal</option>
+            <option value="so">Somalia</option>
+            <option value="sr">Suriname</option>
+            <option value="st">Sao Tome and Principe</option>
+            <option value="sv">El Salvador</option>
+            <option value="sy">Syrian Arab Republic</option>
+            <option value="sz">Swaziland</option>
+            <option value="tc">Turks and Caicos Islands</option>
+            <option value="td">Chad</option>
+            <option value="tf">French Southern Territories</option>
+            <option value="tg">Togo</option>
+            <option value="th">Thailand</option>
+            <option value="tj">Tajikistan</option>
+            <option value="tk">Tokelau</option>
+            <option value="tl">Timor-Leste</option>
+            <option value="tm">Turkmenistan</option>
+            <option value="tn">Tunisia</option>
+            <option value="to">Tonga</option>
+            <option value="tr">Turkey</option>
+            <option value="tt">Trinidad and Tobago</option>
+            <option value="tv">Tuvalu</option>
+            <option value="tw">Taiwan</option>
+            <option value="tz">Tanzania United Republic of</option>
+            <option value="ua">Ukraine</option>
+            <option value="ug">Uganda</option>
+            <option selected="" value="us">United States</option>
+            <option value="um">United States Minor Outlying Islands</option>
+            <option value="uy">Uruguay</option>
+            <option value="uz">Uzbekistan</option>
+            <option value="va">Holy See (Vatican City State)</option>
+            <option value="vc">Saint Vincent and the Grenadines</option>
+            <option value="ve">Venezuela</option>
+            <option value="vg">Virgin Islands British</option>
+            <option value="vi">Virgin Islands U.S.</option>
+            <option value="vn">Vietnam</option>
+            <option value="vu">Vanuatu</option>
+            <option value="wf">Wallis and Futuna</option>
+            <option value="ws">Samoa</option>
+            <option value="ye">Yemen</option>
+            <option value="yt">Mayotte</option>
+            <option value="za">South Africa</option>
+            <option value="zm">Zambia</option>
+            <option value="zw">Zimbabwe</option>
+          </select>
+
+        </div>
+        <div class="formline">
+          <label for="phone3"><span id="phone3_mobile_phone">Mobile Phone</span><span class="requiredText">*</span></label>
+          <input type="number" pattern="[0-9]*" name="phonenumber" id="phone3" size="35" maxlength="140">
+        </div>
+      </fieldset>
+
+    </div>
+  </div>
+  <div class="pc">
+    <div class="smalltext">Your order may be canceled without notice if you do not use the exact billing address of your credit card.<br>
+
+      <strong>Age Restrictions</strong><br>
+      If you are under 13 years old you cannot provide us with any information about yourself. If you fill in this form, you represent that you are at least 13 years old.
+    </div>
+  </div>
+  <div class="pc">
+    <input type="submit" class="btn btn-primarycta btn-large" value="Save Card">
+    <br><em>You will not be charged</em>
+  </div>
+</form>
diff --git a/chrome/test/data/autofill/heuristics/output/24_checkout_harryanddavid.com.out b/chrome/test/data/autofill/heuristics/output/24_checkout_harryanddavid.com.out
index e4f854ec..610ea83 100644
--- a/chrome/test/data/autofill/heuristics/output/24_checkout_harryanddavid.com.out
+++ b/chrome/test/data/autofill/heuristics/output/24_checkout_harryanddavid.com.out
@@ -24,7 +24,7 @@
 UNKNOWN_TYPE |  | Phone number | Χ  | typeOfAddress_1-default
 PHONE_HOME_WHOLE_NUMBER | phone1 | Phone number |  | typeOfAddress_1-default
 UNKNOWN_TYPE |  | Ext | Χ  | typeOfAddress_1-default
-PHONE_HOME_WHOLE_NUMBER | phone2 | Ext |  | typeOfAddress_1-default
+UNKNOWN_TYPE | phone2 | Ext |  | typeOfAddress_1-default
 UNKNOWN_TYPE | phone1Type | Cell phone | C | typeOfAddress_1-default
 UNKNOWN_TYPE |  | Email address* | Χ  | typeOfAddress_1-default
 EMAIL_ADDRESS | email1 | Email address* |  | typeOfAddress_1-default
diff --git a/chrome/test/data/autofill/heuristics/output/25_checkout_m_llbean.com.out b/chrome/test/data/autofill/heuristics/output/25_checkout_m_llbean.com.out
index 68060b3..86628d8 100644
--- a/chrome/test/data/autofill/heuristics/output/25_checkout_m_llbean.com.out
+++ b/chrome/test/data/autofill/heuristics/output/25_checkout_m_llbean.com.out
@@ -11,23 +11,23 @@
 ADDRESS_HOME_LINE1 | _1_address1 | Address |  | _1_personTitle_1-default
 ADDRESS_HOME_LINE2 | _1_address2 | Address Line 2 (optional) |  | _1_personTitle_1-default
 ADDRESS_HOME_LINE3 | _1_address3 | Address Line 3 (optional) |  | _1_personTitle_1-default
-ADDRESS_HOME_STATE | _1_zipCode | Enter Zip for City and State |  | _1_personTitle_1-default
-ADDRESS_HOME_CITY | _1_city | City |  | _1_personTitle_1-default
-ADDRESS_HOME_STATE | _1_state |  |  | _1_personTitle_1-default
-ADDRESS_HOME_ZIP | _1_CAPostal | Postal Code |  | _1_personTitle_1-default
-ADDRESS_HOME_CITY | _1_CACity | City |  | _1_personTitle_1-default
-ADDRESS_HOME_STATE | _1_CAProvince | Province |  | _1_personTitle_1-default
-ADDRESS_HOME_CITY | _1_INTLCity | City |  | _1_personTitle_1-default
-ADDRESS_HOME_STATE | _1_INTLCounty | County |  | _1_personTitle_1-default
-ADDRESS_HOME_ZIP | _1_INTLPostal | Postal Code |  | _1_personTitle_1-default
-UNKNOWN_TYPE | _1_addressSelect | Home | homeAddress | _1_personTitle_1-default
-UNKNOWN_TYPE | _1_addressSelect | Business | bizAddress | _1_personTitle_1-default
-COMPANY_NAME | _1_otherAddress | Company Name (optional) |  | _1_personTitle_1-default
-PHONE_HOME_WHOLE_NUMBER | _1_phone1 | Daytime Phone Number |  | _1_personTitle_1-default
-PHONE_HOME_WHOLE_NUMBER | _1_intlPhone1Prefix | Daytime Phone Number |  | _1_personTitle_1-default
-PHONE_HOME_WHOLE_NUMBER | _1_intlPhone1 | Daytime Phone Number |  | _1_personTitle_1-default
-UNKNOWN_TYPE | _1_phone1_Loc | Home | H | _1_personTitle_1-default
-UNKNOWN_TYPE | _1_phone1_Loc | Work | B | _1_personTitle_1-default
-UNKNOWN_TYPE | _1_phone1_Loc | Mobile | M | _1_personTitle_1-default
-UNKNOWN_TYPE | _1_ckoutShippingCheck | Include my phone number on shipping forms | ckoutShippingCheck | _1_personTitle_1-default
-UNKNOWN_TYPE | _1_shipLiability | I accept liability for shipping these items uninsured | on | _1_personTitle_1-default
+ADDRESS_HOME_ZIP | _1_zipCode | Enter Zip for City and State |  | _1_zipCode_1-default
+ADDRESS_HOME_CITY | _1_city | City |  | _1_zipCode_1-default
+ADDRESS_HOME_STATE | _1_state |  |  | _1_zipCode_1-default
+ADDRESS_HOME_ZIP | _1_CAPostal | Postal Code |  | _1_zipCode_1-default
+ADDRESS_HOME_CITY | _1_CACity | City |  | _1_zipCode_1-default
+ADDRESS_HOME_STATE | _1_CAProvince | Province |  | _1_zipCode_1-default
+ADDRESS_HOME_CITY | _1_INTLCity | City |  | _1_zipCode_1-default
+ADDRESS_HOME_STATE | _1_INTLCounty | County |  | _1_zipCode_1-default
+ADDRESS_HOME_ZIP | _1_INTLPostal | Postal Code |  | _1_zipCode_1-default
+UNKNOWN_TYPE | _1_addressSelect | Home | homeAddress | _1_zipCode_1-default
+UNKNOWN_TYPE | _1_addressSelect | Business | bizAddress | _1_zipCode_1-default
+COMPANY_NAME | _1_otherAddress | Company Name (optional) |  | _1_zipCode_1-default
+PHONE_HOME_WHOLE_NUMBER | _1_phone1 | Daytime Phone Number |  | _1_zipCode_1-default
+PHONE_HOME_WHOLE_NUMBER | _1_intlPhone1Prefix | Daytime Phone Number |  | _1_zipCode_1-default
+PHONE_HOME_WHOLE_NUMBER | _1_intlPhone1 | Daytime Phone Number |  | _1_zipCode_1-default
+UNKNOWN_TYPE | _1_phone1_Loc | Home | H | _1_zipCode_1-default
+UNKNOWN_TYPE | _1_phone1_Loc | Work | B | _1_zipCode_1-default
+UNKNOWN_TYPE | _1_phone1_Loc | Mobile | M | _1_zipCode_1-default
+UNKNOWN_TYPE | _1_ckoutShippingCheck | Include my phone number on shipping forms | ckoutShippingCheck | _1_zipCode_1-default
+UNKNOWN_TYPE | _1_shipLiability | I accept liability for shipping these items uninsured | on | _1_zipCode_1-default
diff --git a/chrome/test/data/autofill/heuristics/output/bug_463986.out b/chrome/test/data/autofill/heuristics/output/bug_463986.out
index 1d5ed8d9..039977d 100644
--- a/chrome/test/data/autofill/heuristics/output/bug_463986.out
+++ b/chrome/test/data/autofill/heuristics/output/bug_463986.out
@@ -3,8 +3,8 @@
 NAME_LAST | familyName | Family Name |  | salutation_1-default
 COMPANY_NAME | company | Company * |  | salutation_1-default
 ADDRESS_HOME_LINE1 | street | Street |  | salutation_1-default
-ADDRESS_HOME_CITY | zip | Zip and City |  | salutation_1-default
-ADDRESS_HOME_ZIP | city | Zip and City |  | salutation_1-default
+ADDRESS_HOME_ZIP | zip | Zip and City |  | salutation_1-default
+ADDRESS_HOME_CITY | city | Zip and City |  | salutation_1-default
 ADDRESS_HOME_COUNTRY | country | Country | US | salutation_1-default
 ADDRESS_HOME_STATE | state | State | CA | salutation_1-default
 EMAIL_ADDRESS | email | Email |  | salutation_1-default
@@ -18,7 +18,7 @@
 CREDIT_CARD_EXP_MONTH | accountExpiryMonth | Expiration Date | - | salutation_1-cc
 CREDIT_CARD_EXP_4_DIGIT_YEAR | accountExpiryYear | Expiration Date | - | salutation_1-cc
 CREDIT_CARD_VERIFICATION_CODE | accountVerification | Verification Number * |  | salutation_1-cc
-CREDIT_CARD_TYPE | accountBrandDropDown | Card Brand |  | salutation_1-cc
+UNKNOWN_TYPE | accountBrandDropDown | Card Brand |  | salutation_1-default
 CREDIT_CARD_NUMBER | cardNumber | Card Number |  | salutation_1-cc
 CREDIT_CARD_NAME | ccHolder | Card holder |  | salutation_1-cc
 CREDIT_CARD_EXP_MONTH | accountExpiryMonth | Expiration Date | - | salutation_1-cc
diff --git a/chrome/test/data/autofill/heuristics/output/bug_464002.out b/chrome/test/data/autofill/heuristics/output/bug_464002.out
new file mode 100644
index 0000000..0df9a7f1
--- /dev/null
+++ b/chrome/test/data/autofill/heuristics/output/bug_464002.out
@@ -0,0 +1,7 @@
+CREDIT_CARD_NAME | paymentMethodForm.newCreditCardCustomerName | Name on card* |  | paymentMethodForm.newCreditCardCustomerName_1-cc
+CREDIT_CARD_NUMBER | paymentMethodForm.cardNumber | Card number* |  | paymentMethodForm.newCreditCardCustomerName_1-cc
+CREDIT_CARD_VERIFICATION_CODE | paymentMethodForm.cardVerificationNumber | Security code* |  | paymentMethodForm.newCreditCardCustomerName_1-cc
+CREDIT_CARD_EXP_MONTH | paymentMethodForm.expirationMonth | 04 |  | paymentMethodForm.newCreditCardCustomerName_1-cc
+CREDIT_CARD_EXP_4_DIGIT_YEAR | paymentMethodForm.expirationYear | 2018 |  | paymentMethodForm.newCreditCardCustomerName_1-cc
+ADDRESS_HOME_ZIP | paymentMethodForm.zip | Card billing ZIP Code* |  | paymentMethodForm.newCreditCardCustomerName_1-default
+UNKNOWN_TYPE | paymentMethodForm.custCode | Customer code |  | paymentMethodForm.newCreditCardCustomerName_1-default
diff --git a/chrome/test/data/autofill/heuristics/output/bug_469012.out b/chrome/test/data/autofill/heuristics/output/bug_469012.out
new file mode 100644
index 0000000..414b8a53
--- /dev/null
+++ b/chrome/test/data/autofill/heuristics/output/bug_469012.out
@@ -0,0 +1,14 @@
+CREDIT_CARD_TYPE | cardissuer | Card Issuer |  | cardissuer_1-cc
+CREDIT_CARD_NUMBER | cardnumber | Your cardnumber |  | cardissuer_1-cc
+CREDIT_CARD_EXP_MONTH | expire_month | Expiration Date |  | cardissuer_1-cc
+CREDIT_CARD_EXP_4_DIGIT_YEAR | expire_year |  |  | cardissuer_1-cc
+NAME_FIRST | firstname | First Name * |  | cardissuer_1-default
+NAME_LAST | lastname | Last Name * |  | cardissuer_1-default
+ADDRESS_HOME_LINE1 | address1 | Address * |  | cardissuer_1-default
+ADDRESS_HOME_LINE2 | unit | Unit # |  | cardissuer_1-default
+ADDRESS_HOME_LINE3 | address2 | Address Line 2 |  | cardissuer_1-default
+ADDRESS_HOME_CITY | city | City * |  | cardissuer_1-default
+ADDRESS_HOME_STATE | state | State * |  | cardissuer_1-default
+ADDRESS_HOME_ZIP | zip | State * |  | cardissuer_1-default
+ADDRESS_HOME_COUNTRY | country | Country * | us | cardissuer_1-default
+PHONE_HOME_WHOLE_NUMBER | phonenumber | Mobile Phone* |  | cardissuer_1-default
diff --git a/chrome/test/data/chromeos/virtual_keyboard/layout_test.js b/chrome/test/data/chromeos/virtual_keyboard/layout_test.js
index 2a7e12f..69a67c59 100644
--- a/chrome/test/data/chromeos/virtual_keyboard/layout_test.js
+++ b/chrome/test/data/chromeos/virtual_keyboard/layout_test.js
@@ -179,7 +179,8 @@
       var hwtSelectBounds = hwtSelect.getBoundingClientRect();
       assertTrue(hwtSelectBounds.width > 0 && hwtSelectBounds.height > 0,
                  'Expect non-zero size for hwt select button.');
-      onSwitchToKeyset('hwt', function() {
+      // TODO(fengyuan): bring back this handwriting test once found the cause.
+      /*onSwitchToKeyset('hwt', function() {
         var view = getActiveView();
         assertEquals('hwt', view.id, 'Handwriting layout is not active.');
         var hwtCanvasView = view.querySelector('#canvasView');
@@ -196,7 +197,8 @@
         });
         mockTap(backButton);
       });
-      mockTap(hwtSelect);
+      mockTap(hwtSelect);*/
+      testDoneCallback();
     });
   };
   var config = {
diff --git a/chrome/test/data/components/flapper/windows_x64/manifest.json b/chrome/test/data/components/flapper/windows_x64/manifest.json
index d8585a32..bdb2567 100644
--- a/chrome/test/data/components/flapper/windows_x64/manifest.json
+++ b/chrome/test/data/components/flapper/windows_x64/manifest.json
@@ -3,7 +3,7 @@
     "name": "Flapper",
     "version": "11.9.900.170",
     "x-flapper-revision": "1232353",
-    "x-ppapi-arch": "ia32",
+    "x-ppapi-arch": "x64",
     "x-ppapi-os": "win",
     "x-ppapi-required-interfaces": [
         "PPB_AudioConfig;1.1|PPB_AudioConfig;1.0",
diff --git a/chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/manifest.json b/chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/manifest.json
index cf635f2..2642a752 100644
--- a/chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/manifest.json
@@ -17,7 +17,6 @@
   },
   "permissions": [
     "fileManagerPrivate",
-    "fileBrowserHandler",
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
diff --git a/chrome/test/data/extensions/api_test/file_browser/content_checksum_test/manifest.json b/chrome/test/data/extensions/api_test/file_browser/content_checksum_test/manifest.json
index 8d6ec08..bba7f22 100644
--- a/chrome/test/data/extensions/api_test/file_browser/content_checksum_test/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_browser/content_checksum_test/manifest.json
@@ -11,7 +11,6 @@
     }
   },
   "permissions": [
-    "fileBrowserHandler",
     "fileManagerPrivate",
     {
       "fileSystem": ["requestFileSystem"]
diff --git a/chrome/test/data/extensions/api_test/file_browser/drive_search_test/manifest.json b/chrome/test/data/extensions/api_test/file_browser/drive_search_test/manifest.json
index 2c7652f..3536715 100644
--- a/chrome/test/data/extensions/api_test/file_browser/drive_search_test/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_browser/drive_search_test/manifest.json
@@ -11,7 +11,6 @@
     }
   },
   "permissions": [
-    "fileBrowserHandler",
     "fileManagerPrivate",
     {"fileSystem": ["requestFileSystem"]}
   ]
diff --git a/chrome/test/data/extensions/api_test/file_browser/file_watcher_test/manifest.json b/chrome/test/data/extensions/api_test/file_browser/file_watcher_test/manifest.json
index 5951cc4c..88fc5e9 100644
--- a/chrome/test/data/extensions/api_test/file_browser/file_watcher_test/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_browser/file_watcher_test/manifest.json
@@ -11,7 +11,6 @@
     }
   },
   "permissions": [
-    "fileBrowserHandler",
     "fileManagerPrivate",
     {
       "fileSystem": ["requestFileSystem", "write"]
diff --git a/chrome/test/data/extensions/api_test/file_browser/filesystem_operations_test/manifest.json b/chrome/test/data/extensions/api_test/file_browser/filesystem_operations_test/manifest.json
index 5951cc4c..88fc5e9 100644
--- a/chrome/test/data/extensions/api_test/file_browser/filesystem_operations_test/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_browser/filesystem_operations_test/manifest.json
@@ -11,7 +11,6 @@
     }
   },
   "permissions": [
-    "fileBrowserHandler",
     "fileManagerPrivate",
     {
       "fileSystem": ["requestFileSystem", "write"]
diff --git a/chrome/test/data/extensions/api_test/file_browser/handler_test_runner/manifest.json b/chrome/test/data/extensions/api_test/file_browser/handler_test_runner/manifest.json
index 3f82d21..290034c 100644
--- a/chrome/test/data/extensions/api_test/file_browser/handler_test_runner/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_browser/handler_test_runner/manifest.json
@@ -11,7 +11,6 @@
     }
   },
   "permissions": [
-    "fileBrowserHandler",
     "fileManagerPrivate",
     {
       "fileSystem": ["requestFileSystem"]
diff --git a/chrome/test/data/extensions/api_test/file_browser/multi_profile_copy/manifest.json b/chrome/test/data/extensions/api_test/file_browser/multi_profile_copy/manifest.json
index 5951cc4c..88fc5e9 100644
--- a/chrome/test/data/extensions/api_test/file_browser/multi_profile_copy/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_browser/multi_profile_copy/manifest.json
@@ -11,7 +11,6 @@
     }
   },
   "permissions": [
-    "fileBrowserHandler",
     "fileManagerPrivate",
     {
       "fileSystem": ["requestFileSystem", "write"]
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/add_watcher/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/add_watcher/manifest.json
index 26e2c579..2ebef7a3 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/add_watcher/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/add_watcher/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/big_file/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/big_file/manifest.json
index 2edefe5..0368eb6 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/big_file/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/big_file/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/copy_entry/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/copy_entry/manifest.json
index f11b3b21..3c5ce06 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/copy_entry/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/copy_entry/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/create_directory/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/create_directory/manifest.json
index 269aab4e..ea17771 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/create_directory/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/create_directory/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/create_file/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/create_file/manifest.json
index 78e42e93..4fefa4ac 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/create_file/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/create_file/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/delete_entry/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/delete_entry/manifest.json
index 92a1ff3..c8435b2 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/delete_entry/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/delete_entry/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/evil/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/evil/manifest.json
index 5dd53a21..083ea7ab 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/evil/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/evil/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/get_all/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/get_all/manifest.json
index 49643b4..1f3e113 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/get_all/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/get_all/manifest.json
@@ -10,8 +10,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/manifest.json
index 9d51633d..8554830 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/mime_type/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/mime_type/manifest.json
index 8272fa9..6529f3b2 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/mime_type/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/mime_type/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "file_handlers": {
     "magic_handler": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/move_entry/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/move_entry/manifest.json
index f11b3b21..3c5ce06 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/move_entry/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/move_entry/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/notify/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/notify/manifest.json
index 6e9b965f..de6d85b 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/notify/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/notify/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/read_directory/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/read_directory/manifest.json
index 174908a..423816b7 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/read_directory/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/read_directory/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/read_file/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/read_file/manifest.json
index 10e4e018..b664754 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/read_file/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/read_file/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/remove_watcher/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/remove_watcher/manifest.json
index 99bb779..4bf2a8060 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/remove_watcher/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/remove_watcher/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/thumbnail/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/thumbnail/manifest.json
index a574bba5..d90308d 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/thumbnail/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/thumbnail/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/truncate/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/truncate/manifest.json
index b78611e..8f3ce4c 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/truncate/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/truncate/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/write_file/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/write_file/manifest.json
index 693a721..397e2dd7 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/write_file/manifest.json
+++ b/chrome/test/data/extensions/api_test/file_system_provider/write_file/manifest.json
@@ -11,8 +11,7 @@
     {
       "fileSystem": ["requestFileSystem", "write"]
     },
-    "fileManagerPrivate",
-    "fileBrowserHandler"
+    "fileManagerPrivate"
   ],
   "app": {
     "background": {
diff --git a/chrome/test/data/extensions/api_test/networking_private/chromeos/test.js b/chrome/test/data/extensions/api_test/networking_private/chromeos/test.js
index c0550b3a..69afe31a 100644
--- a/chrome/test/data/extensions/api_test/networking_private/chromeos/test.js
+++ b/chrome/test/data/extensions/api_test/networking_private/chromeos/test.js
@@ -139,6 +139,32 @@
           }));
       }));
   },
+  function forgetNetwork() {
+    var kNumNetworks = 2;
+    var kTestNetworkGuid = "stub_wifi1_guid";
+    function guidExists(networks, guid) {
+      for (var n of networks) {
+        if (n.GUID == kTestNetworkGuid)
+          return true;
+      }
+      return false;
+    }
+    chrome.networkingPrivate.getNetworks(
+      { "networkType": "WiFi", "visible": true, "configured": true },
+      callbackPass(function(networks) {
+        assertEq(kNumNetworks, networks.length);
+        assertTrue(guidExists(networks, kTestNetworkGuid));
+        chrome.networkingPrivate.forgetNetwork(
+          kTestNetworkGuid, callbackPass(function() {
+            chrome.networkingPrivate.getNetworks(
+              { "networkType": "WiFi", "visible": true, "configured": true },
+              callbackPass(function(networks) {
+                assertEq(kNumNetworks - 1, networks.length);
+                assertFalse(guidExists(networks, kTestNetworkGuid));
+              }));
+          }));
+      }));
+  },
   function getNetworks() {
     // Test 'type' and 'configured'.
     chrome.networkingPrivate.getNetworks(
diff --git a/chrome/test/data/extensions/api_test/networking_private/test.js b/chrome/test/data/extensions/api_test/networking_private/test.js
index 5254cf07..fc23d63e 100644
--- a/chrome/test/data/extensions/api_test/networking_private/test.js
+++ b/chrome/test/data/extensions/api_test/networking_private/test.js
@@ -51,6 +51,10 @@
     chrome.networkingPrivate.createNetwork(
         false, { 'GUID': kGuid }, callbackPass(callbackResult));
   },
+  function forgetNetwork() {
+    chrome.networkingPrivate.forgetNetwork(
+        kGuid, callbackPass(callbackResult));
+  },
   function getNetworks() {
     chrome.networkingPrivate.getNetworks(
         { networkType: 'Ethernet' }, callbackPass(callbackResult));
diff --git a/chrome/test/data/extensions/api_test/platform_keys/basic.js b/chrome/test/data/extensions/api_test/platform_keys/basic.js
index 8c88d36..c50523a 100644
--- a/chrome/test/data/extensions/api_test/platform_keys/basic.js
+++ b/chrome/test/data/extensions/api_test/platform_keys/basic.js
@@ -19,11 +19,14 @@
 // Each value is the path to a file in this extension's folder that will be
 // loaded and replaced by a Uint8Array in the setUp() function below.
 var data = {
-  // X.509 client certificates in DER encoding.
+  // X.509 client certificate in DER encoding.
+  // Algorithm in SPKI: rsaEncryption.
   // openssl x509 -in net/data/ssl/certificates/client_1.pem -outform DER -out
   //   client_1.der
   client_1: 'client_1.der',
 
+  // X.509 client certificate in DER encoding.
+  // Algorithm in SPKI: rsaEncryption.
   // openssl x509 -in net/data/ssl/certificates/client_2.pem -outform DER -out
   //   client_2.der
   client_2: 'client_2.der',
@@ -267,6 +270,17 @@
   }
 }
 
+function testGetKeyPairRejectsRSAPSS() {
+  var keyParams = {
+    name: 'RSA-PSS',
+    hash: {name: 'SHA-1'}
+  };
+  chrome.platformKeys.getKeyPair(
+      data.client_1.buffer, keyParams,
+      callbackFail(
+          'The requested Algorithm is not permitted by the certificate.'));
+}
+
 function testGetKeyPair() {
   var keyParams = {
     // Algorithm names are case-insensitive.
@@ -390,6 +404,7 @@
       testInteractiveSelectNoCerts,
       testMatchResult,
       testGetKeyPairMissingAlgorithName,
+      testGetKeyPairRejectsRSAPSS,
       testGetKeyPair,
       testSignNoHash,
       testSignSha1Client1,
diff --git a/chrome/test/data/extensions/api_test/stubs_app/manifest.json b/chrome/test/data/extensions/api_test/stubs_app/manifest.json
index bbe2cff..79a32eb 100644
--- a/chrome/test/data/extensions/api_test/stubs_app/manifest.json
+++ b/chrome/test/data/extensions/api_test/stubs_app/manifest.json
@@ -15,7 +15,6 @@
     "clipboardWrite",
     "contextMenus",
     "desktopCapture",
-    "fileBrowserHandler",
     "fileSystem",
     "gcm",
     "geolocation",
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_access_permission.json b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_access_permission.json
index ca3a3e7..7b7add7 100644
--- a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_access_permission.json
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_access_permission.json
@@ -1,6 +1,9 @@
 {
   "name": "test",
   "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
   "file_browser_handlers": [
     {
       "id" : "ID",
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_access_permission_list.json b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_access_permission_list.json
index 6563ebec..b381702 100644
--- a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_access_permission_list.json
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_access_permission_list.json
@@ -1,6 +1,9 @@
 {
   "name": "test",
   "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
   "file_browser_handlers": [
     {
       "id" : "ID",
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_action_id.json b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_action_id.json
index 8dbe069..dbd3ee8 100644
--- a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_action_id.json
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_action_id.json
@@ -1,6 +1,9 @@
 {
   "name": "test",
   "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
   "file_browser_handlers": [
     {
       "default_title" : "Test",
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_action_title.json b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_action_title.json
index d0cf287..aeccf6e0 100644
--- a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_action_title.json
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_action_title.json
@@ -1,6 +1,9 @@
 {
   "name": "test",
   "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
   "file_browser_handlers": [
     {
       "id" : "ID",
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_actions_1.json b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_actions_1.json
index 4138cfdd..9576907 100644
--- a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_actions_1.json
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_actions_1.json
@@ -1,6 +1,9 @@
 {
   "name": "test",
   "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
   "file_browser_handlers": {
   }
 }
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_actions_2.json b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_actions_2.json
index c5c50b1..c3ab66d 100644
--- a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_actions_2.json
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_actions_2.json
@@ -1,6 +1,9 @@
 {
   "name": "test",
   "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
   "file_browser_handlers": [
     "bad"
   ]
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_empty_access_permission_list.json b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_empty_access_permission_list.json
index 2cc9cbb..ecc86d43 100644
--- a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_empty_access_permission_list.json
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_empty_access_permission_list.json
@@ -1,6 +1,9 @@
 {
   "name": "test",
   "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
   "file_browser_handlers": [
     {
       "id" : "ID",
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_1.json b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_1.json
index fd63a021..fb3a155 100644
--- a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_1.json
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_1.json
@@ -1,6 +1,9 @@
 {
   "name": "test",
   "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
   "file_browser_handlers": [
     {
       "id" : "ID",
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_2.json b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_2.json
index a2b3bde0..f70b3d86 100644
--- a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_2.json
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_2.json
@@ -1,6 +1,9 @@
 {
   "name": "test",
   "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
   "file_browser_handlers": [
     {
       "id" : "ID",
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_url.json b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_url.json
index 9c876db..c5b20bb 100644
--- a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_url.json
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_file_filters_url.json
@@ -1,6 +1,9 @@
 {
   "name": "test",
   "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
   "file_browser_handlers": [
     {
       "id" : "ID",
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_value.json b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_value.json
new file mode 100644
index 0000000..f5eb85c
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_invalid_value.json
@@ -0,0 +1,8 @@
+{
+  "name": "test",
+  "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
+  "file_browser_handlers": 123
+}
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_missing_permission.json b/chrome/test/data/extensions/manifest_tests/filebrowser_missing_permission.json
new file mode 100644
index 0000000..86e0384
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_missing_permission.json
@@ -0,0 +1,14 @@
+{
+  "name": "test",
+  "version": "1",
+  "file_browser_handlers": [
+    {
+      "id": "test",
+      "default_title" : "Test",
+      "default_icon" : "icon.png",
+      "file_filters" : [
+        "filesystem:*.txt"
+      ]
+    }
+  ]
+}
diff --git a/chrome/test/data/extensions/manifest_tests/filebrowser_valid.json b/chrome/test/data/extensions/manifest_tests/filebrowser_valid.json
new file mode 100644
index 0000000..ad88ae43
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/filebrowser_valid.json
@@ -0,0 +1,17 @@
+{
+  "name": "test",
+  "version": "1",
+  "permissions": [
+    "fileBrowserHandler"
+  ],
+  "file_browser_handlers": [
+    {
+      "id" : "ID",
+      "default_title" : "Test",
+      "default_icon" : "icon.png",
+      "file_filters" : [
+        "filesystem:*.html"
+      ]
+    }
+  ]
+}
diff --git a/chrome/test/data/extensions/platform_apps/web_view/context_menus/basic/embedder.js b/chrome/test/data/extensions/platform_apps/web_view/context_menus/basic/embedder.js
index 4963ff80..65007ee 100644
--- a/chrome/test/data/extensions/platform_apps/web_view/context_menus/basic/embedder.js
+++ b/chrome/test/data/extensions/platform_apps/web_view/context_menus/basic/embedder.js
@@ -105,6 +105,26 @@
   });
 };
 
+ContextMenuTester.prototype.registerPreventDefault = function() {
+  this.preventDefaultLister_ = function(e) {
+    e.preventDefault();
+    this.proceedTest_('DEFAULT_PREVENTED');
+  }.bind(this);
+
+  this.webview_.contextMenus.onShow.addListener(this.preventDefaultLister_);
+};
+
+ContextMenuTester.prototype.removePreventDefault = function() {
+  if (!this.preventDefaultLister_) {
+    LOG('Error: A listener is expected to setup prior to calling ' +
+        'contextMenus.onShow');
+    return;
+  }
+  this.webview_.contextMenus.onShow.removeListener(this.preventDefaultLister_);
+
+  this.proceedTest_('PREVENT_DEFAULT_LISTENER_REMOVED');
+};
+
 ContextMenuTester.prototype.onClick_ = function(type) {
   if (type == 'global') {
     this.globalClickCalled_ = true;
@@ -146,6 +166,12 @@
     case 'ITEM_ALL_REMOVED':
       document.title = 'ITEM_ALL_REMOVED';
       break;
+    case 'DEFAULT_PREVENTED':
+      chrome.test.sendMessage('WebViewTest.CONTEXT_MENU_DEFAULT_PREVENTED');
+      break;
+    case 'PREVENT_DEFAULT_LISTENER_REMOVED':
+      document.title = 'PREVENT_DEFAULT_LISTENER_REMOVED';
+      break;
     default:
       break;
   }
@@ -187,9 +213,14 @@
   tester.createThreeMenuItems();
 };
 window.removeAllItems = function() {
-  LOG('window.testRemoveAllItems');
   tester.testRemoveAllItems();
 };
+window.registerPreventDefault = function() {
+  tester.registerPreventDefault();
+};
+window.removePreventDefault = function() {
+  tester.removePreventDefault();
+};
 // window.* exported functions end.
 
 function setUpTest(messageCallback) {
diff --git a/chrome/test/data/extensions/platform_apps/web_view/context_menus/basic/guest.js b/chrome/test/data/extensions/platform_apps/web_view/context_menus/basic/guest.js
index 52f0bf1..bb0ffda3 100644
--- a/chrome/test/data/extensions/platform_apps/web_view/context_menus/basic/guest.js
+++ b/chrome/test/data/extensions/platform_apps/web_view/context_menus/basic/guest.js
@@ -25,4 +25,22 @@
     return;
   }
 };
+
+var setup = function() {
+  // Make sure there is always stuff to show in context menu by
+  // inserting an <input> element.
+  // Note that we don't always show "Inspect element", so this is
+  // necessary.
+  var div = document.createElement('div');
+  div.style.position = 'absolute';
+  div.style.top = 0;
+  div.style.left = 0;
+  var input = document.createElement('input');
+  div.appendChild(input);
+  document.body.style.padding = 0;
+  document.body.style.margin = 0;
+  document.body.appendChild(div);
+};
+
+setup();
 window.addEventListener('message', onPostMessageReceived, false);
diff --git a/chrome/test/data/pdf/viewport_test.js b/chrome/test/data/pdf/viewport_test.js
index 2b279a2e..fc232a93 100644
--- a/chrome/test/data/pdf/viewport_test.js
+++ b/chrome/test/data/pdf/viewport_test.js
@@ -109,6 +109,27 @@
     chrome.test.assertEq('200px', mockSizer.style.height);
     chrome.test.assertEq(100, mockWindow.pageXOffset);
     chrome.test.assertEq(100, mockWindow.pageYOffset);
+
+    var documentDimensions = new MockDocumentDimensions(0, 0);
+    documentDimensions.addPage(200, 200);
+    viewport.setDocumentDimensions(documentDimensions);
+    mockWindow.scrollTo(0, 0);
+    viewport.fitToPage();
+    viewport.setZoom(1);
+    chrome.test.assertEq(Viewport.FittingType.NONE, viewport.fittingType);
+    chrome.test.assertEq('200px', mockSizer.style.width);
+    chrome.test.assertEq('200px', mockSizer.style.height);
+    chrome.test.assertEq(0, mockWindow.pageXOffset);
+    chrome.test.assertEq(0, mockWindow.pageYOffset);
+
+    viewport.fitToWidth();
+    viewport.setZoom(1);
+    chrome.test.assertEq(Viewport.FittingType.NONE, viewport.fittingType);
+    chrome.test.assertEq('200px', mockSizer.style.width);
+    chrome.test.assertEq('200px', mockSizer.style.height);
+    chrome.test.assertEq(0, mockWindow.pageXOffset);
+    chrome.test.assertEq(0, mockWindow.pageYOffset);
+
     chrome.test.succeed();
   },
 
@@ -174,6 +195,8 @@
     viewport.setZoom(0.1);
     mockCallback.reset();
     viewport.fitToWidth();
+    chrome.test.assertEq(Viewport.FittingType.FIT_TO_WIDTH,
+                         viewport.fittingType);
     chrome.test.assertTrue(mockCallback.wasCalled);
     chrome.test.assertEq('100px', mockSizer.style.width);
     chrome.test.assertEq(1, viewport.zoom);
@@ -184,6 +207,8 @@
     viewport.setDocumentDimensions(documentDimensions);
     mockCallback.reset();
     viewport.fitToWidth();
+    chrome.test.assertEq(Viewport.FittingType.FIT_TO_WIDTH,
+                         viewport.fittingType);
     chrome.test.assertTrue(mockCallback.wasCalled);
     chrome.test.assertEq('100px', mockSizer.style.width);
     chrome.test.assertEq(0.5, viewport.zoom);
@@ -194,6 +219,8 @@
     viewport.setDocumentDimensions(documentDimensions);
     mockCallback.reset();
     viewport.fitToWidth();
+    chrome.test.assertEq(Viewport.FittingType.FIT_TO_WIDTH,
+                         viewport.fittingType);
     chrome.test.assertTrue(mockCallback.wasCalled);
     chrome.test.assertEq('100px', mockSizer.style.width);
     chrome.test.assertEq(2, viewport.zoom);
@@ -207,6 +234,8 @@
     mockWindow.scrollTo(0, 100);
     mockCallback.reset();
     viewport.fitToWidth();
+    chrome.test.assertEq(Viewport.FittingType.FIT_TO_WIDTH,
+                         viewport.fittingType);
     chrome.test.assertTrue(mockCallback.wasCalled);
     chrome.test.assertEq(2, viewport.zoom);
     chrome.test.assertEq(0, viewport.position.x);
@@ -222,6 +251,8 @@
     viewport.setDocumentDimensions(documentDimensions);
     mockCallback.reset();
     viewport.fitToWidth();
+    chrome.test.assertEq(Viewport.FittingType.FIT_TO_WIDTH,
+                         viewport.fittingType);
     chrome.test.assertTrue(mockCallback.wasCalled);
     chrome.test.assertEq('85px', mockSizer.style.width);
     chrome.test.assertEq(1.7, viewport.zoom);
@@ -242,6 +273,8 @@
     viewport.setZoom(0.1);
     mockCallback.reset();
     viewport.fitToPage();
+    chrome.test.assertEq(Viewport.FittingType.FIT_TO_PAGE,
+                         viewport.fittingType);
     chrome.test.assertTrue(mockCallback.wasCalled);
     chrome.test.assertEq('100px', mockSizer.style.width);
     chrome.test.assertEq('100px', mockSizer.style.height);
@@ -253,6 +286,8 @@
     viewport.setDocumentDimensions(documentDimensions);
     mockCallback.reset();
     viewport.fitToPage();
+    chrome.test.assertEq(Viewport.FittingType.FIT_TO_PAGE,
+                         viewport.fittingType);
     chrome.test.assertTrue(mockCallback.wasCalled);
     chrome.test.assertEq('100px', mockSizer.style.width);
     chrome.test.assertEq('50px', mockSizer.style.height);
@@ -264,6 +299,8 @@
     viewport.setDocumentDimensions(documentDimensions);
     mockCallback.reset();
     viewport.fitToPage();
+    chrome.test.assertEq(Viewport.FittingType.FIT_TO_PAGE,
+                         viewport.fittingType);
     chrome.test.assertTrue(mockCallback.wasCalled);
     chrome.test.assertEq('50px', mockSizer.style.width);
     chrome.test.assertEq('100px', mockSizer.style.height);
@@ -279,6 +316,8 @@
     mockWindow.scrollTo(0, 0);
     mockCallback.reset();
     viewport.fitToPage();
+    chrome.test.assertEq(Viewport.FittingType.FIT_TO_PAGE,
+                         viewport.fittingType);
     chrome.test.assertTrue(mockCallback.wasCalled);
     chrome.test.assertEq('100px', mockSizer.style.width);
     chrome.test.assertEq('250px', mockSizer.style.height);
@@ -300,6 +339,8 @@
     viewport.setZoom(1);
     mockWindow.scrollTo(0, 0);
     viewport.fitToPage();
+    chrome.test.assertEq(Viewport.FittingType.FIT_TO_PAGE,
+                         viewport.fittingType);
     chrome.test.assertEq(0.5, viewport.zoom);
     chrome.test.assertEq(0, viewport.position.x);
     chrome.test.assertEq(0, viewport.position.y);
@@ -315,6 +356,8 @@
     // position).
     mockWindow.scrollTo(0, 0);
     viewport.fitToPage();
+    chrome.test.assertEq(Viewport.FittingType.FIT_TO_PAGE,
+                         viewport.fittingType);
     chrome.test.assertEq(0.5, viewport.zoom);
     mockWindow.scrollTo(0, 10);
     mockWindow.setSize(50, 50);
diff --git a/chrome/test/ext_auto/auto_provider/manifest.json b/chrome/test/ext_auto/auto_provider/manifest.json
index d46db849..134a3fea 100644
--- a/chrome/test/ext_auto/auto_provider/manifest.json
+++ b/chrome/test/ext_auto/auto_provider/manifest.json
@@ -31,7 +31,6 @@
      "declarativeWebRequest",
      "downloads",
      "experimental",
-     "fileBrowserHandler",
      "fileManagerPrivate",
      "fileSystem",
      "fileSystem.write",
diff --git a/chrome/test/telemetry/chromeos/login_unittest.py b/chrome/test/telemetry/chromeos/login_unittest.py
index 8066552..3b3e578a 100644
--- a/chrome/test/telemetry/chromeos/login_unittest.py
+++ b/chrome/test/telemetry/chromeos/login_unittest.py
@@ -6,11 +6,11 @@
 import os
 import unittest
 
+from telemetry.core.backends.chrome import cros_interface
 from telemetry.core import browser_finder
 from telemetry.core import exceptions
 from telemetry.core import extension_to_load
 from telemetry.core import util
-from telemetry.core.backends.chrome import cros_interface
 from telemetry.unittest import options_for_unittests
 
 class CrOSAutoTest(unittest.TestCase):
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 5d5dbfd..1c79cdf4 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -238,20 +238,25 @@
 
 // static
 void ChromeContentUtilityClient::DecodeImageAndSend(
-    const std::vector<unsigned char>& encoded_data, bool shrink_to_fit){
+    const std::vector<unsigned char>& encoded_data,
+    bool shrink_to_fit,
+    int request_id) {
   SkBitmap decoded_image = DecodeImage(encoded_data, shrink_to_fit);
 
   if (decoded_image.empty()) {
-    Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
+    Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id));
   } else {
-    Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(decoded_image));
+    Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(decoded_image,
+                                                        request_id));
   }
   ReleaseProcessIfNeeded();
 }
 
 void ChromeContentUtilityClient::OnDecodeImage(
-    const std::vector<unsigned char>& encoded_data, bool shrink_to_fit) {
-  DecodeImageAndSend(encoded_data, shrink_to_fit);
+    const std::vector<unsigned char>& encoded_data,
+    bool shrink_to_fit,
+    int request_id) {
+  DecodeImageAndSend(encoded_data, shrink_to_fit, request_id);
 }
 
 #if defined(OS_CHROMEOS)
@@ -303,19 +308,21 @@
 #endif  // defined(OS_ANDROID) && defined(USE_SECCOMP_BPF)
 
 void ChromeContentUtilityClient::OnRobustJPEGDecodeImage(
-    const std::vector<unsigned char>& encoded_data) {
+    const std::vector<unsigned char>& encoded_data,
+    int request_id) {
   // Our robust jpeg decoding is using IJG libjpeg.
   if (gfx::JPEGCodec::JpegLibraryVariant() == gfx::JPEGCodec::IJG_LIBJPEG &&
       !encoded_data.empty()) {
     scoped_ptr<SkBitmap> decoded_image(gfx::JPEGCodec::Decode(
         &encoded_data[0], encoded_data.size()));
     if (!decoded_image.get() || decoded_image->empty()) {
-      Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
+      Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id));
     } else {
-      Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(*decoded_image));
+      Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(*decoded_image,
+                                                          request_id));
     }
   } else {
-    Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
+    Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id));
   }
   ReleaseProcessIfNeeded();
 }
diff --git a/chrome/utility/chrome_content_utility_client.h b/chrome/utility/chrome_content_utility_client.h
index f2eee64..8134fe4f 100644
--- a/chrome/utility/chrome_content_utility_client.h
+++ b/chrome/utility/chrome_content_utility_client.h
@@ -36,7 +36,8 @@
   static SkBitmap DecodeImage(const std::vector<unsigned char>& encoded_data,
                               bool shrink_to_fit);
   static void DecodeImageAndSend(const std::vector<unsigned char>& encoded_data,
-                                 bool shrink_to_fit);
+                                 bool shrink_to_fit,
+                                 int request_id);
 
   static void set_max_ipc_message_size_for_test(int64_t max_message_size) {
     max_ipc_message_size_ = max_message_size;
@@ -46,9 +47,10 @@
   // IPC message handlers.
   void OnUnpackWebResource(const std::string& resource_data);
   void OnDecodeImage(const std::vector<unsigned char>& encoded_data,
-                     bool shrink_to_fit);
-  void OnRobustJPEGDecodeImage(
-      const std::vector<unsigned char>& encoded_data);
+                     bool shrink_to_fit,
+                     int request_id);
+  void OnRobustJPEGDecodeImage(const std::vector<unsigned char>& encoded_data,
+                               int request_id);
 
 #if defined(OS_CHROMEOS)
   void OnCreateZipFile(const base::FilePath& src_dir,
diff --git a/chrome/utility/extensions/extensions_handler.cc b/chrome/utility/extensions/extensions_handler.cc
index 72930f55..ca6972d 100644
--- a/chrome/utility/extensions/extensions_handler.cc
+++ b/chrome/utility/extensions/extensions_handler.cc
@@ -80,7 +80,6 @@
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(ExtensionsHandler, message)
     IPC_MESSAGE_HANDLER(ChromeUtilityMsg_UnzipToDir, OnUnzipToDir)
-    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImageBase64, OnDecodeImageBase64)
     IPC_MESSAGE_HANDLER(ChromeUtilityMsg_CheckMediaFile, OnCheckMediaFile)
 #if defined(OS_WIN)
     IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseITunesPrefXml,
@@ -123,23 +122,6 @@
   ReleaseProcessIfNeeded();
 }
 
-void ExtensionsHandler::OnDecodeImageBase64(
-    const std::string& encoded_string) {
-  std::string decoded_string;
-
-  if (!base::Base64Decode(encoded_string, &decoded_string)) {
-    Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
-    return;
-  }
-
-  std::vector<unsigned char> decoded_vector(decoded_string.size());
-  for (size_t i = 0; i < decoded_string.size(); ++i) {
-    decoded_vector[i] = static_cast<unsigned char>(decoded_string[i]);
-  }
-
-  ChromeContentUtilityClient::DecodeImageAndSend(decoded_vector, false);
-}
-
 void ExtensionsHandler::OnCheckMediaFile(
     int64 milliseconds_of_decoding,
     const IPC::PlatformFileForTransit& media_file) {
diff --git a/chrome/utility/extensions/extensions_handler.h b/chrome/utility/extensions/extensions_handler.h
index aeae769..272fee7 100644
--- a/chrome/utility/extensions/extensions_handler.h
+++ b/chrome/utility/extensions/extensions_handler.h
@@ -36,7 +36,6 @@
  private:
   // IPC message handlers.
   void OnUnzipToDir(const base::FilePath& zip_path, const base::FilePath& dir);
-  void OnDecodeImageBase64(const std::string& encoded_data);
   void OnCheckMediaFile(int64 milliseconds_of_decoding,
                         const IPC::PlatformFileForTransit& media_file);
 
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index f1dfbe3..7c64a67 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -4,8 +4,10 @@
 
 #include "chromecast/browser/cast_browser_main_parts.h"
 
+#if !defined(OS_ANDROID)
 #include <signal.h>
 #include <sys/prctl.h>
+#endif
 
 #include "base/command_line.h"
 #include "base/files/file_util.h"
@@ -40,6 +42,11 @@
 #include "net/android/network_change_notifier_factory_android.h"
 #endif
 
+#if defined(USE_AURA)
+#include "ui/aura/test/test_screen.h"
+#include "ui/gfx/screen.h"
+#endif
+
 namespace {
 
 #if !defined(OS_ANDROID)
@@ -81,6 +88,49 @@
   // Get the first signal to exit when the parent process dies.
   prctl(PR_SET_PDEATHSIG, kSignalsToRunClosure[0]);
 }
+
+const int kKillOnAlarmTimeoutSec = 5;  // 5 seconds
+
+void KillOnAlarm(int signum) {
+  LOG(ERROR) << "Got alarm signal for termination: " << signum;
+  raise(SIGKILL);
+}
+
+void RegisterKillOnAlarm(int timeout_seconds) {
+  struct sigaction sa_new;
+  memset(&sa_new, 0, sizeof(sa_new));
+  sa_new.sa_handler = KillOnAlarm;
+  sigfillset(&sa_new.sa_mask);
+  sa_new.sa_flags = SA_RESTART;
+
+  struct sigaction sa_old;
+  if (sigaction(SIGALRM, &sa_new, &sa_old) == -1) {
+    NOTREACHED();
+  } else {
+    DCHECK_EQ(sa_old.sa_handler, SIG_DFL);
+  }
+
+  if (alarm(timeout_seconds) > 0)
+    NOTREACHED() << "Previous alarm() was cancelled";
+}
+
+void DeregisterKillOnAlarm() {
+  // Explicitly cancel any outstanding alarm() calls.
+  alarm(0);
+
+  struct sigaction sa_new;
+  memset(&sa_new, 0, sizeof(sa_new));
+  sa_new.sa_handler = SIG_DFL;
+  sigfillset(&sa_new.sa_mask);
+  sa_new.sa_flags = SA_RESTART;
+
+  struct sigaction sa_old;
+  if (sigaction(SIGALRM, &sa_new, &sa_old) == -1) {
+    NOTREACHED();
+  } else {
+    DCHECK_EQ(sa_old.sa_handler, KillOnAlarm);
+  }
+}
 #endif  // !defined(OS_ANDROID)
 
 }  // namespace
@@ -194,6 +244,16 @@
   if (!base::CreateDirectory(home_dir))
     return 1;
 #endif
+
+#if defined(USE_AURA)
+  // Screen can (and should) exist even with no displays connected. Its presence
+  // is assumed as an interface to access display information, e.g. from metrics
+  // code.  See CastContentWindow::CreateWindowTree for update when resolution
+  // is available.
+  DCHECK(!gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE));
+  gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE,
+                                 aura::TestScreen::Create(gfx::Size(0, 0)));
+#endif
   return 0;
 }
 
@@ -266,6 +326,12 @@
 
   run_loop.Run();
 
+  // Once the main loop has stopped running, we give the browser process a few
+  // seconds to stop cast service and finalize all resources. If a hang occurs
+  // and cast services refuse to terminate successfully, then we SIGKILL the
+  // current process to avoid indefinte hangs.
+  RegisterKillOnAlarm(kKillOnAlarmTimeoutSec);
+
   cast_browser_process_->cast_service()->Stop();
   return true;
 #endif
@@ -279,6 +345,7 @@
   cast_browser_process_->cast_service()->Finalize();
   cast_browser_process_->metrics_service_client()->Finalize();
   cast_browser_process_.reset();
+  DeregisterKillOnAlarm();
 #endif
 }
 
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 09043fe..e0ef086 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -253,6 +253,26 @@
   }
 }
 
+void CastContentBrowserClient::RequestPermission(
+    content::PermissionType permission,
+    content::WebContents* web_contents,
+    int bridge_id,
+    const GURL& requesting_frame,
+    bool user_gesture,
+    const base::Callback<void(content::PermissionStatus)>& callback) {
+  LOG(INFO) << __FUNCTION__ << ": " << static_cast<int>(permission);
+  callback.Run(content::PermissionStatus::PERMISSION_STATUS_GRANTED);
+}
+
+content::PermissionStatus CastContentBrowserClient::GetPermissionStatus(
+    content::PermissionType permission,
+    content::BrowserContext* browser_context,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin) {
+  LOG(INFO) << __FUNCTION__ << ": " << static_cast<int>(permission);
+  return content::PermissionStatus::PERMISSION_STATUS_GRANTED;
+}
+
 bool CastContentBrowserClient::CanCreateWindow(
     const GURL& opener_url,
     const GURL& opener_top_level_frame_url,
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index 41846746..5e395f1 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -71,6 +71,18 @@
       content::WebContents* web_contents,
       net::SSLCertRequestInfo* cert_request_info,
       scoped_ptr<content::ClientCertificateDelegate> delegate) override;
+  void RequestPermission(
+      content::PermissionType permission,
+      content::WebContents* web_contents,
+      int bridge_id,
+      const GURL& requesting_frame,
+      bool user_gesture,
+      const base::Callback<void(content::PermissionStatus)>& callback) override;
+  content::PermissionStatus GetPermissionStatus(
+      content::PermissionType permission,
+      content::BrowserContext* browser_context,
+      const GURL& requesting_origin,
+      const GURL& embedding_origin) override;
   bool CanCreateWindow(
       const GURL& opener_url,
       const GURL& opener_top_level_frame_url,
diff --git a/chromecast/browser/cast_content_window.cc b/chromecast/browser/cast_content_window.cc
index b813e91..27c91b74 100644
--- a/chromecast/browser/cast_content_window.cc
+++ b/chromecast/browser/cast_content_window.cc
@@ -119,4 +119,12 @@
   metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstPaint();
 }
 
+void CastContentWindow::MediaPaused() {
+  metrics::CastMetricsHelper::GetInstance()->LogMediaPause();
+}
+
+void CastContentWindow::MediaStartedPlaying() {
+  metrics::CastMetricsHelper::GetInstance()->LogMediaPlay();
+}
+
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_content_window.h b/chromecast/browser/cast_content_window.h
index 8c6f6e0..1360a5e 100644
--- a/chromecast/browser/cast_content_window.h
+++ b/chromecast/browser/cast_content_window.h
@@ -44,6 +44,8 @@
 
   // content::WebContentsObserver implementation:
   void DidFirstVisuallyNonEmptyPaint() override;
+  void MediaPaused() override;
+  void MediaStartedPlaying() override;
 
  private:
 #if defined(USE_AURA)
diff --git a/chromecast/browser/cast_http_user_agent_settings.cc b/chromecast/browser/cast_http_user_agent_settings.cc
index c8bffe52..ef752dc 100644
--- a/chromecast/browser/cast_http_user_agent_settings.cc
+++ b/chromecast/browser/cast_http_user_agent_settings.cc
@@ -20,11 +20,11 @@
 namespace shell {
 
 CastHttpUserAgentSettings::CastHttpUserAgentSettings() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 }
 
 CastHttpUserAgentSettings::~CastHttpUserAgentSettings() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 }
 
 std::string CastHttpUserAgentSettings::GetAcceptLanguage() const {
diff --git a/chromecast/browser/devtools/remote_debugging_server.cc b/chromecast/browser/devtools/remote_debugging_server.cc
index 0591644..29aa7d89 100644
--- a/chromecast/browser/devtools/remote_debugging_server.cc
+++ b/chromecast/browser/devtools/remote_debugging_server.cc
@@ -110,7 +110,7 @@
 }  // namespace
 
 RemoteDebuggingServer::RemoteDebuggingServer() : port_(0) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   pref_port_.Init(prefs::kRemoteDebuggingPort,
                   CastBrowserProcess::GetInstance()->pref_service(),
                   base::Bind(&RemoteDebuggingServer::OnPortChanged,
diff --git a/chromecast/browser/media/cma_message_filter_host.cc b/chromecast/browser/media/cma_message_filter_host.cc
index 246c034..21ad293 100644
--- a/chromecast/browser/media/cma_message_filter_host.cc
+++ b/chromecast/browser/media/cma_message_filter_host.cc
@@ -96,7 +96,7 @@
 }
 
 void CmaMessageFilterHost::DeleteEntries() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   for (MediaPipelineMap::iterator it = media_pipelines_.begin();
        it != media_pipelines_.end(); ) {
@@ -119,7 +119,7 @@
 // *** Handle incoming messages ***
 
 void CmaMessageFilterHost::CreateMedia(int media_id, LoadType load_type) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   scoped_ptr<MediaPipelineHost> media_pipeline_host(new MediaPipelineHost());
   MediaPipelineClient client;
@@ -144,7 +144,7 @@
 }
 
 void CmaMessageFilterHost::DestroyMedia(int media_id) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   MediaPipelineMap::iterator it = media_pipelines_.find(media_id);
   if (it == media_pipelines_.end())
@@ -160,7 +160,7 @@
 void CmaMessageFilterHost::SetCdm(int media_id,
                                   int render_frame_id,
                                   int cdm_id) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   MediaPipelineHost* media_pipeline = LookupById(media_id);
   if (!media_pipeline)
     return;
@@ -195,7 +195,7 @@
 
 void CmaMessageFilterHost::CreateAvPipe(
     int media_id, TrackId track_id, size_t shared_mem_size) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   base::FileDescriptor foreign_socket_handle;
   base::SharedMemoryHandle foreign_memory_handle;
@@ -275,7 +275,7 @@
 
 void CmaMessageFilterHost::AudioInitialize(
     int media_id, TrackId track_id, const ::media::AudioDecoderConfig& config) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   MediaPipelineHost* media_pipeline = LookupById(media_id);
   if (!media_pipeline) {
     Send(new CmaMsg_TrackStateChanged(
@@ -304,7 +304,7 @@
 
 void CmaMessageFilterHost::VideoInitialize(
     int media_id, TrackId track_id, const ::media::VideoDecoderConfig& config) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   MediaPipelineHost* media_pipeline = LookupById(media_id);
   if (!media_pipeline) {
     Send(new CmaMsg_TrackStateChanged(
@@ -338,7 +338,7 @@
 
 void CmaMessageFilterHost::StartPlayingFrom(
     int media_id, base::TimeDelta time) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   MediaPipelineHost* media_pipeline = LookupById(media_id);
   if (!media_pipeline)
     return;
@@ -346,7 +346,7 @@
 }
 
 void CmaMessageFilterHost::Flush(int media_id) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   MediaPipelineHost* media_pipeline = LookupById(media_id);
   if (!media_pipeline) {
     Send(new CmaMsg_MediaStateChanged(
@@ -360,7 +360,7 @@
 }
 
 void CmaMessageFilterHost::Stop(int media_id) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   MediaPipelineHost* media_pipeline = LookupById(media_id);
   if (!media_pipeline)
     return;
@@ -372,7 +372,7 @@
 
 void CmaMessageFilterHost::SetPlaybackRate(
     int media_id, float playback_rate) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   MediaPipelineHost* media_pipeline = LookupById(media_id);
   if (!media_pipeline)
     return;
@@ -381,7 +381,7 @@
 
 void CmaMessageFilterHost::SetVolume(
     int media_id, TrackId track_id, float volume) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   MediaPipelineHost* media_pipeline = LookupById(media_id);
   if (!media_pipeline)
     return;
@@ -389,7 +389,7 @@
 }
 
 void CmaMessageFilterHost::NotifyPipeWrite(int media_id, TrackId track_id) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   MediaPipelineHost* media_pipeline = LookupById(media_id);
   if (!media_pipeline)
     return;
@@ -400,7 +400,7 @@
     int surface_id,
     const gfx::PointF& p0, const gfx::PointF& p1,
     const gfx::PointF& p2, const gfx::PointF& p3) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&UpdateVideoSurfaceHost, surface_id,
@@ -411,18 +411,18 @@
 
 void CmaMessageFilterHost::OnMediaStateChanged(
     int media_id, ::media::PipelineStatus status) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   Send(new CmaMsg_MediaStateChanged(media_id, status));
 }
 
 void CmaMessageFilterHost::OnTrackStateChanged(
     int media_id, TrackId track_id, ::media::PipelineStatus status) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   Send(new CmaMsg_TrackStateChanged(media_id, track_id, status));
 }
 
 void CmaMessageFilterHost::OnPipeReadActivity(int media_id, TrackId track_id) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   Send(new CmaMsg_NotifyPipeRead(media_id, track_id));
 }
 
@@ -431,37 +431,37 @@
     base::TimeDelta media_time,
     base::TimeDelta max_media_time,
     base::TimeTicks stc) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   Send(new CmaMsg_TimeUpdate(media_id,
                              media_time, max_media_time, stc));
 }
 
 void CmaMessageFilterHost::OnBufferingNotification(
     int media_id, ::media::BufferingState state) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   Send(new CmaMsg_BufferingNotification(media_id, state));
 }
 
 void CmaMessageFilterHost::OnEos(int media_id, TrackId track_id) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   Send(new CmaMsg_Eos(media_id, track_id));
 }
 
 void CmaMessageFilterHost::OnPlaybackError(
     int media_id, TrackId track_id, ::media::PipelineStatus status) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   Send(new CmaMsg_PlaybackError(media_id, track_id, status));
 }
 
 void CmaMessageFilterHost::OnStatisticsUpdated(
     int media_id, TrackId track_id, const ::media::PipelineStatistics& stats) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   Send(new CmaMsg_PlaybackStatistics(media_id, track_id, stats));
 }
 
 void CmaMessageFilterHost::OnNaturalSizeChanged(
     int media_id, TrackId track_id, const gfx::Size& size) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   Send(new CmaMsg_NaturalSizeChanged(media_id, track_id, size));
 }
 
diff --git a/chromecast/browser/url_request_context_factory.cc b/chromecast/browser/url_request_context_factory.cc
index 4a25eb6..57091c7 100644
--- a/chromecast/browser/url_request_context_factory.cc
+++ b/chromecast/browser/url_request_context_factory.cc
@@ -143,7 +143,7 @@
 }
 
 void URLRequestContextFactory::InitializeOnUIThread() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // Cast http user agent settings must be initialized in UI thread
   // because it registers itself to pref notification observer which is not
   // thread safe.
@@ -297,7 +297,7 @@
 }
 
 net::URLRequestContext* URLRequestContextFactory::CreateSystemRequestContext() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   InitializeSystemContextDependencies();
   net::HttpNetworkSession::Params system_params;
   PopulateNetworkSessionParams(false, &system_params);
@@ -329,7 +329,7 @@
 }
 
 net::URLRequestContext* URLRequestContextFactory::CreateMediaRequestContext() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(main_getter_.get())
       << "Getting MediaRequestContext before MainRequestContext";
   net::URLRequestContext* main_context = main_getter_->GetURLRequestContext();
@@ -351,7 +351,7 @@
     content::BrowserContext* browser_context,
     content::ProtocolHandlerMap* protocol_handlers,
     content::URLRequestInterceptorScopedVector request_interceptors) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   InitializeSystemContextDependencies();
 
   bool ignore_certificate_errors = false;
diff --git a/chromecast/chromecast.gyp b/chromecast/chromecast.gyp
index 6543a00..32ceb8bf 100644
--- a/chromecast/chromecast.gyp
+++ b/chromecast/chromecast.gyp
@@ -26,6 +26,14 @@
     ],
   },
   'targets': [
+    # Public API target for OEM partners to replace shlibs.
+    {
+      'target_name': 'cast_public_api',
+      'type': '<(component)',
+      'sources': [
+        'public/chromecast_export.h',
+      ],
+    },
     # TODO(gunsch): Remove this fake target once it's either added or no
     # longer referenced from internal code.
     {'target_name': 'cast_media_audio', 'type': 'none'},
@@ -114,7 +122,7 @@
           'action_name': 'repack_cast_shell_pak',
           'variables': {
             'pak_inputs': [
-              '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources.pak',
+              '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources_100_percent.pak',
               '<(SHARED_INTERMEDIATE_DIR)/chromecast/shell_resources.pak',
               '<(SHARED_INTERMEDIATE_DIR)/content/content_resources.pak',
               '<(SHARED_INTERMEDIATE_DIR)/content/app/resources/content_resources_100_percent.pak',
diff --git a/chromecast/chromecast_tests.gypi b/chromecast/chromecast_tests.gypi
index 4551b2a..2233fe1 100644
--- a/chromecast/chromecast_tests.gypi
+++ b/chromecast/chromecast_tests.gypi
@@ -47,7 +47,8 @@
               # readonly certdb (b/8153161)
               # URLRequestTestHTTP.GetTest_ManyCookies takes roughly 55s to run. Increase
               # timeout to 75s from 45s to allow it to pass (b/19821476)
-             'net_unittests --gtest_filter=-KeygenHandlerTest.SmokeTest:KeygenHandlerTest.ConcurrencyTest --test-launcher-timeout=75000',
+              # ProxyScriptFetcherImplTest.HttpMimeType is flaking (b/19848784)
+             'net_unittests --gtest_filter=-KeygenHandlerTest.SmokeTest:KeygenHandlerTest.ConcurrencyTest:ProxyScriptFetcherImplTest.HttpMimeType --test-launcher-timeout=75000',
               # Disable OutOfMemoryDeathTest.ViaSharedLibraries due to gTrusty eglibc incompatibility (crbug/428211)
               # Disable ProcessMetricsTest.GetNumberOfThreads (b/15610509)
               # Disable ProcessUtilTest.* (need to define OS_ANDROID)
diff --git a/chromecast/net/connectivity_checker.cc b/chromecast/net/connectivity_checker.cc
index e6e1b409..de30812 100644
--- a/chromecast/net/connectivity_checker.cc
+++ b/chromecast/net/connectivity_checker.cc
@@ -105,6 +105,11 @@
   }
   DCHECK(url_request_context_.get());
 
+  // Don't check connectivity if network is offline, because internet could be
+  // accessible via netifs ignored.
+  if (net::NetworkChangeNotifier::IsOffline())
+    return;
+
   // If url_request_ is non-null, there is already a check going on. Don't
   // start another.
   if (url_request_.get())
diff --git a/chromecast/public/chromecast_export.h b/chromecast/public/chromecast_export.h
new file mode 100644
index 0000000..022ffc7
--- /dev/null
+++ b/chromecast/public/chromecast_export.h
@@ -0,0 +1,12 @@
+// 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 CHROMECAST_PUBLIC_CHROMECAST_EXPORT_H_
+#define CHROMECAST_PUBLIC_CHROMECAST_EXPORT_H_
+
+// Export attribute for classes that are exposed in shared libraries,
+// allowing OEM partners to replace with their own implementations.
+#define CHROMECAST_EXPORT __attribute__((visibility("default")))
+
+#endif  // CHROMECAST_PUBLIC_CHROMECAST_EXPORT_H_
diff --git a/chromecast/renderer/key_systems_cast.cc b/chromecast/renderer/key_systems_cast.cc
index 246d6ade..cdb8c9c7f 100644
--- a/chromecast/renderer/key_systems_cast.cc
+++ b/chromecast/renderer/key_systems_cast.cc
@@ -22,9 +22,11 @@
     std::vector<::media::KeySystemInfo>* key_systems_info) {
   ::media::KeySystemInfo info;
   info.key_system = key_system_name;
+  info.supported_init_data_types = ::media::kInitDataTypeMaskCenc;
   info.supported_codecs =
       ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1;
-  info.supported_init_data_types = ::media::EME_INIT_DATA_TYPE_CENC;
+  info.max_audio_robustness = ::media::EmeRobustness::EMPTY;
+  info.max_video_robustness = ::media::EmeRobustness::EMPTY;
   info.persistent_license_support = ::media::EME_SESSION_TYPE_NOT_SUPPORTED;
   info.persistent_release_message_support =
       ::media::EME_SESSION_TYPE_NOT_SUPPORTED;
@@ -39,8 +41,10 @@
   AddWidevineWithCodecs(
       cdm::WIDEVINE,
       ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1,
-      ::media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent license.
-      ::media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent release message.
+      ::media::EmeRobustness::HW_SECURE_ALL,    // Max audio robustness.
+      ::media::EmeRobustness::HW_SECURE_ALL,    // Max video robustness.
+      ::media::EME_SESSION_TYPE_NOT_SUPPORTED,  // persistent-license.
+      ::media::EME_SESSION_TYPE_NOT_SUPPORTED,  // persistent-release-message.
       ::media::EME_FEATURE_NOT_SUPPORTED,       // Persistent state.
       ::media::EME_FEATURE_ALWAYS_ENABLED,      // Distinctive identifier.
       key_systems_info);
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 1dab9bf..8678dcfe 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-6906.0.0
\ No newline at end of file
+6915.0.0
\ No newline at end of file
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index ffb83b9..4c7ea09 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -116,8 +116,8 @@
 // chrome://settings/languages.
 const char kEnableNewKoreanIme[] = "enable-new-korean-ime";
 
-// If this switch is set, the input view keyboard will be in materia design.
-const char kEnableNewMDInputView[] = "enable-new-md-input-view";
+// If this switch is set, the input view keyboard will disable materia design.
+const char kDisableNewMDInputView[] = "disable-new-md-input-view";
 
 // If this switch is set, the options for suggestions as typing on physical
 // keyboard will be enabled.
@@ -325,6 +325,9 @@
 const char kDisableTimeZoneTrackingOption[] =
     "disable-timezone-tracking-option";
 
+// Enable OAuth token validation on sign in screen.
+const char kEnableOAuthTokenHandlers[] = "enable-oauth-token-handlers";
+
 // Disable new GAIA sign-in flow.
 const char kDisableWebviewSigninFlow[] = "disable-webview-signin-flow";
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 6b381a2..1c21f0cb 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -46,6 +46,7 @@
 CHROMEOS_EXPORT extern const char kDisableNetworkPortalNotification[];
 CHROMEOS_EXPORT extern const char kDisableNewChannelSwitcherUI[];
 CHROMEOS_EXPORT extern const char kDisableNewKioskUI[];
+CHROMEOS_EXPORT extern const char kDisableNewMDInputView[];
 CHROMEOS_EXPORT extern const char kDisableNewZIPUnpacker[];
 CHROMEOS_EXPORT extern const char kDisableOfficeEditingComponentApp[];
 CHROMEOS_EXPORT extern const char kDisablePhysicalKeyboardAutocorrect[];
@@ -63,7 +64,6 @@
 CHROMEOS_EXPORT extern const char kEnableKioskMode[];
 CHROMEOS_EXPORT extern const char kEnableNetworkPortalNotification[];
 CHROMEOS_EXPORT extern const char kEnableNewKoreanIme[];
-CHROMEOS_EXPORT extern const char kEnableNewMDInputView[];
 CHROMEOS_EXPORT extern const char kEnablePhysicalKeyboardAutocorrect[];
 CHROMEOS_EXPORT extern const char kEnableRequestTabletSite[];
 CHROMEOS_EXPORT extern const char kEnableScreenshotTestingWithMode[];
@@ -108,6 +108,7 @@
 CHROMEOS_EXPORT extern const char kWakeOnPackets[];
 CHROMEOS_EXPORT extern const char kEnableCaptivePortalBypassProxyOption[];
 CHROMEOS_EXPORT extern const char kDisableTimeZoneTrackingOption[];
+CHROMEOS_EXPORT extern const char kEnableOAuthTokenHandlers[];
 CHROMEOS_EXPORT extern const char kDisableWebviewSigninFlow[];
 
 CHROMEOS_EXPORT bool WakeOnWifiEnabled();
diff --git a/chromeos/dbus/fake_bluetooth_media_transport_client.cc b/chromeos/dbus/fake_bluetooth_media_transport_client.cc
index 93ee315..441a66b 100644
--- a/chromeos/dbus/fake_bluetooth_media_transport_client.cc
+++ b/chromeos/dbus/fake_bluetooth_media_transport_client.cc
@@ -30,8 +30,6 @@
 const char kNotAvailable[] = "org.bluez.NotAvailable";
 
 const int kInvalidFd = -1;
-const uint16_t kReadMtu = 20;
-const uint16_t kWriteMtu = 25;
 
 ObjectPath GenerateTransportPath() {
   static unsigned int sequence_number = 0;
@@ -56,6 +54,8 @@
         0x21, 0x15, 0x33, 0x2C};
 const uint16_t FakeBluetoothMediaTransportClient::kTransportDelay = 5;
 const uint16_t FakeBluetoothMediaTransportClient::kTransportVolume = 50;
+const uint16_t FakeBluetoothMediaTransportClient::kDefaultReadMtu = 20;
+const uint16_t FakeBluetoothMediaTransportClient::kDefaultWriteMtu = 25;
 
 FakeBluetoothMediaTransportClient::Properties::Properties(
     const PropertyChangedCallback& callback)
@@ -319,7 +319,7 @@
   transport->input_fd.reset(new base::File(fds[0]));
 
   dbus::FileDescriptor out_fd(fds[1]);
-  callback.Run(&out_fd, kReadMtu, kWriteMtu);
+  callback.Run(&out_fd, kDefaultReadMtu, kDefaultWriteMtu);
   SetState(endpoint_path, "active");
 }
 
diff --git a/chromeos/dbus/fake_bluetooth_media_transport_client.h b/chromeos/dbus/fake_bluetooth_media_transport_client.h
index eebbea8..997d28d 100644
--- a/chromeos/dbus/fake_bluetooth_media_transport_client.h
+++ b/chromeos/dbus/fake_bluetooth_media_transport_client.h
@@ -45,6 +45,10 @@
   static const uint16_t kTransportDelay;
   static const uint16_t kTransportVolume;
 
+  // The default MTUs for read and write.
+  static const uint16_t kDefaultReadMtu;
+  static const uint16_t kDefaultWriteMtu;
+
   FakeBluetoothMediaTransportClient();
   ~FakeBluetoothMediaTransportClient() override;
 
diff --git a/chromeos/network/onc/onc_signature.cc b/chromeos/network/onc/onc_signature.cc
index ad05682..568d150 100644
--- a/chromeos/network/onc/onc_signature.cc
+++ b/chromeos/network/onc/onc_signature.cc
@@ -13,469 +13,478 @@
 namespace onc {
 namespace {
 
-const OncValueSignature kBoolSignature = {
-  base::Value::TYPE_BOOLEAN, NULL
-};
-const OncValueSignature kStringSignature = {
-  base::Value::TYPE_STRING, NULL
-};
-const OncValueSignature kIntegerSignature = {
-  base::Value::TYPE_INTEGER, NULL
-};
-const OncValueSignature kDoubleSignature = {
-  base::Value::TYPE_DOUBLE, NULL
-};
-const OncValueSignature kStringListSignature = {
-  base::Value::TYPE_LIST, NULL, &kStringSignature
-};
-const OncValueSignature kIntegerListSignature = {
-  base::Value::TYPE_LIST, NULL, &kIntegerSignature
-};
-const OncValueSignature kIPConfigListSignature = {
-  base::Value::TYPE_LIST, NULL, &kIPConfigSignature
-};
-const OncValueSignature kCellularApnListSignature = {
-  base::Value::TYPE_LIST, NULL, &kCellularApnSignature
-};
+const OncValueSignature kBoolSignature = {base::Value::TYPE_BOOLEAN, NULL};
+const OncValueSignature kStringSignature = {base::Value::TYPE_STRING, NULL};
+const OncValueSignature kIntegerSignature = {base::Value::TYPE_INTEGER, NULL};
+const OncValueSignature kDoubleSignature = {base::Value::TYPE_DOUBLE, NULL};
+const OncValueSignature kStringListSignature = {base::Value::TYPE_LIST,
+                                                NULL,
+                                                &kStringSignature};
+const OncValueSignature kIntegerListSignature = {base::Value::TYPE_LIST,
+                                                 NULL,
+                                                 &kIntegerSignature};
+const OncValueSignature kIPConfigListSignature = {base::Value::TYPE_LIST,
+                                                  NULL,
+                                                  &kIPConfigSignature};
+const OncValueSignature kCellularApnListSignature = {base::Value::TYPE_LIST,
+                                                     NULL,
+                                                     &kCellularApnSignature};
 const OncValueSignature kCellularFoundNetworkListSignature = {
-  base::Value::TYPE_LIST, NULL, &kCellularFoundNetworkSignature
-};
+    base::Value::TYPE_LIST,
+    NULL,
+    &kCellularFoundNetworkSignature};
 
 const OncFieldSignature issuer_subject_pattern_fields[] = {
-    { ::onc::client_cert::kCommonName, &kStringSignature},
-    { ::onc::client_cert::kLocality, &kStringSignature},
-    { ::onc::client_cert::kOrganization, &kStringSignature},
-    { ::onc::client_cert::kOrganizationalUnit, &kStringSignature},
+    {::onc::client_cert::kCommonName, &kStringSignature},
+    {::onc::client_cert::kLocality, &kStringSignature},
+    {::onc::client_cert::kOrganization, &kStringSignature},
+    {::onc::client_cert::kOrganizationalUnit, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature certificate_pattern_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::client_cert::kEnrollmentURI, &kStringListSignature},
-    { ::onc::client_cert::kIssuer, &kIssuerSubjectPatternSignature},
-    { ::onc::client_cert::kIssuerCARef, &kStringListSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::client_cert::kEnrollmentURI, &kStringListSignature},
+    {::onc::client_cert::kIssuer, &kIssuerSubjectPatternSignature},
+    {::onc::client_cert::kIssuerCARef, &kStringListSignature},
     // Used internally. Not officially supported.
-    { ::onc::client_cert::kIssuerCAPEMs, &kStringListSignature},
-    { ::onc::client_cert::kSubject, &kIssuerSubjectPatternSignature},
+    {::onc::client_cert::kIssuerCAPEMs, &kStringListSignature},
+    {::onc::client_cert::kSubject, &kIssuerSubjectPatternSignature},
     {NULL}};
 
 const OncFieldSignature eap_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::eap::kAnonymousIdentity, &kStringSignature},
-    { ::onc::client_cert::kClientCertPattern, &kCertificatePatternSignature},
-    { ::onc::client_cert::kClientCertRef, &kStringSignature},
-    { ::onc::client_cert::kClientCertType, &kStringSignature},
-    { ::onc::eap::kIdentity, &kStringSignature},
-    { ::onc::eap::kInner, &kStringSignature},
-    { ::onc::eap::kOuter, &kStringSignature},
-    { ::onc::eap::kPassword, &kStringSignature},
-    { ::onc::eap::kSaveCredentials, &kBoolSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::eap::kAnonymousIdentity, &kStringSignature},
+    {::onc::client_cert::kClientCertPattern, &kCertificatePatternSignature},
+    {::onc::client_cert::kClientCertRef, &kStringSignature},
+    {::onc::client_cert::kClientCertType, &kStringSignature},
+    {::onc::eap::kIdentity, &kStringSignature},
+    {::onc::eap::kInner, &kStringSignature},
+    {::onc::eap::kOuter, &kStringSignature},
+    {::onc::eap::kPassword, &kStringSignature},
+    {::onc::eap::kSaveCredentials, &kBoolSignature},
     // Used internally. Not officially supported.
-    { ::onc::eap::kServerCAPEMs, &kStringListSignature},
-    { ::onc::eap::kServerCARef, &kStringSignature},
-    { ::onc::eap::kServerCARefs, &kStringListSignature},
-    { ::onc::eap::kUseSystemCAs, &kBoolSignature},
+    {::onc::eap::kServerCAPEMs, &kStringListSignature},
+    {::onc::eap::kServerCARef, &kStringSignature},
+    {::onc::eap::kServerCARefs, &kStringListSignature},
+    {::onc::eap::kUseSystemCAs, &kBoolSignature},
     {NULL}};
 
 const OncFieldSignature ipsec_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::ipsec::kAuthenticationType, &kStringSignature},
-    { ::onc::client_cert::kClientCertPattern, &kCertificatePatternSignature},
-    { ::onc::client_cert::kClientCertRef, &kStringSignature},
-    { ::onc::client_cert::kClientCertType, &kStringSignature},
-    { ::onc::ipsec::kGroup, &kStringSignature},
-    { ::onc::ipsec::kIKEVersion, &kIntegerSignature},
-    { ::onc::ipsec::kPSK, &kStringSignature},
-    { ::onc::vpn::kSaveCredentials, &kBoolSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::ipsec::kAuthenticationType, &kStringSignature},
+    {::onc::client_cert::kClientCertPattern, &kCertificatePatternSignature},
+    {::onc::client_cert::kClientCertRef, &kStringSignature},
+    {::onc::client_cert::kClientCertType, &kStringSignature},
+    {::onc::ipsec::kGroup, &kStringSignature},
+    {::onc::ipsec::kIKEVersion, &kIntegerSignature},
+    {::onc::ipsec::kPSK, &kStringSignature},
+    {::onc::vpn::kSaveCredentials, &kBoolSignature},
     // Used internally. Not officially supported.
-    { ::onc::ipsec::kServerCAPEMs, &kStringListSignature},
-    { ::onc::ipsec::kServerCARef, &kStringSignature},
-    { ::onc::ipsec::kServerCARefs, &kStringListSignature},
-    { ::onc::ipsec::kXAUTH, &kXAUTHSignature},
+    {::onc::ipsec::kServerCAPEMs, &kStringListSignature},
+    {::onc::ipsec::kServerCARef, &kStringSignature},
+    {::onc::ipsec::kServerCARefs, &kStringListSignature},
+    {::onc::ipsec::kXAUTH, &kXAUTHSignature},
     // Not yet supported.
     //  { ipsec::kEAP, &kEAPSignature },
     {NULL}};
 
 const OncFieldSignature xauth_fields[] = {
-    { ::onc::vpn::kPassword, &kStringSignature},
-    { ::onc::vpn::kUsername, &kStringSignature},
+    {::onc::vpn::kPassword, &kStringSignature},
+    {::onc::vpn::kUsername, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature l2tp_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::vpn::kPassword, &kStringSignature},
-    { ::onc::vpn::kSaveCredentials, &kBoolSignature},
-    { ::onc::vpn::kUsername, &kStringSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::vpn::kPassword, &kStringSignature},
+    {::onc::vpn::kSaveCredentials, &kBoolSignature},
+    {::onc::vpn::kUsername, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature openvpn_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::openvpn::kAuth, &kStringSignature},
-    { ::onc::openvpn::kAuthNoCache, &kBoolSignature},
-    { ::onc::openvpn::kAuthRetry, &kStringSignature},
-    { ::onc::openvpn::kCipher, &kStringSignature},
-    { ::onc::client_cert::kClientCertPattern, &kCertificatePatternSignature},
-    { ::onc::client_cert::kClientCertRef, &kStringSignature},
-    { ::onc::client_cert::kClientCertType, &kStringSignature},
-    { ::onc::openvpn::kCompLZO, &kStringSignature},
-    { ::onc::openvpn::kCompNoAdapt, &kBoolSignature},
-    { ::onc::openvpn::kIgnoreDefaultRoute, &kBoolSignature},
-    { ::onc::openvpn::kKeyDirection, &kStringSignature},
-    { ::onc::openvpn::kNsCertType, &kStringSignature},
-    { ::onc::openvpn::kOTP, &kStringSignature},
-    { ::onc::openvpn::kPassword, &kStringSignature},
-    { ::onc::openvpn::kPort, &kIntegerSignature},
-    { ::onc::openvpn::kProto, &kStringSignature},
-    { ::onc::openvpn::kPushPeerInfo, &kBoolSignature},
-    { ::onc::openvpn::kRemoteCertEKU, &kStringSignature},
-    { ::onc::openvpn::kRemoteCertKU, &kStringListSignature},
-    { ::onc::openvpn::kRemoteCertTLS, &kStringSignature},
-    { ::onc::openvpn::kRenegSec, &kIntegerSignature},
-    { ::onc::vpn::kSaveCredentials, &kBoolSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::openvpn::kAuth, &kStringSignature},
+    {::onc::openvpn::kAuthNoCache, &kBoolSignature},
+    {::onc::openvpn::kAuthRetry, &kStringSignature},
+    {::onc::openvpn::kCipher, &kStringSignature},
+    {::onc::client_cert::kClientCertPattern, &kCertificatePatternSignature},
+    {::onc::client_cert::kClientCertRef, &kStringSignature},
+    {::onc::client_cert::kClientCertType, &kStringSignature},
+    {::onc::openvpn::kCompLZO, &kStringSignature},
+    {::onc::openvpn::kCompNoAdapt, &kBoolSignature},
+    {::onc::openvpn::kIgnoreDefaultRoute, &kBoolSignature},
+    {::onc::openvpn::kKeyDirection, &kStringSignature},
+    {::onc::openvpn::kNsCertType, &kStringSignature},
+    {::onc::openvpn::kOTP, &kStringSignature},
+    {::onc::openvpn::kPassword, &kStringSignature},
+    {::onc::openvpn::kPort, &kIntegerSignature},
+    {::onc::openvpn::kProto, &kStringSignature},
+    {::onc::openvpn::kPushPeerInfo, &kBoolSignature},
+    {::onc::openvpn::kRemoteCertEKU, &kStringSignature},
+    {::onc::openvpn::kRemoteCertKU, &kStringListSignature},
+    {::onc::openvpn::kRemoteCertTLS, &kStringSignature},
+    {::onc::openvpn::kRenegSec, &kIntegerSignature},
+    {::onc::vpn::kSaveCredentials, &kBoolSignature},
     // Used internally. Not officially supported.
-    { ::onc::openvpn::kServerCAPEMs, &kStringListSignature},
-    { ::onc::openvpn::kServerCARef, &kStringSignature},
-    { ::onc::openvpn::kServerCARefs, &kStringListSignature},
+    {::onc::openvpn::kServerCAPEMs, &kStringListSignature},
+    {::onc::openvpn::kServerCARef, &kStringSignature},
+    {::onc::openvpn::kServerCARefs, &kStringListSignature},
     // Not supported, yet.
-    { ::onc::openvpn::kServerCertPEM, &kStringSignature},
-    { ::onc::openvpn::kServerCertRef, &kStringSignature},
-    { ::onc::openvpn::kServerPollTimeout, &kIntegerSignature},
-    { ::onc::openvpn::kShaper, &kIntegerSignature},
-    { ::onc::openvpn::kStaticChallenge, &kStringSignature},
-    { ::onc::openvpn::kTLSAuthContents, &kStringSignature},
-    { ::onc::openvpn::kTLSRemote, &kStringSignature},
-    { ::onc::openvpn::kUserAuthenticationType, &kStringSignature},
-    { ::onc::vpn::kUsername, &kStringSignature},
+    {::onc::openvpn::kServerCertPEM, &kStringSignature},
+    {::onc::openvpn::kServerCertRef, &kStringSignature},
+    {::onc::openvpn::kServerPollTimeout, &kIntegerSignature},
+    {::onc::openvpn::kShaper, &kIntegerSignature},
+    {::onc::openvpn::kStaticChallenge, &kStringSignature},
+    {::onc::openvpn::kTLSAuthContents, &kStringSignature},
+    {::onc::openvpn::kTLSRemote, &kStringSignature},
+    {::onc::openvpn::kUserAuthenticationType, &kStringSignature},
+    {::onc::vpn::kUsername, &kStringSignature},
     // Not supported, yet.
-    { ::onc::openvpn::kVerb, &kStringSignature},
-    { ::onc::openvpn::kVerifyHash, &kStringSignature},
-    { ::onc::openvpn::kVerifyX509, &kVerifyX509Signature},
+    {::onc::openvpn::kVerb, &kStringSignature},
+    {::onc::openvpn::kVerifyHash, &kStringSignature},
+    {::onc::openvpn::kVerifyX509, &kVerifyX509Signature},
     {NULL}};
 
 const OncFieldSignature third_party_vpn_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::third_party_vpn::kExtensionID, &kStringSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::third_party_vpn::kExtensionID, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature verify_x509_fields[] = {
-    { ::onc::verify_x509::kName, &kStringSignature},
-    { ::onc::verify_x509::kType, &kStringSignature},
+    {::onc::verify_x509::kName, &kStringSignature},
+    {::onc::verify_x509::kType, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature vpn_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::vpn::kAutoConnect, &kBoolSignature},
-    { ::onc::vpn::kHost, &kStringSignature},
-    { ::onc::vpn::kIPsec, &kIPsecSignature},
-    { ::onc::vpn::kL2TP, &kL2TPSignature},
-    { ::onc::vpn::kOpenVPN, &kOpenVPNSignature},
-    { ::onc::vpn::kThirdPartyVpn, &kThirdPartyVPNSignature},
-    { ::onc::vpn::kType, &kStringSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::vpn::kAutoConnect, &kBoolSignature},
+    {::onc::vpn::kHost, &kStringSignature},
+    {::onc::vpn::kIPsec, &kIPsecSignature},
+    {::onc::vpn::kL2TP, &kL2TPSignature},
+    {::onc::vpn::kOpenVPN, &kOpenVPNSignature},
+    {::onc::vpn::kThirdPartyVpn, &kThirdPartyVPNSignature},
+    {::onc::vpn::kType, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature ethernet_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::ethernet::kAuthentication, &kStringSignature},
-    { ::onc::ethernet::kEAP, &kEAPSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::ethernet::kAuthentication, &kStringSignature},
+    {::onc::ethernet::kEAP, &kEAPSignature},
     {NULL}};
 
 const OncFieldSignature ipconfig_fields[] = {
-    { ::onc::ipconfig::kGateway, &kStringSignature},
-    { ::onc::ipconfig::kIPAddress, &kStringSignature},
-    { ::onc::ipconfig::kNameServers, &kStringListSignature},
-    { ::onc::ipconfig::kRoutingPrefix, &kIntegerSignature},
-    { ::onc::ipconfig::kSearchDomains, &kStringListSignature},
-    { ::onc::ipconfig::kType, &kStringSignature},
-    { ::onc::ipconfig::kWebProxyAutoDiscoveryUrl, &kStringSignature},
+    {::onc::ipconfig::kGateway, &kStringSignature},
+    {::onc::ipconfig::kIPAddress, &kStringSignature},
+    {::onc::ipconfig::kNameServers, &kStringListSignature},
+    {::onc::ipconfig::kRoutingPrefix, &kIntegerSignature},
+    {::onc::ipconfig::kSearchDomains, &kStringListSignature},
+    {::onc::ipconfig::kType, &kStringSignature},
+    {::onc::ipconfig::kWebProxyAutoDiscoveryUrl, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature proxy_location_fields[] = {
-    { ::onc::proxy::kHost, &kStringSignature},
-    { ::onc::proxy::kPort, &kIntegerSignature}, {NULL}};
+    {::onc::proxy::kHost, &kStringSignature},
+    {::onc::proxy::kPort, &kIntegerSignature},
+    {NULL}};
 
 const OncFieldSignature proxy_manual_fields[] = {
-    { ::onc::proxy::kFtp, &kProxyLocationSignature},
-    { ::onc::proxy::kHttp, &kProxyLocationSignature},
-    { ::onc::proxy::kHttps, &kProxyLocationSignature},
-    { ::onc::proxy::kSocks, &kProxyLocationSignature},
+    {::onc::proxy::kFtp, &kProxyLocationSignature},
+    {::onc::proxy::kHttp, &kProxyLocationSignature},
+    {::onc::proxy::kHttps, &kProxyLocationSignature},
+    {::onc::proxy::kSocks, &kProxyLocationSignature},
     {NULL}};
 
 const OncFieldSignature proxy_settings_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::proxy::kExcludeDomains, &kStringListSignature},
-    { ::onc::proxy::kManual, &kProxyManualSignature},
-    { ::onc::proxy::kPAC, &kStringSignature},
-    { ::onc::proxy::kType, &kStringSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::proxy::kExcludeDomains, &kStringListSignature},
+    {::onc::proxy::kManual, &kProxyManualSignature},
+    {::onc::proxy::kPAC, &kStringSignature},
+    {::onc::proxy::kType, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature wifi_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::wifi::kAllowGatewayARPPolling, &kBoolSignature},
-    { ::onc::wifi::kAutoConnect, &kBoolSignature},
-    { ::onc::wifi::kEAP, &kEAPSignature},
-    { ::onc::wifi::kHexSSID, &kStringSignature},
-    { ::onc::wifi::kHiddenSSID, &kBoolSignature},
-    { ::onc::wifi::kPassphrase, &kStringSignature},
-    { ::onc::wifi::kSSID, &kStringSignature},
-    { ::onc::wifi::kSecurity, &kStringSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::wifi::kAllowGatewayARPPolling, &kBoolSignature},
+    {::onc::wifi::kAutoConnect, &kBoolSignature},
+    {::onc::wifi::kEAP, &kEAPSignature},
+    {::onc::wifi::kHexSSID, &kStringSignature},
+    {::onc::wifi::kHiddenSSID, &kBoolSignature},
+    {::onc::wifi::kPassphrase, &kStringSignature},
+    {::onc::wifi::kSSID, &kStringSignature},
+    {::onc::wifi::kSecurity, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature wifi_with_state_fields[] = {
-    { ::onc::wifi::kBSSID, &kStringSignature},
-    { ::onc::wifi::kFrequency, &kIntegerSignature},
-    { ::onc::wifi::kFrequencyList, &kIntegerListSignature},
-    { ::onc::wifi::kSignalStrength, &kIntegerSignature},
+    {::onc::wifi::kBSSID, &kStringSignature},
+    {::onc::wifi::kFrequency, &kIntegerSignature},
+    {::onc::wifi::kFrequencyList, &kIntegerListSignature},
+    {::onc::wifi::kSignalStrength, &kIntegerSignature},
     {NULL}};
 
 const OncFieldSignature wimax_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::wimax::kAutoConnect, &kBoolSignature},
-    { ::onc::wimax::kEAP, &kEAPSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::wimax::kAutoConnect, &kBoolSignature},
+    {::onc::wimax::kEAP, &kEAPSignature},
     {NULL}};
 
 const OncFieldSignature wimax_with_state_fields[] = {
-    { ::onc::wimax::kSignalStrength, &kIntegerSignature},
+    {::onc::wimax::kSignalStrength, &kIntegerSignature},
     {NULL}};
 
 const OncFieldSignature cellular_provider_fields[] = {
-    { ::onc::cellular_provider::kCode, &kStringSignature},
-    { ::onc::cellular_provider::kCountry, &kStringSignature},
-    { ::onc::cellular_provider::kName, &kStringSignature},
+    {::onc::cellular_provider::kCode, &kStringSignature},
+    {::onc::cellular_provider::kCountry, &kStringSignature},
+    {::onc::cellular_provider::kName, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature cellular_apn_fields[] = {
-    { ::onc::cellular_apn::kAccessPointName, &kStringSignature},
-    { ::onc::cellular_apn::kName, &kStringSignature},
-    { ::onc::cellular_apn::kUsername, &kStringSignature},
-    { ::onc::cellular_apn::kPassword, &kStringSignature},
-    { ::onc::cellular_apn::kLocalizedName, &kStringSignature},
-    { ::onc::cellular_apn::kLanguage, &kStringSignature},
+    {::onc::cellular_apn::kAccessPointName, &kStringSignature},
+    {::onc::cellular_apn::kName, &kStringSignature},
+    {::onc::cellular_apn::kUsername, &kStringSignature},
+    {::onc::cellular_apn::kPassword, &kStringSignature},
+    {::onc::cellular_apn::kLocalizedName, &kStringSignature},
+    {::onc::cellular_apn::kLanguage, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature cellular_found_network_fields[] = {
-    { ::onc::cellular_found_network::kStatus, &kStringSignature},
-    { ::onc::cellular_found_network::kNetworkId, &kStringSignature},
-    { ::onc::cellular_found_network::kShortName, &kStringSignature},
-    { ::onc::cellular_found_network::kLongName, &kStringSignature},
-    { ::onc::cellular_found_network::kTechnology, &kStringSignature},
+    {::onc::cellular_found_network::kStatus, &kStringSignature},
+    {::onc::cellular_found_network::kNetworkId, &kStringSignature},
+    {::onc::cellular_found_network::kShortName, &kStringSignature},
+    {::onc::cellular_found_network::kLongName, &kStringSignature},
+    {::onc::cellular_found_network::kTechnology, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature sim_lock_status_fields[] = {
-    { ::onc::sim_lock_status::kLockEnabled, &kBoolSignature},
-    { ::onc::sim_lock_status::kLockType, &kStringSignature},
-    { ::onc::sim_lock_status::kRetriesLeft, &kDoubleSignature},
+    {::onc::sim_lock_status::kLockEnabled, &kBoolSignature},
+    {::onc::sim_lock_status::kLockType, &kStringSignature},
+    {::onc::sim_lock_status::kRetriesLeft, &kDoubleSignature},
     {NULL}};
 
 const OncFieldSignature cellular_fields[] = {
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::cellular::kAPN, &kCellularApnSignature },
-    { ::onc::cellular::kAPNList, &kCellularApnListSignature},
-    { ::onc::vpn::kAutoConnect, &kBoolSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::cellular::kAPN, &kCellularApnSignature},
+    {::onc::cellular::kAPNList, &kCellularApnListSignature},
+    {::onc::vpn::kAutoConnect, &kBoolSignature},
     {NULL}};
 
 const OncFieldSignature cellular_with_state_fields[] = {
-    { ::onc::cellular::kActivationType, &kStringSignature},
-    { ::onc::cellular::kActivationState, &kStringSignature},
-    { ::onc::cellular::kAllowRoaming, &kBoolSignature},
-    { ::onc::cellular::kCarrier, &kStringSignature},
-    { ::onc::cellular::kESN, &kStringSignature},
-    { ::onc::cellular::kFamily, &kStringSignature},
-    { ::onc::cellular::kFirmwareRevision, &kStringSignature},
-    { ::onc::cellular::kFoundNetworks, &kCellularFoundNetworkListSignature},
-    { ::onc::cellular::kHardwareRevision, &kStringSignature},
-    { ::onc::cellular::kHomeProvider, &kCellularProviderSignature},
-    { ::onc::cellular::kICCID, &kStringSignature},
-    { ::onc::cellular::kIMEI, &kStringSignature},
-    { ::onc::cellular::kIMSI, &kStringSignature},
-    { ::onc::cellular::kLastGoodAPN, &kCellularApnSignature },
-    { ::onc::cellular::kManufacturer, &kStringSignature},
-    { ::onc::cellular::kMDN, &kStringSignature},
-    { ::onc::cellular::kMEID, &kStringSignature},
-    { ::onc::cellular::kMIN, &kStringSignature},
-    { ::onc::cellular::kModelID, &kStringSignature},
-    { ::onc::cellular::kNetworkTechnology, &kStringSignature},
-    { ::onc::cellular::kPRLVersion, &kIntegerSignature},
-    { ::onc::cellular::kRoamingState, &kStringSignature},
-    { ::onc::cellular::kServingOperator, &kCellularProviderSignature},
-    { ::onc::cellular::kSignalStrength, &kIntegerSignature},
-    { ::onc::cellular::kSIMLockStatus, &kSIMLockStatusSignature},
-    { ::onc::cellular::kSIMPresent, &kBoolSignature},
-    { ::onc::cellular::kSupportNetworkScan, &kBoolSignature},
-    { ::onc::cellular::kSupportedCarriers, &kStringListSignature},
+    {::onc::cellular::kActivationType, &kStringSignature},
+    {::onc::cellular::kActivationState, &kStringSignature},
+    {::onc::cellular::kAllowRoaming, &kBoolSignature},
+    {::onc::cellular::kCarrier, &kStringSignature},
+    {::onc::cellular::kESN, &kStringSignature},
+    {::onc::cellular::kFamily, &kStringSignature},
+    {::onc::cellular::kFirmwareRevision, &kStringSignature},
+    {::onc::cellular::kFoundNetworks, &kCellularFoundNetworkListSignature},
+    {::onc::cellular::kHardwareRevision, &kStringSignature},
+    {::onc::cellular::kHomeProvider, &kCellularProviderSignature},
+    {::onc::cellular::kICCID, &kStringSignature},
+    {::onc::cellular::kIMEI, &kStringSignature},
+    {::onc::cellular::kIMSI, &kStringSignature},
+    {::onc::cellular::kLastGoodAPN, &kCellularApnSignature},
+    {::onc::cellular::kManufacturer, &kStringSignature},
+    {::onc::cellular::kMDN, &kStringSignature},
+    {::onc::cellular::kMEID, &kStringSignature},
+    {::onc::cellular::kMIN, &kStringSignature},
+    {::onc::cellular::kModelID, &kStringSignature},
+    {::onc::cellular::kNetworkTechnology, &kStringSignature},
+    {::onc::cellular::kPRLVersion, &kIntegerSignature},
+    {::onc::cellular::kRoamingState, &kStringSignature},
+    {::onc::cellular::kServingOperator, &kCellularProviderSignature},
+    {::onc::cellular::kSignalStrength, &kIntegerSignature},
+    {::onc::cellular::kSIMLockStatus, &kSIMLockStatusSignature},
+    {::onc::cellular::kSIMPresent, &kBoolSignature},
+    {::onc::cellular::kSupportNetworkScan, &kBoolSignature},
+    {::onc::cellular::kSupportedCarriers, &kStringListSignature},
     {NULL}};
 
 const OncFieldSignature network_configuration_fields[] = {
-    { ::onc::network_config::kCellular, &kCellularSignature},
-    { ::onc::network_config::kEthernet, &kEthernetSignature},
-    { ::onc::network_config::kGUID, &kStringSignature},
-    { ::onc::network_config::kIPAddressConfigType, &kStringSignature},
-    { ::onc::network_config::kName, &kStringSignature},
-    { ::onc::network_config::kNameServersConfigType, &kStringSignature},
-    { ::onc::network_config::kPriority, &kIntegerSignature},
-    { ::onc::network_config::kProxySettings, &kProxySettingsSignature},
-    { ::onc::kRecommended, &kRecommendedSignature},
-    { ::onc::kRemove, &kBoolSignature},
-    { ::onc::network_config::kStaticIPConfig, &kStaticIPConfigSignature},
-    { ::onc::network_config::kType, &kStringSignature},
-    { ::onc::network_config::kVPN, &kVPNSignature},
-    { ::onc::network_config::kWiFi, &kWiFiSignature},
-    { ::onc::network_config::kWimax, &kWiMAXSignature},
+    {::onc::network_config::kCellular, &kCellularSignature},
+    {::onc::network_config::kEthernet, &kEthernetSignature},
+    {::onc::network_config::kGUID, &kStringSignature},
+    {::onc::network_config::kIPAddressConfigType, &kStringSignature},
+    {::onc::network_config::kName, &kStringSignature},
+    {::onc::network_config::kNameServersConfigType, &kStringSignature},
+    {::onc::network_config::kPriority, &kIntegerSignature},
+    {::onc::network_config::kProxySettings, &kProxySettingsSignature},
+    {::onc::kRecommended, &kRecommendedSignature},
+    {::onc::kRemove, &kBoolSignature},
+    {::onc::network_config::kStaticIPConfig, &kStaticIPConfigSignature},
+    {::onc::network_config::kType, &kStringSignature},
+    {::onc::network_config::kVPN, &kVPNSignature},
+    {::onc::network_config::kWiFi, &kWiFiSignature},
+    {::onc::network_config::kWimax, &kWiMAXSignature},
     {NULL}};
 
 const OncFieldSignature network_with_state_fields[] = {
-    { ::onc::network_config::kCellular, &kCellularWithStateSignature},
-    { ::onc::network_config::kConnectionState, &kStringSignature},
-    { ::onc::network_config::kConnectable, &kBoolSignature},
-    { ::onc::network_config::kErrorState, &kStringSignature},
-    { ::onc::network_config::kIPConfigs, &kIPConfigListSignature},
-    { ::onc::network_config::kMacAddress, &kStringSignature},
-    { ::onc::network_config::kRestrictedConnectivity, &kBoolSignature},
-    { ::onc::network_config::kSavedIPConfig, &kSavedIPConfigSignature},
-    { ::onc::network_config::kSource, &kStringSignature},
-    { ::onc::network_config::kWiFi, &kWiFiWithStateSignature},
-    { ::onc::network_config::kWimax, &kWiMAXWithStateSignature},
+    {::onc::network_config::kCellular, &kCellularWithStateSignature},
+    {::onc::network_config::kConnectionState, &kStringSignature},
+    {::onc::network_config::kConnectable, &kBoolSignature},
+    {::onc::network_config::kErrorState, &kStringSignature},
+    {::onc::network_config::kIPConfigs, &kIPConfigListSignature},
+    {::onc::network_config::kMacAddress, &kStringSignature},
+    {::onc::network_config::kRestrictedConnectivity, &kBoolSignature},
+    {::onc::network_config::kSavedIPConfig, &kSavedIPConfigSignature},
+    {::onc::network_config::kSource, &kStringSignature},
+    {::onc::network_config::kWiFi, &kWiFiWithStateSignature},
+    {::onc::network_config::kWimax, &kWiMAXWithStateSignature},
     {NULL}};
 
 const OncFieldSignature global_network_configuration_fields[] = {
-    { ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
-      &kBoolSignature},
+    {::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
+     &kBoolSignature},
     {NULL}};
 
 const OncFieldSignature certificate_fields[] = {
-    { ::onc::certificate::kGUID, &kStringSignature},
-    { ::onc::certificate::kPKCS12, &kStringSignature},
-    { ::onc::kRemove, &kBoolSignature},
-    { ::onc::certificate::kTrustBits, &kStringListSignature},
-    { ::onc::certificate::kType, &kStringSignature},
-    { ::onc::certificate::kX509, &kStringSignature},
+    {::onc::certificate::kGUID, &kStringSignature},
+    {::onc::certificate::kPKCS12, &kStringSignature},
+    {::onc::kRemove, &kBoolSignature},
+    {::onc::certificate::kTrustBits, &kStringListSignature},
+    {::onc::certificate::kType, &kStringSignature},
+    {::onc::certificate::kX509, &kStringSignature},
     {NULL}};
 
 const OncFieldSignature toplevel_configuration_fields[] = {
-    { ::onc::toplevel_config::kCertificates, &kCertificateListSignature},
-    { ::onc::toplevel_config::kNetworkConfigurations,
-      &kNetworkConfigurationListSignature},
-    { ::onc::toplevel_config::kGlobalNetworkConfiguration,
-      &kGlobalNetworkConfigurationSignature},
-    { ::onc::toplevel_config::kType, &kStringSignature},
-    { ::onc::encrypted::kCipher, &kStringSignature},
-    { ::onc::encrypted::kCiphertext, &kStringSignature},
-    { ::onc::encrypted::kHMAC, &kStringSignature},
-    { ::onc::encrypted::kHMACMethod, &kStringSignature},
-    { ::onc::encrypted::kIV, &kStringSignature},
-    { ::onc::encrypted::kIterations, &kIntegerSignature},
-    { ::onc::encrypted::kSalt, &kStringSignature},
-    { ::onc::encrypted::kStretch, &kStringSignature}, {NULL}};
+    {::onc::toplevel_config::kCertificates, &kCertificateListSignature},
+    {::onc::toplevel_config::kNetworkConfigurations,
+     &kNetworkConfigurationListSignature},
+    {::onc::toplevel_config::kGlobalNetworkConfiguration,
+     &kGlobalNetworkConfigurationSignature},
+    {::onc::toplevel_config::kType, &kStringSignature},
+    {::onc::encrypted::kCipher, &kStringSignature},
+    {::onc::encrypted::kCiphertext, &kStringSignature},
+    {::onc::encrypted::kHMAC, &kStringSignature},
+    {::onc::encrypted::kHMACMethod, &kStringSignature},
+    {::onc::encrypted::kIV, &kStringSignature},
+    {::onc::encrypted::kIterations, &kIntegerSignature},
+    {::onc::encrypted::kSalt, &kStringSignature},
+    {::onc::encrypted::kStretch, &kStringSignature},
+    {NULL}};
 
 }  // namespace
 
-const OncValueSignature kRecommendedSignature = {
-  base::Value::TYPE_LIST, NULL, &kStringSignature
-};
-const OncValueSignature kEAPSignature = {
-  base::Value::TYPE_DICTIONARY, eap_fields, NULL
-};
+const OncValueSignature kRecommendedSignature = {base::Value::TYPE_LIST,
+                                                 NULL,
+                                                 &kStringSignature};
+const OncValueSignature kEAPSignature = {base::Value::TYPE_DICTIONARY,
+                                         eap_fields,
+                                         NULL};
 const OncValueSignature kIssuerSubjectPatternSignature = {
-  base::Value::TYPE_DICTIONARY, issuer_subject_pattern_fields, NULL
-};
+    base::Value::TYPE_DICTIONARY,
+    issuer_subject_pattern_fields,
+    NULL};
 const OncValueSignature kCertificatePatternSignature = {
-  base::Value::TYPE_DICTIONARY, certificate_pattern_fields, NULL
-};
-const OncValueSignature kIPsecSignature = {
-  base::Value::TYPE_DICTIONARY, ipsec_fields, NULL
-};
-const OncValueSignature kXAUTHSignature = {
-  base::Value::TYPE_DICTIONARY, xauth_fields, NULL
-};
-const OncValueSignature kL2TPSignature = {
-  base::Value::TYPE_DICTIONARY, l2tp_fields, NULL
-};
-const OncValueSignature kOpenVPNSignature = {
-  base::Value::TYPE_DICTIONARY, openvpn_fields, NULL
-};
-const OncValueSignature kThirdPartyVPNSignature = {
-  base::Value::TYPE_DICTIONARY, third_party_vpn_fields, NULL
-};
-const OncValueSignature kVerifyX509Signature = {
-  base::Value::TYPE_DICTIONARY, verify_x509_fields, NULL
-};
-const OncValueSignature kVPNSignature = {
-  base::Value::TYPE_DICTIONARY, vpn_fields, NULL
-};
-const OncValueSignature kEthernetSignature = {
-  base::Value::TYPE_DICTIONARY, ethernet_fields, NULL
-};
-const OncValueSignature kIPConfigSignature = {
-  base::Value::TYPE_DICTIONARY, ipconfig_fields, NULL
-};
-const OncValueSignature kSavedIPConfigSignature = {
-  base::Value::TYPE_DICTIONARY, ipconfig_fields, NULL
-};
+    base::Value::TYPE_DICTIONARY,
+    certificate_pattern_fields,
+    NULL};
+const OncValueSignature kIPsecSignature = {base::Value::TYPE_DICTIONARY,
+                                           ipsec_fields,
+                                           NULL};
+const OncValueSignature kXAUTHSignature = {base::Value::TYPE_DICTIONARY,
+                                           xauth_fields,
+                                           NULL};
+const OncValueSignature kL2TPSignature = {base::Value::TYPE_DICTIONARY,
+                                          l2tp_fields,
+                                          NULL};
+const OncValueSignature kOpenVPNSignature = {base::Value::TYPE_DICTIONARY,
+                                             openvpn_fields,
+                                             NULL};
+const OncValueSignature kThirdPartyVPNSignature = {base::Value::TYPE_DICTIONARY,
+                                                   third_party_vpn_fields,
+                                                   NULL};
+const OncValueSignature kVerifyX509Signature = {base::Value::TYPE_DICTIONARY,
+                                                verify_x509_fields,
+                                                NULL};
+const OncValueSignature kVPNSignature = {base::Value::TYPE_DICTIONARY,
+                                         vpn_fields,
+                                         NULL};
+const OncValueSignature kEthernetSignature = {base::Value::TYPE_DICTIONARY,
+                                              ethernet_fields,
+                                              NULL};
+const OncValueSignature kIPConfigSignature = {base::Value::TYPE_DICTIONARY,
+                                              ipconfig_fields,
+                                              NULL};
+const OncValueSignature kSavedIPConfigSignature = {base::Value::TYPE_DICTIONARY,
+                                                   ipconfig_fields,
+                                                   NULL};
 const OncValueSignature kStaticIPConfigSignature = {
-  base::Value::TYPE_DICTIONARY, ipconfig_fields, NULL
-};
-const OncValueSignature kProxyLocationSignature = {
-  base::Value::TYPE_DICTIONARY, proxy_location_fields, NULL
-};
-const OncValueSignature kProxyManualSignature = {
-  base::Value::TYPE_DICTIONARY, proxy_manual_fields, NULL
-};
-const OncValueSignature kProxySettingsSignature = {
-  base::Value::TYPE_DICTIONARY, proxy_settings_fields, NULL
-};
-const OncValueSignature kWiFiSignature = {
-  base::Value::TYPE_DICTIONARY, wifi_fields, NULL
-};
-const OncValueSignature kWiMAXSignature = {
-  base::Value::TYPE_DICTIONARY, wimax_fields, NULL
-};
-const OncValueSignature kCertificateSignature = {
-  base::Value::TYPE_DICTIONARY, certificate_fields, NULL
-};
+    base::Value::TYPE_DICTIONARY,
+    ipconfig_fields,
+    NULL};
+const OncValueSignature kProxyLocationSignature = {base::Value::TYPE_DICTIONARY,
+                                                   proxy_location_fields,
+                                                   NULL};
+const OncValueSignature kProxyManualSignature = {base::Value::TYPE_DICTIONARY,
+                                                 proxy_manual_fields,
+                                                 NULL};
+const OncValueSignature kProxySettingsSignature = {base::Value::TYPE_DICTIONARY,
+                                                   proxy_settings_fields,
+                                                   NULL};
+const OncValueSignature kWiFiSignature = {base::Value::TYPE_DICTIONARY,
+                                          wifi_fields,
+                                          NULL};
+const OncValueSignature kWiMAXSignature = {base::Value::TYPE_DICTIONARY,
+                                           wimax_fields,
+                                           NULL};
+const OncValueSignature kCertificateSignature = {base::Value::TYPE_DICTIONARY,
+                                                 certificate_fields,
+                                                 NULL};
 const OncValueSignature kNetworkConfigurationSignature = {
-  base::Value::TYPE_DICTIONARY, network_configuration_fields, NULL
-};
+    base::Value::TYPE_DICTIONARY,
+    network_configuration_fields,
+    NULL};
 const OncValueSignature kGlobalNetworkConfigurationSignature = {
-  base::Value::TYPE_DICTIONARY, global_network_configuration_fields, NULL
-};
-const OncValueSignature kCertificateListSignature = {
-  base::Value::TYPE_LIST, NULL, &kCertificateSignature
-};
+    base::Value::TYPE_DICTIONARY,
+    global_network_configuration_fields,
+    NULL};
+const OncValueSignature kCertificateListSignature = {base::Value::TYPE_LIST,
+                                                     NULL,
+                                                     &kCertificateSignature};
 const OncValueSignature kNetworkConfigurationListSignature = {
-  base::Value::TYPE_LIST, NULL, &kNetworkConfigurationSignature
-};
+    base::Value::TYPE_LIST,
+    NULL,
+    &kNetworkConfigurationSignature};
 const OncValueSignature kToplevelConfigurationSignature = {
-  base::Value::TYPE_DICTIONARY, toplevel_configuration_fields, NULL
-};
+    base::Value::TYPE_DICTIONARY,
+    toplevel_configuration_fields,
+    NULL};
 
 // Derived "ONC with State" signatures.
 const OncValueSignature kNetworkWithStateSignature = {
-  base::Value::TYPE_DICTIONARY, network_with_state_fields, NULL,
-  &kNetworkConfigurationSignature
-};
-const OncValueSignature kWiFiWithStateSignature = {
-  base::Value::TYPE_DICTIONARY, wifi_with_state_fields, NULL, &kWiFiSignature
-};
+    base::Value::TYPE_DICTIONARY,
+    network_with_state_fields,
+    NULL,
+    &kNetworkConfigurationSignature};
+const OncValueSignature kWiFiWithStateSignature = {base::Value::TYPE_DICTIONARY,
+                                                   wifi_with_state_fields,
+                                                   NULL,
+                                                   &kWiFiSignature};
 const OncValueSignature kWiMAXWithStateSignature = {
-  base::Value::TYPE_DICTIONARY, wimax_with_state_fields, NULL, &kWiMAXSignature
-};
-const OncValueSignature kCellularSignature = {
-  base::Value::TYPE_DICTIONARY, cellular_fields, NULL
-};
+    base::Value::TYPE_DICTIONARY,
+    wimax_with_state_fields,
+    NULL,
+    &kWiMAXSignature};
+const OncValueSignature kCellularSignature = {base::Value::TYPE_DICTIONARY,
+                                              cellular_fields,
+                                              NULL};
 const OncValueSignature kCellularWithStateSignature = {
-  base::Value::TYPE_DICTIONARY, cellular_with_state_fields, NULL,
-  &kCellularSignature
-};
+    base::Value::TYPE_DICTIONARY,
+    cellular_with_state_fields,
+    NULL,
+    &kCellularSignature};
 const OncValueSignature kCellularProviderSignature = {
-  base::Value::TYPE_DICTIONARY, cellular_provider_fields, NULL
-};
-const OncValueSignature kCellularApnSignature = {
-  base::Value::TYPE_DICTIONARY, cellular_apn_fields, NULL
-};
+    base::Value::TYPE_DICTIONARY,
+    cellular_provider_fields,
+    NULL};
+const OncValueSignature kCellularApnSignature = {base::Value::TYPE_DICTIONARY,
+                                                 cellular_apn_fields,
+                                                 NULL};
 const OncValueSignature kCellularFoundNetworkSignature = {
-  base::Value::TYPE_DICTIONARY, cellular_found_network_fields, NULL
-};
-const OncValueSignature kSIMLockStatusSignature = {
-  base::Value::TYPE_DICTIONARY, sim_lock_status_fields, NULL
-};
+    base::Value::TYPE_DICTIONARY,
+    cellular_found_network_fields,
+    NULL};
+const OncValueSignature kSIMLockStatusSignature = {base::Value::TYPE_DICTIONARY,
+                                                   sim_lock_status_fields,
+                                                   NULL};
 
 const OncFieldSignature* GetFieldSignature(const OncValueSignature& signature,
                                            const std::string& onc_field_name) {
diff --git a/chromeos/network/onc/onc_signature.h b/chromeos/network/onc/onc_signature.h
index 316ee86..d0c9e508 100644
--- a/chromeos/network/onc/onc_signature.h
+++ b/chromeos/network/onc/onc_signature.h
@@ -31,9 +31,8 @@
     const OncValueSignature& signature,
     const std::string& onc_field_name);
 
-CHROMEOS_EXPORT bool FieldIsCredential(
-    const OncValueSignature& signature,
-    const std::string& onc_field_name);
+CHROMEOS_EXPORT bool FieldIsCredential(const OncValueSignature& signature,
+                                       const std::string& onc_field_name);
 
 CHROMEOS_EXPORT extern const OncValueSignature kRecommendedSignature;
 CHROMEOS_EXPORT extern const OncValueSignature kEAPSignature;
diff --git a/chromeos/network/onc/onc_translation_tables.cc b/chromeos/network/onc/onc_translation_tables.cc
index 94af3ecc..a514527 100644
--- a/chromeos/network/onc/onc_translation_tables.cc
+++ b/chromeos/network/onc/onc_translation_tables.cc
@@ -22,161 +22,164 @@
 namespace {
 
 const FieldTranslationEntry eap_fields[] = {
-    { ::onc::eap::kAnonymousIdentity, shill::kEapAnonymousIdentityProperty},
-    { ::onc::eap::kIdentity, shill::kEapIdentityProperty},
+    {::onc::eap::kAnonymousIdentity, shill::kEapAnonymousIdentityProperty},
+    {::onc::eap::kIdentity, shill::kEapIdentityProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::eap::kInner, shill::kEapPhase2AuthProperty },
 
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::eap::kOuter, shill::kEapMethodProperty },
-    { ::onc::eap::kPassword, shill::kEapPasswordProperty},
-    { ::onc::eap::kSaveCredentials, shill::kSaveCredentialsProperty},
-    { ::onc::eap::kServerCAPEMs, shill::kEapCaCertPemProperty},
-    { ::onc::eap::kUseSystemCAs, shill::kEapUseSystemCasProperty},
+    {::onc::eap::kPassword, shill::kEapPasswordProperty},
+    {::onc::eap::kSaveCredentials, shill::kSaveCredentialsProperty},
+    {::onc::eap::kServerCAPEMs, shill::kEapCaCertPemProperty},
+    {::onc::eap::kUseSystemCAs, shill::kEapUseSystemCasProperty},
     {NULL}};
 
 const FieldTranslationEntry ipsec_fields[] = {
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::ipsec::kAuthenticationType, shill::kL2tpIpsecAuthenticationType
     // },
-    { ::onc::ipsec::kGroup, shill::kL2tpIpsecTunnelGroupProperty},
+    {::onc::ipsec::kGroup, shill::kL2tpIpsecTunnelGroupProperty},
     // Ignored by Shill, not necessary to synchronize.
     // { ::onc::ipsec::kIKEVersion, shill::kL2tpIpsecIkeVersion },
-    { ::onc::ipsec::kPSK, shill::kL2tpIpsecPskProperty},
+    {::onc::ipsec::kPSK, shill::kL2tpIpsecPskProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::vpn::kSaveCredentials, shill::kSaveCredentialsProperty},
-    { ::onc::ipsec::kServerCAPEMs, shill::kL2tpIpsecCaCertPemProperty},
+    {::onc::ipsec::kServerCAPEMs, shill::kL2tpIpsecCaCertPemProperty},
     {NULL}};
 
 const FieldTranslationEntry xauth_fields[] = {
-    { ::onc::vpn::kPassword, shill::kL2tpIpsecXauthPasswordProperty},
-    { ::onc::vpn::kUsername, shill::kL2tpIpsecXauthUserProperty},
+    {::onc::vpn::kPassword, shill::kL2tpIpsecXauthPasswordProperty},
+    {::onc::vpn::kUsername, shill::kL2tpIpsecXauthUserProperty},
     {NULL}};
 
 const FieldTranslationEntry l2tp_fields[] = {
-    { ::onc::vpn::kPassword, shill::kL2tpIpsecPasswordProperty},
+    {::onc::vpn::kPassword, shill::kL2tpIpsecPasswordProperty},
     // We don't synchronize l2tp's SaveCredentials field for now, as Shill
     // doesn't support separate settings for ipsec and l2tp.
     // { ::onc::vpn::kSaveCredentials, &kBoolSignature },
-    { ::onc::vpn::kUsername, shill::kL2tpIpsecUserProperty}, {NULL}};
+    {::onc::vpn::kUsername, shill::kL2tpIpsecUserProperty},
+    {NULL}};
 
 const FieldTranslationEntry openvpn_fields[] = {
-    { ::onc::openvpn::kAuth, shill::kOpenVPNAuthProperty},
-    { ::onc::openvpn::kAuthNoCache, shill::kOpenVPNAuthNoCacheProperty},
-    { ::onc::openvpn::kAuthRetry, shill::kOpenVPNAuthRetryProperty},
-    { ::onc::openvpn::kCipher, shill::kOpenVPNCipherProperty},
-    { ::onc::openvpn::kCompLZO, shill::kOpenVPNCompLZOProperty},
-    { ::onc::openvpn::kCompNoAdapt, shill::kOpenVPNCompNoAdaptProperty},
-    { ::onc::openvpn::kIgnoreDefaultRoute,
-      shill::kOpenVPNIgnoreDefaultRouteProperty},
-    { ::onc::openvpn::kKeyDirection, shill::kOpenVPNKeyDirectionProperty},
-    { ::onc::openvpn::kNsCertType, shill::kOpenVPNNsCertTypeProperty},
+    {::onc::openvpn::kAuth, shill::kOpenVPNAuthProperty},
+    {::onc::openvpn::kAuthNoCache, shill::kOpenVPNAuthNoCacheProperty},
+    {::onc::openvpn::kAuthRetry, shill::kOpenVPNAuthRetryProperty},
+    {::onc::openvpn::kCipher, shill::kOpenVPNCipherProperty},
+    {::onc::openvpn::kCompLZO, shill::kOpenVPNCompLZOProperty},
+    {::onc::openvpn::kCompNoAdapt, shill::kOpenVPNCompNoAdaptProperty},
+    {::onc::openvpn::kIgnoreDefaultRoute,
+     shill::kOpenVPNIgnoreDefaultRouteProperty},
+    {::onc::openvpn::kKeyDirection, shill::kOpenVPNKeyDirectionProperty},
+    {::onc::openvpn::kNsCertType, shill::kOpenVPNNsCertTypeProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::vpn::kOTP, shill::kOpenVPNTokenProperty or kOpenVPNOTPProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::vpn::kPassword, shill::kOpenVPNPasswordProperty},
-    { ::onc::openvpn::kPort, shill::kOpenVPNPortProperty},
-    { ::onc::openvpn::kProto, shill::kOpenVPNProtoProperty},
-    { ::onc::openvpn::kPushPeerInfo, shill::kOpenVPNPushPeerInfoProperty},
-    { ::onc::openvpn::kRemoteCertEKU, shill::kOpenVPNRemoteCertEKUProperty},
+    {::onc::openvpn::kPort, shill::kOpenVPNPortProperty},
+    {::onc::openvpn::kProto, shill::kOpenVPNProtoProperty},
+    {::onc::openvpn::kPushPeerInfo, shill::kOpenVPNPushPeerInfoProperty},
+    {::onc::openvpn::kRemoteCertEKU, shill::kOpenVPNRemoteCertEKUProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::openvpn::kRemoteCertKU, shill::kOpenVPNRemoteCertKUProperty },
-    { ::onc::openvpn::kRemoteCertTLS, shill::kOpenVPNRemoteCertTLSProperty},
-    { ::onc::openvpn::kRenegSec, shill::kOpenVPNRenegSecProperty},
+    {::onc::openvpn::kRemoteCertTLS, shill::kOpenVPNRemoteCertTLSProperty},
+    {::onc::openvpn::kRenegSec, shill::kOpenVPNRenegSecProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::vpn::kSaveCredentials, shill::kSaveCredentialsProperty},
-    { ::onc::openvpn::kServerCAPEMs, shill::kOpenVPNCaCertPemProperty},
-    { ::onc::openvpn::kServerPollTimeout,
-      shill::kOpenVPNServerPollTimeoutProperty},
-    { ::onc::openvpn::kShaper, shill::kOpenVPNShaperProperty},
-    { ::onc::openvpn::kStaticChallenge, shill::kOpenVPNStaticChallengeProperty},
-    { ::onc::openvpn::kTLSAuthContents, shill::kOpenVPNTLSAuthContentsProperty},
-    { ::onc::openvpn::kTLSRemote, shill::kOpenVPNTLSRemoteProperty},
-    { ::onc::vpn::kUsername, shill::kOpenVPNUserProperty},
-    { ::onc::openvpn::kVerifyHash, shill::kOpenVPNVerifyHashProperty},
+    {::onc::openvpn::kServerCAPEMs, shill::kOpenVPNCaCertPemProperty},
+    {::onc::openvpn::kServerPollTimeout,
+     shill::kOpenVPNServerPollTimeoutProperty},
+    {::onc::openvpn::kShaper, shill::kOpenVPNShaperProperty},
+    {::onc::openvpn::kStaticChallenge, shill::kOpenVPNStaticChallengeProperty},
+    {::onc::openvpn::kTLSAuthContents, shill::kOpenVPNTLSAuthContentsProperty},
+    {::onc::openvpn::kTLSRemote, shill::kOpenVPNTLSRemoteProperty},
+    {::onc::vpn::kUsername, shill::kOpenVPNUserProperty},
+    {::onc::openvpn::kVerifyHash, shill::kOpenVPNVerifyHashProperty},
     {NULL}};
 
 const FieldTranslationEntry verify_x509_fields[] = {
-    { ::onc::verify_x509::kName, shill::kOpenVPNVerifyX509NameProperty},
-    { ::onc::verify_x509::kType, shill::kOpenVPNVerifyX509TypeProperty},
+    {::onc::verify_x509::kName, shill::kOpenVPNVerifyX509NameProperty},
+    {::onc::verify_x509::kType, shill::kOpenVPNVerifyX509TypeProperty},
     {NULL}};
 
 const FieldTranslationEntry vpn_fields[] = {
-    { ::onc::vpn::kAutoConnect, shill::kAutoConnectProperty},
+    {::onc::vpn::kAutoConnect, shill::kAutoConnectProperty},
     // These fields are converted during translation, see onc_translator_*.
     // { ::onc::vpn::kHost, shill::kProviderHostProperty},
     // { ::onc::vpn::kType, shill::kProviderTypeProperty },
     {NULL}};
 
 const FieldTranslationEntry wifi_fields[] = {
-    { ::onc::wifi::kAutoConnect, shill::kAutoConnectProperty},
-    { ::onc::wifi::kBSSID, shill::kWifiBSsid},
+    {::onc::wifi::kAutoConnect, shill::kAutoConnectProperty},
+    {::onc::wifi::kBSSID, shill::kWifiBSsid},
     // This dictionary is converted during translation, see onc_translator_*.
     // { ::onc::wifi::kEAP, shill::kEap*},
-    { ::onc::wifi::kFrequency, shill::kWifiFrequency},
-    { ::onc::wifi::kFrequencyList, shill::kWifiFrequencyListProperty},
-    { ::onc::wifi::kHexSSID, shill::kWifiHexSsid},
-    { ::onc::wifi::kHiddenSSID, shill::kWifiHiddenSsid},
-    { ::onc::wifi::kPassphrase, shill::kPassphraseProperty},
+    {::onc::wifi::kFrequency, shill::kWifiFrequency},
+    {::onc::wifi::kFrequencyList, shill::kWifiFrequencyListProperty},
+    {::onc::wifi::kHexSSID, shill::kWifiHexSsid},
+    {::onc::wifi::kHiddenSSID, shill::kWifiHiddenSsid},
+    {::onc::wifi::kPassphrase, shill::kPassphraseProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::wifi::kSecurity, shill::kSecurityClassProperty },
-    { ::onc::wifi::kSignalStrength, shill::kSignalStrengthProperty},
+    {::onc::wifi::kSignalStrength, shill::kSignalStrengthProperty},
     {NULL}};
 
 const FieldTranslationEntry wimax_fields[] = {
-    { ::onc::wimax::kAutoConnect, shill::kAutoConnectProperty},
+    {::onc::wimax::kAutoConnect, shill::kAutoConnectProperty},
     // This dictionary is converted during translation, see onc_translator_*.
     // { ::onc::wimax::kEAP, shill::kEap*},
-    { ::onc::wimax::kSignalStrength, shill::kSignalStrengthProperty},
+    {::onc::wimax::kSignalStrength, shill::kSignalStrengthProperty},
     {NULL}};
 
 const FieldTranslationEntry cellular_apn_fields[] = {
-    { ::onc::cellular_apn::kAccessPointName, shill::kApnProperty},
-    { ::onc::cellular_apn::kName, shill::kApnNameProperty},
-    { ::onc::cellular_apn::kUsername, shill::kApnUsernameProperty},
-    { ::onc::cellular_apn::kPassword, shill::kApnPasswordProperty},
-    { ::onc::cellular_apn::kLocalizedName, shill::kApnLocalizedNameProperty},
-    { ::onc::cellular_apn::kLanguage, shill::kApnLanguageProperty},
+    {::onc::cellular_apn::kAccessPointName, shill::kApnProperty},
+    {::onc::cellular_apn::kName, shill::kApnNameProperty},
+    {::onc::cellular_apn::kUsername, shill::kApnUsernameProperty},
+    {::onc::cellular_apn::kPassword, shill::kApnPasswordProperty},
+    {::onc::cellular_apn::kLocalizedName, shill::kApnLocalizedNameProperty},
+    {::onc::cellular_apn::kLanguage, shill::kApnLanguageProperty},
     {NULL}};
 
 const FieldTranslationEntry cellular_found_network_fields[] = {
-    { ::onc::cellular_found_network::kNetworkId, shill::kNetworkIdProperty},
-    { ::onc::cellular_found_network::kStatus, shill::kStatusProperty},
-    { ::onc::cellular_found_network::kTechnology, shill::kTechnologyProperty},
-    { ::onc::cellular_found_network::kShortName, shill::kShortNameProperty},
-    { ::onc::cellular_found_network::kLongName, shill::kLongNameProperty},
+    {::onc::cellular_found_network::kNetworkId, shill::kNetworkIdProperty},
+    {::onc::cellular_found_network::kStatus, shill::kStatusProperty},
+    {::onc::cellular_found_network::kTechnology, shill::kTechnologyProperty},
+    {::onc::cellular_found_network::kShortName, shill::kShortNameProperty},
+    {::onc::cellular_found_network::kLongName, shill::kLongNameProperty},
     {NULL}};
 
 const FieldTranslationEntry cellular_provider_fields[] = {
-    { ::onc::cellular_provider::kCode, shill::kOperatorCodeKey},
-    { ::onc::cellular_provider::kCountry, shill::kOperatorCountryKey},
-    { ::onc::cellular_provider::kName, shill::kOperatorNameKey},
+    {::onc::cellular_provider::kCode, shill::kOperatorCodeKey},
+    {::onc::cellular_provider::kCountry, shill::kOperatorCountryKey},
+    {::onc::cellular_provider::kName, shill::kOperatorNameKey},
     {NULL}};
 
 const FieldTranslationEntry sim_lock_status_fields[] = {
-    { ::onc::sim_lock_status::kLockEnabled, shill::kSIMLockEnabledProperty},
-    { ::onc::sim_lock_status::kLockType, shill::kSIMLockTypeProperty},
-    { ::onc::sim_lock_status::kRetriesLeft, shill::kSIMLockRetriesLeftProperty},
+    {::onc::sim_lock_status::kLockEnabled, shill::kSIMLockEnabledProperty},
+    {::onc::sim_lock_status::kLockType, shill::kSIMLockTypeProperty},
+    {::onc::sim_lock_status::kRetriesLeft, shill::kSIMLockRetriesLeftProperty},
     {NULL}};
 
 // This must only contain Service properties and not Device properties.
 // For Device properties see kCellularDeviceTable.
 const FieldTranslationEntry cellular_fields[] = {
-    { ::onc::cellular::kActivationType, shill::kActivationTypeProperty},
+    {::onc::cellular::kActivationType, shill::kActivationTypeProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::cellular::kActivationState, shill::kActivationStateProperty},
-    { ::onc::vpn::kAutoConnect, shill::kAutoConnectProperty},
-    { ::onc::cellular::kNetworkTechnology, shill::kNetworkTechnologyProperty},
+    {::onc::vpn::kAutoConnect, shill::kAutoConnectProperty},
+    // This field is converted during translation, see onc_translator_*.
+    // { ::onc::cellular::kNetworkTechnology,
+    //   shill::kNetworkTechnologyProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::cellular::kRoamingState, shill::kRoamingStateProperty},
-    { ::onc::cellular::kSignalStrength, shill::kSignalStrengthProperty},
+    {::onc::cellular::kSignalStrength, shill::kSignalStrengthProperty},
     {NULL}};
 
 const FieldTranslationEntry network_fields[] = {
-    { ::onc::network_config::kGUID, shill::kGuidProperty},
-    { ::onc::network_config::kConnectable, shill::kConnectableProperty },
-    { ::onc::network_config::kErrorState, shill::kErrorProperty },
-    { ::onc::network_config::kPriority, shill::kPriorityProperty },
+    {::onc::network_config::kGUID, shill::kGuidProperty},
+    {::onc::network_config::kConnectable, shill::kConnectableProperty},
+    {::onc::network_config::kErrorState, shill::kErrorProperty},
+    {::onc::network_config::kPriority, shill::kPriorityProperty},
 
     // Shill doesn't allow setting the name for non-VPN networks.
     // Name is conditionally translated, see onc_translator_*.
@@ -196,22 +199,22 @@
     {NULL}};
 
 const FieldTranslationEntry ipconfig_fields[] = {
-    { ::onc::ipconfig::kIPAddress, shill::kAddressProperty},
-    { ::onc::ipconfig::kGateway, shill::kGatewayProperty},
-    { ::onc::ipconfig::kRoutingPrefix, shill::kPrefixlenProperty},
-    { ::onc::ipconfig::kNameServers, shill::kNameServersProperty},
+    {::onc::ipconfig::kIPAddress, shill::kAddressProperty},
+    {::onc::ipconfig::kGateway, shill::kGatewayProperty},
+    {::onc::ipconfig::kRoutingPrefix, shill::kPrefixlenProperty},
+    {::onc::ipconfig::kNameServers, shill::kNameServersProperty},
     // This field is converted during translation, see ShillToONCTranslator::
     // TranslateIPConfig. It is only converted from Shill->ONC.
     // { ::onc::ipconfig::kType, shill::kMethodProperty},
-    { ::onc::ipconfig::kWebProxyAutoDiscoveryUrl,
-      shill::kWebProxyAutoDiscoveryUrlProperty},
+    {::onc::ipconfig::kWebProxyAutoDiscoveryUrl,
+     shill::kWebProxyAutoDiscoveryUrlProperty},
     {NULL}};
 
 const FieldTranslationEntry static_or_saved_ipconfig_fields[] = {
-    { ::onc::ipconfig::kIPAddress, shill::kAddressProperty},
-    { ::onc::ipconfig::kGateway, shill::kGatewayProperty},
-    { ::onc::ipconfig::kRoutingPrefix, shill::kPrefixlenProperty},
-    { ::onc::ipconfig::kNameServers, shill::kNameServersProperty},
+    {::onc::ipconfig::kIPAddress, shill::kAddressProperty},
+    {::onc::ipconfig::kGateway, shill::kGatewayProperty},
+    {::onc::ipconfig::kRoutingPrefix, shill::kPrefixlenProperty},
+    {::onc::ipconfig::kNameServers, shill::kNameServersProperty},
     {NULL}};
 
 struct OncValueTranslationEntry {
@@ -220,30 +223,29 @@
 };
 
 const OncValueTranslationEntry onc_value_translation_table[] = {
-  { &kEAPSignature, eap_fields },
-  { &kIPsecSignature, ipsec_fields },
-  { &kL2TPSignature, l2tp_fields },
-  { &kXAUTHSignature, xauth_fields },
-  { &kOpenVPNSignature, openvpn_fields },
-  { &kVerifyX509Signature, verify_x509_fields },
-  { &kVPNSignature, vpn_fields },
-  { &kWiFiSignature, wifi_fields },
-  { &kWiFiWithStateSignature, wifi_fields },
-  { &kWiMAXSignature, wimax_fields },
-  { &kWiMAXWithStateSignature, wimax_fields },
-  { &kCellularApnSignature, cellular_apn_fields },
-  { &kCellularFoundNetworkSignature, cellular_found_network_fields },
-  { &kCellularProviderSignature, cellular_provider_fields },
-  { &kSIMLockStatusSignature, sim_lock_status_fields },
-  { &kCellularSignature, cellular_fields },
-  { &kCellularWithStateSignature, cellular_fields },
-  { &kNetworkWithStateSignature, network_fields },
-  { &kNetworkConfigurationSignature, network_fields },
-  { &kIPConfigSignature, ipconfig_fields },
-  { &kSavedIPConfigSignature, static_or_saved_ipconfig_fields },
-  { &kStaticIPConfigSignature, static_or_saved_ipconfig_fields },
-  { NULL }
-};
+    {&kEAPSignature, eap_fields},
+    {&kIPsecSignature, ipsec_fields},
+    {&kL2TPSignature, l2tp_fields},
+    {&kXAUTHSignature, xauth_fields},
+    {&kOpenVPNSignature, openvpn_fields},
+    {&kVerifyX509Signature, verify_x509_fields},
+    {&kVPNSignature, vpn_fields},
+    {&kWiFiSignature, wifi_fields},
+    {&kWiFiWithStateSignature, wifi_fields},
+    {&kWiMAXSignature, wimax_fields},
+    {&kWiMAXWithStateSignature, wimax_fields},
+    {&kCellularApnSignature, cellular_apn_fields},
+    {&kCellularFoundNetworkSignature, cellular_found_network_fields},
+    {&kCellularProviderSignature, cellular_provider_fields},
+    {&kSIMLockStatusSignature, sim_lock_status_fields},
+    {&kCellularSignature, cellular_fields},
+    {&kCellularWithStateSignature, cellular_fields},
+    {&kNetworkWithStateSignature, network_fields},
+    {&kNetworkConfigurationSignature, network_fields},
+    {&kIPConfigSignature, ipconfig_fields},
+    {&kSavedIPConfigSignature, static_or_saved_ipconfig_fields},
+    {&kStaticIPConfigSignature, static_or_saved_ipconfig_fields},
+    {NULL}};
 
 struct NestedShillDictionaryEntry {
   const OncValueSignature* onc_signature;
@@ -251,21 +253,15 @@
   const char* const* shill_property_path;
 };
 
-const char* cellular_apn_path_entries[] = {
-  shill::kCellularApnProperty,
-  NULL
-};
+const char* cellular_apn_path_entries[] = {shill::kCellularApnProperty, NULL};
 
-const char* static_ip_config_path_entries[] = {
-  shill::kStaticIPConfigProperty,
-  NULL
-};
+const char* static_ip_config_path_entries[] = {shill::kStaticIPConfigProperty,
+                                               NULL};
 
 const NestedShillDictionaryEntry nested_shill_dictionaries[] = {
-  { &kCellularApnSignature, cellular_apn_path_entries },
-  { &kStaticIPConfigSignature, static_ip_config_path_entries },
-  { NULL }
-};
+    {&kCellularApnSignature, cellular_apn_path_entries},
+    {&kStaticIPConfigSignature, static_ip_config_path_entries},
+    {NULL}};
 
 }  // namespace
 
@@ -273,55 +269,70 @@
     // This mapping is ensured in the translation code.
     //  { network_type::kEthernet, shill::kTypeEthernet },
     //  { network_type::kEthernet, shill::kTypeEthernetEap },
-    { ::onc::network_type::kWiFi, shill::kTypeWifi},
-    { ::onc::network_type::kWimax, shill::kTypeWimax},
-    { ::onc::network_type::kCellular, shill::kTypeCellular},
-    { ::onc::network_type::kVPN, shill::kTypeVPN},
+    {::onc::network_type::kWiFi, shill::kTypeWifi},
+    {::onc::network_type::kWimax, shill::kTypeWimax},
+    {::onc::network_type::kCellular, shill::kTypeCellular},
+    {::onc::network_type::kVPN, shill::kTypeVPN},
     {NULL}};
 
 const StringTranslationEntry kVPNTypeTable[] = {
-    { ::onc::vpn::kTypeL2TP_IPsec, shill::kProviderL2tpIpsec},
-    { ::onc::vpn::kOpenVPN, shill::kProviderOpenVpn},
-    { ::onc::vpn::kThirdPartyVpn, shill::kProviderThirdPartyVpn},
+    {::onc::vpn::kTypeL2TP_IPsec, shill::kProviderL2tpIpsec},
+    {::onc::vpn::kOpenVPN, shill::kProviderOpenVpn},
+    {::onc::vpn::kThirdPartyVpn, shill::kProviderThirdPartyVpn},
     {NULL}};
 
 const StringTranslationEntry kWiFiSecurityTable[] = {
-    { ::onc::wifi::kSecurityNone, shill::kSecurityNone},
-    { ::onc::wifi::kWEP_PSK, shill::kSecurityWep},
-    { ::onc::wifi::kWPA_PSK, shill::kSecurityPsk},
-    { ::onc::wifi::kWPA_EAP, shill::kSecurity8021x},
+    {::onc::wifi::kSecurityNone, shill::kSecurityNone},
+    {::onc::wifi::kWEP_PSK, shill::kSecurityWep},
+    {::onc::wifi::kWPA_PSK, shill::kSecurityPsk},
+    {::onc::wifi::kWPA_EAP, shill::kSecurity8021x},
     {NULL}};
 
 const StringTranslationEntry kEAPOuterTable[] = {
-    { ::onc::eap::kPEAP, shill::kEapMethodPEAP},
-    { ::onc::eap::kEAP_TLS, shill::kEapMethodTLS},
-    { ::onc::eap::kEAP_TTLS, shill::kEapMethodTTLS},
-    { ::onc::eap::kLEAP, shill::kEapMethodLEAP},
+    {::onc::eap::kPEAP, shill::kEapMethodPEAP},
+    {::onc::eap::kEAP_TLS, shill::kEapMethodTLS},
+    {::onc::eap::kEAP_TTLS, shill::kEapMethodTTLS},
+    {::onc::eap::kLEAP, shill::kEapMethodLEAP},
     {NULL}};
 
 // Translation of the EAP.Inner field in case of EAP.Outer == PEAP
 const StringTranslationEntry kEAP_PEAP_InnerTable[] = {
-    { ::onc::eap::kMD5, shill::kEapPhase2AuthPEAPMD5},
-    { ::onc::eap::kMSCHAPv2, shill::kEapPhase2AuthPEAPMSCHAPV2}, {NULL}};
+    {::onc::eap::kMD5, shill::kEapPhase2AuthPEAPMD5},
+    {::onc::eap::kMSCHAPv2, shill::kEapPhase2AuthPEAPMSCHAPV2},
+    {NULL}};
 
 // Translation of the EAP.Inner field in case of EAP.Outer == TTLS
 const StringTranslationEntry kEAP_TTLS_InnerTable[] = {
-    { ::onc::eap::kMD5, shill::kEapPhase2AuthTTLSMD5},
-    { ::onc::eap::kMSCHAPv2, shill::kEapPhase2AuthTTLSMSCHAPV2},
-    { ::onc::eap::kPAP, shill::kEapPhase2AuthTTLSPAP},
+    {::onc::eap::kMD5, shill::kEapPhase2AuthTTLSMD5},
+    {::onc::eap::kMSCHAPv2, shill::kEapPhase2AuthTTLSMSCHAPV2},
+    {::onc::eap::kPAP, shill::kEapPhase2AuthTTLSPAP},
     {NULL}};
 
 const StringTranslationEntry kActivationStateTable[] = {
-    { ::onc::cellular::kActivated, shill::kActivationStateActivated},
-    { ::onc::cellular::kActivating, shill::kActivationStateActivating},
-    { ::onc::cellular::kNotActivated, shill::kActivationStateNotActivated},
-    { ::onc::cellular::kPartiallyActivated,
-      shill::kActivationStatePartiallyActivated},
+    {::onc::cellular::kActivated, shill::kActivationStateActivated},
+    {::onc::cellular::kActivating, shill::kActivationStateActivating},
+    {::onc::cellular::kNotActivated, shill::kActivationStateNotActivated},
+    {::onc::cellular::kPartiallyActivated,
+     shill::kActivationStatePartiallyActivated},
+    {NULL}};
+
+const StringTranslationEntry kNetworkTechnologyTable[] = {
+    {::onc::cellular::kTechnologyCdma1Xrtt, shill::kNetworkTechnology1Xrtt},
+    {::onc::cellular::kTechnologyGsm, shill::kNetworkTechnologyGsm},
+    {::onc::cellular::kTechnologyEdge, shill::kNetworkTechnologyEdge},
+    {::onc::cellular::kTechnologyEvdo, shill::kNetworkTechnologyEvdo},
+    {::onc::cellular::kTechnologyGprs, shill::kNetworkTechnologyGprs},
+    {::onc::cellular::kTechnologyHspa, shill::kNetworkTechnologyHspa},
+    {::onc::cellular::kTechnologyHspaPlus, shill::kNetworkTechnologyHspaPlus},
+    {::onc::cellular::kTechnologyLte, shill::kNetworkTechnologyLte},
+    {::onc::cellular::kTechnologyLteAdvanced,
+     shill::kNetworkTechnologyLteAdvanced},
+    {::onc::cellular::kTechnologyUmts, shill::kNetworkTechnologyUmts},
     {NULL}};
 
 const StringTranslationEntry kRoamingStateTable[] = {
-    { ::onc::cellular::kRoamingHome, shill::kRoamingStateHome},
-    { ::onc::cellular::kRoamingRoaming, shill::kRoamingStateRoaming},
+    {::onc::cellular::kRoamingHome, shill::kRoamingStateHome},
+    {::onc::cellular::kRoamingRoaming, shill::kRoamingStateRoaming},
     {NULL}};
 
 // This must contain only Shill Device properties and no Service properties.
@@ -329,30 +340,30 @@
 const FieldTranslationEntry kCellularDeviceTable[] = {
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::cellular::kAPNList, shill::kCellularApnListProperty},
-    { ::onc::cellular::kAllowRoaming, shill::kCellularAllowRoamingProperty},
-    { ::onc::cellular::kCarrier, shill::kCarrierProperty},
-    { ::onc::cellular::kESN, shill::kEsnProperty},
-    { ::onc::cellular::kFamily, shill::kTechnologyFamilyProperty},
-    { ::onc::cellular::kFirmwareRevision, shill::kFirmwareRevisionProperty},
+    {::onc::cellular::kAllowRoaming, shill::kCellularAllowRoamingProperty},
+    {::onc::cellular::kCarrier, shill::kCarrierProperty},
+    {::onc::cellular::kESN, shill::kEsnProperty},
+    {::onc::cellular::kFamily, shill::kTechnologyFamilyProperty},
+    {::onc::cellular::kFirmwareRevision, shill::kFirmwareRevisionProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::cellular::kFoundNetworks, shill::kFoundNetworksProperty},
-    { ::onc::cellular::kHardwareRevision, shill::kHardwareRevisionProperty},
+    {::onc::cellular::kHardwareRevision, shill::kHardwareRevisionProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::cellular::kHomeProvider, shill::kHomeProviderProperty},
-    { ::onc::cellular::kICCID, shill::kIccidProperty},
-    { ::onc::cellular::kIMEI, shill::kImeiProperty},
-    { ::onc::cellular::kIMSI, shill::kImsiProperty},
-    { ::onc::cellular::kManufacturer, shill::kManufacturerProperty},
-    { ::onc::cellular::kMDN, shill::kMdnProperty},
-    { ::onc::cellular::kMEID, shill::kMeidProperty},
-    { ::onc::cellular::kMIN, shill::kMinProperty},
-    { ::onc::cellular::kModelID, shill::kModelIDProperty},
-    { ::onc::cellular::kPRLVersion, shill::kPRLVersionProperty},
+    {::onc::cellular::kICCID, shill::kIccidProperty},
+    {::onc::cellular::kIMEI, shill::kImeiProperty},
+    {::onc::cellular::kIMSI, shill::kImsiProperty},
+    {::onc::cellular::kManufacturer, shill::kManufacturerProperty},
+    {::onc::cellular::kMDN, shill::kMdnProperty},
+    {::onc::cellular::kMEID, shill::kMeidProperty},
+    {::onc::cellular::kMIN, shill::kMinProperty},
+    {::onc::cellular::kModelID, shill::kModelIDProperty},
+    {::onc::cellular::kPRLVersion, shill::kPRLVersionProperty},
     // This field is converted during translation, see onc_translator_*.
     // { ::onc::cellular::kSIMLockStatus, shill::kSIMLockStatusProperty},
-    { ::onc::cellular::kSIMPresent, shill::kSIMPresentProperty},
-    { ::onc::cellular::kSupportedCarriers, shill::kSupportedCarriersProperty},
-    { ::onc::cellular::kSupportNetworkScan, shill::kSupportNetworkScanProperty},
+    {::onc::cellular::kSIMPresent, shill::kSIMPresentProperty},
+    {::onc::cellular::kSupportedCarriers, shill::kSupportedCarriersProperty},
+    {::onc::cellular::kSupportNetworkScan, shill::kSupportNetworkScanProperty},
     {NULL}};
 
 const FieldTranslationEntry* GetFieldTranslationTable(
@@ -384,8 +395,8 @@
 bool GetShillPropertyName(const std::string& onc_field_name,
                           const FieldTranslationEntry table[],
                           std::string* shill_property_name) {
-  for (const FieldTranslationEntry* it = table;
-       it->onc_field_name != NULL; ++it) {
+  for (const FieldTranslationEntry* it = table; it->onc_field_name != NULL;
+       ++it) {
     if (it->onc_field_name != onc_field_name)
       continue;
     *shill_property_name = it->shill_property_name;
diff --git a/chromeos/network/onc/onc_translation_tables.h b/chromeos/network/onc/onc_translation_tables.h
index b737542..058c4f23 100644
--- a/chromeos/network/onc/onc_translation_tables.h
+++ b/chromeos/network/onc/onc_translation_tables.h
@@ -33,6 +33,7 @@
 CHROMEOS_EXPORT extern const StringTranslationEntry kEAP_PEAP_InnerTable[];
 CHROMEOS_EXPORT extern const StringTranslationEntry kEAP_TTLS_InnerTable[];
 CHROMEOS_EXPORT extern const StringTranslationEntry kActivationStateTable[];
+CHROMEOS_EXPORT extern const StringTranslationEntry kNetworkTechnologyTable[];
 CHROMEOS_EXPORT extern const StringTranslationEntry kRoamingStateTable[];
 
 // A separate translation table for cellular properties that are stored in a
diff --git a/chromeos/network/onc/onc_translator_onc_to_shill.cc b/chromeos/network/onc/onc_translator_onc_to_shill.cc
index 3cf9789f..9cea033 100644
--- a/chromeos/network/onc/onc_translator_onc_to_shill.cc
+++ b/chromeos/network/onc/onc_translator_onc_to_shill.cc
@@ -126,7 +126,6 @@
   CopyFieldsAccordingToSignature();
 }
 
-
 void LocalTranslator::TranslateOpenVPN() {
   // SaveCredentials needs special handling when translating from Shill -> ONC
   // so handle it explicitly here.
@@ -216,8 +215,8 @@
 
 void LocalTranslator::TranslateWiFi() {
   std::string security;
-  if (onc_object_->GetStringWithoutPathExpansion(
-          ::onc::wifi::kSecurity, &security)) {
+  if (onc_object_->GetStringWithoutPathExpansion(::onc::wifi::kSecurity,
+                                                 &security)) {
     TranslateWithTableAndSet(security, kWiFiSecurityTable,
                              shill::kSecurityClassProperty);
   }
@@ -249,9 +248,9 @@
     // Shill.
     onc_object_->GetStringWithoutPathExpansion(::onc::eap::kInner, &inner);
     if (inner != ::onc::eap::kAutomatic) {
-      const StringTranslationEntry* table =
-          outer == ::onc::eap::kPEAP ? kEAP_PEAP_InnerTable :
-                                       kEAP_TTLS_InnerTable;
+      const StringTranslationEntry* table = outer == ::onc::eap::kPEAP
+                                                ? kEAP_PEAP_InnerTable
+                                                : kEAP_TTLS_InnerTable;
       TranslateWithTableAndSet(inner, table, shill::kEapPhase2AuthProperty);
     }
   }
@@ -328,8 +327,8 @@
   if (!value || !field_translation_table_)
     return;
   std::string shill_property_name;
-  if (!GetShillPropertyName(
-          onc_name, field_translation_table_, &shill_property_name)) {
+  if (!GetShillPropertyName(onc_name, field_translation_table_,
+                            &shill_property_name)) {
     return;
   }
 
@@ -366,8 +365,7 @@
       GetPathToNestedShillDictionary(signature);
   for (std::vector<std::string>::const_iterator it =
            path_to_shill_dictionary.begin();
-       it != path_to_shill_dictionary.end();
-       ++it) {
+       it != path_to_shill_dictionary.end(); ++it) {
     base::DictionaryValue* nested_shill_dict = NULL;
     target_shill_dictionary->GetDictionaryWithoutPathExpansion(
         *it, &nested_shill_dict);
diff --git a/chromeos/network/onc/onc_translator_shill_to_onc.cc b/chromeos/network/onc/onc_translator_shill_to_onc.cc
index 43e7b3a..a97d51e7 100644
--- a/chromeos/network/onc/onc_translator_shill_to_onc.cc
+++ b/chromeos/network/onc/onc_translator_shill_to_onc.cc
@@ -66,8 +66,7 @@
       : shill_dictionary_(&shill_dictionary),
         onc_source_(onc_source),
         onc_signature_(&onc_signature),
-        field_translation_table_(field_translation_table) {
-  }
+        field_translation_table_(field_translation_table) {}
 
   // Translates the associated Shill dictionary and creates an ONC object of the
   // given signature.
@@ -222,8 +221,7 @@
     const base::Value* shill_value = NULL;
     if (!field_translation_table_ ||
         !GetShillPropertyName(field_signature->onc_field_name,
-                              field_translation_table_,
-                              &shill_property_name) ||
+                              field_translation_table_, &shill_property_name) ||
         !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
                                                     &shill_value)) {
       continue;
@@ -235,23 +233,21 @@
       // Shill wants all Provider/VPN fields to be strings. Translates these
       // strings back to the correct ONC type.
       translated = ConvertStringToValue(
-          shill_str,
-          field_signature->value_signature->onc_type);
+          shill_str, field_signature->value_signature->onc_type);
 
       if (translated.get() == NULL) {
         LOG(ERROR) << "Shill property '" << shill_property_name
                    << "' with value " << *shill_value
                    << " couldn't be converted to base::Value::Type "
-                   << field_signature->value_signature->onc_type
-                   << ": " << GetName();
+                   << field_signature->value_signature->onc_type << ": "
+                   << GetName();
       } else {
         onc_object_->SetWithoutPathExpansion(onc_field_name,
                                              translated.release());
       }
     } else {
-      LOG(ERROR) << "Shill property '" << shill_property_name
-                 << "' has value " << *shill_value
-                 << ", but expected a string: " << GetName();
+      LOG(ERROR) << "Shill property '" << shill_property_name << "' has value "
+                 << *shill_value << ", but expected a string: " << GetName();
     }
   }
 }
@@ -294,8 +290,8 @@
   std::string shill_provider_type, onc_provider_type;
   provider->GetStringWithoutPathExpansion(shill::kTypeProperty,
                                           &shill_provider_type);
-  if (!TranslateStringToONC(
-          kVPNTypeTable, shill_provider_type, &onc_provider_type)) {
+  if (!TranslateStringToONC(kVPNTypeTable, shill_provider_type,
+                            &onc_provider_type)) {
     return;
   }
   onc_object_->SetStringWithoutPathExpansion(::onc::vpn::kType,
@@ -323,15 +319,13 @@
   if (onc_provider_type != ::onc::vpn::kThirdPartyVpn &&
       shill_dictionary_->GetBooleanWithoutPathExpansion(
           shill::kSaveCredentialsProperty, &save_credentials)) {
-    SetNestedOncValue(provider_type_dictionary,
-                      ::onc::vpn::kSaveCredentials,
+    SetNestedOncValue(provider_type_dictionary, ::onc::vpn::kSaveCredentials,
                       base::FundamentalValue(save_credentials));
   }
 }
 
 void ShillToONCTranslator::TranslateWiFiWithState() {
-  TranslateWithTableAndSet(shill::kSecurityClassProperty,
-                           kWiFiSecurityTable,
+  TranslateWithTableAndSet(shill::kSecurityClassProperty, kWiFiSecurityTable,
                            ::onc::wifi::kSecurity);
   bool unknown_encoding = true;
   std::string ssid = shill_property_util::GetSSIDFromProperties(
@@ -360,17 +354,20 @@
   TranslateWithTableAndSet(shill::kActivationStateProperty,
                            kActivationStateTable,
                            ::onc::cellular::kActivationState);
+  TranslateWithTableAndSet(shill::kNetworkTechnologyProperty,
+                           kNetworkTechnologyTable,
+                           ::onc::cellular::kNetworkTechnology);
   const base::DictionaryValue* dictionary = NULL;
   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
-        shill::kServingOperatorProperty, &dictionary)) {
+          shill::kServingOperatorProperty, &dictionary)) {
     TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary);
   }
   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
-        shill::kCellularApnProperty, &dictionary)) {
+          shill::kCellularApnProperty, &dictionary)) {
     TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary);
   }
   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
-        shill::kCellularLastGoodApnProperty, &dictionary)) {
+          shill::kCellularLastGoodApnProperty, &dictionary)) {
     TranslateAndAddNestedObject(::onc::cellular::kLastGoodAPN, *dictionary);
   }
   const base::DictionaryValue* device_dictionary = NULL;
@@ -436,8 +433,8 @@
   std::string onc_network_type = ::onc::network_type::kEthernet;
   if (shill_network_type != shill::kTypeEthernet &&
       shill_network_type != shill::kTypeEthernetEap) {
-    TranslateStringToONC(
-        kNetworkTypeTable, shill_network_type, &onc_network_type);
+    TranslateStringToONC(kNetworkTypeTable, shill_network_type,
+                         &onc_network_type);
   }
   // Translate nested Cellular, WiFi, etc. properties.
   if (!onc_network_type.empty()) {
@@ -487,8 +484,8 @@
       source = ::onc::network_config::kSourceUser;
     else
       source = ::onc::network_config::kSourceNone;
-    onc_object_->SetStringWithoutPathExpansion(
-        ::onc::network_config::kSource, source);
+    onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kSource,
+                                               source);
   }
 
   // Use a human-readable aa:bb format for any hardware MAC address. Note:
@@ -514,7 +511,7 @@
 
   const base::DictionaryValue* saved_ipconfig = nullptr;
   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
-        shill::kSavedIPConfigProperty, &saved_ipconfig)) {
+          shill::kSavedIPConfigProperty, &saved_ipconfig)) {
     TranslateAndAddNestedObject(::onc::network_config::kSavedIPConfig,
                                 *saved_ipconfig);
   }
@@ -598,8 +595,8 @@
     NOTREACHED() << "Unable to find signature for field: " << onc_field_name;
     return;
   }
-  ShillToONCTranslator nested_translator(
-      dictionary, onc_source_, *field_signature->value_signature);
+  ShillToONCTranslator nested_translator(dictionary, onc_source_,
+                                         *field_signature->value_signature);
   scoped_ptr<base::DictionaryValue> nested_object =
       nested_translator.CreateTranslatedONCObject();
   if (nested_object->empty())
@@ -612,8 +609,8 @@
     const std::string& onc_field_name,
     const base::Value& value) {
   base::DictionaryValue* nested;
-  if (!onc_object_->GetDictionaryWithoutPathExpansion(
-          onc_dictionary_name, &nested)) {
+  if (!onc_object_->GetDictionaryWithoutPathExpansion(onc_dictionary_name,
+                                                      &nested)) {
     nested = new base::DictionaryValue;
     onc_object_->SetWithoutPathExpansion(onc_dictionary_name, nested);
   }
@@ -633,14 +630,13 @@
   }
   DCHECK(field_signature->value_signature->onc_array_entry_signature);
   scoped_ptr<base::ListValue> result(new base::ListValue());
-  for (base::ListValue::const_iterator it = list.begin();
-       it != list.end(); ++it) {
+  for (base::ListValue::const_iterator it = list.begin(); it != list.end();
+       ++it) {
     const base::DictionaryValue* shill_value = NULL;
     if (!(*it)->GetAsDictionary(&shill_value))
       continue;
     ShillToONCTranslator nested_translator(
-        *shill_value,
-        onc_source_,
+        *shill_value, onc_source_,
         *field_signature->value_signature->onc_array_entry_signature);
     scoped_ptr<base::DictionaryValue> nested_object =
         nested_translator.CreateTranslatedONCObject();
@@ -677,21 +673,19 @@
   const base::Value* shill_value = NULL;
   if (!field_translation_table_ ||
       !GetShillPropertyName(field_signature->onc_field_name,
-                            field_translation_table_,
-                            &shill_property_name) ||
+                            field_translation_table_, &shill_property_name) ||
       !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
                                                   &shill_value)) {
     return;
   }
 
   if (shill_value->GetType() != field_signature->value_signature->onc_type) {
-    LOG(ERROR) << "Shill property '" << shill_property_name
-               << "' with value " << *shill_value
-               << " has base::Value::Type " << shill_value->GetType()
-               << " but ONC field '" << field_signature->onc_field_name
-               << "' requires type "
-               << field_signature->value_signature->onc_type
-               << ": " << GetName();
+    LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
+               << *shill_value << " has base::Value::Type "
+               << shill_value->GetType() << " but ONC field '"
+               << field_signature->onc_field_name << "' requires type "
+               << field_signature->value_signature->onc_type << ": "
+               << GetName();
     return;
   }
 
diff --git a/chromeos/test/data/network/shill_cellular_with_state.json b/chromeos/test/data/network/shill_cellular_with_state.json
index 82e3fcb..149eeac 100644
--- a/chromeos/test/data/network/shill_cellular_with_state.json
+++ b/chromeos/test/data/network/shill_cellular_with_state.json
@@ -4,6 +4,7 @@
   "Name": "Test Network",
   "Cellular.ActivationState": "activated",
   "Cellular.ActivationType": "OTASP",
+  "Cellular.NetworkTechnology": "LTE Advanced",
   "Cellular.RoamingState": "home",
   "Cellular.ServingOperator": {
     "code": "test-code",
diff --git a/chromeos/test/data/network/translation_of_shill_cellular_with_state.onc b/chromeos/test/data/network/translation_of_shill_cellular_with_state.onc
index 71847b2..da0b0d87 100644
--- a/chromeos/test/data/network/translation_of_shill_cellular_with_state.onc
+++ b/chromeos/test/data/network/translation_of_shill_cellular_with_state.onc
@@ -11,6 +11,7 @@
       "Country": "us",
       "Name": "cellular_provider"
     },
+    "NetworkTechnology": "LTEAdvanced",
     "RoamingState": "Home",
     "ServingOperator": {
       "Code": "test-code",
diff --git a/components/OWNERS b/components/OWNERS
index 89bfbf50..4dec212 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -48,6 +48,7 @@
 per-file data_reduction_proxy*=marq@chromium.org
 per-file data_reduction_proxy*=bolian@chromium.org
 per-file data_reduction_proxy*=sclittle@chromium.org
+per-file data_reduction_proxy*=jeremyim@chromium.org
 
 per-file devtools_bridge.gyp=mnaganov@chromium.org
 per-file devtools_bridge.gyp=serya@chromium.org
@@ -174,6 +175,7 @@
 
 per-file precache*=bengr@chromium.org
 per-file precache*=sclittle@chromium.org
+per-file precache*=jeremyim@chromium.org
 
 per-file printing*=alekseys@chromium.org
 per-file printing*=gene@chromium.org
diff --git a/components/autofill/content/browser/wallet/real_pan_wallet_client_unittest.cc b/components/autofill/content/browser/wallet/real_pan_wallet_client_unittest.cc
new file mode 100644
index 0000000..92defc0
--- /dev/null
+++ b/components/autofill/content/browser/wallet/real_pan_wallet_client_unittest.cc
@@ -0,0 +1,196 @@
+// 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 "base/command_line.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/wallet/real_pan_wallet_client.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "google_apis/gaia/fake_identity_provider.h"
+#include "google_apis/gaia/fake_oauth2_token_service.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+namespace wallet {
+
+class RealPanWalletClientTest : public testing::Test,
+                                public RealPanWalletClient::Delegate {
+ public:
+  RealPanWalletClientTest() : result_(AutofillClient::SUCCESS) {}
+  ~RealPanWalletClientTest() override {}
+
+  void SetUp() override {
+    // Silence the warning for mismatching sync and wallet servers.
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kWalletServiceUseSandbox, "0");
+
+    request_context_ = new net::TestURLRequestContextGetter(
+        base::MessageLoopProxy::current());
+    token_service_.reset(new FakeOAuth2TokenService());
+    identity_provider_.reset(new FakeIdentityProvider(token_service_.get()));
+    client_.reset(new RealPanWalletClient(request_context_.get(), this));
+  }
+
+  void TearDown() override { client_.reset(); }
+
+  // RealPanWalletClient::Delegate
+
+  IdentityProvider* GetIdentityProvider() override {
+    return identity_provider_.get();
+  }
+
+  void OnDidGetRealPan(AutofillClient::GetRealPanResult result,
+                       const std::string& real_pan) override {
+    result_ = result;
+    real_pan_ = real_pan;
+  }
+
+ protected:
+  void StartUnmasking() {
+    token_service_->AddAccount("example@gmail.com");
+    identity_provider_->LogIn("example@gmail.com");
+    CreditCard card = test::GetMaskedServerCard();
+    CardUnmaskDelegate::UnmaskResponse response;
+    response.cvc = base::ASCIIToUTF16("123");
+    client_->UnmaskCard(card, response);
+  }
+
+  void IssueOAuthToken() {
+    token_service_->IssueAllTokensForAccount(
+        "example@gmail.com",
+        "totally_real_token",
+        base::Time::Now() + base::TimeDelta::FromDays(10));
+
+    // Verify the auth header.
+    net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+    net::HttpRequestHeaders request_headers;
+    fetcher->GetExtraRequestHeaders(&request_headers);
+    std::string auth_header_value;
+    EXPECT_TRUE(request_headers.GetHeader(
+        net::HttpRequestHeaders::kAuthorization,
+        &auth_header_value)) << request_headers.ToString();
+    EXPECT_EQ("Bearer totally_real_token", auth_header_value);
+  }
+
+  void ReturnResponse(net::HttpStatusCode response_code,
+                      const std::string& response_body) {
+    net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+    ASSERT_TRUE(fetcher);
+    fetcher->set_response_code(response_code);
+    fetcher->SetResponseString(response_body);
+    fetcher->delegate()->OnURLFetchComplete(fetcher);
+  }
+
+  AutofillClient::GetRealPanResult result_;
+  std::string real_pan_;
+
+  content::TestBrowserThreadBundle thread_bundle_;
+  net::TestURLFetcherFactory factory_;
+  scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+  scoped_ptr<FakeOAuth2TokenService> token_service_;
+  scoped_ptr<FakeIdentityProvider> identity_provider_;
+  scoped_ptr<RealPanWalletClient> client_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RealPanWalletClientTest);
+};
+
+TEST_F(RealPanWalletClientTest, OAuthError) {
+  StartUnmasking();
+  token_service_->IssueErrorForAllPendingRequestsForAccount(
+      "example@gmail.com",
+      GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
+  EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_);
+  EXPECT_TRUE(real_pan_.empty());
+}
+
+TEST_F(RealPanWalletClientTest, Success) {
+  StartUnmasking();
+  IssueOAuthToken();
+  ReturnResponse(net::HTTP_OK, "{ \"pan\": \"1234\" }");
+  EXPECT_EQ(AutofillClient::SUCCESS, result_);
+  EXPECT_EQ("1234", real_pan_);
+}
+
+TEST_F(RealPanWalletClientTest, RetryFailure) {
+  StartUnmasking();
+  IssueOAuthToken();
+  ReturnResponse(net::HTTP_OK, "{ \"error\": { \"code\": \"INTERNAL\" } }");
+  EXPECT_EQ(AutofillClient::TRY_AGAIN_FAILURE, result_);
+  EXPECT_EQ("", real_pan_);
+}
+
+TEST_F(RealPanWalletClientTest, PermanentFailure) {
+  StartUnmasking();
+  IssueOAuthToken();
+  ReturnResponse(net::HTTP_OK,
+      "{ \"error\": { \"code\": \"ANYTHING_ELSE\" } }");
+  EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_);
+  EXPECT_EQ("", real_pan_);
+}
+
+TEST_F(RealPanWalletClientTest, MalformedResponse) {
+  StartUnmasking();
+  IssueOAuthToken();
+  ReturnResponse(net::HTTP_OK,
+      "{ \"error_code\": \"WRONG_JSON_FORMAT\" }");
+  EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_);
+  EXPECT_EQ("", real_pan_);
+}
+
+TEST_F(RealPanWalletClientTest, ReauthNeeded) {
+  {
+    StartUnmasking();
+    IssueOAuthToken();
+    ReturnResponse(net::HTTP_UNAUTHORIZED, "");
+    // No response yet.
+    EXPECT_EQ(AutofillClient::SUCCESS, result_);
+    EXPECT_EQ("", real_pan_);
+
+    // Second HTTP_UNAUTHORIZED causes permanent failure.
+    IssueOAuthToken();
+    ReturnResponse(net::HTTP_UNAUTHORIZED, "");
+    EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_);
+    EXPECT_EQ("", real_pan_);
+  }
+
+  result_ = AutofillClient::SUCCESS;
+  real_pan_.clear();
+
+  {
+    StartUnmasking();
+    IssueOAuthToken();
+    ReturnResponse(net::HTTP_UNAUTHORIZED, "");
+    // No response yet.
+    EXPECT_EQ(AutofillClient::SUCCESS, result_);
+    EXPECT_EQ("", real_pan_);
+
+    // HTTP_OK after first HTTP_UNAUTHORIZED results in success.
+    IssueOAuthToken();
+    ReturnResponse(net::HTTP_OK, "{ \"pan\": \"1234\" }");
+    EXPECT_EQ(AutofillClient::SUCCESS, result_);
+    EXPECT_EQ("1234", real_pan_);
+  }
+}
+
+TEST_F(RealPanWalletClientTest, NetworkError) {
+  StartUnmasking();
+  IssueOAuthToken();
+  ReturnResponse(net::HTTP_REQUEST_TIMEOUT, std::string());
+  EXPECT_EQ(AutofillClient::NETWORK_ERROR, result_);
+  EXPECT_EQ("", real_pan_);
+}
+
+TEST_F(RealPanWalletClientTest, OtherError) {
+  StartUnmasking();
+  IssueOAuthToken();
+  ReturnResponse(net::HTTP_FORBIDDEN, std::string());
+  EXPECT_EQ(AutofillClient::PERMANENT_FAILURE, result_);
+  EXPECT_EQ("", real_pan_);
+}
+
+}  // namespace autofill
+}  // namespace wallet
diff --git a/components/autofill/core/browser/address_field.cc b/components/autofill/core/browser/address_field.cc
index 23644cd7..bf96dc2 100644
--- a/components/autofill/core/browser/address_field.cc
+++ b/components/autofill/core/browser/address_field.cc
@@ -20,6 +20,26 @@
 
 namespace autofill {
 
+namespace {
+
+bool SetFieldAndAdvanceCursor(AutofillScanner* scanner, AutofillField** field) {
+  *field = scanner->Cursor();
+  scanner->Advance();
+  return true;
+}
+
+}  // namespace
+
+// Some sites use type="tel" for zip fields (to get a numerical input).
+// http://crbug.com/426958
+const int AddressField::kZipCodeMatchType =
+    MATCH_DEFAULT | MATCH_TELEPHONE | MATCH_NUMBER;
+
+// Select fields are allowed here.  This occurs on top-100 site rediff.com.
+const int AddressField::kCityMatchType = MATCH_DEFAULT | MATCH_SELECT;
+
+const int AddressField::kStateMatchType = MATCH_DEFAULT | MATCH_SELECT;
+
 scoped_ptr<FormField> AddressField::Parse(AutofillScanner* scanner) {
   if (scanner->IsEnd())
     return NULL;
@@ -37,9 +57,7 @@
   while (!scanner->IsEnd()) {
     const size_t cursor = scanner->SaveCursor();
     if (address_field->ParseAddressLines(scanner) ||
-        address_field->ParseCity(scanner) ||
-        address_field->ParseState(scanner) ||
-        address_field->ParseZipCode(scanner) ||
+        address_field->ParseCityStateZipCode(scanner) ||
         address_field->ParseCountry(scanner) ||
         address_field->ParseCompany(scanner)) {
       has_trailing_non_labeled_fields = false;
@@ -216,39 +234,29 @@
 }
 
 bool AddressField::ParseZipCode(AutofillScanner* scanner) {
-  // Parse a zip code.  On some UK pages (e.g. The China Shop2.html) this
-  // is called a "post code".
   if (zip_)
     return false;
 
-  // Some sites use type="tel" for zip fields (to get a numerical input).
-  // http://crbug.com/426958
   if (!ParseFieldSpecifics(scanner,
                            UTF8ToUTF16(kZipCodeRe),
-                           MATCH_DEFAULT | MATCH_TELEPHONE,
+                           kZipCodeMatchType,
                            &zip_)) {
     return false;
   }
 
   // Look for a zip+4, whose field name will also often contain
   // the substring "zip".
-  ParseFieldSpecifics(scanner,
-                      UTF8ToUTF16(kZip4Re),
-                      MATCH_DEFAULT | MATCH_TELEPHONE,
-                      &zip4_);
+  ParseFieldSpecifics(scanner, UTF8ToUTF16(kZip4Re), kZipCodeMatchType, &zip4_);
   return true;
 }
 
 bool AddressField::ParseCity(AutofillScanner* scanner) {
-  // Parse a city name.  Some UK pages (e.g. The China Shop2.html) use
-  // the term "town".
   if (city_)
     return false;
 
-  // Select fields are allowed here.  This occurs on top-100 site rediff.com.
   return ParseFieldSpecifics(scanner,
                              UTF8ToUTF16(kCityRe),
-                             MATCH_DEFAULT | MATCH_SELECT,
+                             kCityMatchType,
                              &city_);
 }
 
@@ -258,8 +266,113 @@
 
   return ParseFieldSpecifics(scanner,
                              UTF8ToUTF16(kStateRe),
-                             MATCH_DEFAULT | MATCH_SELECT,
+                             kStateMatchType,
                              &state_);
 }
 
+bool AddressField::ParseCityStateZipCode(AutofillScanner* scanner) {
+  // Simple cases.
+  if (scanner->IsEnd())
+    return false;
+  if (city_ && state_ && zip_)
+    return false;
+  if (state_ && zip_)
+    return ParseCity(scanner);
+  if (city_ && zip_)
+    return ParseState(scanner);
+  if (city_ && state_)
+    return ParseZipCode(scanner);
+
+  // Check for matches to both name and label.
+  ParseNameLabelResult city_result = ParseNameAndLabelForCity(scanner);
+  if (city_result == RESULT_MATCH_NAME_LABEL)
+    return true;
+  ParseNameLabelResult state_result = ParseNameAndLabelForState(scanner);
+  if (state_result == RESULT_MATCH_NAME_LABEL)
+    return true;
+  ParseNameLabelResult zip_result = ParseNameAndLabelForZipCode(scanner);
+  if (zip_result == RESULT_MATCH_NAME_LABEL)
+    return true;
+
+  // Check if there is only one potential match.
+  bool maybe_city = city_result != RESULT_MATCH_NONE;
+  bool maybe_state = state_result != RESULT_MATCH_NONE;
+  bool maybe_zip = zip_result != RESULT_MATCH_NONE;
+  if (maybe_city && !maybe_state && !maybe_zip)
+    return SetFieldAndAdvanceCursor(scanner, &city_);
+  if (maybe_state && !maybe_city && !maybe_zip)
+    return SetFieldAndAdvanceCursor(scanner, &state_);
+  if (maybe_zip && !maybe_city && !maybe_state)
+    return ParseZipCode(scanner);
+
+  // Otherwise give name priority over label.
+  if (city_result == RESULT_MATCH_NAME)
+    return SetFieldAndAdvanceCursor(scanner, &city_);
+  if (state_result == RESULT_MATCH_NAME)
+    return SetFieldAndAdvanceCursor(scanner, &state_);
+  if (zip_result == RESULT_MATCH_NAME)
+    return ParseZipCode(scanner);
+
+  if (city_result == RESULT_MATCH_LABEL)
+    return SetFieldAndAdvanceCursor(scanner, &city_);
+  if (state_result == RESULT_MATCH_LABEL)
+    return SetFieldAndAdvanceCursor(scanner, &state_);
+  if (zip_result == RESULT_MATCH_LABEL)
+    return ParseZipCode(scanner);
+
+  return false;
+}
+
+AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForZipCode(
+    AutofillScanner* scanner) {
+  if (zip_)
+    return RESULT_MATCH_NONE;
+
+  ParseNameLabelResult result = ParseNameAndLabelSeparately(
+      scanner, UTF8ToUTF16(kZipCodeRe), kZipCodeMatchType, &zip_);
+
+  if (result != RESULT_MATCH_NAME_LABEL || scanner->IsEnd())
+    return result;
+
+  size_t saved_cursor = scanner->SaveCursor();
+  bool found_non_zip4 = ParseCity(scanner);
+  if (found_non_zip4)
+    city_ = nullptr;
+  scanner->RewindTo(saved_cursor);
+  if (!found_non_zip4) {
+    found_non_zip4 = ParseState(scanner);
+    if (found_non_zip4)
+      state_ = nullptr;
+    scanner->RewindTo(saved_cursor);
+  }
+
+  if (!found_non_zip4) {
+    // Look for a zip+4, whose field name will also often contain
+    // the substring "zip".
+    ParseFieldSpecifics(scanner,
+                        UTF8ToUTF16(kZip4Re),
+                        kZipCodeMatchType,
+                        &zip4_);
+  }
+  return result;
+}
+
+AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForCity(
+    AutofillScanner* scanner) {
+  if (city_)
+    return RESULT_MATCH_NONE;
+
+  return ParseNameAndLabelSeparately(
+      scanner, UTF8ToUTF16(kCityRe), kCityMatchType, &city_);
+}
+
+AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForState(
+    AutofillScanner* scanner) {
+  if (state_)
+    return RESULT_MATCH_NONE;
+
+  return ParseNameAndLabelSeparately(
+      scanner, UTF8ToUTF16(kStateRe), kStateMatchType, &state_);
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/address_field.h b/components/autofill/core/browser/address_field.h
index 1966ac4..721545e 100644
--- a/components/autofill/core/browser/address_field.h
+++ b/components/autofill/core/browser/address_field.h
@@ -41,6 +41,10 @@
   FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseTwoLineAddressMissingLabel);
   FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseCompany);
 
+  static const int kZipCodeMatchType;
+  static const int kCityMatchType;
+  static const int kStateMatchType;
+
   AddressField();
 
   bool ParseCompany(AutofillScanner* scanner);
@@ -50,6 +54,17 @@
   bool ParseCity(AutofillScanner* scanner);
   bool ParseState(AutofillScanner* scanner);
 
+  // Parses the current field pointed to by |scanner|, if it exists, and tries
+  // to figure out whether the field's type: city, state, zip, or none of those.
+  bool ParseCityStateZipCode(AutofillScanner* scanner);
+
+  // Run matches on the name and label separately. If the return result is
+  // RESULT_MATCH_NAME_LABEL, then |scanner| advances and the field is set.
+  // Otherwise |scanner| rewinds and the field is cleared.
+  ParseNameLabelResult ParseNameAndLabelForZipCode(AutofillScanner* scanner);
+  ParseNameLabelResult ParseNameAndLabelForCity(AutofillScanner* scanner);
+  ParseNameLabelResult ParseNameAndLabelForState(AutofillScanner* scanner);
+
   AutofillField* company_;
   AutofillField* address1_;
   AutofillField* address2_;
diff --git a/components/autofill/core/browser/autofill_field.cc b/components/autofill/core/browser/autofill_field.cc
index 75a0d0a4..db3046ad 100644
--- a/components/autofill/core/browser/autofill_field.cc
+++ b/components/autofill/core/browser/autofill_field.cc
@@ -248,26 +248,10 @@
 // given |field|.
 bool FillCreditCardTypeSelectControl(const base::string16& value,
                                      FormFieldData* field) {
-  // Try stripping off spaces.
-  base::string16 value_stripped;
-  base::RemoveChars(base::StringToLowerASCII(value), base::kWhitespaceUTF16,
-                    &value_stripped);
-
-  for (size_t i = 0; i < field->option_values.size(); ++i) {
-    base::string16 option_value_lowercase;
-    base::RemoveChars(base::StringToLowerASCII(field->option_values[i]),
-                      base::kWhitespaceUTF16, &option_value_lowercase);
-    base::string16 option_contents_lowercase;
-    base::RemoveChars(base::StringToLowerASCII(field->option_contents[i]),
-                      base::kWhitespaceUTF16, &option_contents_lowercase);
-
-    // Perform a case-insensitive comparison; but fill the form with the
-    // original text, not the lowercased version.
-    if (value_stripped == option_value_lowercase ||
-        value_stripped == option_contents_lowercase) {
-      field->value = field->option_values[i];
-      return true;
-    }
+  size_t idx;
+  if (AutofillField::FindValueInSelectControl(*field, value, &idx)) {
+    field->value = field->option_values[idx];
+    return true;
   }
 
   // For American Express, also try filling as "AmEx".
@@ -541,4 +525,32 @@
   return number;
 }
 
+// static
+bool AutofillField::FindValueInSelectControl(const FormFieldData& field,
+                                             const base::string16& value,
+                                             size_t* index) {
+  // TODO(thestig): Improve this. See http://crbug.com/470726)
+  // Try stripping off spaces.
+  base::string16 value_stripped;
+  base::RemoveChars(base::StringToLowerASCII(value), base::kWhitespaceUTF16,
+                    &value_stripped);
+  for (size_t i = 0; i < field.option_values.size(); ++i) {
+    base::string16 option_value_lowercase;
+    base::RemoveChars(base::StringToLowerASCII(field.option_values[i]),
+                      base::kWhitespaceUTF16, &option_value_lowercase);
+    base::string16 option_contents_lowercase;
+    base::RemoveChars(base::StringToLowerASCII(field.option_contents[i]),
+                      base::kWhitespaceUTF16, &option_contents_lowercase);
+
+    // Perform a case-insensitive comparison.
+    if (value_stripped == option_value_lowercase ||
+        value_stripped == option_contents_lowercase) {
+      if (index)
+        *index = i;
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_field.h b/components/autofill/core/browser/autofill_field.h
index 1c79bb2..19a26c2 100644
--- a/components/autofill/core/browser/autofill_field.h
+++ b/components/autofill/core/browser/autofill_field.h
@@ -89,6 +89,13 @@
                                             const base::string16& number,
                                             const FormFieldData& field_data);
 
+  // Returns true if the select |field| contains an option that matches |value|.
+  // If the return value is true and |index| is non-NULL, write the index of the
+  // matching option into |index|.
+  static bool FindValueInSelectControl(const FormFieldData& field,
+                                       const base::string16& value,
+                                       size_t* index);
+
  private:
   // The unique name of this field, generated by Autofill.
   base::string16 unique_name_;
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 0ccf7d9..a316f6e 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -328,8 +328,8 @@
   if (!submitted_form)
     return false;
 
-  address_form_event_logger_->OnDidSubmitForm();
-  credit_card_form_event_logger_->OnDidSubmitForm();
+  address_form_event_logger_->OnWillSubmitForm();
+  credit_card_form_event_logger_->OnWillSubmitForm();
 
   // Only upload server statistics and UMA metrics if at least some local data
   // is available to use as a baseline.
@@ -389,6 +389,9 @@
   if (!submitted_form)
     return false;
 
+  address_form_event_logger_->OnFormSubmitted();
+  credit_card_form_event_logger_->OnFormSubmitted();
+
   // Update Personal Data with the form's submitted data.
   if (submitted_form->IsAutofillable())
     ImportFormData(*submitted_form);
@@ -551,6 +554,34 @@
   }
 }
 
+bool AutofillManager::WillFillCreditCardNumber(const FormData& form,
+                                               const FormFieldData& field) {
+  FormStructure* form_structure = nullptr;
+  AutofillField* autofill_field = nullptr;
+  if (!GetCachedFormAndField(form, field, &form_structure, &autofill_field))
+    return false;
+
+  if (autofill_field->Type().GetStorableType() == CREDIT_CARD_NUMBER)
+    return true;
+
+  // If the relevant section is already autofilled, the new fill operation will
+  // only fill |autofill_field|.
+  if (SectionIsAutofilled(*form_structure, form, autofill_field->section()))
+    return false;
+
+  DCHECK_EQ(form_structure->field_count(), form.fields.size());
+  for (size_t i = 0; i < form_structure->field_count(); ++i) {
+    if (form_structure->field(i)->section() == autofill_field->section() &&
+        form_structure->field(i)->Type().GetStorableType() ==
+            CREDIT_CARD_NUMBER &&
+        form.fields[i].value.empty()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 void AutofillManager::FillOrPreviewCreditCardForm(
     AutofillDriver::RendererFormDataAction action,
     int query_id,
@@ -559,7 +590,8 @@
     const CreditCard& credit_card,
     size_t variant) {
   if (action == AutofillDriver::FORM_DATA_ACTION_FILL) {
-    if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) {
+    if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD &&
+        WillFillCreditCardNumber(form, field)) {
       unmasking_card_ = credit_card;
       unmasking_query_id_ = query_id;
       unmasking_form_ = form;
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 0568256c..fdbf20d6 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -260,6 +260,12 @@
   // based on it. Returns true if the credit card exists.
   bool GetCreditCard(int unique_id, const CreditCard** credit_card);
 
+  // Determines whether a fill on |form| initiated from |field| will wind up
+  // filling a credit card number. This is useful to determine if we will need
+  // to unmask a card.
+  bool WillFillCreditCardNumber(const FormData& form,
+                                const FormFieldData& field);
+
   // Fills or previews the credit card form.
   // Assumes the form and field are valid.
   void FillOrPreviewCreditCardForm(
@@ -437,10 +443,12 @@
                            DisabledAutofillDispatchesError);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressFilledFormEvents);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressSubmittedFormEvents);
+  FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressWillSubmitFormEvents);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressSuggestionsCount);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutofillIsEnabledAtPageLoad);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardSelectedFormEvents);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardFilledFormEvents);
+  FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardWillSubmitFormEvents);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardSubmittedFormEvents);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, DeveloperEngagement);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, FormFillDuration);
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index 9945243..8778990e 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -711,6 +711,11 @@
     return autofill_manager_->MakeFrontendID(cc_sid, profile_sid);
   }
 
+  bool WillFillCreditCardNumber(const FormData& form,
+                                const FormFieldData& field) {
+    return autofill_manager_->WillFillCreditCardNumber(form, field);
+  }
+
  protected:
   base::MessageLoop message_loop_;
   TestAutofillClient autofill_client_;
@@ -1472,6 +1477,41 @@
   EXPECT_NE(base::Time(), profile->use_date());
 }
 
+TEST_F(AutofillManagerTest, WillFillCreditCardNumber) {
+  // Set up our form data.
+  FormData form;
+  CreateTestCreditCardFormData(&form, true, false);
+  std::vector<FormData> forms(1, form);
+  FormsSeen(forms);
+
+  FormFieldData* number_field = nullptr;
+  FormFieldData* name_field = nullptr;
+  FormFieldData* month_field = nullptr;
+  for (size_t i = 0; i < form.fields.size(); ++i) {
+    if (form.fields[i].name == ASCIIToUTF16("cardnumber"))
+      number_field = &form.fields[i];
+    else if (form.fields[i].name == ASCIIToUTF16("nameoncard"))
+      name_field = &form.fields[i];
+    else if (form.fields[i].name == ASCIIToUTF16("ccmonth"))
+      month_field = &form.fields[i];
+  }
+
+  // Empty form - whole form is Autofilled.
+  EXPECT_TRUE(WillFillCreditCardNumber(form, *number_field));
+  EXPECT_TRUE(WillFillCreditCardNumber(form, *name_field));
+  // If the user has entered a value, it won't be overridden.
+  number_field->value = ASCIIToUTF16("gibberish");
+  EXPECT_TRUE(WillFillCreditCardNumber(form, *number_field));
+  EXPECT_FALSE(WillFillCreditCardNumber(form, *name_field));
+  number_field->value.clear();
+  EXPECT_TRUE(WillFillCreditCardNumber(form, *name_field));
+
+  // When part of the section is Autofilled, only fill the initiating field.
+  month_field->is_autofilled = true;
+  EXPECT_FALSE(WillFillCreditCardNumber(form, *name_field));
+  EXPECT_TRUE(WillFillCreditCardNumber(form, *number_field));
+}
+
 // Test that we correctly fill an address form from an auxiliary profile.
 TEST_F(AutofillManagerTest, FillAddressFormFromAuxiliaryProfile) {
   personal_data_.ClearAutofillProfiles();
diff --git a/components/autofill/core/browser/autofill_metrics.cc b/components/autofill/core/browser/autofill_metrics.cc
index 57716f24..0280961 100644
--- a/components/autofill/core/browser/autofill_metrics.cc
+++ b/components/autofill/core/browser/autofill_metrics.cc
@@ -522,9 +522,11 @@
       has_logged_suggestions_shown_(false),
       has_logged_masked_server_card_suggestion_selected_(false),
       has_logged_suggestion_filled_(false),
+      has_logged_will_submit_(false),
       has_logged_submitted_(false),
       logged_suggestion_filled_was_server_data_(false),
-      logged_suggestion_filled_was_masked_server_card_(false) {};
+      logged_suggestion_filled_was_masked_server_card_(false) {
+}
 
 void AutofillMetrics::FormEventLogger::OnDidInteractWithAutofillableForm() {
   if (!has_logged_interacted_) {
@@ -597,7 +599,29 @@
   }
 }
 
-void AutofillMetrics::FormEventLogger::OnDidSubmitForm() {
+void AutofillMetrics::FormEventLogger::OnWillSubmitForm() {
+  // Not logging this kind of form if we haven't logged a user interaction.
+  if (!has_logged_interacted_)
+    return;
+
+  // Not logging twice.
+  if (has_logged_will_submit_)
+    return;
+  has_logged_will_submit_ = true;
+
+  if (!has_logged_suggestion_filled_) {
+    Log(AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE);
+  } else if (logged_suggestion_filled_was_masked_server_card_) {
+    Log(AutofillMetrics::
+            FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE);
+  } else if (logged_suggestion_filled_was_server_data_) {
+    Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE);
+  } else {
+    Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE);
+  }
+}
+
+void AutofillMetrics::FormEventLogger::OnFormSubmitted() {
   // Not logging this kind of form if we haven't logged a user interaction.
   if (!has_logged_interacted_)
     return;
diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h
index 88419a5..c2ddfbe 100644
--- a/components/autofill/core/browser/autofill_metrics.h
+++ b/components/autofill/core/browser/autofill_metrics.h
@@ -281,6 +281,16 @@
     FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED,
     // Same as above but only triggered once per page load.
     FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE,
+    // An autofillable form is about to be submitted. If the submission is not
+    // interrupted by JavaScript, the "form submitted" events above will also be
+    // logged. Depending on the user filling a local, server, masked server card
+    // or no suggestion one of the following will be triggered, at most once per
+    // page load.
+    FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE,
+    FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE,
+    FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE,
+    FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+
     NUM_FORM_EVENTS,
   };
 
@@ -546,7 +556,9 @@
 
     void OnDidFillSuggestion(const AutofillProfile& profile);
 
-    void OnDidSubmitForm();
+    void OnWillSubmitForm();
+
+    void OnFormSubmitted();
 
    private:
     void Log(FormEvent event) const;
@@ -558,6 +570,7 @@
     bool has_logged_suggestions_shown_;
     bool has_logged_masked_server_card_suggestion_selected_;
     bool has_logged_suggestion_filled_;
+    bool has_logged_will_submit_;
     bool has_logged_submitted_;
     bool logged_suggestion_filled_was_server_data_;
     bool logged_suggestion_filled_was_masked_server_card_;
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 973a6d5..46a18e1 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -229,6 +229,7 @@
     form_structures()->push_back(form_structure);
   }
 
+  // Calls AutofillManager::OnWillSubmitForm and waits for it to complete.
   void WillSubmitForm(const FormData& form, const TimeTicks& timestamp) {
     run_loop_.reset(new base::RunLoop());
     if (!OnWillSubmitForm(form, timestamp))
@@ -238,6 +239,13 @@
     run_loop_->Run();
   }
 
+  // Calls both AutofillManager::OnWillSubmitForm and
+  // AutofillManager::OnFormSubmitted.
+  void SubmitForm(const FormData& form, const TimeTicks& timestamp) {
+    WillSubmitForm(form, timestamp);
+    OnFormSubmitted(form);
+  }
+
   void UploadFormDataAsyncCallback(
       const FormStructure* submitted_form,
       const base::TimeTicks& load_time,
@@ -377,7 +385,7 @@
 
   // Simulate form submission.
   base::HistogramTester histogram_tester;
-  autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+  autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
   // Heuristic predictions.
   // Unknown:
@@ -499,7 +507,7 @@
   autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
 
   // Simulate form submission.
-  autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+  autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
   // The number of mismatches did not trigger the RAPPOR metric logging.
   EXPECT_EQ(0, autofill_client_.test_rappor_service()->GetReportsCount());
@@ -542,7 +550,7 @@
   autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
 
   // Simulate form submission.
-  autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+  autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
   // No RAPPOR metrics are logged in the case of multiple UNKNOWN_TYPE and
   // NO_SERVER_DATA for heuristics and server predictions, respectively.
@@ -585,7 +593,7 @@
   autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
 
   // Simulate form submission.
-  autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+  autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
   // The number of mismatches did trigger the RAPPOR metric logging for server
   // predictions.
@@ -638,7 +646,7 @@
   autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
 
   // Simulate form submission.
-  autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+  autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
   // The number of mismatches did trigger the RAPPOR metric logging for
   // heuristic predictions.
@@ -691,7 +699,7 @@
     // match what is in the test profile.
     form.fields[1].value = base::ASCIIToUTF16("79401");
     form.fields[2].value = base::ASCIIToUTF16("2345678901");
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
     // First verify that country was not predicted by client or server.
     histogram_tester.ExpectBucketCount(
@@ -804,7 +812,7 @@
 
   // Simulate form submission.
   base::HistogramTester histogram_tester;
-  autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+  autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
   // Heuristic predictions.
   // Unknown:
@@ -914,7 +922,7 @@
   // Simulate form submission.
   base::HistogramTester histogram_tester;
   autofill_manager_->OnFormsSeen(forms, TimeTicks());
-  autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+  autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
   // An autofillable form was submitted, and the number of stored profiles is
   // logged.
@@ -944,7 +952,7 @@
   // Simulate form submission.
   base::HistogramTester histogram_tester;
   autofill_manager_->OnFormsSeen(forms, TimeTicks());
-  autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+  autofill_manager_->SubmitForm(form, TimeTicks::Now());
 
   // A non-autofillable form was submitted, and number of stored profiles is NOT
   // logged.
@@ -1490,7 +1498,10 @@
     base::HistogramTester histogram_tester;
     autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
                                                 false);
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.CreditCard",
         AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
@@ -1511,7 +1522,10 @@
         AutofillDriver::FORM_DATA_ACTION_FILL,
         0, form, form.fields.front(),
         autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1);
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.CreditCard",
         AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1);
@@ -1532,7 +1546,10 @@
         AutofillDriver::FORM_DATA_ACTION_FILL,
         0, form, form.fields.front(),
         autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1);
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.CreditCard",
         AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1);
@@ -1578,11 +1595,218 @@
     base::HistogramTester histogram_tester;
     autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
                                                 false);
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::
+            FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+        0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::
+            FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+        0);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission without previous interaction.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+}
+
+// Test that we log "will submit" (but not submitted) form events for credit
+// cards. Mirrors CreditCardSubmittedFormEvents test but does not expect any
+// "submitted" metrics.
+TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) {
+  EnableWalletSync();
+  // Creating all kinds of cards.
+  personal_data_->RecreateCreditCards(
+      true /* include_local_credit_card */,
+      true /* include_masked_server_credit_card */,
+      true /* include_full_server_credit_card */);
+  // Set up our form data.
+  FormData form;
+  form.name = ASCIIToUTF16("TestForm");
+  form.origin = GURL("http://example.com/form.html");
+  form.action = GURL("http://example.com/submit.html");
+  form.user_submitted = true;
+
+  FormFieldData field;
+  std::vector<ServerFieldType> field_types;
+  test::CreateTestFormField("Month", "card_month", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_EXP_MONTH);
+  test::CreateTestFormField("Year", "card_year", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_EXP_2_DIGIT_YEAR);
+  test::CreateTestFormField("Credit card", "card", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_NUMBER);
+
+  // Simulate having seen this form on page load.
+  // |form_structure| will be owned by |autofill_manager_|.
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with no filled data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with filled local data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    SuggestionBackendID guid("10000000-0000-0000-0000-000000000001",
+                             0);  // local card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with filled server data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    SuggestionBackendID guid("10000000-0000-0000-0000-000000000003",
+                             0);  // full server card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with a masked card server suggestion.
+    base::HistogramTester histogram_tester;
+    SuggestionBackendID guid("10000000-0000-0000-0000-000000000002",
+                             0);  // masked server card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    autofill_manager_->OnDidGetRealPan(AutofillClient::SUCCESS,
+                                       "6011000990139424");
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE,
+        1);
+  }
+
+  // Recreating cards as the previous test should have upgraded the masked
+  // card to a full card.
+  personal_data_->RecreateCreditCards(
+      true /* include_local_credit_card */,
+      true /* include_masked_server_credit_card */,
+      true /* include_full_server_credit_card */);
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating multiple submissions.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
     autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
     autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.CreditCard",
-        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::
+            FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+        0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.CreditCard",
         AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
@@ -1606,6 +1830,15 @@
     autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
         AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.CreditCard",
@@ -1871,7 +2104,10 @@
     base::HistogramTester histogram_tester;
     autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
                                                 false);
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.Address",
         AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
@@ -1892,7 +2128,10 @@
         AutofillDriver::FORM_DATA_ACTION_FILL,
         0, form, form.fields.front(),
         autofill_manager_->MakeFrontendID(SuggestionBackendID(), guid));
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1);
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.Address",
         AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1);
@@ -1913,7 +2152,10 @@
         AutofillDriver::FORM_DATA_ACTION_FILL,
         0, form, form.fields.front(),
         autofill_manager_->MakeFrontendID(SuggestionBackendID(), guid));
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1);
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.Address",
         AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1);
@@ -1928,7 +2170,17 @@
     base::HistogramTester histogram_tester;
     autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
                                                 false);
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.Address",
         AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
@@ -1947,9 +2199,163 @@
   {
     // Simulating submission without previous interaction.
     base::HistogramTester histogram_tester;
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+}
+
+// Test that we log "will submit" (but not submitted) form events for address.
+// Mirrors AddressSubmittedFormEvents test but does not expect any "submitted"
+// metrics.
+TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
+  EnableWalletSync();
+  // Creating all kinds of profiles.
+  personal_data_->RecreateProfiles(true /* include_local_profile */,
+                                   true /* include_server_profile */);
+  // Set up our form data.
+  FormData form;
+  form.name = ASCIIToUTF16("TestForm");
+  form.origin = GURL("http://example.com/form.html");
+  form.action = GURL("http://example.com/submit.html");
+  form.user_submitted = true;
+
+  FormFieldData field;
+  std::vector<ServerFieldType> field_types;
+  test::CreateTestFormField("State", "state", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_STATE);
+  test::CreateTestFormField("City", "city", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_CITY);
+  test::CreateTestFormField("Street", "street", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
+
+  // Simulate having seen this form on page load.
+  // |form_structure| will be owned by |autofill_manager_|.
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with no filled data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
     autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with filled local data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    SuggestionBackendID guid("00000000-0000-0000-0000-000000000001",
+                             0);  // local profile
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(SuggestionBackendID(), guid));
+    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with filled server data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    SuggestionBackendID guid("00000000-0000-0000-0000-000000000002",
+                             0);  // server profile
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(SuggestionBackendID(), guid));
+    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating multiple submissions.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission without previous interaction.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
         AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.Address",
@@ -2208,7 +2614,7 @@
   // Expect no notifications when the form is submitted.
   {
     base::HistogramTester histogram_tester;
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
     histogram_tester.ExpectTotalCount("Autofill.UserHappiness", 0);
   }
 
@@ -2230,7 +2636,7 @@
   // Expect a notification when the form is submitted.
   {
     base::HistogramTester histogram_tester;
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
     histogram_tester.ExpectUniqueSample(
         "Autofill.UserHappiness", AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM,
         1);
@@ -2244,7 +2650,7 @@
   // Expect a notification when the form is submitted.
   {
     base::HistogramTester histogram_tester;
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
     histogram_tester.ExpectUniqueSample(
         "Autofill.UserHappiness", AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM,
         1);
@@ -2257,7 +2663,7 @@
   // Expect notifications when the form is submitted.
   {
     base::HistogramTester histogram_tester;
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
     histogram_tester.ExpectUniqueSample(
         "Autofill.UserHappiness",
         AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_NONE, 1);
@@ -2271,7 +2677,7 @@
   // Expect notifications when the form is submitted.
   {
     base::HistogramTester histogram_tester;
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
     histogram_tester.ExpectUniqueSample(
         "Autofill.UserHappiness",
         AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME, 1);
@@ -2285,7 +2691,7 @@
   // Expect notifications when the form is submitted.
   {
     base::HistogramTester histogram_tester;
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
     histogram_tester.ExpectUniqueSample(
         "Autofill.UserHappiness",
         AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_ALL, 1);
@@ -2298,7 +2704,7 @@
   // Expect notifications when the form is submitted.
   {
     base::HistogramTester histogram_tester;
-    autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+    autofill_manager_->SubmitForm(form, TimeTicks::Now());
     histogram_tester.ExpectUniqueSample(
         "Autofill.UserHappiness", AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM,
         1);
@@ -2454,7 +2860,7 @@
   {
     base::HistogramTester histogram_tester;
     autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1));
-    autofill_manager_->WillSubmitForm(form, TimeTicks::FromInternalValue(17));
+    autofill_manager_->SubmitForm(form, TimeTicks::FromInternalValue(17));
 
     histogram_tester.ExpectTotalCount(
         "Autofill.FillDuration.FromLoad.WithAutofill", 0);
@@ -2474,7 +2880,7 @@
     autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1));
     autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
                                             TimeTicks::FromInternalValue(3));
-    autofill_manager_->WillSubmitForm(form, TimeTicks::FromInternalValue(17));
+    autofill_manager_->SubmitForm(form, TimeTicks::FromInternalValue(17));
 
     histogram_tester.ExpectTotalCount(
         "Autofill.FillDuration.FromLoad.WithAutofill", 0);
@@ -2495,7 +2901,7 @@
     autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1));
     autofill_manager_->OnDidFillAutofillFormData(
         TimeTicks::FromInternalValue(5));
-    autofill_manager_->WillSubmitForm(form, TimeTicks::FromInternalValue(17));
+    autofill_manager_->SubmitForm(form, TimeTicks::FromInternalValue(17));
 
     histogram_tester.ExpectUniqueSample(
         "Autofill.FillDuration.FromLoad.WithAutofill", 16, 1);
@@ -2520,7 +2926,7 @@
         TimeTicks::FromInternalValue(5));
     autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
                                             TimeTicks::FromInternalValue(3));
-    autofill_manager_->WillSubmitForm(form, TimeTicks::FromInternalValue(17));
+    autofill_manager_->SubmitForm(form, TimeTicks::FromInternalValue(17));
 
     histogram_tester.ExpectUniqueSample(
         "Autofill.FillDuration.FromLoad.WithAutofill", 16, 1);
@@ -2545,7 +2951,7 @@
         TimeTicks::FromInternalValue(5));
     autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
                                             TimeTicks::FromInternalValue(3));
-    autofill_manager_->WillSubmitForm(form, TimeTicks::FromInternalValue(17));
+    autofill_manager_->SubmitForm(form, TimeTicks::FromInternalValue(17));
 
     histogram_tester.ExpectUniqueSample(
         "Autofill.FillDuration.FromLoad.WithAutofill", 16, 1);
@@ -2566,8 +2972,8 @@
     autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1));
     autofill_manager_->OnFormsSeen(second_forms,
                                    TimeTicks::FromInternalValue(5));
-    autofill_manager_->WillSubmitForm(second_form,
-                                      TimeTicks::FromInternalValue(17));
+    autofill_manager_->SubmitForm(second_form,
+                                  TimeTicks::FromInternalValue(17));
 
     histogram_tester.ExpectTotalCount(
         "Autofill.FillDuration.FromLoad.WithAutofill", 0);
diff --git a/components/autofill/core/browser/autofill_regex_constants.cc.utf8 b/components/autofill/core/browser/autofill_regex_constants.cc.utf8
index 7339f8a..22cc386 100644
--- a/components/autofill/core/browser/autofill_regex_constants.cc.utf8
+++ b/components/autofill/core/browser/autofill_regex_constants.cc.utf8
@@ -149,8 +149,6 @@
     "verification|card identification|security code|card code"
     "|cvn|cvv|cvc|csc|cvd|cid|ccv"
     "|\\bcid\\b";
-const char kCardTypeRe[] =
-    "(card|cc|payment).?type|payment.?method|card.*brand";
 
 // "Expiration date" is the most common label here, but some pages have
 // "Expires", "exp. date" or "exp. month" and "exp. year".  We also look
@@ -197,8 +195,6 @@
     "|有効期限"  // ja-JP
     "|validade"  // pt-BR, pt-PT
     "|Срок действия карты";  // ru
-const char kCardIgnoredRe[] =
-    "^card";
 const char kGiftCardRe[] =
     "gift.?card";
 
diff --git a/components/autofill/core/browser/credit_card_field.cc b/components/autofill/core/browser/credit_card_field.cc
index a9fe79e..0e8d25d 100644
--- a/components/autofill/core/browser/credit_card_field.cc
+++ b/components/autofill/core/browser/credit_card_field.cc
@@ -18,6 +18,8 @@
 #include "components/autofill/core/browser/autofill_regexes.h"
 #include "components/autofill/core/browser/autofill_scanner.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace autofill {
 
@@ -60,7 +62,7 @@
 
   // Credit card fields can appear in many different orders.
   // We loop until no more credit card related fields are found, see |break| at
-  // bottom of the loop.
+  // the bottom of the loop.
   for (int fields = 0; !scanner->IsEnd(); ++fields) {
     // Ignore gift card fields.
     if (ParseField(scanner, base::UTF8ToUTF16(kGiftCardRe), nullptr))
@@ -89,11 +91,10 @@
     }
 
     // Check for a credit card type (Visa, MasterCard, etc.) field.
-    if (!credit_card_field->type_ &&
-        ParseFieldSpecifics(scanner,
-                            base::UTF8ToUTF16(kCardTypeRe),
-                            MATCH_DEFAULT | MATCH_SELECT,
-                            &credit_card_field->type_)) {
+    // All CC type fields encountered so far have been of type select.
+    if (!credit_card_field->type_ && LikelyCardTypeSelectField(scanner)) {
+      credit_card_field->type_ = scanner->Cursor();
+      scanner->Advance();
       continue;
     }
 
@@ -150,14 +151,6 @@
       return nullptr;
     }
 
-    // Some pages (e.g. ExpediaBilling.html) have a "card description"
-    // field; we parse this field but ignore it.
-    // We also ignore any other fields within a credit card block that
-    // start with "card", under the assumption that they are related to
-    // the credit card section being processed but are uninteresting to us.
-    if (ParseField(scanner, base::UTF8ToUTF16(kCardIgnoredRe), nullptr))
-      continue;
-
     break;
   }
 
@@ -247,6 +240,23 @@
           FindConsecutiveStrings(years_to_check, field->option_contents));
 }
 
+// static
+bool CreditCardField::LikelyCardTypeSelectField(AutofillScanner* scanner) {
+  if (scanner->IsEnd())
+    return false;
+
+  AutofillField* field = scanner->Cursor();
+  if (!MatchesFormControlType(field->form_control_type, MATCH_SELECT))
+    return false;
+
+  return AutofillField::FindValueInSelectControl(
+             *field, l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_VISA),
+             nullptr) ||
+         AutofillField::FindValueInSelectControl(
+             *field, l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_MASTERCARD),
+             nullptr);
+}
+
 CreditCardField::CreditCardField()
     : cardholder_(nullptr),
       cardholder_last_(nullptr),
diff --git a/components/autofill/core/browser/credit_card_field.h b/components/autofill/core/browser/credit_card_field.h
index 2d8ba6f..0f94f3edd 100644
--- a/components/autofill/core/browser/credit_card_field.h
+++ b/components/autofill/core/browser/credit_card_field.h
@@ -40,6 +40,10 @@
   // the next few years.
   static bool LikelyCardYearSelectField(AutofillScanner* scanner);
 
+  // Returns true if |scanner| points to a <select> field that contains credit
+  // card type options.
+  static bool LikelyCardTypeSelectField(AutofillScanner* scanner);
+
   CreditCardField();
 
   // Parses the expiration month/year/date fields. Returns true if it finds
diff --git a/components/autofill/core/browser/credit_card_field_unittest.cc b/components/autofill/core/browser/credit_card_field_unittest.cc
index 3e4f21c..56ed3aee 100644
--- a/components/autofill/core/browser/credit_card_field_unittest.cc
+++ b/components/autofill/core/browser/credit_card_field_unittest.cc
@@ -117,10 +117,6 @@
   FormFieldData field;
   field.form_control_type = "text";
 
-  field.label = ASCIIToUTF16("Card Type");
-  field.name = ASCIIToUTF16("card_type");
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("type")));
-
   field.label = ASCIIToUTF16("Name on Card");
   field.name = ASCIIToUTF16("name_on_card");
   list_.push_back(new AutofillField(field, ASCIIToUTF16("name")));
@@ -141,6 +137,13 @@
   field.name = ASCIIToUTF16("verification");
   list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc")));
 
+  field.form_control_type = "select-one";
+  field.label = ASCIIToUTF16("Card Type");
+  field.name = ASCIIToUTF16("card_type");
+  field.option_contents.push_back(ASCIIToUTF16("visa"));
+  field.option_values.push_back(ASCIIToUTF16("visa"));
+  list_.push_back(new AutofillField(field, ASCIIToUTF16("type")));
+
   Parse();
   ASSERT_NE(nullptr, field_.get());
   EXPECT_TRUE(ClassifyField());
diff --git a/components/autofill/core/browser/form_field.cc b/components/autofill/core/browser/form_field.cc
index 5ed4be6..7ea3ce4 100644
--- a/components/autofill/core/browser/form_field.cc
+++ b/components/autofill/core/browser/form_field.cc
@@ -106,6 +106,40 @@
 }
 
 // static
+FormField::ParseNameLabelResult FormField::ParseNameAndLabelSeparately(
+    AutofillScanner* scanner,
+    const base::string16& pattern,
+    int match_type,
+    AutofillField** match) {
+  if (scanner->IsEnd())
+    return RESULT_MATCH_NONE;
+
+  AutofillField* cur_match = nullptr;
+  size_t saved_cursor = scanner->SaveCursor();
+  bool parsed_name = ParseFieldSpecifics(scanner,
+                                         pattern,
+                                         match_type & ~MATCH_LABEL,
+                                         &cur_match);
+  scanner->RewindTo(saved_cursor);
+  bool parsed_label = ParseFieldSpecifics(scanner,
+                                          pattern,
+                                          match_type & ~MATCH_NAME,
+                                          &cur_match);
+  if (parsed_name && parsed_label) {
+    if (match)
+      *match = cur_match;
+    return RESULT_MATCH_NAME_LABEL;
+  }
+
+  scanner->RewindTo(saved_cursor);
+  if (parsed_name)
+    return RESULT_MATCH_NAME;
+  if (parsed_label)
+    return RESULT_MATCH_LABEL;
+  return RESULT_MATCH_NONE;
+}
+
+// static
 bool FormField::ParseEmptyLabel(AutofillScanner* scanner,
                                 AutofillField** match) {
   return ParseFieldSpecifics(scanner,
diff --git a/components/autofill/core/browser/form_field.h b/components/autofill/core/browser/form_field.h
index e1f4d36..c27dc08 100644
--- a/components/autofill/core/browser/form_field.h
+++ b/components/autofill/core/browser/form_field.h
@@ -55,6 +55,14 @@
     MATCH_DEFAULT    = MATCH_LABEL | MATCH_NAME | MATCH_TEXT,
   };
 
+  // When parsing a field's label and name separately with a given pattern:
+  enum ParseNameLabelResult {
+    RESULT_MATCH_NONE,       // No match with the label or name.
+    RESULT_MATCH_LABEL,      // Only the label matches the pattern.
+    RESULT_MATCH_NAME,       // Only the name matches the pattern.
+    RESULT_MATCH_NAME_LABEL  // Name and label both match the pattern.
+  };
+
   // Only derived classes may instantiate.
   FormField() {}
 
@@ -74,6 +82,17 @@
                                   int match_type,
                                   AutofillField** match);
 
+  // Like ParseFieldSpecifics(), but applies |pattern| against the name and
+  // label of the current field separately. If the return value is
+  // RESULT_MATCH_NAME_LABEL, then |scanner| advances and |match| is filled if
+  // it is non-NULL. Otherwise |scanner| does not advance and |match| does not
+  // change.
+  static ParseNameLabelResult ParseNameAndLabelSeparately(
+      AutofillScanner* scanner,
+      const base::string16& pattern,
+      int match_type,
+      AutofillField** match);
+
   // Attempts to parse a field with an empty label.  Returns true
   // on success and fills |match| with a pointer to the field.
   static bool ParseEmptyLabel(AutofillScanner* scanner, AutofillField** match);
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 5441a47..617c9dc 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -504,7 +504,7 @@
 }
 
 void PersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) {
-  if (!database_.get())
+  if (is_off_the_record_ || !database_.get())
     return;
 
   CreditCard* credit_card = GetCreditCardByGUID(data_model.guid());
diff --git a/components/autofill/core/browser/phone_field.cc b/components/autofill/core/browser/phone_field.cc
index d7860497..77a8d9f0 100644
--- a/components/autofill/core/browser/phone_field.cc
+++ b/components/autofill/core/browser/phone_field.cc
@@ -17,10 +17,10 @@
 namespace {
 
 // This string includes all area code separators, including NoText.
-base::string16 GetAreaRegex() {
-  base::string16 area_code = base::UTF8ToUTF16(kAreaCodeRe);
-  area_code.append(base::ASCIIToUTF16("|"));  // Regexp separator.
-  area_code.append(base::UTF8ToUTF16(kAreaCodeNotextRe));
+std::string GetAreaRegex() {
+  std::string area_code = kAreaCodeRe;
+  area_code.append("|");  // Regexp separator.
+  area_code.append(kAreaCodeNotextRe);
   return area_code;
 }
 
@@ -119,24 +119,23 @@
 // static
 scoped_ptr<FormField> PhoneField::Parse(AutofillScanner* scanner) {
   if (scanner->IsEnd())
-    return NULL;
+    return nullptr;
 
-  scanner->SaveCursor();
+  size_t start_cursor = scanner->SaveCursor();
 
   // The form owns the following variables, so they should not be deleted.
   AutofillField* parsed_fields[FIELD_MAX];
 
   for (size_t i = 0; i < arraysize(kPhoneFieldGrammars); ++i) {
     memset(parsed_fields, 0, sizeof(parsed_fields));
-    scanner->SaveCursor();
+    size_t saved_cursor = scanner->SaveCursor();
 
     // Attempt to parse according to the next grammar.
     for (; i < arraysize(kPhoneFieldGrammars) &&
          kPhoneFieldGrammars[i].regex != REGEX_SEPARATOR; ++i) {
-      if (!ParseFieldSpecifics(
+      if (!ParsePhoneField(
               scanner,
               GetRegExp(kPhoneFieldGrammars[i].regex),
-              MATCH_DEFAULT | MATCH_TELEPHONE,
               &parsed_fields[kPhoneFieldGrammars[i].phone_part]))
         break;
       if (kPhoneFieldGrammars[i].max_size &&
@@ -148,8 +147,8 @@
     }
 
     if (i >= arraysize(kPhoneFieldGrammars)) {
-      scanner->Rewind();
-      return NULL;  // Parsing failed.
+      scanner->RewindTo(saved_cursor);
+      return nullptr;  // Parsing failed.
     }
     if (kPhoneFieldGrammars[i].regex == REGEX_SEPARATOR)
       break;  // Parsing succeeded.
@@ -160,17 +159,15 @@
     } while (i < arraysize(kPhoneFieldGrammars) &&
              kPhoneFieldGrammars[i].regex != REGEX_SEPARATOR);
 
+    scanner->RewindTo(saved_cursor);
     if (i + 1 == arraysize(kPhoneFieldGrammars)) {
-      scanner->Rewind();
-      return NULL;  // Tried through all the possibilities - did not match.
+      return nullptr;  // Tried through all the possibilities - did not match.
     }
-
-    scanner->Rewind();
   }
 
   if (!parsed_fields[FIELD_PHONE]) {
-    scanner->Rewind();
-    return NULL;
+    scanner->RewindTo(start_cursor);
+    return nullptr;
   }
 
   scoped_ptr<PhoneField> phone_field(new PhoneField);
@@ -181,16 +178,19 @@
 
   // Look for a third text box.
   if (!phone_field->parsed_phone_fields_[FIELD_SUFFIX]) {
-    if (!ParseField(scanner, base::UTF8ToUTF16(kPhoneSuffixRe),
-                    &phone_field->parsed_phone_fields_[FIELD_SUFFIX])) {
-      ParseField(scanner, base::UTF8ToUTF16(kPhoneSuffixSeparatorRe),
-                 &phone_field->parsed_phone_fields_[FIELD_SUFFIX]);
+    if (!ParsePhoneField(scanner, kPhoneSuffixRe,
+                         &phone_field->parsed_phone_fields_[FIELD_SUFFIX])) {
+      ParsePhoneField(scanner, kPhoneSuffixSeparatorRe,
+                      &phone_field->parsed_phone_fields_[FIELD_SUFFIX]);
     }
   }
 
   // Now look for an extension.
-  ParseField(scanner, base::UTF8ToUTF16(kPhoneExtensionRe),
-             &phone_field->parsed_phone_fields_[FIELD_EXTENSION]);
+  // The extension is not actually used, so this just eats the field so other
+  // parsers do not mistaken it for something else.
+  ParsePhoneField(scanner,
+                  kPhoneExtensionRe,
+                  &phone_field->parsed_phone_fields_[FIELD_EXTENSION]);
 
   return phone_field.Pass();
 }
@@ -200,21 +200,21 @@
 
   DCHECK(parsed_phone_fields_[FIELD_PHONE]);  // Phone was correctly parsed.
 
-  if ((parsed_phone_fields_[FIELD_COUNTRY_CODE] != NULL) ||
-      (parsed_phone_fields_[FIELD_AREA_CODE] != NULL) ||
-      (parsed_phone_fields_[FIELD_SUFFIX] != NULL)) {
-    if (parsed_phone_fields_[FIELD_COUNTRY_CODE] != NULL) {
+  if ((parsed_phone_fields_[FIELD_COUNTRY_CODE]) ||
+      (parsed_phone_fields_[FIELD_AREA_CODE]) ||
+      (parsed_phone_fields_[FIELD_SUFFIX])) {
+    if (parsed_phone_fields_[FIELD_COUNTRY_CODE]) {
       ok = ok && AddClassification(parsed_phone_fields_[FIELD_COUNTRY_CODE],
                                    PHONE_HOME_COUNTRY_CODE,
                                    map);
     }
 
     ServerFieldType field_number_type = PHONE_HOME_NUMBER;
-    if (parsed_phone_fields_[FIELD_AREA_CODE] != NULL) {
+    if (parsed_phone_fields_[FIELD_AREA_CODE]) {
       ok = ok && AddClassification(parsed_phone_fields_[FIELD_AREA_CODE],
                                    PHONE_HOME_CITY_CODE,
                                    map);
-    } else if (parsed_phone_fields_[FIELD_COUNTRY_CODE] != NULL) {
+    } else if (parsed_phone_fields_[FIELD_COUNTRY_CODE]) {
       // Only if we can find country code without city code, it means the phone
       // number include city code.
       field_number_type = PHONE_HOME_CITY_AND_NUMBER;
@@ -226,7 +226,7 @@
                                  map);
     // We tag the suffix as PHONE_HOME_NUMBER, then when filling the form
     // we fill only the suffix depending on the size of the input field.
-    if (parsed_phone_fields_[FIELD_SUFFIX] != NULL) {
+    if (parsed_phone_fields_[FIELD_SUFFIX]) {
       ok = ok && AddClassification(parsed_phone_fields_[FIELD_SUFFIX],
                                    PHONE_HOME_NUMBER,
                                    map);
@@ -245,31 +245,41 @@
 }
 
 // static
-base::string16 PhoneField::GetRegExp(RegexType regex_id) {
+std::string PhoneField::GetRegExp(RegexType regex_id) {
   switch (regex_id) {
     case REGEX_COUNTRY:
-      return base::UTF8ToUTF16(kCountryCodeRe);
+      return kCountryCodeRe;
     case REGEX_AREA:
       return GetAreaRegex();
     case REGEX_AREA_NOTEXT:
-      return base::UTF8ToUTF16(kAreaCodeNotextRe);
+      return kAreaCodeNotextRe;
     case REGEX_PHONE:
-      return base::UTF8ToUTF16(kPhoneRe);
+      return kPhoneRe;
     case REGEX_PREFIX_SEPARATOR:
-      return base::UTF8ToUTF16(kPhonePrefixSeparatorRe);
+      return kPhonePrefixSeparatorRe;
     case REGEX_PREFIX:
-      return base::UTF8ToUTF16(kPhonePrefixRe);
+      return kPhonePrefixRe;
     case REGEX_SUFFIX_SEPARATOR:
-      return base::UTF8ToUTF16(kPhoneSuffixSeparatorRe);
+      return kPhoneSuffixSeparatorRe;
     case REGEX_SUFFIX:
-      return base::UTF8ToUTF16(kPhoneSuffixRe);
+      return kPhoneSuffixRe;
     case REGEX_EXTENSION:
-      return base::UTF8ToUTF16(kPhoneExtensionRe);
+      return kPhoneExtensionRe;
     default:
       NOTREACHED();
       break;
   }
-  return base::string16();
+  return std::string();
+}
+
+// static
+bool PhoneField::ParsePhoneField(AutofillScanner* scanner,
+                                 const std::string& regex,
+                                 AutofillField** field) {
+  return ParseFieldSpecifics(scanner,
+                             base::UTF8ToUTF16(regex),
+                             MATCH_DEFAULT | MATCH_TELEPHONE | MATCH_NUMBER,
+                             field);
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/phone_field.h b/components/autofill/core/browser/phone_field.h
index 2736290..5534179 100644
--- a/components/autofill/core/browser/phone_field.h
+++ b/components/autofill/core/browser/phone_field.h
@@ -79,8 +79,13 @@
 
   PhoneField();
 
-  // Returns the regular expression string correspoding to |regex_id|
-  static base::string16 GetRegExp(RegexType regex_id);
+  // Returns the regular expression string corresponding to |regex_id|
+  static std::string GetRegExp(RegexType regex_id);
+
+  // Convenient wrapper for ParseFieldSpecifics().
+  static bool ParsePhoneField(AutofillScanner* scanner,
+                              const std::string& regex,
+                              AutofillField** field);
 
   // FIELD_PHONE is always present; holds suffix if prefix is present.
   // The rest could be NULL.
diff --git a/components/autofill/core/browser/phone_field_unittest.cc b/components/autofill/core/browser/phone_field_unittest.cc
index f2e04fab..5b5991b8 100644
--- a/components/autofill/core/browser/phone_field_unittest.cc
+++ b/components/autofill/core/browser/phone_field_unittest.cc
@@ -15,21 +15,44 @@
 
 namespace autofill {
 
+namespace {
+
+const char* const kFieldTypes[] = {
+  "text",
+  "tel",
+  "number",
+};
+
+}  // namespace
+
 class PhoneFieldTest : public testing::Test {
  public:
   PhoneFieldTest() {}
 
  protected:
-  ScopedVector<AutofillField> list_;
-  scoped_ptr<PhoneField> field_;
-  ServerFieldTypeMap field_type_map_;
-
   // Downcast for tests.
   static scoped_ptr<PhoneField> Parse(AutofillScanner* scanner) {
     scoped_ptr<FormField> field = PhoneField::Parse(scanner);
     return make_scoped_ptr(static_cast<PhoneField*>(field.release()));
   }
 
+  void Clear() {
+    list_.clear();
+    field_.reset();
+    field_type_map_.clear();
+  }
+
+  void CheckField(const std::string& name,
+                  ServerFieldType expected_type) const {
+    auto it = field_type_map_.find(ASCIIToUTF16(name));
+    ASSERT_TRUE(it != field_type_map_.end()) << name;
+    EXPECT_EQ(expected_type, it->second) << name;
+  }
+
+  ScopedVector<AutofillField> list_;
+  scoped_ptr<PhoneField> field_;
+  ServerFieldTypeMap field_type_map_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(PhoneFieldTest);
 };
@@ -49,43 +72,45 @@
 
 TEST_F(PhoneFieldTest, ParseOneLinePhone) {
   FormFieldData field;
-  field.form_control_type = "text";
 
-  field.label = ASCIIToUTF16("Phone");
-  field.name = ASCIIToUTF16("phone");
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("phone1")));
+  for (size_t i = 0; i < arraysize(kFieldTypes); ++i) {
+    Clear();
 
-  AutofillScanner scanner(list_.get());
-  field_ = Parse(&scanner);
-  ASSERT_NE(nullptr, field_.get());
-  ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("phone1")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER, field_type_map_[ASCIIToUTF16("phone1")]);
+    field.form_control_type = kFieldTypes[i];
+    field.label = ASCIIToUTF16("Phone");
+    field.name = ASCIIToUTF16("phone");
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("phone1")));
+
+    AutofillScanner scanner(list_.get());
+    field_ = Parse(&scanner);
+    ASSERT_NE(nullptr, field_.get());
+    ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+    CheckField("phone1", PHONE_HOME_WHOLE_NUMBER);
+  }
 }
 
 TEST_F(PhoneFieldTest, ParseTwoLinePhone) {
   FormFieldData field;
-  field.form_control_type = "text";
 
-  field.label = ASCIIToUTF16("Area Code");
-  field.name = ASCIIToUTF16("area code");
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
+  for (size_t i = 0; i < arraysize(kFieldTypes); ++i) {
+    Clear();
 
-  field.label = ASCIIToUTF16("Phone");
-  field.name = ASCIIToUTF16("phone");
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("phone2")));
+    field.form_control_type = kFieldTypes[i];
+    field.label = ASCIIToUTF16("Area Code");
+    field.name = ASCIIToUTF16("area code");
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
 
-  AutofillScanner scanner(list_.get());
-  field_ = Parse(&scanner);
-  ASSERT_NE(nullptr, field_.get());
-  ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("areacode1")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_CITY_CODE, field_type_map_[ASCIIToUTF16("areacode1")]);
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("phone2")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("phone2")]);
+    field.label = ASCIIToUTF16("Phone");
+    field.name = ASCIIToUTF16("phone");
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("phone2")));
+
+    AutofillScanner scanner(list_.get());
+    field_ = Parse(&scanner);
+    ASSERT_NE(nullptr, field_.get());
+    ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+    CheckField("areacode1", PHONE_HOME_CITY_CODE);
+    CheckField("phone2", PHONE_HOME_NUMBER);
+  }
 }
 
 TEST_F(PhoneFieldTest, ThreePartPhoneNumber) {
@@ -95,43 +120,40 @@
   // size: <prefix> is no bigger than 3 characters, and <suffix> is no bigger
   // than 4.
   FormFieldData field;
-  field.form_control_type = "text";
 
-  field.label = ASCIIToUTF16("Phone:");
-  field.name = ASCIIToUTF16("dayphone1");
-  field.max_length = 0;
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
+  for (size_t i = 0; i < arraysize(kFieldTypes); ++i) {
+    Clear();
 
-  field.label = ASCIIToUTF16("-");
-  field.name = ASCIIToUTF16("dayphone2");
-  field.max_length = 3;
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("prefix2")));
+    field.form_control_type = kFieldTypes[i];
+    field.label = ASCIIToUTF16("Phone:");
+    field.name = ASCIIToUTF16("dayphone1");
+    field.max_length = 0;
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
 
-  field.label = ASCIIToUTF16("-");
-  field.name = ASCIIToUTF16("dayphone3");
-  field.max_length = 4;
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("suffix3")));
+    field.label = ASCIIToUTF16("-");
+    field.name = ASCIIToUTF16("dayphone2");
+    field.max_length = 3;
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("prefix2")));
 
-  field.label = ASCIIToUTF16("ext.:");
-  field.name = ASCIIToUTF16("dayphone4");
-  field.max_length = 0;
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("ext4")));
+    field.label = ASCIIToUTF16("-");
+    field.name = ASCIIToUTF16("dayphone3");
+    field.max_length = 4;
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("suffix3")));
 
-  AutofillScanner scanner(list_.get());
-  field_ = Parse(&scanner);
-  ASSERT_NE(nullptr, field_.get());
-  ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("areacode1")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_CITY_CODE, field_type_map_[ASCIIToUTF16("areacode1")]);
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("prefix2")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("prefix2")]);
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("suffix3")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("suffix3")]);
-  EXPECT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("ext4")) == field_type_map_.end());
+    field.label = ASCIIToUTF16("ext.:");
+    field.name = ASCIIToUTF16("dayphone4");
+    field.max_length = 0;
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("ext4")));
+
+    AutofillScanner scanner(list_.get());
+    field_ = Parse(&scanner);
+    ASSERT_NE(nullptr, field_.get());
+    ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+    CheckField("areacode1", PHONE_HOME_CITY_CODE);
+    CheckField("prefix2", PHONE_HOME_NUMBER);
+    CheckField("suffix3", PHONE_HOME_NUMBER);
+    EXPECT_FALSE(ContainsKey(field_type_map_, ASCIIToUTF16("ext4")));
+  }
 }
 
 // This scenario of explicitly labeled "prefix" and "suffix" phone numbers
@@ -139,95 +161,91 @@
 // https://www.wrapables.com/jsp/Signup.jsp.
 TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix) {
   FormFieldData field;
-  field.form_control_type = "text";
 
-  field.label = ASCIIToUTF16("Phone:");
-  field.name = ASCIIToUTF16("area");
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
+  for (size_t i = 0; i < arraysize(kFieldTypes); ++i) {
+    Clear();
 
-  field.label = base::string16();
-  field.name = ASCIIToUTF16("prefix");
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("prefix2")));
+    field.form_control_type = kFieldTypes[i];
+    field.label = ASCIIToUTF16("Phone:");
+    field.name = ASCIIToUTF16("area");
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
 
-  field.label = base::string16();
-  field.name = ASCIIToUTF16("suffix");
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("suffix3")));
+    field.label = base::string16();
+    field.name = ASCIIToUTF16("prefix");
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("prefix2")));
 
-  AutofillScanner scanner(list_.get());
-  field_ = Parse(&scanner);
-  ASSERT_NE(nullptr, field_.get());
-  ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("areacode1")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_CITY_CODE, field_type_map_[ASCIIToUTF16("areacode1")]);
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("prefix2")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("prefix2")]);
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("suffix3")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("suffix3")]);
+    field.label = base::string16();
+    field.name = ASCIIToUTF16("suffix");
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("suffix3")));
+
+    AutofillScanner scanner(list_.get());
+    field_ = Parse(&scanner);
+    ASSERT_NE(nullptr, field_.get());
+    ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+    CheckField("areacode1", PHONE_HOME_CITY_CODE);
+    CheckField("prefix2", PHONE_HOME_NUMBER);
+    CheckField("suffix3", PHONE_HOME_NUMBER);
+  }
 }
 
 TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix2) {
   FormFieldData field;
-  field.form_control_type = "text";
 
-  field.label = ASCIIToUTF16("(");
-  field.name = ASCIIToUTF16("phone1");
-  field.max_length = 3;
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("phone1")));
+  for (size_t i = 0; i < arraysize(kFieldTypes); ++i) {
+    Clear();
 
-  field.label = ASCIIToUTF16(")");
-  field.name = ASCIIToUTF16("phone2");
-  field.max_length = 3;
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("phone2")));
+    field.form_control_type = kFieldTypes[i];
+    field.label = ASCIIToUTF16("(");
+    field.name = ASCIIToUTF16("phone1");
+    field.max_length = 3;
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("phone1")));
 
-  field.label = base::string16();
-  field.name = ASCIIToUTF16("phone3");
-  field.max_length = 4;
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("phone3")));
+    field.label = ASCIIToUTF16(")");
+    field.name = ASCIIToUTF16("phone2");
+    field.max_length = 3;
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("phone2")));
 
-  AutofillScanner scanner(list_.get());
-  field_ = Parse(&scanner);
-  ASSERT_NE(nullptr, field_.get());
-  ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("phone1")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_CITY_CODE, field_type_map_[ASCIIToUTF16("phone1")]);
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("phone2")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("phone2")]);
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("phone3")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("phone3")]);
+    field.label = base::string16();
+    field.name = ASCIIToUTF16("phone3");
+    field.max_length = 4;
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("phone3")));
+
+    AutofillScanner scanner(list_.get());
+    field_ = Parse(&scanner);
+    ASSERT_NE(nullptr, field_.get());
+    ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+    CheckField("phone1", PHONE_HOME_CITY_CODE);
+    CheckField("phone2", PHONE_HOME_NUMBER);
+    CheckField("phone3", PHONE_HOME_NUMBER);
+  }
 }
 
 TEST_F(PhoneFieldTest, CountryAndCityAndPhoneNumber) {
   // Phone in format <country code>:3 - <city and number>:10
   // The |maxlength| is considered, otherwise it's too broad.
   FormFieldData field;
-  field.form_control_type = "text";
 
-  field.label = ASCIIToUTF16("Phone Number");
-  field.name = ASCIIToUTF16("CountryCode");
-  field.max_length = 3;
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("country")));
+  for (size_t i = 0; i < arraysize(kFieldTypes); ++i) {
+    Clear();
 
-  field.label = ASCIIToUTF16("Phone Number");
-  field.name = ASCIIToUTF16("PhoneNumber");
-  field.max_length = 10;
-  list_.push_back(new AutofillField(field, ASCIIToUTF16("phone")));
+    field.form_control_type = kFieldTypes[i];
+    field.label = ASCIIToUTF16("Phone Number");
+    field.name = ASCIIToUTF16("CountryCode");
+    field.max_length = 3;
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("country")));
 
-  AutofillScanner scanner(list_.get());
-  field_ = Parse(&scanner);
-  ASSERT_NE(nullptr, field_.get());
-  ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("country")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_COUNTRY_CODE, field_type_map_[ASCIIToUTF16("country")]);
-  ASSERT_TRUE(
-      field_type_map_.find(ASCIIToUTF16("phone")) != field_type_map_.end());
-  EXPECT_EQ(PHONE_HOME_CITY_AND_NUMBER, field_type_map_[ASCIIToUTF16("phone")]);
+    field.label = ASCIIToUTF16("Phone Number");
+    field.name = ASCIIToUTF16("PhoneNumber");
+    field.max_length = 10;
+    list_.push_back(new AutofillField(field, ASCIIToUTF16("phone")));
+
+    AutofillScanner scanner(list_.get());
+    field_ = Parse(&scanner);
+    ASSERT_NE(nullptr, field_.get());
+    ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+    CheckField("country", PHONE_HOME_COUNTRY_CODE);
+    CheckField("phone", PHONE_HOME_CITY_AND_NUMBER);
+  }
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/wallet/real_pan_wallet_client.cc b/components/autofill/core/browser/wallet/real_pan_wallet_client.cc
index 9353500..3f6a51d 100644
--- a/components/autofill/core/browser/wallet/real_pan_wallet_client.cc
+++ b/components/autofill/core/browser/wallet/real_pan_wallet_client.cc
@@ -10,6 +10,7 @@
 #include "base/json/json_writer.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
@@ -128,6 +129,7 @@
   int response_code = source->GetResponseCode();
   std::string data;
   source->GetResponseAsString(&data);
+  VLOG(2) << "Got data: " << data;
 
   std::string real_pan;
   AutofillClient::GetRealPanResult result = AutofillClient::SUCCESS;
@@ -135,16 +137,20 @@
   switch (response_code) {
     // Valid response.
     case net::HTTP_OK: {
+      std::string error_code;
       scoped_ptr<base::Value> message_value(base::JSONReader::Read(data));
       if (message_value.get() &&
           message_value->IsType(base::Value::TYPE_DICTIONARY)) {
         response_dict.reset(
             static_cast<base::DictionaryValue*>(message_value.release()));
         response_dict->GetString("pan", &real_pan);
-        // TODO(estade): check response for allow_retry.
+        response_dict->GetString("error.code", &error_code);
       }
-      if (real_pan.empty())
+
+      if (LowerCaseEqualsASCII(error_code, "internal"))
         result = AutofillClient::TRY_AGAIN_FAILURE;
+      else if (real_pan.empty() || !error_code.empty())
+        result = AutofillClient::PERMANENT_FAILURE;
 
       break;
     }
@@ -176,8 +182,8 @@
   }
 
   if (real_pan.empty()) {
-    LOG(ERROR) << "Wallet returned error: " << response_code
-               << " with data: " << data;
+    VLOG(1) << "Wallet returned error: " << response_code
+            << " with data: " << data;
   }
 
   DCHECK_EQ(result != AutofillClient::SUCCESS, real_pan.empty());
@@ -200,7 +206,7 @@
     const OAuth2TokenService::Request* request,
     const GoogleServiceAuthError& error) {
   DCHECK_EQ(request, access_token_request_.get());
-  LOG(ERROR) << "Unhandled OAuth2 error: " << error.ToString();
+  VLOG(1) << "Unhandled OAuth2 error: " << error.ToString();
   if (request_) {
     request_.reset();
     delegate_->OnDidGetRealPan(AutofillClient::PERMANENT_FAILURE,
diff --git a/components/autofill/core/browser/wallet/real_pan_wallet_client.h b/components/autofill/core/browser/wallet/real_pan_wallet_client.h
index 6dd7a2d7..b656d4a 100644
--- a/components/autofill/core/browser/wallet/real_pan_wallet_client.h
+++ b/components/autofill/core/browser/wallet/real_pan_wallet_client.h
@@ -27,6 +27,7 @@
 // RealPanWalletClient is modelled on WalletClient. Whereas the latter is used
 // for requestAutocomplete-related requests, RealPanWalletClient is used to
 // import user data from Wallet for normal web Autofill.
+// Tests: See content/browser/wallet/real_pan_wallet_client_unittest.cc
 class RealPanWalletClient : public net::URLFetcherDelegate,
                             public OAuth2TokenService::Consumer  {
  public:
diff --git a/components/browser_watcher.gypi b/components/browser_watcher.gypi
index e1997ac..b65d463 100644
--- a/components/browser_watcher.gypi
+++ b/components/browser_watcher.gypi
@@ -18,6 +18,8 @@
               'browser_watcher/exit_code_watcher_win.h',
               'browser_watcher/exit_funnel_win.cc',
               'browser_watcher/exit_funnel_win.h',
+              'browser_watcher/window_hang_monitor_win.cc',
+              'browser_watcher/window_hang_monitor_win.h',
             ],
             'dependencies': [
               '../base/base.gyp:base',
diff --git a/components/browser_watcher/BUILD.gn b/components/browser_watcher/BUILD.gn
index 1f782c2a0..6720895 100644
--- a/components/browser_watcher/BUILD.gn
+++ b/components/browser_watcher/BUILD.gn
@@ -12,6 +12,8 @@
     "exit_code_watcher_win.h",
     "exit_funnel_win.cc",
     "exit_funnel_win.h",
+    "window_hang_monitor_win.cc",
+    "window_hang_monitor_win.h",
   ]
   deps = [
     "//base",
@@ -38,6 +40,7 @@
     "exit_funnel_win_unittest.cc",
     "watcher_client_win_unittest.cc",
     "watcher_metrics_provider_win_unittest.cc",
+    "window_hang_monitor_win_unittest.cc",
   ]
   configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
   deps = [
diff --git a/components/browser_watcher/window_hang_monitor_win.cc b/components/browser_watcher/window_hang_monitor_win.cc
new file mode 100644
index 0000000..1218fc5b
--- /dev/null
+++ b/components/browser_watcher/window_hang_monitor_win.cc
@@ -0,0 +1,182 @@
+// 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 "components/browser_watcher/window_hang_monitor_win.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/win/message_window.h"
+
+namespace browser_watcher {
+
+namespace {
+
+const size_t kPingIntervalSeconds = 60;
+const size_t kHangTimeoutSeconds = 20;
+
+bool IsWindowValid(HWND window,
+                   const base::string16& window_name,
+                   base::ProcessId pid) {
+  // Validate the Window in two respects:
+  // 1. The window handle might have been re-assigned to a different window
+  //    from the time we found it to the point where we query for its owning
+  //    process.
+  // 2. The window handle might have been re-assigned to a different process
+  //    at any point after we found it.
+  if (window != base::win::MessageWindow::FindWindow(window_name)) {
+    // The window handle has been reassigned, bail.
+    return false;
+  }
+
+  // Re-do the process ID lookup.
+  DWORD new_process_id = 0;
+  DWORD thread_id = ::GetWindowThreadProcessId(window, &new_process_id);
+  if (thread_id == 0 || pid != new_process_id) {
+    // Another process has taken over the handle.
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+WindowHangMonitor::WindowHangMonitor(const WindowEventCallback& callback)
+    : callback_(callback),
+      window_(NULL),
+      outstanding_ping_(nullptr),
+      timer_(false /* don't retain user task */, false /* don't repeat */),
+      ping_interval_(base::TimeDelta::FromSeconds(kPingIntervalSeconds)),
+      hang_timeout_(base::TimeDelta::FromSeconds(kHangTimeoutSeconds)) {
+}
+
+WindowHangMonitor::~WindowHangMonitor() {
+  if (outstanding_ping_) {
+    // We have an outstanding ping, disable it and leak it intentionally as
+    // if the callback arrives eventually, it'll cause a use-after-free.
+    outstanding_ping_->monitor = nullptr;
+    outstanding_ping_ = nullptr;
+  }
+}
+
+bool WindowHangMonitor::Initialize(const base::string16& window_name) {
+  window_name_ = window_name;
+  timer_.SetTaskRunner(base::MessageLoop::current()->task_runner());
+
+  // This code is fraught with all kinds of races. As the purpose here is
+  // only monitoring, this code simply bails if any kind of race is encountered.
+  // Find the window to monitor by name.
+  window_ = base::win::MessageWindow::FindWindow(window_name);
+  if (window_ == NULL)
+    return false;
+
+  // Find and open the process owning this window.
+  DWORD process_id = 0;
+  DWORD thread_id = ::GetWindowThreadProcessId(window_, &process_id);
+  if (thread_id == 0 || process_id == 0) {
+    // The window has vanished or there was some other problem with the handle.
+    return false;
+  }
+
+  // Keep an open handle on the process to make sure the PID isn't reused.
+  window_process_ = base::Process::Open(process_id);
+  if (!window_process_.IsValid()) {
+    // The process may be at a different security level.
+    return false;
+  }
+
+  return MaybeSendPing();
+}
+
+void WindowHangMonitor::SetPingIntervalForTesting(
+    base::TimeDelta ping_interval) {
+  ping_interval_ = ping_interval;
+}
+
+void WindowHangMonitor::SetHangTimeoutForTesting(base::TimeDelta hang_timeout) {
+  hang_timeout_ = hang_timeout;
+}
+
+void CALLBACK WindowHangMonitor::OnPongReceived(HWND window,
+                                                UINT msg,
+                                                ULONG_PTR data,
+                                                LRESULT lresult) {
+  OutstandingPing* outstanding = reinterpret_cast<OutstandingPing*>(data);
+
+  // If the monitor is still around, clear its pointer.
+  if (outstanding->monitor)
+    outstanding->monitor->outstanding_ping_ = nullptr;
+
+  delete outstanding;
+}
+
+bool WindowHangMonitor::MaybeSendPing() {
+  DCHECK(window_process_.IsValid());
+  DCHECK(window_);
+  DCHECK(!outstanding_ping_);
+
+  if (!IsWindowValid(window_, window_name_, window_process_.Pid())) {
+    // The window is no longer valid, issue the callback.
+    callback_.Run(WINDOW_VANISHED);
+    return false;
+  }
+
+  // The window checks out, issue a ping against it. Set up all state ahead of
+  // time to allow for the possibility of the callback being invoked from within
+  // SendMessageCallback.
+  outstanding_ping_ = new OutstandingPing;
+  outstanding_ping_->monitor = this;
+
+  // Note that this is still racy to |window_| having been re-assigned, but
+  // the race is as small as we can make it, and the next attempt will re-try.
+  if (!::SendMessageCallback(window_, WM_NULL, 0, 0, &OnPongReceived,
+                             reinterpret_cast<ULONG_PTR>(outstanding_ping_))) {
+    // Message sending failed, assume the window is no longer valid,
+    // issue the callback and stop the polling.
+    delete outstanding_ping_;
+    outstanding_ping_ = nullptr;
+
+    callback_.Run(WINDOW_VANISHED);
+    return false;
+  }
+
+  // Issue the count-out callback.
+  timer_.Start(
+      FROM_HERE, hang_timeout_,
+      base::Bind(&WindowHangMonitor::OnHangTimeout, base::Unretained(this)));
+
+  return true;
+}
+
+void WindowHangMonitor::OnHangTimeout() {
+  DCHECK(window_process_.IsValid());
+  DCHECK(window_);
+
+  if (outstanding_ping_) {
+    // The ping is still outstanding, the window is hung or has vanished.
+    // Orphan the outstanding ping. If the callback arrives late, it will
+    // delete it, or if the callback never arrives it'll leak.
+    outstanding_ping_->monitor = NULL;
+    outstanding_ping_ = NULL;
+
+    if (!IsWindowValid(window_, window_name_, window_process_.Pid())) {
+      // The window vanished.
+      callback_.Run(WINDOW_VANISHED);
+    } else {
+      // The window hung.
+      callback_.Run(WINDOW_HUNG);
+    }
+  } else {
+    // No ping outstanding, window is not yet hung. Schedule the next retry.
+    timer_.Start(
+        FROM_HERE, hang_timeout_ - ping_interval_,
+        base::Bind(&WindowHangMonitor::OnRetryTimeout, base::Unretained(this)));
+  }
+}
+
+void WindowHangMonitor::OnRetryTimeout() {
+  MaybeSendPing();
+}
+
+}  // namespace browser_watcher
diff --git a/components/browser_watcher/window_hang_monitor_win.h b/components/browser_watcher/window_hang_monitor_win.h
new file mode 100644
index 0000000..47c24bc
--- /dev/null
+++ b/components/browser_watcher/window_hang_monitor_win.h
@@ -0,0 +1,102 @@
+// 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 COMPONENTS_BROWSER_WATCHER_WINDOW_HANG_MONITOR_WIN_H_
+#define COMPONENTS_BROWSER_WATCHER_WINDOW_HANG_MONITOR_WIN_H_
+
+#include <windows.h>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/process/process.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+
+namespace browser_watcher {
+
+// Monitors a window for hanging by periodically sending it a WM_NULL message
+// and timing the response.
+class WindowHangMonitor {
+ public:
+  enum WindowEvent {
+    WINDOW_HUNG,
+    WINDOW_VANISHED,
+  };
+  // Called when a hang is detected or when the window has gone away.
+  // Called precisely zero or one time(s).
+  typedef base::Callback<void(WindowEvent)> WindowEventCallback;
+
+  // Initialize the monitor with an event callback.
+  explicit WindowHangMonitor(const WindowEventCallback& callback);
+  ~WindowHangMonitor();
+
+  // Initializes the watcher to monitor the window answering to |window_name|.
+  // Returns true on success.
+  bool Initialize(const base::string16& window_name);
+
+  // Testing accessors.
+  bool IsIdleForTesting() const { return !timer_.IsRunning(); }
+  void SetPingIntervalForTesting(base::TimeDelta ping_interval);
+  void SetHangTimeoutForTesting(base::TimeDelta hang_timeout);
+
+  HWND window() const { return window_; }
+  const base::Process& window_process() const { return window_process_; }
+
+ private:
+  struct OutstandingPing {
+    WindowHangMonitor* monitor;
+  };
+
+  static void CALLBACK
+  OnPongReceived(HWND window, UINT msg, ULONG_PTR data, LRESULT lresult);
+
+  // Checks that |window_| is still valid, and sends it a ping.
+  // Issues a |WINDOW_VANISHED| callback if the window's no longer valid.
+  // Schedules OnHangTimeout in case of success.
+  // Returns true on success, false if the window is no longer valid or other
+  // failure.
+  bool MaybeSendPing();
+
+  // Runs after a |hang_timeout_| delay after sending a ping. Checks whether
+  // a pong was received. Either issues a callback or schedules OnRetryTimeout.
+  void OnHangTimeout();
+
+  // Runs periodically at |ping_interval_| interval, as long as the window is
+  // still valid and not hung.
+  void OnRetryTimeout();
+
+  // Invoked on significant window events.
+  WindowEventCallback callback_;
+
+  // The name of the (message) window to monitor.
+  base::string16 window_name_;
+
+  // The monitored window handle.
+  HWND window_;
+
+  // The process that owned |window_| when Initialize was called.
+  base::Process window_process_;
+
+  // The time the last message was sent.
+  base::Time last_ping_;
+
+  // The ping interval, must be larger than |hang_timeout_|.
+  base::TimeDelta ping_interval_;
+
+  // The time after which |window_| is assumed hung.
+  base::TimeDelta hang_timeout_;
+
+  // The timer used to schedule polls.
+  base::Timer timer_;
+
+  // Non-null when there is an outstanding ping.
+  // This is intentionally leaked when a hang is detected.
+  OutstandingPing* outstanding_ping_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowHangMonitor);
+};
+
+}  // namespace browser_watcher
+
+#endif  // COMPONENTS_BROWSER_WATCHER_WINDOW_HANG_MONITOR_WIN_H_
diff --git a/components/browser_watcher/window_hang_monitor_win_unittest.cc b/components/browser_watcher/window_hang_monitor_win_unittest.cc
new file mode 100644
index 0000000..236c18d
--- /dev/null
+++ b/components/browser_watcher/window_hang_monitor_win_unittest.cc
@@ -0,0 +1,221 @@
+// 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 "components/browser_watcher/window_hang_monitor_win.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/process/process_handle.h"
+#include "base/run_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/win/message_window.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_watcher {
+
+namespace {
+
+class WindowHangMonitorTest : public testing::Test {
+ public:
+  typedef std::vector<WindowHangMonitor::WindowEvent> WindowEventVector;
+
+  WindowHangMonitorTest()
+      : monitor_(base::Bind(&WindowHangMonitorTest::OnWindowEvent,
+                            base::Unretained(this))),
+        message_loop_(base::MessageLoop::TYPE_UI),
+        run_loop_(nullptr),
+        pings_(0),
+        worker_thread_("HangMan") {}
+
+  // Callback from the hang detector.
+  void OnWindowEvent(WindowHangMonitor::WindowEvent event) {
+    // Record the event and terminate the message loop.
+    events_.push_back(event);
+    run_loop_->Quit();
+  }
+
+  void SetUp() override {
+    // Pick a window name unique to this process.
+    window_name_ = base::StringPrintf(L"WindowHanMonitorTest-%d",
+                                      base::GetCurrentProcId());
+    ASSERT_TRUE(worker_thread_.StartWithOptions(
+        base::Thread::Options(base::MessageLoop::TYPE_UI, 0)));
+
+    // Set relatively short hang detection and ping intervals.
+    monitor_.SetHangTimeoutForTesting(base::TimeDelta::FromMilliseconds(50));
+    monitor_.SetPingIntervalForTesting(base::TimeDelta::FromMilliseconds(200));
+  }
+
+  void TearDown() override {
+    DeleteMessageWindow();
+    worker_thread_.Stop();
+  }
+
+  void CreateMessageWindow() {
+    bool succeeded = false;
+    base::WaitableEvent created(true, false);
+    ASSERT_TRUE(worker_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&WindowHangMonitorTest::CreateMessageWindowInWorkerThread,
+                   base::Unretained(this), window_name_, &succeeded,
+                   &created)));
+    created.Wait();
+    ASSERT_TRUE(succeeded);
+  }
+
+  void DeleteMessageWindow() {
+    base::WaitableEvent deleted(true, false);
+    worker_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&WindowHangMonitorTest::DeleteMessageWindowInWorkerThread,
+                   base::Unretained(this), &deleted));
+    deleted.Wait();
+  }
+
+  bool MessageCallback(UINT message,
+                       WPARAM wparam,
+                       LPARAM lparam,
+                       LRESULT* result) {
+    EXPECT_EQ(worker_thread_.message_loop(), base::MessageLoop::current());
+    if (message == WM_NULL)
+      ++pings_;
+
+    return false;  // Pass through to DefWindowProc.
+  }
+
+  void RunMessageLoop() {
+    ASSERT_FALSE(run_loop_);
+
+    base::RunLoop loop;
+
+    run_loop_ = &loop;
+    loop.Run();
+    run_loop_ = nullptr;
+  }
+
+  WindowHangMonitor* monitor() { return &monitor_; }
+  const WindowEventVector& events() const { return events_; }
+  const base::win::MessageWindow* message_window() const {
+    return message_window_.get();
+  }
+  size_t pings() const { return pings_; }
+  const base::string16& window_name() const { return window_name_; }
+  base::Thread* worker_thread() { return &worker_thread_; }
+
+ private:
+  void CreateMessageWindowInWorkerThread(const base::string16& name,
+                                         bool* success,
+                                         base::WaitableEvent* created) {
+    message_window_.reset(new base::win::MessageWindow);
+    *success = message_window_->CreateNamed(
+        base::Bind(&WindowHangMonitorTest::MessageCallback,
+                   base::Unretained(this)),
+        name);
+    created->Signal();
+  }
+
+  void DeleteMessageWindowInWorkerThread(base::WaitableEvent* deleted) {
+    message_window_.reset();
+    if (deleted)
+      deleted->Signal();
+  }
+
+  WindowHangMonitor monitor_;
+  WindowEventVector events_;
+
+  // Message and run loops for the main thread.
+  base::MessageLoop message_loop_;
+  base::RunLoop* run_loop_;
+  scoped_ptr<base::win::MessageWindow> message_window_;
+  base::string16 window_name_;
+  size_t pings_;
+  base::Thread worker_thread_;
+};
+
+}  // namespace
+
+TEST_F(WindowHangMonitorTest, InitFailsWhenNoWindow) {
+  ASSERT_FALSE(monitor()->Initialize(window_name()));
+  EXPECT_TRUE(monitor()->IsIdleForTesting());
+  EXPECT_EQ(0, pings());
+  EXPECT_EQ(0, events().size());
+}
+
+TEST_F(WindowHangMonitorTest, InitSucceedsWhenWindow) {
+  CreateMessageWindow();
+
+  ASSERT_TRUE(monitor()->Initialize(window_name()));
+  EXPECT_FALSE(monitor()->IsIdleForTesting());
+
+  // Delete the window to synchronize against any pending message pings.
+  DeleteMessageWindow();
+
+  EXPECT_EQ(1, pings());
+  EXPECT_EQ(0, events().size());
+}
+
+TEST_F(WindowHangMonitorTest, DetectsWindowDisappearance) {
+  CreateMessageWindow();
+
+  EXPECT_TRUE(monitor()->Initialize(window_name()));
+  EXPECT_EQ(0, events().size());
+
+  DeleteMessageWindow();
+
+  RunMessageLoop();
+
+  EXPECT_TRUE(monitor()->IsIdleForTesting());
+  ASSERT_EQ(1, events().size());
+  EXPECT_EQ(WindowHangMonitor::WINDOW_VANISHED, events()[0]);
+}
+
+TEST_F(WindowHangMonitorTest, DetectsWindowNameChange) {
+  // This test changes the title of the message window as a proxy for what
+  // happens if the window handle is reused for a different purpose. The latter
+  // is impossible to test in a deterministic fashion.
+  CreateMessageWindow();
+
+  ASSERT_TRUE(monitor()->Initialize(window_name()));
+  EXPECT_EQ(0, events().size());
+
+  ASSERT_TRUE(::SetWindowText(message_window()->hwnd(), L"Gonsky"));
+
+  RunMessageLoop();
+
+  EXPECT_TRUE(monitor()->IsIdleForTesting());
+  ASSERT_EQ(1, events().size());
+  EXPECT_EQ(WindowHangMonitor::WINDOW_VANISHED, events()[0]);
+}
+
+TEST_F(WindowHangMonitorTest, DetectsWindowHang) {
+  CreateMessageWindow();
+
+  ASSERT_TRUE(monitor()->Initialize(window_name()));
+  EXPECT_EQ(0, events().size());
+
+  // Block the worker thread.
+  base::WaitableEvent hang(true, false);
+
+  worker_thread()->message_loop_proxy()->PostTask(
+      FROM_HERE,
+      base::Bind(&base::WaitableEvent::Wait, base::Unretained(&hang)));
+
+  RunMessageLoop();
+
+  // Unblock the worker thread.
+  hang.Signal();
+
+  EXPECT_TRUE(monitor()->IsIdleForTesting());
+  ASSERT_EQ(1, events().size());
+  EXPECT_EQ(WindowHangMonitor::WINDOW_HUNG, events()[0]);
+}
+
+}  // namespace browser_watcher
diff --git a/components/cdm/renderer/android_key_systems.cc b/components/cdm/renderer/android_key_systems.cc
index d1e0f2a..d9bb106 100644
--- a/components/cdm/renderer/android_key_systems.cc
+++ b/components/cdm/renderer/android_key_systems.cc
@@ -7,14 +7,17 @@
 #include <string>
 #include <vector>
 
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "components/cdm/common/cdm_messages_android.h"
 #include "components/cdm/renderer/widevine_key_systems.h"
 #include "content/public/renderer/render_thread.h"
 #include "media/base/eme_constants.h"
+#include "media/base/media_switches.h"
 
 #include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.
 
+using media::EmeRobustness;
 using media::KeySystemInfo;
 using media::SupportedCodecs;
 
@@ -39,24 +42,48 @@
 void AddAndroidWidevine(std::vector<KeySystemInfo>* concrete_key_systems) {
   SupportedKeySystemResponse response = QueryKeySystemSupport(
       kWidevineKeySystem);
-  if (response.compositing_codecs != media::EME_CODEC_NONE) {
+
+  // When creating the WIDEVINE key system, BrowserCdmFactoryAndroid configures
+  // the CDM's security level based on the --mediadrm-enable-non-compositing
+  // flag (L1 if the flag is enabled, L3 otherwise). Therefore the supported
+  // codec/robustenss combinations depend on that flag.
+  // TODO(sandersd): For unprefixed, set the security level based on the
+  // requested robustness instead of the flag. http://crbug.com/467779
+  SupportedCodecs codecs = response.compositing_codecs;
+  EmeRobustness max_audio_robustness = EmeRobustness::SW_SECURE_CRYPTO;
+  EmeRobustness max_video_robustness = EmeRobustness::SW_SECURE_CRYPTO;
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kMediaDrmEnableNonCompositing)) {
+    codecs = response.non_compositing_codecs;
+    max_audio_robustness = EmeRobustness::HW_SECURE_CRYPTO;
+    max_video_robustness = EmeRobustness::HW_SECURE_ALL;
+  }
+  if (codecs != media::EME_CODEC_NONE) {
     AddWidevineWithCodecs(
         WIDEVINE,
-        static_cast<SupportedCodecs>(response.compositing_codecs),
-        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent license.
-        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent release message.
+        codecs,
+        max_audio_robustness,
+        max_video_robustness,
+        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // persistent-license.
+        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // persistent-release-message.
         media::EME_FEATURE_NOT_SUPPORTED,       // Persistent state.
         media::EME_FEATURE_ALWAYS_ENABLED,      // Distinctive identifier.
         concrete_key_systems);
   }
 
+  // For compatibility with the prefixed API, register a separate L1 key system.
+  // When creating the WIDEVINE_HR_NON_COMPOSITING key system,
+  // BrowserCdmFactoryAndroid does not configure the CDM's security level (that
+  // is, leaves it as L1); therefore only secure codecs are supported.
+  // TODO(ddorwin): Remove with unprefixed. http://crbug.com/249976
   if (response.non_compositing_codecs != media::EME_CODEC_NONE) {
-    // TODO(ddorwin): Remove with unprefixed. http://crbug.com/249976
     AddWidevineWithCodecs(
         WIDEVINE_HR_NON_COMPOSITING,
-        static_cast<SupportedCodecs>(response.non_compositing_codecs),
-        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent license.
-        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent release message.
+        response.non_compositing_codecs,
+        EmeRobustness::HW_SECURE_CRYPTO,        // Max audio robustness.
+        EmeRobustness::HW_SECURE_ALL,           // Max video robustness.
+        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // persistent-license.
+        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // persistent-release-message.
         media::EME_FEATURE_NOT_SUPPORTED,       // Persistent state.
         media::EME_FEATURE_ALWAYS_ENABLED,      // Distinctive identifier.
         concrete_key_systems);
@@ -80,11 +107,13 @@
       // associated initialization data type. KeySystems handles validating
       // |init_data_type| x |container| pairings.
       if (response.compositing_codecs & media::EME_CODEC_WEBM_ALL)
-        info.supported_init_data_types |= media::EME_INIT_DATA_TYPE_WEBM;
+        info.supported_init_data_types |= media::kInitDataTypeMaskWebM;
 #if defined(USE_PROPRIETARY_CODECS)
       if (response.compositing_codecs & media::EME_CODEC_MP4_ALL)
-        info.supported_init_data_types |= media::EME_INIT_DATA_TYPE_CENC;
+        info.supported_init_data_types |= media::kInitDataTypeMaskCenc;
 #endif  // defined(USE_PROPRIETARY_CODECS)
+      info.max_audio_robustness = EmeRobustness::EMPTY;
+      info.max_video_robustness = EmeRobustness::EMPTY;
       // Assume the worst case (from a user point of view).
       info.persistent_license_support = media::EME_SESSION_TYPE_NOT_SUPPORTED;
       info.persistent_release_message_support =
diff --git a/components/cdm/renderer/widevine_key_systems.cc b/components/cdm/renderer/widevine_key_systems.cc
index 037df1b..c1412c3 100644
--- a/components/cdm/renderer/widevine_key_systems.cc
+++ b/components/cdm/renderer/widevine_key_systems.cc
@@ -29,6 +29,8 @@
 void AddWidevineWithCodecs(
     WidevineCdmType widevine_cdm_type,
     SupportedCodecs supported_codecs,
+    media::EmeRobustness max_audio_robustness,
+    media::EmeRobustness max_video_robustness,
     media::EmeSessionTypeSupport persistent_license_support,
     media::EmeSessionTypeSupport persistent_release_message_support,
     media::EmeFeatureSupport persistent_state_support,
@@ -60,12 +62,14 @@
   // associated initialization data type. KeySystems handles validating
   // |init_data_type| x |container| pairings.
   if (supported_codecs & media::EME_CODEC_WEBM_ALL)
-    info.supported_init_data_types |= media::EME_INIT_DATA_TYPE_WEBM;
+    info.supported_init_data_types |= media::kInitDataTypeMaskWebM;
 #if defined(USE_PROPRIETARY_CODECS)
   if (supported_codecs & media::EME_CODEC_MP4_ALL)
-    info.supported_init_data_types |= media::EME_INIT_DATA_TYPE_CENC;
+    info.supported_init_data_types |= media::kInitDataTypeMaskCenc;
 #endif  // defined(USE_PROPRIETARY_CODECS)
 
+  info.max_audio_robustness = max_audio_robustness;
+  info.max_video_robustness = max_video_robustness;
   info.persistent_license_support = persistent_license_support;
   info.persistent_release_message_support = persistent_release_message_support;
   info.persistent_state_support = persistent_state_support;
diff --git a/components/cdm/renderer/widevine_key_systems.h b/components/cdm/renderer/widevine_key_systems.h
index 15c55daf8..dc05bb8 100644
--- a/components/cdm/renderer/widevine_key_systems.h
+++ b/components/cdm/renderer/widevine_key_systems.h
@@ -22,6 +22,8 @@
 void AddWidevineWithCodecs(
     WidevineCdmType widevine_cdm_type,
     media::SupportedCodecs supported_codecs,
+    media::EmeRobustness max_audio_robustness,
+    media::EmeRobustness max_video_robustness,
     media::EmeSessionTypeSupport persistent_license_support,
     media::EmeSessionTypeSupport persistent_release_message_support,
     media::EmeFeatureSupport persistent_state_support,
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index df6bbf1a..7a8045c 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -20,6 +20,7 @@
       'autofill/content/browser/request_autocomplete_manager_unittest.cc',
       'autofill/content/browser/wallet/full_wallet_unittest.cc',
       'autofill/content/browser/wallet/instrument_unittest.cc',
+      'autofill/content/browser/wallet/real_pan_wallet_client_unittest.cc',
       'autofill/content/browser/wallet/wallet_address_unittest.cc',
       'autofill/content/browser/wallet/wallet_client_unittest.cc',
       'autofill/content/browser/wallet/wallet_items_unittest.cc',
@@ -77,6 +78,7 @@
       'browser_watcher/exit_funnel_win_unittest.cc',
       'browser_watcher/watcher_client_win_unittest.cc',
       'browser_watcher/watcher_metrics_provider_win_unittest.cc',
+      'browser_watcher/window_hang_monitor_win_unittest.cc',
     ],
     'captive_portal_unittest_sources': [
       'captive_portal/captive_portal_detector_unittest.cc',
@@ -103,6 +105,7 @@
     'data_reduction_proxy_unittest_sources': [
       'data_reduction_proxy/content/browser/data_reduction_proxy_message_filter_unittest.cc',
       'data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc',
+      'data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc',
       'data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc',
       'data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc',
       'data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc',
@@ -115,6 +118,7 @@
       'data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs_unittest.cc',
       'data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection_unittest.cc',
       'data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats_unittest.cc',
+      'data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser_unittest.cc',
       'data_reduction_proxy/core/common/data_reduction_proxy_event_store_unittest.cc',
       'data_reduction_proxy/core/common/data_reduction_proxy_headers_unittest.cc',
       'data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc',
@@ -303,7 +307,6 @@
     ],
     'password_manager_unittest_sources': [
       'password_manager/content/browser/credential_manager_dispatcher_unittest.cc',
-      'password_manager/content/common/credential_manager_types_unittest.cc',
       'password_manager/core/browser/affiliated_match_helper_unittest.cc',
       'password_manager/core/browser/affiliation_backend_unittest.cc',
       'password_manager/core/browser/affiliation_database_unittest.cc',
@@ -325,6 +328,7 @@
       'password_manager/core/browser/password_store_unittest.cc',
       'password_manager/core/browser/password_syncable_service_unittest.cc',
       'password_manager/core/browser/psl_matching_helper_unittest.cc',
+      'password_manager/core/common/credential_manager_types_unittest.cc',
     ],
     'policy_unittest_sources': [
       'policy/core/browser/autofill_policy_handler_unittest.cc',
diff --git a/components/content_settings/core/browser/content_settings_default_provider.cc b/components/content_settings/core/browser/content_settings_default_provider.cc
index a0a3e33..57c573f1 100644
--- a/components/content_settings/core/browser/content_settings_default_provider.cc
+++ b/components/content_settings/core/browser/content_settings_default_provider.cc
@@ -133,6 +133,13 @@
       prefs::kMigratedDefaultContentSettings,
       false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+
+  // Whether the deprecated mediastream default setting has already been
+  // migrated into microphone and camera default settings.
+  registry->RegisterBooleanPref(
+      prefs::kMigratedDefaultMediaStreamSetting,
+      false,
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
 }
 
 DefaultProvider::DefaultProvider(PrefService* prefs, bool incognito)
@@ -145,6 +152,10 @@
   // preferences.
   MigrateDefaultSettings();
 
+  // Migrate the obsolete media stream default setting into the new microphone
+  // and camera settings.
+  MigrateObsoleteMediaContentSetting();
+
   // Read global defaults.
   ReadDefaultSettings();
 
@@ -505,4 +516,18 @@
   prefs_->SetBoolean(prefs::kMigratedDefaultContentSettings, true);
 }
 
+void DefaultProvider::MigrateObsoleteMediaContentSetting() {
+  // We only do the migration once.
+  if (prefs_->GetBoolean(prefs::kMigratedDefaultMediaStreamSetting))
+    return;
+
+  scoped_ptr<base::Value> value = ReadIndividualPref(
+      CONTENT_SETTINGS_TYPE_MEDIASTREAM);
+  WriteIndividualPref(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, value.get());
+  WriteIndividualPref(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, value.get());
+  WriteIndividualPref(CONTENT_SETTINGS_TYPE_MEDIASTREAM, NULL);
+
+  prefs_->SetBoolean(prefs::kMigratedDefaultMediaStreamSetting, true);
+}
+
 }  // namespace content_settings
diff --git a/components/content_settings/core/browser/content_settings_default_provider.h b/components/content_settings/core/browser/content_settings_default_provider.h
index 2a58fa8..86ec1c8 100644
--- a/components/content_settings/core/browser/content_settings_default_provider.h
+++ b/components/content_settings/core/browser/content_settings_default_provider.h
@@ -97,6 +97,10 @@
   // once during the first run.
   void MigrateDefaultSettings();
 
+  // Migrates the obsolete media stream default setting to the new microphone
+  // and camera settings.
+  void MigrateObsoleteMediaContentSetting();
+
   // Copies of the pref data, so that we can read it on the IO thread.
   ValueMap default_settings_;
 
diff --git a/components/content_settings/core/browser/content_settings_policy_provider.cc b/components/content_settings/core/browser/content_settings_policy_provider.cc
index 97116406..445a4ad 100644
--- a/components/content_settings/core/browser/content_settings_policy_provider.cc
+++ b/components/content_settings/core/browser/content_settings_policy_provider.cc
@@ -32,9 +32,9 @@
   NULL,  // No policy for default value of fullscreen requests
   NULL,  // No policy for default value of mouse lock requests
   NULL,  // No policy for default value of mixed script blocking
+  NULL,  // The MEDIASTREAM setting is deprecated
   prefs::kManagedDefaultMediaStreamSetting,
-  NULL,  // No policy for default value of media stream mic
-  NULL,  // No policy for default value of media stream camera
+  prefs::kManagedDefaultMediaStreamSetting,
   NULL,  // No policy for default value of protocol handlers
   NULL,  // No policy for default value of PPAPI broker
   NULL,  // No policy for default value of multiple automatic downloads
@@ -466,7 +466,8 @@
   } else if (name == prefs::kManagedDefaultNotificationsSetting) {
     UpdateManagedDefaultSetting(CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
   } else if (name == prefs::kManagedDefaultMediaStreamSetting) {
-    UpdateManagedDefaultSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM);
+    UpdateManagedDefaultSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC);
+    UpdateManagedDefaultSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
   } else if (name == prefs::kManagedAutoSelectCertificateForUrls ||
              name == prefs::kManagedCookiesAllowedForUrls ||
              name == prefs::kManagedCookiesBlockedForUrls ||
diff --git a/components/content_settings/core/browser/host_content_settings_map.cc b/components/content_settings/core/browser/host_content_settings_map.cc
index 0f22c831..37eb659 100644
--- a/components/content_settings/core/browser/host_content_settings_map.cc
+++ b/components/content_settings/core/browser/host_content_settings_map.cc
@@ -245,6 +245,10 @@
     ContentSettingsType content_type,
     const std::string& resource_identifier,
     base::Value* value) {
+  // TODO(msramek): MEDIASTREAM is deprecated. Remove this check when all
+  // references to MEDIASTREAM are removed from the code.
+  DCHECK_NE(content_type, CONTENT_SETTINGS_TYPE_MEDIASTREAM);
+
   DCHECK(IsValueAllowedForType(prefs_, value, content_type));
   DCHECK(SupportsResourceIdentifier(content_type) ||
          resource_identifier.empty());
@@ -496,9 +500,8 @@
     return false;
   }
 
-  // We don't support ALLOW for media default setting.
-  if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM &&
-      setting == CONTENT_SETTING_ALLOW) {
+  // We don't support the mediastream setting.
+  if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM) {
     return false;
   }
 
@@ -713,6 +716,10 @@
     const std::string& resource_identifier,
     content_settings::SettingInfo* info,
     bool get_override) const {
+  // TODO(msramek): MEDIASTREAM is deprecated. Remove this check when all
+  // references to MEDIASTREAM are removed from the code.
+  DCHECK_NE(CONTENT_SETTINGS_TYPE_MEDIASTREAM, content_type);
+
   UsedContentSettingsProviders();
   ContentSettingsPattern* primary_pattern = NULL;
   ContentSettingsPattern* secondary_pattern = NULL;
diff --git a/components/content_settings/core/common/pref_names.cc b/components/content_settings/core/common/pref_names.cc
index 10353a5..da7bd8b 100644
--- a/components/content_settings/core/common/pref_names.cc
+++ b/components/content_settings/core/common/pref_names.cc
@@ -85,6 +85,11 @@
 // If a value is not set, it means the setting is allowed.
 const char kOverrideContentSettings[] = "profile.override_content_settings";
 
+// Boolean indicating whether the media stream default setting had been
+// migrated into two separate microphone and camera settings.
+const char kMigratedDefaultMediaStreamSetting[] =
+    "profile.migrated_default_media_stream_content_settings";
+
 // Preferences that are exclusively used to store managed values for default
 // content settings.
 const char kManagedDefaultCookiesSetting[] =
diff --git a/components/content_settings/core/common/pref_names.h b/components/content_settings/core/common/pref_names.h
index 77c6f53..01620a3 100644
--- a/components/content_settings/core/common/pref_names.h
+++ b/components/content_settings/core/common/pref_names.h
@@ -43,6 +43,8 @@
 #endif
 extern const char kDefaultAppBannerSetting[];
 
+extern const char kMigratedDefaultMediaStreamSetting[];
+
 extern const char kManagedDefaultCookiesSetting[];
 extern const char kManagedDefaultImagesSetting[];
 extern const char kManagedDefaultJavaScriptSetting[];
diff --git a/components/crash/browser/crash_dump_manager_android.cc b/components/crash/browser/crash_dump_manager_android.cc
index b7d2378..4814afdc 100644
--- a/components/crash/browser/crash_dump_manager_android.cc
+++ b/components/crash/browser/crash_dump_manager_android.cc
@@ -35,7 +35,7 @@
 
 CrashDumpManager::CrashDumpManager(const base::FilePath& crash_dump_dir)
     : crash_dump_dir_(crash_dump_dir) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!instance_);
 
   instance_ = this;
@@ -83,7 +83,7 @@
 // static
 void CrashDumpManager::ProcessMinidump(const base::FilePath& minidump_path,
                                        base::ProcessHandle pid) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   CHECK(instance_);
   int64 file_size = 0;
   int r = base::GetFileSize(minidump_path, &file_size);
diff --git a/components/crash/browser/crash_handler_host_linux.cc b/components/crash/browser/crash_handler_host_linux.cc
index 7811a048..2c5841ba 100644
--- a/components/crash/browser/crash_handler_host_linux.cc
+++ b/components/crash/browser/crash_handler_host_linux.cc
@@ -402,7 +402,7 @@
 
 void CrashHandlerHostLinux::QueueCrashDumpTask(scoped_ptr<BreakpadInfo> info,
                                                int signal_fd) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   // Send the done signal to the process: it can exit now.
   struct msghdr msg = {0};
diff --git a/components/cronet.gypi b/components/cronet.gypi
index 24409ed..3d94b44 100644
--- a/components/cronet.gypi
+++ b/components/cronet.gypi
@@ -288,7 +288,7 @@
             '../base/base.gyp:base',
             '../net/net.gyp:net',
             '../net/net.gyp:net_test_support',
-            '../net/net.gyp:quic_tools',
+            '../net/net.gyp:simple_quic_tools',
             '../url/url.gyp:url_lib',
             '../base/base.gyp:base_i18n',
             '../third_party/icu/icu.gyp:icui18n',
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
index e03568c..a2d6446 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
+++ b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
@@ -15,6 +15,7 @@
 import org.chromium.base.CalledByNative;
 import org.chromium.base.JNINamespace;
 import org.chromium.base.NativeClassQualifiedName;
+import org.chromium.base.UsedByReflection;
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -23,6 +24,7 @@
  * UrlRequest context using Chromium HTTP stack implementation.
  */
 @JNINamespace("cronet")
+@UsedByReflection("UrlRequestContext.java")
 public class CronetUrlRequestContext extends UrlRequestContext  {
     private static final int LOG_NONE = 3;  // LOG(FATAL), no VLOG.
     private static final int LOG_DEBUG = -1;  // LOG(FATAL...INFO), VLOG(1)
@@ -39,6 +41,7 @@
     private long mUrlRequestContextAdapter = 0;
     private Thread mNetworkThread;
 
+    @UsedByReflection("UrlRequestContext.java")
     public CronetUrlRequestContext(Context context,
                                    UrlRequestContextConfig config) {
         CronetLibraryLoader.ensureInitialized(context, config);
diff --git a/components/data_reduction_proxy.gypi b/components/data_reduction_proxy.gypi
index e387910..f8b37a5c 100644
--- a/components/data_reduction_proxy.gypi
+++ b/components/data_reduction_proxy.gypi
@@ -83,6 +83,7 @@
         '../net/net.gyp:net',
         '../url/url.gyp:url_lib',
         'data_reduction_proxy_core_common',
+        'data_reduction_proxy_proto',
         'pref_registry',
       ],
       'include_dirs': [
@@ -95,6 +96,8 @@
         'data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h',
         'data_reduction_proxy/core/browser/data_reduction_proxy_config.cc',
         'data_reduction_proxy/core/browser/data_reduction_proxy_config.h',
+        'data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc',
+        'data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h',
         'data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc',
         'data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h',
         'data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc',
@@ -105,6 +108,8 @@
         'data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h',
         'data_reduction_proxy/core/browser/data_reduction_proxy_metrics.cc',
         'data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h',
+        'data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc',
+        'data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h',
         'data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc',
         'data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h',
         'data_reduction_proxy/core/browser/data_reduction_proxy_prefs.cc',
@@ -130,6 +135,7 @@
       'dependencies': [
         '../base/base.gyp:base',
         '../url/url.gyp:url_lib',
+        'data_reduction_proxy_proto',
       ],
       'include_dirs': [
         '..',
@@ -137,6 +143,8 @@
       'sources': [
         # Note: sources list duplicated in GN build.
         'data_reduction_proxy/core/common/data_reduction_proxy_bypass_type_list.h',
+        'data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.cc',
+        'data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h',
         'data_reduction_proxy/core/common/data_reduction_proxy_config_values.h',
         'data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc',
         'data_reduction_proxy/core/common/data_reduction_proxy_event_store.h',
@@ -186,6 +194,24 @@
       'msvs_disabled_warnings': [4267, ],
     },
     {
+      # GN version: //components/data_reduction_proxy/proto
+      'target_name': 'data_reduction_proxy_proto',
+      'type': 'static_library',
+      'dependencies': [
+      ],
+      'include_dirs': [
+      ],
+      'sources': [
+        # Note: sources list duplicated in GN build.
+        'data_reduction_proxy/proto/client_config.proto',
+      ],
+      'variables': {
+        'proto_in_dir': 'data_reduction_proxy/proto',
+        'proto_out_dir': 'components/data_reduction_proxy/proto',
+      },
+      'includes': [ '../build/protoc.gypi' ],
+    },
+    {
       'target_name': 'data_reduction_proxy_version_header',
       'type': 'none',
       'direct_dependent_settings': {
diff --git a/components/data_reduction_proxy/OWNERS b/components/data_reduction_proxy/OWNERS
index 33ac864a..78f98aaa 100644
--- a/components/data_reduction_proxy/OWNERS
+++ b/components/data_reduction_proxy/OWNERS
@@ -2,6 +2,7 @@
 bolian@chromium.org
 marq@chromium.org
 sclittle@chromium.org
+jeremyim@chromium.org
 
 # Changes to IPC messages require a security review to avoid introducing
 # new sandbox escapes.
diff --git a/components/data_reduction_proxy/core/browser/BUILD.gn b/components/data_reduction_proxy/core/browser/BUILD.gn
index 0bc2867..3e660fd 100644
--- a/components/data_reduction_proxy/core/browser/BUILD.gn
+++ b/components/data_reduction_proxy/core/browser/BUILD.gn
@@ -8,6 +8,8 @@
     "data_reduction_proxy_bypass_protocol.h",
     "data_reduction_proxy_config.cc",
     "data_reduction_proxy_config.h",
+    "data_reduction_proxy_config_service_client.cc",
+    "data_reduction_proxy_config_service_client.h",
     "data_reduction_proxy_configurator.cc",
     "data_reduction_proxy_configurator.h",
     "data_reduction_proxy_debug_ui_service.h",
@@ -19,6 +21,8 @@
     "data_reduction_proxy_io_data.h",
     "data_reduction_proxy_metrics.cc",
     "data_reduction_proxy_metrics.h",
+    "data_reduction_proxy_mutable_config_values.cc",
+    "data_reduction_proxy_mutable_config_values.h",
     "data_reduction_proxy_network_delegate.cc",
     "data_reduction_proxy_network_delegate.h",
     "data_reduction_proxy_prefs.cc",
@@ -41,6 +45,7 @@
     "//base",
     "//base:prefs",
     "//components/data_reduction_proxy/core/common",
+    "//components/data_reduction_proxy/proto:data_reduction_proxy_proto",
     "//components/pref_registry",
     "//crypto",
     "//net",
@@ -85,6 +90,7 @@
   testonly = true
   sources = [
     "data_reduction_proxy_bypass_protocol_unittest.cc",
+    "data_reduction_proxy_config_service_client_unittest.cc",
     "data_reduction_proxy_config_unittest.cc",
     "data_reduction_proxy_configurator_unittest.cc",
     "data_reduction_proxy_interceptor_unittest.cc",
@@ -105,6 +111,7 @@
     "//base:prefs_test_support",
     "//base/test:test_support",
     "//components/data_reduction_proxy/core/common:test_support",
+    "//components/data_reduction_proxy/proto:data_reduction_proxy_proto",
     "//net:test_support",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
index bb7ab767..932af6f2 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -90,6 +90,12 @@
                             alternative_enabled, at_startup));
 }
 
+void DataReductionProxyConfig::ReloadConfig() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  UpdateConfigurator(enabled_by_user_, alternative_enabled_by_user_,
+                     restricted_by_carrier_, false /* at_startup */);
+}
+
 bool DataReductionProxyConfig::WasDataReductionProxyUsed(
     const net::URLRequest* request,
     DataReductionProxyTypeInfo* proxy_info) const {
@@ -276,18 +282,31 @@
   LogProxyState(enabled, restricted, at_startup);
   // The alternative is only configured if the standard configuration is
   // is enabled.
-  if (enabled & !config_values_->holdback()) {
+  std::string origin;
+  std::string fallback_origin;
+  std::string ssl_origin;
+  bool fallback_allowed = false;
+  if (enabled && !disabled_on_vpn_ && !config_values_->holdback()) {
     if (alternative_enabled) {
-      configurator_->Enable(restricted,
-                            !config_values_->alternative_fallback_allowed(),
-                            config_values_->alt_origin().ToURI(), std::string(),
-                            config_values_->ssl_origin().ToURI());
+      fallback_allowed = config_values_->alternative_fallback_allowed();
+      if (config_values_->alt_origin().is_valid())
+        origin = config_values_->alt_origin().ToURI();
+      if (config_values_->ssl_origin().is_valid())
+        ssl_origin = config_values_->ssl_origin().ToURI();
     } else {
-      configurator_->Enable(restricted, !config_values_->fallback_allowed(),
-                            config_values_->origin().ToURI(),
-                            config_values_->fallback_origin().ToURI(),
-                            std::string());
+      fallback_allowed = config_values_->fallback_allowed();
+      if (config_values_->origin().is_valid())
+        origin = config_values_->origin().ToURI();
+      if (config_values_->fallback_origin().is_valid())
+        fallback_origin = config_values_->fallback_origin().ToURI();
     }
+  }
+
+  // TODO(jeremyim): Enable should take std::vector<net::ProxyServer> as its
+  // parameters.
+  if (!origin.empty() || !fallback_origin.empty() || !ssl_origin.empty()) {
+    configurator_->Enable(restricted, !fallback_allowed, origin,
+                          fallback_origin, ssl_origin);
   } else {
     configurator_->Disable();
   }
@@ -475,17 +494,18 @@
             interface_name.begin(),
             interface_name.begin() + vpn_interface_name_prefix.size(),
             vpn_interface_name_prefix.c_str())) {
-      UpdateConfigurator(false, alternative_enabled_by_user_, false, false);
       disabled_on_vpn_ = true;
+      UpdateConfigurator(enabled_by_user_, alternative_enabled_by_user_,
+                         restricted_by_carrier_, false);
       RecordNetworkChangeEvent(DISABLED_ON_VPN);
       return true;
     }
   }
   if (disabled_on_vpn_) {
+    disabled_on_vpn_ = false;
     UpdateConfigurator(enabled_by_user_, alternative_enabled_by_user_,
                        restricted_by_carrier_, false);
   }
-  disabled_on_vpn_ = false;
   return false;
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
index 65733380..69098b7 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
@@ -90,6 +90,11 @@
                              bool alternative_enabled,
                              bool at_startup);
 
+  // Provides a mechanism for an external object to force |this| to refresh
+  // the Data Reduction Proxy configuration from |config_values_| and apply to
+  // |configurator_|. Used by the Data Reduction Proxy config service client.
+  void ReloadConfig();
+
   // Returns true if a Data Reduction Proxy was used for the given |request|.
   // If true, |proxy_info.proxy_servers.first| will contain the name of the
   // proxy that was used. |proxy_info.proxy_servers.second| will contain the
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
new file mode 100644
index 0000000..59081c3
--- /dev/null
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
@@ -0,0 +1,181 @@
+// 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 "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/json/json_writer.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
+#include "net/base/host_port_pair.h"
+#include "net/proxy/proxy_server.h"
+
+namespace data_reduction_proxy {
+
+namespace {
+
+// This is the default backoff policy used to communicate with the Data
+// Reduction Proxy configuration service.
+const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
+    0,               // num_errors_to_ignore
+    1 * 1000,        // initial_delay_ms
+    4,               // multiply_factor
+    0.10,            // jitter_factor,
+    30 * 60 * 1000,  // maximum_backoff_ms
+    -1,              // entry_lifetime_ms
+    true,            // always_use_initial_delay
+};
+
+// Extracts the list of Data Reduction Proxy servers to use for HTTP requests.
+std::vector<net::ProxyServer> GetProxiesForHTTP(
+    const data_reduction_proxy::ProxyConfig& proxy_config) {
+  std::vector<net::ProxyServer> proxies;
+  for (const auto& server : proxy_config.http_proxy_servers()) {
+    if (server.scheme() != ProxyServer_ProxyScheme_UNSPECIFIED) {
+      proxies.push_back(net::ProxyServer(
+          config_parser::SchemeFromProxyScheme(server.scheme()),
+          net::HostPortPair(server.host(), server.port())));
+    }
+  }
+
+  return proxies;
+}
+
+// Calculate the next time at which the Data Reduction Proxy configuration
+// should be retrieved, based on response success, configuration expiration,
+// and the backoff delay. |backoff_delay| must be non-negative. Note that it is
+// possible for |config_expiration| to be prior to |now|, but on a successful
+// config refresh, |backoff_delay| will be returned.
+base::TimeDelta CalculateNextConfigRefreshTime(
+    bool fetch_succeeded,
+    const base::Time& config_expiration,
+    const base::Time& now,
+    const base::TimeDelta& backoff_delay) {
+  DCHECK(backoff_delay >= base::TimeDelta());
+  if (fetch_succeeded) {
+    base::TimeDelta success_delay = config_expiration - now;
+    if (success_delay > backoff_delay)
+      return success_delay;
+  }
+
+  return backoff_delay;
+}
+
+}  // namespace
+
+const net::BackoffEntry::Policy& GetBackoffPolicy() {
+  return kDefaultBackoffPolicy;
+}
+
+DataReductionProxyConfigServiceClient::DataReductionProxyConfigServiceClient(
+    scoped_ptr<DataReductionProxyParams> params,
+    const net::BackoffEntry::Policy& backoff_policy,
+    DataReductionProxyRequestOptions* request_options,
+    DataReductionProxyMutableConfigValues* config_values,
+    DataReductionProxyConfig* config,
+    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+    : params_(params.Pass()),
+      request_options_(request_options),
+      config_values_(config_values),
+      config_(config),
+      io_task_runner_(io_task_runner),
+      backoff_entry_(&backoff_policy) {
+  DCHECK(request_options);
+  DCHECK(config_values);
+  DCHECK(config);
+  DCHECK(io_task_runner.get());
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&DataReductionProxyConfigServiceClient::RetrieveConfig,
+                 base::Unretained(this)));
+}
+
+DataReductionProxyConfigServiceClient::
+    ~DataReductionProxyConfigServiceClient() {
+}
+
+void DataReductionProxyConfigServiceClient::RetrieveConfig() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  std::string static_response = ConstructStaticResponse();
+  ClientConfig config;
+  bool succeeded = false;
+  if (config_parser::ParseClientConfig(static_response, &config)) {
+    if (config.has_proxy_config()) {
+      net::ProxyServer origin;
+      net::ProxyServer fallback_origin;
+      std::vector<net::ProxyServer> proxies =
+          GetProxiesForHTTP(config.proxy_config());
+      if (proxies.size() > 0) {
+        origin = proxies[0];
+        if (proxies.size() > 1)
+          fallback_origin = proxies[1];
+
+        std::string session;
+        std::string credentials;
+        if (DataReductionProxyRequestOptions::ParseLocalSessionKey(
+                config.session_key(), &session, &credentials)) {
+          request_options_->SetCredentials(session, credentials);
+          config_values_->UpdateValues(origin, fallback_origin);
+          config_->ReloadConfig();
+          succeeded = true;
+        }
+      }
+    }
+  }
+
+  base::Time expiration_time;
+  if (succeeded) {
+    expiration_time = config_parser::TimestampToTime(config.expire_time());
+  }
+
+  GetBackoffEntry()->InformOfRequest(succeeded);
+  base::TimeDelta next_config_refresh_time =
+      CalculateNextConfigRefreshTime(succeeded, expiration_time, Now(),
+                                     GetBackoffEntry()->GetTimeUntilRelease());
+  SetConfigRefreshTimer(next_config_refresh_time);
+}
+
+net::BackoffEntry* DataReductionProxyConfigServiceClient::GetBackoffEntry() {
+  return &backoff_entry_;
+}
+
+void DataReductionProxyConfigServiceClient::SetConfigRefreshTimer(
+    const base::TimeDelta& delay) {
+  DCHECK(delay >= base::TimeDelta());
+  config_refresh_timer_.Stop();
+  config_refresh_timer_.Start(
+      FROM_HERE, delay, this,
+      &DataReductionProxyConfigServiceClient::RetrieveConfig);
+}
+
+base::Time DataReductionProxyConfigServiceClient::Now() const {
+  return base::Time::Now();
+}
+
+std::string
+DataReductionProxyConfigServiceClient::ConstructStaticResponse() const {
+  std::string response;
+  scoped_ptr<base::DictionaryValue> values(new base::DictionaryValue());
+  params_->PopulateConfigResponse(values.get());
+  request_options_->PopulateConfigResponse(values.get());
+  base::JSONWriter::Write(values.get(), &response);
+
+  return response;
+}
+
+}  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
new file mode 100644
index 0000000..3f6ae23
--- /dev/null
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
@@ -0,0 +1,105 @@
+// 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 COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_CONFIG_SERVICE_CLIENT_H_
+#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_CONFIG_SERVICE_CLIENT_H_
+
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/timer/timer.h"
+#include "net/base/backoff_entry.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+class Time;
+class TimeDelta;
+}
+
+namespace data_reduction_proxy {
+
+class DataReductionProxyConfig;
+class DataReductionProxyMutableConfigValues;
+class DataReductionProxyParams;
+class DataReductionProxyRequestOptions;
+
+// Retrieves the default net::BackoffEntry::Policy for the Data Reduction Proxy
+// configuration service client.
+const net::BackoffEntry::Policy& GetBackoffPolicy();
+
+// Retrieves the Data Reduction Proxy configuration from a remote service. This
+// object lives on the IO thread.
+class DataReductionProxyConfigServiceClient {
+ public:
+  // The caller must ensure that all parameters remain alive for the lifetime of
+  // the |DataReductionProxyConfigClient|, with the exception of |params|
+  // which this instance will own.
+  DataReductionProxyConfigServiceClient(
+      scoped_ptr<DataReductionProxyParams> params,
+      const net::BackoffEntry::Policy& backoff_policy,
+      DataReductionProxyRequestOptions* request_options,
+      DataReductionProxyMutableConfigValues* config_values,
+      DataReductionProxyConfig* config,
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+
+  virtual ~DataReductionProxyConfigServiceClient();
+
+  // Request the retrieval of the Data Reduction Proxy configuration.
+  void RetrieveConfig();
+
+ protected:
+  // Retrieves the backoff entry object being used to throttle request failures.
+  // Virtual for testing.
+  virtual net::BackoffEntry* GetBackoffEntry();
+
+  // Sets a timer to determine when to next refresh the Data Reduction Proxy
+  // configuration.
+  // Virtual for testing.
+  virtual void SetConfigRefreshTimer(const base::TimeDelta& delay);
+
+  // Returns the current time.
+  // Virtual for testing.
+  virtual base::Time Now() const;
+
+  // Constructs a synthetic response based on |params_|.
+  // Virtual for testing.
+  virtual std::string ConstructStaticResponse() const;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigServiceClientTest,
+                           TestConstructStaticResponse);
+  friend class TestDataReductionProxyConfigServiceClient;
+
+  // Contains the static configuration data to use.
+  scoped_ptr<DataReductionProxyParams> params_;
+
+  // The caller must ensure that the |request_options_| outlives this instance.
+  DataReductionProxyRequestOptions* request_options_;
+
+  // The caller must ensure that the |config_values_| outlives this instance.
+  DataReductionProxyMutableConfigValues* config_values_;
+
+  // The caller must ensure that the |config_| outlives this instance.
+  DataReductionProxyConfig* config_;
+
+  // |io_task_runner_| should be the task runner for running operations on the
+  // IO thread.
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+  // Used to calculate the backoff time on request failures.
+  net::BackoffEntry backoff_entry_;
+
+  // An event that fires when it is time to refresh the Data Reduction Proxy
+  // configuration.
+  base::OneShotTimer<DataReductionProxyConfigServiceClient>
+      config_refresh_timer_;
+
+  DISALLOW_COPY_AND_ASSIGN(DataReductionProxyConfigServiceClient);
+};
+
+}  // namespace data_reduction_proxy
+#endif  // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_CONFIG_SERVICE_CLIENT_H_
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
new file mode 100644
index 0000000..f8e7c0f3
--- /dev/null
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
@@ -0,0 +1,212 @@
+// 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 "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h"
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/time/tick_clock.h"
+#include "base/values.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace data_reduction_proxy {
+
+namespace {
+
+class RequestOptionsPopulator {
+ public:
+  RequestOptionsPopulator(const base::Time& expiration_time,
+                          const base::TimeDelta& increment)
+      : expiration_time_(expiration_time),
+        increment_(increment) {
+  }
+
+  void PopulateResponse(base::DictionaryValue* response) {
+    response->SetString("sessionKey", "abcdef|1234-5678-12345678");
+    response->SetString("expireTime",
+                        config_parser::TimeToISO8601(expiration_time_));
+    expiration_time_ += increment_;
+  }
+
+ private:
+  base::Time expiration_time_;
+  base::TimeDelta increment_;
+};
+
+void PopulateResponseFailure(base::DictionaryValue* response) {
+}
+
+}  // namespace
+
+class DataReductionProxyConfigServiceClientTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    test_context_ =
+        DataReductionProxyTestContext::Builder()
+            .WithParamsFlags(DataReductionProxyParams::kAllowed |
+                             DataReductionProxyParams::kFallbackAllowed |
+                             DataReductionProxyParams::kPromoAllowed)
+            .WithParamsDefinitions(TestDataReductionProxyParams::HAS_EVERYTHING)
+            .WithTestConfigurator()
+            .WithMockRequestOptions()
+            .WithTestConfigClient()
+            .Build();
+    test_context_->test_config_client()->SetCustomReleaseTime(
+        base::TimeTicks::UnixEpoch());
+    test_context_->test_config_client()->SetNow(base::Time::UnixEpoch());
+  }
+
+  void SetDataReductionProxyEnabled(bool enabled) {
+    test_context_->config()->SetStateForTest(enabled, false, false);
+  }
+
+  scoped_ptr<DataReductionProxyConfigServiceClient> BuildConfigClient() {
+    scoped_ptr<DataReductionProxyParams> params(new DataReductionProxyParams(
+        DataReductionProxyParams::kAllowed |
+        DataReductionProxyParams::kFallbackAllowed |
+        DataReductionProxyParams::kPromoAllowed));
+    request_options_.reset(
+        new DataReductionProxyRequestOptions(Client::UNKNOWN,
+                                             test_context_->io_data()->config(),
+                                             test_context_->task_runner()));
+    return scoped_ptr<DataReductionProxyConfigServiceClient>(
+        new DataReductionProxyConfigServiceClient(
+            params.Pass(), GetBackoffPolicy(),
+            request_options_.get(),
+            test_context_->mutable_config_values(),
+            test_context_->io_data()->config(), test_context_->task_runner()));
+  }
+
+  DataReductionProxyParams* params() {
+    return test_context_->test_params();
+  }
+
+  TestDataReductionProxyConfigServiceClient* config_client() {
+    return test_context_->test_config_client();
+  }
+
+  TestDataReductionProxyConfigurator* configurator() {
+    return test_context_->test_configurator();
+  }
+
+  MockDataReductionProxyRequestOptions* request_options() {
+    return test_context_->mock_request_options();
+  }
+
+  void RunUntilIdle() {
+    test_context_->RunUntilIdle();
+  }
+
+ private:
+  scoped_ptr<DataReductionProxyTestContext> test_context_;
+  scoped_ptr<DataReductionProxyRequestOptions> request_options_;
+};
+
+TEST_F(DataReductionProxyConfigServiceClientTest, TestConstructStaticResponse) {
+  scoped_ptr<DataReductionProxyConfigServiceClient> config_client =
+      BuildConfigClient();
+  std::string config_data = config_client->ConstructStaticResponse();
+  ClientConfig config;
+  EXPECT_TRUE(config_parser::ParseClientConfig(config_data, &config));
+}
+
+TEST_F(DataReductionProxyConfigServiceClientTest, SuccessfulLoop) {
+  RequestOptionsPopulator populator(
+      base::Time::UnixEpoch() + base::TimeDelta::FromDays(1),
+      base::TimeDelta::FromDays(1));
+  SetDataReductionProxyEnabled(true);
+  EXPECT_TRUE(configurator()->origin().empty());
+  EXPECT_TRUE(configurator()->fallback_origin().empty());
+  EXPECT_TRUE(configurator()->ssl_origin().empty());
+  EXPECT_CALL(*request_options(), PopulateConfigResponse(testing::_))
+      .Times(2)
+      .WillRepeatedly(
+          testing::Invoke(&populator,
+                          &RequestOptionsPopulator::PopulateResponse));
+  RunUntilIdle();
+  EXPECT_EQ(base::TimeDelta::FromDays(1), config_client()->GetDelay());
+  EXPECT_EQ(params()->origin().ToURI(), configurator()->origin());
+  EXPECT_EQ(params()->fallback_origin().ToURI(),
+            configurator()->fallback_origin());
+  EXPECT_TRUE(configurator()->ssl_origin().empty());
+  config_client()->SetNow(base::Time::UnixEpoch() + base::TimeDelta::FromDays(1)
+                          + base::TimeDelta::FromSeconds(5));
+  config_client()->RetrieveConfig();
+  EXPECT_EQ(base::TimeDelta::FromDays(1) - base::TimeDelta::FromSeconds(5),
+            config_client()->GetDelay());
+  EXPECT_EQ(params()->origin().ToURI(), configurator()->origin());
+  EXPECT_EQ(params()->fallback_origin().ToURI(),
+            configurator()->fallback_origin());
+  EXPECT_TRUE(configurator()->ssl_origin().empty());
+}
+
+TEST_F(DataReductionProxyConfigServiceClientTest, SuccessfulLoopShortDuration) {
+  RequestOptionsPopulator populator(
+      base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(1),
+      base::TimeDelta::FromSeconds(1));
+  SetDataReductionProxyEnabled(true);
+  EXPECT_TRUE(configurator()->origin().empty());
+  EXPECT_TRUE(configurator()->fallback_origin().empty());
+  EXPECT_TRUE(configurator()->ssl_origin().empty());
+  EXPECT_CALL(*request_options(), PopulateConfigResponse(testing::_))
+      .Times(1)
+      .WillOnce(testing::Invoke(&populator,
+                                &RequestOptionsPopulator::PopulateResponse));
+  RunUntilIdle();
+  EXPECT_EQ(base::TimeDelta::FromSeconds(10), config_client()->GetDelay());
+  EXPECT_EQ(params()->origin().ToURI(), configurator()->origin());
+  EXPECT_EQ(params()->fallback_origin().ToURI(),
+            configurator()->fallback_origin());
+  EXPECT_TRUE(configurator()->ssl_origin().empty());
+}
+
+TEST_F(DataReductionProxyConfigServiceClientTest, EnsureBackoff) {
+  SetDataReductionProxyEnabled(true);
+  EXPECT_TRUE(configurator()->origin().empty());
+  EXPECT_TRUE(configurator()->fallback_origin().empty());
+  EXPECT_TRUE(configurator()->ssl_origin().empty());
+  EXPECT_CALL(*request_options(), PopulateConfigResponse(testing::_))
+      .Times(2)
+      .WillRepeatedly(testing::Invoke(&PopulateResponseFailure));
+  config_client()->RetrieveConfig();
+  EXPECT_TRUE(configurator()->origin().empty());
+  EXPECT_TRUE(configurator()->fallback_origin().empty());
+  EXPECT_TRUE(configurator()->ssl_origin().empty());
+  EXPECT_EQ(base::TimeDelta::FromSeconds(20), config_client()->GetDelay());
+  config_client()->RetrieveConfig();
+  EXPECT_TRUE(configurator()->origin().empty());
+  EXPECT_TRUE(configurator()->fallback_origin().empty());
+  EXPECT_TRUE(configurator()->ssl_origin().empty());
+  EXPECT_EQ(base::TimeDelta::FromSeconds(40), config_client()->GetDelay());
+}
+
+TEST_F(DataReductionProxyConfigServiceClientTest, ConfigDisabled) {
+  RequestOptionsPopulator populator(
+      base::Time::UnixEpoch() + base::TimeDelta::FromDays(1),
+      base::TimeDelta::FromDays(1));
+  SetDataReductionProxyEnabled(false);
+  EXPECT_TRUE(configurator()->origin().empty());
+  EXPECT_TRUE(configurator()->fallback_origin().empty());
+  EXPECT_TRUE(configurator()->ssl_origin().empty());
+  EXPECT_CALL(*request_options(), PopulateConfigResponse(testing::_))
+      .Times(1)
+      .WillOnce(testing::Invoke(&populator,
+                                &RequestOptionsPopulator::PopulateResponse));
+  RunUntilIdle();
+  EXPECT_TRUE(configurator()->origin().empty());
+  EXPECT_TRUE(configurator()->fallback_origin().empty());
+  EXPECT_TRUE(configurator()->ssl_origin().empty());
+  EXPECT_EQ(base::TimeDelta::FromDays(1), config_client()->GetDelay());
+}
+
+}  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
index 03676c96..f6e836c 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
@@ -4,6 +4,7 @@
 
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
 
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
 #include "net/base/net_util.h"
 #include "net/url_request/test_url_fetcher_factory.h"
@@ -21,12 +22,28 @@
     net::NetLog* net_log,
     DataReductionProxyConfigurator* configurator,
     DataReductionProxyEventStore* event_store)
-    : DataReductionProxyConfig(
-        task_runner, task_runner, net_log,
-        make_scoped_ptr(
-            new TestDataReductionProxyParams(params_flags, params_definitions))
-            .Pass(),
-        configurator, event_store) {
+    : TestDataReductionProxyConfig(
+          make_scoped_ptr(
+              new TestDataReductionProxyParams(params_flags,
+                                               params_definitions)).Pass(),
+          task_runner,
+          net_log,
+          configurator,
+          event_store) {
+}
+
+TestDataReductionProxyConfig::TestDataReductionProxyConfig(
+    scoped_ptr<DataReductionProxyConfigValues> config_values,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    net::NetLog* net_log,
+    DataReductionProxyConfigurator* configurator,
+    DataReductionProxyEventStore* event_store)
+    : DataReductionProxyConfig(task_runner,
+                               task_runner,
+                               net_log,
+                               config_values.Pass(),
+                               configurator,
+                               event_store) {
   network_interfaces_.reset(new net::NetworkInterfaceList());
 }
 
@@ -58,28 +75,30 @@
   return static_cast<TestDataReductionProxyParams*>(config_values_.get());
 }
 
+DataReductionProxyConfigValues* TestDataReductionProxyConfig::config_values() {
+  return config_values_.get();
+}
+
 void TestDataReductionProxyConfig::SetStateForTest(
     bool enabled_by_user,
     bool alternative_enabled_by_user,
-    bool restricted_by_carrier,
-    bool at_startup) {
+    bool restricted_by_carrier) {
   enabled_by_user_ = enabled_by_user;
   alternative_enabled_by_user_ = alternative_enabled_by_user;
   restricted_by_carrier_ = restricted_by_carrier;
-  UpdateConfigurator(enabled_by_user_, alternative_enabled_by_user_,
-                     restricted_by_carrier_, at_startup);
 }
 
 MockDataReductionProxyConfig::MockDataReductionProxyConfig(
-    int params_flags,
-    unsigned int params_definitions,
+    scoped_ptr<DataReductionProxyConfigValues> config_values,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     net::NetLog* net_log,
     DataReductionProxyConfigurator* configurator,
     DataReductionProxyEventStore* event_store)
-    : TestDataReductionProxyConfig(
-        params_flags, params_definitions, task_runner, net_log, configurator,
-        event_store) {
+    : TestDataReductionProxyConfig(config_values.Pass(),
+                                   task_runner,
+                                   net_log,
+                                   configurator,
+                                   event_store) {
 }
 
 MockDataReductionProxyConfig::~MockDataReductionProxyConfig() {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
index 79c5548..fba88ee 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
@@ -23,6 +23,7 @@
 
 class DataReductionProxyConfigurator;
 class DataReductionProxyEventStore;
+class DataReductionProxyMutableConfigValues;
 class TestDataReductionProxyParams;
 
 // Test version of |DataReductionProxyConfig|, which uses an underlying
@@ -39,6 +40,17 @@
       net::NetLog* net_log,
       DataReductionProxyConfigurator* configurator,
       DataReductionProxyEventStore* event_store);
+
+  // Creates a |TestDataReductionProxyConfig| with the provided |config_values|.
+  // This permits any DataReductionProxyConfigValues to be used (such as
+  // DataReductionProxyParams or DataReductionProxyMutableConfigValues).
+  TestDataReductionProxyConfig(
+      scoped_ptr<DataReductionProxyConfigValues> config_values,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      net::NetLog* net_log,
+      DataReductionProxyConfigurator* configurator,
+      DataReductionProxyEventStore* event_store);
+
   ~TestDataReductionProxyConfig() override;
 
   void GetNetworkList(net::NetworkInterfaceList* interfaces,
@@ -53,11 +65,14 @@
   // Retrieves the test params being used for the configuration.
   TestDataReductionProxyParams* test_params();
 
+  // Retrieves the underlying config values.
+  // TODO(jeremyim): Rationalize with test_params().
+  DataReductionProxyConfigValues* config_values();
+
   // Allows tests to set the internal state.
   void SetStateForTest(bool enabled_by_user,
                        bool alternative_enabled_by_user,
-                       bool restricted_by_carrier,
-                       bool at_startup);
+                       bool restricted_by_carrier);
 
   net::NetworkInterfaceList* interfaces() {
     return network_interfaces_.get();
@@ -71,10 +86,9 @@
 // testing.
 class MockDataReductionProxyConfig : public TestDataReductionProxyConfig {
  public:
-  // Creates a |MockDataReductionProxyConfig| with the provided |params_flags|.
+  // Creates a |MockDataReductionProxyConfig|.
   MockDataReductionProxyConfig(
-      int params_flags,
-      unsigned int params_definitions,
+      scoped_ptr<DataReductionProxyConfigValues> config_values,
       scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
       net::NetLog* net_log,
       DataReductionProxyConfigurator* configurator,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
index 57b79f7..b957ab5 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -10,9 +10,11 @@
 #include "base/single_thread_task_runner.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
@@ -46,12 +48,32 @@
   event_store_.reset(new DataReductionProxyEventStore(ui_task_runner));
   configurator_.reset(new DataReductionProxyConfigurator(
       io_task_runner, net_log, event_store_.get()));
-  config_.reset(new DataReductionProxyConfig(
-      io_task_runner_, ui_task_runner_, net_log, params.Pass(),
-      configurator_.get(), event_store_.get()));
+  bool use_config_client = DataReductionProxyParams::IsConfigClientEnabled();
+  DataReductionProxyMutableConfigValues* raw_mutable_config = nullptr;
+  if (use_config_client) {
+    scoped_ptr<DataReductionProxyMutableConfigValues> mutable_config =
+        DataReductionProxyMutableConfigValues::CreateFromParams(io_task_runner_,
+                                                                params.get());
+    raw_mutable_config = mutable_config.get();
+    config_.reset(new DataReductionProxyConfig(
+        io_task_runner_, ui_task_runner_, net_log, mutable_config.Pass(),
+        configurator_.get(), event_store_.get()));
+  } else {
+    config_.reset(new DataReductionProxyConfig(
+        io_task_runner_, ui_task_runner_, net_log, params.Pass(),
+        configurator_.get(), event_store_.get()));
+  }
+
   request_options_.reset(new DataReductionProxyRequestOptions(
       client_, config_.get(), io_task_runner_));
   request_options_->Init();
+
+  if (use_config_client) {
+    config_client_.reset(new DataReductionProxyConfigServiceClient(
+        params.Pass(), GetBackoffPolicy(), request_options_.get(),
+        raw_mutable_config, config_.get(), io_task_runner_));
+  }
+
   proxy_delegate_.reset(
       new DataReductionProxyDelegate(request_options_.get(), config_.get()));
  }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
index 0403c0a7..ff0967b 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
@@ -21,6 +21,7 @@
 namespace data_reduction_proxy {
 
 class DataReductionProxyConfig;
+class DataReductionProxyConfigServiceClient;
 class DataReductionProxyConfigurator;
 class DataReductionProxyEventStore;
 class DataReductionProxyService;
@@ -150,6 +151,9 @@
   // Constructs credentials suitable for authenticating the client.
   scoped_ptr<DataReductionProxyRequestOptions> request_options_;
 
+  // Requests new Data Reduction Proxy configurations from a remote service.
+  scoped_ptr<DataReductionProxyConfigServiceClient> config_client_;
+
   // A net log.
   net::NetLog* net_log_;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
new file mode 100644
index 0000000..cebbdaca
--- /dev/null
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
@@ -0,0 +1,140 @@
+// 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 "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
+
+#include "base/single_thread_task_runner.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+
+namespace data_reduction_proxy {
+
+scoped_ptr<DataReductionProxyMutableConfigValues>
+DataReductionProxyMutableConfigValues::CreateFromParams(
+    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+    const DataReductionProxyParams* params) {
+  scoped_ptr<DataReductionProxyMutableConfigValues> config_values(
+      new DataReductionProxyMutableConfigValues(io_task_runner));
+  config_values->promo_allowed_ = params->promo_allowed();
+  config_values->holdback_ = params->holdback();
+  config_values->allowed_ = params->allowed();
+  config_values->fallback_allowed_ = params->fallback_allowed();
+  config_values->secure_proxy_check_url_ = params->secure_proxy_check_url();
+  return config_values.Pass();
+}
+
+DataReductionProxyMutableConfigValues::DataReductionProxyMutableConfigValues(
+    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+    : empty_origin_(),
+      promo_allowed_(false),
+      holdback_(false),
+      allowed_(false),
+      fallback_allowed_(false),
+      origin_(empty_origin_),
+      fallback_origin_(empty_origin_),
+      io_task_runner_(io_task_runner) {
+  DCHECK(io_task_runner.get());
+}
+
+DataReductionProxyMutableConfigValues::
+    ~DataReductionProxyMutableConfigValues() {
+}
+
+bool DataReductionProxyMutableConfigValues::promo_allowed() const {
+  return promo_allowed_;
+}
+
+bool DataReductionProxyMutableConfigValues::holdback() const {
+  return holdback_;
+}
+
+bool DataReductionProxyMutableConfigValues::allowed() const {
+  return allowed_;
+}
+
+bool DataReductionProxyMutableConfigValues::fallback_allowed() const {
+  return fallback_allowed_;
+}
+
+bool DataReductionProxyMutableConfigValues::alternative_allowed() const {
+  return false;
+}
+
+bool DataReductionProxyMutableConfigValues::alternative_fallback_allowed()
+    const {
+  return false;
+}
+
+bool DataReductionProxyMutableConfigValues::UsingHTTPTunnel(
+    const net::HostPortPair& proxy_server) const {
+  return false;
+}
+
+bool DataReductionProxyMutableConfigValues::IsDataReductionProxy(
+    const net::HostPortPair& host_port_pair,
+    DataReductionProxyTypeInfo* proxy_info) const {
+  // TODO(jeremyim): Rework as part of ConfigValues interface changes.
+  if (allowed() && origin().is_valid() &&
+      origin().host_port_pair().Equals(host_port_pair)) {
+    if (proxy_info) {
+      proxy_info->proxy_servers.first = origin();
+      if (fallback_allowed())
+        proxy_info->proxy_servers.second = fallback_origin();
+    }
+    return true;
+  }
+
+  if (!fallback_allowed() || !fallback_origin().is_valid() ||
+      !fallback_origin().host_port_pair().Equals(host_port_pair))
+    return false;
+
+  if (proxy_info) {
+    proxy_info->proxy_servers.first = fallback_origin();
+    proxy_info->proxy_servers.second = net::ProxyServer::FromURI(
+        std::string(), net::ProxyServer::SCHEME_HTTP);
+    proxy_info->is_fallback = true;
+  }
+
+  return true;
+}
+
+const net::ProxyServer& DataReductionProxyMutableConfigValues::origin() const {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  return origin_;
+}
+
+const net::ProxyServer& DataReductionProxyMutableConfigValues::fallback_origin()
+    const {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  return fallback_origin_;
+}
+
+const net::ProxyServer& DataReductionProxyMutableConfigValues::alt_origin()
+    const {
+  return empty_origin_;
+}
+
+const net::ProxyServer&
+DataReductionProxyMutableConfigValues::alt_fallback_origin() const {
+  return empty_origin_;
+}
+
+const net::ProxyServer& DataReductionProxyMutableConfigValues::ssl_origin()
+    const {
+  return empty_origin_;
+}
+
+const GURL& DataReductionProxyMutableConfigValues::secure_proxy_check_url()
+    const {
+  return secure_proxy_check_url_;
+}
+
+void DataReductionProxyMutableConfigValues::UpdateValues(
+    const net::ProxyServer& origin,
+    const net::ProxyServer& fallback_origin) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  origin_ = origin;
+  fallback_origin_ = fallback_origin;
+}
+
+}  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
new file mode 100644
index 0000000..6897cb9
--- /dev/null
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
@@ -0,0 +1,81 @@
+// 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 COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_MUTABLE_CONFIG_VALUES_H_
+#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_MUTABLE_CONFIG_VALUES_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h"
+#include "net/proxy/proxy_server.h"
+#include "url/gurl.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace data_reduction_proxy {
+
+class DataReductionProxyParams;
+
+// A |DataReductionProxyConfigValues| which is permitted to change its
+// underlying values via the UpdateValues method.
+class DataReductionProxyMutableConfigValues
+    : public DataReductionProxyConfigValues {
+ public:
+  // Creates a new |DataReductionProxyMutableConfigValues| using |params| as
+  // the basis for its initial values.
+  static scoped_ptr<DataReductionProxyMutableConfigValues> CreateFromParams(
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+      const DataReductionProxyParams* params);
+
+  ~DataReductionProxyMutableConfigValues() override;
+
+  // Updates |origin_| and  |fallback_origin_| with the provided values.
+  // Virtual for testing.
+  virtual void UpdateValues(const net::ProxyServer& origin,
+                            const net::ProxyServer& fallback_origin);
+
+  // Overrides of |DataReductionProxyConfigValues|
+  bool promo_allowed() const override;
+  bool holdback() const override;
+  bool allowed() const override;
+  bool fallback_allowed() const override;
+  bool alternative_allowed() const override;
+  bool alternative_fallback_allowed() const override;
+  bool UsingHTTPTunnel(const net::HostPortPair& proxy_server) const override;
+  bool IsDataReductionProxy(
+      const net::HostPortPair& host_port_pair,
+      DataReductionProxyTypeInfo* proxy_info) const override;
+  const net::ProxyServer& origin() const override;
+  const net::ProxyServer& fallback_origin() const override;
+  const net::ProxyServer& alt_origin() const override;
+  const net::ProxyServer& alt_fallback_origin() const override;
+  const net::ProxyServer& ssl_origin() const override;
+  const GURL& secure_proxy_check_url() const override;
+
+ protected:
+  DataReductionProxyMutableConfigValues(
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+
+ private:
+  net::ProxyServer empty_origin_;
+  bool promo_allowed_;
+  bool holdback_;
+  bool allowed_;
+  bool fallback_allowed_;
+  net::ProxyServer origin_;
+  net::ProxyServer fallback_origin_;
+  GURL secure_proxy_check_url_;
+
+  // |io_task_runner_| should be the task runner for running operations on the
+  // IO thread.
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(DataReductionProxyMutableConfigValues);
+};
+
+}  // namespace data_reduction_proxy
+#endif  // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_MUTABLE_CONFIG_VALUES_H_
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
index 985dfa5..1b95890b 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
@@ -13,8 +13,10 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "base/values.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
@@ -69,11 +71,35 @@
       data_reduction_proxy::switches::kDataReductionProxyKey);
 }
 
+// static
+std::string DataReductionProxyRequestOptions::CreateLocalSessionKey(
+    const std::string& session,
+    const std::string& credentials) {
+  return base::StringPrintf("%s|%s", session.c_str(), credentials.c_str());
+}
+
+// static
+bool DataReductionProxyRequestOptions::ParseLocalSessionKey(
+    const std::string& session_key,
+    std::string* session,
+    std::string* credentials) {
+  std::vector<std::string> auth_values;
+  base::SplitString(session_key, '|', &auth_values);
+  if (auth_values.size() == 2) {
+    *session = auth_values[0];
+    *credentials = auth_values[1];
+    return true;
+  }
+
+  return false;
+}
+
 DataReductionProxyRequestOptions::DataReductionProxyRequestOptions(
     Client client,
     DataReductionProxyConfig* config,
     scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
     : client_(GetString(client)),
+      use_assigned_credentials_(false),
       data_reduction_proxy_config_(config),
       network_task_runner_(network_task_runner) {
   GetChromiumBuildAndPatch(ChromiumVersion(), &build_, &patch_);
@@ -85,6 +111,7 @@
     DataReductionProxyConfig* config,
     scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
     : client_(GetString(client)),
+      use_assigned_credentials_(false),
       data_reduction_proxy_config_(config),
       network_task_runner_(network_task_runner) {
   GetChromiumBuildAndPatch(version, &build_, &patch_);
@@ -172,7 +199,8 @@
   return base::Time::Now();
 }
 
-void DataReductionProxyRequestOptions::RandBytes(void* output, size_t length) {
+void DataReductionProxyRequestOptions::RandBytes(void* output,
+                                                 size_t length) const {
   crypto::RandBytes(output, length);
 }
 
@@ -200,10 +228,9 @@
 void DataReductionProxyRequestOptions::SetHeader(
     net::HttpRequestHeaders* headers) {
   base::Time now = Now();
-  // Authorization credentials must be regenerated at least every 24 hours.
-  if (now - last_credentials_update_time_ > base::TimeDelta::FromHours(24)) {
+  // Authorization credentials must be regenerated if they are expired.
+  if (!use_assigned_credentials_ && (now > credentials_expiration_time_))
     UpdateCredentials();
-  }
   UpdateLoFi();
   const char kChromeProxyHeader[] = "Chrome-Proxy";
   std::string header_value;
@@ -219,7 +246,7 @@
 void DataReductionProxyRequestOptions::ComputeCredentials(
     const base::Time& now,
     std::string* session,
-    std::string* credentials) {
+    std::string* credentials) const {
   DCHECK(session);
   DCHECK(credentials);
   int64 timestamp =
@@ -239,10 +266,9 @@
 }
 
 void DataReductionProxyRequestOptions::UpdateCredentials() {
-  std::string session;
-  std::string credentials;
-  last_credentials_update_time_ = Now();
-  ComputeCredentials(last_credentials_update_time_, &session_, &credentials_);
+  base::Time now = Now();
+  ComputeCredentials(now, &session_, &credentials_);
+  credentials_expiration_time_ = now + base::TimeDelta::FromHours(24);
   RegenerateRequestHeaderValue();
 }
 
@@ -253,6 +279,32 @@
   }
 }
 
+void DataReductionProxyRequestOptions::PopulateConfigResponse(
+    base::DictionaryValue* response) const {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  std::string session;
+  std::string credentials;
+  base::Time now = Now();
+  base::Time expiration_time = now + base::TimeDelta::FromHours(24);
+  ComputeCredentials(now, &session, &credentials);
+  response->SetString("sessionKey",
+                      CreateLocalSessionKey(session, credentials));
+  response->SetString("expireTime",
+                      config_parser::TimeToISO8601(expiration_time));
+}
+
+void DataReductionProxyRequestOptions::SetCredentials(
+    const std::string& session,
+    const std::string& credentials) {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  session_ = session;
+  credentials_ = credentials;
+  // Force skipping of credential regeneration. It should be handled by the
+  // caller.
+  use_assigned_credentials_ = true;
+  RegenerateRequestHeaderValue();
+}
+
 std::string DataReductionProxyRequestOptions::GetDefaultKey() const {
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
index e5f7ce1..8d6c289 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
@@ -14,6 +14,7 @@
 #include "base/time/time.h"
 
 namespace base {
+class DictionaryValue;
 class SingleThreadTaskRunner;
 }
 
@@ -65,6 +66,22 @@
  public:
   static bool IsKeySetOnCommandLine();
 
+  // A pair of functions to convert the session and credentials for the Data
+  // Reduction Proxy to and from a single string; they are used to encode the
+  // session and credentials values into the |session_key| field of the
+  // ClientConfig protocol buffer. The delimiter used is '|', as it is not a
+  // valid character in a session or credentials string.
+  //
+  // CreateLocalSessionKey joins session and credentials with the delimiter.
+  static std::string CreateLocalSessionKey(const std::string& session,
+                                           const std::string& credentials);
+
+  // ParseLocalSessionKey splits the output of CreateLocalSessionKey into its
+  // two components. |session| and |credentials| must not be null.
+  static bool ParseLocalSessionKey(const std::string& session_key,
+                                   std::string* session,
+                                   std::string* credentials);
+
   // Constructs a DataReductionProxyRequestOptions object with the given
   // client type, config, and network task runner.
   DataReductionProxyRequestOptions(
@@ -104,6 +121,14 @@
   // SetKeyOnIO is called.
   void SetKeyOnIO(const std::string& key);
 
+  // Populates |response| with the Data Reduction Proxy authentication info.
+  // Virtualized for testing.
+  virtual void PopulateConfigResponse(base::DictionaryValue* response) const;
+
+  // Sets the credentials for sending to the Data Reduction Proxy.
+  void SetCredentials(const std::string& session,
+                      const std::string& credentials);
+
  protected:
   void SetHeader(net::HttpRequestHeaders* headers);
 
@@ -114,7 +139,7 @@
                                         const std::string& key);
   // Visible for testing.
   virtual base::Time Now() const;
-  virtual void RandBytes(void* output, size_t length);
+  virtual void RandBytes(void* output, size_t length) const;
 
   // Visible for testing.
   virtual std::string GetDefaultKey() const;
@@ -153,7 +178,7 @@
   // the data reduction proxy.
   void ComputeCredentials(const base::Time& now,
                           std::string* session,
-                          std::string* credentials);
+                          std::string* credentials) const;
 
   // Generates and updates the session ID and credentials.
   void UpdateCredentials();
@@ -186,9 +211,13 @@
   std::string lofi_;
   std::vector<std::string> experiments_;
 
-  // The last time the session was updated. Used to ensure that a session is
+  // The time at which the session expires. Used to ensure that a session is
   // never used for more than twenty-four hours.
-  base::Time last_credentials_update_time_;
+  base::Time credentials_expiration_time_;
+
+  // Whether the authentication headers are sourced by |this| or injected via
+  // |SetCredentials|.
+  bool use_assigned_credentials_;
 
   DataReductionProxyConfig* data_reduction_proxy_config_;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
index 70ea1f4..d34ebf2 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
@@ -28,7 +28,6 @@
 const char kExpectedBuild[] = "2";
 const char kExpectedPatch[] = "3";
 const char kBogusVersion[] = "0.0";
-const char kTestKey[] = "test-key";
 const char kExpectedCredentials[] = "96bd72ec4a050ba60981743d41787768";
 const char kExpectedSession[] = "0-1633771873-1633771873-1633771873";
 
@@ -77,41 +76,6 @@
 const char kClientStr[] = "";
 #endif
 
-class TestDataReductionProxyRequestOptions
-    : public DataReductionProxyRequestOptions {
- public:
-  TestDataReductionProxyRequestOptions(
-      Client client,
-      const std::string& version,
-      DataReductionProxyConfig* config,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-      : DataReductionProxyRequestOptions(
-            client, version, config, task_runner) {}
-
-  std::string GetDefaultKey() const override {
-    return kTestKey;
-  }
-
-  base::Time Now() const override {
-    return base::Time::UnixEpoch() + now_offset_;
-  }
-
-  void RandBytes(void* output, size_t length) override {
-    char* c =  static_cast<char*>(output);
-    for (size_t i = 0; i < length; ++i) {
-      c[i] = 'a';
-    }
-  }
-
-  // Time after the unix epoch that Now() reports.
-  void set_offset(const base::TimeDelta& now_offset) {
-    now_offset_ = now_offset;
-  }
-
- private:
-  base::TimeDelta now_offset_;
-};
-
 void SetHeaderExpectations(const std::string& session,
                            const std::string& credentials,
                            const std::string& client,
@@ -339,4 +303,64 @@
   VerifyExpectedHeader(params()->DefaultOrigin(), expected_header);
 }
 
+TEST_F(DataReductionProxyRequestOptionsTest, ParseLocalSessionKey) {
+  const struct {
+    bool should_succeed;
+    std::string session_key;
+    std::string expected_session;
+    std::string expected_credentials;
+  } tests[] = {
+      {
+          true,
+          "foobar|1234",
+          "foobar",
+          "1234",
+      },
+      {
+          false,
+          "foobar|1234|foobaz",
+          std::string(),
+          std::string(),
+      },
+      {
+          false,
+          "foobar",
+          std::string(),
+          std::string(),
+      },
+      {
+          false,
+          std::string(),
+          std::string(),
+          std::string(),
+      },
+  };
+
+  std::string session;
+  std::string credentials;
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    EXPECT_EQ(tests[i].should_succeed,
+              DataReductionProxyRequestOptions::ParseLocalSessionKey(
+                  tests[i].session_key, &session, &credentials));
+    if (tests[i].should_succeed) {
+      EXPECT_EQ(tests[i].expected_session, session);
+      EXPECT_EQ(tests[i].expected_credentials, credentials);
+    }
+  }
+}
+
+TEST_F(DataReductionProxyRequestOptionsTest, PopulateConfigResponse) {
+  CreateRequestOptions(kBogusVersion);
+  scoped_ptr<base::DictionaryValue> values(new base::DictionaryValue());
+  request_options()->PopulateConfigResponse(values.get());
+  std::string session;
+  std::string expire_time;
+  EXPECT_TRUE(values->GetString("sessionKey", &session));
+  EXPECT_TRUE(values->GetString("expireTime", &expire_time));
+  EXPECT_EQ(
+      "0-1633771873-1633771873-1633771873|96bd72ec4a050ba60981743d41787768",
+      session);
+  EXPECT_EQ("1970-01-02T00:00:00.000Z", expire_time);
+}
+
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
index 7e73e60..fe091dce 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
@@ -45,7 +45,7 @@
     test_context_->pref_service()->SetBoolean(prefs::kDataReductionProxyEnabled,
                                               initially_enabled);
     test_context_->config()->SetStateForTest(initially_enabled, false,
-                                             !request_succeeded, false);
+                                             !request_succeeded);
     ExpectSetProxyPrefs(expected_enabled, false, false);
     settings_->MaybeActivateDataReductionProxy(false);
     test_context_->RunUntilIdle();
@@ -55,7 +55,7 @@
 TEST_F(DataReductionProxySettingsTest, TestIsProxyEnabledOrManaged) {
   settings_->InitPrefMembers();
   // The proxy is disabled initially.
-  test_context_->config()->SetStateForTest(false, false, false, false);
+  test_context_->config()->SetStateForTest(false, false, false);
 
   EXPECT_FALSE(settings_->IsDataReductionProxyEnabled());
   EXPECT_FALSE(settings_->IsDataReductionProxyManaged());
@@ -74,7 +74,7 @@
 TEST_F(DataReductionProxySettingsTest, TestCanUseDataReductionProxy) {
   settings_->InitPrefMembers();
   // The proxy is disabled initially.
-  test_context_->config()->SetStateForTest(false, false, false, false);
+  test_context_->config()->SetStateForTest(false, false, false);
 
   GURL http_gurl("http://url.com/");
   EXPECT_FALSE(settings_->CanUseDataReductionProxy(http_gurl));
@@ -240,7 +240,7 @@
           .Build();
 
   // The proxy is enabled initially.
-  drp_test_context->config()->SetStateForTest(true, false, false, true);
+  drp_test_context->config()->SetStateForTest(true, false, false);
   drp_test_context->InitSettings();
 
   // The pref is disabled, so correspondingly should be the proxy.
@@ -269,7 +269,7 @@
 
   // TODO(bengr): Test enabling/disabling while a secure proxy check is
   // outstanding.
-  // The proxy is enabled and unrestructed initially.
+  // The proxy is enabled and unrestricted initially.
   // Request succeeded but with bad response, expect proxy to be restricted.
   CheckMaybeActivateDataReductionProxy(true, true, true, true, false);
   // Request succeeded with valid response, expect proxy to be unrestricted.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
index 422cd424..aae60c3 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -6,9 +6,11 @@
 
 #include "base/message_loop/message_loop.h"
 #include "base/prefs/testing_pref_service.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
@@ -22,8 +24,141 @@
 #include "net/url_request/url_request_job_factory_impl.h"
 #include "net/url_request/url_request_test_util.h"
 
+namespace {
+
+const char kTestKey[] = "test-key";
+
+const net::BackoffEntry::Policy kTestBackoffPolicy = {
+    0,               // num_errors_to_ignore
+    10 * 1000,       // initial_delay_ms
+    2,               // multiply_factor
+    0,               // jitter_factor
+    30 * 60 * 1000,  // maximum_backoff_ms
+    -1,              // entry_lifetime_ms
+    true,            // always_use_initial_delay
+};
+
+}  // namespace
+
 namespace data_reduction_proxy {
 
+TestDataReductionProxyRequestOptions::TestDataReductionProxyRequestOptions(
+    Client client,
+    const std::string& version,
+    DataReductionProxyConfig* config,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : DataReductionProxyRequestOptions(client, version, config, task_runner) {
+}
+
+std::string TestDataReductionProxyRequestOptions::GetDefaultKey() const {
+  return kTestKey;
+}
+
+base::Time TestDataReductionProxyRequestOptions::Now() const {
+  return base::Time::UnixEpoch() + now_offset_;
+}
+
+void TestDataReductionProxyRequestOptions::RandBytes(void* output,
+                                                     size_t length) const {
+  char* c = static_cast<char*>(output);
+  for (size_t i = 0; i < length; ++i) {
+    c[i] = 'a';
+  }
+}
+
+// Time after the unix epoch that Now() reports.
+void TestDataReductionProxyRequestOptions::set_offset(
+    const base::TimeDelta& now_offset) {
+  now_offset_ = now_offset;
+}
+
+MockDataReductionProxyRequestOptions::MockDataReductionProxyRequestOptions(
+    Client client,
+    const std::string& version,
+    DataReductionProxyConfig* config,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : DataReductionProxyRequestOptions(client, version, config, task_runner) {
+}
+
+MockDataReductionProxyRequestOptions::~MockDataReductionProxyRequestOptions() {
+}
+
+TestDataReductionProxyConfigServiceClient::
+    TestDataReductionProxyConfigServiceClient(
+        scoped_ptr<DataReductionProxyParams> params,
+        DataReductionProxyRequestOptions* request_options,
+        DataReductionProxyMutableConfigValues* config_values,
+        DataReductionProxyConfig* config,
+        scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+    : DataReductionProxyConfigServiceClient(params.Pass(),
+                                            kTestBackoffPolicy,
+                                            request_options,
+                                            config_values,
+                                            config,
+                                            io_task_runner),
+      tick_clock_(base::Time::UnixEpoch()),
+      test_backoff_entry_(&kTestBackoffPolicy, &tick_clock_) {
+}
+
+TestDataReductionProxyConfigServiceClient::
+    ~TestDataReductionProxyConfigServiceClient() {
+}
+
+void TestDataReductionProxyConfigServiceClient::SetNow(const base::Time& time) {
+  tick_clock_.SetTime(time);
+}
+
+void TestDataReductionProxyConfigServiceClient::SetCustomReleaseTime(
+    const base::TimeTicks& release_time) {
+  test_backoff_entry_.SetCustomReleaseTime(release_time);
+}
+
+base::TimeDelta TestDataReductionProxyConfigServiceClient::GetDelay() const {
+  return config_refresh_timer_.GetCurrentDelay();
+}
+
+base::Time TestDataReductionProxyConfigServiceClient::Now() const {
+  return tick_clock_.Now();
+}
+
+net::BackoffEntry*
+TestDataReductionProxyConfigServiceClient::GetBackoffEntry() {
+  return &test_backoff_entry_;
+}
+
+TestDataReductionProxyConfigServiceClient::TestTickClock::TestTickClock(
+    const base::Time& initial_time)
+    : time_(initial_time) {
+}
+
+base::TimeTicks
+TestDataReductionProxyConfigServiceClient::TestTickClock::NowTicks() const {
+  return base::TimeTicks::UnixEpoch() + (time_ - base::Time::UnixEpoch());
+}
+
+base::Time
+TestDataReductionProxyConfigServiceClient::TestTickClock::Now() const {
+  return time_;
+}
+
+void TestDataReductionProxyConfigServiceClient::TestTickClock::SetTime(
+    const base::Time& time) {
+  time_ = time;
+}
+
+TestDataReductionProxyConfigServiceClient::TestBackoffEntry::TestBackoffEntry(
+    const net::BackoffEntry::Policy* const policy,
+    const TestTickClock* tick_clock)
+    : net::BackoffEntry(policy),
+      tick_clock_(tick_clock) {
+}
+
+base::TimeTicks
+TestDataReductionProxyConfigServiceClient::TestBackoffEntry::ImplGetTimeNow()
+    const {
+  return tick_clock_->NowTicks();
+}
+
 MockDataReductionProxyService::MockDataReductionProxyService(
     scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs,
     DataReductionProxySettings* settings,
@@ -37,10 +172,11 @@
 
 TestDataReductionProxyIOData::TestDataReductionProxyIOData(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    scoped_ptr<TestDataReductionProxyConfig> config,
+    scoped_ptr<DataReductionProxyConfig> config,
     scoped_ptr<DataReductionProxyEventStore> event_store,
     scoped_ptr<DataReductionProxyRequestOptions> request_options,
-    scoped_ptr<DataReductionProxyConfigurator> configurator)
+    scoped_ptr<DataReductionProxyConfigurator> configurator,
+    scoped_ptr<DataReductionProxyConfigServiceClient> config_client)
     : DataReductionProxyIOData() {
   io_task_runner_ = task_runner;
   ui_task_runner_ = task_runner;
@@ -48,6 +184,7 @@
   event_store_ = event_store.Pass();
   request_options_ = request_options.Pass();
   configurator_ = configurator.Pass();
+  config_client_ = config_client.Pass();
   io_task_runner_ = task_runner;
   ui_task_runner_ = task_runner;
 }
@@ -65,6 +202,9 @@
       use_mock_config_(false),
       use_test_configurator_(false),
       use_mock_service_(false),
+      use_mock_request_options_(false),
+      use_config_client_(false),
+      use_test_config_client_(false),
       skip_settings_initialization_(false) {
 }
 
@@ -120,6 +260,25 @@
 }
 
 DataReductionProxyTestContext::Builder&
+DataReductionProxyTestContext::Builder::WithMockRequestOptions() {
+  use_mock_request_options_ = true;
+  return *this;
+}
+
+DataReductionProxyTestContext::Builder&
+DataReductionProxyTestContext::Builder::WithConfigClient() {
+  use_config_client_ = true;
+  return *this;
+}
+
+DataReductionProxyTestContext::Builder&
+DataReductionProxyTestContext::Builder::WithTestConfigClient() {
+  use_config_client_ = true;
+  use_test_config_client_ = true;
+  return *this;
+}
+
+DataReductionProxyTestContext::Builder&
 DataReductionProxyTestContext::Builder::SkipSettingsInitialization() {
   skip_settings_initialization_ = true;
   return *this;
@@ -127,6 +286,8 @@
 
 scoped_ptr<DataReductionProxyTestContext>
 DataReductionProxyTestContext::Builder::Build() {
+  // Check for invalid builder combinations.
+  DCHECK(!(use_mock_config_ && use_config_client_));
   scoped_ptr<base::MessageLoopForIO> loop(new base::MessageLoopForIO());
 
   unsigned int test_context_flags = 0;
@@ -162,19 +323,51 @@
   }
 
   scoped_ptr<TestDataReductionProxyConfig> config;
-  if (use_mock_config_) {
+  scoped_ptr<DataReductionProxyConfigServiceClient> config_client;
+  DataReductionProxyMutableConfigValues* raw_mutable_config = nullptr;
+  scoped_ptr<TestDataReductionProxyParams> params(
+      new TestDataReductionProxyParams(params_flags_, params_definitions_));
+  TestDataReductionProxyParams* raw_params = params.get();
+  if (use_config_client_) {
+    test_context_flags |= USE_CONFIG_CLIENT;
+    scoped_ptr<DataReductionProxyMutableConfigValues> mutable_config =
+        DataReductionProxyMutableConfigValues::CreateFromParams(task_runner,
+                                                                params.get());
+    raw_mutable_config = mutable_config.get();
+    config.reset(new TestDataReductionProxyConfig(
+        mutable_config.Pass(), task_runner, net_log.get(), configurator.get(),
+        event_store.get()));
+  } else if (use_mock_config_) {
     test_context_flags |= USE_MOCK_CONFIG;
     config.reset(new MockDataReductionProxyConfig(
-        params_flags_, params_definitions_, task_runner, net_log.get(),
-        configurator.get(), event_store.get()));
+        params.Pass(), task_runner, net_log.get(), configurator.get(),
+        event_store.get()));
   } else {
     config.reset(new TestDataReductionProxyConfig(
-        params_flags_, params_definitions_, task_runner, net_log.get(),
-        configurator.get(), event_store.get()));
+        params.Pass(), task_runner, net_log.get(), configurator.get(),
+        event_store.get()));
   }
 
-  scoped_ptr<DataReductionProxyRequestOptions> request_options(
-      new DataReductionProxyRequestOptions(client_, config.get(), task_runner));
+  scoped_ptr<DataReductionProxyRequestOptions> request_options;
+  if (use_mock_request_options_) {
+    test_context_flags |= USE_MOCK_REQUEST_OPTIONS;
+    request_options.reset(new MockDataReductionProxyRequestOptions(
+        client_, std::string(), config.get(), task_runner));
+  } else {
+    request_options.reset(new DataReductionProxyRequestOptions(
+        client_, config.get(), task_runner));
+  }
+
+  if (use_test_config_client_) {
+    test_context_flags |= USE_TEST_CONFIG_CLIENT;
+    config_client.reset(new TestDataReductionProxyConfigServiceClient(
+        params.Pass(), request_options.get(), raw_mutable_config, config.get(),
+        task_runner));
+  } else if (use_config_client_) {
+    config_client.reset(new DataReductionProxyConfigServiceClient(
+        params.Pass(), GetBackoffPolicy(), request_options.get(),
+        raw_mutable_config, config.get(), task_runner));
+  }
 
   scoped_ptr<DataReductionProxySettings> settings(
       new DataReductionProxySettings());
@@ -189,14 +382,14 @@
   scoped_ptr<TestDataReductionProxyIOData> io_data(
       new TestDataReductionProxyIOData(
           task_runner, config.Pass(), event_store.Pass(),
-          request_options.Pass(), configurator.Pass()));
+          request_options.Pass(), configurator.Pass(), config_client.Pass()));
   io_data->InitOnUIThread(pref_service.get());
 
   scoped_ptr<DataReductionProxyTestContext> test_context(
       new DataReductionProxyTestContext(
           loop.Pass(), task_runner, pref_service.Pass(), net_log.Pass(),
           request_context_getter, mock_socket_factory_, io_data.Pass(),
-          settings.Pass(), test_context_flags));
+          settings.Pass(), raw_params, test_context_flags));
 
   if (!skip_settings_initialization_)
     test_context->InitSettingsWithoutCheck();
@@ -213,6 +406,7 @@
     net::MockClientSocketFactory* mock_socket_factory,
     scoped_ptr<TestDataReductionProxyIOData> io_data,
     scoped_ptr<DataReductionProxySettings> settings,
+    TestDataReductionProxyParams* params,
     unsigned int test_context_flags)
     : test_context_flags_(test_context_flags),
       loop_(loop.Pass()),
@@ -222,7 +416,8 @@
       request_context_getter_(request_context_getter),
       mock_socket_factory_(mock_socket_factory),
       io_data_(io_data.Pass()),
-      settings_(settings.Pass()) {
+      settings_(settings.Pass()),
+      params_(params) {
 }
 
 DataReductionProxyTestContext::~DataReductionProxyTestContext() {
@@ -344,6 +539,34 @@
       data_reduction_proxy_service());
 }
 
+MockDataReductionProxyRequestOptions*
+DataReductionProxyTestContext::mock_request_options() const {
+  DCHECK(test_context_flags_ &
+         DataReductionProxyTestContext::USE_MOCK_REQUEST_OPTIONS);
+  return reinterpret_cast<MockDataReductionProxyRequestOptions*>(
+      io_data_->request_options());
+}
+
+TestDataReductionProxyConfig* DataReductionProxyTestContext::config() const {
+  return reinterpret_cast<TestDataReductionProxyConfig*>(io_data_->config());
+}
+
+DataReductionProxyMutableConfigValues*
+DataReductionProxyTestContext::mutable_config_values() {
+  DCHECK(test_context_flags_ &
+         DataReductionProxyTestContext::USE_CONFIG_CLIENT);
+  return reinterpret_cast<DataReductionProxyMutableConfigValues*>(
+      config()->config_values());
+}
+
+TestDataReductionProxyConfigServiceClient*
+DataReductionProxyTestContext::test_config_client() {
+  DCHECK(test_context_flags_ &
+         DataReductionProxyTestContext::USE_TEST_CONFIG_CLIENT);
+  return reinterpret_cast<TestDataReductionProxyConfigServiceClient*>(
+      io_data_->config_client());
+}
+
 DataReductionProxyUsageStats::UnreachableCallback
 DataReductionProxyTestContext::unreachable_callback() const {
   return base::Bind(&DataReductionProxySettings::SetUnreachable,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
index 589e391..df1b2c0 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
@@ -5,12 +5,20 @@
 #ifndef COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_TEST_UTILS_H_
 #define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_TEST_UTILS_H_
 
+#include <string>
+
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h"
+#include "net/base/backoff_entry.h"
 #include "net/base/capturing_net_log.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -18,7 +26,6 @@
 
 namespace base {
 class MessageLoopForUI;
-class SingleThreadTaskRunner;
 }
 
 namespace net {
@@ -33,12 +40,115 @@
 
 class DataReductionProxyConfigurator;
 class DataReductionProxyEventStore;
+class DataReductionProxyMutableConfigValues;
 class DataReductionProxyRequestOptions;
 class DataReductionProxySettings;
 class DataReductionProxyStatisticsPrefs;
 class MockDataReductionProxyConfig;
 class TestDataReductionProxyConfig;
 class TestDataReductionProxyConfigurator;
+class TestDataReductionProxyParams;
+
+// Test version of |DataReductionProxyRequestOptions|.
+class TestDataReductionProxyRequestOptions
+    : public DataReductionProxyRequestOptions {
+ public:
+  TestDataReductionProxyRequestOptions(
+      Client client,
+      const std::string& version,
+      DataReductionProxyConfig* config,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  // Overrides of DataReductionProxyRequestOptions.
+  std::string GetDefaultKey() const override;
+  base::Time Now() const override;
+  void RandBytes(void* output, size_t length) const override;
+
+  // Time after the unix epoch that Now() reports.
+  void set_offset(const base::TimeDelta& now_offset);
+
+ private:
+  base::TimeDelta now_offset_;
+};
+
+// Mock version of |DataReductionProxyRequestOptions|.
+class MockDataReductionProxyRequestOptions
+    : public DataReductionProxyRequestOptions {
+ public:
+  MockDataReductionProxyRequestOptions(
+      Client client,
+      const std::string& version,
+      DataReductionProxyConfig* config,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  ~MockDataReductionProxyRequestOptions();
+
+  MOCK_CONST_METHOD1(PopulateConfigResponse,
+                     void(base::DictionaryValue* response));
+};
+
+// Test version of |DataReductionProxyConfigServiceClient|, which permits
+// finely controlling the backoff timer.
+class TestDataReductionProxyConfigServiceClient
+    : public DataReductionProxyConfigServiceClient {
+ public:
+  TestDataReductionProxyConfigServiceClient(
+      scoped_ptr<DataReductionProxyParams> params,
+      DataReductionProxyRequestOptions* request_options,
+      DataReductionProxyMutableConfigValues* config_values,
+      DataReductionProxyConfig* config,
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+
+  ~TestDataReductionProxyConfigServiceClient() override;
+
+  void SetNow(const base::Time& time);
+
+  void SetCustomReleaseTime(const base::TimeTicks& release_time);
+
+  base::TimeDelta GetDelay() const;
+
+ protected:
+  // Overrides of DataReductionProxyConfigServiceClient
+  base::Time Now() const override;
+  net::BackoffEntry* GetBackoffEntry() override;
+
+ private:
+  // A clock which returns a fixed value in both base::Time and base::TimeTicks.
+  class TestTickClock {
+   public:
+    TestTickClock(const base::Time& initial_time);
+
+    // Returns the current base::TimeTicks
+    base::TimeTicks NowTicks() const;
+
+    // Returns the current base::Time
+    base::Time Now() const;
+
+    // Sets the current time.
+    void SetTime(const base::Time& time);
+
+   private:
+    base::Time time_;
+  };
+
+  // A net::BackoffEntry which uses an injected base::TickClock to control
+  // the backoff expiration time.
+  class TestBackoffEntry : public net::BackoffEntry {
+   public:
+    TestBackoffEntry(const BackoffEntry::Policy* const policy,
+                     const TestTickClock* tick_clock);
+
+   protected:
+    // Override of net::BackoffEntry.
+    base::TimeTicks ImplGetTimeNow() const override;
+
+   private:
+    const TestTickClock* tick_clock_;
+  };
+
+  TestTickClock tick_clock_;
+  TestBackoffEntry test_backoff_entry_;
+};
 
 // Test version of |DataReductionProxyService|, which permits mocking of various
 // methods.
@@ -62,15 +172,20 @@
  public:
   TestDataReductionProxyIOData(
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-      scoped_ptr<TestDataReductionProxyConfig> config,
+      scoped_ptr<DataReductionProxyConfig> config,
       scoped_ptr<DataReductionProxyEventStore> event_store,
       scoped_ptr<DataReductionProxyRequestOptions> request_options,
-      scoped_ptr<DataReductionProxyConfigurator> configurator);
+      scoped_ptr<DataReductionProxyConfigurator> configurator,
+      scoped_ptr<DataReductionProxyConfigServiceClient> config_client);
   ~TestDataReductionProxyIOData() override;
 
   DataReductionProxyConfigurator* configurator() const {
     return configurator_.get();
   }
+
+  DataReductionProxyConfigServiceClient* config_client() const {
+    return config_client_.get();
+  }
 };
 
 // Builds a test version of the Data Reduction Proxy stack for use in tests.
@@ -118,6 +233,17 @@
     // |DataReductionProxyService|.
     Builder& WithMockDataReductionProxyService();
 
+    // Specifies the use of |MockDataReductionProxyRequestOptions| instead of
+    // |DataReductionProxyRequestOptions|.
+    Builder& WithMockRequestOptions();
+
+    // Specifies the use of the |DataReductionProxyConfigServiceClient|.
+    Builder& WithConfigClient();
+
+    // Specifies the use of the a |TestDataReductionProxyConfigServiceClient|
+    // instead of a |DataReductionProxyConfigServiceClient|.
+    Builder& WithTestConfigClient();
+
     // Construct, but do not initialize the |DataReductionProxySettings| object.
     Builder& SkipSettingsInitialization();
 
@@ -134,6 +260,9 @@
     bool use_mock_config_;
     bool use_test_configurator_;
     bool use_mock_service_;
+    bool use_mock_request_options_;
+    bool use_config_client_;
+    bool use_test_config_client_;
     bool skip_settings_initialization_;
   };
 
@@ -179,6 +308,21 @@
   // be called if built with WithMockDataReductionProxyService.
   MockDataReductionProxyService* mock_data_reduction_proxy_service() const;
 
+  // Returns the underlying |MockDataReductionProxyRequestOptions|. This can
+  // only be called if built with WithMockRequestOptions.
+  MockDataReductionProxyRequestOptions* mock_request_options() const;
+
+  // Returns the underlying |TestDataReductionProxyConfig|.
+  TestDataReductionProxyConfig* config() const;
+
+  // Returns the underlying |DataReductionProxyMutableConfigValues|. This can
+  // only be called if built with WithConfigClient.
+  DataReductionProxyMutableConfigValues* mutable_config_values();
+
+  // Returns the underlying |TestDataReductionProxyConfigServiceClient|. This
+  // can only be called if built with WithTestConfigClient.
+  TestDataReductionProxyConfigServiceClient* test_config_client();
+
   // Obtains a callback for notifying that the Data Reduction Proxy is no
   // longer reachable.
   DataReductionProxyUsageStats::UnreachableCallback
@@ -208,10 +352,6 @@
     return io_data_->configurator();
   }
 
-  TestDataReductionProxyConfig* config() const {
-    return reinterpret_cast<TestDataReductionProxyConfig*>(io_data_->config());
-  }
-
   TestDataReductionProxyIOData* io_data() const {
     return io_data_.get();
   }
@@ -220,6 +360,10 @@
     return settings_.get();
   }
 
+  TestDataReductionProxyParams* test_params() const {
+    return params_;
+  }
+
  private:
   enum TestContextOptions {
     // Permits mocking of the underlying |DataReductionProxyConfig|.
@@ -233,6 +377,12 @@
     SKIP_SETTINGS_INITIALIZATION = 0x4,
     // Permits mocking of the underlying |DataReductionProxyService|.
     USE_MOCK_SERVICE = 0x8,
+    // Permits mocking of the underlying |DataReductionProxyRequestOptions|.
+    USE_MOCK_REQUEST_OPTIONS = 0x10,
+    // Specifies the use of the |DataReductionProxyConfigServiceClient|.
+    USE_CONFIG_CLIENT = 0x20,
+    // Specifies the use of the |TESTDataReductionProxyConfigServiceClient|.
+    USE_TEST_CONFIG_CLIENT = 0x40,
   };
 
   DataReductionProxyTestContext(
@@ -244,6 +394,7 @@
       net::MockClientSocketFactory* mock_socket_factory,
       scoped_ptr<TestDataReductionProxyIOData> io_data,
       scoped_ptr<DataReductionProxySettings> settings,
+      TestDataReductionProxyParams* params,
       unsigned int test_context_flags);
 
   void InitSettingsWithoutCheck();
@@ -266,6 +417,8 @@
   scoped_ptr<TestDataReductionProxyIOData> io_data_;
   scoped_ptr<DataReductionProxySettings> settings_;
 
+  TestDataReductionProxyParams* params_;
+
   DISALLOW_COPY_AND_ASSIGN(DataReductionProxyTestContext);
 };
 
diff --git a/components/data_reduction_proxy/core/common/BUILD.gn b/components/data_reduction_proxy/core/common/BUILD.gn
index a28e6d0..6976004 100644
--- a/components/data_reduction_proxy/core/common/BUILD.gn
+++ b/components/data_reduction_proxy/core/common/BUILD.gn
@@ -7,6 +7,8 @@
 static_library("common") {
   sources = [
     "data_reduction_proxy_bypass_type_list.h",
+    "data_reduction_proxy_client_config_parser.cc",
+    "data_reduction_proxy_client_config_parser.h",
     "data_reduction_proxy_config_values.h",
     "data_reduction_proxy_event_store.cc",
     "data_reduction_proxy_event_store.h",
@@ -25,6 +27,7 @@
   ]
   deps = [
     "//base",
+    "//components/data_reduction_proxy/proto:data_reduction_proxy_proto",
     "//net",
     "//url",
   ]
@@ -54,6 +57,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "data_reduction_proxy_client_config_parser_unittest.cc",
     "data_reduction_proxy_event_store_unittest.cc",
     "data_reduction_proxy_headers_unittest.cc",
     "data_reduction_proxy_params_unittest.cc",
@@ -64,6 +68,7 @@
     ":test_support",
     "//base",
     "//base/test:test_support",
+    "//components/data_reduction_proxy/proto:data_reduction_proxy_proto",
     "//net:test_support",
     "//testing/gtest",
   ]
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.cc
new file mode 100644
index 0000000..586ad68
--- /dev/null
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.cc
@@ -0,0 +1,166 @@
+// 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 "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h"
+
+#include <string>
+
+#include "base/json/json_reader.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "base/values.h"
+
+namespace {
+
+// String representations of ProxyServer schemes.
+const char kSchemeHTTP[] = "HTTP";
+const char kSchemeHTTPS[] = "HTTPS";
+const char kSchemeQUIC[] = "QUIC";
+const char kSchemeUnspecified[] = "UNSPECIFIED";
+
+}  // namespace
+
+namespace data_reduction_proxy {
+
+namespace config_parser {
+
+std::string GetSchemeString(net::ProxyServer::Scheme scheme) {
+  switch (scheme) {
+    case net::ProxyServer::SCHEME_HTTP:
+      return kSchemeHTTP;
+    case net::ProxyServer::SCHEME_HTTPS:
+      return kSchemeHTTPS;
+    case net::ProxyServer::SCHEME_QUIC:
+      return kSchemeQUIC;
+    default:
+      return kSchemeUnspecified;
+  }
+}
+
+net::ProxyServer::Scheme SchemeFromProxyScheme(
+    ProxyServer_ProxyScheme proxy_scheme) {
+  switch (proxy_scheme) {
+    case ProxyServer_ProxyScheme_HTTP:
+      return net::ProxyServer::SCHEME_HTTP;
+    case ProxyServer_ProxyScheme_HTTPS:
+      return net::ProxyServer::SCHEME_HTTPS;
+    case ProxyServer_ProxyScheme_QUIC:
+      return net::ProxyServer::SCHEME_QUIC;
+    default:
+      return net::ProxyServer::SCHEME_INVALID;
+  }
+}
+
+ProxyServer_ProxyScheme GetProxyScheme(const std::string& scheme) {
+  if (scheme == kSchemeHTTP)
+    return ProxyServer_ProxyScheme_HTTP;
+
+  if (scheme == kSchemeHTTPS)
+    return ProxyServer_ProxyScheme_HTTPS;
+
+  if (scheme == kSchemeQUIC)
+    return ProxyServer_ProxyScheme_QUIC;
+
+  return ProxyServer_ProxyScheme_UNSPECIFIED;
+}
+
+std::string TimeToISO8601(const base::Time& time) {
+  base::Time::Exploded exploded;
+  time.UTCExplode(&exploded);
+  return base::StringPrintf(
+      "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", exploded.year, exploded.month,
+      exploded.day_of_month, exploded.hour, exploded.minute, exploded.second,
+      exploded.millisecond);
+}
+
+bool ISO8601ToTimestamp(const std::string& time, Timestamp* timestamp) {
+  base::Time t;
+  if (!base::Time::FromUTCString(time.c_str(), &t))
+    return false;
+
+  timestamp->set_seconds((t - base::Time::UnixEpoch()).InSeconds());
+  // Discard fractional seconds; it isn't worth the code effort to
+  // calculate it.
+  timestamp->set_nanos(0);
+  return true;
+}
+
+base::Time TimestampToTime(const Timestamp& timestamp) {
+  base::Time t = base::Time::UnixEpoch();
+  t += base::TimeDelta::FromSeconds(timestamp.seconds());
+  t += base::TimeDelta::FromMicroseconds(
+      timestamp.nanos() / base::Time::kNanosecondsPerMicrosecond);
+  return t;
+}
+
+bool ParseClientConfig(const std::string& config_data, ClientConfig* config) {
+  scoped_ptr<base::Value> parsed_data(base::JSONReader::Read(config_data));
+  if (!parsed_data)
+    return false;
+
+  const base::DictionaryValue* parsed_dict;
+  if (!parsed_data->GetAsDictionary(&parsed_dict))
+    return false;
+
+  std::string session_key;
+  if (!parsed_dict->GetString("sessionKey", &session_key))
+    return false;
+
+  config->set_session_key(session_key);
+
+  std::string expire_time;
+  if (!parsed_dict->GetString("expireTime", &expire_time))
+    return false;
+
+  if (!ISO8601ToTimestamp(expire_time, config->mutable_expire_time()))
+    return false;
+
+  const base::DictionaryValue* proxy_config_dict;
+  if (!parsed_dict->GetDictionary("proxyConfig", &proxy_config_dict))
+    return false;
+
+  ProxyConfig* proxy_config = config->mutable_proxy_config();
+
+  const base::ListValue* http_proxy_servers;
+  if (!proxy_config_dict->GetList("httpProxyServers", &http_proxy_servers))
+    return false;
+
+  base::ListValue::const_iterator it = http_proxy_servers->begin();
+  for (; it != http_proxy_servers->end(); ++it) {
+    const base::DictionaryValue* server_value;
+    if (!(*it)->GetAsDictionary(&server_value)) {
+      continue;
+    }
+
+    std::string scheme;
+    std::string host;
+    int port;
+    if (!server_value->GetString("scheme", &scheme)) {
+      continue;
+    }
+
+    if (!server_value->GetString("host", &host)) {
+      continue;
+    }
+
+    if (!server_value->GetInteger("port", &port)) {
+      continue;
+    }
+
+    ProxyServer_ProxyScheme proxy_scheme = GetProxyScheme(scheme);
+    if (proxy_scheme == ProxyServer_ProxyScheme_UNSPECIFIED)
+      continue;
+
+    ProxyServer* proxy_server = proxy_config->add_http_proxy_servers();
+    proxy_server->set_scheme(GetProxyScheme(scheme));
+    proxy_server->set_host(host);
+    proxy_server->set_port(port);
+  }
+
+  return true;
+}
+
+}  // namespace config_parser
+
+}  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h
new file mode 100644
index 0000000..461d9451
--- /dev/null
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h
@@ -0,0 +1,51 @@
+// 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 COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_CLIENT_CONFIG_RESPONSE_PARSER_H_
+#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_CLIENT_CONFIG_RESPONSE_PARSER_H_
+
+#include <string>
+
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
+#include "net/proxy/proxy_server.h"
+
+namespace base {
+class Time;
+}
+
+namespace data_reduction_proxy {
+
+namespace config_parser {
+
+// Returns a string representation (which is actually the string representation
+// of ProxyServer_ProxyScheme) of a |net::ProxyServer::Scheme|.
+std::string GetSchemeString(net::ProxyServer::Scheme scheme);
+
+// Returns the |net::ProxyServer::Scheme| for a ProxyServer_ProxyScheme.
+net::ProxyServer::Scheme SchemeFromProxyScheme(
+    ProxyServer_ProxyScheme proxy_scheme);
+
+// Retrieves the ProxyServer_ProxyScheme for its string representation.
+ProxyServer_ProxyScheme GetProxyScheme(const std::string& scheme);
+
+// Returns the ISO-8601 representation of |time|.
+std::string TimeToISO8601(const base::Time& time);
+
+// Parses an ISO-8601 time string into a Timestamp proto.
+bool ISO8601ToTimestamp(const std::string& time, Timestamp* timestamp);
+
+// Returns the |base::Time| representation of |timestamp|.
+base::Time TimestampToTime(const Timestamp& timestamp);
+
+// Takes a JSON representation of a |ClientConfig| and populates |config|.
+// Returns false if the JSON has an unexpected structure.
+// TODO(jeremyim): This should be deprecated once gRPC support can be added
+// (which would give the binary proto instead of JSON).
+bool ParseClientConfig(const std::string& config_data, ClientConfig* config);
+
+}  // namespace config_parser
+
+}  // namespace data_reduction_proxy
+
+#endif  // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_CLIENT_CONFIG_RESPONSE_PARSER_H_
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser_unittest.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser_unittest.cc
new file mode 100644
index 0000000..997d6f28
--- /dev/null
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser_unittest.cc
@@ -0,0 +1,234 @@
+// 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 "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h"
+
+#include <string>
+
+#include "base/time/time.h"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace data_reduction_proxy {
+
+namespace {
+
+const char kValidPreamble[] =
+    "{ \"sessionKey\": \"foobar\", "
+    "\"expireTime\": \"2100-12-31T23:59:59.999999Z\", "
+    "\"proxyConfig\": { \"httpProxyServers\": [";
+const char kValidPostamble[] = "] } }";
+const char kFutureTime[] = "31 Dec 2020 23:59:59";
+
+}  // namespace
+
+class DataReductionProxyClientConfigParserTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    EXPECT_TRUE(base::Time::FromUTCString(kFutureTime, &future_time_));
+  }
+
+  const base::Time& GetFutureTime() {
+    return future_time_;
+  }
+
+ private:
+  base::Time future_time_;
+};
+
+TEST_F(DataReductionProxyClientConfigParserTest, TimeToISO8601) {
+  const struct {
+    base::Time time;
+    std::string expected;
+  } tests[] = {
+      {
+          base::Time::UnixEpoch(),
+          "1970-01-01T00:00:00.000Z",
+      },
+      {
+          GetFutureTime(),
+          "2020-12-31T23:59:59.000Z",
+      },
+  };
+
+  for (const auto& test : tests) {
+    EXPECT_EQ(test.expected, config_parser::TimeToISO8601(test.time));
+  }
+}
+
+TEST_F(DataReductionProxyClientConfigParserTest, ISO8601ToTimestamp) {
+  const struct {
+    std::string time_string;
+    int64 epoch_seconds;
+  } tests[] = {
+      {
+          "1970-01-01T00:00:00.000Z",
+          0,
+      },
+      {
+          "1970-01-01T00:00:00.999Z",
+          0,
+      },
+      {
+          "1950-01-01T00:00:00.000Z",
+          -631152000,
+      },
+      {
+          "1950-01-01T00:00:00.500Z",
+          -631151999,  // Rounding of negative fractional values causes this.
+      },
+  };
+
+  for (const auto& test : tests) {
+    Timestamp timestamp;
+    EXPECT_TRUE(
+        config_parser::ISO8601ToTimestamp(test.time_string, &timestamp));
+    EXPECT_EQ(test.epoch_seconds, timestamp.seconds());
+    EXPECT_EQ(0, timestamp.nanos());
+  }
+}
+
+TEST_F(DataReductionProxyClientConfigParserTest, ISO8601ToTimestampTestFailures)
+{
+  const std::string inputs[] = {
+      "",
+      "Not a time",
+      "1234",
+      "2099-43-12",
+      "2099-11-52",
+  };
+
+  for (const auto& input : inputs) {
+    Timestamp timestamp;
+    EXPECT_FALSE(config_parser::ISO8601ToTimestamp(input, &timestamp));
+  }
+}
+
+TEST_F(DataReductionProxyClientConfigParserTest, TimestampToTime) {
+  const struct {
+    int64 timestamp_seconds;
+    int32 timestamp_nanos;
+    base::Time expected_time;
+  } tests[] = {
+      {
+        0,
+        0,
+        base::Time::UnixEpoch(),
+      },
+      {
+        24 * 60 * 60 - 1,
+        base::Time::kNanosecondsPerSecond - 1,
+        base::Time::UnixEpoch() + base::TimeDelta::FromDays(1) -
+            base::TimeDelta::FromMicroseconds(1),
+      },
+      {
+          // 2020-12-31T23:59:59.000Z
+          1609459199,
+          0,
+          GetFutureTime(),
+      },
+  };
+
+  for (const auto& test : tests) {
+    Timestamp ts;
+    ts.set_seconds(test.timestamp_seconds);
+    ts.set_nanos(test.timestamp_nanos);
+    EXPECT_EQ(test.expected_time, config_parser::TimestampToTime(ts));
+  }
+}
+
+TEST_F(DataReductionProxyClientConfigParserTest, SingleServer) {
+  const std::string inputs[] = {
+    "{ \"scheme\": \"HTTP\", \"host\": \"foo.com\", \"port\": 80 }",
+    "{ \"scheme\": \"HTTPS\", \"host\": \"foo.com\", \"port\": 443 }",
+    "{ \"scheme\": \"QUIC\", \"host\": \"foo.com\", \"port\": 443 }",
+  };
+
+  for (const auto& scheme_fragment : inputs) {
+    std::string input = kValidPreamble + scheme_fragment + kValidPostamble;
+    ClientConfig config;
+    EXPECT_TRUE(config_parser::ParseClientConfig(input, &config));
+    EXPECT_EQ(1, config.proxy_config().http_proxy_servers_size());
+  }
+}
+
+TEST_F(DataReductionProxyClientConfigParserTest, MultipleServers) {
+  std::string input =
+      "{ \"scheme\": \"HTTP\", ""\"host\": \"foo.com\", \"port\": 80 }, "
+      "{ \"scheme\": \"HTTPS\", \"host\": \"foo.com\", \"port\": 443 }, "
+      "{ \"scheme\": \"QUIC\", \"host\": \"foo.com\", \"port\": 443 }";
+  ClientConfig config;
+  EXPECT_TRUE(config_parser::ParseClientConfig(
+      kValidPreamble + input + kValidPostamble, &config));
+  EXPECT_EQ(3, config.proxy_config().http_proxy_servers_size());
+}
+
+TEST_F(DataReductionProxyClientConfigParserTest, FailureCases) {
+  const std::string inputs[] = {
+    "",
+    "invalid json",
+    // No sessionKey
+    "{ \"expireTime\": \"2100-12-31T23:59:59.999999Z\","
+      "\"proxyConfig\": { \"httpProxyServers\": [ ] } }",
+    // No expireTime
+    "{ \"sessionKey\": \"foobar\","
+      "\"proxyConfig\": { \"httpProxyServers\": [ ] } }",
+    // No proxyConfig
+    "{ \"sessionKey\": \"foobar\", "
+      "\"expireTime\": \"2100-12-31T23:59:59.999999Z\" }",
+    // No proxyConfig.httpProxyServers
+    "{ \"sessionKey\": \"foobar\", "
+      "\"expireTime\": \"2100-12-31T23:59:59.999999Z\", \"proxyConfig\": { } }",
+    // Invalid sessionKey
+    "{ \"sessionKey\": 12345, "
+      "\"expireTime\": \"2100-12-31T23:59:59.999999Z\","
+      " \"proxyConfig\": { \"httpProxyServers\": [ ] } }",
+    // Invalid sessionKey
+    "{ \"sessionKey\": { }, "
+      "\"expireTime\": \"2100-12-31T23:59:59.999999Z\","
+      " \"proxyConfig\": { \"httpProxyServers\": [ ] } }",
+    // Invalid sessionKey
+    "{ \"sessionKey\": [ ], "
+      "\"expireTime\": \"2100-12-31T23:59:59.999999Z\","
+      " \"proxyConfig\": { \"httpProxyServers\": [ ] } }",
+    // Invalid expireTime
+    "{ \"sessionKey\": \"foobar\", "
+      "\"expireTime\": \"abcd\","
+      " \"proxyConfig\": { \"httpProxyServers\": [ ] } }",
+    // Invalid expireTime
+    "{ \"sessionKey\": \"foobar\", "
+      "\"expireTime\": [ ],"
+      " \"proxyConfig\": { \"httpProxyServers\": [ ] } }",
+    // Invalid expireTime
+    "{ \"sessionKey\": \"foobar\", "
+      "\"expireTime\": { },"
+      " \"proxyConfig\": { \"httpProxyServers\": [ ] } }",
+  };
+
+  for (const auto& input : inputs) {
+    ClientConfig config;
+    EXPECT_FALSE(config_parser::ParseClientConfig(input, &config));
+  }
+}
+
+TEST_F(DataReductionProxyClientConfigParserTest, EmptyServerLists) {
+  const std::string inputs[] = {
+    "",
+    "{ }",
+    "{ \"scheme\": \"foo\", \"host\": \"foo.com\", \"port\": 80 }",
+    "{ \"scheme\": \"HTTP\", \"port\": 80 }",
+    "{ \"scheme\": \"HTTP\", \"host\": \"foo.com\" }",
+    "{ \"scheme\": \"HTTP\", \"host\": \"foo.com\", \"port\": \"bar\" }",
+    "{ \"scheme\": \"HTTP\", \"host\": 12345, \"port\": 80 }",
+  };
+
+  for (const auto& scheme_fragment : inputs) {
+    std::string input = kValidPreamble + scheme_fragment + kValidPostamble;
+    ClientConfig config;
+    EXPECT_TRUE(config_parser::ParseClientConfig(input, &config));
+    EXPECT_EQ(0, config.proxy_config().http_proxy_servers_size());
+  }
+}
+
+}  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
index 770e3316..79205a2 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
@@ -7,8 +7,11 @@
 #include <string>
 
 #include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/metrics/field_trial.h"
 #include "base/strings/string_piece.h"
+#include "base/values.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "net/base/host_port_pair.h"
 #include "net/proxy/proxy_server.h"
@@ -36,6 +39,11 @@
 const char kAndroidOneIdentifier[] = "sprout";
 
 const char kQuicFieldTrial[] = "DataReductionProxyUseQuic";
+
+const char kConfigScheme[] = "scheme";
+const char kConfigHost[] = "host";
+const char kConfigPort[] = "port";
+
 }  // namespace
 
 namespace data_reduction_proxy {
@@ -122,6 +130,12 @@
   return kQuicFieldTrial;
 }
 
+// static
+bool DataReductionProxyParams::IsConfigClientEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      data_reduction_proxy::switches::kEnableDataReductionProxyConfigClient);
+}
+
 void DataReductionProxyParams::EnableQuic(bool enable) {
   quic_enabled_ = enable;
   DCHECK(!quic_enabled_ || IsIncludedInQuicFieldTrial());
@@ -386,6 +400,41 @@
   return false;
 }
 
+void DataReductionProxyParams::PopulateConfigResponse(
+    base::DictionaryValue* response) const {
+  scoped_ptr<base::Value> proxy_config(new base::DictionaryValue());
+  if (!holdback_) {
+    base::DictionaryValue* proxy_config_dict = nullptr;
+    if (!proxy_config->GetAsDictionary(&proxy_config_dict))
+      return;
+
+    scoped_ptr<base::Value> proxy_servers(new base::ListValue());
+    base::ListValue* proxy_servers_list = nullptr;
+    if (!proxy_servers->GetAsList(&proxy_servers_list))
+      return;
+
+    proxy_servers->GetAsList(&proxy_servers_list);
+    scoped_ptr<base::DictionaryValue> server(new base::DictionaryValue());
+
+    server->SetString(kConfigScheme,
+                      config_parser::GetSchemeString(origin_.scheme()));
+    server->SetString(kConfigHost, origin_.host_port_pair().host());
+    server->SetInteger(kConfigPort, origin_.host_port_pair().port());
+    proxy_servers_list->Append(server.release());
+    server.reset(new base::DictionaryValue());
+
+    server->SetString(kConfigScheme, config_parser::GetSchemeString(
+                                         fallback_origin_.scheme()));
+    server->SetString(kConfigHost, fallback_origin_.host_port_pair().host());
+    server->SetInteger(kConfigPort, fallback_origin_.host_port_pair().port());
+    proxy_servers_list->Append(server.release());
+
+    proxy_config_dict->Set("httpProxyServers", proxy_servers.Pass());
+  }
+
+  response->Set("proxyConfig", proxy_config.Pass());
+}
+
 // Returns the data reduction proxy primary origin.
 const net::ProxyServer& DataReductionProxyParams::origin() const {
   return origin_;
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
index 256c6c4..0f6aea8 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
@@ -13,6 +13,7 @@
 #include "url/gurl.h"
 
 namespace base {
+class DictionaryValue;
 class TimeDelta;
 }
 
@@ -112,6 +113,9 @@
 
   static std::string GetQuicFieldTrialName();
 
+  // Returns true if the Data Reduction Proxy config client should be used.
+  static bool IsConfigClientEnabled();
+
   // Constructs configuration parameters. If |kAllowed|, then the standard
   // data reduction proxy configuration is allowed to be used. If
   // |kfallbackAllowed| a fallback proxy can be used if the primary proxy is
@@ -131,6 +135,10 @@
   // If true, uses QUIC instead of SPDY to connect to proxies that use TLS.
   void EnableQuic(bool enable);
 
+  // Populates |response| with the Data Reduction Proxy server configuration.
+  // Virtual for mocking.
+  virtual void PopulateConfigResponse(base::DictionaryValue* response) const;
+
   // Overrides of |DataReductionProxyConfigValues|
   bool UsingHTTPTunnel(const net::HostPortPair& proxy_server) const override;
 
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
index e2e8120..aa50741 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
@@ -7,6 +7,7 @@
 #include <map>
 
 #include "base/command_line.h"
+#include "base/values.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "net/proxy/proxy_server.h"
@@ -766,4 +767,30 @@
       "google/hammerhead/hammerhead:5.0/LRX210/1570415:user/release-keys"));
 }
 
+TEST_F(DataReductionProxyParamsTest, PopulateConfigResponse) {
+  DataReductionProxyParams params(
+      DataReductionProxyParams::kAllowed |
+      DataReductionProxyParams::kAlternativeAllowed);
+  scoped_ptr<base::DictionaryValue> values(new base::DictionaryValue());
+  params.PopulateConfigResponse(values.get());
+  base::DictionaryValue* proxy_config;
+  EXPECT_TRUE(values->GetDictionary("proxyConfig", &proxy_config));
+  base::ListValue* proxy_servers;
+  EXPECT_TRUE(proxy_config->GetList("httpProxyServers", &proxy_servers));
+  EXPECT_TRUE(proxy_servers->GetSize() == 2);
+  base::DictionaryValue* server;
+  EXPECT_TRUE(proxy_servers->GetDictionary(0, &server));
+  std::string host;
+  int port;
+  EXPECT_TRUE(server->GetString("host", &host));
+  EXPECT_TRUE(server->GetInteger("port", &port));
+  EXPECT_EQ(params.origin().host_port_pair().host(), host);
+  EXPECT_EQ(params.origin().host_port_pair().port(), port);
+  EXPECT_TRUE(proxy_servers->GetDictionary(1, &server));
+  EXPECT_TRUE(server->GetString("host", &host));
+  EXPECT_TRUE(server->GetInteger("port", &port));
+  EXPECT_EQ(params.fallback_origin().host_port_pair().host(), host);
+  EXPECT_EQ(params.fallback_origin().host_port_pair().port(), port);
+}
+
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc
index bab8af00..2386931 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc
@@ -70,5 +70,9 @@
 const char kClearDataReductionProxyDataSavings[] =
     "clear-data-reduction-proxy-data-savings";
 
+// Enable the data reduction proxy config client.
+const char kEnableDataReductionProxyConfigClient[] =
+    "enable-data-reduction-proxy-config-client";
+
 }  // namespace switches
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h
index cd6d08d8..1a8f7c0 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h
@@ -28,6 +28,7 @@
 extern const char kEnableDataReductionProxyLoFi[];
 extern const char kEnableDataReductionProxyBypassWarning[];
 extern const char kClearDataReductionProxyDataSavings[];
+extern const char kEnableDataReductionProxyConfigClient[];
 
 }  // namespace switches
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/proto/BUILD.gn b/components/data_reduction_proxy/proto/BUILD.gn
new file mode 100644
index 0000000..889c1067
--- /dev/null
+++ b/components/data_reduction_proxy/proto/BUILD.gn
@@ -0,0 +1,11 @@
+# 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.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("data_reduction_proxy_proto") {
+  sources = [
+    "client_config.proto",
+  ]
+}
diff --git a/components/data_reduction_proxy/proto/client_config.proto b/components/data_reduction_proxy/proto/client_config.proto
new file mode 100644
index 0000000..f2c796c
--- /dev/null
+++ b/components/data_reduction_proxy/proto/client_config.proto
@@ -0,0 +1,70 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package data_reduction_proxy;
+
+// The client configuration information for using the Data Saver service.
+message ClientConfig {
+  // An opaque per-session key assigned by the server which permits use of the
+  // Data Saver HTTP proxy servers.
+  optional string session_key = 1;
+  // The time at which the secure_session_key is no longer valid. This is
+  // enforced by the Data Saver service, and is provided to permit the client
+  // to request a new session key prior to expiration.
+  optional Timestamp expire_time = 2;
+  // The DataSaver proxy configuration that is valid for the session.
+  optional ProxyConfig proxy_config = 3;
+}
+
+// A Timestamp represents a point in time independent of any time zone
+// or calendar, represented as seconds and fractions of seconds at
+// nanosecond resolution in UTC Epoch time.
+message Timestamp {
+  // Represents seconds of UTC time since Unix epoch
+  // 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to
+  // 9999-12-31T23:59:59Z inclusive.
+  optional int64 seconds = 1;
+
+  // Non-negative fractions of a second at nanosecond resolution. Negative
+  // second values with fractions must still have non-negative nanos values
+  // that count forward in time. Must be from 0 to 999,999,999
+  // inclusive.
+  optional int32 nanos = 2;
+}
+
+// Data Saver proxy configuration.
+message ProxyConfig {
+  // Provides proxy server information for HTTP URIs.
+  repeated ProxyServer http_proxy_servers = 1;
+}
+
+// Configuration information for a specific proxy server.
+message ProxyServer {
+  // The scheme of the proxy server.
+  enum ProxyScheme {
+    // The proxy scheme is unspecified.
+    UNSPECIFIED = 0;
+    // HTTP
+    HTTP = 1;
+    // HTTPS
+    HTTPS = 2;
+    // HTTPS over QUIC
+    QUIC = 3;
+  }
+
+  // The scheme for the proxy server.
+  optional ProxyScheme scheme = 1;
+  // The host name for the proxy server.
+  optional string host = 2;
+  // The port number for the proxy server.
+  optional int32 port = 3;
+}
+
+// Request object to create a client configuration object.
+message CreateClientConfigRequest {
+}
diff --git a/components/dom_distiller/core/css/distilledpage.css b/components/dom_distiller/core/css/distilledpage.css
index c34d058d..01caad9a 100644
--- a/components/dom_distiller/core/css/distilledpage.css
+++ b/components/dom_distiller/core/css/distilledpage.css
@@ -79,6 +79,7 @@
   line-height: 1.4;
   text-rendering: optimizeLegibility;
   overflow-x: hidden;
+  -webkit-overflow-scrolling: touch;
 }
 
 /* Classes for light, dark and sepia themes.
diff --git a/components/dom_distiller/core/html/dom_distiller_viewer.html b/components/dom_distiller/core/html/dom_distiller_viewer.html
index 96908cb..334cd30 100644
--- a/components/dom_distiller/core/html/dom_distiller_viewer.html
+++ b/components/dom_distiller/core/html/dom_distiller_viewer.html
@@ -9,7 +9,7 @@
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
   <title>$1</title>
-  <link rel="stylesheet" href="/$2">
+  $2
   <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
 </head>
 <body dir="$9" class="$4">
@@ -78,5 +78,5 @@
     <a href="$7">$8</a>
   </div>
 </body>
-<script src="/$3"></script>
+$3
 </html>
diff --git a/components/dom_distiller/core/viewer.cc b/components/dom_distiller/core/viewer.cc
index 00e05ca..6ae11a91 100644
--- a/components/dom_distiller/core/viewer.cc
+++ b/components/dom_distiller/core/viewer.cc
@@ -113,8 +113,21 @@
           IDR_DOM_DISTILLER_VIEWER_HTML);
   std::vector<std::string> substitutions;
   substitutions.push_back(title);                                         // $1
-  substitutions.push_back(kViewerCssPath);                                // $2
-  substitutions.push_back(kViewerJsPath);                                 // $3
+
+  std::ostringstream css;
+  std::ostringstream script;
+#if defined(OS_IOS)
+  // On iOS the content is inlined as there is no API to detect those requests
+  // and return the local data once a page is loaded.
+  css << "<style>" << viewer::GetCss() << "</style>";
+  script << "<script>\n" << viewer::GetJavaScript() << "\n</script>";
+#else
+  css << "<link rel=\"stylesheet\" href=\"/" << kViewerCssPath << "\">";
+  script << "<script src=\"" << kViewerJsPath << "\"></script>";
+#endif  // defined(OS_IOS)
+  substitutions.push_back(css.str());                                     // $2
+  substitutions.push_back(script.str());                                  // $3
+
   substitutions.push_back(GetThemeCssClass(theme) + " " +
                           GetFontCssClass(font_family));                  // $4
   substitutions.push_back(content);                                       // $5
diff --git a/components/favicon.gypi b/components/favicon.gypi
index be91eeba..cddc746 100644
--- a/components/favicon.gypi
+++ b/components/favicon.gypi
@@ -19,6 +19,8 @@
       ],
       'sources': [
         # Note: sources list duplicated in GN build.
+        'favicon/core/fallback_icon_service.cc',
+        'favicon/core/fallback_icon_service.h',
         'favicon/core/favicon_client.h',
         'favicon/core/favicon_driver.h',
         'favicon/core/favicon_handler.cc',
diff --git a/components/favicon/core/BUILD.gn b/components/favicon/core/BUILD.gn
index f7ffefa..bbda145 100644
--- a/components/favicon/core/BUILD.gn
+++ b/components/favicon/core/BUILD.gn
@@ -4,6 +4,8 @@
 
 static_library("core") {
   sources = [
+    "fallback_icon_service.cc",
+    "fallback_icon_service.h",
     "favicon_client.h",
     "favicon_driver.h",
     "favicon_handler.cc",
diff --git a/components/favicon/core/DEPS b/components/favicon/core/DEPS
index 8a86e5b..ccb5231 100644
--- a/components/favicon/core/DEPS
+++ b/components/favicon/core/DEPS
@@ -2,6 +2,9 @@
   "+components/bookmarks/browser",
   "+components/history/core/browser",
   "+components/keyed_service/core",
+  "+net/base/registry_controlled_domains/registry_controlled_domain.h",
   "+skia",
   "+third_party/skia",
+  "+third_party/skia/include",
+  "+ui/gfx",
 ]
diff --git a/components/favicon_base/fallback_icon_service.cc b/components/favicon/core/fallback_icon_service.cc
similarity index 87%
rename from components/favicon_base/fallback_icon_service.cc
rename to components/favicon/core/fallback_icon_service.cc
index adce72e1..e174818 100644
--- a/components/favicon_base/fallback_icon_service.cc
+++ b/components/favicon/core/fallback_icon_service.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/favicon_base/fallback_icon_service.h"
+#include "components/favicon/core/fallback_icon_service.h"
 
 #include <algorithm>
 
@@ -18,8 +18,6 @@
 #include "ui/gfx/geometry/size.h"
 #include "url/gurl.h"
 
-namespace favicon_base {
-
 namespace {
 
 // Arbitrary maximum icon size, can be reasonably increased if needed.
@@ -48,7 +46,7 @@
 std::vector<unsigned char> FallbackIconService::RenderFallbackIconBitmap(
     const GURL& icon_url,
     int size,
-    const FallbackIconStyle& style) {
+    const favicon_base::FallbackIconStyle& style) {
   int size_to_use = std::min(kMaxFallbackFaviconSize, size);
   gfx::Canvas canvas(gfx::Size(size_to_use, size_to_use), 1.0f, false);
   DrawFallbackIcon(icon_url, size_to_use, style, &canvas);
@@ -61,10 +59,11 @@
   return bitmap_data;
 }
 
-void FallbackIconService::DrawFallbackIcon(const GURL& icon_url,
-                                           int size,
-                                           const FallbackIconStyle& style,
-                                           gfx::Canvas* canvas) {
+void FallbackIconService::DrawFallbackIcon(
+    const GURL& icon_url,
+    int size,
+    const favicon_base::FallbackIconStyle& style,
+    gfx::Canvas* canvas) {
   const int kOffsetX = 0;
   const int kOffsetY = 0;
   SkPaint paint;
@@ -93,5 +92,3 @@
       gfx::Rect(kOffsetX, kOffsetY, size, size),
       gfx::Canvas::TEXT_ALIGN_CENTER);
 }
-
-}  // namespace favicon_base
diff --git a/components/favicon_base/fallback_icon_service.h b/components/favicon/core/fallback_icon_service.h
similarity index 78%
rename from components/favicon_base/fallback_icon_service.h
rename to components/favicon/core/fallback_icon_service.h
index cc98adfe2..9b3f6ba1 100644
--- a/components/favicon_base/fallback_icon_service.h
+++ b/components/favicon/core/fallback_icon_service.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_FAVICON_BASE_FALLBACK_ICON_SERVICE_H_
-#define COMPONENTS_FAVICON_BASE_FALLBACK_ICON_SERVICE_H_
+#ifndef COMPONENTS_FAVICON_CORE_FALLBACK_ICON_SERVICE_H_
+#define COMPONENTS_FAVICON_CORE_FALLBACK_ICON_SERVICE_H_
 
 #include <string>
 #include <vector>
@@ -17,8 +17,8 @@
 }
 
 namespace favicon_base {
-
 struct FallbackIconStyle;
+}
 
 // A service to provide methods to render fallback favicons.
 class FallbackIconService {
@@ -31,14 +31,14 @@
   std::vector<unsigned char> RenderFallbackIconBitmap(
       const GURL& icon_url,
       int size,
-      const FallbackIconStyle& style);
+      const favicon_base::FallbackIconStyle& style);
 
  private:
   // Renders a fallback icon on |canvas| at position (|x|, |y|). |size| is icon
   // width and height in pixels.
   void DrawFallbackIcon(const GURL& icon_url,
                         int size,
-                        const FallbackIconStyle& style,
+                        const favicon_base::FallbackIconStyle& style,
                         gfx::Canvas* canvas);
 
   std::vector<std::string> font_list_;
@@ -46,6 +46,4 @@
   DISALLOW_COPY_AND_ASSIGN(FallbackIconService);
 };
 
-}  // namespace favicon_base
-
-#endif  // COMPONENTS_FAVICON_BASE_FALLBACK_ICON_SERVICE_H_
+#endif  // COMPONENTS_FAVICON_CORE_FALLBACK_ICON_SERVICE_H_
diff --git a/components/favicon_base.gypi b/components/favicon_base.gypi
index 0ea6331..852b9be 100644
--- a/components/favicon_base.gypi
+++ b/components/favicon_base.gypi
@@ -20,8 +20,6 @@
         '../url/url.gyp:url_lib',
       ],
       'sources': [
-        'favicon_base/fallback_icon_service.cc',
-        'favicon_base/fallback_icon_service.h',
         'favicon_base/fallback_icon_style.cc',
         'favicon_base/fallback_icon_style.h',
         'favicon_base/favicon_callback.h',
diff --git a/components/favicon_base/BUILD.gn b/components/favicon_base/BUILD.gn
index 07daa0a..2fcfb9c 100644
--- a/components/favicon_base/BUILD.gn
+++ b/components/favicon_base/BUILD.gn
@@ -4,8 +4,6 @@
 
 source_set("favicon_base") {
   sources = [
-    "fallback_icon_service.cc",
-    "fallback_icon_service.h",
     "fallback_icon_style.cc",
     "fallback_icon_style.h",
     "favicon_callback.h",
diff --git a/components/favicon_base/DEPS b/components/favicon_base/DEPS
index dc47487..0265afa1 100644
--- a/components/favicon_base/DEPS
+++ b/components/favicon_base/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
-  "+net/base/registry_controlled_domains/registry_controlled_domain.h",
   "+skia/ext",
   "+third_party/skia/include",
   "+ui/base",
diff --git a/components/feedback/feedback_data.cc b/components/feedback/feedback_data.cc
index 6b2fc49..5c424d9 100644
--- a/components/feedback/feedback_data.cc
+++ b/components/feedback/feedback_data.cc
@@ -44,7 +44,7 @@
 
 void FeedbackData::SetAndCompressSystemInfo(
     scoped_ptr<FeedbackData::SystemLogsMap> sys_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (trace_id_ != 0) {
     TracingManager* manager = TracingManager::Get();
@@ -70,7 +70,7 @@
 
 void FeedbackData::SetAndCompressHistograms(
     scoped_ptr<std::string> histograms) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!histograms.get())
     return;
@@ -87,7 +87,7 @@
 
 void FeedbackData::AttachAndCompressFileData(
     scoped_ptr<std::string> attached_filedata) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!attached_filedata.get() || attached_filedata->empty())
     return;
@@ -107,7 +107,7 @@
 void FeedbackData::OnGetTraceData(
     int trace_id,
     scoped_refptr<base::RefCountedString> trace_data) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   TracingManager* manager = TracingManager::Get();
   if (manager)
     manager->DiscardTraceData(trace_id);
@@ -124,7 +124,7 @@
 }
 
 void FeedbackData::OnCompressComplete() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   --pending_op_count_;
   SendReport();
 }
@@ -134,7 +134,7 @@
 }
 
 void FeedbackData::SendReport() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (IsDataComplete() && !report_sent_) {
     report_sent_ = true;
     send_report_.Run(this);
diff --git a/components/history/core/browser/thumbnail_database.cc b/components/history/core/browser/thumbnail_database.cc
index e828884..05a247d 100644
--- a/components/history/core/browser/thumbnail_database.cc
+++ b/components/history/core/browser/thumbnail_database.cc
@@ -79,7 +79,7 @@
 
 // Version 7: 911a634d/r209424 by qsr@chromium.org on 2013-07-01
 // Version 6: 610f923b/r152367 by pkotwicz@chromium.org on 2012-08-20
-// Version 5: e2ee8ae9/r105004 by groby@chromium.org on 2011-10-12
+// Version 5: e2ee8ae9/r105004 by groby@chromium.org on 2011-10-12 (deprecated)
 // Version 4: 5f104d76/r77288 by sky@chromium.org on 2011-03-08 (deprecated)
 // Version 3: 09911bf3/r15 by initial.commit on 2008-07-26 (deprecated)
 
@@ -88,7 +88,7 @@
 // the new version and a test to verify that Init() works with it.
 const int kCurrentVersionNumber = 7;
 const int kCompatibleVersionNumber = 7;
-const int kDeprecatedVersionNumber = 4;  // and earlier.
+const int kDeprecatedVersionNumber = 5;  // and earlier.
 
 void FillIconMapping(const sql::Statement& statement,
                      const GURL& page_url,
@@ -438,8 +438,8 @@
 
   // This code may be able to fetch version information that the regular
   // deprecation path cannot.
-  // NOTE(shess): v5 and v6 are currently not deprecated in the normal Init()
-  // path, but are deprecated in the recovery path in the interest of keeping
+  // NOTE(shess,rogerm): v6 is not currently deprecated in the normal Init()
+  // path, but is deprecated in the recovery path in the interest of keeping
   // the code simple.  http://crbug.com/327485 for numbers.
   DCHECK_LE(kDeprecatedVersionNumber, 6);
   if (version <= 6) {
@@ -1240,12 +1240,6 @@
     return sql::INIT_FAILURE;
   }
 
-  if (cur_version == 5) {
-    ++cur_version;
-    if (!UpgradeToVersion6())
-      return CantUpgradeToVersion(cur_version);
-  }
-
   if (cur_version == 6) {
     ++cur_version;
     if (!UpgradeToVersion7())
@@ -1283,32 +1277,6 @@
   return sql::INIT_FAILURE;
 }
 
-bool ThumbnailDatabase::UpgradeToVersion6() {
-  // Move bitmap data from favicons to favicon_bitmaps.
-  bool success =
-      db_.Execute("INSERT INTO favicon_bitmaps (icon_id, last_updated, "
-                  "image_data, width, height)"
-                  "SELECT id, last_updated, image_data, 0, 0 FROM favicons") &&
-      db_.Execute("CREATE TABLE temp_favicons ("
-                  "id INTEGER PRIMARY KEY,"
-                  "url LONGVARCHAR NOT NULL,"
-                  "icon_type INTEGER DEFAULT 1,"
-                  // default icon_type FAVICON to be consistent with
-                  // past migration.
-                  "sizes LONGVARCHAR)") &&
-      db_.Execute("INSERT INTO temp_favicons (id, url, icon_type) "
-                  "SELECT id, url, icon_type FROM favicons") &&
-      db_.Execute("DROP TABLE favicons") &&
-      db_.Execute("ALTER TABLE temp_favicons RENAME TO favicons");
-  // NOTE(shess): v7 will re-create the index.
-  if (!success)
-    return false;
-
-  meta_table_.SetVersionNumber(6);
-  meta_table_.SetCompatibleVersionNumber(std::min(6, kCompatibleVersionNumber));
-  return true;
-}
-
 bool ThumbnailDatabase::UpgradeToVersion7() {
   // Sizes column was never used, remove it.
   bool success =
diff --git a/components/leveldb_proto/DEPS b/components/leveldb_proto/DEPS
index 19109d2..743a2f3b 100644
--- a/components/leveldb_proto/DEPS
+++ b/components/leveldb_proto/DEPS
@@ -1,3 +1,3 @@
 include_rules = [
-  "+third_party/leveldatabase/src/include",
+  "+third_party/leveldatabase",
 ]
diff --git a/components/leveldb_proto/leveldb_database.cc b/components/leveldb_proto/leveldb_database.cc
index 45f220e9..b3e1a05 100644
--- a/components/leveldb_proto/leveldb_database.cc
+++ b/components/leveldb_proto/leveldb_database.cc
@@ -12,6 +12,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string_split.h"
 #include "base/threading/thread_checker.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
 #include "third_party/leveldatabase/src/include/leveldb/options.h"
@@ -55,6 +56,7 @@
   leveldb::Options options;
   options.create_if_missing = true;
   options.max_open_files = 0;  // Use minimum.
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   return InitWithOptions(database_dir, options);
 }
 
diff --git a/components/login/base_screen_handler_utils.cc b/components/login/base_screen_handler_utils.cc
index 782c064..6454cdf 100644
--- a/components/login/base_screen_handler_utils.cc
+++ b/components/login/base_screen_handler_utils.cc
@@ -76,11 +76,4 @@
   return base::StringValue(v);
 }
 
-void CallbackWrapper0(base::Callback<void()> callback,
-                      const base::ListValue* args) {
-  DCHECK(args);
-  DCHECK(args->empty());
-  callback.Run();
-}
-
 }  // namespace login
diff --git a/components/login/base_screen_handler_utils.h b/components/login/base_screen_handler_utils.h
index 63e55129..55497a0d 100644
--- a/components/login/base_screen_handler_utils.h
+++ b/components/login/base_screen_handler_utils.h
@@ -11,6 +11,7 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/strings/string16.h"
+#include "base/tuple.h"
 #include "base/values.h"
 #include "components/login/login_export.h"
 
@@ -59,68 +60,29 @@
   return v;
 }
 
-void LOGIN_EXPORT CallbackWrapper0(base::Callback<void()> callback,
-                                   const base::ListValue* args);
-
-template <typename A1>
-void CallbackWrapper1(base::Callback<void(A1)> callback,
-                      const base::ListValue* args) {
-  DCHECK(args);
-  DCHECK_EQ(1u, args->GetSize());
-  typename UnwrapConstRef<A1>::Type arg1;
-  if (!GetArg(args, 0, &arg1)) {
-    NOTREACHED();
-    return;
-  }
-  callback.Run(arg1);
+template <typename Arg, size_t index>
+typename UnwrapConstRef<Arg>::Type ParseArg(const base::ListValue* args) {
+  typename UnwrapConstRef<Arg>::Type parsed;
+  CHECK(GetArg(args, index, &parsed));
+  return parsed;
 }
 
-template <typename A1, typename A2>
-void CallbackWrapper2(base::Callback<void(A1, A2)> callback,
-                      const base::ListValue* args) {
+template <typename... Args, size_t... Ns>
+inline void DispatchToCallback(const base::Callback<void(Args...)>& callback,
+                               const base::ListValue* args,
+                               IndexSequence<Ns...> indexes) {
   DCHECK(args);
-  DCHECK_EQ(2u, args->GetSize());
-  typename UnwrapConstRef<A1>::Type arg1;
-  typename UnwrapConstRef<A2>::Type arg2;
-  if (!GetArg(args, 0, &arg1) || !GetArg(args, 1, &arg2)) {
-    NOTREACHED();
-    return;
-  }
-  callback.Run(arg1, arg2);
+  DCHECK_EQ(sizeof...(Args), args->GetSize());
+
+  callback.Run(ParseArg<Args, Ns>(args)...);
 }
 
-template <typename A1, typename A2, typename A3>
-void CallbackWrapper3(base::Callback<void(A1, A2, A3)> callback,
-                      const base::ListValue* args) {
-  DCHECK(args);
-  DCHECK_EQ(3u, args->GetSize());
-  typename UnwrapConstRef<A1>::Type arg1;
-  typename UnwrapConstRef<A2>::Type arg2;
-  typename UnwrapConstRef<A3>::Type arg3;
-  if (!GetArg(args, 0, &arg1) || !GetArg(args, 1, &arg2) ||
-      !GetArg(args, 2, &arg3)) {
-    NOTREACHED();
-    return;
-  }
-  callback.Run(arg1, arg2, arg3);
+template <typename... Args>
+void CallbackWrapper(const base::Callback<void(Args...)>& callback,
+                     const base::ListValue* args) {
+  DispatchToCallback(callback, args, MakeIndexSequence<sizeof...(Args)>());
 }
 
-template <typename A1, typename A2, typename A3, typename A4>
-void CallbackWrapper4(base::Callback<void(A1, A2, A3, A4)> callback,
-                      const base::ListValue* args) {
-  DCHECK(args);
-  DCHECK_EQ(4u, args->GetSize());
-  typename UnwrapConstRef<A1>::Type arg1;
-  typename UnwrapConstRef<A2>::Type arg2;
-  typename UnwrapConstRef<A3>::Type arg3;
-  typename UnwrapConstRef<A4>::Type arg4;
-  if (!GetArg(args, 0, &arg1) || !GetArg(args, 1, &arg2) ||
-      !GetArg(args, 2, &arg3) || !GetArg(args, 3, &arg4)) {
-    NOTREACHED();
-    return;
-  }
-  callback.Run(arg1, arg2, arg3, arg4);
-}
 
 }  // namespace login
 
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc
index 358d4237..60a3f30 100644
--- a/components/metrics/metrics_service.cc
+++ b/components/metrics/metrics_service.cc
@@ -209,6 +209,7 @@
   }
 }
 
+#if defined(OS_ANDROID) || defined(OS_IOS)
 void MarkAppCleanShutdownAndCommit(CleanExitBeacon* clean_exit_beacon,
                                    PrefService* local_state) {
   clean_exit_beacon->WriteBeaconValue(true);
@@ -217,6 +218,7 @@
   // Start writing right away (write happens on a different thread).
   local_state->CommitPendingWrite();
 }
+#endif  // defined(OS_ANDROID) || defined(OS_IOS)
 
 }  // namespace
 
@@ -764,6 +766,7 @@
 }
 
 void MetricsService::StartScheduledUpload() {
+  DCHECK(state_ >= INIT_TASK_DONE);
   // If we're getting no notifications, then the log won't have much in it, and
   // it's possible the computer is about to go to sleep, so don't upload and
   // stop the scheduler.
@@ -783,9 +786,9 @@
   // If there are unsent logs, send the next one. If not, start the asynchronous
   // process of finalizing the current log for upload.
   if (state_ == SENDING_LOGS && log_manager_.has_unsent_logs()) {
-    log_manager_.StageNextLogForUpload();
-    SendStagedLog();
+    SendNextLog();
   } else {
+    // There are no logs left to send, so start creating a new one.
     client_->CollectFinalMetrics(
         base::Bind(&MetricsService::OnFinalLogInfoCollectionDone,
                    self_ptr_factory_.GetWeakPtr()));
@@ -807,54 +810,33 @@
     return;
   }
 
-  StageNewLog();
+  if (state_ == INIT_TASK_DONE) {
+    PrepareInitialMetricsLog();
+  } else {
+    DCHECK_EQ(SENDING_LOGS, state_);
+    CloseCurrentLog();
+    OpenNewLog();
+  }
+  SendNextLog();
+}
 
-  // If logs shouldn't be uploaded, stop here. It's important that this check
-  // be after StageNewLog(), otherwise the previous logs will never be loaded,
-  // and thus the open log won't be persisted.
-  // TODO(stuartmorgan): This is unnecessarily complicated; restructure loading
-  // of previous logs to not require running part of the upload logic.
-  // http://crbug.com/157337
+void MetricsService::SendNextLog() {
+  DCHECK_EQ(SENDING_LOGS, state_);
   if (!reporting_active()) {
     scheduler_->Stop();
     scheduler_->UploadCancelled();
     return;
   }
-
-  SendStagedLog();
-}
-
-void MetricsService::StageNewLog() {
-  if (log_manager_.has_staged_log())
+  if (!log_manager_.has_unsent_logs()) {
+    // Should only get here if serializing the log failed somehow.
+    // Just tell the scheduler it was uploaded and wait for the next log
+    // interval.
+    scheduler_->UploadFinished(true, log_manager_.has_unsent_logs());
     return;
-
-  switch (state_) {
-    case INITIALIZED:
-    case INIT_TASK_SCHEDULED:  // We should be further along by now.
-      NOTREACHED();
-      return;
-
-    case INIT_TASK_DONE:
-      PrepareInitialMetricsLog();
-      // Stage the first log, which could be a stability log (either one
-      // for created in this session or from a previous session) or the
-      // initial metrics log that was just created.
-      log_manager_.StageNextLogForUpload();
-      state_ = SENDING_LOGS;
-      break;
-
-    case SENDING_LOGS:
-      CloseCurrentLog();
-      OpenNewLog();
-      log_manager_.StageNextLogForUpload();
-      break;
-
-    default:
-      NOTREACHED();
-      return;
   }
-
-  DCHECK(log_manager_.has_staged_log());
+  if (!log_manager_.has_staged_log())
+    log_manager_.StageNextLogForUpload();
+  SendStagedLog();
 }
 
 bool MetricsService::ProvidersHaveStabilityMetrics() {
@@ -928,6 +910,8 @@
   // Store unsent logs, including the initial log that was just saved, so
   // that they're not lost in case of a crash before upload time.
   log_manager_.PersistUnsentLogs();
+
+  state_ = SENDING_LOGS;
 }
 
 void MetricsService::SendStagedLog() {
@@ -1106,9 +1090,6 @@
 }
 
 void MetricsService::LogCleanShutdown() {
-  // Redundant hack to write pref ASAP.
-  MarkAppCleanShutdownAndCommit(&clean_exit_beacon_, local_state_);
-
   // Redundant setting to assure that we always reset this value at shutdown
   // (and that we don't use some alternate path, and not call LogCleanShutdown).
   clean_shutdown_status_ = CLEANLY_SHUTDOWN;
diff --git a/components/metrics/metrics_service.h b/components/metrics/metrics_service.h
index d7a612c8..0aba710 100644
--- a/components/metrics/metrics_service.h
+++ b/components/metrics/metrics_service.h
@@ -329,12 +329,13 @@
   // Starts the process of uploading metrics data.
   void StartScheduledUpload();
 
-  // Called by the client when final log info collection is complete.
+  // Called by the client via a callback when final log info collection is
+  // complete.
   void OnFinalLogInfoCollectionDone();
 
-  // Either closes the current log or creates and closes the initial log
-  // (depending on |state_|), and stages it for upload.
-  void StageNewLog();
+  // If recording is enabled, begins uploading the next completed log from
+  // the log manager, staging it if necessary.
+  void SendNextLog();
 
   // Returns true if any of the registered metrics providers have stability
   // metrics to report.
diff --git a/components/metrics/profiler/tracking_synchronizer.cc b/components/metrics/profiler/tracking_synchronizer.cc
index 8d53bd2..327c94dd 100644
--- a/components/metrics/profiler/tracking_synchronizer.cc
+++ b/components/metrics/profiler/tracking_synchronizer.cc
@@ -54,23 +54,23 @@
   ~RequestContext() {}
 
   void SetReceivedProcessGroupCount(bool done) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     received_process_group_count_ = done;
   }
 
   // Methods for book keeping of processes_pending_.
   void IncrementProcessesPending() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     ++processes_pending_;
   }
 
   void AddProcessesPending(int processes_pending) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     processes_pending_ += processes_pending;
   }
 
   void DecrementProcessesPending() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     --processes_pending_;
   }
 
@@ -79,7 +79,7 @@
   // |processes_pending_| are zero, then delete the current object by calling
   // Unregister.
   void DeleteIfAllDone() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     if (processes_pending_ <= 0 && received_process_group_count_)
       RequestContext::Unregister(sequence_number_);
@@ -90,7 +90,7 @@
   static RequestContext* Register(
       int sequence_number,
       const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     RequestContext* request = new RequestContext(
         callback_object, sequence_number);
@@ -102,7 +102,7 @@
   // Find the |RequestContext| in |outstanding_requests_| map for the given
   // |sequence_number|.
   static RequestContext* GetRequestContext(int sequence_number) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     RequestContextMap::iterator it =
         outstanding_requests_.Get().find(sequence_number);
@@ -118,7 +118,7 @@
   // |outstanding_requests_| map. This method is called when all changes have
   // been acquired, or when the wait time expires (whichever is sooner).
   static void Unregister(int sequence_number) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     RequestContextMap::iterator it =
         outstanding_requests_.Get().find(sequence_number);
@@ -198,7 +198,7 @@
 // static
 void TrackingSynchronizer::FetchProfilerDataAsynchronously(
     const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!g_tracking_synchronizer) {
     // System teardown is happening.
@@ -219,7 +219,7 @@
 void TrackingSynchronizer::OnPendingProcesses(int sequence_number,
                                               int pending_processes,
                                               bool end) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   RequestContext* request = RequestContext::GetRequestContext(sequence_number);
   if (!request)
@@ -233,14 +233,14 @@
     int sequence_number,
     const tracked_objects::ProcessDataSnapshot& profiler_data,
     int process_type) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DecrementPendingProcessesAndSendData(sequence_number, profiler_data,
                                        process_type);
 }
 
 int TrackingSynchronizer::RegisterAndNotifyAllProcesses(
     const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   int sequence_number = GetNextAvailableSequenceNumber();
 
@@ -266,7 +266,7 @@
     int sequence_number,
     const tracked_objects::ProcessDataSnapshot& profiler_data,
     int process_type) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   RequestContext* request = RequestContext::GetRequestContext(sequence_number);
   if (!request)
@@ -283,7 +283,7 @@
 }
 
 int TrackingSynchronizer::GetNextAvailableSequenceNumber() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   ++last_used_sequence_number_;
 
diff --git a/components/metrics/proto/chrome_user_metrics_extension.proto b/components/metrics/proto/chrome_user_metrics_extension.proto
index cf52be2..42f8c15 100644
--- a/components/metrics/proto/chrome_user_metrics_extension.proto
+++ b/components/metrics/proto/chrome_user_metrics_extension.proto
@@ -60,8 +60,8 @@
   repeated HistogramEventProto histogram_event = 6;
   repeated ProfilerEventProto profiler_event = 7;
 
-  // TODO(sque): Deprecate this field and use |sampled_profile| instead.
-  repeated PerfDataProto perf_data = 8;
+  // This field is no longer used. Use |sampled_profile| instead.
+  repeated PerfDataProto perf_data = 8 [deprecated=true];
 
   // A list of all collected sample-based profiles since the last UMA upload.
   repeated SampledProfile sampled_profile = 11;
diff --git a/components/nacl/BUILD.gn b/components/nacl/BUILD.gn
index 8c63c51..b67835cc 100644
--- a/components/nacl/BUILD.gn
+++ b/components/nacl/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
+import("//testing/test.gni")
 
 if (enable_nacl) {
   source_set("nacl") {
@@ -36,13 +37,13 @@
 
     if (enable_nacl_untrusted) {
       data_deps = [
-        # TODO(GYP): handle other cpu_arch's correctly.
+        # TODO(GYP): handle other architectures correctly.
         "//ppapi/native_client:nacl_irt(//native_client/build/toolchain/nacl:irt_x64)",
       ]
     }
     if (enable_pnacl) {
       data_deps += [
-        # TODO(GYP): handle other cpu_arch's correctly.
+        # TODO(GYP): handle other architectures correctly.
         "//ppapi/native_client/src/untrusted/pnacl_support_extension",
       ]
     }
@@ -126,8 +127,7 @@
     ]
   }
 
-  executable("nacl_loader_unittests") {
-    testonly = true
+  test("nacl_loader_unittests") {
     sources = [
       "loader/run_all_unittests.cc",
     ]
diff --git a/components/nacl/browser/nacl_file_host.cc b/components/nacl/browser/nacl_file_host.cc
index 2410e11b..f384910 100644
--- a/components/nacl/browser/nacl_file_host.cc
+++ b/components/nacl/browser/nacl_file_host.cc
@@ -49,7 +49,7 @@
     IPC::Message* reply_msg,
     WriteFileInfoReply write_reply_message) {
   // IO thread owns the NaClBrowser singleton.
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   nacl::NaClBrowser* nacl_browser = nacl::NaClBrowser::GetInstance();
   uint64 file_token_lo = 0;
diff --git a/components/nacl/browser/pnacl_host.cc b/components/nacl/browser/pnacl_host.cc
index 2069385..bae7e97d 100644
--- a/components/nacl/browser/pnacl_host.cc
+++ b/components/nacl/browser/pnacl_host.cc
@@ -147,7 +147,7 @@
 void PnaclHost::Init() {
   // Extra check that we're on the real IO thread since this version of
   // Init isn't used in unit tests.
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(thread_checker_.CalledOnValidThread());
   base::FilePath cache_path(GetCachePath());
   if (cache_path.empty() || cache_state_ != CacheUninitialized)
diff --git a/components/nacl/renderer/plugin/BUILD.gn b/components/nacl/renderer/plugin/BUILD.gn
index 5defb10..14f75cc1 100644
--- a/components/nacl/renderer/plugin/BUILD.gn
+++ b/components/nacl/renderer/plugin/BUILD.gn
@@ -20,6 +20,7 @@
   ]
 
   deps = [
+    "//base",
     "//media:shared_memory_support",
     "//native_client/src/shared/gio",
     "//native_client/src/shared/imc",
@@ -29,6 +30,7 @@
     "//native_client/src/trusted/platform_qualify:platform_qual_lib",
     "//native_client/src/trusted/simple_service",
     "//native_client/src/trusted/weak_ref",
+    "//ppapi/c",
     "//ppapi/cpp:objects",
     "//ppapi/cpp/private:internal_module",
   ]
diff --git a/components/navigation_interception/intercept_navigation_delegate.cc b/components/navigation_interception/intercept_navigation_delegate.cc
index aec92e7..7de30a5 100644
--- a/components/navigation_interception/intercept_navigation_delegate.cc
+++ b/components/navigation_interception/intercept_navigation_delegate.cc
@@ -32,7 +32,7 @@
 
 bool CheckIfShouldIgnoreNavigationOnUIThread(WebContents* source,
                                              const NavigationParams& params) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(source);
 
   InterceptNavigationDelegate* intercept_navigation_delegate =
diff --git a/components/navigation_interception/intercept_navigation_resource_throttle.cc b/components/navigation_interception/intercept_navigation_resource_throttle.cc
index 675c060..05741e6 100644
--- a/components/navigation_interception/intercept_navigation_resource_throttle.cc
+++ b/components/navigation_interception/intercept_navigation_resource_throttle.cc
@@ -73,7 +73,7 @@
 }
 
 InterceptNavigationResourceThrottle::~InterceptNavigationResourceThrottle() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
 void InterceptNavigationResourceThrottle::WillStartRequest(bool* defer) {
@@ -134,7 +134,7 @@
 
 void InterceptNavigationResourceThrottle::OnResultObtained(
     bool should_ignore_navigation) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (should_ignore_navigation) {
     controller()->CancelAndIgnore();
diff --git a/components/onc/docs/onc_spec.html b/components/onc/docs/onc_spec.html
index 7c6c5535..a0b89ab 100644
--- a/components/onc/docs/onc_spec.html
+++ b/components/onc/docs/onc_spec.html
@@ -20,7 +20,7 @@
   <h1>Objective</h1>
   <p>
     We would like to create a simple, open, but complete format to describe
-    multiple network configurations for Wi-Fi, Ethernet, Cellular,
+    multiple network configurations for WiFi, Ethernet, Cellular,
     Bluetooth/WiFi-Direct, and VPN connections in a single file format, in order
     to simplify and automate network configuration for users.
   </p>
@@ -33,7 +33,7 @@
     is a problem shared across desktop, laptop, tablet, and phone users of all
     operating system types. It is exacerbated in business and schools which
     often have complex network configurations (VPNs and 802.1X networking) that
-    change often and have many connected devices. Configuration of Wi-Fi is
+    change often and have many connected devices. Configuration of WiFi is
     still done manually, often by administrators physically standing next to
     users working on devices. Certificate distribution is particularly painful
     which often results in admins instead using passphrases to protect networks
@@ -202,7 +202,7 @@
         (optional)
         <span class="type">array of NetworkConfiguration</span>
       </span>
-      Describes Wi-Fi, Ethernet, VPN, and wireless connections.
+      Describes WiFi, Ethernet, VPN, and wireless connections.
     </dd>
 
     <dt class="field">Certificates</dt>
@@ -374,7 +374,7 @@
         <span class="value">WiFi</span>, otherwise ignored)
         <span class="type">WiFi</span>
       </span>
-      Wi-Fi settings.
+      WiFi settings.
     </dd>
 
     <dt class="field">WiMAX</dt>
@@ -649,9 +649,9 @@
 </section>
 
 <section>
-  <h1>Wi-Fi networks</h1>
+  <h1>WiFi networks</h1>
   <p>
-    For Wi-Fi connections, <span class="field">Type</span> must be set to
+    For WiFi connections, <span class="field">Type</span> must be set to
     <span class="value">WiFi</span> and the
     field <span class="field">WiFi</span> must be set to an object of
     type <span class="type">WiFi</span> containing the following fields:
@@ -2304,11 +2304,16 @@
       network technology currently in use.
       <span class="rule"><span class="rule_id"></span>
         Allowed values are
-        <span class="value">1xRTT</span>, <span class="value">EVDO</span>,
-        <span class="value">GPRS</span>, <span class="value">EDGE</span>,
+        <span class="value">CDMA1XRTT</span>,
+        <span class="value">EDGE</span>,
+        <span class="value">EVDO</span>,
+        <span class="value">GPRS</span>,
+        <span class="value">GSM</span>,
+        <span class="value">HSPA</span>,
+        <span class="value">HSPAPlus</span>,
+        <span class="value">LTE</span>,
+        <span class="value">LTEAdvanced</span>
         <span class="value">UMTS</span>,
-        <span class="value">HSPA</span>, <span class="value">HSPA+</span>,
-        <span class="value">LTE</span>, <span class="value">LTE Advanced</span>
       </span>
     </dd>
 
@@ -2547,7 +2552,7 @@
 <section>
   <h1>Bluetooth / WiFi Direct Networks</h1>
   <p>
-    This format will eventually also cover configuration of Bluetooth and Wi-Fi
+    This format will eventually also cover configuration of Bluetooth and WiFi
     Direct network technologies, however they are currently not supported.
   </p>
 </section>
diff --git a/components/onc/onc_constants.cc b/components/onc/onc_constants.cc
index 8945059a..41510b5 100644
--- a/components/onc/onc_constants.cc
+++ b/components/onc/onc_constants.cc
@@ -124,6 +124,16 @@
 const char kSIMPresent[] = "SIMPresent";
 const char kSupportedCarriers[] = "SupportedCarriers";
 const char kSupportNetworkScan[] = "SupportNetworkScan";
+const char kTechnologyCdma1Xrtt[] = "CDMA1XRTT";
+const char kTechnologyEdge[] = "EDGE";
+const char kTechnologyEvdo[] = "EVDO";
+const char kTechnologyGprs[] = "GPRS";
+const char kTechnologyGsm[] = "GSM";
+const char kTechnologyHspa[] = "HSPA";
+const char kTechnologyHspaPlus[] = "HSPAPlus";
+const char kTechnologyLte[] = "LTE";
+const char kTechnologyLteAdvanced[] = "LTEAdvanced";
+const char kTechnologyUmts[] = "UMTS";
 }  // namespace cellular
 
 namespace cellular_provider {
diff --git a/components/onc/onc_constants.h b/components/onc/onc_constants.h
index e3b6594..779003d 100644
--- a/components/onc/onc_constants.h
+++ b/components/onc/onc_constants.h
@@ -142,6 +142,16 @@
 ONC_EXPORT extern const char kSIMPresent[];
 ONC_EXPORT extern const char kSupportedCarriers[];
 ONC_EXPORT extern const char kSupportNetworkScan[];
+ONC_EXPORT extern const char kTechnologyCdma1Xrtt[];
+ONC_EXPORT extern const char kTechnologyEdge[];
+ONC_EXPORT extern const char kTechnologyEvdo[];
+ONC_EXPORT extern const char kTechnologyGprs[];
+ONC_EXPORT extern const char kTechnologyGsm[];
+ONC_EXPORT extern const char kTechnologyHspa[];
+ONC_EXPORT extern const char kTechnologyHspaPlus[];
+ONC_EXPORT extern const char kTechnologyLte[];
+ONC_EXPORT extern const char kTechnologyLteAdvanced[];
+ONC_EXPORT extern const char kTechnologyUmts[];
 }  // namespace cellular
 
 namespace cellular_provider {
diff --git a/components/password_manager.gypi b/components/password_manager.gypi
index 19dde01..bf083bfa 100644
--- a/components/password_manager.gypi
+++ b/components/password_manager.gypi
@@ -192,6 +192,8 @@
       ],
       'sources': [
         # Note: sources list duplicated in GN build.
+        'password_manager/core/common/credential_manager_types.cc',
+        'password_manager/core/common/credential_manager_types.h',
         'password_manager/core/common/experiments.cc',
         'password_manager/core/common/experiments.h',
         'password_manager/core/common/password_manager_pref_names.cc',
@@ -220,11 +222,11 @@
             '..',
           ],
           'sources': [
+            'password_manager/content/common/credential_manager_content_utils.cc',
+            'password_manager/content/common/credential_manager_content_utils.h',
             'password_manager/content/common/credential_manager_message_generator.cc',
             'password_manager/content/common/credential_manager_message_generator.h',
             'password_manager/content/common/credential_manager_messages.h',
-            'password_manager/content/common/credential_manager_types.cc',
-            'password_manager/content/common/credential_manager_types.h',
           ],
         },
         {
diff --git a/components/password_manager/content/browser/credential_manager_dispatcher.cc b/components/password_manager/content/browser/credential_manager_dispatcher.cc
index 6a2b3a5..8d20793 100644
--- a/components/password_manager/content/browser/credential_manager_dispatcher.cc
+++ b/components/password_manager/content/browser/credential_manager_dispatcher.cc
@@ -13,9 +13,9 @@
 #include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
 #include "components/password_manager/content/browser/credential_manager_password_form_manager.h"
 #include "components/password_manager/content/common/credential_manager_messages.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
diff --git a/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc b/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc
index 20085fe..11a6a2f 100644
--- a/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc
+++ b/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc
@@ -13,10 +13,10 @@
 #include "base/strings/utf_string_conversions.h"
 #include "components/password_manager/content/browser/credential_manager_password_form_manager.h"
 #include "components/password_manager/content/common/credential_manager_messages.h"
-#include "components/password_manager/content/common/credential_manager_types.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/browser/test_password_store.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/mock_render_process_host.h"
diff --git a/components/password_manager/content/common/BUILD.gn b/components/password_manager/content/common/BUILD.gn
index 88d4d12..dc0d9383 100644
--- a/components/password_manager/content/common/BUILD.gn
+++ b/components/password_manager/content/common/BUILD.gn
@@ -4,11 +4,11 @@
 
 static_library("common") {
   sources = [
+    "credential_manager_content_utils.cc",
+    "credential_manager_content_utils.h",
     "credential_manager_message_generator.cc",
     "credential_manager_message_generator.h",
     "credential_manager_messages.h",
-    "credential_manager_types.cc",
-    "credential_manager_types.h",
   ]
 
   deps = [
diff --git a/components/password_manager/content/common/credential_manager_content_utils.cc b/components/password_manager/content/common/credential_manager_content_utils.cc
new file mode 100644
index 0000000..49a24e80
--- /dev/null
+++ b/components/password_manager/content/common/credential_manager_content_utils.cc
@@ -0,0 +1,36 @@
+// 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 "components/password_manager/content/common/credential_manager_content_utils.h"
+
+#include "base/logging.h"
+#include "third_party/WebKit/public/platform/WebCredential.h"
+#include "third_party/WebKit/public/platform/WebFederatedCredential.h"
+#include "third_party/WebKit/public/platform/WebLocalCredential.h"
+
+namespace password_manager {
+
+CredentialInfo WebCredentialToCredentialInfo(
+    const blink::WebCredential& credential) {
+  CredentialInfo credential_info;
+  credential_info.id = credential.id();
+  credential_info.name = credential.name();
+  credential_info.avatar = credential.avatarURL();
+  credential_info.type = credential.isLocalCredential()
+                             ? CredentialType::CREDENTIAL_TYPE_LOCAL
+                             : CredentialType::CREDENTIAL_TYPE_FEDERATED;
+  if (credential_info.type == CredentialType::CREDENTIAL_TYPE_LOCAL) {
+    DCHECK(credential.isLocalCredential());
+    credential_info.password =
+        static_cast<const blink::WebLocalCredential&>(credential).password();
+  } else {
+    DCHECK(credential.isFederatedCredential());
+    credential_info.federation =
+        static_cast<const blink::WebFederatedCredential&>(credential)
+            .federation();
+  }
+  return credential_info;
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/content/common/credential_manager_content_utils.h b/components/password_manager/content/common/credential_manager_content_utils.h
new file mode 100644
index 0000000..77315f7
--- /dev/null
+++ b/components/password_manager/content/common/credential_manager_content_utils.h
@@ -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.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CONTENT_COMMON_CREDENTIAL_MANAGER_CONTENT_UTILS_H_
+#define COMPONENTS_PASSWORD_MANAGER_CONTENT_COMMON_CREDENTIAL_MANAGER_CONTENT_UTILS_H_
+
+#include "components/password_manager/core/common/credential_manager_types.h"
+
+namespace blink {
+class WebCredential;
+};
+
+namespace password_manager {
+
+// Returns a CredentialInfo struct populated from |credential|.
+CredentialInfo WebCredentialToCredentialInfo(
+    const blink::WebCredential& credential);
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CONTENT_COMMON_CREDENTIAL_MANAGER_CONTENT_UTILS_H_
diff --git a/components/password_manager/content/common/credential_manager_messages.h b/components/password_manager/content/common/credential_manager_messages.h
index 9acaa50..32ee1e4 100644
--- a/components/password_manager/content/common/credential_manager_messages.h
+++ b/components/password_manager/content/common/credential_manager_messages.h
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "base/strings/string16.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "content/public/common/common_param_traits.h"
 #include "content/public/common/common_param_traits_macros.h"
 #include "ipc/ipc_message_macros.h"
diff --git a/components/password_manager/content/renderer/credential_manager_client.cc b/components/password_manager/content/renderer/credential_manager_client.cc
index 7185096..9485ed1 100644
--- a/components/password_manager/content/renderer/credential_manager_client.cc
+++ b/components/password_manager/content/renderer/credential_manager_client.cc
@@ -4,8 +4,9 @@
 
 #include "components/password_manager/content/renderer/credential_manager_client.h"
 
+#include "components/password_manager/content/common/credential_manager_content_utils.h"
 #include "components/password_manager/content/common/credential_manager_messages.h"
-#include "components/password_manager/content/common/credential_manager_types.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 #include "content/public/renderer/render_view.h"
 #include "third_party/WebKit/public/platform/WebCredential.h"
 #include "third_party/WebKit/public/platform/WebCredentialManagerError.h"
@@ -116,7 +117,7 @@
     const blink::WebCredential& credential,
     blink::WebCredentialManagerClient::NotificationCallbacks* callbacks) {
   int request_id = failed_sign_in_callbacks_.Add(callbacks);
-  CredentialInfo info(credential);
+  CredentialInfo info(WebCredentialToCredentialInfo(credential));
   Send(new CredentialManagerHostMsg_NotifyFailedSignIn(
       routing_id(), request_id, info));
 }
@@ -125,7 +126,7 @@
     const blink::WebCredential& credential,
     blink::WebCredentialManagerClient::NotificationCallbacks* callbacks) {
   int request_id = signed_in_callbacks_.Add(callbacks);
-  CredentialInfo info(credential);
+  CredentialInfo info(WebCredentialToCredentialInfo(credential));
   Send(new CredentialManagerHostMsg_NotifySignedIn(
       routing_id(), request_id, info));
 }
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index acccc0d..ef53abb 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -35,9 +35,13 @@
     "affiliation_utils.h",
     "browser_save_password_progress_logger.cc",
     "browser_save_password_progress_logger.h",
+    "export/csv_writer.cc",
+    "export/csv_writer.h",
     "facet_manager.cc",
     "facet_manager.h",
     "facet_manager_host.h",
+    "import/csv_reader.cc",
+    "import/csv_reader.h",
     "log_receiver.h",
     "log_router.cc",
     "log_router.h",
@@ -92,6 +96,7 @@
     "//net",
     "//sql",
     "//third_party/protobuf:protobuf_lite",
+    "//third_party/re2",
     "//url",
   ]
 
diff --git a/components/password_manager/core/common/BUILD.gn b/components/password_manager/core/common/BUILD.gn
index 1505b7f..e280c64f 100644
--- a/components/password_manager/core/common/BUILD.gn
+++ b/components/password_manager/core/common/BUILD.gn
@@ -4,6 +4,8 @@
 
 static_library("common") {
   sources = [
+    "credential_manager_types.cc",
+    "credential_manager_types.h",
     "experiments.cc",
     "experiments.h",
     "password_manager_pref_names.cc",
diff --git a/components/password_manager/content/common/credential_manager_types.cc b/components/password_manager/core/common/credential_manager_types.cc
similarity index 66%
rename from components/password_manager/content/common/credential_manager_types.cc
rename to components/password_manager/core/common/credential_manager_types.cc
index 36aa18a..cc43c52 100644
--- a/components/password_manager/content/common/credential_manager_types.cc
+++ b/components/password_manager/core/common/credential_manager_types.cc
@@ -2,37 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/password_manager/content/common/credential_manager_types.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 
 #include "base/logging.h"
 #include "components/autofill/core/common/password_form.h"
-#include "third_party/WebKit/public/platform/WebCredential.h"
-#include "third_party/WebKit/public/platform/WebFederatedCredential.h"
-#include "third_party/WebKit/public/platform/WebLocalCredential.h"
 
 namespace password_manager {
 
 CredentialInfo::CredentialInfo() : type(CredentialType::CREDENTIAL_TYPE_EMPTY) {
 }
 
-CredentialInfo::CredentialInfo(const blink::WebCredential& credential)
-    : id(credential.id()),
-      name(credential.name()),
-      avatar(credential.avatarURL()) {
-  type = credential.isLocalCredential()
-             ? CredentialType::CREDENTIAL_TYPE_LOCAL
-             : CredentialType::CREDENTIAL_TYPE_FEDERATED;
-  if (type == CredentialType::CREDENTIAL_TYPE_LOCAL) {
-    DCHECK(credential.isLocalCredential());
-    password =
-        static_cast<const blink::WebLocalCredential&>(credential).password();
-  } else {
-    DCHECK(credential.isFederatedCredential());
-    federation = static_cast<const blink::WebFederatedCredential&>(credential)
-                     .federation();
-  }
-}
-
 CredentialInfo::CredentialInfo(const autofill::PasswordForm& form,
                                CredentialType form_type)
     : type(form_type),
diff --git a/components/password_manager/content/common/credential_manager_types.h b/components/password_manager/core/common/credential_manager_types.h
similarity index 79%
rename from components/password_manager/content/common/credential_manager_types.h
rename to components/password_manager/core/common/credential_manager_types.h
index 5fb8ee2..606ea28 100644
--- a/components/password_manager/content/common/credential_manager_types.h
+++ b/components/password_manager/core/common/credential_manager_types.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_PASSWORD_MANAGER_CONTENT_COMMON_CREDENTIAL_MANAGER_TYPES_H_
-#define COMPONENTS_PASSWORD_MANAGER_CONTENT_COMMON_CREDENTIAL_MANAGER_TYPES_H_
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_COMMON_CREDENTIAL_MANAGER_TYPES_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_COMMON_CREDENTIAL_MANAGER_TYPES_H_
 
 #include <string>
 
@@ -16,10 +16,6 @@
 struct PasswordForm;
 }
 
-namespace blink {
-class WebCredential;
-};
-
 namespace password_manager {
 
 // Limit the size of the federations array that we pass to the browser to
@@ -36,9 +32,7 @@
 
 struct CredentialInfo {
   CredentialInfo();
-  explicit CredentialInfo(const blink::WebCredential& credential);
-  explicit CredentialInfo(const autofill::PasswordForm& form,
-                          CredentialType form_type);
+  CredentialInfo(const autofill::PasswordForm& form, CredentialType form_type);
   ~CredentialInfo();
 
   CredentialType type;
@@ -71,4 +65,4 @@
 
 }  // namespace password_manager
 
-#endif  // COMPONENTS_PASSWORD_MANAGER_CONTENT_COMMON_CREDENTIAL_MANAGER_TYPES_H_
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_COMMON_CREDENTIAL_MANAGER_TYPES_H_
diff --git a/components/password_manager/content/common/credential_manager_types_unittest.cc b/components/password_manager/core/common/credential_manager_types_unittest.cc
similarity index 96%
rename from components/password_manager/content/common/credential_manager_types_unittest.cc
rename to components/password_manager/core/common/credential_manager_types_unittest.cc
index e21156d..8588599 100644
--- a/components/password_manager/content/common/credential_manager_types_unittest.cc
+++ b/components/password_manager/core/common/credential_manager_types_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/password_manager/content/common/credential_manager_types.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
 
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/components/plugins/renderer/loadable_plugin_placeholder.cc b/components/plugins/renderer/loadable_plugin_placeholder.cc
index f4e0369f..3719130 100644
--- a/components/plugins/renderer/loadable_plugin_placeholder.cc
+++ b/components/plugins/renderer/loadable_plugin_placeholder.cc
@@ -101,13 +101,13 @@
     return;
 
   plugin_marked_essential_ = true;
-  if (premade_throttler_) {
+  if (premade_throttler_)
     premade_throttler_->MarkPluginEssential(method);
-  }
+  else
+    PluginInstanceThrottler::RecordUnthrottleMethodMetric(method);
 
   if (is_blocked_for_power_saver_poster_) {
     is_blocked_for_power_saver_poster_ = false;
-    PluginInstanceThrottler::RecordUnthrottleMethodMetric(method);
     if (!LoadingBlocked())
       LoadPlugin();
   }
@@ -299,8 +299,8 @@
 #if defined(ENABLE_PLUGINS)
     // If the plugin has already been marked essential in its placeholder form,
     // we shouldn't create a new throttler and start the process all over again.
-    if (!plugin_marked_essential_)
-      throttler = PluginInstanceThrottler::Create(power_saver_enabled_);
+    if (!plugin_marked_essential_ && power_saver_enabled_)
+      throttler = PluginInstanceThrottler::Create();
 #endif
     WebPlugin* plugin = render_frame()->CreatePlugin(
         GetFrame(), plugin_info_, GetPluginParams(), throttler.Pass());
diff --git a/components/policy.gypi b/components/policy.gypi
index d6ae095..02d4c594 100644
--- a/components/policy.gypi
+++ b/components/policy.gypi
@@ -12,7 +12,6 @@
     'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/chrome',
     'policy_out_dir': '<(SHARED_INTERMEDIATE_DIR)/policy',
     'protoc_out_dir': '<(SHARED_INTERMEDIATE_DIR)/protoc_out',
-    'android_resources_out_dir': '<(policy_out_dir)/android_resources',
     'generate_policy_source_script_path':
         'policy/tools/generate_policy_source.py',
     'policy_constant_header_path':
@@ -22,9 +21,7 @@
     'protobuf_decoder_path':
         '<(policy_out_dir)/policy/cloud_policy_generated.cc',
     'app_restrictions_path':
-        '<(android_resources_out_dir)/xml-v21/app_restrictions.xml',
-    'app_resources_path':
-        '<(android_resources_out_dir)/values-v21/restriction_values.xml',
+        '<(policy_out_dir)/app_restrictions.xml',
     # This is the "full" protobuf, which defines one protobuf message per
     # policy. It is also the format currently used by the server.
     'chrome_settings_proto_path':
@@ -115,7 +112,6 @@
                 '<(chrome_settings_proto_path)',
                 '<(cloud_policy_proto_path)',
                 '<(app_restrictions_path)',
-                '<(app_resources_path)',
               ],
               'action_name': 'generate_policy_source',
               'action': [
@@ -127,7 +123,6 @@
                 '--cloud-policy-protobuf=<(cloud_policy_proto_path)',
                 '--cloud-policy-decoder=<(protobuf_decoder_path)',
                 '--app-restrictions-definition=<(app_restrictions_path)',
-                '--app-restrictions-resources=<(app_resources_path)',
                 '<(OS)',
                 '<(chromeos)',
                 'policy/resources/policy_templates.json',
@@ -137,7 +132,6 @@
                 ['OS!="android"', {
                   'outputs!': [
                     '<(app_restrictions_path)',
-                    '<(app_resources_path)',
                   ],
                 }],
               ],
@@ -372,7 +366,7 @@
         },
       ],
     }],
-    ['OS=="win" or OS=="mac" or OS=="linux"', {
+    ['OS!="ios"', {
       'targets': [
         {
           # policy_templates has different inputs and outputs, so it can't use
diff --git a/components/policy/BUILD.gn b/components/policy/BUILD.gn
index 31dc83e..24b424f 100644
--- a/components/policy/BUILD.gn
+++ b/components/policy/BUILD.gn
@@ -47,9 +47,6 @@
   # build puts everything into the following directory. We do the same for now.
   policy_gen_dir = "$root_gen_dir/policy"
 
-  # Directory for generating Android App Restrictions resources
-  android_resources_gen_dir = "$policy_gen_dir/android_resources"
-
   # This protobuf is equivalent to chrome_settings.proto but shares messages
   # for policies of the same type, so that less classes have to be generated
   # and compiled.
@@ -62,10 +59,7 @@
   constants_header_path = "$policy_gen_dir/policy_constants.h"
   constants_source_path = "$policy_gen_dir/policy_constants.cc"
   protobuf_decoder_path = "$policy_gen_dir/cloud_policy_generated.cc"
-  app_restrictions_path =
-      "$android_resources_gen_dir/xml-v21/app_restrictions.xml"
-  app_resources_path =
-      "$android_resources_gen_dir/values-v21/restriction_values.xml"
+  app_restrictions_path = "$policy_gen_dir/app_restrictions.xml"
 
   action("cloud_policy_code_generate") {
     script = "tools/generate_policy_source.py"
@@ -86,14 +80,10 @@
       chrome_settings_proto_path,
       cloud_policy_proto_path,
       app_restrictions_path,
-      app_resources_path,
     ]
 
-    if (current_os != "android") {
-      outputs -= [
-        app_restrictions_path,
-        app_resources_path,
-      ]
+    if (target_os != "android") {
+      outputs -= [ app_restrictions_path ]
     }
 
     args = [
@@ -109,14 +99,41 @@
           rebase_path(protobuf_decoder_path, root_build_dir),
       "--app-restrictions-definition=" +
           rebase_path(app_restrictions_path, root_build_dir),
-      "--app-restrictions-resources=" +
-          rebase_path(app_resources_path, root_build_dir),
-      current_os,
+      target_os,
       chromeos_flag,
       rebase_path("resources/policy_templates.json", root_build_dir),
     ]
   }
 
+  grit("policy_templates") {
+    import("resources/policy_templates.gni")
+    source = "resources/policy_templates.grd"
+    use_qualified_include = true
+    depend_on_stamp = true
+    output_dir = "$root_gen_dir/chrome"
+    outputs = policy_templates_doc_outputs
+    if (is_android) {
+      outputs += policy_templates_android_outputs
+    }
+    if (is_linux) {
+      outputs += policy_templates_linux_outputs
+    }
+    if (is_mac) {
+      outputs += policy_templates_mac_outputs
+
+      # TODO(knn) : Move this out once more targets use this.
+      if (is_chrome_branded) {
+        mac_bundle_id = "com.google.Chrome"
+      } else {
+        mac_bundle_id = "org.chromium.Chromium"
+      }
+      defines = [ "mac_bundle_id=$mac_bundle_id" ]
+    }
+    if (is_win) {
+      outputs += policy_templates_windows_outputs
+    }
+  }
+
   proto_library("cloud_policy_proto_generated_compile") {
     sources = [
       cloud_policy_proto_path,
@@ -220,4 +237,4 @@
     ]
   }
 }
-#TODO(GYP) policy templates, chrome_manifest_bundle
+#TODO(GYP) chrome_manifest_bundle
diff --git a/components/policy/core/browser/configuration_policy_handler.cc b/components/policy/core/browser/configuration_policy_handler.cc
index 5927ab4..e315b09 100644
--- a/components/policy/core/browser/configuration_policy_handler.cc
+++ b/components/policy/core/browser/configuration_policy_handler.cc
@@ -48,12 +48,6 @@
 void ConfigurationPolicyHandler::PrepareForDisplaying(
     PolicyMap* policies) const {}
 
-void ConfigurationPolicyHandler::ApplyPolicySettings(
-    const policy::PolicyMap& policies,
-    PrefValueMap* prefs) {
-  NOTREACHED();
-}
-
 void ConfigurationPolicyHandler::ApplyPolicySettingsWithParameters(
     const PolicyMap& policies,
     const PolicyHandlerParameters& parameters,
@@ -466,11 +460,13 @@
   }
 }
 
-void LegacyPoliciesDeprecatingPolicyHandler::ApplyPolicySettings(
-    const PolicyMap& policies,
+void LegacyPoliciesDeprecatingPolicyHandler::ApplyPolicySettingsWithParameters(
+    const policy::PolicyMap& policies,
+    const policy::PolicyHandlerParameters& parameters,
     PrefValueMap* prefs) {
   if (policies.Get(new_policy_handler_->policy_name())) {
-    new_policy_handler_->ApplyPolicySettings(policies, prefs);
+    new_policy_handler_->ApplyPolicySettingsWithParameters(policies, parameters,
+                                                           prefs);
   } else {
     // The new policy is not set, fall back to legacy ones.
     PolicyErrorMap scoped_errors;
@@ -478,10 +474,17 @@
     for (handler = legacy_policy_handlers_.begin();
          handler != legacy_policy_handlers_.end();
          ++handler) {
-      if ((*handler)->CheckPolicySettings(policies, &scoped_errors))
-        (*handler)->ApplyPolicySettings(policies, prefs);
+      if ((*handler)->CheckPolicySettings(policies, &scoped_errors)) {
+        (*handler)
+            ->ApplyPolicySettingsWithParameters(policies, parameters, prefs);
+      }
     }
   }
 }
+void LegacyPoliciesDeprecatingPolicyHandler::ApplyPolicySettings(
+    const policy::PolicyMap& /* policies */,
+    PrefValueMap* /* prefs */) {
+  NOTREACHED();
+}
 
 }  // namespace policy
diff --git a/components/policy/core/browser/configuration_policy_handler.h b/components/policy/core/browser/configuration_policy_handler.h
index c8101d7..1fdddc4 100644
--- a/components/policy/core/browser/configuration_policy_handler.h
+++ b/components/policy/core/browser/configuration_policy_handler.h
@@ -55,18 +55,21 @@
       const PolicyHandlerParameters& parameters,
       PrefValueMap* prefs);
 
-  // This is a convenience version of ApplyPolicySettingsWithParameters()
-  // that leaves out the |parameters|. Anyone extending
-  // ConfigurationPolicyHandler should implement either ApplyPolicySettings or
-  // ApplyPolicySettingsWithParameters.
-  virtual void ApplyPolicySettings(const PolicyMap& policies,
-                                   PrefValueMap* prefs);
-
   // Modifies the values of some of the policies in |policies| so that they
   // are more suitable to display to the user. This can be used to remove
   // sensitive values such as passwords, or to pretty-print values.
   virtual void PrepareForDisplaying(PolicyMap* policies) const;
 
+ protected:
+  // This is a convenience version of ApplyPolicySettingsWithParameters()
+  // for derived classes that leaves out the |parameters|. Anyone extending
+  // ConfigurationPolicyHandler should implement either
+  // ApplyPolicySettingsWithParameters directly and implement
+  // ApplyPolicySettings with a NOTREACHED or implement only
+  // ApplyPolicySettings.
+  virtual void ApplyPolicySettings(const PolicyMap& policies,
+                                   PrefValueMap* prefs) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyHandler);
 };
@@ -339,6 +342,12 @@
   // ConfigurationPolicyHandler:
   bool CheckPolicySettings(const PolicyMap& policies,
                            PolicyErrorMap* errors) override;
+  void ApplyPolicySettingsWithParameters(
+      const policy::PolicyMap& policies,
+      const policy::PolicyHandlerParameters& parameters,
+      PrefValueMap* prefs) override;
+
+ protected:
   void ApplyPolicySettings(const PolicyMap& policies,
                            PrefValueMap* prefs) override;
 
diff --git a/components/policy/resources/policy_templates.gni b/components/policy/resources/policy_templates.gni
new file mode 100644
index 0000000..786617b
--- /dev/null
+++ b/components/policy/resources/policy_templates.gni
@@ -0,0 +1,292 @@
+# 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.
+
+# This file contains the list of outputs from policy_templates.grd
+# for use within GN. It should always mirror the list in policy_templates.grd.
+
+policy_templates_base_dir = "$root_gen_dir/chrome/app/policy"
+
+policy_templates_android_outputs = [
+  "$policy_templates_base_dir/android/values-am-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-ar-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-bg-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-bn-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-ca-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-cs-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-da-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-de-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-el-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-en-rGB-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-es-rES-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-es-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-et-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-fa-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-fi-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-fil-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-fr-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-gu-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-he-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-hi-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-hr-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-hu-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-id-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-it-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-ja-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-kn-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-ko-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-lt-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-lv-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-ml-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-mr-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-ms-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-nb-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-nl-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-pl-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-pt-rBR-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-pt-rPT-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-ro-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-ru-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-sk-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-sl-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-sr-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-sv-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-sw-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-ta-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-te-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-th-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-tr-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-uk-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-vi-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-zh-rCN-v21/restriction_values.xml",
+  "$policy_templates_base_dir/android/values-zh-rTW-v21/restriction_values.xml",
+]
+
+policy_templates_doc_outputs = [
+  "$policy_templates_base_dir/common/html/am/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/ar/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/bg/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/bn/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/ca/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/cs/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/da/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/de/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/el/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/en-GB/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/en-US/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/es-419/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/es/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/et/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/fa/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/fi/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/fil/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/fr/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/gu/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/he/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/hi/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/hr/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/hu/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/id/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/it/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/ja/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/kn/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/ko/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/lt/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/lv/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/ml/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/mr/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/ms/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/nb/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/nl/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/pl/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/pt-BR/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/pt-PT/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/ro/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/ru/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/sk/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/sl/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/sr/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/sv/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/sw/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/ta/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/te/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/th/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/tr/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/uk/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/vi/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/zh-CN/chrome_policy_list.html",
+  "$policy_templates_base_dir/common/html/zh-TW/chrome_policy_list.html",
+]
+
+policy_templates_linux_outputs =
+    [ "$policy_templates_base_dir/linux/examples/chrome.json" ]
+
+policy_templates_mac_outputs = [
+  "$policy_templates_base_dir/ios/chrome_policy.plist",
+  "$policy_templates_base_dir/mac/app-Manifest.plist",
+  "$policy_templates_base_dir/mac/strings/am.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/ar.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/bg.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/bn.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/ca.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/cs.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/da.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/de.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/el.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/en.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/en_GB.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/es.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/es_419.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/et.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/fa.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/fi.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/fil.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/fr.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/gu.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/he.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/hi.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/hr.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/hu.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/id.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/it.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/ja.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/kn.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/ko.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/lt.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/lv.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/ml.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/mr.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/ms.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/nb.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/nl.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/pl.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/pt_BR.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/pt_PT.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/ro.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/ru.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/sk.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/sl.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/sr.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/sv.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/sw.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/ta.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/te.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/th.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/tr.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/uk.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/vi.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/zh_CN.lproj/Localizable.strings",
+  "$policy_templates_base_dir/mac/strings/zh_TW.lproj/Localizable.strings",
+]
+
+policy_templates_windows_outputs = [
+  "$policy_templates_base_dir/windows/adm/am/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/ar/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/bg/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/bn/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/ca/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/cs/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/da/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/de/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/el/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/en-GB/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/en-US/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/es-419/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/es/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/et/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/fa/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/fi/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/fil/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/fr/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/gu/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/he/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/hi/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/hr/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/hu/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/id/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/it/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/ja/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/kn/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/ko/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/lt/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/lv/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/ml/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/mr/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/ms/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/nb/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/nl/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/pl/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/pt-BR/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/pt-PT/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/ro/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/ru/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/sk/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/sl/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/sr/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/sv/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/sw/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/ta/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/te/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/th/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/tr/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/uk/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/vi/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/zh-CN/chrome.adm",
+  "$policy_templates_base_dir/windows/adm/zh-TW/chrome.adm",
+  "$policy_templates_base_dir/windows/admx/am/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/ar/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/bg/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/bn/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/ca/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/chrome.admx",
+  "$policy_templates_base_dir/windows/admx/cs/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/da/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/de/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/el/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/en-GB/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/en-US/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/es-419/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/es/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/et/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/fa/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/fi/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/fil/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/fr/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/gu/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/he/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/hi/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/hr/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/hu/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/id/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/it/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/ja/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/kn/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/ko/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/lt/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/lv/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/ml/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/mr/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/ms/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/nb/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/nl/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/pl/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/pt-BR/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/pt-PT/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/ro/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/ru/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/sk/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/sl/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/sr/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/sv/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/sw/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/ta/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/te/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/th/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/tr/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/uk/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/vi/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/zh-CN/chrome.adml",
+  "$policy_templates_base_dir/windows/admx/zh-TW/chrome.adml",
+  "$policy_templates_base_dir/windows/examples/chrome.reg",
+]
diff --git a/components/policy/resources/policy_templates.grd b/components/policy/resources/policy_templates.grd
index cd26190..2fb729d 100644
--- a/components/policy/resources/policy_templates.grd
+++ b/components/policy/resources/policy_templates.grd
@@ -6,7 +6,60 @@
 <grit base_dir="." latest_public_release="0" current_release="1"
       source_lang_id="en" enc_check="möl">
   <outputs>
-    <output filename="app/policy/linux/examples/chrome.json" type="json" lang="en" />
+    <output filename="app/policy/common/html/am/chrome_policy_list.html" type="doc" lang="am" />
+    <output filename="app/policy/common/html/ar/chrome_policy_list.html" type="doc" lang="ar" />
+    <output filename="app/policy/common/html/bg/chrome_policy_list.html" type="doc" lang="bg" />
+    <output filename="app/policy/common/html/bn/chrome_policy_list.html" type="doc" lang="bn" />
+    <output filename="app/policy/common/html/ca/chrome_policy_list.html" type="doc" lang="ca" />
+    <output filename="app/policy/common/html/cs/chrome_policy_list.html" type="doc" lang="cs" />
+    <output filename="app/policy/common/html/da/chrome_policy_list.html" type="doc" lang="da" />
+    <output filename="app/policy/common/html/de/chrome_policy_list.html" type="doc" lang="de" />
+    <output filename="app/policy/common/html/el/chrome_policy_list.html" type="doc" lang="el" />
+    <output filename="app/policy/common/html/en-GB/chrome_policy_list.html" type="doc" lang="en-GB" />
+    <output filename="app/policy/common/html/en-US/chrome_policy_list.html" type="doc" lang="en" />
+    <output filename="app/policy/common/html/es/chrome_policy_list.html" type="doc" lang="es" />
+    <output filename="app/policy/common/html/es-419/chrome_policy_list.html" type="doc" lang="es-419" />
+    <output filename="app/policy/common/html/et/chrome_policy_list.html" type="doc" lang="et" />
+    <output filename="app/policy/common/html/fa/chrome_policy_list.html" type="doc" lang="fa" />
+    <output filename="app/policy/common/html/fi/chrome_policy_list.html" type="doc" lang="fi" />
+    <output filename="app/policy/common/html/fil/chrome_policy_list.html" type="doc" lang="fil" />
+    <output filename="app/policy/common/html/fr/chrome_policy_list.html" type="doc" lang="fr" />
+    <output filename="app/policy/common/html/gu/chrome_policy_list.html" type="doc" lang="gu" />
+    <output filename="app/policy/common/html/he/chrome_policy_list.html" type="doc" lang="he" />
+    <output filename="app/policy/common/html/hi/chrome_policy_list.html" type="doc" lang="hi" />
+    <output filename="app/policy/common/html/hr/chrome_policy_list.html" type="doc" lang="hr" />
+    <output filename="app/policy/common/html/hu/chrome_policy_list.html" type="doc" lang="hu" />
+    <output filename="app/policy/common/html/id/chrome_policy_list.html" type="doc" lang="id" />
+    <output filename="app/policy/common/html/it/chrome_policy_list.html" type="doc" lang="it" />
+    <output filename="app/policy/common/html/ja/chrome_policy_list.html" type="doc" lang="ja" />
+    <output filename="app/policy/common/html/kn/chrome_policy_list.html" type="doc" lang="kn" />
+    <output filename="app/policy/common/html/ko/chrome_policy_list.html" type="doc" lang="ko" />
+    <output filename="app/policy/common/html/lt/chrome_policy_list.html" type="doc" lang="lt" />
+    <output filename="app/policy/common/html/lv/chrome_policy_list.html" type="doc" lang="lv" />
+    <output filename="app/policy/common/html/ml/chrome_policy_list.html" type="doc" lang="ml" />
+    <output filename="app/policy/common/html/mr/chrome_policy_list.html" type="doc" lang="mr" />
+    <output filename="app/policy/common/html/ms/chrome_policy_list.html" type="doc" lang="ms" />
+    <output filename="app/policy/common/html/nl/chrome_policy_list.html" type="doc" lang="nl" />
+    <output filename="app/policy/common/html/nb/chrome_policy_list.html" type="doc" lang="no" />
+    <!-- 'no' for Norwegian Bokmål. It should be 'nb'. -->
+    <output filename="app/policy/common/html/pl/chrome_policy_list.html" type="doc" lang="pl" />
+    <output filename="app/policy/common/html/pt-BR/chrome_policy_list.html" type="doc" lang="pt-BR" />
+    <output filename="app/policy/common/html/pt-PT/chrome_policy_list.html" type="doc" lang="pt-PT" />
+    <output filename="app/policy/common/html/ro/chrome_policy_list.html" type="doc" lang="ro" />
+    <output filename="app/policy/common/html/ru/chrome_policy_list.html" type="doc" lang="ru" />
+    <output filename="app/policy/common/html/sk/chrome_policy_list.html" type="doc" lang="sk" />
+    <output filename="app/policy/common/html/sl/chrome_policy_list.html" type="doc" lang="sl" />
+    <output filename="app/policy/common/html/sr/chrome_policy_list.html" type="doc" lang="sr" />
+    <output filename="app/policy/common/html/sv/chrome_policy_list.html" type="doc" lang="sv" />
+    <output filename="app/policy/common/html/sw/chrome_policy_list.html" type="doc" lang="sw" />
+    <output filename="app/policy/common/html/ta/chrome_policy_list.html" type="doc" lang="ta" />
+    <output filename="app/policy/common/html/te/chrome_policy_list.html" type="doc" lang="te" />
+    <output filename="app/policy/common/html/th/chrome_policy_list.html" type="doc" lang="th" />
+    <output filename="app/policy/common/html/tr/chrome_policy_list.html" type="doc" lang="tr" />
+    <output filename="app/policy/common/html/uk/chrome_policy_list.html" type="doc" lang="uk" />
+    <output filename="app/policy/common/html/vi/chrome_policy_list.html" type="doc" lang="vi" />
+    <output filename="app/policy/common/html/zh-CN/chrome_policy_list.html" type="doc" lang="zh-CN" />
+    <output filename="app/policy/common/html/zh-TW/chrome_policy_list.html" type="doc" lang="zh-TW" />
     <if expr="is_win">
       <output filename="app/policy/windows/examples/chrome.reg" type="reg" lang="en" />
       <output filename="app/policy/windows/admx/chrome.admx" type="admx" lang="en" />
@@ -121,62 +174,65 @@
       <output filename="app/policy/windows/adm/zh-CN/chrome.adm" type="adm" lang="zh-CN" />
       <output filename="app/policy/windows/adm/zh-TW/chrome.adm" type="adm" lang="zh-TW" />
     </if>
-
-    <output filename="app/policy/common/html/am/chrome_policy_list.html" type="doc" lang="am" />
-    <output filename="app/policy/common/html/ar/chrome_policy_list.html" type="doc" lang="ar" />
-    <output filename="app/policy/common/html/bg/chrome_policy_list.html" type="doc" lang="bg" />
-    <output filename="app/policy/common/html/bn/chrome_policy_list.html" type="doc" lang="bn" />
-    <output filename="app/policy/common/html/ca/chrome_policy_list.html" type="doc" lang="ca" />
-    <output filename="app/policy/common/html/cs/chrome_policy_list.html" type="doc" lang="cs" />
-    <output filename="app/policy/common/html/da/chrome_policy_list.html" type="doc" lang="da" />
-    <output filename="app/policy/common/html/de/chrome_policy_list.html" type="doc" lang="de" />
-    <output filename="app/policy/common/html/el/chrome_policy_list.html" type="doc" lang="el" />
-    <output filename="app/policy/common/html/en-GB/chrome_policy_list.html" type="doc" lang="en-GB" />
-    <output filename="app/policy/common/html/en-US/chrome_policy_list.html" type="doc" lang="en" />
-    <output filename="app/policy/common/html/es/chrome_policy_list.html" type="doc" lang="es" />
-    <output filename="app/policy/common/html/es-419/chrome_policy_list.html" type="doc" lang="es-419" />
-    <output filename="app/policy/common/html/et/chrome_policy_list.html" type="doc" lang="et" />
-    <output filename="app/policy/common/html/fa/chrome_policy_list.html" type="doc" lang="fa" />
-    <output filename="app/policy/common/html/fi/chrome_policy_list.html" type="doc" lang="fi" />
-    <output filename="app/policy/common/html/fil/chrome_policy_list.html" type="doc" lang="fil" />
-    <output filename="app/policy/common/html/fr/chrome_policy_list.html" type="doc" lang="fr" />
-    <output filename="app/policy/common/html/gu/chrome_policy_list.html" type="doc" lang="gu" />
-    <output filename="app/policy/common/html/he/chrome_policy_list.html" type="doc" lang="he" />
-    <output filename="app/policy/common/html/hi/chrome_policy_list.html" type="doc" lang="hi" />
-    <output filename="app/policy/common/html/hr/chrome_policy_list.html" type="doc" lang="hr" />
-    <output filename="app/policy/common/html/hu/chrome_policy_list.html" type="doc" lang="hu" />
-    <output filename="app/policy/common/html/id/chrome_policy_list.html" type="doc" lang="id" />
-    <output filename="app/policy/common/html/it/chrome_policy_list.html" type="doc" lang="it" />
-    <output filename="app/policy/common/html/ja/chrome_policy_list.html" type="doc" lang="ja" />
-    <output filename="app/policy/common/html/kn/chrome_policy_list.html" type="doc" lang="kn" />
-    <output filename="app/policy/common/html/ko/chrome_policy_list.html" type="doc" lang="ko" />
-    <output filename="app/policy/common/html/lt/chrome_policy_list.html" type="doc" lang="lt" />
-    <output filename="app/policy/common/html/lv/chrome_policy_list.html" type="doc" lang="lv" />
-    <output filename="app/policy/common/html/ml/chrome_policy_list.html" type="doc" lang="ml" />
-    <output filename="app/policy/common/html/mr/chrome_policy_list.html" type="doc" lang="mr" />
-    <output filename="app/policy/common/html/ms/chrome_policy_list.html" type="doc" lang="ms" />
-    <output filename="app/policy/common/html/nl/chrome_policy_list.html" type="doc" lang="nl" />
-    <output filename="app/policy/common/html/nb/chrome_policy_list.html" type="doc" lang="no" />
-    <!-- 'no' for Norwegian Bokmål. It should be 'nb'. -->
-    <output filename="app/policy/common/html/pl/chrome_policy_list.html" type="doc" lang="pl" />
-    <output filename="app/policy/common/html/pt-BR/chrome_policy_list.html" type="doc" lang="pt-BR" />
-    <output filename="app/policy/common/html/pt-PT/chrome_policy_list.html" type="doc" lang="pt-PT" />
-    <output filename="app/policy/common/html/ro/chrome_policy_list.html" type="doc" lang="ro" />
-    <output filename="app/policy/common/html/ru/chrome_policy_list.html" type="doc" lang="ru" />
-    <output filename="app/policy/common/html/sk/chrome_policy_list.html" type="doc" lang="sk" />
-    <output filename="app/policy/common/html/sl/chrome_policy_list.html" type="doc" lang="sl" />
-    <output filename="app/policy/common/html/sr/chrome_policy_list.html" type="doc" lang="sr" />
-    <output filename="app/policy/common/html/sv/chrome_policy_list.html" type="doc" lang="sv" />
-    <output filename="app/policy/common/html/sw/chrome_policy_list.html" type="doc" lang="sw" />
-    <output filename="app/policy/common/html/ta/chrome_policy_list.html" type="doc" lang="ta" />
-    <output filename="app/policy/common/html/te/chrome_policy_list.html" type="doc" lang="te" />
-    <output filename="app/policy/common/html/th/chrome_policy_list.html" type="doc" lang="th" />
-    <output filename="app/policy/common/html/tr/chrome_policy_list.html" type="doc" lang="tr" />
-    <output filename="app/policy/common/html/uk/chrome_policy_list.html" type="doc" lang="uk" />
-    <output filename="app/policy/common/html/vi/chrome_policy_list.html" type="doc" lang="vi" />
-    <output filename="app/policy/common/html/zh-CN/chrome_policy_list.html" type="doc" lang="zh-CN" />
-    <output filename="app/policy/common/html/zh-TW/chrome_policy_list.html" type="doc" lang="zh-TW" />
-
+    <if expr="is_linux">
+      <output filename="app/policy/linux/examples/chrome.json" type="json" lang="en" />
+    </if>
+    <if expr="is_android">
+      <output filename="app/policy/android/values-am-v21/restriction_values.xml" type="android_policy" lang="am" />
+      <output filename="app/policy/android/values-ar-v21/restriction_values.xml" type="android_policy" lang="ar" />
+      <output filename="app/policy/android/values-bg-v21/restriction_values.xml" type="android_policy" lang="bg" />
+      <output filename="app/policy/android/values-bn-v21/restriction_values.xml" type="android_policy" lang="bn" />
+      <output filename="app/policy/android/values-ca-v21/restriction_values.xml" type="android_policy" lang="ca" />
+      <output filename="app/policy/android/values-cs-v21/restriction_values.xml" type="android_policy" lang="cs" />
+      <output filename="app/policy/android/values-da-v21/restriction_values.xml" type="android_policy" lang="da" />
+      <output filename="app/policy/android/values-de-v21/restriction_values.xml" type="android_policy" lang="de" />
+      <output filename="app/policy/android/values-el-v21/restriction_values.xml" type="android_policy" lang="el" />
+      <output filename="app/policy/android/values-en-rGB-v21/restriction_values.xml" type="android_policy" lang="en-GB" />
+      <output filename="app/policy/android/values-v21/restriction_values.xml" type="android_policy" lang="en" />
+      <output filename="app/policy/android/values-es-rES-v21/restriction_values.xml" type="android_policy" lang="es" />
+      <output filename="app/policy/android/values-es-v21/restriction_values.xml" type="android_policy" lang="es-419" />
+      <output filename="app/policy/android/values-et-v21/restriction_values.xml" type="android_policy" lang="et" />
+      <output filename="app/policy/android/values-fa-v21/restriction_values.xml" type="android_policy" lang="fa" />
+      <output filename="app/policy/android/values-fi-v21/restriction_values.xml" type="android_policy" lang="fi" />
+      <output filename="app/policy/android/values-fil-v21/restriction_values.xml" type="android_policy" lang="fil" />
+      <output filename="app/policy/android/values-fr-v21/restriction_values.xml" type="android_policy" lang="fr" />
+      <output filename="app/policy/android/values-gu-v21/restriction_values.xml" type="android_policy" lang="gu" />
+      <output filename="app/policy/android/values-he-v21/restriction_values.xml" type="android_policy" lang="he" />
+      <output filename="app/policy/android/values-hi-v21/restriction_values.xml" type="android_policy" lang="hi" />
+      <output filename="app/policy/android/values-hr-v21/restriction_values.xml" type="android_policy" lang="hr" />
+      <output filename="app/policy/android/values-hu-v21/restriction_values.xml" type="android_policy" lang="hu" />
+      <output filename="app/policy/android/values-id-v21/restriction_values.xml" type="android_policy" lang="id" />
+      <output filename="app/policy/android/values-it-v21/restriction_values.xml" type="android_policy" lang="it" />
+      <output filename="app/policy/android/values-ja-v21/restriction_values.xml" type="android_policy" lang="ja" />
+      <output filename="app/policy/android/values-kn-v21/restriction_values.xml" type="android_policy" lang="kn" />
+      <output filename="app/policy/android/values-ko-v21/restriction_values.xml" type="android_policy" lang="ko" />
+      <output filename="app/policy/android/values-lt-v21/restriction_values.xml" type="android_policy" lang="lt" />
+      <output filename="app/policy/android/values-lv-v21/restriction_values.xml" type="android_policy" lang="lv" />
+      <output filename="app/policy/android/values-ml-v21/restriction_values.xml" type="android_policy" lang="ml" />
+      <output filename="app/policy/android/values-mr-v21/restriction_values.xml" type="android_policy" lang="mr" />
+      <output filename="app/policy/android/values-ms-v21/restriction_values.xml" type="android_policy" lang="ms" />
+      <output filename="app/policy/android/values-nl-v21/restriction_values.xml" type="android_policy" lang="nl" />
+      <output filename="app/policy/android/values-nb-v21/restriction_values.xml" type="android_policy" lang="no" />
+      <!-- 'no' for Norwegian Bokmål. It should be 'nb'. -->
+      <output filename="app/policy/android/values-pl-v21/restriction_values.xml" type="android_policy" lang="pl" />
+      <output filename="app/policy/android/values-pt-rBR-v21/restriction_values.xml" type="android_policy" lang="pt-BR" />
+      <output filename="app/policy/android/values-pt-rPT-v21/restriction_values.xml" type="android_policy" lang="pt-PT" />
+      <output filename="app/policy/android/values-ro-v21/restriction_values.xml" type="android_policy" lang="ro" />
+      <output filename="app/policy/android/values-ru-v21/restriction_values.xml" type="android_policy" lang="ru" />
+      <output filename="app/policy/android/values-sk-v21/restriction_values.xml" type="android_policy" lang="sk" />
+      <output filename="app/policy/android/values-sl-v21/restriction_values.xml" type="android_policy" lang="sl" />
+      <output filename="app/policy/android/values-sr-v21/restriction_values.xml" type="android_policy" lang="sr" />
+      <output filename="app/policy/android/values-sv-v21/restriction_values.xml" type="android_policy" lang="sv" />
+      <output filename="app/policy/android/values-sw-v21/restriction_values.xml" type="android_policy" lang="sw" />
+      <output filename="app/policy/android/values-ta-v21/restriction_values.xml" type="android_policy" lang="ta" />
+      <output filename="app/policy/android/values-te-v21/restriction_values.xml" type="android_policy" lang="te" />
+      <output filename="app/policy/android/values-th-v21/restriction_values.xml" type="android_policy" lang="th" />
+      <output filename="app/policy/android/values-tr-v21/restriction_values.xml" type="android_policy" lang="tr" />
+      <output filename="app/policy/android/values-uk-v21/restriction_values.xml" type="android_policy" lang="uk" />
+      <output filename="app/policy/android/values-vi-v21/restriction_values.xml" type="android_policy" lang="vi" />
+      <output filename="app/policy/android/values-zh-rCN-v21/restriction_values.xml" type="android_policy" lang="zh-CN" />
+      <output filename="app/policy/android/values-zh-rTW-v21/restriction_values.xml" type="android_policy" lang="zh-TW" />
+    </if>
     <if expr="is_macosx">
       <output filename="app/policy/mac/app-Manifest.plist" type="plist" lang="en" />
 
@@ -239,8 +295,7 @@
 
       <!-- Generate the sample iOS plist when building the templates for the Mac. -->
       <output filename="app/policy/ios/chrome_policy.plist" type="ios_plist" lang="en" />
-   </if>
-
+    </if>
   </outputs>
   <translations>
     <file path="policy_templates_am.xtb" lang="am" />
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 078cc451..618eee8 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -7327,6 +7327,10 @@
       'desc': '''Caption text of the field 'mac/linux preference name' in the summary chart of a policy in the generated documentation''',
       'text': '''Mac/Linux preference name:'''
     },
+    'doc_android_restriction_name': {
+      'desc': '''Caption text of the field 'android restriction name' in the summary chart of a policy in the generated documentation''',
+      'text': '''Android restriction name:'''
+    },
     'doc_supported_on': {
       'desc': '''Caption text of the list of 'products, platforms and versions where this policy is supported' in the summary chart of a policy in the generated documentation''',
       'text': '''Supported on:'''
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index e343e337..b3a723b 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -28,34 +28,33 @@
 class PolicyDetails:
   """Parses a policy template and caches all its details."""
 
-  # Maps policy types to a tuple with 5 other types:
+  # Maps policy types to a tuple with 4 other types:
   # - the equivalent base::Value::Type or 'TYPE_EXTERNAL' if the policy
   #   references external data
   # - the equivalent Protobuf field type
   # - the name of one of the protobufs for shared policy types
   # - the equivalent type in Android's App Restriction Schema
-  # - whether the equivalent app restriction type needs supporting resources
   # TODO(joaodasilva): refactor the 'dict' type into a more generic 'json' type
   # that can also be used to represent lists of other JSON objects.
   TYPE_MAP = {
     'dict':             ('TYPE_DICTIONARY',   'string',       'String',
-                        'string',             False),
+                        'string'),
     'external':         ('TYPE_EXTERNAL',     'string',       'String',
-                        'invalid',            False),
+                        'invalid'),
     'int':              ('TYPE_INTEGER',      'int64',        'Integer',
-                        'integer',            False),
+                        'integer'),
     'int-enum':         ('TYPE_INTEGER',      'int64',        'Integer',
-                        'choice',             True),
+                        'choice'),
     'list':             ('TYPE_LIST',         'StringList',   'StringList',
-                        'string',             False),
+                        'string'),
     'main':             ('TYPE_BOOLEAN',      'bool',         'Boolean',
-                        'bool',               False),
+                        'bool'),
     'string':           ('TYPE_STRING',       'string',       'String',
-                        'string',             False),
+                        'string'),
     'string-enum':      ('TYPE_STRING',       'string',       'String',
-                        'choice',             True),
+                        'choice'),
     'string-enum-list': ('TYPE_LIST',         'StringList',   'StringList',
-                        'multi-select',       True),
+                        'multi-select'),
   }
 
   class EnumItem:
@@ -98,8 +97,7 @@
       raise NotImplementedError('Unknown policy type for %s: %s' %
                                 (policy['name'], policy['type']))
     self.policy_type, self.protobuf_type, self.policy_protobuf_type, \
-        self.restriction_type, self.has_restriction_resources = \
-            PolicyDetails.TYPE_MAP[policy['type']]
+        self.restriction_type = PolicyDetails.TYPE_MAP[policy['type']]
     self.schema = policy['schema']
 
     self.desc = '\n'.join(
@@ -154,12 +152,6 @@
                     help='generate an XML file as specified by '
                     'Android\'s App Restriction Schema',
                     metavar='FILE')
-  parser.add_option('--arr', '--app-restrictions-resources',
-                    dest='app_resources_path',
-                    help='generate an XML file with resources supporting the '
-                    'restrictions defined in --app-restrictions-definition '
-                    'parameter',
-                    metavar='FILE')
 
   (opts, args) = parser.parse_args()
 
@@ -191,7 +183,6 @@
 
   if os == 'android':
     GenerateFile(opts.app_restrictions_path, _WriteAppRestrictions, xml=True)
-    GenerateFile(opts.app_resources_path, _WriteResourcesForPolicies, xml=True)
 
   return 0
 
@@ -1007,14 +998,6 @@
   f.write(CPP_FOOT)
 
 
-def _EscapeResourceString(raw_resource):
-  if type(raw_resource) == int:
-    return raw_resource
-  return xml_escape(raw_resource)\
-      .replace('\\', '\\\\')\
-      .replace('\"','\\\"')\
-      .replace('\'','\\\'')
-
 def _WriteAppRestrictions(policies, os, f):
 
   def WriteRestrictionCommon(key):
@@ -1031,7 +1014,7 @@
     policy_name = policy.name
     WriteRestrictionCommon(policy_name)
 
-    if policy.has_restriction_resources:
+    if policy.items is not None:
       WriteItemsDefinition(policy_name)
 
     f.write('        android:restrictionType="%s"/>' % policy.restriction_type)
@@ -1045,44 +1028,5 @@
       WriteAppRestriction(policy)
   f.write('</restrictions>')
 
-
-def _WriteResourcesForPolicies(policies, os, f):
-
-  # TODO(knn): Update this to support i18n.
-  def WriteString(key, value):
-    f.write('    <string name="%s">%s</string>\n'
-            % (key, _EscapeResourceString(value)))
-
-  def WriteItems(key, items):
-    if items:
-      f.write('    <string-array name="%sEntries">\n' % key)
-      for item in items:
-        f.write('        <item>%s</item>\n' %
-                _EscapeResourceString(item.caption))
-      f.write('    </string-array>\n')
-      f.write('    <string-array name="%sValues">\n' % key)
-      for item in items:
-        f.write('        <item>%s</item>\n' % _EscapeResourceString(item.value))
-      f.write('    </string-array>\n')
-
-  def WriteResourceForPolicy(policy):
-    policy_name = policy.name
-    WriteString(policy_name + 'Title', policy.caption)
-
-    # Get the first line of the policy description.
-    description = policy.desc.split('\n', 1)[0]
-    WriteString(policy_name + 'Desc', description)
-
-    if policy.has_restriction_resources:
-      WriteItems(policy_name, policy.items)
-
-  # _WriteResourcesForPolicies body
-  f.write('<resources>\n\n')
-  for policy in policies:
-    if policy.is_supported and policy.restriction_type != 'invalid':
-      WriteResourceForPolicy(policy)
-  f.write('</resources>')
-
-
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/components/precache/content/precache_manager.cc b/components/precache/content/precache_manager.cc
index 62cf590..08f9cb8 100644
--- a/components/precache/content/precache_manager.cc
+++ b/components/precache/content/precache_manager.cc
@@ -55,7 +55,7 @@
 }
 
 bool PrecacheManager::IsPrecachingAllowed() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return user_prefs::UserPrefs::Get(browser_context_)->GetBoolean(
       data_reduction_proxy::prefs::kDataReductionProxyEnabled);
 }
@@ -63,7 +63,7 @@
 void PrecacheManager::StartPrecaching(
     const PrecacheCompletionCallback& precache_completion_callback,
     URLListProvider* url_list_provider) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (is_precaching_) {
     DLOG(WARNING) << "Cannot start precaching because precaching is already "
@@ -84,7 +84,7 @@
 }
 
 void PrecacheManager::CancelPrecaching() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!is_precaching_) {
     // Do nothing if precaching is not in progress.
@@ -100,7 +100,7 @@
 }
 
 bool PrecacheManager::IsPrecaching() const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return is_precaching_;
 }
 
@@ -108,7 +108,7 @@
                                           const base::Time& fetch_time,
                                           int64 size,
                                           bool was_cached) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (size == 0 || url.is_empty() || !url.SchemeIsHTTPOrHTTPS()) {
     // Ignore empty responses, empty URLs, or URLs that aren't HTTP or HTTPS.
@@ -142,7 +142,7 @@
 }
 
 void PrecacheManager::OnDone() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // If OnDone has been called, then we should just be finishing precaching.
   DCHECK(is_precaching_);
@@ -156,7 +156,7 @@
 }
 
 void PrecacheManager::OnURLsReceived(const std::list<GURL>& urls) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!is_precaching_) {
     // Don't start precaching if it was canceled while waiting for the list of
diff --git a/components/renderer_context_menu/context_menu_content_type.h b/components/renderer_context_menu/context_menu_content_type.h
index 81003dd..c1e6a74 100644
--- a/components/renderer_context_menu/context_menu_content_type.h
+++ b/components/renderer_context_menu/context_menu_content_type.h
@@ -70,6 +70,10 @@
 
   const extensions::Extension* GetExtension() const;
 
+  const content::WebContents* source_web_contents() const {
+    return source_web_contents_;
+  }
+
  private:
   bool SupportsGroupInternal(int group);
 
diff --git a/components/signin/core/browser/about_signin_internals.cc b/components/signin/core/browser/about_signin_internals.cc
index a693d5e..c6294fa 100644
--- a/components/signin/core/browser/about_signin_internals.cc
+++ b/components/signin/core/browser/about_signin_internals.cc
@@ -134,11 +134,13 @@
 AboutSigninInternals::AboutSigninInternals(
     ProfileOAuth2TokenService* token_service,
     AccountTrackerService* account_tracker,
-    SigninManagerBase* signin_manager)
+    SigninManagerBase* signin_manager,
+    SigninErrorController* signin_error_controller)
     : token_service_(token_service),
       account_tracker_(account_tracker),
       signin_manager_(signin_manager),
-      client_(NULL) {}
+      client_(NULL),
+      signin_error_controller_(signin_error_controller) {}
 
 AboutSigninInternals::~AboutSigninInternals() {}
 
@@ -206,6 +208,7 @@
 
   RefreshSigninPrefs();
 
+  signin_error_controller_->AddObserver(this);
   signin_manager_->AddSigninDiagnosticsObserver(this);
   token_service_->AddDiagnosticsObserver(this);
   cookie_changed_subscription_ = client_->AddCookieChangedCallback(
@@ -216,6 +219,7 @@
 }
 
 void AboutSigninInternals::Shutdown() {
+  signin_error_controller_->RemoveObserver(this);
   signin_manager_->RemoveSigninDiagnosticsObserver(this);
   token_service_->RemoveDiagnosticsObserver(this);
   cookie_changed_subscription_.reset();
@@ -240,7 +244,9 @@
           "422460 AboutSigninInternals::NotifyObservers 0.5"));
 
   scoped_ptr<base::DictionaryValue> signin_status_value =
-      signin_status_.ToValue(account_tracker_, signin_manager_,
+      signin_status_.ToValue(account_tracker_,
+                             signin_manager_,
+                             signin_error_controller_,
                              product_version);
 
   // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
@@ -255,7 +261,9 @@
 }
 
 scoped_ptr<base::DictionaryValue> AboutSigninInternals::GetSigninStatus() {
-  return signin_status_.ToValue(account_tracker_, signin_manager_,
+  return signin_status_.ToValue(account_tracker_,
+                                signin_manager_,
+                                signin_error_controller_,
                                 client_->GetProductVersion()).Pass();
 }
 
@@ -328,6 +336,10 @@
   }
 }
 
+void AboutSigninInternals::OnErrorChanged() {
+  NotifyObservers();
+}
+
 void AboutSigninInternals::GetCookieAccountsAsync() {
   // Don't bother calling /ListAccounts if no one will observe the response.
   if (!gaia_fetcher_ && signin_observers_.might_have_observers()) {
@@ -479,6 +491,7 @@
 scoped_ptr<base::DictionaryValue> AboutSigninInternals::SigninStatus::ToValue(
     AccountTrackerService* account_tracker,
     SigninManagerBase* signin_manager,
+    SigninErrorController* signin_error_controller,
     const std::string& product_version) {
   // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
   // fixed.
@@ -524,6 +537,14 @@
                     SigninStatusFieldToLabel(
                         static_cast<UntimedSigninStatusField>(USERNAME)),
                     signin_manager->GetAuthenticatedUsername());
+    if (signin_error_controller->HasError()) {
+      AddSectionEntry(basic_info, "Auth Error",
+          signin_error_controller->auth_error().ToString());
+      AddSectionEntry(basic_info, "Auth Error Username",
+          signin_error_controller->error_username());
+    } else {
+      AddSectionEntry(basic_info, "Auth Error", "None");
+    }
   }
 
 #if !defined(OS_CHROMEOS)
diff --git a/components/signin/core/browser/about_signin_internals.h b/components/signin/core/browser/about_signin_internals.h
index 9e285c4..196a4fc 100644
--- a/components/signin/core/browser/about_signin_internals.h
+++ b/components/signin/core/browser/about_signin_internals.h
@@ -14,6 +14,7 @@
 #include "base/values.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/signin/core/browser/signin_client.h"
+#include "components/signin/core/browser/signin_error_controller.h"
 #include "components/signin/core/browser/signin_internals_util.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
@@ -35,7 +36,8 @@
       public signin_internals_util::SigninDiagnosticsObserver,
       public OAuth2TokenService::DiagnosticsObserver,
       public GaiaAuthConsumer,
-      SigninManagerBase::Observer {
+      SigninManagerBase::Observer,
+      SigninErrorController::Observer {
  public:
   class Observer {
    public:
@@ -49,7 +51,8 @@
 
   AboutSigninInternals(ProfileOAuth2TokenService* token_service,
                        AccountTrackerService* account_tracker,
-                       SigninManagerBase* signin_manager);
+                       SigninManagerBase* signin_manager,
+                       SigninErrorController* signin_error_controller);
   ~AboutSigninInternals() override;
 
   // Each instance of SigninInternalsUI adds itself as an observer to be
@@ -147,6 +150,7 @@
     scoped_ptr<base::DictionaryValue> ToValue(
         AccountTrackerService* account_tracker,
         SigninManagerBase* signin_manager,
+        SigninErrorController* signin_error_controller,
         const std::string& product_version);
   };
 
@@ -191,6 +195,9 @@
   // then we call ListAccounts and update the UI element.
   void OnCookieChanged(const net::CanonicalCookie& cookie, bool removed);
 
+  // SigninErrorController::Observer implementation
+  void OnErrorChanged() override;
+
   // Weak pointer to the token service.
   ProfileOAuth2TokenService* token_service_;
 
@@ -203,6 +210,9 @@
   // Weak pointer to the client.
   SigninClient* client_;
 
+  // Weak pointer to the SigninErrorController
+  SigninErrorController* signin_error_controller_;
+
   // Fetcher for information about accounts in the cookie jar from GAIA.
   scoped_ptr<GaiaAuthFetcher> gaia_fetcher_;
 
diff --git a/components/storage_monitor/media_storage_util.cc b/components/storage_monitor/media_storage_util.cc
index 11cfe5ee..8a39d5b 100644
--- a/components/storage_monitor/media_storage_util.cc
+++ b/components/storage_monitor/media_storage_util.cc
@@ -55,7 +55,7 @@
 }
 
 void FilterAttachedDevicesOnFileThread(MediaStorageUtil::DeviceIdSet* devices) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   MediaStorageUtil::DeviceIdSet missing_devices;
 
   for (MediaStorageUtil::DeviceIdSet::const_iterator it = devices->begin();
diff --git a/components/storage_monitor/storage_monitor_chromeos.cc b/components/storage_monitor/storage_monitor_chromeos.cc
index a584f673..92e7cfd 100644
--- a/components/storage_monitor/storage_monitor_chromeos.cc
+++ b/components/storage_monitor/storage_monitor_chromeos.cc
@@ -115,7 +115,7 @@
 }
 
 void StorageMonitorCros::CheckExistingMountPoints() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   const DiskMountManager::MountPointMap& mount_point_map =
       DiskMountManager::GetInstance()->mount_points();
   for (DiskMountManager::MountPointMap::const_iterator it =
@@ -148,7 +148,7 @@
     DiskMountManager::MountEvent event,
     chromeos::MountError error_code,
     const DiskMountManager::MountPointInfo& mount_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Ignore mount points that are not devices.
   if (mount_info.mount_type != chromeos::MOUNT_TYPE_DEVICE)
@@ -277,7 +277,7 @@
 void StorageMonitorCros::AddMountedPath(
     const DiskMountManager::MountPointInfo& mount_info,
     bool has_dcim) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (ContainsKey(mount_map_, mount_info.mount_path)) {
     // CheckExistingMountPointsOnUIThread() added the mount point information
diff --git a/components/storage_monitor/storage_monitor_linux.cc b/components/storage_monitor/storage_monitor_linux.cc
index b1068e4..43630498 100644
--- a/components/storage_monitor/storage_monitor_linux.cc
+++ b/components/storage_monitor/storage_monitor_linux.cc
@@ -119,7 +119,7 @@
 // Gets the device information using udev library.
 scoped_ptr<StorageInfo> GetDeviceInfo(const base::FilePath& device_path,
                                       const base::FilePath& mount_point) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   DCHECK(!device_path.empty());
 
   scoped_ptr<StorageInfo> storage_info;
@@ -198,7 +198,7 @@
 MtabWatcherLinux* CreateMtabWatcherLinuxOnFileThread(
     const base::FilePath& mtab_path,
     base::WeakPtr<MtabWatcherLinux::Delegate> delegate) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
   // Owned by caller.
   return new MtabWatcherLinux(mtab_path, delegate);
 }
@@ -206,7 +206,7 @@
 StorageMonitor::EjectStatus EjectPathOnFileThread(
     const base::FilePath& path,
     const base::FilePath& device) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
 
   // Note: Linux LSB says umount should exist in /bin.
   static const char kUmountBinary[] = "/bin/umount";
@@ -244,11 +244,11 @@
     : mtab_path_(path),
       get_device_info_callback_(base::Bind(&GetDeviceInfo)),
       weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 StorageMonitorLinux::~StorageMonitorLinux() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 void StorageMonitorLinux::Init() {
@@ -278,7 +278,7 @@
     const base::FilePath& path,
     StorageInfo* device_info) const {
   DCHECK(device_info);
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // TODO(thestig) |media_transfer_protocol_device_observer_| should always be
   // valid.
@@ -359,12 +359,12 @@
 }
 
 void StorageMonitorLinux::OnMtabWatcherCreated(MtabWatcherLinux* watcher) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   mtab_watcher_.reset(watcher);
 }
 
 void StorageMonitorLinux::UpdateMtab(const MountPointDeviceMap& new_mtab) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Check existing mtab entries for unaccounted mount points.
   // These mount points must have been removed in the new mtab.
@@ -465,14 +465,14 @@
 
 bool StorageMonitorLinux::IsDeviceAlreadyMounted(
     const base::FilePath& mount_device) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return ContainsKey(mount_priority_map_, mount_device);
 }
 
 void StorageMonitorLinux::HandleDeviceMountedMultipleTimes(
     const base::FilePath& mount_device,
     const base::FilePath& mount_point) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   MountPriorityMap::iterator priority = mount_priority_map_.find(mount_device);
   DCHECK(priority != mount_priority_map_.end());
@@ -484,7 +484,7 @@
 
 void StorageMonitorLinux::AddNewMount(const base::FilePath& mount_device,
                                       scoped_ptr<StorageInfo> storage_info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!storage_info)
     return;
diff --git a/components/storage_monitor/volume_mount_watcher_win.cc b/components/storage_monitor/volume_mount_watcher_win.cc
index 6a89e1a1..bdc7634 100644
--- a/components/storage_monitor/volume_mount_watcher_win.cc
+++ b/components/storage_monitor/volume_mount_watcher_win.cc
@@ -351,7 +351,7 @@
 // c) Retrieve metadata on the volumes and then
 // d) Notify that metadata to listeners.
 void VolumeMountWatcherWin::Init() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // When VolumeMountWatcherWin is created, the message pumps are not running
   // so a posted task from the constructor would never run. Therefore, do all
@@ -364,7 +364,7 @@
 
 void VolumeMountWatcherWin::AddDevicesOnUIThread(
     std::vector<base::FilePath> removable_devices) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   for (size_t i = 0; i < removable_devices.size(); i++) {
     if (ContainsKey(pending_device_checks_, removable_devices[i]))
@@ -400,7 +400,7 @@
 
 void VolumeMountWatcherWin::DeviceCheckComplete(
     const base::FilePath& device_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   pending_device_checks_.erase(device_path);
 
   if (pending_device_checks_.size() == 0) {
@@ -421,7 +421,7 @@
 
 bool VolumeMountWatcherWin::GetDeviceInfo(const base::FilePath& device_path,
                                           StorageInfo* info) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(info);
   base::FilePath path(device_path);
   MountPointDeviceMetadataMap::const_iterator iter =
@@ -439,7 +439,7 @@
 }
 
 void VolumeMountWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   switch (event_type) {
     case DBT_DEVICEARRIVAL: {
       if (IsLogicalVolumeStructure(data)) {
@@ -504,7 +504,7 @@
 void VolumeMountWatcherWin::HandleDeviceAttachEventOnUIThread(
     const base::FilePath& device_path,
     const StorageInfo& info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   device_metadata_[device_path] = info;
 
@@ -516,7 +516,7 @@
 
 void VolumeMountWatcherWin::HandleDeviceDetachEventOnUIThread(
     const base::string16& device_location) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   MountPointDeviceMetadataMap::const_iterator device_info =
       device_metadata_.find(base::FilePath(device_location));
@@ -532,7 +532,7 @@
 void VolumeMountWatcherWin::EjectDevice(
     const std::string& device_id,
     base::Callback<void(StorageMonitor::EjectStatus)> callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::FilePath device = MediaStorageUtil::FindDevicePathById(device_id);
   if (device.empty()) {
     callback.Run(StorageMonitor::EJECT_FAILURE);
diff --git a/components/suggestions/suggestions_service.cc b/components/suggestions/suggestions_service.cc
index fee193e..dbc7036 100644
--- a/components/suggestions/suggestions_service.cc
+++ b/components/suggestions/suggestions_service.cc
@@ -17,7 +17,6 @@
 #include "components/suggestions/blacklist_store.h"
 #include "components/suggestions/suggestions_store.h"
 #include "components/variations/net/variations_http_header_provider.h"
-#include "components/variations/variations_associated_data.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
@@ -51,12 +50,6 @@
                             RESPONSE_STATE_SIZE);
 }
 
-// Obtains the experiment parameter under the supplied |key|, or empty string
-// if the parameter does not exist.
-std::string GetExperimentParam(const std::string& key) {
-  return variations::GetVariationParamValue(kSuggestionsFieldTrialName, key);
-}
-
 GURL BuildBlacklistRequestURL(const std::string& blacklist_url_prefix,
                               const GURL& candidate_url) {
   return GURL(blacklist_url_prefix +
@@ -88,11 +81,6 @@
 
 }  // namespace
 
-const char kSuggestionsFieldTrialName[] = "ChromeSuggestions";
-const char kSuggestionsFieldTrialControlParam[] = "control";
-const char kSuggestionsFieldTrialStateEnabled[] = "enabled";
-const char kSuggestionsFieldTrialStateParam[] = "state";
-
 // TODO(mathp): Put this in TemplateURL.
 const char kSuggestionsURL[] = "https://www.google.com/chromesuggestions?t=2";
 const char kSuggestionsBlacklistURLPrefix[] =
@@ -118,12 +106,6 @@
 
 SuggestionsService::~SuggestionsService() {}
 
-// static
-bool SuggestionsService::IsControlGroup() {
-  return GetExperimentParam(kSuggestionsFieldTrialControlParam) ==
-      kSuggestionsFieldTrialStateEnabled;
-}
-
 void SuggestionsService::FetchSuggestionsData(
     SyncState sync_state,
     SuggestionsService::ResponseCallback callback) {
diff --git a/components/suggestions/suggestions_service.h b/components/suggestions/suggestions_service.h
index 4128b93f..b0d0f4c 100644
--- a/components/suggestions/suggestions_service.h
+++ b/components/suggestions/suggestions_service.h
@@ -37,11 +37,6 @@
 class BlacklistStore;
 class SuggestionsStore;
 
-extern const char kSuggestionsFieldTrialName[];
-extern const char kSuggestionsFieldTrialControlParam[];
-extern const char kSuggestionsFieldTrialStateEnabled[];
-extern const char kSuggestionsFieldTrialStateParam[];
-
 extern const char kSuggestionsURL[];
 extern const char kSuggestionsBlacklistURLPrefix[];
 extern const char kSuggestionsBlacklistURLParam[];
@@ -61,9 +56,6 @@
       scoped_ptr<BlacklistStore> blacklist_store);
   ~SuggestionsService() override;
 
-  // Whether the user is part of a control group.
-  static bool IsControlGroup();
-
   // Request suggestions data, which will be passed to |callback|. |sync_state|
   // will influence the behavior of this function (see SyncState definition).
   //
diff --git a/components/suggestions/suggestions_service_unittest.cc b/components/suggestions/suggestions_service_unittest.cc
index c6253c7..78ac147e 100644
--- a/components/suggestions/suggestions_service_unittest.cc
+++ b/components/suggestions/suggestions_service_unittest.cc
@@ -10,15 +10,11 @@
 #include "base/bind.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
-#include "base/metrics/field_trial.h"
-#include "base/strings/utf_string_conversions.h"
 #include "components/suggestions/blacklist_store.h"
 #include "components/suggestions/image_manager.h"
 #include "components/suggestions/proto/suggestions.pb.h"
 #include "components/suggestions/suggestions_store.h"
 #include "components/suggestions/suggestions_utils.h"
-#include "components/variations/entropy_provider.h"
-#include "components/variations/variations_associated_data.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
 #include "net/url_request/test_url_fetcher_factory.h"
@@ -199,27 +195,6 @@
         io_message_loop_.message_loop_proxy());
   }
 
-  void EnableFieldTrial(bool control_group) {
-    // Clear the existing |field_trial_list_| to avoid firing a DCHECK.
-    field_trial_list_.reset(NULL);
-    field_trial_list_.reset(
-        new base::FieldTrialList(new metrics::SHA1EntropyProvider("foo")));
-
-    variations::testing::ClearAllVariationParams();
-    std::map<std::string, std::string> params;
-    params[kSuggestionsFieldTrialStateParam] =
-        kSuggestionsFieldTrialStateEnabled;
-    if (control_group) {
-      params[kSuggestionsFieldTrialControlParam] =
-          kSuggestionsFieldTrialStateEnabled;
-    }
-    variations::AssociateVariationParams(kSuggestionsFieldTrialName, "Group1",
-                                         params);
-    field_trial_ = base::FieldTrialList::CreateFieldTrial(
-        kSuggestionsFieldTrialName, "Group1");
-    field_trial_->group();
-  }
-
   void FetchSuggestionsDataHelper(SyncState sync_state) {
     scoped_ptr<SuggestionsService> suggestions_service(
         CreateSuggestionsServiceWithMocks());
@@ -343,32 +318,18 @@
   scoped_refptr<net::TestURLRequestContextGetter> request_context_;
 
  private:
-  scoped_ptr<base::FieldTrialList> field_trial_list_;
-  scoped_refptr<base::FieldTrial> field_trial_;
-
   DISALLOW_COPY_AND_ASSIGN(SuggestionsServiceTest);
 };
 
-TEST_F(SuggestionsServiceTest, IsControlGroup) {
-  EnableFieldTrial(false);
-  EXPECT_FALSE(SuggestionsService::IsControlGroup());
-
-  EnableFieldTrial(true);
-  EXPECT_TRUE(SuggestionsService::IsControlGroup());
-}
-
 TEST_F(SuggestionsServiceTest, FetchSuggestionsData) {
-  EnableFieldTrial(false);
   FetchSuggestionsDataHelper(INITIALIZED_ENABLED_HISTORY);
 }
 
 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncNotInitializedEnabled) {
-  EnableFieldTrial(false);
   FetchSuggestionsDataHelper(NOT_INITIALIZED_ENABLED);
 }
 
 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncDisabled) {
-  EnableFieldTrial(false);
   scoped_ptr<SuggestionsService> suggestions_service(
       CreateSuggestionsServiceWithMocks());
   EXPECT_TRUE(suggestions_service != NULL);
@@ -387,7 +348,6 @@
 }
 
 TEST_F(SuggestionsServiceTest, IssueRequestIfNoneOngoingError) {
-  EnableFieldTrial(false);
   scoped_ptr<SuggestionsService> suggestions_service(
       CreateSuggestionsServiceWithMocks());
   EXPECT_TRUE(suggestions_service != NULL);
@@ -407,7 +367,6 @@
 }
 
 TEST_F(SuggestionsServiceTest, IssueRequestIfNoneOngoingResponseNotOK) {
-  EnableFieldTrial(false);
   scoped_ptr<SuggestionsService> suggestions_service(
       CreateSuggestionsServiceWithMocks());
   EXPECT_TRUE(suggestions_service != NULL);
@@ -436,7 +395,6 @@
 }
 
 TEST_F(SuggestionsServiceTest, BlacklistURL) {
-  EnableFieldTrial(false);
   scoped_ptr<SuggestionsService> suggestions_service(
       CreateSuggestionsServiceWithMocks());
   EXPECT_TRUE(suggestions_service != NULL);
@@ -480,7 +438,6 @@
 }
 
 TEST_F(SuggestionsServiceTest, BlacklistURLFails) {
-  EnableFieldTrial(false);
   scoped_ptr<SuggestionsService> suggestions_service(
       CreateSuggestionsServiceWithMocks());
   EXPECT_TRUE(suggestions_service != NULL);
@@ -496,7 +453,6 @@
 
 // Initial blacklist request fails, triggering a second which succeeds.
 TEST_F(SuggestionsServiceTest, BlacklistURLRequestFails) {
-  EnableFieldTrial(false);
   scoped_ptr<SuggestionsService> suggestions_service(
       CreateSuggestionsServiceWithMocks());
   EXPECT_TRUE(suggestions_service != NULL);
@@ -553,7 +509,6 @@
 }
 
 TEST_F(SuggestionsServiceTest, UndoBlacklistURL) {
-  EnableFieldTrial(false);
   scoped_ptr<SuggestionsService> suggestions_service(
       CreateSuggestionsServiceWithMocks());
   EXPECT_TRUE(suggestions_service != NULL);
@@ -590,17 +545,14 @@
 
 
 TEST_F(SuggestionsServiceTest, UndoBlacklistURLFailsIfNotInBlacklist) {
-  EnableFieldTrial(false);
   UndoBlacklistURLFailsHelper(true);
 }
 
 TEST_F(SuggestionsServiceTest, UndoBlacklistURLFailsIfAlreadyCandidate) {
-  EnableFieldTrial(false);
   UndoBlacklistURLFailsHelper(false);
 }
 
 TEST_F(SuggestionsServiceTest, GetBlacklistedUrl) {
-  EnableFieldTrial(false);
   scoped_ptr<GURL> request_url;
   scoped_ptr<net::FakeURLFetcher> fetcher;
   GURL retrieved_url;
@@ -626,7 +578,6 @@
 }
 
 TEST_F(SuggestionsServiceTest, UpdateBlacklistDelay) {
-  EnableFieldTrial(false);
   scoped_ptr<SuggestionsService> suggestions_service(
       CreateSuggestionsServiceWithMocks());
   base::TimeDelta initial_delay = suggestions_service->blacklist_delay();
@@ -645,7 +596,6 @@
 }
 
 TEST_F(SuggestionsServiceTest, CheckDefaultTimeStamps) {
-  EnableFieldTrial(false);
   scoped_ptr<SuggestionsService> suggestions_service(
       CreateSuggestionsServiceWithMocks());
   SuggestionsProfile suggestions =
diff --git a/components/sync_driver/data_type_manager_impl.cc b/components/sync_driver/data_type_manager_impl.cc
index dfffa3c..a12185e2b6 100644
--- a/components/sync_driver/data_type_manager_impl.cc
+++ b/components/sync_driver/data_type_manager_impl.cc
@@ -260,12 +260,17 @@
 
   DCHECK(state_ == STOPPED || state_ == CONFIGURED || state_ == RETRYING);
 
+  const State old_state = state_;
+  state_ = DOWNLOAD_PENDING;
+
   // Starting from a "steady state" (stopped or configured) state
   // should send a start notification.
-  if (state_ == STOPPED || state_ == CONFIGURED)
+  // Note: NotifyStart() must be called with the updated (non-idle) state,
+  // otherwise logic listening for the configuration start might not be aware
+  // of the fact that the DTM is in a configuration state.
+  if (old_state == STOPPED || old_state == CONFIGURED)
     NotifyStart();
 
-  state_ = DOWNLOAD_PENDING;
   download_types_queue_ = PrioritizeTypes(enabled_types);
   association_types_queue_ = std::queue<AssociationTypesInfo>();
 
diff --git a/components/sync_driver/device_info_data_type_controller_unittest.cc b/components/sync_driver/device_info_data_type_controller_unittest.cc
index e2b7cbc3..50f43f9 100644
--- a/components/sync_driver/device_info_data_type_controller_unittest.cc
+++ b/components/sync_driver/device_info_data_type_controller_unittest.cc
@@ -65,7 +65,7 @@
   }
 
   scoped_ptr<syncer::AttachmentService> CreateAttachmentService(
-      scoped_ptr<syncer::AttachmentStore> attachment_store,
+      scoped_ptr<syncer::AttachmentStoreForSync> attachment_store,
       const syncer::UserShare& user_share,
       const std::string& store_birthday,
       syncer::ModelType model_type,
diff --git a/components/sync_driver/generic_change_processor.cc b/components/sync_driver/generic_change_processor.cc
index 8f3eff19..3a1f19f7 100644
--- a/components/sync_driver/generic_change_processor.cc
+++ b/components/sync_driver/generic_change_processor.cc
@@ -94,7 +94,7 @@
     const base::WeakPtr<syncer::SyncMergeResult>& merge_result,
     syncer::UserShare* user_share,
     SyncApiComponentFactory* sync_factory,
-    scoped_ptr<syncer::AttachmentStore> attachment_store)
+    scoped_ptr<syncer::AttachmentStoreForSync> attachment_store)
     : ChangeProcessor(error_handler),
       type_(type),
       local_service_(local_service),
@@ -471,7 +471,11 @@
       NOTREACHED();
       return error;
     }
-    attachment_service_->UploadAttachments(new_attachments);
+    syncer::AttachmentIdList ids_to_upload;
+    ids_to_upload.reserve(new_attachments.size());
+    std::copy(new_attachments.begin(), new_attachments.end(),
+              std::back_inserter(ids_to_upload));
+    attachment_service_->UploadAttachments(ids_to_upload);
   }
 
   return syncer::SyncError();
@@ -705,13 +709,13 @@
 void GenericChangeProcessor::UploadAllAttachmentsNotOnServer() {
   DCHECK(CalledOnValidThread());
   DCHECK(attachment_service_.get());
-  syncer::AttachmentIdSet id_set;
+  syncer::AttachmentIdList ids;
   {
     syncer::ReadTransaction trans(FROM_HERE, share_handle());
-    trans.GetAttachmentIdsToUpload(type_, &id_set);
+    trans.GetAttachmentIdsToUpload(type_, &ids);
   }
-  if (!id_set.empty()) {
-    attachment_service_->UploadAttachments(id_set);
+  if (!ids.empty()) {
+    attachment_service_->UploadAttachments(ids);
   }
 }
 
diff --git a/components/sync_driver/generic_change_processor.h b/components/sync_driver/generic_change_processor.h
index e49d8ac..e692233 100644
--- a/components/sync_driver/generic_change_processor.h
+++ b/components/sync_driver/generic_change_processor.h
@@ -54,7 +54,7 @@
       const base::WeakPtr<syncer::SyncMergeResult>& merge_result,
       syncer::UserShare* user_share,
       SyncApiComponentFactory* sync_factory,
-      scoped_ptr<syncer::AttachmentStore> attachment_store);
+      scoped_ptr<syncer::AttachmentStoreForSync> attachment_store);
   ~GenericChangeProcessor() override;
 
   // ChangeProcessor interface.
diff --git a/components/sync_driver/generic_change_processor_unittest.cc b/components/sync_driver/generic_change_processor_unittest.cc
index bbd99e42..9d34f11 100644
--- a/components/sync_driver/generic_change_processor_unittest.cc
+++ b/components/sync_driver/generic_change_processor_unittest.cc
@@ -37,18 +37,19 @@
 // A mock that keeps track of attachments passed to UploadAttachments.
 class MockAttachmentService : public syncer::AttachmentServiceImpl {
  public:
-  MockAttachmentService(scoped_ptr<syncer::AttachmentStore> attachment_store);
+  MockAttachmentService(
+      scoped_ptr<syncer::AttachmentStoreForSync> attachment_store);
   ~MockAttachmentService() override;
   void UploadAttachments(
-      const syncer::AttachmentIdSet& attachment_ids) override;
-  std::vector<syncer::AttachmentIdSet>* attachment_id_sets();
+      const syncer::AttachmentIdList& attachment_ids) override;
+  std::vector<syncer::AttachmentIdList>* attachment_id_lists();
 
  private:
-  std::vector<syncer::AttachmentIdSet> attachment_id_sets_;
+  std::vector<syncer::AttachmentIdList> attachment_id_lists_;
 };
 
 MockAttachmentService::MockAttachmentService(
-    scoped_ptr<syncer::AttachmentStore> attachment_store)
+    scoped_ptr<syncer::AttachmentStoreForSync> attachment_store)
     : AttachmentServiceImpl(attachment_store.Pass(),
                             scoped_ptr<syncer::AttachmentUploader>(
                                 new syncer::FakeAttachmentUploader),
@@ -63,14 +64,14 @@
 }
 
 void MockAttachmentService::UploadAttachments(
-    const syncer::AttachmentIdSet& attachment_ids) {
-  attachment_id_sets_.push_back(attachment_ids);
+    const syncer::AttachmentIdList& attachment_ids) {
+  attachment_id_lists_.push_back(attachment_ids);
   AttachmentServiceImpl::UploadAttachments(attachment_ids);
 }
 
-std::vector<syncer::AttachmentIdSet>*
-MockAttachmentService::attachment_id_sets() {
-  return &attachment_id_sets_;
+std::vector<syncer::AttachmentIdList>*
+MockAttachmentService::attachment_id_lists() {
+  return &attachment_id_lists_;
 }
 
 // MockSyncApiComponentFactory needed to initialize GenericChangeProcessor and
@@ -87,7 +88,7 @@
   }
 
   scoped_ptr<syncer::AttachmentService> CreateAttachmentService(
-      scoped_ptr<syncer::AttachmentStore> attachment_store,
+      scoped_ptr<syncer::AttachmentStoreForSync> attachment_store,
       const syncer::UserShare& user_share,
       const std::string& store_birthday,
       syncer::ModelType model_type,
@@ -163,7 +164,7 @@
         type, &data_type_error_handler_,
         syncable_service_ptr_factory_.GetWeakPtr(),
         merge_result_ptr_factory_->GetWeakPtr(), test_user_share_->user_share(),
-        &sync_factory, attachment_store.Pass()));
+        &sync_factory, attachment_store->CreateAttachmentStoreForSync()));
     mock_attachment_service_ = sync_factory.GetMockAttachmentService();
   }
 
@@ -385,9 +386,9 @@
   RunLoop();
 
   // Check that the AttachmentService received the new attachments.
-  ASSERT_EQ(mock_attachment_service()->attachment_id_sets()->size(), 1U);
-  const syncer::AttachmentIdSet& attachments_added =
-      mock_attachment_service()->attachment_id_sets()->front();
+  ASSERT_EQ(mock_attachment_service()->attachment_id_lists()->size(), 1U);
+  const syncer::AttachmentIdList& attachments_added =
+      mock_attachment_service()->attachment_id_lists()->front();
   ASSERT_THAT(
       attachments_added,
       testing::UnorderedElementsAre(attachment_ids[0], attachment_ids[1]));
@@ -395,7 +396,7 @@
   // Update the SyncData, replacing its two attachments with one new attachment.
   syncer::AttachmentIdList new_attachment_ids;
   new_attachment_ids.push_back(syncer::AttachmentId::Create(0, 0));
-  mock_attachment_service()->attachment_id_sets()->clear();
+  mock_attachment_service()->attachment_id_lists()->clear();
   change_list.clear();
   change_list.push_back(
       syncer::SyncChange(FROM_HERE,
@@ -407,9 +408,9 @@
   RunLoop();
 
   // Check that the AttachmentService received it.
-  ASSERT_EQ(mock_attachment_service()->attachment_id_sets()->size(), 1U);
-  const syncer::AttachmentIdSet& new_attachments_added =
-      mock_attachment_service()->attachment_id_sets()->front();
+  ASSERT_EQ(mock_attachment_service()->attachment_id_lists()->size(), 1U);
+  const syncer::AttachmentIdList& new_attachments_added =
+      mock_attachment_service()->attachment_id_lists()->front();
   ASSERT_THAT(new_attachments_added,
               testing::UnorderedElementsAre(new_attachment_ids[0]));
 }
@@ -473,8 +474,8 @@
   // Construct the GenericChangeProcessor and see that it asks the
   // AttachmentService to upload id1 only.
   ConstructGenericChangeProcessor(kType);
-  ASSERT_EQ(1U, mock_attachment_service()->attachment_id_sets()->size());
-  ASSERT_THAT(mock_attachment_service()->attachment_id_sets()->front(),
+  ASSERT_EQ(1U, mock_attachment_service()->attachment_id_lists()->size());
+  ASSERT_THAT(mock_attachment_service()->attachment_id_lists()->front(),
               testing::UnorderedElementsAre(id1));
 }
 
diff --git a/components/sync_driver/shared_change_processor_unittest.cc b/components/sync_driver/shared_change_processor_unittest.cc
index 8177e01..d65fb54d 100644
--- a/components/sync_driver/shared_change_processor_unittest.cc
+++ b/components/sync_driver/shared_change_processor_unittest.cc
@@ -51,7 +51,7 @@
   }
 
   virtual scoped_ptr<syncer::AttachmentService> CreateAttachmentService(
-      scoped_ptr<syncer::AttachmentStore> attachment_store,
+      scoped_ptr<syncer::AttachmentStoreForSync> attachment_store,
       const syncer::UserShare& user_share,
       const std::string& store_birthday,
       syncer::ModelType model_type,
diff --git a/components/sync_driver/sync_api_component_factory.h b/components/sync_driver/sync_api_component_factory.h
index 7ba8200..f781c08 100644
--- a/components/sync_driver/sync_api_component_factory.h
+++ b/components/sync_driver/sync_api_component_factory.h
@@ -37,7 +37,7 @@
   // provided. AttachmentService doesn't take ownership of delegate, the pointer
   // must be valid throughout AttachmentService lifetime.
   virtual scoped_ptr<syncer::AttachmentService> CreateAttachmentService(
-      scoped_ptr<syncer::AttachmentStore> attachment_store,
+      scoped_ptr<syncer::AttachmentStoreForSync> attachment_store,
       const syncer::UserShare& user_share,
       const std::string& store_birthday,
       syncer::ModelType model_type,
diff --git a/components/sync_driver/ui_data_type_controller_unittest.cc b/components/sync_driver/ui_data_type_controller_unittest.cc
index f6f2b94..8d0f70c 100644
--- a/components/sync_driver/ui_data_type_controller_unittest.cc
+++ b/components/sync_driver/ui_data_type_controller_unittest.cc
@@ -57,7 +57,7 @@
   }
 
   scoped_ptr<syncer::AttachmentService> CreateAttachmentService(
-      scoped_ptr<syncer::AttachmentStore> attachment_store,
+      scoped_ptr<syncer::AttachmentStoreForSync> attachment_store,
       const syncer::UserShare& user_share,
       const std::string& store_birthday,
       syncer::ModelType model_type,
diff --git a/components/test/data/data_reduction_proxy/OWNERS b/components/test/data/data_reduction_proxy/OWNERS
index a33576ac..530b41b5 100644
--- a/components/test/data/data_reduction_proxy/OWNERS
+++ b/components/test/data/data_reduction_proxy/OWNERS
@@ -2,3 +2,4 @@
 marq@chromium.org
 bolian@chromium.org
 sclittle@chromium.org
+jeremyim@chromium.org
diff --git a/components/test/data/password_manager/automated_tests/README b/components/test/data/password_manager/automated_tests/README
index 91e7f98..3738862 100644
--- a/components/test/data/password_manager/automated_tests/README
+++ b/components/test/data/password_manager/automated_tests/README
@@ -109,8 +109,6 @@
 self.GoTo("url")
 * HoverOver: find an element using CSS Selector and hover over it.
 self.HoverOver("css_selector")
-* SendEnterTo: find an element using CSS Selector and send enter to it.
-self.SendEnterTo("css_selector")
 
 * IsDisplayed: check if an element is displayed.
 self.IsDisplayed("css_selector")
diff --git a/components/test/data/password_manager/automated_tests/environment.py b/components/test/data/password_manager/automated_tests/environment.py
index dddd8944..45cec1c 100644
--- a/components/test/data/password_manager/automated_tests/environment.py
+++ b/components/test/data/password_manager/automated_tests/environment.py
@@ -4,19 +4,12 @@
 
 """The testing Environment class."""
 
-import logging
+import os
 import shutil
-import sys
 import time
-import traceback
 from xml.etree import ElementTree
-from xml.sax.saxutils import escape
-
-sys.path.insert(0, '../../../../third_party/webdriver/pylib/')
 
 from selenium import webdriver
-from selenium.common.exceptions import NoSuchElementException
-from selenium.common.exceptions import WebDriverException
 from selenium.webdriver.chrome.options import Options
 
 
@@ -46,8 +39,7 @@
   """Sets up the testing Environment. """
 
   def __init__(self, chrome_path, chromedriver_path, profile_path,
-               passwords_path, enable_automatic_password_saving,
-               numeric_level=None, log_to_console=False, log_file=""):
+               passwords_path, enable_automatic_password_saving):
     """Creates a new testing Environment.
 
     Args:
@@ -57,75 +49,43 @@
       passwords_path: The usernames and passwords file.
       enable_automatic_password_saving: If True, the passwords are going to be
           saved without showing the prompt.
-      numeric_level: The log verbosity.
-      log_to_console: If True, the debug logs will be shown on the console.
-      log_file: The file where to store the log. If it's empty, the log will
-          not be stored.
 
     Raises:
       Exception: An exception is raised if |profile_path| folder could not be
       removed.
     """
-    # Setting up the login.
-    if numeric_level is not None:
-      if log_file:
-        # Set up logging to file.
-        logging.basicConfig(level=numeric_level,
-                            filename=log_file,
-                            filemode='w')
-
-        if log_to_console:
-          console = logging.StreamHandler()
-          console.setLevel(numeric_level)
-          # Add the handler to the root logger.
-          logging.getLogger('').addHandler(console)
-
-      elif log_to_console:
-        logging.basicConfig(level=numeric_level)
 
     # Cleaning the chrome testing profile folder.
-    try:
+    if os.path.exists(profile_path):
       shutil.rmtree(profile_path)
-    except Exception, e:
-      pass
-    # If |chrome_path| is not defined, this means that we are in the dashboard
-    # website, and we just need to get the list of all websites. In this case,
-    # we don't need to initilize the webdriver.
-    if chrome_path:
-      options = Options()
-      self.enable_automatic_password_saving = enable_automatic_password_saving
-      if enable_automatic_password_saving:
-        options.add_argument("enable-automatic-password-saving")
-      # Chrome path.
-      options.binary_location = chrome_path
-      # Chrome testing profile path.
-      options.add_argument("user-data-dir=%s" % profile_path)
+    options = Options()
+    self.enable_automatic_password_saving = enable_automatic_password_saving
+    if enable_automatic_password_saving:
+      options.add_argument("enable-automatic-password-saving")
+    # Chrome path.
+    options.binary_location = chrome_path
+    # Chrome testing profile path.
+    options.add_argument("user-data-dir=%s" % profile_path)
 
-      # The webdriver. It's possible to choose the port the service is going to
-      # run on. If it's left to 0, a free port will be found.
-      self.driver = webdriver.Chrome(chromedriver_path, 0, options)
-      # The password internals window.
-      self.internals_window = self.driver.current_window_handle
-      if passwords_path:
-        # An xml tree filled with logins and passwords.
-        self.passwords_tree = ElementTree.parse(passwords_path).getroot()
-      else:
-        raise Exception("Error: |passwords_path| needs to be provided if"
-            "|chrome_path| is provided, otherwise the tests could not be run")
+    # The webdriver. It's possible to choose the port the service is going to
+    # run on. If it's left to 0, a free port will be found.
+    self.driver = webdriver.Chrome(chromedriver_path, 0, options)
+    # The password internals window.
+    self.internals_window = self.driver.current_window_handle
+    if passwords_path:
+      # An xml tree filled with logins and passwords.
+      self.passwords_tree = ElementTree.parse(passwords_path).getroot()
+    else:
+      raise Exception("Error: |passwords_path| needs to be provided if"
+          "|chrome_path| is provided, otherwise the tests could not be run")
     # Password internals page.
     self.internals_page = "chrome://password-manager-internals/"
     # The Website window.
     self.website_window = None
     # The WebsiteTests list.
     self.websitetests = []
-    # The enabled WebsiteTests list.
-    self.working_tests = []
-    # The disabled WebsiteTests list.
-    self.disabled_tests = []
     # Map messages to the number of their appearance in the log.
-    self.message_count = dict()
-    self.message_count[MESSAGE_ASK] = 0
-    self.message_count[MESSAGE_SAVE] = 0
+    self.message_count = { MESSAGE_ASK: 0, MESSAGE_SAVE: 0 }
     # The tests needs two tabs to work. A new tab is opened with the first
     # GoTo. This is why we store here whether or not it's the first time to
     # execute GoTo.
@@ -133,34 +93,33 @@
     # List of all tests results.
     self.tests_results = []
 
-  def AddWebsiteTest(self, websitetest, disabled=False):
+  def AddWebsiteTest(self, websitetest):
     """Adds a WebsiteTest to the testing Environment.
 
+    TODO(vabr): Currently, this is only called at most once for each
+    Environment instance. That is because to run all tests efficiently in
+    parallel, each test gets its own process spawned (outside of Python).
+    That makes sense, but then we should flatten the hierarchy of calls
+    and consider making the 1:1 relation of environment to tests more
+    explicit.
+
     Args:
       websitetest: The WebsiteTest instance to be added.
-      disabled: Whether test is disabled.
     """
     websitetest.environment = self
-    if hasattr(self, "driver"):
-      websitetest.driver = self.driver
-    if hasattr(self, "passwords_tree") and self.passwords_tree is not None:
-      if not websitetest.username:
-        username_tag = (
-            self.passwords_tree.find(
-                ".//*[@name='%s']/username" % websitetest.name))
-        if username_tag.text:
-          websitetest.username = username_tag.text
-      if not websitetest.password:
-        password_tag = (
-            self.passwords_tree.find(
-                ".//*[@name='%s']/password" % websitetest.name))
-        if password_tag.text:
-          websitetest.password = password_tag.text
+    # TODO(vabr): Make driver a property of WebsiteTest.
+    websitetest.driver = self.driver
+    if not websitetest.username:
+      username_tag = (
+          self.passwords_tree.find(
+              ".//*[@name='%s']/username" % websitetest.name))
+      websitetest.username = username_tag.text
+    if not websitetest.password:
+      password_tag = (
+          self.passwords_tree.find(
+              ".//*[@name='%s']/password" % websitetest.name))
+      websitetest.password = password_tag.text
     self.websitetests.append(websitetest)
-    if disabled:
-      self.disabled_tests.append(websitetest.name)
-    else:
-      self.working_tests.append(websitetest.name)
 
   def ClearCache(self, clear_passwords):
     """Clear the browser cookies. If |clear_passwords| is true, clear all the
@@ -169,7 +128,6 @@
     Args:
       clear_passwords : Clear all the passwords if the bool value is true.
     """
-    logging.info("\nClearCache\n")
     self.driver.get("chrome://settings/clearBrowserData")
     self.driver.switch_to_frame("settings")
     script = (
@@ -193,7 +151,6 @@
     self.EnablePasswordsSaving()
 
   def EnablePasswordsSaving(self):
-    logging.info("\nEnablePasswordSaving\n")
     self.driver.get("chrome://settings")
     self.driver.switch_to_frame("settings")
     script = "document.getElementById('advanced-settings-expander').click();"
@@ -303,6 +260,8 @@
   def AllTests(self, prompt_test):
     """Runs the tests on all the WebsiteTests.
 
+    TODO(vabr): Currently, "all tests" always means one.
+
     Args:
       prompt_test: If True, tests caring about showing the save-password
           prompt are going to be run, otherwise tests which don't care about
@@ -316,32 +275,6 @@
     else:
       self.TestList(self.websitetests)
 
-  def DisabledTests(self, prompt_test):
-    """Runs the tests on all the disabled WebsiteTests.
-
-    Args:
-      prompt_test: If True, tests caring about showing the save-password
-          prompt are going to be run, otherwise tests which don't care about
-          the prompt are going to be executed.
-
-    Raises:
-      Exception: An exception is raised if the tests fail.
-    """
-    self.Test(self.disabled_tests, prompt_test)
-
-  def WorkingTests(self, prompt_test):
-    """Runs the tests on all the enabled WebsiteTests.
-
-    Args:
-      prompt_test: If True, tests caring about showing the save-password
-          prompt are going to be run, otherwise tests which don't care about
-          the prompt are going to be executed.
-
-    Raises:
-      Exception: An exception is raised if the tests fail.
-    """
-    self.Test(self.working_tests, prompt_test)
-
   def Test(self, tests, prompt_test):
     """Runs the tests on websites named in |tests|.
 
diff --git a/components/test/data/password_manager/automated_tests/run_tests.py b/components/test/data/password_manager/automated_tests/run_tests.py
index ea6d8464..a561c6aa 100644
--- a/components/test/data/password_manager/automated_tests/run_tests.py
+++ b/components/test/data/password_manager/automated_tests/run_tests.py
@@ -36,7 +36,6 @@
 import tempfile
 import time
 
-from environment import Environment
 import tests
 
 
@@ -173,7 +172,6 @@
       for format description.
   """
 
-  environment = Environment("", "", "", None, False)
   defaults = {("run_options", "tests_in_parallel"): "1"}
   config = ConfigParser.ConfigParser()
   _apply_defaults(config, defaults)
@@ -194,8 +192,7 @@
     user_selected_tests = config.get("run_options", "tests_to_run").split(",")
     tests_to_run = user_selected_tests
   else:
-    tests.Tests(environment)
-    tests_to_run = [test.name for test in environment.websitetests]
+    tests_to_run = tests.all_tests.keys()
 
   logger = logging.getLogger("run_tests")
   logger.log(SCRIPT_DEBUG, "%d tests to run: %s", len(tests_to_run),
diff --git a/components/test/data/password_manager/automated_tests/tests.py b/components/test/data/password_manager/automated_tests/tests.py
index f21ce1d..c1da46db 100644
--- a/components/test/data/password_manager/automated_tests/tests.py
+++ b/components/test/data/password_manager/automated_tests/tests.py
@@ -6,28 +6,11 @@
 """Automated tests for many websites"""
 
 import argparse
-import logging
 
 from environment import Environment
 from websitetest import WebsiteTest
 
 
-class TypeOfTestedWebsites:
-  """An enum to specify which groups of tests to run."""
-  # Runs only the disabled tests.
-  # TODO(vabr): Remove this option.
-  DISABLED_TESTS = 0
-  # Runs only the enabled tests.
-  ENABLED_TESTS = 1
-  # Runs all the tests.
-  ALL_TESTS = 2
-  # Runs a specified list of tests.
-  LIST_OF_TESTS = 3
-
-  def __init__(self):
-    pass
-
-
 class Alexa(WebsiteTest):
 
   def Login(self):
@@ -219,8 +202,6 @@
     self.Click(".b-mail-button__button")
 
 
-# Disabled tests.
-
 # Fails due to test framework issue(?).
 class Aliexpress(WebsiteTest):
 
@@ -452,68 +433,46 @@
     self.Click(".login input")
 
 
-def Tests(environment, tests_to_run=None):
-
-  working_tests = {
-    "alexa": Alexa("alexa"),
-    "dropbox": Dropbox("dropbox"),
-    "facebook": Facebook("facebook"),
-    "github": Github("github"),
-    "google": Google("google"),
-    "imgur": Imgur("imgur"),
-    "liveinternet": Liveinternet("liveinternet"),
-    "linkedin": Linkedin("linkedin"),
-    "mailru": Mailru("mailru"),
-    "nytimes": Nytimes("nytimes"),
-    "odnoklassniki": Odnoklassniki("odnoklassniki"),
-    "pinterest": Pinterest("pinterest"),
-    "reddit": Reddit("reddit", username_not_auto=True),
-    "tumblr": Tumblr("tumblr", username_not_auto=True),
-    "twitter": Twitter("twitter"),
-    "vkontakte": Vkontakte("vkontakte"),
-    "wikia": Wikia("wikia"),
-    "wikipedia": Wikipedia("wikipedia", username_not_auto=True),
-    "wordpress": Wordpress("wordpress"),
-    "yahoo": Yahoo("yahoo", username_not_auto=True),
-    "yandex": Yandex("yandex")
-  }
-
-  disabled_tests = {
-    "adobe": Adobe("adobe"), # Password saving not offered.
-    "aliexpress": Aliexpress("aliexpress"), # Fails due to test framework issue.
-    "amazon": Amazon("amazon"), # Bug not reproducible without test.
-    "ask": Ask("ask"), # Password not saved.
-    "baidu": Baidu("baidu"), # Password not saved.
-    "cnn": Cnn("cnn"), # http://crbug.com/368690
-    "craigslist": Craigslist("craigslist"), # Too many failed logins per time.
-    "dailymotion": Dailymotion("dailymotion"), # Crashes.
-    "ebay": Ebay("ebay"), # http://crbug.com/368690
-    "espn": Espn("espn"), # Iframe, password saved but not autofilled.
-    "flipkart": Flipkart("flipkart"), # Fails due to test framework issue.
-    "instagram": Instagram("instagram"), # Iframe, pw saved but not autofilled.
-    "live": Live("live", username_not_auto=True),  # http://crbug.com/367768
-    "163": One63("163"), # http://crbug.com/368690
-    "stackexchange": StackExchange("stackexchange"), # Iframe, not autofilled.
-    "vube": Vube("vube"), # http://crbug.com/368690
-    "ziddu": Ziddu("ziddu"), # Password not saved.
-  }
-
-  if tests_to_run:
-    for test in tests_to_run:
-      if (test not in working_tests.keys() and
-          test not in disabled_tests.keys()):
-        print "Skip test: test {} is not in known tests".format(test)
-        continue
-      if test in working_tests.keys():
-        test_class = working_tests[test]
-      else:
-        test_class = disabled_tests[test]
-      environment.AddWebsiteTest(test_class)
-  else:
-    for test in working_tests.itervalues():
-      environment.AddWebsiteTest(test)
-    for test in disabled_tests.itervalues():
-      environment.AddWebsiteTest(test, disabled=True)
+all_tests = {
+  "163": One63("163"), # http://crbug.com/368690
+  "adobe": Adobe("adobe"), # Password saving not offered.
+  "alexa": Alexa("alexa"),
+  "aliexpress": Aliexpress("aliexpress"), # Fails due to test framework issue.
+  "amazon": Amazon("amazon"), # Bug not reproducible without test.
+  "ask": Ask("ask"), # Password not saved.
+  "baidu": Baidu("baidu"), # Password not saved.
+  "cnn": Cnn("cnn"), # http://crbug.com/368690
+  "craigslist": Craigslist("craigslist"), # Too many failed logins per time.
+  "dailymotion": Dailymotion("dailymotion"), # Crashes.
+  "dropbox": Dropbox("dropbox"),
+  "ebay": Ebay("ebay"), # http://crbug.com/368690
+  "espn": Espn("espn"), # Iframe, password saved but not autofilled.
+  "facebook": Facebook("facebook"),
+  "flipkart": Flipkart("flipkart"), # Fails due to test framework issue.
+  "github": Github("github"),
+  "google": Google("google"),
+  "imgur": Imgur("imgur"),
+  "instagram": Instagram("instagram"), # Iframe, pw saved but not autofilled.
+  "linkedin": Linkedin("linkedin"),
+  "liveinternet": Liveinternet("liveinternet"),
+  "live": Live("live", username_not_auto=True),  # http://crbug.com/367768
+  "mailru": Mailru("mailru"),
+  "nytimes": Nytimes("nytimes"),
+  "odnoklassniki": Odnoklassniki("odnoklassniki"),
+  "pinterest": Pinterest("pinterest"),
+  "reddit": Reddit("reddit", username_not_auto=True),
+  "stackexchange": StackExchange("stackexchange"), # Iframe, not autofilled.
+  "tumblr": Tumblr("tumblr", username_not_auto=True),
+  "twitter": Twitter("twitter"),
+  "vkontakte": Vkontakte("vkontakte"),
+  "vube": Vube("vube"), # http://crbug.com/368690
+  "wikia": Wikia("wikia"),
+  "wikipedia": Wikipedia("wikipedia", username_not_auto=True),
+  "wordpress": Wordpress("wordpress"),
+  "yahoo": Yahoo("yahoo", username_not_auto=True),
+  "yandex": Yandex("yandex"),
+  "ziddu": Ziddu("ziddu"), # Password not saved.
+}
 
 
 def saveResults(environment_tests_results, environment_save_path):
@@ -537,12 +496,10 @@
     with open(environment_save_path, "w") as save_file:
       save_file.write(xml)
 
-def RunTests(chrome_path, chromedriver_path, profile_path,
-             environment_passwords_path, enable_automatic_password_saving,
-             environment_numeric_level, log_to_console, environment_log_file,
-             environment_tested_websites, tests=None):
-
-  """Runs the the tests
+def RunTest(chrome_path, chromedriver_path, profile_path,
+            environment_passwords_path, enable_automatic_password_saving,
+            website_test_name):
+  """Runs the test for the specified website.
 
   Args:
     chrome_path: The chrome binary file.
@@ -551,46 +508,31 @@
     environment_passwords_path: The usernames and passwords file.
     enable_automatic_password_saving: If True, the passwords are going to be
         saved without showing the prompt.
-    environment_numeric_level: The log verbosity.
-    log_to_console: If True, the debug logs will be shown on the console.
-    environment_log_file: The file where to store the log. If it's empty, the
-        log is not stored.
-    environment_tested_websites: One of the TypeOfTestedWebsites values,
-        indicating which group of tests to run.
-    tests: Specifies which tests to run. Ignored unless
-       |environment_tested_websites| is equal to LIST_OF_TESTS.
+    website_test_name: Name of the website to test (refer to keys in
+        all_tests above).
 
   Returns:
-    The results of tests as list of TestResults.
+    The results of the test as list of TestResults.
+
   Raises:
-    Exception: An exception is raised if one of the tests fails.
+    Exception: An exception is raised if one of the tests for the website
+        fails, or if the website name is not known.
   """
 
   environment = Environment(chrome_path, chromedriver_path, profile_path,
                             environment_passwords_path,
-                            enable_automatic_password_saving,
-                            environment_numeric_level,
-                            log_to_console,
-                            environment_log_file)
+                            enable_automatic_password_saving)
 
   # Test which care about the save-password prompt need the prompt
   # to be shown. Automatic password saving results in no prompt.
   run_prompt_tests = not enable_automatic_password_saving
 
-  Tests(environment, tests)
-
-  if environment_tested_websites == TypeOfTestedWebsites.ALL_TESTS:
-    environment.AllTests(run_prompt_tests)
-  elif environment_tested_websites == TypeOfTestedWebsites.DISABLED_TESTS:
-    environment.DisabledTests(run_prompt_tests)
-  elif environment_tested_websites == TypeOfTestedWebsites.LIST_OF_TESTS:
-    environment.Test(tests, run_prompt_tests)
-  elif environment_tested_websites == TypeOfTestedWebsites.ENABLED_TESTS:
-    environment.WorkingTests(run_prompt_tests)
+  if website_test_name in all_tests:
+    environment.AddWebsiteTest(all_tests[website_test_name])
   else:
-    raise Exception("Error: |environment_tested_websites| has to be one of the"
-        "TypeOfTestedWebsites values")
+    raise Exception("Test name {} is unknown.".format(website_test_name))
 
+  environment.AllTests(run_prompt_tests)
 
   environment.Quit()
   return environment.tests_results
@@ -602,86 +544,46 @@
 
   parser.add_argument(
       "--chrome-path", action="store", dest="chrome_path",
-      help="Set the chrome path (required).", nargs=1, required=True)
+      help="Set the chrome path (required).", required=True)
   parser.add_argument(
       "--chromedriver-path", action="store", dest="chromedriver_path",
-      help="Set the chromedriver path (required).", nargs=1, required=True)
+      help="Set the chromedriver path (required).", required=True)
   parser.add_argument(
       "--profile-path", action="store", dest="profile_path",
       help="Set the profile path (required). You just need to choose a "
            "temporary empty folder. If the folder is not empty all its content "
            "is going to be removed.",
-      nargs=1, required=True)
+      required=True)
 
   parser.add_argument(
       "--passwords-path", action="store", dest="passwords_path",
-      help="Set the usernames/passwords path (required).", nargs=1,
-      required=True)
-  parser.add_argument("--all", action="store_true", dest="all",
-                      help="Run all tests.")
-  parser.add_argument("--disabled", action="store_true", dest="disabled",
-                      help="Run only disabled tests.")
-  parser.add_argument("--log", action="store", nargs=1, dest="log_level",
-                      help="Set log level.")
-  parser.add_argument("--log-screen", action="store_true", dest="log_screen",
-                      help="Show log on the screen.")
-  parser.add_argument("--log-file", action="store", dest="log_file",
-                      help="Write the log in a file.", nargs=1)
-  parser.add_argument("--save-path", action="store", nargs=1, dest="save_path",
+      help="Set the usernames/passwords path (required).", required=True)
+  parser.add_argument("--save-path", action="store", dest="save_path",
                       help="Write the results in a file.")
-  parser.add_argument("tests", help="Tests to be run.",  nargs="*")
+  parser.add_argument("test", help="Test to be run.")
 
   args = parser.parse_args()
 
-  passwords_path = args.passwords_path[0]
-
-  if args.all:
-    tested_websites = TypeOfTestedWebsites.ALL_TESTS
-  elif args.disabled:
-    tested_websites = TypeOfTestedWebsites.DISABLED_TESTS
-  elif args.tests:
-    tested_websites = TypeOfTestedWebsites.LIST_OF_TESTS
-  else:
-    tested_websites = TypeOfTestedWebsites.ENABLED_TESTS
-
-  numeric_level = None
-  if args.log_level:
-    numeric_level = getattr(logging, args.log_level[0].upper(), None)
-    if not isinstance(numeric_level, int):
-      raise ValueError("Invalid log level: %s" % args.log_level[0])
-
-  log_file = None
-  if args.log_file:
-    log_file = args.log_file[0]
-
   save_path = None
   if args.save_path:
-    save_path = args.save_path[0]
+    save_path = args.save_path
 
   # Run the test without enable-automatic-password-saving to check whether or
   # not the prompt is shown in the way we expected.
-  tests_results = RunTests(args.chrome_path[0],
-                           args.chromedriver_path[0],
-                           args.profile_path[0],
-                           passwords_path,
-                           False,
-                           numeric_level,
-                           args.log_screen,
-                           log_file,
-                           tested_websites,
-                           args.tests)
+  tests_results = RunTest(args.chrome_path,
+                          args.chromedriver_path,
+                          args.profile_path,
+                          args.passwords_path,
+                          False,
+                          args.test)
 
   # Run the test with enable-automatic-password-saving to check whether or not
   # the passwords is stored in the the way we expected.
-  tests_results += RunTests(args.chrome_path[0],
-                            args.chromedriver_path[0],
-                            args.profile_path[0],
-                            passwords_path,
-                            True,
-                            numeric_level,
-                            args.log_screen,
-                            log_file,
-                            tested_websites,
-                            args.tests)
+  tests_results += RunTest(args.chrome_path,
+                           args.chromedriver_path,
+                           args.profile_path,
+                           args.passwords_path,
+                           True,
+                           args.test)
 
   saveResults(tests_results, save_path)
diff --git a/components/test/data/password_manager/automated_tests/websitetest.py b/components/test/data/password_manager/automated_tests/websitetest.py
index f7ae7ea..cc5bc8f5 100644
--- a/components/test/data/password_manager/automated_tests/websitetest.py
+++ b/components/test/data/password_manager/automated_tests/websitetest.py
@@ -5,11 +5,8 @@
 """WebsiteTest testing class."""
 
 import logging
-import sys
 import time
 
-sys.path.insert(0, '../../../../third_party/webdriver/pylib/')
-
 from selenium.webdriver.common.action_chains import ActionChains
 from selenium.webdriver.common.keys import Keys
 
@@ -135,16 +132,6 @@
     hover = ActionChains(self.driver).move_to_element(element)
     hover.perform()
 
-  def SendEnterTo(self, selector):
-    """Sends an enter key to an element.
-
-    Args:
-      selector: The element CSS selector.
-    """
-    logging.info("action: SendEnterTo %s" % selector)
-    body = self.driver.find_element_by_tag_name("body")
-    body.send_keys(Keys.ENTER)
-
   # Waiting/Displaying actions.
 
   def IsDisplayed(self, selector):
diff --git a/components/ui/zoom/zoom_controller.cc b/components/ui/zoom/zoom_controller.cc
index 964d20f7..a56aaa9 100644
--- a/components/ui/zoom/zoom_controller.cc
+++ b/components/ui/zoom/zoom_controller.cc
@@ -135,6 +135,9 @@
   } else {
     if (!entry) {
       last_client_ = NULL;
+      // If we exit without triggering an update, we should clear event_data_,
+      // else we may later trigger a DCHECK(event_data_).
+      event_data_.reset();
       return false;
     }
     std::string host =
diff --git a/components/user_manager.gypi b/components/user_manager.gypi
index 9154cce7..d48b38f0 100644
--- a/components/user_manager.gypi
+++ b/components/user_manager.gypi
@@ -8,6 +8,7 @@
     'user_manager_shared_sources': [
       'user_manager/empty_user_info.cc',
       'user_manager/empty_user_info.h',
+      'user_manager/user_id.h',
       'user_manager/user_info.cc',
       'user_manager/user_info.h',
       'user_manager/user_info_impl.cc',
@@ -19,7 +20,6 @@
       'user_manager/remove_user_delegate.h',
       'user_manager/user.cc',
       'user_manager/user.h',
-      'user_manager/user_id.h',
       'user_manager/user_image/default_user_images.cc',
       'user_manager/user_image/default_user_images.h',
       'user_manager/user_image/user_image.cc',
diff --git a/components/user_manager/BUILD.gn b/components/user_manager/BUILD.gn
index 1d98ed4..7a5143e88 100644
--- a/components/user_manager/BUILD.gn
+++ b/components/user_manager/BUILD.gn
@@ -6,6 +6,7 @@
   sources = [
     "empty_user_info.cc",
     "empty_user_info.h",
+    "user_id.h",
     "user_info.cc",
     "user_info.h",
     "user_info_impl.cc",
@@ -26,7 +27,6 @@
       "remove_user_delegate.h",
       "user.cc",
       "user.h",
-      "user_id.h",
       "user_image/default_user_images.cc",
       "user_image/default_user_images.h",
       "user_image/user_image.cc",
diff --git a/components/user_manager/user.cc b/components/user_manager/user.cc
index f460af3..6a7dec1 100644
--- a/components/user_manager/user.cc
+++ b/components/user_manager/user.cc
@@ -118,7 +118,7 @@
   return user_image_.image();
 }
 
-std::string User::GetUserID() const {
+UserID User::GetUserID() const {
   return gaia::CanonicalizeEmail(gaia::SanitizeEmail(email()));
 }
 
diff --git a/components/user_manager/user.h b/components/user_manager/user.h
index a413cce18..39b71823 100644
--- a/components/user_manager/user.h
+++ b/components/user_manager/user.h
@@ -10,6 +10,7 @@
 
 #include "base/basictypes.h"
 #include "base/strings/string16.h"
+#include "components/user_manager/user_id.h"
 #include "components/user_manager/user_image/user_image.h"
 #include "components/user_manager/user_info.h"
 #include "components/user_manager/user_manager_export.h"
@@ -92,7 +93,7 @@
   base::string16 GetDisplayName() const override;
   base::string16 GetGivenName() const override;
   const gfx::ImageSkia& GetImage() const override;
-  std::string GetUserID() const override;
+  UserID GetUserID() const override;
 
   // Allows managing child status of the user. Used for RegularUser.
   virtual void SetIsChild(bool is_child);
@@ -175,11 +176,11 @@
   friend class chromeos::UserAddingScreenTest;
 
   // Do not allow anyone else to create new User instances.
-  static User* CreateRegularUser(const std::string& email);
+  static User* CreateRegularUser(const UserID& email);
   static User* CreateGuestUser();
-  static User* CreateKioskAppUser(const std::string& kiosk_app_username);
-  static User* CreateSupervisedUser(const std::string& username);
-  static User* CreatePublicAccountUser(const std::string& email);
+  static User* CreateKioskAppUser(const UserID& kiosk_app_username);
+  static User* CreateSupervisedUser(const UserID& username);
+  static User* CreatePublicAccountUser(const UserID& email);
 
   explicit User(const std::string& email);
   ~User() override;
diff --git a/components/user_manager/user_id.h b/components/user_manager/user_id.h
index fb89ddd..bf8730a 100644
--- a/components/user_manager/user_id.h
+++ b/components/user_manager/user_id.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_USER_MANAGER_USER_ID_H_
 #define COMPONENTS_USER_MANAGER_USER_ID_H_
 
+#include <string>
+
 namespace user_manager {
 
 // Type that contains enough information to identify user on ChromeOS.
diff --git a/components/user_manager/user_info.h b/components/user_manager/user_info.h
index 9540946..cc613c1 100644
--- a/components/user_manager/user_info.h
+++ b/components/user_manager/user_info.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/strings/string16.h"
+#include "components/user_manager/user_id.h"
 #include "components/user_manager/user_manager_export.h"
 
 namespace gfx {
@@ -35,7 +36,7 @@
 
   // Gets the user id (sanitized email address) for the user.
   // The function would return something like "foobar@mock.com".
-  virtual std::string GetUserID() const = 0;
+  virtual UserID GetUserID() const = 0;
 
   // Gets the avatar image for the user.
   virtual const gfx::ImageSkia& GetImage() const = 0;
diff --git a/components/user_manager/user_manager.h b/components/user_manager/user_manager.h
index 1ac1a7f..3f628db 100644
--- a/components/user_manager/user_manager.h
+++ b/components/user_manager/user_manager.h
@@ -8,9 +8,14 @@
 #include <string>
 
 #include "components/user_manager/user.h"
+#include "components/user_manager/user_id.h"
 #include "components/user_manager/user_manager_export.h"
 #include "components/user_manager/user_type.h"
 
+namespace base {
+class DictionaryValue;
+}
+
 namespace chromeos {
 class ScopedUserManagerEnabler;
 }
@@ -306,6 +311,21 @@
   // Returns true if supervised users allowed.
   virtual bool AreSupervisedUsersAllowed() const = 0;
 
+  // Methods for storage/retrieval of per-user properties in Local State.
+
+  // Performs a lookup of properties associated with |user_id|. If found,
+  // returns |true| and fills |out_value|. |out_value| can be NULL, if
+  // only existence check is required.
+  virtual bool FindKnownUserPrefs(const UserID& user_id,
+                                  const base::DictionaryValue** out_value) = 0;
+
+  // Updates (or creates) properties associated with |user_id| based
+  // on |values|. |clear| defines if existing properties are cleared (|true|)
+  // or if it is just a incremental update (|false|).
+  virtual void UpdateKnownUserPrefs(const UserID& user_id,
+                                    const base::DictionaryValue& values,
+                                    bool clear) = 0;
+
  protected:
   // Sets UserManager instance.
   static void SetInstance(UserManager* user_manager);
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index 364b135f..3513586c 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -971,14 +971,14 @@
   DictionaryPrefUpdate prefs_force_online_update(prefs, kUserForceOnlineSignin);
   prefs_force_online_update->RemoveWithoutPathExpansion(user_id, NULL);
 
-  RemoveKnowUserPrefs(user_id);
+  RemoveKnownUserPrefs(user_id);
 
   std::string last_active_user = GetLocalState()->GetString(kLastActiveUser);
   if (user_id == last_active_user)
     GetLocalState()->SetString(kLastActiveUser, std::string());
 }
 
-bool UserManagerBase::FindKnowUserPrefs(
+bool UserManagerBase::FindKnownUserPrefs(
     const UserID& user_id,
     const base::DictionaryValue** out_value) {
   PrefService* local_state = GetLocalState();
@@ -995,9 +995,9 @@
   return false;
 }
 
-void UserManagerBase::UpdateKnowUserPrefs(const UserID& user_id,
-                                          const base::DictionaryValue& values,
-                                          bool clear) {
+void UserManagerBase::UpdateKnownUserPrefs(const UserID& user_id,
+                                           const base::DictionaryValue& values,
+                                           bool clear) {
   ListPrefUpdate update(GetLocalState(), kKnownUsers);
   for (size_t i = 0; i < update->GetSize(); ++i) {
     base::DictionaryValue* element = nullptr;
@@ -1036,7 +1036,7 @@
   return user;
 }
 
-void UserManagerBase::RemoveKnowUserPrefs(const UserID& user_id) {
+void UserManagerBase::RemoveKnownUserPrefs(const UserID& user_id) {
   ListPrefUpdate update(GetLocalState(), kKnownUsers);
   for (size_t i = 0; i < update->GetSize(); ++i) {
     base::DictionaryValue* element = nullptr;
diff --git a/components/user_manager/user_manager_base.h b/components/user_manager/user_manager_base.h
index 4faae77f..ef662b5 100644
--- a/components/user_manager/user_manager_base.h
+++ b/components/user_manager/user_manager_base.h
@@ -105,6 +105,11 @@
       UserManager::UserSessionStateObserver* obs) override;
   void NotifyLocalStateChanged() override;
   void ChangeUserChildStatus(User* user, bool is_child) override;
+  bool FindKnownUserPrefs(const UserID& user_id,
+                          const base::DictionaryValue** out_value) override;
+  void UpdateKnownUserPrefs(const UserID& user_id,
+                            const base::DictionaryValue& values,
+                            bool clear) override;
 
   virtual void SetIsCurrentUserNew(bool is_new);
 
@@ -202,21 +207,6 @@
   // avatar, OAuth token status, display name, display email).
   virtual void RemoveNonCryptohomeData(const std::string& user_id);
 
-  // Methods for storage/retrieval of per-user properties in Local State.
-
-  // Performs a lookup of properties associated with |user_id|. If found,
-  // returns |true| and fills |out_value|. |out_value| can be NULL, if
-  // only existence check is required.
-  bool FindKnowUserPrefs(const UserID& user_id,
-                         const base::DictionaryValue** out_value);
-
-  // Updates (or creates) properties associated with |user_id| based
-  // on |values|. |clear| defines if existing properties are cleared (|true|)
-  // or if it is just a incremental update (|false|).
-  void UpdateKnowUserPrefs(const UserID& user_id,
-                           const base::DictionaryValue& values,
-                           bool clear);
-
   // Check for a particular user type.
 
   // Returns true if |user_id| represents demo app.
@@ -343,7 +333,7 @@
                              scoped_ptr<std::string> resolved_locale);
 
   // Removes all user preferences associated with |user_id|.
-  void RemoveKnowUserPrefs(const UserID& user_id);
+  void RemoveKnownUserPrefs(const UserID& user_id);
 
   // Indicates stage of loading user from prefs.
   UserLoadStage user_loading_stage_;
diff --git a/components/wallpaper/wallpaper_manager_base.cc b/components/wallpaper/wallpaper_manager_base.cc
index e40ea566..3e0b014e 100644
--- a/components/wallpaper/wallpaper_manager_base.cc
+++ b/components/wallpaper/wallpaper_manager_base.cc
@@ -344,7 +344,7 @@
 }
 
 bool WallpaperManagerBase::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (user_manager::UserManager::Get()->IsLoggedInAsStub()) {
     info->location = current_user_wallpaper_info_.location = "";
@@ -683,7 +683,7 @@
 
 bool WallpaperManagerBase::GetWallpaperFromCache(const std::string& user_id,
                                                  gfx::ImageSkia* image) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(user_id);
   if (it != wallpaper_cache_.end() && !(*it).second.second.isNull()) {
     *image = (*it).second.second;
@@ -694,7 +694,7 @@
 
 bool WallpaperManagerBase::GetPathFromCache(const std::string& user_id,
                                             base::FilePath* path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(user_id);
   if (it != wallpaper_cache_.end()) {
     *path = (*it).second.first;
@@ -709,7 +709,7 @@
 
 void WallpaperManagerBase::CacheUsersWallpapers() {
   // TODO(dpolukhin): crbug.com/408734.
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   user_manager::UserList users = user_manager::UserManager::Get()->GetUsers();
 
   if (!users.empty()) {
@@ -943,7 +943,7 @@
     const GURL& wallpaper_url,
     scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files,
     const user_manager::UserImage& wallpaper) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // If decoded wallpaper is empty, we have probably failed to decode the file.
   if (wallpaper.image().isNull()) {
diff --git a/components/wallpaper/wallpaper_manager_base.h b/components/wallpaper/wallpaper_manager_base.h
index 65e1c75..ae1ddd6 100644
--- a/components/wallpaper/wallpaper_manager_base.h
+++ b/components/wallpaper/wallpaper_manager_base.h
@@ -443,9 +443,6 @@
   // world, logged in users' wallpaper cache is not disposable.
   virtual void ClearDisposableWallpaperCache();
 
-  // Clears all obsolete wallpaper prefs from old version wallpaper pickers.
-  virtual void ClearObsoleteWallpaperPrefs() = 0;
-
   // Deletes all |user_id| related custom wallpapers and directories.
   virtual void DeleteUserWallpapers(const std::string& user_id,
                                     const std::string& path_to_file);
diff --git a/components/webui_generator/web_ui_view.h b/components/webui_generator/web_ui_view.h
index 9fd26cf..bfb8c9fa 100644
--- a/components/webui_generator/web_ui_view.h
+++ b/components/webui_generator/web_ui_view.h
@@ -58,20 +58,12 @@
 
   content::WebUI* web_ui() { return web_ui_; }
 
-  template <typename T>
-  void AddCallback(const std::string& name, void (T::*method)()) {
-    base::Callback<void()> callback =
+  template <typename T, typename... Args>
+  void AddCallback(const std::string& name, void (T::*method)(Args...)) {
+    base::Callback<void(Args...)> callback =
         base::Bind(method, base::Unretained(static_cast<T*>(this)));
     web_ui_->RegisterMessageCallback(
-        name, base::Bind(&::login::CallbackWrapper0, callback));
-  }
-
-  template <typename T, typename A1>
-  void AddCallback(const std::string& name, void (T::*method)(A1 arg1)) {
-    base::Callback<void(A1)> callback =
-        base::Bind(method, base::Unretained(static_cast<T*>(this)));
-    web_ui_->RegisterMessageCallback(
-        name, base::Bind(&::login::CallbackWrapper1<A1>, callback));
+        name, base::Bind(&::login::CallbackWrapper<Args...>, callback));
   }
 
   // Overridden from View:
diff --git a/content/app/mojo/mojo_init.cc b/content/app/mojo/mojo_init.cc
index c706255..bf92f64 100644
--- a/content/app/mojo/mojo_init.cc
+++ b/content/app/mojo/mojo_init.cc
@@ -6,6 +6,7 @@
 
 #include "base/lazy_instance.h"
 #include "base/memory/scoped_ptr.h"
+#include "ipc/ipc_channel.h"
 #include "third_party/mojo/src/mojo/edk/embedder/configuration.h"
 #include "third_party/mojo/src/mojo/edk/embedder/embedder.h"
 #include "third_party/mojo/src/mojo/edk/embedder/simple_platform_support.h"
@@ -18,7 +19,7 @@
  public:
   MojoInitializer() {
     mojo::embedder::GetConfiguration()->max_message_num_bytes =
-        64 * 1024 * 1024;
+        IPC::Channel::kMaximumMessageSize;
     mojo::embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
         new mojo::embedder::SimplePlatformSupport()));
   }
diff --git a/content/app/resources/content_resources.grd b/content/app/resources/content_resources.grd
index 9e90cd5..75913e3 100644
--- a/content/app/resources/content_resources.grd
+++ b/content/app/resources/content_resources.grd
@@ -9,46 +9,6 @@
   </outputs>
   <release seq="1">
     <structures fallback_to_low_resolution="true">
-      <structure type="chrome_scaled_image" name="IDR2_BROKENIMAGE" file="broken_image.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_PAUSE_BUTTON" file="mediaplayer_pause.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_PAUSE_BUTTON_HOVER" file="mediaplayer_pause_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_PAUSE_BUTTON_DOWN" file="mediaplayer_pause_down.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_PLAY_BUTTON" file="mediaplayer_play.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_PLAY_BUTTON_HOVER" file="mediaplayer_play_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_PLAY_BUTTON_DOWN" file="mediaplayer_play_down.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_PLAY_BUTTON_DISABLED" file="mediaplayer_play_disabled.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL3_BUTTON" file="mediaplayer_sound_level3.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL3_BUTTON_HOVER" file="mediaplayer_sound_level3_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL3_BUTTON_DOWN" file="mediaplayer_sound_level3_down.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL2_BUTTON" file="mediaplayer_sound_level2.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL2_BUTTON_HOVER" file="mediaplayer_sound_level2_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL2_BUTTON_DOWN" file="mediaplayer_sound_level2_down.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL1_BUTTON" file="mediaplayer_sound_level1.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL1_BUTTON_HOVER" file="mediaplayer_sound_level1_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL1_BUTTON_DOWN" file="mediaplayer_sound_level1_down.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL0_BUTTON" file="mediaplayer_sound_level0.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL0_BUTTON_HOVER" file="mediaplayer_sound_level0_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_LEVEL0_BUTTON_DOWN" file="mediaplayer_sound_level0_down.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SOUND_DISABLED" file="mediaplayer_sound_disabled.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SLIDER_THUMB" file="mediaplayer_slider_thumb.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SLIDER_THUMB_HOVER" file="mediaplayer_slider_thumb_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_SLIDER_THUMB_DOWN" file="mediaplayer_slider_thumb_down.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_VOLUME_SLIDER_THUMB" file="mediaplayer_volume_slider_thumb.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_VOLUME_SLIDER_THUMB_HOVER" file="mediaplayer_volume_slider_thumb_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_VOLUME_SLIDER_THUMB_DOWN" file="mediaplayer_volume_slider_thumb_down.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_VOLUME_SLIDER_THUMB_DISABLED" file="mediaplayer_volume_slider_thumb_disabled.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_CLOSEDCAPTION_BUTTON" file="mediaplayer_closedcaption.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_HOVER" file="mediaplayer_closedcaption_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_DOWN" file="mediaplayer_closedcaption_down.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_DISABLED" file="mediaplayer_closedcaption_disabled.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_FULLSCREEN_BUTTON" file="mediaplayer_fullscreen.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_FULLSCREEN_BUTTON_HOVER" file="mediaplayer_fullscreen_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_FULLSCREEN_BUTTON_DOWN" file="mediaplayer_fullscreen_down.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_FULLSCREEN_BUTTON_DISABLED" file="mediaplayer_fullscreen_disabled.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_CAST_BUTTON_OFF" file="mediaplayer_cast_off.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_CAST_BUTTON_ON" file="mediaplayer_cast_on.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_OVERLAY_CAST_BUTTON_OFF" file="mediaplayer_overlay_cast_off.png" />
-      <structure type="chrome_scaled_image" name="IDR2_MEDIAPLAYER_OVERLAY_PLAY_BUTTON" file="mediaplayer_overlay_play.png" />
       <if expr="is_macosx">
         <structure type="chrome_scaled_image" name="IDR_ALIAS_CURSOR" file="alias_cursor.png" />
         <structure type="chrome_scaled_image" name="IDR_CELL_CURSOR" file="cell_cursor.png" />
@@ -74,14 +34,6 @@
         <structure type="chrome_scaled_image" name="IDR_ZOOMIN_CURSOR" file="zoom_in_cursor.png" />
         <structure type="chrome_scaled_image" name="IDR_ZOOMOUT_CURSOR" file="zoom_out_cursor.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR2_PAN_SCROLL_ICON" file="pan_icon.png" />
-      <structure type="chrome_scaled_image" name="IDR2_SEARCH_CANCEL" file="search_cancel.png" />
-      <structure type="chrome_scaled_image" name="IDR2_SEARCH_CANCEL_PRESSED" file="search_cancel_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR2_SEARCH_MAGNIFIER" file="search_magnifier.png" />
-      <structure type="chrome_scaled_image" name="IDR2_SEARCH_MAGNIFIER_RESULTS" file="search_magnifier_results.png" />
-      <structure type="chrome_scaled_image" name="IDR2_TEXTAREA_RESIZER" file="textarea_resize_corner.png" />
-      <structure type="chrome_scaled_image" name="IDR2_PASSWORD_GENERATION_ICON" file="password_generation.png" />
-      <structure type="chrome_scaled_image" name="IDR2_PASSWORD_GENERATION_ICON_HOVER" file="password_generation_hover.png" />
     </structures>
   </release>
 </grit>
diff --git a/content/app/resources/default_100_percent/broken_image.png b/content/app/resources/default_100_percent/broken_image.png
deleted file mode 100644
index ad76cc1..0000000
--- a/content/app/resources/default_100_percent/broken_image.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_cast_off.png b/content/app/resources/default_100_percent/mediaplayer_cast_off.png
deleted file mode 100644
index 050022d..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_cast_off.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_cast_on.png b/content/app/resources/default_100_percent/mediaplayer_cast_on.png
deleted file mode 100644
index 4f24bf9..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_cast_on.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_closedcaption.png b/content/app/resources/default_100_percent/mediaplayer_closedcaption.png
deleted file mode 100644
index a92b3d2..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_closedcaption.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_closedcaption_disabled.png b/content/app/resources/default_100_percent/mediaplayer_closedcaption_disabled.png
deleted file mode 100644
index 7ac1a89..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_closedcaption_disabled.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_closedcaption_down.png b/content/app/resources/default_100_percent/mediaplayer_closedcaption_down.png
deleted file mode 100644
index 60da8a29..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_closedcaption_down.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_closedcaption_hover.png b/content/app/resources/default_100_percent/mediaplayer_closedcaption_hover.png
deleted file mode 100644
index a3e069f..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_closedcaption_hover.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_fullscreen.png b/content/app/resources/default_100_percent/mediaplayer_fullscreen.png
deleted file mode 100644
index 713a877..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_fullscreen.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_fullscreen_disabled.png b/content/app/resources/default_100_percent/mediaplayer_fullscreen_disabled.png
deleted file mode 100644
index f0958a6..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_fullscreen_disabled.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_fullscreen_down.png b/content/app/resources/default_100_percent/mediaplayer_fullscreen_down.png
deleted file mode 100644
index 3a6cd5db..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_fullscreen_down.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_fullscreen_hover.png b/content/app/resources/default_100_percent/mediaplayer_fullscreen_hover.png
deleted file mode 100644
index 38e3330..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_fullscreen_hover.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_overlay_cast_off.png b/content/app/resources/default_100_percent/mediaplayer_overlay_cast_off.png
deleted file mode 100644
index e16f58e..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_overlay_cast_off.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_overlay_play.png b/content/app/resources/default_100_percent/mediaplayer_overlay_play.png
deleted file mode 100644
index cfd48d74..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_overlay_play.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_pause.png b/content/app/resources/default_100_percent/mediaplayer_pause.png
deleted file mode 100644
index b488e97..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_pause.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_pause_down.png b/content/app/resources/default_100_percent/mediaplayer_pause_down.png
deleted file mode 100644
index bf754e9..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_pause_down.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_pause_hover.png b/content/app/resources/default_100_percent/mediaplayer_pause_hover.png
deleted file mode 100644
index ac3334c..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_pause_hover.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_play.png b/content/app/resources/default_100_percent/mediaplayer_play.png
deleted file mode 100644
index 1e53d43..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_play.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_play_disabled.png b/content/app/resources/default_100_percent/mediaplayer_play_disabled.png
deleted file mode 100644
index 8e122001..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_play_disabled.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_play_down.png b/content/app/resources/default_100_percent/mediaplayer_play_down.png
deleted file mode 100644
index fa6a243..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_play_down.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_play_hover.png b/content/app/resources/default_100_percent/mediaplayer_play_hover.png
deleted file mode 100644
index a71c2f3..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_play_hover.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_slider_thumb.png b/content/app/resources/default_100_percent/mediaplayer_slider_thumb.png
deleted file mode 100644
index 4cc24d1..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_slider_thumb.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_slider_thumb_down.png b/content/app/resources/default_100_percent/mediaplayer_slider_thumb_down.png
deleted file mode 100644
index 483101d..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_slider_thumb_down.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_slider_thumb_hover.png b/content/app/resources/default_100_percent/mediaplayer_slider_thumb_hover.png
deleted file mode 100644
index 2a96842..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_slider_thumb_hover.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_disabled.png b/content/app/resources/default_100_percent/mediaplayer_sound_disabled.png
deleted file mode 100644
index 38fa4baa..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_disabled.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level0.png b/content/app/resources/default_100_percent/mediaplayer_sound_level0.png
deleted file mode 100644
index 22d6a10..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level0.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level0_down.png b/content/app/resources/default_100_percent/mediaplayer_sound_level0_down.png
deleted file mode 100644
index d956e58..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level0_down.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level0_hover.png b/content/app/resources/default_100_percent/mediaplayer_sound_level0_hover.png
deleted file mode 100644
index 56214a1..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level0_hover.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level1.png b/content/app/resources/default_100_percent/mediaplayer_sound_level1.png
deleted file mode 100644
index 2c6809a..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level1.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level1_down.png b/content/app/resources/default_100_percent/mediaplayer_sound_level1_down.png
deleted file mode 100644
index faa1888..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level1_down.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level1_hover.png b/content/app/resources/default_100_percent/mediaplayer_sound_level1_hover.png
deleted file mode 100644
index 1fdaf21..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level1_hover.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level2.png b/content/app/resources/default_100_percent/mediaplayer_sound_level2.png
deleted file mode 100644
index e98c51d..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level2.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level2_down.png b/content/app/resources/default_100_percent/mediaplayer_sound_level2_down.png
deleted file mode 100644
index 37f5d76..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level2_down.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level2_hover.png b/content/app/resources/default_100_percent/mediaplayer_sound_level2_hover.png
deleted file mode 100644
index e44356b..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level2_hover.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level3.png b/content/app/resources/default_100_percent/mediaplayer_sound_level3.png
deleted file mode 100644
index 05caa64..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level3.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level3_down.png b/content/app/resources/default_100_percent/mediaplayer_sound_level3_down.png
deleted file mode 100644
index db384048..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level3_down.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level3_hover.png b/content/app/resources/default_100_percent/mediaplayer_sound_level3_hover.png
deleted file mode 100644
index 838b07f..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_sound_level3_hover.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb.png b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb.png
deleted file mode 100644
index c4620f5..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_disabled.png b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_disabled.png
deleted file mode 100644
index d32434b..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_disabled.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_down.png b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_down.png
deleted file mode 100644
index 98ee8509..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_down.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_hover.png b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_hover.png
deleted file mode 100644
index 93cca9b..0000000
--- a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_hover.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/pan_icon.png b/content/app/resources/default_100_percent/pan_icon.png
deleted file mode 100644
index e07d962..0000000
--- a/content/app/resources/default_100_percent/pan_icon.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/password_generation.png b/content/app/resources/default_100_percent/password_generation.png
deleted file mode 100644
index 2b45ac3..0000000
--- a/content/app/resources/default_100_percent/password_generation.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/password_generation_hover.png b/content/app/resources/default_100_percent/password_generation_hover.png
deleted file mode 100644
index a832cdc..0000000
--- a/content/app/resources/default_100_percent/password_generation_hover.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/search_cancel.png b/content/app/resources/default_100_percent/search_cancel.png
deleted file mode 100644
index 1291bf84..0000000
--- a/content/app/resources/default_100_percent/search_cancel.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/search_cancel_pressed.png b/content/app/resources/default_100_percent/search_cancel_pressed.png
deleted file mode 100644
index 4abc0fd..0000000
--- a/content/app/resources/default_100_percent/search_cancel_pressed.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/search_magnifier.png b/content/app/resources/default_100_percent/search_magnifier.png
deleted file mode 100644
index 0aea5dae..0000000
--- a/content/app/resources/default_100_percent/search_magnifier.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/search_magnifier_results.png b/content/app/resources/default_100_percent/search_magnifier_results.png
deleted file mode 100644
index 2f773c06..0000000
--- a/content/app/resources/default_100_percent/search_magnifier_results.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_100_percent/textarea_resize_corner.png b/content/app/resources/default_100_percent/textarea_resize_corner.png
deleted file mode 100644
index a379bdd3..0000000
--- a/content/app/resources/default_100_percent/textarea_resize_corner.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_200_percent/broken_image.png b/content/app/resources/default_200_percent/broken_image.png
deleted file mode 100644
index 45edebc..0000000
--- a/content/app/resources/default_200_percent/broken_image.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_200_percent/pan_icon.png b/content/app/resources/default_200_percent/pan_icon.png
deleted file mode 100644
index d9c0c15..0000000
--- a/content/app/resources/default_200_percent/pan_icon.png
+++ /dev/null
Binary files differ
diff --git a/content/app/resources/default_200_percent/textarea_resize_corner.png b/content/app/resources/default_200_percent/textarea_resize_corner.png
deleted file mode 100644
index 77fa3ca..0000000
--- a/content/app/resources/default_200_percent/textarea_resize_corner.png
+++ /dev/null
Binary files differ
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index f96f9f5..577ed6d3 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -22,6 +22,7 @@
     "//base",
     "//base:base_static",
     "//content:resources",
+    "//content/browser/background_sync:background_sync_proto",
     "//content/browser/notifications:notification_proto",
     "//content/browser/service_worker:service_worker_proto",
     "//content/browser/speech/proto",
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index ab92723..1361164 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -150,6 +150,8 @@
 
   bool created_live_region = false;
   for (size_t i = 0; i < changes.size(); ++i) {
+    if (changes[i].type != NODE_CREATED && changes[i].type != SUBTREE_CREATED)
+      continue;
     BrowserAccessibility* obj = GetFromAXNode(changes[i].node);
     if (obj && obj->HasStringAttribute(ui::AX_ATTR_LIVE_STATUS)) {
       created_live_region = true;
diff --git a/content/browser/background_sync/BUILD.gn b/content/browser/background_sync/BUILD.gn
new file mode 100644
index 0000000..55aeded
--- /dev/null
+++ b/content/browser/background_sync/BUILD.gn
@@ -0,0 +1,11 @@
+# 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.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("background_sync_proto") {
+  sources = [
+    "background_sync.proto",
+  ]
+}
diff --git a/content/browser/background_sync/OWNERS b/content/browser/background_sync/OWNERS
new file mode 100644
index 0000000..1e6deee
--- /dev/null
+++ b/content/browser/background_sync/OWNERS
@@ -0,0 +1 @@
+jkarlin@chromium.org
diff --git a/content/browser/background_sync/PRESUBMIT.py b/content/browser/background_sync/PRESUBMIT.py
new file mode 100644
index 0000000..192a8c4
--- /dev/null
+++ b/content/browser/background_sync/PRESUBMIT.py
@@ -0,0 +1,12 @@
+# 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.
+
+"""Top-level presubmit script for src/content/browser/background_sync/
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+def CheckChangeOnUpload(input_api, output_api):
+  return input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
diff --git a/content/browser/background_sync/background_sync.proto b/content/browser/background_sync/background_sync.proto
new file mode 100644
index 0000000..46cc136
--- /dev/null
+++ b/content/browser/background_sync/background_sync.proto
@@ -0,0 +1,20 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package content;
+
+message BackgroundSyncRegistrationProto {
+  required int64 id = 1;
+  required string name = 2;
+  optional int64 min_period = 3;
+}
+
+message BackgroundSyncRegistrationsProto {
+  repeated BackgroundSyncRegistrationProto registration = 1;
+  required int64 next_registration_id = 2;
+}
\ No newline at end of file
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
new file mode 100644
index 0000000..7ea194f
--- /dev/null
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -0,0 +1,414 @@
+// 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 "content/browser/background_sync/background_sync_manager.h"
+
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "content/browser/background_sync/background_sync.pb.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace {
+const char kBackgroundSyncUserDataKey[] = "BackgroundSyncUserData";
+}
+
+namespace content {
+
+const BackgroundSyncManager::BackgroundSyncRegistration::RegistrationId
+    BackgroundSyncManager::BackgroundSyncRegistration::kInvalidRegistrationId =
+        -1;
+
+const BackgroundSyncManager::BackgroundSyncRegistration::RegistrationId
+    BackgroundSyncManager::BackgroundSyncRegistrations::kInitialId = 0;
+
+BackgroundSyncManager::BackgroundSyncRegistrations::
+    BackgroundSyncRegistrations()
+    : next_id(kInitialId) {
+}
+BackgroundSyncManager::BackgroundSyncRegistrations::BackgroundSyncRegistrations(
+    BackgroundSyncRegistration::RegistrationId next_id)
+    : next_id(next_id) {
+}
+BackgroundSyncManager::BackgroundSyncRegistrations::
+    ~BackgroundSyncRegistrations() {
+}
+
+// static
+scoped_ptr<BackgroundSyncManager> BackgroundSyncManager::Create(
+    const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context) {
+  BackgroundSyncManager* sync_manager =
+      new BackgroundSyncManager(service_worker_context);
+  sync_manager->Init();
+  return make_scoped_ptr(sync_manager);
+}
+
+BackgroundSyncManager::~BackgroundSyncManager() {
+}
+
+void BackgroundSyncManager::Register(
+    const GURL& origin,
+    int64 sw_registration_id,
+    const BackgroundSyncRegistration& sync_registration,
+    const StatusAndRegistrationCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_EQ(BackgroundSyncRegistration::kInvalidRegistrationId,
+            sync_registration.id);
+
+  StatusAndRegistrationCallback pending_callback =
+      base::Bind(&BackgroundSyncManager::PendingStatusAndRegistrationCallback,
+                 weak_ptr_factory_.GetWeakPtr(), callback);
+
+  op_scheduler_.ScheduleOperation(base::Bind(
+      &BackgroundSyncManager::RegisterImpl, weak_ptr_factory_.GetWeakPtr(),
+      origin, sw_registration_id, sync_registration, pending_callback));
+}
+
+void BackgroundSyncManager::Unregister(
+    const GURL& origin,
+    int64 sw_registration_id,
+    const std::string& sync_registration_name,
+    BackgroundSyncRegistration::RegistrationId sync_registration_id,
+    const StatusCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  StatusCallback pending_callback =
+      base::Bind(&BackgroundSyncManager::PendingStatusCallback,
+                 weak_ptr_factory_.GetWeakPtr(), callback);
+
+  op_scheduler_.ScheduleOperation(base::Bind(
+      &BackgroundSyncManager::UnregisterImpl, weak_ptr_factory_.GetWeakPtr(),
+      origin, sw_registration_id, sync_registration_name, sync_registration_id,
+      pending_callback));
+}
+
+void BackgroundSyncManager::GetRegistration(
+    const GURL& origin,
+    int64 sw_registration_id,
+    const std::string sync_registration_name,
+    const StatusAndRegistrationCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  StatusAndRegistrationCallback pending_callback =
+      base::Bind(&BackgroundSyncManager::PendingStatusAndRegistrationCallback,
+                 weak_ptr_factory_.GetWeakPtr(), callback);
+
+  op_scheduler_.ScheduleOperation(
+      base::Bind(&BackgroundSyncManager::GetRegistrationImpl,
+                 weak_ptr_factory_.GetWeakPtr(), origin, sw_registration_id,
+                 sync_registration_name, pending_callback));
+}
+
+BackgroundSyncManager::BackgroundSyncManager(
+    const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context)
+    : service_worker_context_(service_worker_context), weak_ptr_factory_(this) {
+}
+
+void BackgroundSyncManager::Init() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(!op_scheduler_.ScheduledOperations());
+
+  op_scheduler_.ScheduleOperation(base::Bind(&BackgroundSyncManager::InitImpl,
+                                             weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BackgroundSyncManager::InitImpl() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  GetDataFromBackend(
+      kBackgroundSyncUserDataKey,
+      base::Bind(&BackgroundSyncManager::InitDidGetDataFromBackend,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BackgroundSyncManager::InitDidGetDataFromBackend(
+    const std::vector<std::pair<int64, std::string>>& user_data,
+    ServiceWorkerStatusCode status) {
+  if (status != SERVICE_WORKER_OK && status != SERVICE_WORKER_ERROR_NOT_FOUND)
+    LOG(ERROR) << "Background Sync Failed to load from backend.";
+
+  bool corruption_detected = false;
+  for (const std::pair<int64, std::string>& data : user_data) {
+    BackgroundSyncRegistrationsProto registrations_proto;
+    if (registrations_proto.ParseFromString(data.second)) {
+      sw_to_registrations_map_[data.first] = BackgroundSyncRegistrations(
+          registrations_proto.next_registration_id());
+      BackgroundSyncRegistrations* registrations =
+          &sw_to_registrations_map_[data.first];
+
+      for (int i = 0, max = registrations_proto.registration_size(); i < max;
+           ++i) {
+        const BackgroundSyncRegistrationProto& registration_proto =
+            registrations_proto.registration(i);
+
+        if (registration_proto.id() >= registrations->next_id) {
+          corruption_detected = true;
+          break;
+        }
+
+        BackgroundSyncRegistration registration(registration_proto.id(),
+                                                registration_proto.name());
+        if (registration_proto.has_min_period())
+          registration.min_period = registration_proto.min_period();
+        registrations->name_to_registration_map[registration_proto.name()] =
+            registration;
+      }
+    }
+
+    if (corruption_detected)
+      break;
+  }
+
+  if (corruption_detected) {
+    LOG(ERROR) << "Corruption detected in background sync backend";
+    sw_to_registrations_map_.clear();
+  }
+
+  // TODO(jkarlin): Call the scheduling algorithm here.
+
+  op_scheduler_.CompleteOperationAndRunNext();
+}
+
+void BackgroundSyncManager::RegisterImpl(
+    const GURL& origin,
+    int64 sw_registration_id,
+    const BackgroundSyncRegistration& sync_registration,
+    const StatusAndRegistrationCallback& callback) {
+  BackgroundSyncRegistration existing_registration;
+  if (LookupRegistration(sw_registration_id, sync_registration.name,
+                         &existing_registration)) {
+    if (existing_registration.Equals(sync_registration)) {
+      base::MessageLoop::current()->PostTask(
+          FROM_HERE,
+          base::Bind(callback, ERROR_TYPE_OK, existing_registration));
+      return;
+    }
+  }
+
+  BackgroundSyncRegistration new_registration = sync_registration;
+  BackgroundSyncRegistrations* registrations =
+      &sw_to_registrations_map_[sw_registration_id];
+  new_registration.id = registrations->next_id++;
+
+  AddRegistrationToMap(sw_registration_id, new_registration);
+
+  StoreRegistrations(
+      origin, sw_registration_id,
+      base::Bind(&BackgroundSyncManager::RegisterDidStore,
+                 weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
+                 new_registration, existing_registration, callback));
+}
+
+bool BackgroundSyncManager::LookupRegistration(
+    int64 sw_registration_id,
+    const std::string& sync_registration_name,
+    BackgroundSyncRegistration* existing_registration) {
+  SWIdToRegistrationsMap::iterator it =
+      sw_to_registrations_map_.find(sw_registration_id);
+  if (it == sw_to_registrations_map_.end())
+    return false;
+
+  const BackgroundSyncRegistrations& registrations = it->second;
+  const auto name_and_registration_iter =
+      registrations.name_to_registration_map.find(sync_registration_name);
+  if (name_and_registration_iter ==
+      registrations.name_to_registration_map.end())
+    return false;
+
+  if (existing_registration)
+    *existing_registration = name_and_registration_iter->second;
+
+  return true;
+}
+
+void BackgroundSyncManager::StoreRegistrations(
+    const GURL& origin,
+    int64 sw_registration_id,
+    const ServiceWorkerStorage::StatusCallback& callback) {
+  // Serialize the data.
+  const BackgroundSyncRegistrations& registrations =
+      sw_to_registrations_map_[sw_registration_id];
+  BackgroundSyncRegistrationsProto registrations_proto;
+  registrations_proto.set_next_registration_id(registrations.next_id);
+
+  for (const auto& name_and_registration :
+       registrations.name_to_registration_map) {
+    const BackgroundSyncRegistration& registration =
+        name_and_registration.second;
+    BackgroundSyncRegistrationProto* registration_proto =
+        registrations_proto.add_registration();
+    registration_proto->set_id(registration.id);
+    registration_proto->set_name(registration.name);
+    if (registration.min_period != 0)
+      registration_proto->set_min_period(registration.min_period);
+  }
+  std::string serialized;
+  bool success = registrations_proto.SerializeToString(&serialized);
+  DCHECK(success);
+
+  StoreDataInBackend(sw_registration_id, origin, kBackgroundSyncUserDataKey,
+                     serialized, callback);
+}
+
+void BackgroundSyncManager::RegisterDidStore(
+    int64 sw_registration_id,
+    const BackgroundSyncRegistration& new_registration,
+    const BackgroundSyncRegistration& previous_registration,
+    const StatusAndRegistrationCallback& callback,
+    ServiceWorkerStatusCode status) {
+  if (status != SERVICE_WORKER_OK) {
+    // Restore the previous state.
+    if (previous_registration.id !=
+        BackgroundSyncRegistration::kInvalidRegistrationId) {
+      AddRegistrationToMap(sw_registration_id, previous_registration);
+    } else {
+      RemoveRegistrationFromMap(sw_registration_id, new_registration.name,
+                                nullptr);
+    }
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(callback, ERROR_TYPE_STORAGE, BackgroundSyncRegistration()));
+    return;
+  }
+
+  // TODO(jkarlin): Run the registration algorithm.
+  base::MessageLoop::current()->PostTask(
+      FROM_HERE, base::Bind(callback, ERROR_TYPE_OK, new_registration));
+}
+
+void BackgroundSyncManager::RemoveRegistrationFromMap(
+    int64 sw_registration_id,
+    const std::string& sync_registration_name,
+    BackgroundSyncRegistration* old_registration) {
+  DCHECK(
+      LookupRegistration(sw_registration_id, sync_registration_name, nullptr));
+
+  BackgroundSyncRegistrations* registrations =
+      &sw_to_registrations_map_[sw_registration_id];
+
+  const auto name_and_registration_iter =
+      registrations->name_to_registration_map.find(sync_registration_name);
+  if (old_registration)
+    *old_registration = name_and_registration_iter->second;
+
+  registrations->name_to_registration_map.erase(name_and_registration_iter);
+}
+
+void BackgroundSyncManager::AddRegistrationToMap(
+    int64 sw_registration_id,
+    const BackgroundSyncRegistration& sync_registration) {
+  DCHECK_NE(BackgroundSyncRegistration::kInvalidRegistrationId,
+            sw_registration_id);
+
+  BackgroundSyncRegistrations* registrations =
+      &sw_to_registrations_map_[sw_registration_id];
+  registrations->name_to_registration_map[sync_registration.name] =
+      sync_registration;
+}
+
+void BackgroundSyncManager::StoreDataInBackend(
+    int64 sw_registration_id,
+    const GURL& origin,
+    const std::string& key,
+    const std::string& data,
+    const ServiceWorkerStorage::StatusCallback& callback) {
+  service_worker_context_->context()->storage()->StoreUserData(
+      sw_registration_id, origin, key, data, callback);
+}
+
+void BackgroundSyncManager::GetDataFromBackend(
+    const std::string& key,
+    const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
+        callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  service_worker_context_->context()->storage()->GetUserDataForAllRegistrations(
+      key, callback);
+}
+
+void BackgroundSyncManager::UnregisterImpl(
+    const GURL& origin,
+    int64 sw_registration_id,
+    const std::string& sync_registration_name,
+    BackgroundSyncRegistration::RegistrationId sync_registration_id,
+    const StatusCallback& callback) {
+  BackgroundSyncRegistration existing_registration;
+  if (!LookupRegistration(sw_registration_id, sync_registration_name,
+                          &existing_registration) ||
+      existing_registration.id != sync_registration_id) {
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE, base::Bind(callback, ERROR_TYPE_NOT_FOUND));
+    return;
+  }
+
+  BackgroundSyncRegistration old_sync_registration;
+  RemoveRegistrationFromMap(sw_registration_id, sync_registration_name,
+                            &old_sync_registration);
+
+  StoreRegistrations(
+      origin, sw_registration_id,
+      base::Bind(&BackgroundSyncManager::UnregisterDidStore,
+                 weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
+                 old_sync_registration, callback));
+}
+
+void BackgroundSyncManager::UnregisterDidStore(
+    int64 sw_registration_id,
+    const BackgroundSyncRegistration& old_sync_registration,
+    const StatusCallback& callback,
+    ServiceWorkerStatusCode status) {
+  if (status != SERVICE_WORKER_OK) {
+    // Restore the previous state.
+    AddRegistrationToMap(sw_registration_id, old_sync_registration);
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE, base::Bind(callback, ERROR_TYPE_STORAGE));
+    return;
+  }
+
+  // TODO(jkarlin): Run the registration algorithm.
+  base::MessageLoop::current()->PostTask(FROM_HERE,
+                                         base::Bind(callback, ERROR_TYPE_OK));
+}
+
+void BackgroundSyncManager::GetRegistrationImpl(
+    const GURL& origin,
+    int64 sw_registration_id,
+    const std::string sync_registration_name,
+    const StatusAndRegistrationCallback& callback) {
+  BackgroundSyncRegistration out_registration;
+  if (!LookupRegistration(sw_registration_id, sync_registration_name,
+                          &out_registration)) {
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE, base::Bind(callback, ERROR_TYPE_NOT_FOUND,
+                              BackgroundSyncRegistration()));
+    return;
+  }
+
+  base::MessageLoop::current()->PostTask(
+      FROM_HERE, base::Bind(callback, ERROR_TYPE_OK, out_registration));
+}
+
+void BackgroundSyncManager::PendingStatusAndRegistrationCallback(
+    const StatusAndRegistrationCallback& callback,
+    ErrorType error,
+    const BackgroundSyncRegistration& sync_registration) {
+  // The callback might delete this object, so hang onto a weak ptr to find out.
+  base::WeakPtr<BackgroundSyncManager> manager = weak_ptr_factory_.GetWeakPtr();
+  callback.Run(error, sync_registration);
+  if (manager)
+    op_scheduler_.CompleteOperationAndRunNext();
+}
+
+void BackgroundSyncManager::PendingStatusCallback(
+    const StatusCallback& callback,
+    ErrorType error) {
+  // The callback might delete this object, so hang onto a weak ptr to find out.
+  base::WeakPtr<BackgroundSyncManager> manager = weak_ptr_factory_.GetWeakPtr();
+  callback.Run(error);
+  if (manager)
+    op_scheduler_.CompleteOperationAndRunNext();
+}
+
+}  // namespace content
diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h
new file mode 100644
index 0000000..674186c8
--- /dev/null
+++ b/content/browser/background_sync/background_sync_manager.h
@@ -0,0 +1,221 @@
+// 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 CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_MANAGER_H_
+#define CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_MANAGER_H_
+
+#include <list>
+#include <map>
+
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_cache_scheduler.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class ServiceWorkerContextWrapper;
+
+// BackgroundSyncManager manages and stores the set of background sync
+// registrations across all registered service workers for a profile.
+// Registrations are stored along with their associated Service Worker
+// registration in ServiceWorkerStorage. If the ServiceWorker is unregistered,
+// the sync registrations are removed. This class expects to be run on the IO
+// thread. The asynchronous methods are executed sequentially.
+
+// TODO(jkarlin): Check permissions when registering, scheduling, and firing
+// background sync. In the meantime, --enable-service-worker-sync is required to
+// fire a sync event.
+// TODO(jkarlin): Unregister syncs when permission is revoked.
+// TODO(jkarlin): Create a background sync scheduler to actually run the
+// registered events.
+// TODO(jkarlin): Keep the browser alive if "Let Google Chrome Run in the
+// Background" is true and a sync is registered.
+// TODO(jkarlin): Unregister syncs when storage for an origin is cleared.
+// TODO(jkarlin): Detect and handle a corrupt or broken backend.
+class CONTENT_EXPORT BackgroundSyncManager {
+ public:
+  enum ErrorType {
+    ERROR_TYPE_OK = 0,
+    ERROR_TYPE_STORAGE,
+    ERROR_TYPE_NOT_FOUND
+  };
+
+  // TODO(jkarlin): Remove this and use the struct from IPC messages once it
+  // lands.
+  struct CONTENT_EXPORT BackgroundSyncRegistration {
+    using RegistrationId = int64;
+    static const RegistrationId kInvalidRegistrationId;
+
+    BackgroundSyncRegistration()
+        : BackgroundSyncRegistration(kInvalidRegistrationId, "") {}
+    explicit BackgroundSyncRegistration(const std::string& name)
+        : BackgroundSyncRegistration(kInvalidRegistrationId, name) {}
+    BackgroundSyncRegistration(int64 id, const std::string& name)
+        : id(id), min_period(0), name(name) {}
+
+    bool Equals(const BackgroundSyncRegistration& other) {
+      return this->name == other.name && this->min_period == other.min_period;
+    }
+
+    RegistrationId id;
+    int64 min_period;
+    std::string name;
+  };
+
+  struct CONTENT_EXPORT BackgroundSyncRegistrations {
+    using NameToRegistrationMap =
+        std::map<std::string, BackgroundSyncRegistration>;
+    static const BackgroundSyncRegistration::RegistrationId kInitialId;
+
+    BackgroundSyncRegistrations();
+    explicit BackgroundSyncRegistrations(
+        BackgroundSyncRegistration::RegistrationId next_id);
+    ~BackgroundSyncRegistrations();
+
+    NameToRegistrationMap name_to_registration_map;
+    BackgroundSyncRegistration::RegistrationId next_id;
+  };
+
+  using StatusCallback = base::Callback<void(ErrorType)>;
+  using StatusAndRegistrationCallback =
+      base::Callback<void(ErrorType, const BackgroundSyncRegistration&)>;
+
+  static scoped_ptr<BackgroundSyncManager> Create(
+      const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context);
+  virtual ~BackgroundSyncManager();
+
+  // Stores the given background sync registration and adds it to the scheduling
+  // queue. Overwrites any existing registration with the same name but
+  // different parameters (other than the id). Calls |callback| with ErrorTypeOK
+  // and the accepted registration on success. The accepted registration will
+  // have a unique id. It may also have altered parameters if the user or UA
+  // chose different parameters than those supplied.
+  void Register(const GURL& origin,
+                int64 sw_registration_id,
+                const BackgroundSyncRegistration& sync_registration,
+                const StatusAndRegistrationCallback& callback);
+
+  // Removes the background sync registration with |sync_registration_name| if
+  // the |sync_registration_id| matches. |sync_registration_id| will not match
+  // if, for instance, a new registration with the same name has replaced it.
+  // Calls |callback| with ErrorTypeNotFound if no match is found. Calls
+  // |callback| with ErrorTypeOK on success.
+  void Unregister(
+      const GURL& origin,
+      int64 sw_registration_id,
+      const std::string& sync_registration_name,
+      BackgroundSyncRegistration::RegistrationId sync_registration_id,
+      const StatusCallback& callback);
+
+  // Finds the background sync registration associated with
+  // |sw_registration_id|. Calls |callback| with ErrorTypeNotFound if it doesn't
+  // exist. Calls |callback| with ErrorTypeOK on success.
+  void GetRegistration(const GURL& origin,
+                       int64 sw_registration_id,
+                       const std::string sync_registration_name,
+                       const StatusAndRegistrationCallback& callback);
+
+ protected:
+  explicit BackgroundSyncManager(
+      const scoped_refptr<ServiceWorkerContextWrapper>& context);
+
+  // Init must be called before any public member function. Only call it once.
+  void Init();
+
+  // The following methods are virtual for testing.
+  virtual void StoreDataInBackend(
+      int64 sw_registration_id,
+      const GURL& origin,
+      const std::string& key,
+      const std::string& data,
+      const ServiceWorkerStorage::StatusCallback& callback);
+  virtual void GetDataFromBackend(
+      const std::string& key,
+      const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
+          callback);
+
+ private:
+  using PermissionStatusCallback = base::Callback<void(bool)>;
+  using SWIdToRegistrationsMap = std::map<int64, BackgroundSyncRegistrations>;
+
+  // Returns the existing registration in |existing_registration| if it is not
+  // null.
+  bool LookupRegistration(int64 sw_registration_id,
+                          const std::string& sync_registration_name,
+                          BackgroundSyncRegistration* existing_registration);
+
+  // Store all registrations for a given |sw_registration_id|.
+  void StoreRegistrations(const GURL& origin,
+                          int64 sw_registration_id,
+                          const ServiceWorkerStorage::StatusCallback& callback);
+
+  // If the registration is in the map, removes it and returns the removed
+  // registration in |old_registration|. |old_registration| may be null.
+  void RemoveRegistrationFromMap(int64 sw_registration_id,
+                                 const std::string& sync_registration_name,
+                                 BackgroundSyncRegistration* old_registration);
+
+  void AddRegistrationToMap(
+      int64 sw_registration_id,
+      const BackgroundSyncRegistration& sync_registration);
+
+  void InitImpl();
+  void InitDidGetDataFromBackend(
+      const std::vector<std::pair<int64, std::string>>& user_data,
+      ServiceWorkerStatusCode status);
+
+  // Register callbacks
+  void RegisterImpl(const GURL& origin,
+                    int64 sw_registration_id,
+                    const BackgroundSyncRegistration& sync_registration,
+                    const StatusAndRegistrationCallback& callback);
+  void RegisterDidStore(int64 sw_registration_id,
+                        const BackgroundSyncRegistration& sync_registration,
+                        const BackgroundSyncRegistration& previous_registration,
+                        const StatusAndRegistrationCallback& callback,
+                        ServiceWorkerStatusCode status);
+
+  // Unregister callbacks
+  void UnregisterImpl(
+      const GURL& origin,
+      int64 sw_registration_id,
+      const std::string& sync_registration_name,
+      BackgroundSyncRegistration::RegistrationId sync_registration_id,
+      const StatusCallback& callback);
+  void UnregisterDidStore(
+      int64 sw_registration_id,
+      const BackgroundSyncRegistration& old_sync_registration,
+      const StatusCallback& callback,
+      ServiceWorkerStatusCode status);
+
+  // GetRegistration callbacks
+  void GetRegistrationImpl(const GURL& origin,
+                           int64 sw_registration_id,
+                           const std::string sync_registration_name,
+                           const StatusAndRegistrationCallback& callback);
+
+  // Operation Scheduling callbacks
+  void PendingStatusAndRegistrationCallback(
+      const StatusAndRegistrationCallback& callback,
+      ErrorType error,
+      const BackgroundSyncRegistration& sync_registration);
+  void PendingStatusCallback(const StatusCallback& callback, ErrorType error);
+
+  SWIdToRegistrationsMap sw_to_registrations_map_;
+  ServiceWorkerCacheScheduler op_scheduler_;
+  scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
+
+  base::WeakPtrFactory<BackgroundSyncManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundSyncManager);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_MANAGER_H_
diff --git a/content/browser/background_sync/background_sync_manager_unittest.cc b/content/browser/background_sync/background_sync_manager_unittest.cc
new file mode 100644
index 0000000..c47651a
--- /dev/null
+++ b/content/browser/background_sync/background_sync_manager_unittest.cc
@@ -0,0 +1,515 @@
+// 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 "content/browser/background_sync/background_sync_manager.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/thread_task_runner_handle.h"
+#include "content/browser/browser_thread_impl.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const char kOriginUrl[] = "https://example.com";
+const int64 kServiceWorkerVersionId = 0;
+const int64 kServiceWorkerId1 = 1;
+const int64 kServiceWorkerId2 = 2;
+}
+
+namespace content {
+
+// A BackgroundSyncManager that can simulate delaying and corrupting the
+// backend. This class assumes (and verifies) that only one operation runs at a
+// time.
+class TestBackgroundSyncManager : public BackgroundSyncManager {
+ public:
+  explicit TestBackgroundSyncManager(
+      const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context)
+      : BackgroundSyncManager(service_worker_context) {}
+
+  void DoInit() { Init(); }
+
+  void StoreDataInBackendContinue(
+      int64 sw_registration_id,
+      const GURL& origin,
+      const std::string& key,
+      const std::string& data,
+      const ServiceWorkerStorage::StatusCallback& callback) {
+    BackgroundSyncManager::StoreDataInBackend(sw_registration_id, origin, key,
+                                              data, callback);
+  }
+
+  void GetDataFromBackendContinue(
+      const std::string& key,
+      const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
+          callback) {
+    BackgroundSyncManager::GetDataFromBackend(key, callback);
+  }
+
+  void Continue() {
+    continuation_.Run();
+    continuation_.Reset();
+  }
+
+  void set_corrupt_backend(bool corrupt_backend) {
+    corrupt_backend_ = corrupt_backend;
+  }
+  void set_delay_backend(bool delay_backend) { delay_backend_ = delay_backend; }
+
+ protected:
+  void StoreDataInBackend(
+      int64 sw_registration_id,
+      const GURL& origin,
+      const std::string& key,
+      const std::string& data,
+      const ServiceWorkerStorage::StatusCallback& callback) override {
+    EXPECT_TRUE(continuation_.is_null());
+    if (corrupt_backend_) {
+      base::MessageLoop::current()->PostTask(
+          FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
+      return;
+    }
+    continuation_ =
+        base::Bind(&TestBackgroundSyncManager::StoreDataInBackendContinue,
+                   base::Unretained(this), sw_registration_id, origin, key,
+                   data, callback);
+    if (delay_backend_)
+      return;
+
+    Continue();
+  }
+
+  void GetDataFromBackend(
+      const std::string& key,
+      const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
+          callback) override {
+    EXPECT_TRUE(continuation_.is_null());
+    if (corrupt_backend_) {
+      base::MessageLoop::current()->PostTask(
+          FROM_HERE,
+          base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
+                     SERVICE_WORKER_ERROR_FAILED));
+      return;
+    }
+    continuation_ =
+        base::Bind(&TestBackgroundSyncManager::GetDataFromBackendContinue,
+                   base::Unretained(this), key, callback);
+    if (delay_backend_)
+      return;
+
+    Continue();
+  }
+
+ private:
+  bool corrupt_backend_ = false;
+  bool delay_backend_ = false;
+  base::Closure continuation_;
+};
+
+class BackgroundSyncManagerTest : public testing::Test {
+ public:
+  BackgroundSyncManagerTest()
+      : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+        service_worker_context_(new ServiceWorkerContextWrapper(NULL)),
+        origin_(kOriginUrl),
+        sync_reg_1_(BackgroundSyncManager::BackgroundSyncRegistration("foo")),
+        sync_reg_2_(BackgroundSyncManager::BackgroundSyncRegistration("bar")),
+        callback_error_(BackgroundSyncManager::ERROR_TYPE_OK),
+        callback_sw_status_code_(SERVICE_WORKER_OK) {}
+
+  void SetUp() override {
+    scoped_ptr<ServiceWorkerDatabaseTaskManager> database_task_manager(
+        new MockServiceWorkerDatabaseTaskManager(
+            base::ThreadTaskRunnerHandle::Get()));
+
+    service_worker_context_->InitInternal(
+        base::FilePath(), database_task_manager.Pass(),
+        base::ThreadTaskRunnerHandle::Get(), NULL, NULL);
+    context_ptr_ = service_worker_context_->context()->AsWeakPtr();
+
+    background_sync_manager_ =
+        BackgroundSyncManager::Create(service_worker_context_);
+
+    // Wait for storage to finish initializing before registering service
+    // workers.
+    base::RunLoop().RunUntilIdle();
+
+    RegisterServiceWorker(kServiceWorkerId1);
+    RegisterServiceWorker(kServiceWorkerId2);
+  }
+
+  void StatusAndRegistrationCallback(
+      bool* was_called,
+      BackgroundSyncManager::ErrorType error,
+      const BackgroundSyncManager::BackgroundSyncRegistration& registration) {
+    *was_called = true;
+    callback_error_ = error;
+    callback_registration_ = registration;
+  }
+
+  void StatusCallback(bool* was_called,
+                      BackgroundSyncManager::ErrorType error) {
+    *was_called = true;
+    callback_error_ = error;
+  }
+
+ protected:
+  TestBackgroundSyncManager* UseTestBackgroundSyncManager() {
+    TestBackgroundSyncManager* manager =
+        new TestBackgroundSyncManager(service_worker_context_);
+    background_sync_manager_.reset(manager);
+    manager->DoInit();
+    return manager;
+  }
+
+  bool Register(const BackgroundSyncManager::BackgroundSyncRegistration&
+                    sync_registration) {
+    return RegisterWithServiceWorkerId(kServiceWorkerId1, sync_registration);
+  }
+
+  bool RegisterWithServiceWorkerId(
+      int64 sw_registration_id,
+      const BackgroundSyncManager::BackgroundSyncRegistration&
+          sync_registration) {
+    bool was_called = false;
+    background_sync_manager_->Register(
+        origin_, sw_registration_id, sync_registration,
+        base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationCallback,
+                   base::Unretained(this), &was_called));
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(was_called);
+    return callback_error_ == BackgroundSyncManager::ERROR_TYPE_OK;
+  }
+
+  bool Unregister(const BackgroundSyncManager::BackgroundSyncRegistration&
+                      sync_registration) {
+    return UnregisterWithServiceWorkerId(kServiceWorkerId1, sync_registration);
+  }
+
+  bool UnregisterWithServiceWorkerId(
+      int64 sw_registration_id,
+      const BackgroundSyncManager::BackgroundSyncRegistration&
+          sync_registration) {
+    bool was_called = false;
+    background_sync_manager_->Unregister(
+        origin_, sw_registration_id, sync_registration.name,
+        sync_registration.id,
+        base::Bind(&BackgroundSyncManagerTest::StatusCallback,
+                   base::Unretained(this), &was_called));
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(was_called);
+    return callback_error_ == BackgroundSyncManager::ERROR_TYPE_OK;
+  }
+
+  bool GetRegistration(const std::string& sync_registration_name) {
+    return GetRegistrationWithServiceWorkerId(kServiceWorkerId1,
+                                              sync_registration_name);
+  }
+
+  bool GetRegistrationWithServiceWorkerId(
+      int64 sw_registration_id,
+      const std::string& sync_registration_name) {
+    bool was_called = false;
+    background_sync_manager_->GetRegistration(
+        origin_, sw_registration_id, sync_registration_name,
+        base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationCallback,
+                   base::Unretained(this), &was_called));
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(was_called);
+
+    if (callback_error_ == BackgroundSyncManager::ERROR_TYPE_OK)
+      EXPECT_TRUE(sync_registration_name == callback_registration_.name);
+
+    return callback_error_ == BackgroundSyncManager::ERROR_TYPE_OK;
+  }
+
+  void StorageRegistrationCallback(ServiceWorkerStatusCode result) {
+    callback_sw_status_code_ = result;
+  }
+
+  void RegisterServiceWorker(uint64 sw_registration_id) {
+    scoped_refptr<ServiceWorkerRegistration> live_registration =
+        new ServiceWorkerRegistration(origin_, sw_registration_id,
+                                      context_ptr_);
+
+    scoped_refptr<ServiceWorkerVersion> live_version = new ServiceWorkerVersion(
+        live_registration.get(), GURL(std::string(kOriginUrl) + "/script.js"),
+        kServiceWorkerVersionId, context_ptr_);
+    live_version->SetStatus(ServiceWorkerVersion::INSTALLED);
+    live_registration->SetWaitingVersion(live_version.get());
+
+    service_worker_context_->context()->storage()->StoreRegistration(
+        live_registration.get(), live_version.get(),
+        base::Bind(&BackgroundSyncManagerTest::StorageRegistrationCallback,
+                   base::Unretained(this)));
+
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(SERVICE_WORKER_OK, callback_sw_status_code_);
+  }
+
+  TestBrowserThreadBundle browser_thread_bundle_;
+  scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
+  scoped_ptr<BackgroundSyncManager> background_sync_manager_;
+  base::WeakPtr<ServiceWorkerContextCore> context_ptr_;
+
+  const GURL origin_;
+  BackgroundSyncManager::BackgroundSyncRegistration sync_reg_1_;
+  BackgroundSyncManager::BackgroundSyncRegistration sync_reg_2_;
+
+  // Callback values.
+  BackgroundSyncManager::ErrorType callback_error_;
+  BackgroundSyncManager::BackgroundSyncRegistration callback_registration_;
+  ServiceWorkerStatusCode callback_sw_status_code_;
+};
+
+TEST_F(BackgroundSyncManagerTest, Register) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+}
+
+TEST_F(BackgroundSyncManagerTest, RegistractionIntact) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+  EXPECT_STREQ(sync_reg_1_.name.c_str(), callback_registration_.name.c_str());
+  EXPECT_NE(
+      BackgroundSyncManager::BackgroundSyncRegistration::kInvalidRegistrationId,
+      callback_registration_.id);
+}
+
+TEST_F(BackgroundSyncManagerTest, RegisterExistingKeepsId) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+  BackgroundSyncManager::BackgroundSyncRegistration first_registration =
+      callback_registration_;
+  EXPECT_TRUE(Register(sync_reg_1_));
+  EXPECT_TRUE(callback_registration_.Equals(first_registration));
+  EXPECT_EQ(first_registration.id, callback_registration_.id);
+}
+
+TEST_F(BackgroundSyncManagerTest, RegisterOverwrites) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+  BackgroundSyncManager::BackgroundSyncRegistration first_registration =
+      callback_registration_;
+
+  sync_reg_1_.min_period = 100;
+  EXPECT_TRUE(Register(sync_reg_1_));
+  EXPECT_LT(first_registration.id, callback_registration_.id);
+  EXPECT_FALSE(callback_registration_.Equals(first_registration));
+}
+
+TEST_F(BackgroundSyncManagerTest, RegisterBadBackend) {
+  TestBackgroundSyncManager* manager = UseTestBackgroundSyncManager();
+  manager->set_corrupt_backend(true);
+  EXPECT_FALSE(Register(sync_reg_1_));
+  manager->set_corrupt_backend(false);
+  EXPECT_FALSE(GetRegistration(sync_reg_1_.name));
+}
+
+TEST_F(BackgroundSyncManagerTest, RegisterOverwriteBadBackend) {
+  TestBackgroundSyncManager* manager = UseTestBackgroundSyncManager();
+  EXPECT_TRUE(Register(sync_reg_1_));
+  BackgroundSyncManager::BackgroundSyncRegistration first_registration =
+      callback_registration_;
+
+  sync_reg_1_.min_period = 100;
+
+  manager->set_corrupt_backend(true);
+  EXPECT_FALSE(Register(sync_reg_1_));
+  EXPECT_TRUE(GetRegistration(sync_reg_1_.name));
+  EXPECT_EQ(callback_registration_.id, first_registration.id);
+  EXPECT_TRUE(callback_registration_.Equals(first_registration));
+}
+
+TEST_F(BackgroundSyncManagerTest, TwoRegistrations) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+  EXPECT_TRUE(Register(sync_reg_2_));
+}
+
+TEST_F(BackgroundSyncManagerTest, GetRegistrationNonExisting) {
+  EXPECT_FALSE(GetRegistration(sync_reg_1_.name));
+}
+
+TEST_F(BackgroundSyncManagerTest, GetRegistrationExisting) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+  EXPECT_TRUE(GetRegistration(sync_reg_1_.name));
+  EXPECT_FALSE(GetRegistration(sync_reg_2_.name));
+}
+
+TEST_F(BackgroundSyncManagerTest, GetRegistrationBadBackend) {
+  TestBackgroundSyncManager* manager = UseTestBackgroundSyncManager();
+  EXPECT_TRUE(Register(sync_reg_1_));
+  manager->set_corrupt_backend(true);
+  EXPECT_TRUE(GetRegistration(sync_reg_1_.name));
+  EXPECT_FALSE(GetRegistration(sync_reg_2_.name));
+  manager->set_corrupt_backend(false);
+  EXPECT_TRUE(GetRegistration(sync_reg_1_.name));
+  EXPECT_FALSE(GetRegistration(sync_reg_2_.name));
+}
+
+TEST_F(BackgroundSyncManagerTest, Unregister) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+  EXPECT_TRUE(Unregister(callback_registration_));
+  EXPECT_FALSE(GetRegistration(sync_reg_1_.name));
+}
+
+TEST_F(BackgroundSyncManagerTest, UnregisterWrongId) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+  callback_registration_.id += 1;
+  EXPECT_FALSE(Unregister(callback_registration_));
+}
+
+TEST_F(BackgroundSyncManagerTest, Reregister) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+  EXPECT_TRUE(Unregister(callback_registration_));
+  EXPECT_TRUE(Register(sync_reg_1_));
+}
+
+TEST_F(BackgroundSyncManagerTest, UnregisterNonExisting) {
+  EXPECT_FALSE(Unregister(sync_reg_1_));
+  EXPECT_EQ(BackgroundSyncManager::ERROR_TYPE_NOT_FOUND, callback_error_);
+}
+
+TEST_F(BackgroundSyncManagerTest, UnregisterSecond) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+  EXPECT_TRUE(Register(sync_reg_2_));
+  EXPECT_TRUE(Unregister(callback_registration_));
+  EXPECT_TRUE(GetRegistration(sync_reg_1_.name));
+  EXPECT_TRUE(Register(sync_reg_2_));
+}
+
+TEST_F(BackgroundSyncManagerTest, UnregisterBadBackend) {
+  TestBackgroundSyncManager* manager = UseTestBackgroundSyncManager();
+  sync_reg_1_.min_period += 1;
+  EXPECT_TRUE(Register(sync_reg_1_));
+  manager->set_corrupt_backend(true);
+  EXPECT_FALSE(Unregister(callback_registration_));
+  manager->set_corrupt_backend(false);
+  EXPECT_TRUE(GetRegistration(sync_reg_1_.name));
+  EXPECT_TRUE(callback_registration_.Equals(sync_reg_1_));
+}
+
+TEST_F(BackgroundSyncManagerTest, RegistrationIncreasesId) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+  BackgroundSyncManager::BackgroundSyncRegistration registered_sync =
+      callback_registration_;
+  BackgroundSyncManager::BackgroundSyncRegistration::RegistrationId cur_id =
+      callback_registration_.id;
+
+  EXPECT_TRUE(GetRegistration(sync_reg_1_.name));
+  EXPECT_TRUE(Register(sync_reg_2_));
+  EXPECT_LT(cur_id, callback_registration_.id);
+  cur_id = callback_registration_.id;
+
+  EXPECT_TRUE(Unregister(registered_sync));
+  EXPECT_TRUE(Register(sync_reg_1_));
+  EXPECT_LT(cur_id, callback_registration_.id);
+}
+
+TEST_F(BackgroundSyncManagerTest, RebootRecovery) {
+  EXPECT_TRUE(Register(sync_reg_1_));
+
+  background_sync_manager_ =
+      BackgroundSyncManager::Create(service_worker_context_);
+
+  EXPECT_TRUE(GetRegistration(sync_reg_1_.name));
+  EXPECT_FALSE(GetRegistration(sync_reg_2_.name));
+}
+
+TEST_F(BackgroundSyncManagerTest, RebootRecoveryTwoServiceWorkers) {
+  EXPECT_TRUE(RegisterWithServiceWorkerId(kServiceWorkerId1, sync_reg_1_));
+  EXPECT_TRUE(RegisterWithServiceWorkerId(kServiceWorkerId2, sync_reg_2_));
+
+  background_sync_manager_ =
+      BackgroundSyncManager::Create(service_worker_context_);
+
+  EXPECT_TRUE(
+      GetRegistrationWithServiceWorkerId(kServiceWorkerId1, sync_reg_1_.name));
+  EXPECT_FALSE(
+      GetRegistrationWithServiceWorkerId(kServiceWorkerId1, sync_reg_2_.name));
+  EXPECT_FALSE(
+      GetRegistrationWithServiceWorkerId(kServiceWorkerId2, sync_reg_1_.name));
+  EXPECT_TRUE(
+      GetRegistrationWithServiceWorkerId(kServiceWorkerId2, sync_reg_2_.name));
+
+  EXPECT_TRUE(
+      GetRegistrationWithServiceWorkerId(kServiceWorkerId1, sync_reg_1_.name));
+  EXPECT_TRUE(
+      GetRegistrationWithServiceWorkerId(kServiceWorkerId2, sync_reg_2_.name));
+
+  EXPECT_TRUE(RegisterWithServiceWorkerId(kServiceWorkerId1, sync_reg_2_));
+  EXPECT_TRUE(RegisterWithServiceWorkerId(kServiceWorkerId2, sync_reg_1_));
+}
+
+TEST_F(BackgroundSyncManagerTest, InitWithCorruptBackend) {
+  TestBackgroundSyncManager* manager =
+      new TestBackgroundSyncManager(service_worker_context_);
+  background_sync_manager_.reset(manager);
+  manager->set_corrupt_backend(true);
+  manager->DoInit();
+
+  EXPECT_FALSE(Register(sync_reg_1_));
+  EXPECT_FALSE(GetRegistration(sync_reg_1_.name));
+}
+
+TEST_F(BackgroundSyncManagerTest, SequentialOperations) {
+  // Schedule Init and all of the operations on a delayed backend. Verify that
+  // the operations complete sequentially.
+  TestBackgroundSyncManager* manager =
+      new TestBackgroundSyncManager(service_worker_context_);
+  background_sync_manager_.reset(manager);
+  manager->set_delay_backend(true);
+  manager->DoInit();
+
+  const int64 kExpectedInitialId =
+      BackgroundSyncManager::BackgroundSyncRegistrations::kInitialId;
+
+  bool register_called = false;
+  bool unregister_called = false;
+  bool get_registration_called = false;
+  manager->Register(
+      origin_, kServiceWorkerId1, sync_reg_1_,
+      base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationCallback,
+                 base::Unretained(this), &register_called));
+  manager->Unregister(origin_, kServiceWorkerId1, sync_reg_1_.name,
+                      kExpectedInitialId,
+                      base::Bind(&BackgroundSyncManagerTest::StatusCallback,
+                                 base::Unretained(this), &unregister_called));
+  manager->GetRegistration(
+      origin_, kServiceWorkerId1, sync_reg_1_.name,
+      base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationCallback,
+                 base::Unretained(this), &get_registration_called));
+
+  base::RunLoop().RunUntilIdle();
+  // Init should be blocked while loading from the backend.
+  EXPECT_FALSE(register_called);
+  EXPECT_FALSE(unregister_called);
+  EXPECT_FALSE(get_registration_called);
+
+  manager->Continue();
+  base::RunLoop().RunUntilIdle();
+  // Register should be blocked while storing to the backend.
+  EXPECT_FALSE(register_called);
+  EXPECT_FALSE(unregister_called);
+  EXPECT_FALSE(get_registration_called);
+
+  manager->Continue();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(register_called);
+  EXPECT_EQ(kExpectedInitialId, callback_registration_.id);
+  EXPECT_EQ(BackgroundSyncManager::ERROR_TYPE_OK, callback_error_);
+  // Unregister should be blocked while storing to the backend.
+  EXPECT_FALSE(unregister_called);
+  EXPECT_FALSE(get_registration_called);
+
+  manager->Continue();
+  base::RunLoop().RunUntilIdle();
+  // Unregister should be done and since GetRegistration doesn't require the
+  // backend it should be done too.
+  EXPECT_EQ(BackgroundSyncManager::ERROR_TYPE_NOT_FOUND, callback_error_);
+  EXPECT_TRUE(unregister_called);
+  EXPECT_TRUE(get_registration_called);
+}
+
+}  // namespace content
diff --git a/content/browser/background_sync/background_sync_proto.gyp b/content/browser/background_sync/background_sync_proto.gyp
new file mode 100644
index 0000000..2d5c1e7
--- /dev/null
+++ b/content/browser/background_sync/background_sync_proto.gyp
@@ -0,0 +1,17 @@
+{
+  'targets': [
+    {
+      # GN version: //content/browser/background_sync:background_sync_proto
+      'target_name': 'background_sync_proto',
+      'type': 'static_library',
+      'sources': [
+        'background_sync.proto',
+      ],
+      'variables': {
+        'proto_in_dir': '.',
+        'proto_out_dir': 'content/browser/background_sync',
+      },
+      'includes': [ '../../../build/protoc.gypi' ]
+    },
+  ],
+}
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 86cc1a6..440925423 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -516,15 +516,6 @@
     media::InitializeCPUSpecificMediaFeatures();
   }
   {
-    TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:AudioMan");
-    audio_manager_.reset(media::AudioManager::Create(
-        MediaInternals::GetInstance()));
-  }
-  {
-    TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MidiManager");
-    midi_manager_.reset(media::MidiManager::Create());
-  }
-  {
     TRACE_EVENT0("startup",
                  "BrowserMainLoop::Subsystem:ContentWebUIController");
     WebUIControllerFactory::RegisterFactory(
@@ -566,7 +557,8 @@
     SurfaceTextureManager::InitInstance(new BrowserSurfaceTextureManager);
   }
 
-  {
+  if (!parsed_command_line_.HasSwitch(
+      switches::kDisableScreenOrientationLock)) {
     TRACE_EVENT0("startup",
                  "BrowserMainLoop::Subsystem:ScreenOrientationProvider");
     screen_orientation_delegate_.reset(
@@ -1085,6 +1077,17 @@
   BrowserGpuChannelHostFactory::Initialize(established_gpu_channel);
 #endif
 
+  {
+    TRACE_EVENT0("startup", "BrowserThreadsStarted::Subsystem:AudioMan");
+    audio_manager_.reset(media::AudioManager::CreateWithHangTimer(
+        MediaInternals::GetInstance(), io_thread_->task_runner()));
+  }
+
+  {
+    TRACE_EVENT0("startup", "BrowserThreadsStarted::Subsystem:MidiManager");
+    midi_manager_.reset(media::MidiManager::Create());
+  }
+
 #if defined(OS_LINUX) && defined(USE_UDEV)
   device_monitor_linux_.reset(new DeviceMonitorLinux());
 #elif defined(OS_MACOSX)
diff --git a/content/browser/compositor/buffer_queue_unittest.cc b/content/browser/compositor/buffer_queue_unittest.cc
index 019474f..61e4dab 100644
--- a/content/browser/compositor/buffer_queue_unittest.cc
+++ b/content/browser/compositor/buffer_queue_unittest.cc
@@ -27,11 +27,11 @@
   StubGpuMemoryBufferImpl() {}
 
   // Overridden from gfx::GpuMemoryBuffer:
-  void* Map() override { return nullptr; }
+  bool Map(void** data) override { return false; }
   void Unmap() override {}
   bool IsMapped() const override { return false; }
   Format GetFormat() const override { return gfx::GpuMemoryBuffer::RGBX_8888; }
-  uint32 GetStride() const override { return 0; }
+  void GetStride(uint32* stride) const override {}
   gfx::GpuMemoryBufferHandle GetHandle() const override {
     return gfx::GpuMemoryBufferHandle();
   }
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index f0cc4f7..4fc6a67 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -72,7 +72,7 @@
 class RasterThread : public base::SimpleThread {
  public:
   RasterThread(cc::TaskGraphRunner* task_graph_runner)
-      : base::SimpleThread("UICompositorWorker"),
+      : base::SimpleThread("CompositorTileWorker1"),
         task_graph_runner_(task_graph_runner) {}
 
   // Overridden from base::SimpleThread:
diff --git a/content/browser/devtools/protocol/service_worker_handler.cc b/content/browser/devtools/protocol/service_worker_handler.cc
index c273601..460c1bc9 100644
--- a/content/browser/devtools/protocol/service_worker_handler.cc
+++ b/content/browser/devtools/protocol/service_worker_handler.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/containers/scoped_ptr_hash_map.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "content/browser/devtools/service_worker_devtools_agent_host.h"
 #include "content/browser/devtools/service_worker_devtools_manager.h"
 #include "content/browser/frame_host/frame_tree.h"
@@ -249,6 +250,8 @@
       context_, base::Bind(&ServiceWorkerHandler::OnWorkerRegistrationUpdated,
                            weak_factory_.GetWeakPtr()),
       base::Bind(&ServiceWorkerHandler::OnWorkerVersionUpdated,
+                 weak_factory_.GetWeakPtr()),
+      base::Bind(&ServiceWorkerHandler::OnErrorReported,
                  weak_factory_.GetWeakPtr()));
   context_watcher_->Start();
 
@@ -370,6 +373,21 @@
       WorkerVersionUpdatedParams::Create()->set_versions(version_values));
 }
 
+void ServiceWorkerHandler::OnErrorReported(
+    int64 registration_id,
+    int64 version_id,
+    const ServiceWorkerContextObserver::ErrorInfo& info) {
+  client_->WorkerErrorReported(
+      WorkerErrorReportedParams::Create()->set_error_message(
+          ServiceWorkerErrorMessage::Create()
+              ->set_error_message(base::UTF16ToUTF8(info.error_message))
+              ->set_registration_id(base::Int64ToString(registration_id))
+              ->set_version_id(base::Int64ToString(version_id))
+              ->set_source_url(info.source_url.spec())
+              ->set_line_number(info.line_number)
+              ->set_column_number(info.column_number)));
+}
+
 void ServiceWorkerHandler::DispatchProtocolMessage(
     DevToolsAgentHost* host,
     const std::string& message) {
diff --git a/content/browser/devtools/protocol/service_worker_handler.h b/content/browser/devtools/protocol/service_worker_handler.h
index c23d002..af31e4a4 100644
--- a/content/browser/devtools/protocol/service_worker_handler.h
+++ b/content/browser/devtools/protocol/service_worker_handler.h
@@ -11,6 +11,7 @@
 #include "content/browser/devtools/protocol/devtools_protocol_handler.h"
 #include "content/browser/devtools/service_worker_devtools_agent_host.h"
 #include "content/browser/devtools/service_worker_devtools_manager.h"
+#include "content/browser/service_worker/service_worker_context_observer.h"
 #include "content/browser/service_worker/service_worker_info.h"
 #include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/devtools_agent_host_client.h"
@@ -72,6 +73,9 @@
       const std::vector<ServiceWorkerRegistrationInfo>& registrations);
   void OnWorkerVersionUpdated(
       const std::vector<ServiceWorkerVersionInfo>& registrations);
+  void OnErrorReported(int64 registration_id,
+                       int64 version_id,
+                       const ServiceWorkerContextObserver::ErrorInfo& info);
 
   void OpenNewDevToolsWindow(int process_id, int devtools_agent_route_id);
 
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 29d53670..2c8637f 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -69,6 +69,10 @@
   return NULL;
 }
 
+bool ShouldCreateDevToolsFor(RenderFrameHost* rfh) {
+  return rfh->IsCrossProcessSubframe() || !rfh->GetParent();
+}
+
 }  // namespace
 
 scoped_refptr<DevToolsAgentHost>
@@ -95,7 +99,7 @@
   RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(host);
   if (!rfh->IsRenderFrameLive())
     return;
-  if (rfh->IsCrossProcessSubframe() || !rfh->GetParent())
+  if (ShouldCreateDevToolsFor(rfh))
     result->push_back(RenderFrameDevToolsAgentHost::GetOrCreateFor(rfh));
 }
 
@@ -291,6 +295,12 @@
 
 void
 RenderFrameDevToolsAgentHost::ReattachToRenderFrameHost(RenderFrameHost* rfh) {
+  if (!ShouldCreateDevToolsFor(rfh)) {
+    DestroyOnRenderFrameGone();
+    // |this| may be deleted at this point.
+    return;
+  }
+
   DCHECK(!reattaching_);
   reattaching_ = true;
   DisconnectRenderFrameHost();
@@ -301,7 +311,11 @@
 void RenderFrameDevToolsAgentHost::FrameDeleted(RenderFrameHost* rfh) {
   if (rfh != render_frame_host_)
     return;
+  DestroyOnRenderFrameGone();
+  // |this| may be deleted at this point.
+}
 
+void RenderFrameDevToolsAgentHost::DestroyOnRenderFrameGone() {
   DCHECK(render_frame_host_);
   scoped_refptr<RenderFrameDevToolsAgentHost> protect(this);
   HostClosed();
@@ -396,11 +410,9 @@
 }
 
 void RenderFrameDevToolsAgentHost::SetRenderFrameHost(RenderFrameHost* rfh) {
+  DCHECK(ShouldCreateDevToolsFor(rfh));
   DCHECK(!render_frame_host_);
   render_frame_host_ = static_cast<RenderFrameHostImpl*>(rfh);
-  // TODO(dgozman): here we should DCHECK that frame host is either root or
-  // cross process subframe, but this requires handling cross-process
-  // navigation. See http://crbug.com/464993.
 
   WebContentsObserver::Observe(WebContents::FromRenderFrameHost(rfh));
   RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index ff7fad8..599962d 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -128,6 +128,7 @@
   void InnerClientDetachedFromRenderer();
 
   bool IsChildFrame();
+  void DestroyOnRenderFrameGone();
 
   RenderFrameHostImpl* render_frame_host_;
   scoped_ptr<devtools::dom::DOMHandler> dom_handler_;
diff --git a/content/browser/devtools/site_per_process_devtools_browsertest.cc b/content/browser/devtools/site_per_process_devtools_browsertest.cc
index aaa935f7..1ec78c6 100644
--- a/content/browser/devtools/site_per_process_devtools_browsertest.cc
+++ b/content/browser/devtools/site_per_process_devtools_browsertest.cc
@@ -103,9 +103,7 @@
   EXPECT_EQ(1U, list.size());
   EXPECT_EQ(DevToolsAgentHost::TYPE_WEB_CONTENTS, list[0]->GetType());
   EXPECT_EQ(main_url.spec(), list[0]->GetURL().spec());
-  // TODO(dgozman): we should get closed notification here.
-  // See http://crbug.com/464993.
-  EXPECT_FALSE(client.closed());
+  EXPECT_TRUE(client.closed());
   child_host->DetachClient();
   child_host = nullptr;
 }
diff --git a/content/browser/dom_storage/session_storage_database.cc b/content/browser/dom_storage/session_storage_database.cc
index 3afc669..62d6430 100644
--- a/content/browser/dom_storage/session_storage_database.cc
+++ b/content/browser/dom_storage/session_storage_database.cc
@@ -10,6 +10,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
 #include "third_party/leveldatabase/src/include/leveldb/options.h"
@@ -381,6 +382,7 @@
   // situation gracefully by creating the database now.
   options.max_open_files = 0;  // Use minimum.
   options.create_if_missing = true;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
 #if defined(OS_WIN)
   return leveldb::DB::Open(options, base::WideToUTF8(file_path_.value()), db);
 #elif defined(OS_POSIX)
diff --git a/content/browser/frame_host/frame_tree.cc b/content/browser/frame_host/frame_tree.cc
index 39c0563..f3a0a09 100644
--- a/content/browser/frame_host/frame_tree.cc
+++ b/content/browser/frame_host/frame_tree.cc
@@ -46,6 +46,39 @@
   return true;
 }
 
+// Helper function used with FrameTree::ForEach() for retrieving the total
+// loading progress and number of frames in a frame tree.
+bool CollectLoadProgress(double* progress,
+                         int* frame_count,
+                         FrameTreeNode* node) {
+  // Ignore the current frame if it has not started loading.
+  double frame_progress = node->loading_progress();
+  if (frame_progress == FrameTreeNode::kLoadingProgressNotStarted)
+    return true;
+
+  // Collect progress.
+  *progress += frame_progress;
+  (*frame_count)++;
+  return true;
+}
+
+// Helper function used with FrameTree::ForEach() to reset the load progress.
+bool ResetNodeLoadProgress(FrameTreeNode* node) {
+  node->set_loading_progress(FrameTreeNode::kLoadingProgressNotStarted);
+  return true;
+}
+
+// Helper function used with FrameTree::ForEach() to check if at least one of
+// the nodes is loading.
+bool IsNodeLoading(bool* is_loading, FrameTreeNode* node) {
+  if (node->IsLoading()) {
+    // There is at least one node loading, so abort traversal.
+    *is_loading = true;
+    return false;
+  }
+  return true;
+}
+
 }  // namespace
 
 FrameTree::FrameTree(Navigator* navigator,
@@ -312,4 +345,24 @@
     on_frame_removed_.Run(frame->current_frame_host());
 }
 
+double FrameTree::GetLoadProgress() {
+  double progress = 0.0;
+  int frame_count = 0;
+
+  ForEach(base::Bind(&CollectLoadProgress, &progress, &frame_count));
+  if (frame_count != 0)
+    progress /= frame_count;
+  return progress;
+}
+
+void FrameTree::ResetLoadProgress() {
+  ForEach(base::Bind(&ResetNodeLoadProgress));
+}
+
+bool FrameTree::IsLoading() {
+  bool is_loading = false;
+  ForEach(base::Bind(&IsNodeLoading, &is_loading));
+  return is_loading;
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/frame_tree.h b/content/browser/frame_host/frame_tree.h
index 21eaac9b..164c3c1 100644
--- a/content/browser/frame_host/frame_tree.h
+++ b/content/browser/frame_host/frame_tree.h
@@ -130,6 +130,15 @@
   // the listener installed by SetFrameRemoveListener.
   void FrameRemoved(FrameTreeNode* frame);
 
+  // Returns this FrameTree's total load progress.
+  double GetLoadProgress();
+
+  // Resets the load progress on all nodes in this FrameTree.
+  void ResetLoadProgress();
+
+  // Returns true if at least one of the nodes in this FrameTree is loading.
+  bool IsLoading();
+
  private:
   typedef base::hash_map<int, RenderViewHostImpl*> RenderViewHostMap;
   typedef std::multimap<int, RenderViewHostImpl*> RenderViewHostMultiMap;
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc
index 7df1291a..74a9fac3 100644
--- a/content/browser/frame_host/frame_tree_node.cc
+++ b/content/browser/frame_host/frame_tree_node.cc
@@ -28,11 +28,15 @@
 
 }  // namespace
 
+const double FrameTreeNode::kLoadingProgressNotStarted = 0.0;
+const double FrameTreeNode::kLoadingProgressMinimum = 0.1;
+const double FrameTreeNode::kLoadingProgressDone = 1.0;
+
 int64 FrameTreeNode::next_frame_tree_node_id_ = 1;
 
 // static
 FrameTreeNode* FrameTreeNode::GloballyFindByID(int64 frame_tree_node_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   FrameTreeNodeIDMap* nodes = g_frame_tree_node_id_map.Pointer();
   FrameTreeNodeIDMap::iterator it = nodes->find(frame_tree_node_id);
   return it == nodes->end() ? nullptr : it->second;
@@ -55,7 +59,8 @@
       frame_tree_node_id_(next_frame_tree_node_id_++),
       parent_(NULL),
       replication_state_(name),
-      effective_sandbox_flags_(SandboxFlags::NONE) {
+      effective_sandbox_flags_(SandboxFlags::NONE),
+      loading_progress_(kLoadingProgressNotStarted) {
   std::pair<FrameTreeNodeIDMap::iterator, bool> result =
       g_frame_tree_node_id_map.Get().insert(
           std::make_pair(frame_tree_node_id_, this));
@@ -154,14 +159,6 @@
   return current_frame_host->is_loading();
 }
 
-double FrameTreeNode::GetLoadingProgress() const {
-  RenderFrameHostImpl* current_frame_host =
-      render_manager_.current_frame_host();
-
-  DCHECK(current_frame_host);
-  return current_frame_host->loading_progress();
-}
-
 bool FrameTreeNode::CommitPendingSandboxFlags() {
   bool did_change_flags =
       effective_sandbox_flags_ != replication_state_.sandbox_flags;
diff --git a/content/browser/frame_host/frame_tree_node.h b/content/browser/frame_host/frame_tree_node.h
index 458887b..30843d8 100644
--- a/content/browser/frame_host/frame_tree_node.h
+++ b/content/browser/frame_host/frame_tree_node.h
@@ -30,6 +30,15 @@
 // are frame-specific (as opposed to page-specific).
 class CONTENT_EXPORT FrameTreeNode {
  public:
+  // These values indicate the loading progress status. The minimum progress
+  // value matches what Blink's ProgressTracker has traditionally used for a
+  // minimum progress value.
+  // TODO(fdegans): Move these values to the implementation when the relevant
+  // IPCs are moved from WebContentsImpl to RenderFrameHost.
+  static const double kLoadingProgressNotStarted;
+  static const double kLoadingProgressMinimum;
+  static const double kLoadingProgressDone;
+
   // Returns the FrameTreeNode with the given global |frame_tree_node_id|,
   // regardless of which FrameTree it is in.
   static FrameTreeNode* GloballyFindByID(int64 frame_tree_node_id);
@@ -122,11 +131,16 @@
 
   bool IsDescendantOf(FrameTreeNode* other) const;
 
-  // Returns true if this frame is in a loading state.
+  // Returns true if this node is in a loading state.
   bool IsLoading() const;
 
-  // Returns the loading progress of this frame.
-  double GetLoadingProgress() const;
+  // Sets this node's loading progress (from 0 to 1).
+  void set_loading_progress(double loading_progress) {
+    loading_progress_ = loading_progress;
+  }
+
+  // Returns this node's loading progress.
+  double loading_progress() const { return loading_progress_; }
 
  private:
   void set_parent(FrameTreeNode* parent) { parent_ = parent; }
@@ -178,6 +192,9 @@
   // flags when a navigation for this frame commits.
   SandboxFlags effective_sandbox_flags_;
 
+  // Used to track this node's loading progress (from 0 to 1).
+  double loading_progress_;
+
   DISALLOW_COPY_AND_ASSIGN(FrameTreeNode);
 };
 
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 8473c8f..8ec8dc9 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -1297,6 +1297,18 @@
     return true;
   }
 
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSitePerProcess)) {
+    // This may be a "new auto" case where we add a new FrameNavigationEntry, or
+    // it may be a "history auto" case where we update an existing one.
+    int64 frame_tree_node_id = rfh->frame_tree_node()->frame_tree_node_id();
+    NavigationEntryImpl* last_committed = GetLastCommittedEntry();
+    last_committed->AddOrUpdateFrameEntry(frame_tree_node_id,
+                                          rfh->GetSiteInstance(),
+                                          params.url,
+                                          params.referrer);
+  }
+
   // We do not need to discard the pending entry in this case, since we will
   // not generate commit notifications for this auto-subframe navigation.
   return false;
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index 6f54e00..997a6fdb 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/strings/stringprintf.h"
+#include "content/browser/frame_host/frame_navigation_entry.h"
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/navigation_controller_impl.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
@@ -12,6 +14,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/bindings_policy.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -853,4 +856,55 @@
   }
 }
 
+
+// Verify the tree of FrameNavigationEntries after NAVIGATION_TYPE_AUTO_SUBFRAME
+// commits.
+// TODO(creis): Test cross-site and nested iframes.
+// TODO(creis): Test updating entries for history auto subframe navigations.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
+                       FrameNavigationEntry_AutoSubframe) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "/navigation_controller/simple_page_1.html"));
+  NavigateToURL(shell(), main_url);
+  const NavigationControllerImpl& controller =
+      static_cast<const NavigationControllerImpl&>(
+          shell()->web_contents()->GetController());
+  FrameTreeNode* root =
+      static_cast<WebContentsImpl*>(shell()->web_contents())->
+          GetFrameTree()->root();
+
+  // Create an iframe.
+  GURL frame_url(embedded_test_server()->GetURL(
+      "/navigation_controller/simple_page_2.html"));
+  {
+    LoadCommittedCapturer capturer(shell()->web_contents());
+    std::string script = "var iframe = document.createElement('iframe');"
+                         "iframe.src = '" + frame_url.spec() + "';"
+                         "document.body.appendChild(iframe);";
+    EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
+    capturer.Wait();
+    EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
+  }
+
+  // Check last committed NavigationEntry.
+  EXPECT_EQ(1, controller.GetEntryCount());
+  NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
+  EXPECT_EQ(main_url, entry->GetURL());
+  FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
+  EXPECT_EQ(main_url, root_entry->url());
+
+  // Verify subframe entries if we're in --site-per-process mode.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSitePerProcess)) {
+    // The entry should now have a subframe FrameNavigationEntry.
+    ASSERT_EQ(1U, entry->root_node()->children.size());
+    FrameNavigationEntry* frame_entry =
+        entry->root_node()->children[0]->frame_entry.get();
+    EXPECT_EQ(frame_url, frame_entry->url());
+  } else {
+    // There are no subframe FrameNavigationEntries by default.
+    EXPECT_EQ(0U, entry->root_node()->children.size());
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 60dadb28..c1fce3c 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "content/browser/frame_host/cross_site_transferring_request.h"
+#include "content/browser/frame_host/frame_navigation_entry.h"
 #include "content/browser/frame_host/navigation_controller_impl.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
@@ -1966,34 +1967,103 @@
 
 // Auto subframes are ones the page loads automatically like ads. They should
 // not create new navigation entries.
+// TODO(creis): Test cross-site and nested iframes.
+// TODO(creis): Test updating entries for history auto subframe navigations.
 TEST_F(NavigationControllerTest, AutoSubframe) {
   NavigationControllerImpl& controller = controller_impl();
   TestNotificationTracker notifications;
   RegisterForAllNavNotifications(&notifications, &controller);
 
-  const GURL url1("http://foo1");
-  main_test_rfh()->SendNavigate(0, url1);
+  const GURL url1("http://foo/1");
+  main_test_rfh()->SendNavigate(1, url1);
   EXPECT_EQ(1U, navigation_entry_committed_counter_);
   navigation_entry_committed_counter_ = 0;
 
-  const GURL url2("http://foo2");
-  FrameHostMsg_DidCommitProvisionalLoad_Params params;
-  params.page_id = 0;
-  params.url = url2;
-  params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
-  params.should_update_history = false;
-  params.gesture = NavigationGestureUser;
-  params.is_post = false;
-  params.page_state = PageState::CreateFromURL(url2);
+  // Add a subframe and navigate it.
+  main_test_rfh()->OnCreateChildFrame(MSG_ROUTING_NONE, std::string(),
+                                      SandboxFlags::NONE);
+  RenderFrameHostImpl* subframe =
+      contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
+  const GURL url2("http://foo/2");
+  {
+    FrameHostMsg_DidCommitProvisionalLoad_Params params;
+    params.page_id = 1;
+    params.url = url2;
+    params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
+    params.should_update_history = false;
+    params.gesture = NavigationGestureUser;
+    params.is_post = false;
+    params.page_state = PageState::CreateFromURL(url2);
 
-  // Navigating should do nothing.
-  LoadCommittedDetails details;
-  EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
-                                              &details));
-  EXPECT_EQ(0U, notifications.size());
+    // Navigating should do nothing.
+    LoadCommittedDetails details;
+    EXPECT_FALSE(controller.RendererDidNavigate(subframe, params, &details));
+    EXPECT_EQ(0U, notifications.size());
+  }
 
   // There should still be only one entry.
   EXPECT_EQ(1, controller.GetEntryCount());
+  NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
+  EXPECT_EQ(url1, entry->GetURL());
+  EXPECT_EQ(1, entry->GetPageID());
+  FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
+  EXPECT_EQ(url1, root_entry->url());
+
+  // Verify subframe entries if we're in --site-per-process mode.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSitePerProcess)) {
+    // The entry should now have a subframe FrameNavigationEntry.
+    ASSERT_EQ(1U, entry->root_node()->children.size());
+    FrameNavigationEntry* frame_entry =
+        entry->root_node()->children[0]->frame_entry.get();
+    EXPECT_EQ(url2, frame_entry->url());
+  } else {
+    // There are no subframe FrameNavigationEntries by default.
+    EXPECT_EQ(0U, entry->root_node()->children.size());
+  }
+
+  // Add a second subframe and navigate.
+  main_test_rfh()->OnCreateChildFrame(MSG_ROUTING_NONE, std::string(),
+                                      SandboxFlags::NONE);
+  RenderFrameHostImpl* subframe2 =
+      contents()->GetFrameTree()->root()->child_at(1)->current_frame_host();
+  const GURL url3("http://foo/3");
+  {
+    FrameHostMsg_DidCommitProvisionalLoad_Params params;
+    params.page_id = 1;
+    params.url = url3;
+    params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
+    params.should_update_history = false;
+    params.gesture = NavigationGestureUser;
+    params.is_post = false;
+    params.page_state = PageState::CreateFromURL(url3);
+
+    // Navigating should do nothing.
+    LoadCommittedDetails details;
+    EXPECT_FALSE(controller.RendererDidNavigate(subframe2, params, &details));
+    EXPECT_EQ(0U, notifications.size());
+  }
+
+  // There should still be only one entry, mostly unchanged.
+  EXPECT_EQ(1, controller.GetEntryCount());
+  EXPECT_EQ(entry, controller.GetLastCommittedEntry());
+  EXPECT_EQ(url1, entry->GetURL());
+  EXPECT_EQ(1, entry->GetPageID());
+  EXPECT_EQ(root_entry, entry->root_node()->frame_entry.get());
+  EXPECT_EQ(url1, root_entry->url());
+
+  // Verify subframe entries if we're in --site-per-process mode.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSitePerProcess)) {
+    // The entry should now have 2 subframe FrameNavigationEntries.
+    ASSERT_EQ(2U, entry->root_node()->children.size());
+    FrameNavigationEntry* new_frame_entry =
+        entry->root_node()->children[1]->frame_entry.get();
+    EXPECT_EQ(url3, new_frame_entry->url());
+  } else {
+    // There are no subframe FrameNavigationEntries by default.
+    EXPECT_EQ(0U, entry->root_node()->children.size());
+  }
 }
 
 // Tests navigation and then going back to a subframe navigation.
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index 82b97f9e..12cafa65 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -471,6 +471,18 @@
 #endif
 }
 
+void NavigationEntryImpl::AddOrUpdateFrameEntry(int64 frame_tree_node_id,
+                                                SiteInstanceImpl* site_instance,
+                                                const GURL& url,
+                                                const Referrer& referrer) {
+  // TODO(creis): Walk tree to find the node to update.
+  // TODO(creis): Only create a new entry if one doesn't exist yet.
+  FrameNavigationEntry* frame_entry =
+      new FrameNavigationEntry(site_instance, url, referrer);
+  root_node()->children.push_back(
+      new NavigationEntryImpl::TreeNode(frame_entry));
+}
+
 void NavigationEntryImpl::SetScreenshotPNGData(
     scoped_refptr<base::RefCountedBytes> png_data) {
   screenshot_ = png_data;
diff --git a/content/browser/frame_host/navigation_entry_impl.h b/content/browser/frame_host/navigation_entry_impl.h
index 0d7b3eb..9bebe15 100644
--- a/content/browser/frame_host/navigation_entry_impl.h
+++ b/content/browser/frame_host/navigation_entry_impl.h
@@ -7,6 +7,7 @@
 
 #include "base/basictypes.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
 #include "base/time/time.h"
 #include "content/browser/frame_host/frame_navigation_entry.h"
 #include "content/browser/site_instance_impl.h"
@@ -42,6 +43,9 @@
     // Ref counted pointer that keeps the FrameNavigationEntry alive as long as
     // it is needed by this node's NavigationEntry.
     scoped_refptr<FrameNavigationEntry> frame_entry;
+
+    // List of child TreeNodes, which will be deleted when this node is.
+    ScopedVector<TreeNode> children;
   };
 
   static NavigationEntryImpl* FromNavigationEntry(NavigationEntry* entry);
@@ -137,6 +141,24 @@
   // pieces of non-persisted state, as documented on the members below.
   void ResetForCommit();
 
+  // Exposes the tree of FrameNavigationEntries that make up this joint session
+  // history item.
+  // In default Chrome, this tree only has a root node with an unshared
+  // FrameNavigationEntry.  Subframes are only added to the tree if the
+  // --site-per-process flag is passed.
+  TreeNode* root_node() const {
+    return frame_tree_.get();
+  }
+
+  // Finds the TreeNode associated with |frame_tree_node_id| to add or update
+  // its FrameNavigationEntry.  A new FrameNavigationEntry is added if none
+  // exists, or else the existing one (which might be shared with other
+  // NavigationEntries) is updated with the given parameters.
+  void AddOrUpdateFrameEntry(int64 frame_tree_node_id,
+                             SiteInstanceImpl* site_instance,
+                             const GURL& url,
+                             const Referrer& referrer);
+
   void set_unique_id(int unique_id) {
     unique_id_ = unique_id;
   }
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 88bfbe0..0a6c18a 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -53,6 +53,7 @@
 #include "content/public/browser/browser_plugin_guest_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/permission_type.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/stream_handle.h"
@@ -105,10 +106,6 @@
 
 }  // namespace
 
-const double RenderFrameHostImpl::kLoadingProgressNotStarted = 0.0;
-const double RenderFrameHostImpl::kLoadingProgressMinimum = 0.1;
-const double RenderFrameHostImpl::kLoadingProgressDone = 1.0;
-
 // static
 bool RenderFrameHostImpl::IsRFHStateActive(RenderFrameHostImplState rfh_state) {
   return rfh_state == STATE_DEFAULT;
@@ -149,13 +146,9 @@
       routing_id_(routing_id),
       render_frame_created_(false),
       navigations_suspended_(false),
-      has_beforeunload_handlers_(false),
-      has_unload_handlers_(false),
-      override_sudden_termination_status_(false),
       is_waiting_for_beforeunload_ack_(false),
       unload_ack_is_for_navigation_(false),
       is_loading_(false),
-      loading_progress_(kLoadingProgressNotStarted),
       accessibility_reset_token_(0),
       accessibility_reset_count_(0),
       no_create_browser_accessibility_manager_for_testing_(false),
@@ -358,10 +351,6 @@
     IPC_MESSAGE_HANDLER(FrameHostMsg_DocumentOnLoadCompleted,
                         OnDocumentOnLoadCompleted)
     IPC_MESSAGE_HANDLER(FrameHostMsg_BeforeUnload_ACK, OnBeforeUnloadACK)
-    IPC_MESSAGE_HANDLER(FrameHostMsg_BeforeUnloadHandlersPresent,
-                        OnBeforeUnloadHandlersPresent)
-    IPC_MESSAGE_HANDLER(FrameHostMsg_UnloadHandlersPresent,
-                        OnUnloadHandlersPresent)
     IPC_MESSAGE_HANDLER(FrameHostMsg_SwapOut_ACK, OnSwapOutACK)
     IPC_MESSAGE_HANDLER(FrameHostMsg_ContextMenu, OnContextMenu)
     IPC_MESSAGE_HANDLER(FrameHostMsg_JavaScriptExecuteResponse,
@@ -1053,11 +1042,6 @@
       rfh_state_ == STATE_PENDING_SWAP_OUT;
 }
 
-bool RenderFrameHostImpl::SuddenTerminationAllowed() const {
-  return override_sudden_termination_status_ ||
-      (!has_beforeunload_handlers_ && !has_unload_handlers_);
-}
-
 void RenderFrameHostImpl::OnSwapOutACK() {
   OnSwappedOut();
 }
@@ -1418,14 +1402,6 @@
   render_view_host_->WasResized();
 }
 
-void RenderFrameHostImpl::OnBeforeUnloadHandlersPresent(bool present) {
-  has_beforeunload_handlers_ = present;
-}
-
-void RenderFrameHostImpl::OnUnloadHandlersPresent(bool present) {
-  has_unload_handlers_ = present;
-}
-
 #if defined(OS_MACOSX) || defined(OS_ANDROID)
 void RenderFrameHostImpl::OnShowPopup(
     const FrameHostMsg_ShowPopup_Params& params) {
@@ -1968,7 +1944,7 @@
 void RenderFrameHostImpl::DidUseGeolocationPermission() {
   RenderFrameHost* top_frame = frame_tree_node()->frame_tree()->GetMainFrame();
   GetContentClient()->browser()->RegisterPermissionUsage(
-      PERMISSION_GEOLOCATION,
+      PermissionType::GEOLOCATION,
       delegate_->GetAsWebContents(),
       GetLastCommittedURL().GetOrigin(),
       top_frame->GetLastCommittedURL().GetOrigin());
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index aeefa13..67ee5d5 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -90,15 +90,6 @@
     : public RenderFrameHost,
       public BrowserAccessibilityDelegate {
  public:
-  // These values indicate the loading progress status. The minimum progress
-  // value matches what Blink's ProgressTracker has traditionally used for a
-  // minimum progress value.
-  // TODO(fdegans): Move these values to the implementation when the relevant
-  // IPCs are moved from WebContentsImpl to RenderFrameHost.
-  static const double kLoadingProgressNotStarted;
-  static const double kLoadingProgressMinimum;
-  static const double kLoadingProgressDone;
-
   // Keeps track of the state of the RenderFrameHostImpl, particularly with
   // respect to swap out.
   enum RenderFrameHostImplState {
@@ -218,16 +209,6 @@
   // call FrameTreeNode::IsLoading.
   bool is_loading() const { return is_loading_; }
 
-  // Sets this RenderFrameHost's loading progress (from 0 to 1).
-  void set_loading_progress(double loading_progress) {
-    loading_progress_ = loading_progress;
-  }
-
-  // Returns this RenderFrameHost's loading progress. This is only used by
-  // FrameTreeNode. The proper way to check a frame loading progress is to call
-  // FrameTreeNode::GetLoadingProgress.
-  double loading_progress() const { return loading_progress_; }
-
   // This returns the RenderFrameHost's owned RenderWidgetHost if it has one,
   // or else it returns nullptr.
   // If the RenderFrameHost is the page's main frame, this returns instead a
@@ -295,18 +276,6 @@
   // Whether the RFH is waiting for an unload ACK from the renderer.
   bool IsWaitingForUnloadACK() const;
 
-  // Whether sudden termination is allowed for this frame. This is true if there
-  // are no BeforeUnload handlers or no Unload handlers registered for the
-  // frame, or it was overriden by the browser to be always true.
-  bool SuddenTerminationAllowed() const;
-
-  // Called by the browser to override (or not) the sudden termination status of
-  // the frame. When overriden, sudden termination is always allowed, even if
-  // it would otherwise be prevented.
-  void set_override_sudden_termination_status(bool enabled) {
-    override_sudden_termination_status_ = enabled;
-  }
-
   // Called when either the SwapOut request has been acknowledged or has timed
   // out.
   void OnSwappedOut();
@@ -539,8 +508,6 @@
   void OnAccessibilityFindInPageResult(
       const AccessibilityHostMsg_FindInPageResultParams& params);
   void OnToggleFullscreen(bool enter_fullscreen);
-  void OnBeforeUnloadHandlersPresent(bool present);
-  void OnUnloadHandlersPresent(bool present);
 
 #if defined(OS_MACOSX) || defined(OS_ANDROID)
   void OnShowPopup(const FrameHostMsg_ShowPopup_Params& params);
@@ -666,17 +633,6 @@
   // When the last BeforeUnload message was sent.
   base::TimeTicks send_before_unload_start_time_;
 
-  // Used to track whether sudden termination is allowed for this frame.
-  // has_beforeunload_handlers_ and has_unload_handlers_ are also used to avoid
-  // asking the renderer process to run BeforeUnload or Unload during
-  // navigation. The browser can set override_sudden_termination_status_ to
-  // true, in which case sudden termination will be allowed. This is used when a
-  // renderer executing BeforeUnload or Unload is unresponsive. All other values
-  // are modified based on IPCs received from the renderer.
-  bool has_beforeunload_handlers_;
-  bool has_unload_handlers_;
-  bool override_sudden_termination_status_;
-
   // Set to true when there is a pending FrameMsg_ShouldClose message.  This
   // ensures we don't spam the renderer with multiple beforeunload requests.
   // When either this value or IsWaitingForUnloadACK is true, the value of
@@ -698,9 +654,6 @@
   // document or not.
   bool is_loading_;
 
-  // Used to track this RenderFrameHost's loading progress (from 0 to 1).
-  double loading_progress_;
-
   // Used to swap out or shut down this RFH when the unload event is taking too
   // long to execute, depending on the number of active frames in the
   // SiteInstance.
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 3344f3d..29771b2 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -773,8 +773,8 @@
     // As SiteInstances are the same, check if the WebUI should be reused.
     const NavigationEntry* current_navigation_entry =
         delegate_->GetLastCommittedNavigationEntryForRenderManager();
-    bool should_reuse_web_ui_ = ShouldReuseWebUI(current_navigation_entry,
-                                                 request.common_params().url);
+    should_reuse_web_ui_ = ShouldReuseWebUI(current_navigation_entry,
+                                            request.common_params().url);
     if (!should_reuse_web_ui_) {
       speculative_web_ui_ = CreateWebUI(request.common_params().url,
                                         request.bindings());
diff --git a/content/browser/geofencing/geofencing_manager_unittest.cc b/content/browser/geofencing/geofencing_manager_unittest.cc
index 05e3019..16cf1bbe 100644
--- a/content/browser/geofencing/geofencing_manager_unittest.cc
+++ b/content/browser/geofencing/geofencing_manager_unittest.cc
@@ -123,7 +123,8 @@
   }
 
   void SetUp() override {
-    helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId));
+    helper_.reset(
+        new EmbeddedWorkerTestHelper(base::FilePath(), kRenderProcessId));
     service_ = new TestGeofencingService();
     manager_ = new GeofencingManager(helper_->context_wrapper());
     manager_->SetServiceForTesting(service_);
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 98a6f2a7..18b51fe 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -504,7 +504,7 @@
     return false;
 
   if (in_process_) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
     DCHECK(g_gpu_main_thread_factory);
     in_process_gpu_thread_.reset(
         g_gpu_main_thread_factory(InProcessChildThreadParams(
diff --git a/content/browser/indexed_db/leveldb/leveldb_database.cc b/content/browser/indexed_db/leveldb/leveldb_database.cc
index aab1255..1d816a849 100644
--- a/content/browser/indexed_db/leveldb/leveldb_database.cc
+++ b/content/browser/indexed_db/leveldb/leveldb_database.cc
@@ -103,7 +103,7 @@
   options.create_if_missing = true;
   options.paranoid_checks = true;
   options.filter_policy = filter_policy->get();
-  options.reuse_logs = true;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   options.compression = leveldb::kSnappyCompression;
 
   // For info about the troubles we've run into with this parameter, see:
@@ -199,9 +199,9 @@
 
   std::string error_histogram_name(histogram_name);
 
-  if (result == leveldb_env::METHOD_AND_PFE) {
+  if (result == leveldb_env::METHOD_AND_BFE) {
     DCHECK_LT(error, 0);
-    error_histogram_name.append(std::string(".PFE.") +
+    error_histogram_name.append(std::string(".BFE.") +
                                 leveldb_env::MethodIDToString(method));
     base::LinearHistogram::FactoryGet(
         error_histogram_name,
diff --git a/content/browser/media/capture/aura_window_capture_machine.cc b/content/browser/media/capture/aura_window_capture_machine.cc
index f3a1ec8..0ef3d18 100644
--- a/content/browser/media/capture/aura_window_capture_machine.cc
+++ b/content/browser/media/capture/aura_window_capture_machine.cc
@@ -120,7 +120,7 @@
 bool AuraWindowCaptureMachine::Start(
     const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
     const media::VideoCaptureParams& params) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // The window might be destroyed between SetWindow() and Start().
   if (!desktop_window_)
@@ -157,7 +157,7 @@
 }
 
 void AuraWindowCaptureMachine::Stop(const base::Closure& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   power_save_blocker_.reset();
 
   // Stop observing compositor and window events.
@@ -177,7 +177,7 @@
 }
 
 void AuraWindowCaptureMachine::SetWindow(aura::Window* window) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   DCHECK(!desktop_window_);
   desktop_window_ = window;
@@ -193,7 +193,7 @@
 }
 
 void AuraWindowCaptureMachine::UpdateCaptureSize() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (oracle_proxy_.get() && desktop_window_) {
      ui::Layer* layer = desktop_window_->layer();
      oracle_proxy_->UpdateCaptureSize(ui::ConvertSizeToPixel(
@@ -203,7 +203,7 @@
 }
 
 void AuraWindowCaptureMachine::Capture(bool dirty) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Do not capture if the desktop window is already destroyed.
   if (!desktop_window_)
@@ -267,7 +267,7 @@
     base::TimeTicks start_time,
     const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
     scoped_ptr<cc::CopyOutputResult> result) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (result->IsEmpty() || result->size().IsEmpty() || !desktop_window_)
     return false;
@@ -421,7 +421,7 @@
 }
 
 void AuraWindowCaptureMachine::OnWindowDestroyed(aura::Window* window) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   Stop(base::Bind(&base::DoNothing));
 
diff --git a/content/browser/media/cdm/browser_cdm_manager.cc b/content/browser/media/cdm/browser_cdm_manager.cc
index 1c1b157..6f3c28d 100644
--- a/content/browser/media/cdm/browser_cdm_manager.cc
+++ b/content/browser/media/cdm/browser_cdm_manager.cc
@@ -13,6 +13,7 @@
 #include "content/common/media/cdm_messages.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/permission_type.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
@@ -346,14 +347,14 @@
     return;
   }
 
-  media::EmeInitDataType eme_init_data_type = media::EME_INIT_DATA_TYPE_NONE;
+  media::EmeInitDataType eme_init_data_type;
   switch (init_data_type) {
     case INIT_DATA_TYPE_WEBM:
-      eme_init_data_type = media::EME_INIT_DATA_TYPE_WEBM;
+      eme_init_data_type = media::EmeInitDataType::WEBM;
       break;
 #if defined(USE_PROPRIETARY_CODECS)
     case INIT_DATA_TYPE_CENC:
-      eme_init_data_type = media::EME_INIT_DATA_TYPE_CENC;
+      eme_init_data_type = media::EmeInitDataType::CENC;
       break;
 #endif
     default:
@@ -513,7 +514,7 @@
     int render_frame_id,
     const GURL& security_origin,
     const base::Callback<void(bool)>& permission_status_cb) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   RenderFrameHost* rfh =
       RenderFrameHost::FromID(render_process_id_, render_frame_id);
@@ -522,7 +523,7 @@
 
   PermissionStatus permission_status =
       GetContentClient()->browser()->GetPermissionStatus(
-          content::PERMISSION_PROTECTED_MEDIA_IDENTIFIER,
+          content::PermissionType::PROTECTED_MEDIA_IDENTIFIER,
           web_contents->GetBrowserContext(), security_origin, embedding_origin);
 
   bool allowed = (permission_status == PERMISSION_STATUS_GRANTED);
@@ -559,11 +560,11 @@
   // "audio"/"video" does not matter, so use "video".
   std::string init_data_type_string;
   switch (init_data_type) {
-    case media::EME_INIT_DATA_TYPE_WEBM:
+    case media::EmeInitDataType::WEBM:
       init_data_type_string = "video/webm";
       break;
 #if defined(USE_PROPRIETARY_CODECS)
-    case media::EME_INIT_DATA_TYPE_CENC:
+    case media::EmeInitDataType::CENC:
       init_data_type_string = "video/mp4";
       break;
 #endif
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index dd3c3f0..e908788 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -487,10 +487,10 @@
     device_dict->SetString(
         "name", video_capture_device_info.name.GetNameAndModel());
     device_dict->Set("formats", format_list);
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
+    defined(OS_ANDROID)
     device_dict->SetString(
-        "captureApi",
-        video_capture_device_info.name.GetCaptureApiTypeString());
+        "captureApi", video_capture_device_info.name.GetCaptureApiTypeString());
 #endif
     video_capture_capabilities_cached_data_.Append(device_dict);
   }
diff --git a/content/browser/media/media_internals_unittest.cc b/content/browser/media/media_internals_unittest.cc
index 6a060176..2d43218 100644
--- a/content/browser/media/media_internals_unittest.cc
+++ b/content/browser/media/media_internals_unittest.cc
@@ -109,7 +109,8 @@
   MediaInternals::UpdateCallback update_cb_;
 };
 
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
+    defined(OS_ANDROID)
 TEST_F(MediaInternalsVideoCaptureDeviceTest,
        AllCaptureApiTypesHaveProperStringRepresentation) {
   typedef media::VideoCaptureDevice::Name VideoCaptureDeviceName;
@@ -128,6 +129,12 @@
   m[VideoCaptureDeviceName::AVFOUNDATION] = "AV Foundation";
   m[VideoCaptureDeviceName::QTKIT] = "QTKit";
   m[VideoCaptureDeviceName::DECKLINK] = "DeckLink";
+#elif defined(OS_ANDROID)
+  m[VideoCaptureDeviceName::API1] = "Camera API1";
+  m[VideoCaptureDeviceName::API2_LEGACY] = "Camera API2 Legacy";
+  m[VideoCaptureDeviceName::API2_FULL] = "Camera API2 Full";
+  m[VideoCaptureDeviceName::API2_LIMITED] = "Camera API2 Limited";
+  m[VideoCaptureDeviceName::TANGO] = "Tango API";
 #endif
   EXPECT_EQ(media::VideoCaptureDevice::Name::API_TYPE_UNKNOWN, m.size());
   for (CaptureApiTypeStringMap::iterator it = m.begin(); it != m.end(); ++it) {
@@ -179,6 +186,9 @@
       media::VideoCaptureDevice::Name(
           "dummy", "/dev/dummy",
           media::VideoCaptureDevice::Name::V4L2_SINGLE_PLANE),
+#elif defined(OS_ANDROID)
+      media::VideoCaptureDevice::Name("dummy", "dummy",
+          media::VideoCaptureDevice::Name::API2_LEGACY),
 #else
       media::VideoCaptureDevice::Name("dummy", "dummy"),
 #endif
@@ -207,6 +217,8 @@
   ExpectString("captureApi", "Direct Show");
 #elif defined(OS_MACOSX)
   ExpectString("captureApi", "QTKit");
+#elif defined(OS_ANDROID)
+  ExpectString("captureApi", "Camera API2 Legacy");
 #endif
 }
 
diff --git a/content/browser/media/webrtc_browsertest.cc b/content/browser/media/webrtc_browsertest.cc
index 8369129c..b30f49ab4 100644
--- a/content/browser/media/webrtc_browsertest.cc
+++ b/content/browser/media/webrtc_browsertest.cc
@@ -33,9 +33,6 @@
 #if defined(OS_ANDROID) && defined(ADDRESS_SANITIZER)
 // Renderer crashes under Android ASAN: https://crbug.com/408496.
 #define MAYBE_WebRtcBrowserTest DISABLED_WebRtcBrowserTest
-#elif defined(OS_WIN)
-// Test is flaky on Win. http://crbug.com/470013.
-#define MAYBE_WebRtcBrowserTest DISABLED_WebRtcBrowserTest
 #else
 #define MAYBE_WebRtcBrowserTest WebRtcBrowserTest
 #endif
@@ -48,6 +45,10 @@
   // Convenience function since most peerconnection-call.html tests just load
   // the page, kick off some javascript and wait for the title to change to OK.
   void MakeTypicalPeerConnectionCall(const std::string& javascript) {
+    if (OnWinXp()) {
+      // Test is flaky on Win XP. http://crbug.com/470013.
+      return;
+    }
     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
     GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
@@ -200,22 +201,11 @@
       kUseLenientAudioChecking));
 }
 
-// Below 2 test will make a complete PeerConnection-based call between pc1 and
-// pc2, and then use the remote stream to setup a call between pc3 and pc4, and
-// then verify that video is received on pc3 and pc4.
-// The stream sent from pc3 to pc4 is the stream received on pc1.
-// The stream sent from pc4 to pc3 is cloned from stream the stream received
-// on pc2.
-#if defined(THREAD_SANITIZER) || defined(OS_WIN)
-// Flaky on TSAN v2. http://crbug.com/373637
-// Flaky on Windows: http://crbug.com/469819
-#define MAYBE_CanForwardRemoteStream DISABLED_CanForwardRemoteStream
-#define MAYBE_CanForwardRemoteStream720p DISABLED_CanForwardRemoteStream720p
-#else
-#define MAYBE_CanForwardRemoteStream CanForwardRemoteStream
-#define MAYBE_CanForwardRemoteStream720p CanForwardRemoteStream720p
-#endif
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcBrowserTest, MAYBE_CanForwardRemoteStream) {
+// This test makes a call between pc1 and pc2 where a video only stream is sent
+// from pc1 to pc2. The stream sent from pc1 to pc2 is cloned from the stream
+// received on pc2 to test that cloning of remote video tracks works as
+// intended and is sent back to pc1.
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcBrowserTest, CanForwardRemoteStream) {
 #if defined (OS_ANDROID)
   // This test fails on Nexus 5 devices.
   // TODO(henrika): see http://crbug.com/362437 and http://crbug.com/359389
@@ -228,20 +218,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcBrowserTest,
-                       MAYBE_CanForwardRemoteStream720p) {
-#if defined (OS_ANDROID)
-  // This test fails on Nexus 5 devices.
-  // TODO(henrika): see http://crbug.com/362437 and http://crbug.com/359389
-  // for details.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kDisableWebRtcHWDecoding);
-#endif
-  const std::string javascript = GenerateGetUserMediaCall(
-      "callAndForwardRemoteStream", 1280, 1280, 720, 720, 10, 30);
-  MakeTypicalPeerConnectionCall(javascript);
-}
-
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcBrowserTest,
                        NoCrashWhenConnectChromiumSinkToRemoteTrack) {
   MakeTypicalPeerConnectionCall("ConnectChromiumSinkToRemoteAudioTrack();");
 }
diff --git a/content/browser/notifications/notification_database.cc b/content/browser/notifications/notification_database.cc
index f97ef4cf..10b6ca8 100644
--- a/content/browser/notifications/notification_database.cc
+++ b/content/browser/notifications/notification_database.cc
@@ -14,6 +14,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_database_data.h"
 #include "storage/common/database/database_identifier.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/env.h"
@@ -114,6 +115,7 @@
   leveldb::Options options;
   options.create_if_missing = create_if_missing;
   options.paranoid_checks = true;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   if (IsInMemoryDatabase()) {
     env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
     options.env = env_.get();
diff --git a/content/browser/permissions/permission_service_impl.cc b/content/browser/permissions/permission_service_impl.cc
index 2de48c4..67a7379 100644
--- a/content/browser/permissions/permission_service_impl.cc
+++ b/content/browser/permissions/permission_service_impl.cc
@@ -5,6 +5,7 @@
 #include "content/browser/permissions/permission_service_impl.h"
 
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/permission_type.h"
 
 namespace content {
 
@@ -13,19 +14,19 @@
 PermissionType PermissionNameToPermissionType(PermissionName name) {
   switch(name) {
     case PERMISSION_NAME_GEOLOCATION:
-      return PERMISSION_GEOLOCATION;
+      return PermissionType::GEOLOCATION;
     case PERMISSION_NAME_NOTIFICATIONS:
-      return PERMISSION_NOTIFICATIONS;
+      return PermissionType::NOTIFICATIONS;
     case PERMISSION_NAME_PUSH_NOTIFICATIONS:
-      return PERMISSION_PUSH_MESSAGING;
+      return PermissionType::PUSH_MESSAGING;
     case PERMISSION_NAME_MIDI_SYSEX:
-      return PERMISSION_MIDI_SYSEX;
+      return PermissionType::MIDI_SYSEX;
     case PERMISSION_NAME_PROTECTED_MEDIA_IDENTIFIER:
-      return PERMISSION_PROTECTED_MEDIA_IDENTIFIER;
+      return PermissionType::PROTECTED_MEDIA_IDENTIFIER;
   }
 
   NOTREACHED();
-  return PERMISSION_NUM;
+  return PermissionType::NUM;
 }
 
 } // anonymous namespace
diff --git a/content/browser/permissions/permission_service_impl.h b/content/browser/permissions/permission_service_impl.h
index 33d3321..4982b0a 100644
--- a/content/browser/permissions/permission_service_impl.h
+++ b/content/browser/permissions/permission_service_impl.h
@@ -10,10 +10,11 @@
 #include "base/memory/weak_ptr.h"
 #include "content/browser/permissions/permission_service_context.h"
 #include "content/common/permission_service.mojom.h"
-#include "content/public/browser/permission_type.h"
 
 namespace content {
 
+enum class PermissionType;
+
 // Implements the PermissionService Mojo interface.
 // This service can be created from a RenderFrameHost or a RenderProcessHost.
 // It is owned by a PermissionServiceContext.
diff --git a/content/browser/presentation/presentation_service_impl.cc b/content/browser/presentation/presentation_service_impl.cc
index 2d2a52c..fa4fa3d 100644
--- a/content/browser/presentation/presentation_service_impl.cc
+++ b/content/browser/presentation/presentation_service_impl.cc
@@ -25,6 +25,7 @@
     : WebContentsObserver(web_contents),
       render_frame_host_(render_frame_host),
       delegate_(delegate),
+      next_request_session_id_(0),
       weak_factory_(this) {
   DCHECK(render_frame_host_);
   DCHECK(web_contents);
@@ -84,10 +85,10 @@
   return it->second.get();
 }
 
-void PresentationServiceImpl::GetScreenAvailability(
+void PresentationServiceImpl::ListenForScreenAvailability(
     const mojo::String& presentation_url,
     const ScreenAvailabilityMojoCallback& callback) {
-  DVLOG(2) << "GetScreenAvailability";
+  DVLOG(2) << "ListenForScreenAvailability";
   if (!delegate_) {
     callback.Run(presentation_url, false);
     return;
@@ -102,9 +103,9 @@
   context->CallbackReceived(callback);
 }
 
-void PresentationServiceImpl::OnScreenAvailabilityListenerRemoved(
+void PresentationServiceImpl::RemoveScreenAvailabilityListener(
     const mojo::String& presentation_url) {
-  DVLOG(2) << "OnScreenAvailabilityListenerRemoved";
+  DVLOG(2) << "RemoveScreenAvailabilityListener";
   if (!delegate_)
     return;
 
@@ -133,10 +134,7 @@
     const NewSessionMojoCallback& callback) {
   DVLOG(2) << "StartSession";
   if (!delegate_) {
-    callback.Run(
-        presentation::PresentationSessionInfoPtr(),
-        presentation::PresentationError::From(
-            PresentationError(PRESENTATION_ERROR_UNKNOWN, "")));
+    InvokeNewSessionMojoCallbackWithError(callback);
     return;
   }
 
@@ -152,23 +150,20 @@
     const NewSessionMojoCallback& callback) {
   DVLOG(2) << "JoinSession";
   if (!delegate_) {
-    callback.Run(
-        presentation::PresentationSessionInfoPtr(),
-        presentation::PresentationError::From(
-            PresentationError(PRESENTATION_ERROR_UNKNOWN, "")));
+    InvokeNewSessionMojoCallbackWithError(callback);
     return;
   }
 
+  int request_session_id = RegisterNewSessionCallback(callback);
   delegate_->JoinSession(
       render_frame_host_->GetProcess()->GetID(),
       render_frame_host_->GetRoutingID(),
       presentation_url,
       presentation_id,
-      // TODO(imcheng): These callbacks may be dropped. http://crbug.com/468575
       base::Bind(&PresentationServiceImpl::OnStartOrJoinSessionSucceeded,
-                 weak_factory_.GetWeakPtr(), false, callback),
+                 weak_factory_.GetWeakPtr(), false, request_session_id),
       base::Bind(&PresentationServiceImpl::OnStartOrJoinSessionError,
-                 weak_factory_.GetWeakPtr(), false, callback));
+                 weak_factory_.GetWeakPtr(), false, request_session_id));
 }
 
 void PresentationServiceImpl::HandleQueuedStartSessionRequests() {
@@ -183,27 +178,36 @@
   }
 }
 
+int PresentationServiceImpl::RegisterNewSessionCallback(
+    const NewSessionMojoCallback& callback) {
+  ++next_request_session_id_;
+  pending_session_cbs_[next_request_session_id_].reset(
+      new NewSessionMojoCallback(callback));
+  return next_request_session_id_;
+}
+
 void PresentationServiceImpl::DoStartSession(
     const std::string& presentation_url,
     const std::string& presentation_id,
     const NewSessionMojoCallback& callback) {
+  int request_session_id = RegisterNewSessionCallback(callback);
   delegate_->StartSession(
       render_frame_host_->GetProcess()->GetID(),
       render_frame_host_->GetRoutingID(),
       presentation_url,
       presentation_id,
-      // TODO(imcheng): These callbacks may be dropped. http://crbug.com/468575
       base::Bind(&PresentationServiceImpl::OnStartOrJoinSessionSucceeded,
-                 weak_factory_.GetWeakPtr(), true, callback),
+                 weak_factory_.GetWeakPtr(), true, request_session_id),
       base::Bind(&PresentationServiceImpl::OnStartOrJoinSessionError,
-                 weak_factory_.GetWeakPtr(), true, callback));
+                 weak_factory_.GetWeakPtr(), true, request_session_id));
 }
 
 void PresentationServiceImpl::OnStartOrJoinSessionSucceeded(
     bool is_start_session,
-    const NewSessionMojoCallback& callback,
+    int request_session_id,
     const PresentationSessionInfo& session_info) {
-  callback.Run(
+  RunAndEraseNewSessionMojoCallback(
+      request_session_id,
       presentation::PresentationSessionInfo::From(session_info),
       presentation::PresentationErrorPtr());
   if (is_start_session)
@@ -212,15 +216,29 @@
 
 void PresentationServiceImpl::OnStartOrJoinSessionError(
     bool is_start_session,
-    const NewSessionMojoCallback& callback,
+    int request_session_id,
     const PresentationError& error) {
-  callback.Run(
+  RunAndEraseNewSessionMojoCallback(
+      request_session_id,
       presentation::PresentationSessionInfoPtr(),
       presentation::PresentationError::From(error));
   if (is_start_session)
     HandleQueuedStartSessionRequests();
 }
 
+void PresentationServiceImpl::RunAndEraseNewSessionMojoCallback(
+    int request_session_id,
+    presentation::PresentationSessionInfoPtr session,
+    presentation::PresentationErrorPtr error) {
+  auto it = pending_session_cbs_.find(request_session_id);
+  if (it == pending_session_cbs_.end())
+    return;
+
+  DCHECK(it->second.get());
+  it->second->Run(session.Pass(), error.Pass());
+  pending_session_cbs_.erase(it);
+}
+
 void PresentationServiceImpl::DoSetDefaultPresentationUrl(
     const std::string& default_presentation_url,
     const std::string& default_presentation_id) {
@@ -280,6 +298,11 @@
   NOTIMPLEMENTED();
 }
 
+void PresentationServiceImpl::ListenForSessionStateChange(
+    const SessionStateCallback& callback) {
+  NOTIMPLEMENTED();
+}
+
 void PresentationServiceImpl::DidNavigateAnyFrame(
     content::RenderFrameHost* render_frame_host,
     const content::LoadCommittedDetails& details,
@@ -327,12 +350,26 @@
 
   default_presentation_url_.clear();
   default_presentation_id_.clear();
-  for (const auto& context : availability_contexts_) {
-    context.second->OnScreenAvailabilityChanged(false);
+  for (const auto& context_entry : availability_contexts_) {
+    context_entry.second->OnScreenAvailabilityChanged(false);
   }
   availability_contexts_.clear();
-  // TODO(imcheng): This may drop callbacks. See http://crbug.com/468575.
+  for (auto& request_ptr : queued_start_session_requests_) {
+    InvokeNewSessionMojoCallbackWithError(request_ptr->callback);
+  }
   queued_start_session_requests_.clear();
+  for (auto& pending_entry : pending_session_cbs_) {
+    InvokeNewSessionMojoCallbackWithError(*pending_entry.second);
+  }
+  pending_session_cbs_.clear();
+}
+
+void PresentationServiceImpl::InvokeNewSessionMojoCallbackWithError(
+    const NewSessionMojoCallback& callback) {
+  callback.Run(
+        presentation::PresentationSessionInfoPtr(),
+        presentation::PresentationError::From(
+            PresentationError(PRESENTATION_ERROR_UNKNOWN, "Internal error")));
 }
 
 void PresentationServiceImpl::OnDelegateDestroyed() {
diff --git a/content/browser/presentation/presentation_service_impl.h b/content/browser/presentation/presentation_service_impl.h
index 104b3ae..135320d 100644
--- a/content/browser/presentation/presentation_service_impl.h
+++ b/content/browser/presentation/presentation_service_impl.h
@@ -61,6 +61,9 @@
           presentation::PresentationErrorPtr)>;
   using DefaultSessionMojoCallback =
       mojo::Callback<void(presentation::PresentationSessionInfoPtr)>;
+  using SessionStateCallback =
+      mojo::Callback<void(presentation::PresentationSessionInfoPtr,
+          presentation::PresentationSessionState)>;
 
   // A helper data class used by PresentationServiceImpl to do bookkeeping
   // of currently registered screen availability listeners.
@@ -70,7 +73,7 @@
   // bit, and the state machine will reset.
   // The available bit is obtained from the embedder's media router.
   // The callback is obtained from the renderer via PresentationServiceImpl's
-  // GetScreenAvailability().
+  // ListenForScreenAvailability().
   class CONTENT_EXPORT ScreenAvailabilityContext
       : public PresentationScreenAvailabilityListener {
    public:
@@ -148,10 +151,10 @@
   void SetDefaultPresentationURL(
       const mojo::String& presentation_url,
       const mojo::String& presentation_id) override;
-  void GetScreenAvailability(
+  void ListenForScreenAvailability(
       const mojo::String& presentation_url,
       const ScreenAvailabilityMojoCallback& callback) override;
-  void OnScreenAvailabilityListenerRemoved(
+  void RemoveScreenAvailabilityListener(
       const mojo::String& presentation_url) override;
   void ListenForDefaultSessionStart(
       const DefaultSessionMojoCallback& callback) override;
@@ -166,6 +169,8 @@
   void CloseSession(
       const mojo::String& presentation_url,
       const mojo::String& presentation_id) override;
+  void ListenForSessionStateChange(
+      const SessionStateCallback& callback) override;
 
   // mojo::InterfaceImpl override.
   // Note that this is called when the RenderFrameHost is deleted.
@@ -181,6 +186,14 @@
   // PresentationServiceDelegate::Observer
   void OnDelegateDestroyed() override;
 
+  // Finds the callback from |pending_session_cbs_| using |request_session_id|.
+  // If it exists, invoke it with |session| and |error|, then erase it from
+  // |pending_session_cbs_|.
+  void RunAndEraseNewSessionMojoCallback(
+      int request_session_id,
+      presentation::PresentationSessionInfoPtr session,
+      presentation::PresentationErrorPtr error);
+
   // Sets |default_presentation_url_| to |presentation_url| and informs the
   // delegate of new default presentation URL and ID.
   void DoSetDefaultPresentationUrl(
@@ -196,11 +209,11 @@
   // invocation.
   void OnStartOrJoinSessionSucceeded(
       bool is_start_session,
-      const NewSessionMojoCallback& callback,
+      int request_session_id,
       const PresentationSessionInfo& session_info);
   void OnStartOrJoinSessionError(
       bool is_start_session,
-      const NewSessionMojoCallback& callback,
+      int request_session_id,
       const PresentationError& error);
 
   // Requests delegate to start a session.
@@ -215,6 +228,14 @@
   // the first one in the queue.
   void HandleQueuedStartSessionRequests();
 
+  // Associates |callback| with a unique request ID and stores it in a map.
+  int RegisterNewSessionCallback(
+    const NewSessionMojoCallback& callback);
+
+  // Invokes |callback| with an error.
+  void InvokeNewSessionMojoCallbackWithError(
+      const NewSessionMojoCallback& callback);
+
   // Gets the ScreenAvailabilityContext for |presentation_url|, or creates one
   // if it does not exist.
   ScreenAvailabilityContext* GetOrCreateAvailabilityContext(
@@ -235,6 +256,9 @@
   // it is removed from head of the queue.
   std::deque<linked_ptr<StartSessionRequest>> queued_start_session_requests_;
 
+  int next_request_session_id_;
+  base::hash_map<int, linked_ptr<NewSessionMojoCallback>> pending_session_cbs_;
+
   // NOTE: Weak pointers must be invalidated before all other member variables.
   base::WeakPtrFactory<PresentationServiceImpl> weak_factory_;
 
diff --git a/content/browser/presentation/presentation_service_impl_unittest.cc b/content/browser/presentation/presentation_service_impl_unittest.cc
index fcb7b91..93d02f5c 100644
--- a/content/browser/presentation/presentation_service_impl_unittest.cc
+++ b/content/browser/presentation/presentation_service_impl_unittest.cc
@@ -91,7 +91,7 @@
     RenderViewHostImplTestHarness::TearDown();
   }
 
-  void GetScreenAvailabilityAndWait(
+  void ListenForScreenAvailabilityAndWait(
       const std::string& presentation_url,
       const base::Callback<void(const std::string&, bool)>& callback,
       bool delegate_success) {
@@ -104,7 +104,7 @@
         .WillOnce(DoAll(
             InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
             Return(delegate_success)));
-    service_ptr_->GetScreenAvailability(presentation_url, callback);
+    service_ptr_->ListenForScreenAvailability(presentation_url, callback);
     run_loop.Run();
 
     EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_));
@@ -190,9 +190,9 @@
   int callback_count_;
 };
 
-TEST_F(PresentationServiceImplTest, GetScreenAvailability) {
+TEST_F(PresentationServiceImplTest, ListenForScreenAvailability) {
   std::string presentation_url("http://fooUrl");
-  GetScreenAvailabilityAndWait(
+  ListenForScreenAvailabilityAndWait(
       presentation_url,
       base::Bind(
           &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
@@ -200,7 +200,7 @@
       true);
 
   // Different presentation URL.
-  GetScreenAvailabilityAndWait(
+  ListenForScreenAvailabilityAndWait(
       "http://barUrl",
       base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled,
           base::Unretained(this)),
@@ -217,7 +217,7 @@
 
   // Register another callback which should immediately invoke callback
   // since updated result is available.
-  service_ptr_->GetScreenAvailability(
+  service_ptr_->ListenForScreenAvailability(
       presentation_url,
       base::Bind(
           &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
@@ -229,7 +229,7 @@
 
 TEST_F(PresentationServiceImplTest, Reset) {
   std::string presentation_url("http://fooUrl");
-  GetScreenAvailabilityAndWait(
+  ListenForScreenAvailabilityAndWait(
       presentation_url,
       base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled,
           base::Unretained(this)),
@@ -244,7 +244,7 @@
 
 TEST_F(PresentationServiceImplTest, DidNavigateThisFrame) {
   std::string presentation_url("http://fooUrl");
-  GetScreenAvailabilityAndWait(
+  ListenForScreenAvailabilityAndWait(
       presentation_url,
       base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled,
           base::Unretained(this)),
@@ -260,7 +260,7 @@
 
 TEST_F(PresentationServiceImplTest, DidNavigateNotThisFrame) {
   std::string presentation_url("http://fooUrl");
-  GetScreenAvailabilityAndWait(
+  ListenForScreenAvailabilityAndWait(
       presentation_url,
       base::Bind(
           &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
@@ -283,7 +283,7 @@
 
 TEST_F(PresentationServiceImplTest, ThisRenderFrameDeleted) {
   std::string presentation_url("http://fooUrl");
-  GetScreenAvailabilityAndWait(
+  ListenForScreenAvailabilityAndWait(
       presentation_url,
       base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled,
           base::Unretained(this)),
@@ -296,7 +296,7 @@
 
 TEST_F(PresentationServiceImplTest, NotThisRenderFrameDeleted) {
     std::string presentation_url("http://fooUrl");
-  GetScreenAvailabilityAndWait(
+  ListenForScreenAvailabilityAndWait(
       presentation_url,
       base::Bind(
           &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
@@ -314,9 +314,9 @@
   EXPECT_EQ(1, callback_count_);
 }
 
-TEST_F(PresentationServiceImplTest, GetScreenAvailabilityTwice) {
+TEST_F(PresentationServiceImplTest, ListenForScreenAvailabilityTwice) {
   std::string presentation_url("http://fooUrl");
-  GetScreenAvailabilityAndWait(
+  ListenForScreenAvailabilityAndWait(
       presentation_url,
       base::Bind(
           &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
@@ -326,14 +326,14 @@
 
   // Second call should overwrite the callback from first call.
   // It shouldn't result in an extra call to delegate.
-  service_ptr_->GetScreenAvailability(
+  service_ptr_->ListenForScreenAvailability(
       presentation_url,
       base::Bind(
           &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
           base::Unretained(this),
           false));
 
-  // Cannot use GetScreenAvailabilityAndWait here since the mock delegate
+  // Cannot use ListenForScreenAvailabilityAndWait here since the mock delegate
   // won't be triggered again to quit the RunLoop.
   RunLoopFor(base::TimeDelta::FromMilliseconds(50));
 
@@ -346,7 +346,7 @@
 
 TEST_F(PresentationServiceImplTest, DelegateFails) {
   std::string presentation_url("http://fooUrl");
-  GetScreenAvailabilityAndWait(
+  ListenForScreenAvailabilityAndWait(
       presentation_url,
       base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled,
           base::Unretained(this)),
@@ -365,7 +365,7 @@
   EXPECT_EQ(url1, service_impl_->default_presentation_url_);
 
   // Now there should be a callback registered with the DPU.
-  GetScreenAvailabilityAndWait(
+  ListenForScreenAvailabilityAndWait(
       url1,
       base::Bind(
           &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
@@ -425,7 +425,7 @@
   EXPECT_EQ(url, service_impl_->default_presentation_url_);
 
   // Now there should be a callback registered with the DPU.
-  GetScreenAvailabilityAndWait(
+  ListenForScreenAvailabilityAndWait(
       url,
       base::Bind(
           &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 33e5b5c..853684f 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -143,8 +143,10 @@
     : public cc::TaskGraphRunner,
       public base::DelegateSimpleThread::Delegate {
  public:
-  SingleThreadTaskGraphRunner() : worker_thread_(this, "CompositorWorker") {
+  SingleThreadTaskGraphRunner()
+      : worker_thread_(this, "CompositorTileWorker1") {
     worker_thread_.Start();
+    worker_thread_.SetThreadPriority(base::kThreadPriority_Background);
   }
 
   ~SingleThreadTaskGraphRunner() override {
@@ -436,6 +438,7 @@
 }
 
 void CompositorImpl::SetVisible(bool visible) {
+  TRACE_EVENT1("cc", "CompositorImpl::SetVisible", "visible", visible);
   if (!visible) {
     DCHECK(host_);
     // Look for any layers that were attached to the root for readback
@@ -467,6 +470,7 @@
     CreateLayerTreeHost();
     ui_resource_provider_.SetLayerTreeHost(host_.get());
   }
+  root_window_->OnVisibilityChanged(visible);
 }
 
 void CompositorImpl::setDeviceScaleFactor(float factor) {
diff --git a/content/browser/renderer_host/delegated_frame_evictor.cc b/content/browser/renderer_host/delegated_frame_evictor.cc
index 33d6b2c..65ae4832 100644
--- a/content/browser/renderer_host/delegated_frame_evictor.cc
+++ b/content/browser/renderer_host/delegated_frame_evictor.cc
@@ -10,11 +10,13 @@
 
 DelegatedFrameEvictor::DelegatedFrameEvictor(
     DelegatedFrameEvictorClient* client)
-    : client_(client), has_frame_(false) {}
+    : client_(client), has_frame_(false), visible_(false) {
+}
 
 DelegatedFrameEvictor::~DelegatedFrameEvictor() { DiscardedFrame(); }
 
 void DelegatedFrameEvictor::SwappedFrame(bool visible) {
+  visible_ = visible;
   has_frame_ = true;
   RendererFrameManager::GetInstance()->AddFrame(this, visible);
 }
@@ -25,11 +27,14 @@
 }
 
 void DelegatedFrameEvictor::SetVisible(bool visible) {
+  if (visible_ == visible)
+    return;
+  visible_ = visible;
   if (has_frame_) {
     if (visible) {
-      RendererFrameManager::GetInstance()->LockFrame(this);
+      LockFrame();
     } else {
-      RendererFrameManager::GetInstance()->UnlockFrame(this);
+      UnlockFrame();
     }
   }
 }
diff --git a/content/browser/renderer_host/delegated_frame_evictor.h b/content/browser/renderer_host/delegated_frame_evictor.h
index 796b6c2..9e9f68a61 100644
--- a/content/browser/renderer_host/delegated_frame_evictor.h
+++ b/content/browser/renderer_host/delegated_frame_evictor.h
@@ -35,6 +35,7 @@
 
   DelegatedFrameEvictorClient* client_;
   bool has_frame_;
+  bool visible_;
 
   DISALLOW_COPY_AND_ASSIGN(DelegatedFrameEvictor);
 };
diff --git a/content/browser/renderer_host/media/video_capture_texture_wrapper.cc b/content/browser/renderer_host/media/video_capture_texture_wrapper.cc
index e02b6fc..773de53f4 100644
--- a/content/browser/renderer_host/media/video_capture_texture_wrapper.cc
+++ b/content/browser/renderer_host/media/video_capture_texture_wrapper.cc
@@ -283,11 +283,17 @@
   gpu_memory_buffers_.pop();
   DCHECK(gpu_memory_buffer.get());
 
-  uint8* mapped_buffer = static_cast<uint8*>(gpu_memory_buffer->Map());
+  void* data = NULL;
+  bool rv = gpu_memory_buffer->Map(&data);
+  DCHECK(rv);
+  uint32 stride;
+  gpu_memory_buffer->GetStride(&stride);
+
+  uint8* mapped_buffer = static_cast<uint8*>(data);
   DCHECK(mapped_buffer);
   libyuv::ARGBCopy(
       reinterpret_cast<uint8*>(argb_buffer->data()), frame_size.width() * 4,
-      mapped_buffer, frame_size.width() * 4,
+      mapped_buffer, stride,
       frame_size.width(), frame_size.height());
   gpu_memory_buffer->Unmap();
 
diff --git a/content/browser/renderer_host/p2p/socket_host_udp.cc b/content/browser/renderer_host/p2p/socket_host_udp.cc
index fd09f7d..b67fd55 100644
--- a/content/browser/renderer_host/p2p/socket_host_udp.cc
+++ b/content/browser/renderer_host/p2p/socket_host_udp.cc
@@ -72,13 +72,20 @@
                                    int socket_id,
                                    P2PMessageThrottler* throttler)
     : P2PSocketHost(message_sender, socket_id, P2PSocketHost::UDP),
-      socket_(
-          new net::UDPServerSocket(GetContentClient()->browser()->GetNetLog(),
-                                   net::NetLog::Source())),
       send_pending_(false),
       last_dscp_(net::DSCP_CS0),
       throttler_(throttler),
       send_buffer_size_(0) {
+  net::UDPServerSocket* socket = new net::UDPServerSocket(
+      GetContentClient()->browser()->GetNetLog(), net::NetLog::Source());
+#if defined(OS_WIN)
+  // If configured for finch experiment, use nonblocking IO.
+  if (base::FieldTrialList::FindFullName("WebRTC-UDPSocketNonBlockingIO") ==
+      "Enabled") {
+    socket->UseNonBlockingIO();
+  }
+#endif
+  socket_.reset(socket);
 }
 
 P2PSocketHostUdp::~P2PSocketHostUdp() {
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index c32da90..24f0286b 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -213,7 +213,7 @@
 
 scoped_ptr<IPC::Message> SendAudioHardwareConfig() {
   media::AudioManager* am = media::AudioManager::Get();
-  DCHECK(am->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(am->GetWorkerTaskRunner()->BelongsToCurrentThread());
   return make_scoped_ptr(new ViewMsg_SetAudioHardwareConfig(
       am->GetDefaultOutputStreamParameters(),
       am->GetInputStreamParameters(media::AudioManagerBase::kDefaultDeviceId)));
@@ -1534,7 +1534,7 @@
       IPC_MESSAGE_HANDLER(ChildProcessHostMsg_DumpHandlesDone,
                           OnDumpHandlesDone)
       IPC_MESSAGE_HANDLER(ViewHostMsg_SuddenTerminationChanged,
-                          OnSuddenTerminationChanged)
+                          SuddenTerminationChanged)
       IPC_MESSAGE_HANDLER(ViewHostMsg_UserMetricsRecordAction,
                           OnUserMetricsRecordAction)
       IPC_MESSAGE_HANDLER(ViewHostMsg_SavedPageAsMHTML, OnSavedPageAsMHTML)
@@ -1700,6 +1700,10 @@
   pending_views_--;
 }
 
+void RenderProcessHostImpl::SetSuddenTerminationAllowed(bool enabled) {
+  sudden_termination_allowed_ = enabled;
+}
+
 bool RenderProcessHostImpl::SuddenTerminationAllowed() const {
   return sudden_termination_allowed_;
 }
@@ -2210,8 +2214,8 @@
   Send(new ChildProcessMsg_Shutdown());
 }
 
-void RenderProcessHostImpl::OnSuddenTerminationChanged(bool enabled) {
-  sudden_termination_allowed_ = enabled;
+void RenderProcessHostImpl::SuddenTerminationChanged(bool enabled) {
+  SetSuddenTerminationAllowed(enabled);
 }
 
 void RenderProcessHostImpl::OnDumpHandlesDone() {
@@ -2219,6 +2223,8 @@
 }
 
 void RenderProcessHostImpl::SetBackgrounded(bool backgrounded) {
+  TRACE_EVENT1("renderer_host", "RenderProcessHostImpl::SetBackgrounded",
+               "backgrounded", backgrounded);
   // Note: we always set the backgrounded_ value.  If the process is NULL
   // (and hence hasn't been created yet), we will set the process priority
   // later when we create the process.
@@ -2334,7 +2340,7 @@
   }
 
   base::PostTaskAndReplyWithResult(
-      media::AudioManager::Get()->GetTaskRunner().get(), FROM_HERE,
+      media::AudioManager::Get()->GetWorkerTaskRunner().get(), FROM_HERE,
       base::Bind(&SendAudioHardwareConfig),
       base::Bind(base::IgnoreResult(&RenderProcessHostImpl::SendHelper),
                  weak_factory_.GetWeakPtr()));
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 6f61501e..93b7aa5 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -122,6 +122,7 @@
   void Cleanup() override;
   void AddPendingView() override;
   void RemovePendingView() override;
+  void SetSuddenTerminationAllowed(bool enabled) override;
   bool SuddenTerminationAllowed() const override;
   IPC::ChannelProxy* GetChannel() override;
   void AddFilter(BrowserMessageFilter* filter) override;
@@ -317,7 +318,7 @@
   // Control message handlers.
   void OnShutdownRequest();
   void OnDumpHandlesDone();
-  void OnSuddenTerminationChanged(bool enabled);
+  void SuddenTerminationChanged(bool enabled);
   void OnUserMetricsRecordAction(const std::string& action);
   void OnSavedPageAsMHTML(int job_id, int64 mhtml_file_size);
   void OnCloseACK(int old_route_id);
@@ -418,12 +419,12 @@
   // The observers watching our lifetime.
   ObserverList<RenderProcessHostObserver> observers_;
 
-  // True if the process can be shut down suddenly. If this is true, then it's
-  // sure that all the RenderFrames in the process can be shutdown suddenly. If
-  // it's false, then specific RenderFrames might still be allowed to be
-  // shutdown suddenly by checking their SuddenTerminationAllowed() flag. This
-  // can occur when a RenderFrame has an unload/beforeUnload event listener
-  // registered but another RenderFrame in the same process doesn't.
+  // True if the process can be shut down suddenly.  If this is true, then we're
+  // sure that all the RenderViews in the process can be shutdown suddenly.  If
+  // it's false, then specific RenderViews might still be allowed to be shutdown
+  // suddenly by checking their SuddenTerminationAllowed() flag.  This can occur
+  // if one WebContents has an unload event listener but another WebContents in
+  // the same process doesn't.
   bool sudden_termination_allowed_;
 
   // Set to true if we shouldn't send input events.  We actually do the
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 381ddea..cdc82e9 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -126,19 +126,6 @@
 }
 #endif
 
-// Enable sudden termination for the current RenderFrameHost of
-// |frame_tree_node| if the ID of its SiteInstance is |site_instance_id|.  Used
-// with FrameTree::ForEach.
-bool EnableSuddenTermination(int32 site_instance_id,
-                             FrameTreeNode* frame_tree_node) {
-  if (frame_tree_node->current_frame_host()->GetSiteInstance()->GetId()
-      == site_instance_id) {
-    frame_tree_node->current_frame_host()
-        ->set_override_sudden_termination_status(true);
-  }
-  return true;
-}
-
 }  // namespace
 
 // static
@@ -195,6 +182,7 @@
       is_swapped_out_(swapped_out),
       main_frame_routing_id_(main_frame_routing_id),
       is_waiting_for_close_ack_(false),
+      sudden_termination_allowed_(false),
       render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING),
       virtual_keyboard_requested_(false),
       is_focused_element_editable_(false),
@@ -530,10 +518,7 @@
   StopHangMonitorTimeout();
   is_waiting_for_close_ack_ = false;
 
-  // Enable sudden termination for all RenderFrameHosts in the FrameTree.
-  FrameTree* frame_tree = static_cast<RenderFrameHostImpl*>(GetMainFrame())
-      ->frame_tree_node()->frame_tree();
-  frame_tree->ForEach(base::Bind(&EnableSuddenTermination, instance_->GetId()));
+  sudden_termination_allowed_ = true;
   delegate_->Close(this);
 }
 
@@ -822,6 +807,11 @@
   delegate_->LoadStateChanged(url, load_state, upload_position, upload_size);
 }
 
+bool RenderViewHostImpl::SuddenTerminationAllowed() const {
+  return sudden_termination_allowed_ ||
+      GetProcess()->SuddenTerminationAllowed();
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // RenderViewHostImpl, IPC message handlers:
 
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index 69d6f47..b15019b 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -246,6 +246,11 @@
                         uint64 upload_position,
                         uint64 upload_size);
 
+  bool SuddenTerminationAllowed() const;
+  void set_sudden_termination_allowed(bool enabled) {
+    sudden_termination_allowed_ = enabled;
+  }
+
   // RenderWidgetHost public overrides.
   void Init() override;
   void Shutdown() override;
@@ -427,6 +432,9 @@
   // See http://crbug.com/418265.
   bool is_waiting_for_close_ack_;
 
+  // True if the render view can be shut down suddenly.
+  bool sudden_termination_allowed_;
+
   // The termination status of the last render view that terminated.
   base::TerminationStatus render_view_termination_status_;
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index dfff5e5..4aef7b8 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -511,6 +511,7 @@
   if (is_hidden_)
     return;
 
+  TRACE_EVENT0("renderer_host", "RenderWidgetHostImpl::WasHidden");
   is_hidden_ = true;
 
   // Don't bother reporting hung state when we aren't active.
@@ -533,6 +534,8 @@
 void RenderWidgetHostImpl::WasShown(const ui::LatencyInfo& latency_info) {
   if (!is_hidden_)
     return;
+
+  TRACE_EVENT0("renderer_host", "RenderWidgetHostImpl::WasShown");
   is_hidden_ = false;
 
   SendScreenRects();
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 41416be..b62b269 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -596,23 +596,7 @@
     return;
 
   is_showing_ = true;
-  if (layer_.get())
-    layer_->SetHideLayerAndSubtree(false);
-
-  if (overscroll_controller_)
-    overscroll_controller_->Enable();
-
-  frame_evictor_->SetVisible(true);
-
-  if (!host_ || !host_->is_hidden())
-    return;
-
-  host_->WasShown(ui::LatencyInfo());
-
-  if (content_view_core_) {
-    StartObservingRootWindow();
-    RequestVSyncUpdate(BEGIN_FRAME);
-  }
+  ShowInternal();
 }
 
 void RenderWidgetHostViewAndroid::Hide() {
@@ -620,27 +604,10 @@
     return;
 
   is_showing_ = false;
-  if (layer_.get() && locks_on_frame_count_ == 0)
-    layer_->SetHideLayerAndSubtree(true);
 
-  if (overscroll_controller_)
-    overscroll_controller_->Disable();
-
-  frame_evictor_->SetVisible(false);
-  // We don't know if we will ever get a frame if we are hiding the renderer, so
-  // we need to cancel all requests
-  AbortPendingReadbackRequests();
-
-  RunAckCallbacks(cc::SurfaceDrawStatus::DRAW_SKIPPED);
-
-  if (!host_ || host_->is_hidden())
-    return;
-
-  // Inform the renderer that we are being hidden so it can reduce its resource
-  // utilization.
-  host_->WasHidden();
-
-  StopObservingRootWindow();
+  bool hide_frontbuffer = true;
+  bool stop_observing_root_window = true;
+  HideInternal(hide_frontbuffer, stop_observing_root_window);
 }
 
 bool RenderWidgetHostViewAndroid::IsShowing() {
@@ -1487,6 +1454,57 @@
   accelerated_surface_route_id_ = route_id;
 }
 
+void RenderWidgetHostViewAndroid::ShowInternal() {
+  DCHECK(is_showing_);
+  if (!host_ || !host_->is_hidden())
+    return;
+
+  if (layer_.get())
+    layer_->SetHideLayerAndSubtree(false);
+
+  frame_evictor_->SetVisible(true);
+
+  if (overscroll_controller_)
+    overscroll_controller_->Enable();
+
+  host_->WasShown(ui::LatencyInfo());
+
+  if (content_view_core_) {
+    StartObservingRootWindow();
+    RequestVSyncUpdate(BEGIN_FRAME);
+  }
+}
+
+void RenderWidgetHostViewAndroid::HideInternal(
+    bool hide_frontbuffer,
+    bool stop_observing_root_window) {
+  if (hide_frontbuffer) {
+    if (layer_.get() && locks_on_frame_count_ == 0)
+      layer_->SetHideLayerAndSubtree(true);
+
+    frame_evictor_->SetVisible(false);
+  }
+
+  if (stop_observing_root_window)
+    StopObservingRootWindow();
+
+  if (!host_ || host_->is_hidden())
+    return;
+
+  if (overscroll_controller_)
+    overscroll_controller_->Disable();
+
+  // We don't know if we will ever get a frame if we are hiding the renderer, so
+  // we need to cancel all requests
+  AbortPendingReadbackRequests();
+
+  RunAckCallbacks(cc::SurfaceDrawStatus::DRAW_SKIPPED);
+
+  // Inform the renderer that we are being hidden so it can reduce its resource
+  // utilization.
+  host_->WasHidden();
+}
+
 void RenderWidgetHostViewAndroid::AttachLayers() {
   if (!content_view_core_)
     return;
@@ -1518,6 +1536,12 @@
 
   bool should_request_vsync = !outstanding_vsync_requests_ && requests;
   outstanding_vsync_requests_ |= requests;
+
+  // If the host has been hidden, defer vsync requests until it is shown
+  // again via |Show()|.
+  if (!host_ || host_->is_hidden())
+    return;
+
   // Note that if we're not currently observing the root window, outstanding
   // vsync requests will be pushed if/when we resume observing in
   // |StartObservingRootWindow()|.
@@ -1527,6 +1551,7 @@
 
 void RenderWidgetHostViewAndroid::StartObservingRootWindow() {
   DCHECK(content_view_core_);
+  DCHECK(is_showing_);
   if (observing_root_window_)
     return;
 
@@ -1827,7 +1852,8 @@
   if (!content_view_core_)
     return;
 
-  StartObservingRootWindow();
+  if (is_showing_)
+    StartObservingRootWindow();
 
   if (resize)
     WasResized();
@@ -1868,6 +1894,17 @@
   RunAckCallbacks(cc::SurfaceDrawStatus::DRAWN);
 }
 
+void RenderWidgetHostViewAndroid::OnRootWindowVisibilityChanged(bool visible) {
+  DCHECK(is_showing_);
+  if (visible) {
+    ShowInternal();
+  } else {
+    bool hide_frontbuffer = true;
+    bool stop_observing_root_window = false;
+    HideInternal(hide_frontbuffer, stop_observing_root_window);
+  }
+}
+
 void RenderWidgetHostViewAndroid::OnAttachCompositor() {
   DCHECK(content_view_core_);
   if (!overscroll_controller_)
@@ -1884,7 +1921,7 @@
 void RenderWidgetHostViewAndroid::OnVSync(base::TimeTicks frame_time,
                                           base::TimeDelta vsync_period) {
   TRACE_EVENT0("cc,benchmark", "RenderWidgetHostViewAndroid::OnVSync");
-  if (!host_)
+  if (!host_ || host_->is_hidden())
     return;
 
   const uint32 current_vsync_requests = outstanding_vsync_requests_;
@@ -1907,6 +1944,20 @@
     SetNeedsAnimate();
 }
 
+void RenderWidgetHostViewAndroid::OnActivityPaused() {
+  TRACE_EVENT0("browser", "RenderWidgetHostViewAndroid::OnActivityPaused");
+  DCHECK(is_showing_);
+  bool hide_frontbuffer = false;
+  bool stop_observing_root_window = false;
+  HideInternal(hide_frontbuffer, stop_observing_root_window);
+}
+
+void RenderWidgetHostViewAndroid::OnActivityResumed() {
+  TRACE_EVENT0("browser", "RenderWidgetHostViewAndroid::OnActivityResumed");
+  DCHECK(is_showing_);
+  ShowInternal();
+}
+
 void RenderWidgetHostViewAndroid::OnLostResources() {
   ReleaseLocksOnSurface();
   if (layer_.get())
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index e1cf270d..296c7854 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -193,11 +193,14 @@
 
   // ui::WindowAndroidObserver implementation.
   void OnCompositingDidCommit() override;
+  void OnRootWindowVisibilityChanged(bool visible) override;
   void OnAttachCompositor() override;
   void OnDetachCompositor() override;
   void OnVSync(base::TimeTicks frame_time,
                base::TimeDelta vsync_period) override;
   void OnAnimate(base::TimeTicks begin_frame_time) override;
+  void OnActivityPaused() override;
+  void OnActivityResumed() override;
 
   // DelegatedFrameEvictor implementation
   void EvictDelegatedFrame() override;
@@ -290,6 +293,8 @@
       const cc::CompositorFrameMetadata& frame_metadata);
   void ComputeContentsSize(const cc::CompositorFrameMetadata& frame_metadata);
 
+  void ShowInternal();
+  void HideInternal(bool hide_frontbuffer, bool stop_observing_root_window);
   void AttachLayers();
   void RemoveLayers();
 
diff --git a/content/browser/screen_orientation/screen_orientation_browsertest.cc b/content/browser/screen_orientation/screen_orientation_browsertest.cc
index e22784c..4b1d40b3 100644
--- a/content/browser/screen_orientation/screen_orientation_browsertest.cc
+++ b/content/browser/screen_orientation/screen_orientation_browsertest.cc
@@ -243,4 +243,30 @@
   // here.
 }
 
+#if defined(OS_ANDROID)
+class ScreenOrientationLockDisabledBrowserTest : public ContentBrowserTest  {
+ public:
+  ScreenOrientationLockDisabledBrowserTest() {}
+  ~ScreenOrientationLockDisabledBrowserTest() override {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kDisableScreenOrientationLock);
+  }
+};
+
+// Check that when --disable-screen-orientation-lock is passed to the command
+// line, screen.orientation.lock() correctly reports to not be supported.
+IN_PROC_BROWSER_TEST_F(ScreenOrientationLockDisabledBrowserTest, NotSupported) {
+  GURL test_url = GetTestUrl("screen_orientation",
+                             "screen_orientation_lock_disabled.html");
+
+  TestNavigationObserver navigation_observer(shell()->web_contents(), 2);
+  shell()->LoadURL(test_url);
+  navigation_observer.Wait();
+
+  EXPECT_EQ("NotSupportedError",
+            shell()->web_contents()->GetLastCommittedURL().ref());
+}
+#endif // defined(OS_ANDROID)
+
 } // namespace content
diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc
index c2d753c..fffd891 100644
--- a/content/browser/service_worker/embedded_worker_instance_unittest.cc
+++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -36,7 +36,8 @@
       : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
 
   void SetUp() override {
-    helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId));
+    helper_.reset(
+        new EmbeddedWorkerTestHelper(base::FilePath(), kRenderProcessId));
   }
 
   void TearDown() override { helper_.reset(); }
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index d58ada3..5da4474d 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -18,7 +18,9 @@
 
 namespace content {
 
-EmbeddedWorkerTestHelper::EmbeddedWorkerTestHelper(int mock_render_process_id)
+EmbeddedWorkerTestHelper::EmbeddedWorkerTestHelper(
+    const base::FilePath& user_data_directory,
+    int mock_render_process_id)
     : wrapper_(new ServiceWorkerContextWrapper(NULL)),
       next_thread_id_(0),
       mock_render_process_id_(mock_render_process_id),
@@ -26,11 +28,8 @@
   scoped_ptr<MockServiceWorkerDatabaseTaskManager> database_task_manager(
       new MockServiceWorkerDatabaseTaskManager(
           base::MessageLoopProxy::current()));
-  wrapper_->InitInternal(base::FilePath(),
-                         database_task_manager.Pass(),
-                         base::MessageLoopProxy::current(),
-                         NULL,
-                         NULL);
+  wrapper_->InitInternal(user_data_directory, database_task_manager.Pass(),
+                         base::MessageLoopProxy::current(), NULL, NULL);
   wrapper_->process_manager()->SetProcessIdForTest(mock_render_process_id);
   registry()->AddChildProcessSender(mock_render_process_id, this, nullptr);
 }
diff --git a/content/browser/service_worker/embedded_worker_test_helper.h b/content/browser/service_worker/embedded_worker_test_helper.h
index fe1a16e..c81d191 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.h
+++ b/content/browser/service_worker/embedded_worker_test_helper.h
@@ -44,8 +44,10 @@
                                  public IPC::Listener {
  public:
   // Initialize this helper for |context|, and enable this as an IPC
-  // sender for |mock_render_process_id|.
-  explicit EmbeddedWorkerTestHelper(int mock_render_process_id);
+  // sender for |mock_render_process_id|. If |user_data_directory| is empty,
+  // the context makes storage stuff in memory.
+  EmbeddedWorkerTestHelper(const base::FilePath& user_data_directory,
+                           int mock_render_process_id);
   ~EmbeddedWorkerTestHelper() override;
 
   // Call this to simulate add/associate a process to a pattern.
diff --git a/content/browser/service_worker/service_worker_cache_storage_manager.cc b/content/browser/service_worker/service_worker_cache_storage_manager.cc
index 2af5c4415..93d5b6a 100644
--- a/content/browser/service_worker/service_worker_cache_storage_manager.cc
+++ b/content/browser/service_worker/service_worker_cache_storage_manager.cc
@@ -21,6 +21,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "net/base/net_util.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
+#include "storage/common/database/database_identifier.h"
 #include "storage/common/quota/quota_status_code.h"
 #include "url/gurl.h"
 
@@ -28,14 +29,6 @@
 
 namespace {
 
-base::FilePath ConstructOriginPath(const base::FilePath& root_path,
-                                   const GURL& origin) {
-  std::string origin_hash = base::SHA1HashString(origin.spec());
-  std::string origin_hash_hex = base::StringToLowerASCII(
-      base::HexEncode(origin_hash.c_str(), origin_hash.length()));
-  return root_path.AppendASCII(origin_hash_hex);
-}
-
 bool DeleteDir(const base::FilePath& path) {
   return base::DeleteFile(path, true /* recursive */);
 }
@@ -214,6 +207,7 @@
     return;
   }
 
+  MigrateOrigin(origin_url);
   PostTaskAndReplyWithResult(
       cache_task_runner_.get(),
       FROM_HERE,
@@ -298,6 +292,7 @@
     return;
   }
 
+  cache_manager->MigrateOrigin(origin);
   PostTaskAndReplyWithResult(
       cache_manager->cache_task_runner_.get(), FROM_HERE,
       base::Bind(&DeleteDir,
@@ -328,6 +323,7 @@
   ServiceWorkerCacheStorageMap::const_iterator it =
       cache_storage_map_.find(origin);
   if (it == cache_storage_map_.end()) {
+    MigrateOrigin(origin);
     ServiceWorkerCacheStorage* cache_storage =
         new ServiceWorkerCacheStorage(ConstructOriginPath(root_path_, origin),
                                       IsMemoryBacked(),
@@ -343,4 +339,47 @@
   return it->second;
 }
 
+// static
+base::FilePath ServiceWorkerCacheStorageManager::ConstructLegacyOriginPath(
+    const base::FilePath& root_path,
+    const GURL& origin) {
+  const std::string origin_hash = base::SHA1HashString(origin.spec());
+  const std::string origin_hash_hex = base::StringToLowerASCII(
+      base::HexEncode(origin_hash.c_str(), origin_hash.length()));
+  return root_path.AppendASCII(origin_hash_hex);
+}
+
+// static
+base::FilePath ServiceWorkerCacheStorageManager::ConstructOriginPath(
+    const base::FilePath& root_path,
+    const GURL& origin) {
+  const std::string identifier = storage::GetIdentifierFromOrigin(origin);
+  const std::string origin_hash = base::SHA1HashString(identifier);
+  const std::string origin_hash_hex = base::StringToLowerASCII(
+      base::HexEncode(origin_hash.c_str(), origin_hash.length()));
+  return root_path.AppendASCII(origin_hash_hex);
+}
+
+// Migrate from old origin-based path to storage identifier-based path.
+// TODO(jsbell); Remove after a few releases.
+void ServiceWorkerCacheStorageManager::MigrateOrigin(const GURL& origin) {
+  if (IsMemoryBacked())
+    return;
+  base::FilePath old_path = ConstructLegacyOriginPath(root_path_, origin);
+  base::FilePath new_path = ConstructOriginPath(root_path_, origin);
+  cache_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&MigrateOriginOnTaskRunner, old_path, new_path));
+}
+
+// static
+void ServiceWorkerCacheStorageManager::MigrateOriginOnTaskRunner(
+    const base::FilePath& old_path,
+    const base::FilePath& new_path) {
+  if (base::PathExists(old_path)) {
+    if (!base::PathExists(new_path))
+      base::Move(old_path, new_path);
+    base::DeleteFile(old_path, /*recursive*/ true);
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_cache_storage_manager.h b/content/browser/service_worker/service_worker_cache_storage_manager.h
index a3e2e2b..b43c35f 100644
--- a/content/browser/service_worker/service_worker_cache_storage_manager.h
+++ b/content/browser/service_worker/service_worker_cache_storage_manager.h
@@ -10,6 +10,7 @@
 
 #include "base/basictypes.h"
 #include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
 #include "content/browser/service_worker/service_worker_cache_storage.h"
 #include "content/common/content_export.h"
 #include "storage/browser/quota/quota_client.h"
@@ -31,7 +32,6 @@
 namespace content {
 
 class ServiceWorkerCacheQuotaClient;
-class ServiceWorkerCacheStorageManagerTest;
 
 // Keeps track of a ServiceWorkerCacheStorage per origin. There is one
 // ServiceWorkerCacheStorageManager per ServiceWorkerContextCore.
@@ -87,6 +87,7 @@
  private:
   friend class ServiceWorkerCacheQuotaClient;
   friend class ServiceWorkerCacheStorageManagerTest;
+  friend class ServiceWorkerCacheStorageMigrationTest;
 
   typedef std::map<GURL, ServiceWorkerCacheStorage*>
       ServiceWorkerCacheStorageMap;
@@ -129,6 +130,21 @@
 
   bool IsMemoryBacked() const { return root_path_.empty(); }
 
+  // Map a origin to the path. Exposed for testing.
+  static base::FilePath ConstructLegacyOriginPath(
+      const base::FilePath& root_path,
+      const GURL& origin);
+  // Map a database identifier (computed from an origin) to the path. Exposed
+  // for testing.
+  static base::FilePath ConstructOriginPath(const base::FilePath& root_path,
+                                            const GURL& origin);
+
+  // Migrate from old origin-based path to storage identifier-based path.
+  // TODO(jsbell); Remove method and all calls after a few releases.
+  void MigrateOrigin(const GURL& origin);
+  static void MigrateOriginOnTaskRunner(const base::FilePath& old_path,
+                                        const base::FilePath& new_path);
+
   base::FilePath root_path_;
   scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
 
diff --git a/content/browser/service_worker/service_worker_cache_storage_manager_unittest.cc b/content/browser/service_worker/service_worker_cache_storage_manager_unittest.cc
index 9f0cf59..512ae3f4 100644
--- a/content/browser/service_worker/service_worker_cache_storage_manager_unittest.cc
+++ b/content/browser/service_worker/service_worker_cache_storage_manager_unittest.cc
@@ -5,9 +5,11 @@
 #include "content/browser/service_worker/service_worker_cache_storage_manager.h"
 
 #include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/message_loop/message_loop_proxy.h"
 #include "base/run_loop.h"
+#include "base/stl_util.h"
 #include "content/browser/fileapi/chrome_blob_storage_context.h"
 #include "content/browser/quota/mock_quota_manager_proxy.h"
 #include "content/browser/service_worker/service_worker_cache_quota_client.h"
@@ -567,6 +569,115 @@
   EXPECT_EQ(0, cache_storage->MemoryBackedSize());
 }
 
+class ServiceWorkerCacheStorageMigrationTest
+    : public ServiceWorkerCacheStorageManagerTest {
+ protected:
+  ServiceWorkerCacheStorageMigrationTest() : cache1_("foo"), cache2_("bar") {}
+
+  void SetUp() override {
+    ServiceWorkerCacheStorageManagerTest::SetUp();
+
+    // Populate a cache, then move it to the "legacy" location
+    // so that tests can verify the results of migration.
+    legacy_path_ = ServiceWorkerCacheStorageManager::ConstructLegacyOriginPath(
+        cache_manager_->root_path(), origin1_);
+    new_path_ = ServiceWorkerCacheStorageManager::ConstructOriginPath(
+        cache_manager_->root_path(), origin1_);
+
+    ASSERT_FALSE(base::DirectoryExists(legacy_path_));
+    ASSERT_FALSE(base::DirectoryExists(new_path_));
+    ASSERT_TRUE(Open(origin1_, cache1_));
+    ASSERT_TRUE(Open(origin1_, cache2_));
+    callback_cache_ = nullptr;
+    ASSERT_FALSE(base::DirectoryExists(legacy_path_));
+    ASSERT_TRUE(base::DirectoryExists(new_path_));
+
+    quota_manager_proxy_->SimulateQuotaManagerDestroyed();
+    cache_manager_ =
+        ServiceWorkerCacheStorageManager::Create(cache_manager_.get());
+
+    ASSERT_TRUE(base::Move(new_path_, legacy_path_));
+    ASSERT_TRUE(base::DirectoryExists(legacy_path_));
+    ASSERT_FALSE(base::DirectoryExists(new_path_));
+  }
+
+  int64 GetOriginUsage(const GURL& origin) {
+    scoped_ptr<base::RunLoop> loop(new base::RunLoop());
+    cache_manager_->GetOriginUsage(
+        origin,
+        base::Bind(&ServiceWorkerCacheStorageMigrationTest::UsageCallback,
+                   base::Unretained(this), base::Unretained(loop.get())));
+    loop->Run();
+    return callback_usage_;
+  }
+
+  void UsageCallback(base::RunLoop* run_loop, int64 usage) {
+    callback_usage_ = usage;
+    run_loop->Quit();
+  }
+
+  base::FilePath legacy_path_;
+  base::FilePath new_path_;
+
+  const std::string cache1_;
+  const std::string cache2_;
+
+  int64 callback_usage_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerCacheStorageMigrationTest);
+};
+
+TEST_F(ServiceWorkerCacheStorageMigrationTest, OpenCache) {
+  EXPECT_TRUE(Open(origin1_, cache1_));
+  EXPECT_FALSE(base::DirectoryExists(legacy_path_));
+  EXPECT_TRUE(base::DirectoryExists(new_path_));
+
+  EXPECT_TRUE(Keys(origin1_));
+  std::vector<std::string> expected_keys;
+  expected_keys.push_back(cache1_);
+  expected_keys.push_back(cache2_);
+  EXPECT_EQ(expected_keys, callback_strings_);
+}
+
+TEST_F(ServiceWorkerCacheStorageMigrationTest, DeleteCache) {
+  EXPECT_TRUE(Delete(origin1_, cache1_));
+  EXPECT_FALSE(base::DirectoryExists(legacy_path_));
+  EXPECT_TRUE(base::DirectoryExists(new_path_));
+
+  EXPECT_TRUE(Keys(origin1_));
+  std::vector<std::string> expected_keys;
+  expected_keys.push_back(cache2_);
+  EXPECT_EQ(expected_keys, callback_strings_);
+}
+
+TEST_F(ServiceWorkerCacheStorageMigrationTest, GetOriginUsage) {
+  EXPECT_GT(GetOriginUsage(origin1_), 0);
+  EXPECT_FALSE(base::DirectoryExists(legacy_path_));
+  EXPECT_TRUE(base::DirectoryExists(new_path_));
+}
+
+TEST_F(ServiceWorkerCacheStorageMigrationTest, MoveFailure) {
+  // Revert the migration.
+  ASSERT_TRUE(base::Move(legacy_path_, new_path_));
+  ASSERT_FALSE(base::DirectoryExists(legacy_path_));
+  ASSERT_TRUE(base::DirectoryExists(new_path_));
+
+  // Make a dummy legacy directory.
+  ASSERT_TRUE(base::CreateDirectory(legacy_path_));
+
+  // Ensure that migration doesn't stomp existing new directory,
+  // but does clean up old directory.
+  EXPECT_TRUE(Open(origin1_, cache1_));
+  EXPECT_FALSE(base::DirectoryExists(legacy_path_));
+  EXPECT_TRUE(base::DirectoryExists(new_path_));
+
+  EXPECT_TRUE(Keys(origin1_));
+  std::vector<std::string> expected_keys;
+  expected_keys.push_back(cache1_);
+  expected_keys.push_back(cache2_);
+  EXPECT_EQ(expected_keys, callback_strings_);
+}
+
 class ServiceWorkerCacheQuotaClientTest
     : public ServiceWorkerCacheStorageManagerTest {
  protected:
diff --git a/content/browser/service_worker/service_worker_context_request_handler_unittest.cc b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc
index c5efe010..d7ea3102 100644
--- a/content/browser/service_worker/service_worker_context_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc
@@ -35,7 +35,8 @@
       : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
 
   void SetUp() override {
-    helper_.reset(new EmbeddedWorkerTestHelper(kMockRenderProcessId));
+    helper_.reset(
+        new EmbeddedWorkerTestHelper(base::FilePath(), kMockRenderProcessId));
 
     // A new unstored registration/version.
     scope_ = GURL("http://host/scope/");
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc
index d6c9e4a..60ef24d 100644
--- a/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -79,7 +79,7 @@
 class RejectInstallTestHelper : public EmbeddedWorkerTestHelper {
  public:
   explicit RejectInstallTestHelper(int mock_render_process_id)
-      : EmbeddedWorkerTestHelper(mock_render_process_id) {}
+      : EmbeddedWorkerTestHelper(base::FilePath(), mock_render_process_id) {}
 
   void OnInstallEvent(int embedded_worker_id,
                       int request_id) override {
@@ -93,7 +93,7 @@
 class RejectActivateTestHelper : public EmbeddedWorkerTestHelper {
  public:
   explicit RejectActivateTestHelper(int mock_render_process_id)
-      : EmbeddedWorkerTestHelper(mock_render_process_id) {}
+      : EmbeddedWorkerTestHelper(base::FilePath(), mock_render_process_id) {}
 
   void OnActivateEvent(int embedded_worker_id, int request_id) override {
     SimulateSend(
@@ -125,7 +125,8 @@
         render_process_id_(99) {}
 
   void SetUp() override {
-    helper_.reset(new EmbeddedWorkerTestHelper(render_process_id_));
+    helper_.reset(
+        new EmbeddedWorkerTestHelper(base::FilePath(), render_process_id_));
     helper_->context_wrapper()->AddObserver(this);
   }
 
@@ -519,11 +520,17 @@
   EXPECT_EQ(old_registration_id, notifications_[1].registration_id);
 }
 
-// TODO(nhiroki): Test this for on-disk storage.
 TEST_F(ServiceWorkerContextTest, DeleteAndStartOver) {
   GURL pattern("http://www.example.com/");
   GURL script_url("http://www.example.com/service_worker.js");
 
+  // Reinitialize the helper to test on-disk storage.
+  base::ScopedTempDir user_data_directory;
+  ASSERT_TRUE(user_data_directory.CreateUniqueTempDir());
+  helper_.reset(new EmbeddedWorkerTestHelper(user_data_directory.path(),
+                                             render_process_id_));
+  helper_->context_wrapper()->AddObserver(this);
+
   int64 registration_id = kInvalidServiceWorkerRegistrationId;
   bool called = false;
   context()->RegisterServiceWorker(
diff --git a/content/browser/service_worker/service_worker_context_watcher.cc b/content/browser/service_worker/service_worker_context_watcher.cc
index 45b0065c6..13e1228 100644
--- a/content/browser/service_worker/service_worker_context_watcher.cc
+++ b/content/browser/service_worker/service_worker_context_watcher.cc
@@ -9,6 +9,7 @@
 #include "content/browser/service_worker/service_worker_context_observer.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/service_worker/service_worker_types.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
 
@@ -26,10 +27,12 @@
 ServiceWorkerContextWatcher::ServiceWorkerContextWatcher(
     scoped_refptr<ServiceWorkerContextWrapper> context,
     const WorkerRegistrationUpdatedCallback& registration_callback,
-    const WorkerVersionUpdatedCallback& version_callback)
+    const WorkerVersionUpdatedCallback& version_callback,
+    const WorkerErrorReportedCallback& error_callback)
     : context_(context),
       registration_callback_(registration_callback),
-      version_callback_(version_callback) {
+      version_callback_(version_callback),
+      error_callback_(error_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
@@ -195,6 +198,18 @@
     version_info_map_.erase(version_id);
 }
 
+void ServiceWorkerContextWatcher::OnErrorReported(int64 version_id,
+                                                  int process_id,
+                                                  int thread_id,
+                                                  const ErrorInfo& info) {
+  int64 registration_id = kInvalidServiceWorkerRegistrationId;
+  if (ServiceWorkerVersionInfo* version = version_info_map_.get(version_id))
+    registration_id = version->registration_id;
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(error_callback_, registration_id, version_id, info));
+}
+
 void ServiceWorkerContextWatcher::OnRegistrationStored(int64 registration_id,
                                                        const GURL& pattern) {
   SendRegistrationInfo(registration_id, pattern,
diff --git a/content/browser/service_worker/service_worker_context_watcher.h b/content/browser/service_worker/service_worker_context_watcher.h
index c027282..f9c8aff0 100644
--- a/content/browser/service_worker/service_worker_context_watcher.h
+++ b/content/browser/service_worker/service_worker_context_watcher.h
@@ -27,10 +27,15 @@
       WorkerRegistrationUpdatedCallback;
   typedef base::Callback<void(const std::vector<ServiceWorkerVersionInfo>&)>
       WorkerVersionUpdatedCallback;
+  typedef base::Callback<void(int64 /* registration_id */,
+                              int64 /* version_id */,
+                              const ErrorInfo&)> WorkerErrorReportedCallback;
+
   ServiceWorkerContextWatcher(
       scoped_refptr<ServiceWorkerContextWrapper> context,
       const WorkerRegistrationUpdatedCallback& registration_callback,
-      const WorkerVersionUpdatedCallback& version_callback);
+      const WorkerVersionUpdatedCallback& version_callback,
+      const WorkerErrorReportedCallback& error_callback);
   void Start();
   void Stop();
 
@@ -66,6 +71,10 @@
   void OnVersionStateChanged(
       int64 version_id,
       content::ServiceWorkerVersion::Status status) override;
+  void OnErrorReported(int64 version_id,
+                       int process_id,
+                       int thread_id,
+                       const ErrorInfo& info) override;
   void OnRegistrationStored(int64 registration_id,
                             const GURL& pattern) override;
   void OnRegistrationDeleted(int64 registration_id,
@@ -75,6 +84,7 @@
   scoped_refptr<ServiceWorkerContextWrapper> context_;
   WorkerRegistrationUpdatedCallback registration_callback_;
   WorkerVersionUpdatedCallback version_callback_;
+  WorkerErrorReportedCallback error_callback_;
 };
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index a0baf52..72a1bba4 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -98,6 +98,7 @@
   bool is_incognito() const { return is_incognito_; }
 
  private:
+  friend class BackgroundSyncManagerTest;
   friend class base::RefCountedThreadSafe<ServiceWorkerContextWrapper>;
   friend class EmbeddedWorkerTestHelper;
   friend class ServiceWorkerProcessManager;
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
index 38950015..c159baaa 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
@@ -43,7 +43,8 @@
       : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
 
   void SetUp() override {
-    helper_.reset(new EmbeddedWorkerTestHelper(kMockRenderProcessId));
+    helper_.reset(
+        new EmbeddedWorkerTestHelper(base::FilePath(), kMockRenderProcessId));
 
     // A new unstored registration/version.
     scope_ = GURL("http://host/scope/");
diff --git a/content/browser/service_worker/service_worker_database.cc b/content/browser/service_worker/service_worker_database.cc
index 2d0e2f2..5cd3473e 100644
--- a/content/browser/service_worker/service_worker_database.cc
+++ b/content/browser/service_worker/service_worker_database.cc
@@ -16,6 +16,7 @@
 #include "content/browser/service_worker/service_worker_database.pb.h"
 #include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/common/service_worker/service_worker_types.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/env.h"
@@ -969,8 +970,10 @@
     }
   }
 
-  return LevelDBStatusToStatus(
-      leveldb::DestroyDB(path_.AsUTF8Unsafe(), options));
+  Status status =
+      LevelDBStatusToStatus(leveldb::DestroyDB(path_.AsUTF8Unsafe(), options));
+  ServiceWorkerMetrics::RecordDestroyDatabaseResult(status);
+  return status;
 }
 
 ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
@@ -997,6 +1000,7 @@
 
   leveldb::Options options;
   options.create_if_missing = create_if_missing;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   if (use_in_memory_db) {
     env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
     options.env = env_.get();
diff --git a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
index c761f55..cd3a49e 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
@@ -69,7 +69,8 @@
       : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
 
   void SetUp() override {
-    helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId));
+    helper_.reset(
+        new EmbeddedWorkerTestHelper(base::FilePath(), kRenderProcessId));
     dispatcher_host_ = new TestingServiceWorkerDispatcherHost(
         kRenderProcessId, context_wrapper(), &resource_context_, helper_.get());
   }
diff --git a/content/browser/service_worker/service_worker_handle_unittest.cc b/content/browser/service_worker/service_worker_handle_unittest.cc
index 57144f7..d364fe1 100644
--- a/content/browser/service_worker/service_worker_handle_unittest.cc
+++ b/content/browser/service_worker/service_worker_handle_unittest.cc
@@ -71,7 +71,8 @@
       : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
 
   void SetUp() override {
-    helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId));
+    helper_.reset(
+        new EmbeddedWorkerTestHelper(base::FilePath(), kRenderProcessId));
 
     dispatcher_host_ = new TestingServiceWorkerDispatcherHost(
         kRenderProcessId, helper_->context_wrapper(),
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index 5e54e07..118b234 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -106,7 +106,8 @@
         render_process_id_(kMockRenderProcessId) {}
 
   void SetUp() override {
-    helper_.reset(new EmbeddedWorkerTestHelper(render_process_id_));
+    helper_.reset(
+        new EmbeddedWorkerTestHelper(base::FilePath(), render_process_id_));
   }
 
   void TearDown() override { helper_.reset(); }
@@ -359,7 +360,7 @@
 class FailToStartWorkerTestHelper : public EmbeddedWorkerTestHelper {
  public:
   explicit FailToStartWorkerTestHelper(int mock_render_process_id)
-      : EmbeddedWorkerTestHelper(mock_render_process_id) {}
+      : EmbeddedWorkerTestHelper(base::FilePath(), mock_render_process_id) {}
 
   void OnStartWorker(int embedded_worker_id,
                      int64 service_worker_version_id,
@@ -783,7 +784,7 @@
   };
 
   UpdateJobTestHelper(int mock_render_process_id)
-      : EmbeddedWorkerTestHelper(mock_render_process_id),
+      : EmbeddedWorkerTestHelper(base::FilePath(), mock_render_process_id),
         update_found_(false) {}
   ~UpdateJobTestHelper() override {
     if (registration_.get())
@@ -1257,7 +1258,7 @@
 class EventCallbackHelper : public EmbeddedWorkerTestHelper {
  public:
   explicit EventCallbackHelper(int mock_render_process_id)
-      : EmbeddedWorkerTestHelper(mock_render_process_id),
+      : EmbeddedWorkerTestHelper(base::FilePath(), mock_render_process_id),
         install_event_result_(blink::WebServiceWorkerEventResultCompleted),
         activate_event_result_(blink::WebServiceWorkerEventResultCompleted) {}
 
diff --git a/content/browser/service_worker/service_worker_metrics.cc b/content/browser/service_worker/service_worker_metrics.cc
index 30cd114..6d685e5 100644
--- a/content/browser/service_worker/service_worker_metrics.cc
+++ b/content/browser/service_worker/service_worker_metrics.cc
@@ -23,47 +23,52 @@
 
 }  // namespace
 
-// static
 void ServiceWorkerMetrics::CountInitDiskCacheResult(bool result) {
   UMA_HISTOGRAM_BOOLEAN("ServiceWorker.DiskCache.InitResult", result);
 }
 
-// static
 void ServiceWorkerMetrics::CountReadResponseResult(
     ServiceWorkerMetrics::ReadResponseResult result) {
   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.DiskCache.ReadResponseResult",
                             result, NUM_READ_RESPONSE_RESULT_TYPES);
 }
 
-// static
 void ServiceWorkerMetrics::CountWriteResponseResult(
     ServiceWorkerMetrics::WriteResponseResult result) {
   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.DiskCache.WriteResponseResult",
                             result, NUM_WRITE_RESPONSE_RESULT_TYPES);
 }
 
-// static
 void ServiceWorkerMetrics::CountOpenDatabaseResult(
     ServiceWorkerDatabase::Status status) {
   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.Database.OpenResult",
                             status, ServiceWorkerDatabase::STATUS_ERROR_MAX);
 }
 
-// static
 void ServiceWorkerMetrics::CountReadDatabaseResult(
     ServiceWorkerDatabase::Status status) {
   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.Database.ReadResult",
                             status, ServiceWorkerDatabase::STATUS_ERROR_MAX);
 }
 
-// static
 void ServiceWorkerMetrics::CountWriteDatabaseResult(
     ServiceWorkerDatabase::Status status) {
   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.Database.WriteResult",
                             status, ServiceWorkerDatabase::STATUS_ERROR_MAX);
 }
 
-// static
+void ServiceWorkerMetrics::RecordDestroyDatabaseResult(
+    ServiceWorkerDatabase::Status status) {
+  UMA_HISTOGRAM_ENUMERATION("ServiceWorker.Database.DestroyDatabase", status,
+                            ServiceWorkerDatabase::STATUS_ERROR_MAX);
+}
+
+void ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
+    DeleteAndStartOverResult result) {
+  UMA_HISTOGRAM_ENUMERATION("ServiceWorker.Storage.DeleteAndStartOverResult",
+                            result, NUM_DELETE_AND_START_OVER_RESULT_TYPES);
+}
+
 void ServiceWorkerMetrics::CountControlledPageLoad(const GURL& url) {
   RecordAction(base::UserMetricsAction("ServiceWorker.ControlledPageLoad"));
   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
diff --git a/content/browser/service_worker/service_worker_metrics.h b/content/browser/service_worker/service_worker_metrics.h
index cf2c700..8b058567 100644
--- a/content/browser/service_worker/service_worker_metrics.h
+++ b/content/browser/service_worker/service_worker_metrics.h
@@ -28,6 +28,13 @@
     NUM_WRITE_RESPONSE_RESULT_TYPES,
   };
 
+  enum DeleteAndStartOverResult {
+    DELETE_OK,
+    DELETE_DATABASE_ERROR,
+    DELETE_DISK_CACHE_ERROR,
+    NUM_DELETE_AND_START_OVER_RESULT_TYPES,
+  };
+
   // Used for ServiceWorkerDiskCache.
   static void CountInitDiskCacheResult(bool result);
   static void CountReadResponseResult(ReadResponseResult result);
@@ -37,6 +44,10 @@
   static void CountOpenDatabaseResult(ServiceWorkerDatabase::Status status);
   static void CountReadDatabaseResult(ServiceWorkerDatabase::Status status);
   static void CountWriteDatabaseResult(ServiceWorkerDatabase::Status status);
+  static void RecordDestroyDatabaseResult(ServiceWorkerDatabase::Status status);
+
+  // Used for ServiceWorkerStorage.
+  static void RecordDeleteAndStartOverResult(DeleteAndStartOverResult result);
 
   // Counts the number of page loads controlled by a Service Worker.
   static void CountControlledPageLoad(const GURL& url);
diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc
index 0da68417..1ef3d78 100644
--- a/content/browser/service_worker/service_worker_provider_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -25,7 +25,8 @@
   ~ServiceWorkerProviderHostTest() override {}
 
   void SetUp() override {
-    helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId));
+    helper_.reset(
+        new EmbeddedWorkerTestHelper(base::FilePath(), kRenderProcessId));
     context_ = helper_->context();
     script_url_ = GURL("http://www.example.com/service_worker.js");
     registration1_ = new ServiceWorkerRegistration(
diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc
index d765c3c5..4f6620a 100644
--- a/content/browser/service_worker/service_worker_register_job.cc
+++ b/content/browser/service_worker/service_worker_register_job.cc
@@ -458,6 +458,8 @@
       if (should_uninstall_on_failure_)
         registration()->ClearWhenReady();
       if (new_version()) {
+        if (status != SERVICE_WORKER_ERROR_EXISTS)
+          new_version()->ReportError(status, status_message);
         registration()->UnsetVersion(new_version());
         new_version()->Doom();
       }
diff --git a/content/browser/service_worker/service_worker_registration.cc b/content/browser/service_worker/service_worker_registration.cc
index 09e5836..eef79b4 100644
--- a/content/browser/service_worker/service_worker_registration.cc
+++ b/content/browser/service_worker/service_worker_registration.cc
@@ -162,14 +162,21 @@
     ActivateWaitingVersion();
 }
 
-void ServiceWorkerRegistration::ClaimClients(const StatusCallback& callback) {
+void ServiceWorkerRegistration::ClaimClients() {
   DCHECK(context_);
   DCHECK(active_version());
-  // TODO(xiang): Should better not hit the database http://crbug.com/454250.
-  context_->storage()->GetRegistrationsForOrigin(
-      pattern_.GetOrigin(),
-      base::Bind(&ServiceWorkerRegistration::DidGetRegistrationsForClaimClients,
-                 this, callback, active_version_));
+
+  for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
+           context_->GetProviderHostIterator();
+       !it->IsAtEnd(); it->Advance()) {
+    ServiceWorkerProviderHost* host = it->GetProviderHost();
+    if (host->IsHostToRunningServiceWorker())
+      continue;
+    if (host->controlling_version() == active_version())
+      continue;
+    if (host->MatchRegistration() == this)
+      host->ClaimedByRegistration(this);
+  }
 }
 
 void ServiceWorkerRegistration::ClearWhenReady() {
@@ -375,50 +382,4 @@
   callback.Run(status);
 }
 
-void ServiceWorkerRegistration::DidGetRegistrationsForClaimClients(
-    const StatusCallback& callback,
-    scoped_refptr<ServiceWorkerVersion> version,
-    const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
-  if (!context_) {
-    callback.Run(SERVICE_WORKER_ERROR_ABORT);
-    return;
-  }
-  if (!active_version() || version != active_version()) {
-    callback.Run(SERVICE_WORKER_ERROR_STATE);
-    return;
-  }
-
-  for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
-           context_->GetProviderHostIterator();
-       !it->IsAtEnd(); it->Advance()) {
-    ServiceWorkerProviderHost* host = it->GetProviderHost();
-    if (ShouldClaim(host, registrations))
-      host->ClaimedByRegistration(this);
-  }
-  callback.Run(SERVICE_WORKER_OK);
-}
-
-bool ServiceWorkerRegistration::ShouldClaim(
-    ServiceWorkerProviderHost* provider_host,
-    const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
-  if (provider_host->IsHostToRunningServiceWorker())
-    return false;
-  if (provider_host->controlling_version() == active_version())
-    return false;
-
-  LongestScopeMatcher matcher(provider_host->document_url());
-  if (!matcher.MatchLongest(pattern_))
-    return false;
-  for (const ServiceWorkerRegistrationInfo& info : registrations) {
-    ServiceWorkerRegistration* registration =
-        context_->GetLiveRegistration(info.registration_id);
-    if (registration &&
-        (registration->is_uninstalling() || registration->is_uninstalled()))
-      continue;
-    if (matcher.MatchLongest(info.pattern))
-      return false;
-  }
-  return true;
-}
-
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_registration.h b/content/browser/service_worker/service_worker_registration.h
index 3cd3864d..69e0720e 100644
--- a/content/browser/service_worker/service_worker_registration.h
+++ b/content/browser/service_worker/service_worker_registration.h
@@ -111,7 +111,7 @@
 
   // Takes over control of provider hosts which are currently not controlled or
   // controlled by other registrations.
-  void ClaimClients(const StatusCallback& callback);
+  void ClaimClients();
 
   // Triggers the [[ClearRegistration]] algorithm when the currently
   // active version has no controllees. Deletes this registration
@@ -167,14 +167,6 @@
                          scoped_refptr<ServiceWorkerVersion> version,
                          ServiceWorkerStatusCode status);
 
-  void DidGetRegistrationsForClaimClients(
-      const StatusCallback& callback,
-      scoped_refptr<ServiceWorkerVersion> version,
-      const std::vector<ServiceWorkerRegistrationInfo>& registrations);
-  bool ShouldClaim(
-      ServiceWorkerProviderHost* provider_host,
-      const std::vector<ServiceWorkerRegistrationInfo>& registration_infos);
-
   const GURL pattern_;
   const int64 registration_id_;
   bool is_deleted_;
diff --git a/content/browser/service_worker/service_worker_request_handler_unittest.cc b/content/browser/service_worker/service_worker_request_handler_unittest.cc
index 2d17400..24585145 100644
--- a/content/browser/service_worker/service_worker_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_request_handler_unittest.cc
@@ -38,7 +38,8 @@
       : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
 
   void SetUp() override {
-    helper_.reset(new EmbeddedWorkerTestHelper(kMockRenderProcessId));
+    helper_.reset(
+        new EmbeddedWorkerTestHelper(base::FilePath(), kMockRenderProcessId));
 
     // A new unstored registration/version.
     registration_ = new ServiceWorkerRegistration(
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc
index 3b37e233..0656833 100644
--- a/content/browser/service_worker/service_worker_storage.cc
+++ b/content/browser/service_worker/service_worker_storage.cc
@@ -773,13 +773,17 @@
     const std::string& key,
     const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
         callback) {
-  DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
-  if (IsDisabled() || !context_) {
-    RunSoon(FROM_HERE,
-            base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
-                       SERVICE_WORKER_ERROR_FAILED));
+  if (!LazyInitialize(
+          base::Bind(&ServiceWorkerStorage::GetUserDataForAllRegistrations,
+                     weak_factory_.GetWeakPtr(), key, callback))) {
+    if (state_ != INITIALIZING || !context_) {
+      RunSoon(FROM_HERE,
+              base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
+                         SERVICE_WORKER_ERROR_FAILED));
+    }
     return;
   }
+  DCHECK_EQ(INITIALIZED, state_);
 
   if (key.empty()) {
     RunSoon(FROM_HERE,
@@ -1756,6 +1760,8 @@
     // Give up the corruption recovery until the browser restarts.
     LOG(ERROR) << "Failed to delete the database: "
                << ServiceWorkerDatabase::StatusToString(status);
+    ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
+        ServiceWorkerMetrics::DELETE_DATABASE_ERROR);
     callback.Run(DatabaseStatusToStatusCode(status));
     return;
   }
@@ -1780,10 +1786,14 @@
   if (!result) {
     // Give up the corruption recovery until the browser restarts.
     LOG(ERROR) << "Failed to delete the diskcache.";
+    ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
+        ServiceWorkerMetrics::DELETE_DISK_CACHE_ERROR);
     callback.Run(SERVICE_WORKER_ERROR_FAILED);
     return;
   }
   DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
+  ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
+      ServiceWorkerMetrics::DELETE_OK);
   callback.Run(SERVICE_WORKER_OK);
 }
 
diff --git a/content/browser/service_worker/service_worker_storage.h b/content/browser/service_worker/service_worker_storage.h
index d12b39c..1bfa737 100644
--- a/content/browser/service_worker/service_worker_storage.h
+++ b/content/browser/service_worker/service_worker_storage.h
@@ -294,6 +294,8 @@
   base::FilePath GetDatabasePath();
   base::FilePath GetDiskCachePath();
 
+  // Loads the registration data from backend storage. This must be called
+  // before any method that requires registration data.
   bool LazyInitialize(
       const base::Closure& callback);
   void DidReadInitialData(
diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
index cbf48a9..0321a1d 100644
--- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -113,7 +113,7 @@
   void SetUp() override {
     browser_context_.reset(new TestBrowserContext);
     InitializeResourceContext(browser_context_.get());
-    SetUpWithHelper(new EmbeddedWorkerTestHelper(kProcessID));
+    SetUpWithHelper(new EmbeddedWorkerTestHelper(base::FilePath(), kProcessID));
   }
 
   void SetUpWithHelper(EmbeddedWorkerTestHelper* helper) {
@@ -243,7 +243,7 @@
   BlobResponder(int mock_render_process_id,
                 const std::string& blob_uuid,
                 uint64 blob_size)
-      : EmbeddedWorkerTestHelper(mock_render_process_id),
+      : EmbeddedWorkerTestHelper(base::FilePath(), mock_render_process_id),
         blob_uuid_(blob_uuid),
         blob_size_(blob_size) {}
   ~BlobResponder() override {}
@@ -300,9 +300,8 @@
 // Responds to fetch events with a stream.
 class StreamResponder : public EmbeddedWorkerTestHelper {
  public:
-  StreamResponder(int mock_render_process_id,
-                  const GURL& stream_url)
-      : EmbeddedWorkerTestHelper(mock_render_process_id),
+  StreamResponder(int mock_render_process_id, const GURL& stream_url)
+      : EmbeddedWorkerTestHelper(base::FilePath(), mock_render_process_id),
         stream_url_(stream_url) {}
   ~StreamResponder() override {}
 
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 396659c..98590e0 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -92,6 +92,19 @@
   callbacks->Clear();
 }
 
+template <typename CallbackType, typename... Params>
+bool RunIDMapCallback(IDMap<CallbackType, IDMapOwnPointer>* callbacks,
+                      int request_id,
+                      const Params&... params) {
+  CallbackType* callback = callbacks->Lookup(request_id);
+  if (!callback)
+    return false;
+
+  callback->Run(params...);
+  callbacks->Remove(request_id);
+  return true;
+}
+
 void RunStartWorkerCallback(
     const StatusCallback& callback,
     scoped_refptr<ServiceWorkerRegistration> protect,
@@ -142,7 +155,7 @@
 void RunErrorCrossOriginConnectCallback(
     const ServiceWorkerVersion::CrossOriginConnectCallback& callback,
     ServiceWorkerStatusCode status) {
-  callback.Run(status, false);
+  callback.Run(status, false /* accept_connection */);
 }
 
 using WindowOpenedCallback = base::Callback<void(int, int)>;
@@ -304,6 +317,7 @@
 }  // namespace
 
 const int ServiceWorkerVersion::kStartWorkerTimeoutMinutes = 5;
+const int ServiceWorkerVersion::kRequestTimeoutMinutes = 5;
 
 ServiceWorkerVersion::ServiceWorkerVersion(
     ServiceWorkerRegistration* registration,
@@ -536,7 +550,7 @@
 
   prepare_callback.Run();
 
-  int request_id = fetch_callbacks_.Add(new FetchCallback(fetch_callback));
+  int request_id = AddRequest(fetch_callback, &fetch_callbacks_, REQUEST_FETCH);
   ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
       ServiceWorkerMsg_FetchEvent(request_id, request));
   if (status != SERVICE_WORKER_OK) {
@@ -566,7 +580,7 @@
     return;
   }
 
-  int request_id = sync_callbacks_.Add(new StatusCallback(callback));
+  int request_id = AddRequest(callback, &sync_callbacks_, REQUEST_SYNC);
   ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
       ServiceWorkerMsg_SyncEvent(request_id));
   if (status != SERVICE_WORKER_OK) {
@@ -591,8 +605,8 @@
     return;
   }
 
-  int request_id =
-      notification_click_callbacks_.Add(new StatusCallback(callback));
+  int request_id = AddRequest(callback, &notification_click_callbacks_,
+                              REQUEST_NOTIFICATION_CLICK);
   ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
       ServiceWorkerMsg_NotificationClickEvent(request_id,
                                               notification_id,
@@ -616,7 +630,7 @@
     return;
   }
 
-  int request_id = push_callbacks_.Add(new StatusCallback(callback));
+  int request_id = AddRequest(callback, &push_callbacks_, REQUEST_PUSH);
   ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
       ServiceWorkerMsg_PushEvent(request_id, data));
   if (status != SERVICE_WORKER_OK) {
@@ -652,7 +666,8 @@
     return;
   }
 
-  int request_id = geofencing_callbacks_.Add(new StatusCallback(callback));
+  int request_id =
+      AddRequest(callback, &geofencing_callbacks_, REQUEST_GEOFENCING);
   ServiceWorkerStatusCode status =
       embedded_worker_->SendMessage(ServiceWorkerMsg_GeofencingEvent(
           request_id, event_type, region_id, region));
@@ -683,8 +698,8 @@
     return;
   }
 
-  int request_id = cross_origin_connect_callbacks_.Add(
-      new CrossOriginConnectCallback(callback));
+  int request_id = AddRequest(callback, &cross_origin_connect_callbacks_,
+                              REQUEST_CROSS_ORIGIN_CONNECT);
   ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
       ServiceWorkerMsg_CrossOriginConnectEvent(request_id, client));
   if (status != SERVICE_WORKER_OK) {
@@ -769,6 +784,16 @@
   listeners_.RemoveObserver(listener);
 }
 
+void ServiceWorkerVersion::ReportError(ServiceWorkerStatusCode status,
+                                       const std::string& status_message) {
+  if (status_message.empty()) {
+    OnReportException(base::UTF8ToUTF16(ServiceWorkerStatusToString(status)),
+                      -1, -1, GURL());
+  } else {
+    OnReportException(base::UTF8ToUTF16(status_message), -1, -1, GURL());
+  }
+}
+
 void ServiceWorkerVersion::Doom() {
   if (is_doomed_)
     return;
@@ -780,9 +805,16 @@
 void ServiceWorkerVersion::SetDevToolsAttached(bool attached) {
   embedded_worker()->set_devtools_attached(attached);
   if (attached) {
+    // TODO(falken): Canceling the timeouts when debugging could cause
+    // heisenbugs; we should instead run them as normal show an educational
+    // message in DevTools when they occur. crbug.com/470419
+
     // Don't record the startup time metric once DevTools is attached.
     ClearTick(&start_time_);
     skip_recording_startup_time_ = true;
+
+    // Cancel request timeouts.
+    SetAllRequestTimes(base::TimeTicks());
     return;
   }
   if (!start_callbacks_.empty()) {
@@ -792,6 +824,9 @@
         << running_status();
     RestartTick(&start_time_);
   }
+
+  // Reactivate request timeouts.
+  SetAllRequestTimes(base::TimeTicks::Now());
 }
 
 void ServiceWorkerVersion::SetMainScriptHttpResponseInfo(
@@ -804,6 +839,13 @@
   return main_script_http_info_.get();
 }
 
+ServiceWorkerVersion::RequestInfo::RequestInfo(int id, RequestType type)
+    : id(id), type(type), time(base::TimeTicks::Now()) {
+}
+
+ServiceWorkerVersion::RequestInfo::~RequestInfo() {
+}
+
 void ServiceWorkerVersion::OnScriptLoaded() {
   DCHECK_EQ(STARTING, running_status());
   // Activate ping/pong now that JavaScript execution will start.
@@ -960,7 +1002,7 @@
   DCHECK_EQ(RUNNING, running_status())
       << "Worker stopped too soon after it was started.";
 
-  int request_id = install_callbacks_.Add(new StatusCallback(callback));
+  int request_id = AddRequest(callback, &install_callbacks_, REQUEST_INSTALL);
   ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
       ServiceWorkerMsg_InstallEvent(request_id));
   if (status != SERVICE_WORKER_OK) {
@@ -974,7 +1016,7 @@
   DCHECK_EQ(RUNNING, running_status())
       << "Worker stopped too soon after it was started.";
 
-  int request_id = activate_callbacks_.Add(new StatusCallback(callback));
+  int request_id = AddRequest(callback, &activate_callbacks_, REQUEST_ACTIVATE);
   ServiceWorkerStatusCode status =
       embedded_worker_->SendMessage(ServiceWorkerMsg_ActivateEvent(request_id));
   if (status != SERVICE_WORKER_OK) {
@@ -1396,24 +1438,25 @@
 }
 
 void ServiceWorkerVersion::OnClaimClients(int request_id) {
-  StatusCallback callback = base::Bind(&ServiceWorkerVersion::DidClaimClients,
-                                       weak_factory_.GetWeakPtr(), request_id);
   if (status_ != ACTIVATING && status_ != ACTIVATED) {
-    callback.Run(SERVICE_WORKER_ERROR_STATE);
+    embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
+        request_id, blink::WebServiceWorkerError::ErrorTypeState,
+        base::ASCIIToUTF16(kClaimClientsStateErrorMesage)));
     return;
   }
-  if (!context_) {
-    callback.Run(SERVICE_WORKER_ERROR_ABORT);
-    return;
+  if (context_) {
+    if (ServiceWorkerRegistration* registration =
+            context_->GetLiveRegistration(registration_id_)) {
+      registration->ClaimClients();
+      embedded_worker_->SendMessage(
+          ServiceWorkerMsg_DidClaimClients(request_id));
+      return;
+    }
   }
 
-  ServiceWorkerRegistration* registration =
-      context_->GetLiveRegistration(registration_id_);
-  if (!registration) {
-    callback.Run(SERVICE_WORKER_ERROR_ABORT);
-    return;
-  }
-  registration->ClaimClients(callback);
+  embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
+      request_id, blink::WebServiceWorkerError::ErrorTypeAbort,
+      base::ASCIIToUTF16(kClaimClientsShutdownErrorMesage)));
 }
 
 void ServiceWorkerVersion::OnPongFromWorker() {
@@ -1462,24 +1505,6 @@
   }
 }
 
-void ServiceWorkerVersion::DidClaimClients(
-    int request_id, ServiceWorkerStatusCode status) {
-  if (status == SERVICE_WORKER_ERROR_STATE) {
-    embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
-        request_id, blink::WebServiceWorkerError::ErrorTypeState,
-        base::ASCIIToUTF16(kClaimClientsStateErrorMesage)));
-    return;
-  }
-  if (status == SERVICE_WORKER_ERROR_ABORT) {
-    embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
-        request_id, blink::WebServiceWorkerError::ErrorTypeAbort,
-        base::ASCIIToUTF16(kClaimClientsShutdownErrorMesage)));
-    return;
-  }
-  DCHECK(status == SERVICE_WORKER_OK);
-  embedded_worker_->SendMessage(ServiceWorkerMsg_DidClaimClients(request_id));
-}
-
 void ServiceWorkerVersion::DidGetClients(
     int request_id,
     const std::vector<ServiceWorkerClientInfo>& clients) {
@@ -1533,9 +1558,22 @@
     return;
   }
 
-  // This check occurs after the start_time_ timeout check, since in that case
-  // the start callbacks should fail with ERROR_TIMEOUT. In the other timeout
-  // checks, there's nothing more to do as the worker is already stopping.
+  // Requests have not finished within a certain period.
+  bool request_timed_out = false;
+  while (!requests_.empty()) {
+    RequestInfo info = requests_.front();
+    if (GetTickDuration(info.time) <
+        base::TimeDelta::FromMinutes(kRequestTimeoutMinutes))
+      break;
+    if (OnRequestTimeout(info))
+      request_timed_out = true;
+    requests_.pop();
+  }
+  if (request_timed_out && running_status() != STOPPING)
+    embedded_worker_->Stop();
+
+  // For the timeouts below, there are no callbacks to timeout so there is
+  // nothing more to do if the worker is already stopping.
   if (running_status() == STOPPING)
     return;
 
@@ -1665,6 +1703,7 @@
 void ServiceWorkerVersion::RemoveCallbackAndStopIfDoomed(
     IDMAP* callbacks,
     int request_id) {
+  RestartTick(&idle_time_);
   callbacks->Remove(request_id);
   if (is_doomed_) {
     // The stop should be already scheduled, but try to stop immediately, in
@@ -1673,4 +1712,59 @@
   }
 }
 
+template <typename CallbackType>
+int ServiceWorkerVersion::AddRequest(
+    const CallbackType& callback,
+    IDMap<CallbackType, IDMapOwnPointer>* callback_map,
+    RequestType request_type) {
+  int request_id = callback_map->Add(new CallbackType(callback));
+  requests_.push(RequestInfo(request_id, request_type));
+  return request_id;
+}
+
+bool ServiceWorkerVersion::OnRequestTimeout(const RequestInfo& info) {
+  switch (info.type) {
+    case REQUEST_ACTIVATE:
+      return RunIDMapCallback(&activate_callbacks_, info.id,
+                              SERVICE_WORKER_ERROR_TIMEOUT);
+    case REQUEST_INSTALL:
+      return RunIDMapCallback(&install_callbacks_, info.id,
+                              SERVICE_WORKER_ERROR_TIMEOUT);
+    case REQUEST_FETCH:
+      return RunIDMapCallback(
+          &fetch_callbacks_, info.id, SERVICE_WORKER_ERROR_TIMEOUT,
+          /* The other args are ignored for non-OK status. */
+          SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK, ServiceWorkerResponse());
+    case REQUEST_SYNC:
+      return RunIDMapCallback(&sync_callbacks_, info.id,
+                              SERVICE_WORKER_ERROR_TIMEOUT);
+    case REQUEST_NOTIFICATION_CLICK:
+      return RunIDMapCallback(&notification_click_callbacks_, info.id,
+                              SERVICE_WORKER_ERROR_TIMEOUT);
+    case REQUEST_PUSH:
+      return RunIDMapCallback(&push_callbacks_, info.id,
+                              SERVICE_WORKER_ERROR_TIMEOUT);
+    case REQUEST_GEOFENCING:
+      return RunIDMapCallback(&geofencing_callbacks_, info.id,
+                              SERVICE_WORKER_ERROR_TIMEOUT);
+    case REQUEST_CROSS_ORIGIN_CONNECT:
+      return RunIDMapCallback(&cross_origin_connect_callbacks_, info.id,
+                              SERVICE_WORKER_ERROR_TIMEOUT,
+                              false /* accept_connection */);
+  }
+  NOTREACHED() << "Got unexpected request type: " << info.type;
+  return false;
+}
+
+void ServiceWorkerVersion::SetAllRequestTimes(const base::TimeTicks& ticks) {
+  std::queue<RequestInfo> new_requests;
+  while (!requests_.empty()) {
+    RequestInfo info = requests_.front();
+    info.time = ticks;
+    new_requests.push(info);
+    requests_.pop();
+  }
+  requests_ = new_requests;
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index 917ddcc..f022be9 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -6,6 +6,7 @@
 #define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_VERSION_H_
 
 #include <map>
+#include <queue>
 #include <set>
 #include <string>
 #include <vector>
@@ -67,7 +68,8 @@
   typedef base::Callback<void(ServiceWorkerStatusCode,
                               ServiceWorkerFetchEventResult,
                               const ServiceWorkerResponse&)> FetchCallback;
-  typedef base::Callback<void(ServiceWorkerStatusCode, bool)>
+  typedef base::Callback<void(ServiceWorkerStatusCode,
+                              bool /* accept_connction */)>
       CrossOriginConnectCallback;
 
   enum RunningStatus {
@@ -278,6 +280,10 @@
   ServiceWorkerScriptCacheMap* script_cache_map() { return &script_cache_map_; }
   EmbeddedWorkerInstance* embedded_worker() { return embedded_worker_.get(); }
 
+  // Reports the error message to |listeners_|.
+  void ReportError(ServiceWorkerStatusCode status,
+                   const std::string& status_message);
+
   // Dooms this version to have REDUNDANT status and its resources deleted.  If
   // the version is controlling a page, these changes will happen when the
   // version no longer controls any pages.
@@ -300,10 +306,11 @@
   friend class ServiceWorkerURLRequestJobTest;
   FRIEND_TEST_ALL_PREFIXES(ServiceWorkerControlleeRequestHandlerTest,
                            ActivateWaitingVersion);
-  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, ScheduleStopWorker);
+  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, IdleTimeout);
   FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, KeepAlive);
   FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, ListenerAvailability);
   FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, SetDevToolsAttached);
+  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerWaitForeverInFetchTest, RequestTimeout);
   FRIEND_TEST_ALL_PREFIXES(ServiceWorkerFailToStartTest, Timeout);
   FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionBrowserTest,
                            TimeoutStartingWorker);
@@ -313,10 +320,30 @@
 
   typedef ServiceWorkerVersion self;
 
+  enum RequestType {
+    REQUEST_ACTIVATE,
+    REQUEST_INSTALL,
+    REQUEST_FETCH,
+    REQUEST_SYNC,
+    REQUEST_NOTIFICATION_CLICK,
+    REQUEST_PUSH,
+    REQUEST_GEOFENCING,
+    REQUEST_CROSS_ORIGIN_CONNECT
+  };
   enum PingState { NOT_PINGING, PINGING, PING_TIMED_OUT };
 
+  struct RequestInfo {
+    RequestInfo(int id, RequestType type);
+    ~RequestInfo();
+    int id;
+    RequestType type;
+    base::TimeTicks time;
+  };
+
   // Timeout for the worker to start.
   static const int kStartWorkerTimeoutMinutes;
+  // Timeout for a request to be handled.
+  static const int kRequestTimeoutMinutes;
 
   ~ServiceWorkerVersion() override;
 
@@ -398,7 +425,6 @@
   void StartWorkerInternal(bool pause_after_download);
 
   void DidSkipWaiting(int request_id);
-  void DidClaimClients(int request_id, ServiceWorkerStatusCode status);
   void DidGetClients(
       int request_id, const std::vector<ServiceWorkerClientInfo>& clients);
 
@@ -429,6 +455,14 @@
   template <typename IDMAP>
   void RemoveCallbackAndStopIfDoomed(IDMAP* callbacks, int request_id);
 
+  template <typename CallbackType>
+  int AddRequest(const CallbackType& callback,
+                 IDMap<CallbackType, IDMapOwnPointer>* callback_map,
+                 RequestType request_type);
+
+  bool OnRequestTimeout(const RequestInfo& info);
+  void SetAllRequestTimes(const base::TimeTicks& ticks);
+
   const int64 version_id_;
   int64 registration_id_;
   GURL script_url_;
@@ -471,6 +505,12 @@
   // Holds the time that the outstanding StartWorker() request started.
   base::TimeTicks start_time_;
 
+  // New requests are added to |requests_| along with their entry in a callback
+  // map. The timeout timer periodically checks |requests_| for entries that
+  // should time out or have already been fulfilled (i.e., removed from the
+  // callback map).
+  std::queue<RequestInfo> requests_;
+
   bool is_doomed_ = false;
   bool skip_waiting_ = false;
   bool skip_recording_startup_time_ = false;
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 42ff69f..5f3ec48 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -9,6 +9,7 @@
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
+#include "content/browser/service_worker/service_worker_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -34,7 +35,7 @@
 class MessageReceiver : public EmbeddedWorkerTestHelper {
  public:
   MessageReceiver()
-      : EmbeddedWorkerTestHelper(kRenderProcessId),
+      : EmbeddedWorkerTestHelper(base::FilePath(), kRenderProcessId),
         current_embedded_worker_id_(0) {}
   ~MessageReceiver() override {}
 
@@ -78,6 +79,13 @@
       base::Bind(&ObserveStatusChanges, base::Unretained(version), statuses));
 }
 
+void ReceiveFetchResult(ServiceWorkerStatusCode* status,
+                        ServiceWorkerStatusCode actual_status,
+                        ServiceWorkerFetchEventResult actual_result,
+                        const ServiceWorkerResponse& response) {
+  *status = actual_status;
+}
+
 // A specialized listener class to receive test messages from a worker.
 class MessageReceiverFromWorker : public EmbeddedWorkerInstance::Listener {
  public:
@@ -198,6 +206,33 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerFailToStartTest);
 };
 
+class MessageReceiverDisallowFetch : public MessageReceiver {
+ public:
+  MessageReceiverDisallowFetch() : MessageReceiver() {}
+  ~MessageReceiverDisallowFetch() override {}
+
+  void OnFetchEvent(int embedded_worker_id,
+                    int request_id,
+                    const ServiceWorkerFetchRequest& request) override {
+    // Do nothing.
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MessageReceiverDisallowFetch);
+};
+
+class ServiceWorkerWaitForeverInFetchTest : public ServiceWorkerVersionTest {
+ protected:
+  ServiceWorkerWaitForeverInFetchTest() : ServiceWorkerVersionTest() {}
+
+  scoped_ptr<MessageReceiver> GetMessageReceiver() override {
+    return make_scoped_ptr(new MessageReceiverDisallowFetch());
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerWaitForeverInFetchTest);
+};
+
 TEST_F(ServiceWorkerVersionTest, ConcurrentStartAndStop) {
   // Call StartWorker() multiple times.
   ServiceWorkerStatusCode status1 = SERVICE_WORKER_ERROR_FAILED;
@@ -376,7 +411,11 @@
   ASSERT_EQ(ServiceWorkerVersion::REDUNDANT, statuses[4]);
 }
 
-TEST_F(ServiceWorkerVersionTest, ScheduleStopWorker) {
+TEST_F(ServiceWorkerVersionTest, IdleTimeout) {
+  // Used to reliably test when the idle time gets reset regardless of clock
+  // granularity.
+  const base::TimeDelta kOneSecond = base::TimeDelta::FromSeconds(1);
+
   // Verify the timer is not running when version initializes its status.
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   EXPECT_FALSE(version_->timeout_timer_.IsRunning());
@@ -387,9 +426,13 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(SERVICE_WORKER_OK, status);
   EXPECT_TRUE(version_->timeout_timer_.IsRunning());
+  EXPECT_FALSE(version_->idle_time_.is_null());
 
-  // The timer should be running if the worker is restarted without controllee.
+  // The idle time should be reset if the worker is restarted without
+  // controllee.
   status = SERVICE_WORKER_ERROR_FAILED;
+  version_->idle_time_ -= kOneSecond;
+  base::TimeTicks idle_time = version_->idle_time_;
   version_->StopWorker(CreateReceiverOnCurrentThread(&status));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(SERVICE_WORKER_OK, status);
@@ -398,8 +441,11 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(SERVICE_WORKER_OK, status);
   EXPECT_TRUE(version_->timeout_timer_.IsRunning());
+  EXPECT_LT(idle_time, version_->idle_time_);
 
-  // Adding controllee doesn't stop the stop-worker-timer.
+  // Adding a controllee resets the idle time.
+  version_->idle_time_ -= kOneSecond;
+  idle_time = version_->idle_time_;
   scoped_ptr<ServiceWorkerProviderHost> host(
       new ServiceWorkerProviderHost(33 /* dummy render process id */,
                                     MSG_ROUTING_NONE /* render_frame_id */,
@@ -409,6 +455,19 @@
                                     NULL));
   version_->AddControllee(host.get());
   EXPECT_TRUE(version_->timeout_timer_.IsRunning());
+  EXPECT_LT(idle_time, version_->idle_time_);
+
+  // Completing an event resets the idle time.
+  status = SERVICE_WORKER_ERROR_FAILED;
+  version_->idle_time_ -= kOneSecond;
+  idle_time = version_->idle_time_;
+  version_->DispatchFetchEvent(ServiceWorkerFetchRequest(),
+                               base::Bind(&base::DoNothing),
+                               base::Bind(&ReceiveFetchResult, &status));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(SERVICE_WORKER_OK, status);
+  EXPECT_LT(idle_time, version_->idle_time_);
 }
 
 
@@ -441,6 +500,31 @@
   EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
 }
 
+TEST_F(ServiceWorkerWaitForeverInFetchTest, RequestTimeout) {
+  ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_NETWORK;  // dummy value
+
+  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
+  version_->DispatchFetchEvent(ServiceWorkerFetchRequest(),
+                               base::Bind(&base::DoNothing),
+                               base::Bind(&ReceiveFetchResult, &status));
+  base::RunLoop().RunUntilIdle();
+
+  // Callback has not completed yet.
+  EXPECT_EQ(SERVICE_WORKER_ERROR_NETWORK, status);
+  EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
+
+  // Simulate timeout.
+  EXPECT_TRUE(version_->timeout_timer_.IsRunning());
+  version_->SetAllRequestTimes(
+      base::TimeTicks::Now() -
+      base::TimeDelta::FromMinutes(
+          ServiceWorkerVersion::kRequestTimeoutMinutes + 1));
+  version_->timeout_timer_.user_task().Run();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(SERVICE_WORKER_ERROR_TIMEOUT, status);
+  EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status());
+}
+
 TEST_F(ServiceWorkerFailToStartTest, RendererCrash) {
   ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_NETWORK;  // dummy value
   version_->StartWorker(
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
index 68a85e04..01d8088 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
@@ -174,7 +174,8 @@
   ~ServiceWorkerWriteToCacheJobTest() override {}
 
   void SetUp() override {
-    helper_.reset(new EmbeddedWorkerTestHelper(kMockRenderProcessId));
+    helper_.reset(
+        new EmbeddedWorkerTestHelper(base::FilePath(), kMockRenderProcessId));
 
     // A new unstored registration/version.
     scope_ = GURL("https://host/scope/");
diff --git a/content/browser/shared_worker/worker_browsertest.cc b/content/browser/shared_worker/worker_browsertest.cc
index 3a0593b1..0a9c34c 100644
--- a/content/browser/shared_worker/worker_browsertest.cc
+++ b/content/browser/shared_worker/worker_browsertest.cc
@@ -111,17 +111,26 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WorkerTest, SingleSharedWorker) {
+  if (!SupportsSharedWorker())
+    return;
+
   RunTest("single_worker.html", "shared=true");
 }
 
 // http://crbug.com/96435
 IN_PROC_BROWSER_TEST_F(WorkerTest, MultipleSharedWorkers) {
+  if (!SupportsSharedWorker())
+    return;
+
   RunTest("multi_worker.html", "shared=true");
 }
 
 // Incognito windows should not share workers with non-incognito windows
 // http://crbug.com/30021
 IN_PROC_BROWSER_TEST_F(WorkerTest, IncognitoSharedWorkers) {
+  if (!SupportsSharedWorker())
+    return;
+
   // Load a non-incognito tab and have it create a shared worker
   RunTest("incognito_worker.html", std::string());
 
@@ -146,6 +155,9 @@
 // WebContentsless case from chrome/ to content/ and adjust the test
 // accordingly.
 IN_PROC_BROWSER_TEST_F(WorkerTest, SharedWorkerHttpAuth) {
+  if (!SupportsSharedWorker())
+    return;
+
   ASSERT_TRUE(test_server()->Start());
   GURL url = test_server()->GetURL("files/workers/shared_worker_auth.html");
   NavigateAndWaitForAuth(url);
@@ -188,6 +200,9 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WorkerTest, WebSocketSharedWorker) {
+  if (!SupportsSharedWorker())
+    return;
+
   // Launch WebSocket server.
   net::SpawnedTestServer ws_server(net::SpawnedTestServer::TYPE_WS,
                                    net::SpawnedTestServer::kLocalhost,
@@ -210,11 +225,17 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WorkerTest, PassMessagePortToSharedWorker) {
+  if (!SupportsSharedWorker())
+    return;
+
   RunTest("pass_messageport_to_sharedworker.html", "");
 }
 
 IN_PROC_BROWSER_TEST_F(WorkerTest,
                        PassMessagePortToSharedWorkerDontWaitForConnect) {
+  if (!SupportsSharedWorker())
+    return;
+
   RunTest("pass_messageport_to_sharedworker_dont_wait_for_connect.html", "");
 }
 
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index 480481f..1622819 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -349,7 +349,8 @@
     // Flush asynchronously now, because we don't have any children to wait for.
     TraceLog::GetInstance()->Flush(
         base::Bind(&TracingControllerImpl::OnLocalTraceDataCollected,
-                   base::Unretained(this)));
+                   base::Unretained(this)),
+        true);
   }
 
   // Notify all child processes.
@@ -682,7 +683,8 @@
     // called with the last of the local trace data.
     TraceLog::GetInstance()->Flush(
         base::Bind(&TracingControllerImpl::OnLocalTraceDataCollected,
-                   base::Unretained(this)));
+                   base::Unretained(this)),
+        true);
     return;
   }
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 906e19a..6626ab0 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -165,40 +165,6 @@
   return true;
 }
 
-// Helper function used with FrameTree::ForEach() for retrieving the total
-// loading progress and number of frames in a frame tree.
-bool CollectLoadProgress(double* progress,
-                         int* frame_count,
-                         FrameTreeNode* node) {
-  // Ignore the current frame if it has not started loading.
-  double frame_progress = node->GetLoadingProgress();
-  if (frame_progress == RenderFrameHostImpl::kLoadingProgressNotStarted)
-    return true;
-
-  // Collect progress.
-  *progress += node->GetLoadingProgress();
-  (*frame_count)++;
-  return true;
-}
-
-// Helper function used with FrameTree::ForEach() to check if at least one of
-// the nodes is loading.
-bool IsNodeLoading(bool* is_loading, FrameTreeNode* node) {
-  if (node->IsLoading()) {
-    // There is at least one node loading, so abort traversal.
-    *is_loading = true;
-    return false;
-  }
-  return true;
-}
-
-// Helper function used with FrameTree::ForEach() to reset the load progress.
-bool ResetLoadProgress(FrameTreeNode* node) {
-  node->current_frame_host()->set_loading_progress(
-      RenderFrameHostImpl::kLoadingProgressNotStarted);
-  return true;
-}
-
 bool ForEachFrameInternal(
     const base::Callback<void(RenderFrameHost*)>& on_frame,
     FrameTreeNode* node) {
@@ -236,36 +202,6 @@
   static_cast<RenderFrameHostImpl*>(frame_host)->SetAccessibilityMode(mode);
 }
 
-// Enable sudden termination for the current RenderFrameHost of
-// |frame_tree_node| if the ID of its SiteInstance is |site_instance_id|.  Used
-// with FrameTree::ForEach.
-bool EnableSuddenTermination(int32 site_instance_id,
-                             FrameTreeNode* frame_tree_node) {
-  if (frame_tree_node->current_frame_host()->GetSiteInstance()->GetId()
-      == site_instance_id) {
-    frame_tree_node->current_frame_host()
-        ->set_override_sudden_termination_status(true);
-  }
-  return true;
-}
-
-// Returns false and sets |sudden_termination_allowed| to false if sudden
-// termination is not allowed for the current RenderFrameHost of
-// |frame_tree_node|. Used with FrameTree::ForEach.
-bool SuddenTerminationAllowed(bool* sudden_termination_allowed,
-                              FrameTreeNode* frame_tree_node) {
-  if (frame_tree_node->current_frame_host()->SuddenTerminationAllowed())
-    return true;
-  *sudden_termination_allowed = false;
-  return false;
-}
-
-// Returns true if at least one of the nodes in the |frame_tree| is loading.
-bool IsFrameTreeLoading(FrameTree& frame_tree) {
-  bool is_loading = false;
-  frame_tree.ForEach(base::Bind(&IsNodeLoading, &is_loading));
-  return is_loading;
-}
 
 }  // namespace
 
@@ -855,7 +791,7 @@
 void WebContentsImpl::SetParentNativeViewAccessible(
 gfx::NativeViewAccessible accessible_parent) {
   accessible_parent_ = accessible_parent;
-  RenderFrameHostImpl* rfh = GetMainFrame();
+  RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(GetMainFrame());
   if (rfh)
     rfh->SetParentNativeViewAccessible(accessible_parent);
 }
@@ -1189,17 +1125,16 @@
 }
 
 bool WebContentsImpl::NeedToFireBeforeUnload() {
-  bool sudden_termination_allowed = true;
-  frame_tree_.ForEach(base::Bind(
-        &SuddenTerminationAllowed, &sudden_termination_allowed));
   // TODO(creis): Should we fire even for interstitial pages?
   return WillNotifyDisconnection() &&
       !ShowingInterstitialPage() &&
-      !sudden_termination_allowed;
+      !static_cast<RenderViewHostImpl*>(
+          GetRenderViewHost())->SuddenTerminationAllowed();
 }
 
 void WebContentsImpl::DispatchBeforeUnload(bool for_cross_site_transition) {
-  GetMainFrame()->DispatchBeforeUnload(for_cross_site_transition);
+  static_cast<RenderFrameHostImpl*>(GetMainFrame())->DispatchBeforeUnload(
+      for_cross_site_transition);
 }
 
 void WebContentsImpl::Stop() {
@@ -1448,7 +1383,7 @@
 void WebContentsImpl::RenderWidgetWasResized(
     RenderWidgetHostImpl* render_widget_host,
     bool width_changed) {
-  RenderFrameHostImpl* rfh = GetMainFrame();
+  RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(GetMainFrame());
   if (!rfh || render_widget_host != rfh->GetRenderWidgetHost())
     return;
 
@@ -1881,7 +1816,7 @@
   // Resume blocked requests for both the RenderViewHost and RenderFrameHost.
   // TODO(brettw): It seems bogus to reach into here and initialize the host.
   static_cast<RenderViewHostImpl*>(new_contents->GetRenderViewHost())->Init();
-  new_contents->GetMainFrame()->Init();
+  static_cast<RenderFrameHostImpl*>(new_contents->GetMainFrame())->Init();
 
   return new_contents;
 }
@@ -1999,13 +1934,13 @@
 
 BrowserAccessibilityManager*
     WebContentsImpl::GetRootBrowserAccessibilityManager() {
-  RenderFrameHostImpl* rfh = GetMainFrame();
+  RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(GetMainFrame());
   return rfh ? rfh->browser_accessibility_manager() : NULL;
 }
 
 BrowserAccessibilityManager*
     WebContentsImpl::GetOrCreateRootBrowserAccessibilityManager() {
-  RenderFrameHostImpl* rfh = GetMainFrame();
+  RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(GetMainFrame());
   return rfh ? rfh->GetOrCreateBrowserAccessibilityManager() : NULL;
 }
 
@@ -2962,14 +2897,16 @@
     return;
   }
 
-  if (!IsFrameTreeLoading(frame_tree_))
+  if (!frame_tree_.IsLoading())
     DidStartLoading(rfh, to_different_document);
 
   rfh->set_is_loading(true);
-  rfh->set_loading_progress(RenderFrameHostImpl::kLoadingProgressMinimum);
+
+  FrameTreeNode* ftn = rfh->frame_tree_node();
+  ftn->set_loading_progress(FrameTreeNode::kLoadingProgressMinimum);
 
   // Notify the RenderFrameHostManager of the event.
-  rfh->frame_tree_node()->render_manager()->OnDidStartLoading();
+  ftn->render_manager()->OnDidStartLoading();
 
   SendLoadProgressChanged();
 }
@@ -2998,7 +2935,9 @@
   }
 
   rfh->set_is_loading(false);
-  rfh->set_loading_progress(RenderFrameHostImpl::kLoadingProgressDone);
+
+  FrameTreeNode* ftn = rfh->frame_tree_node();
+  ftn->set_loading_progress(FrameTreeNode::kLoadingProgressDone);
 
   // TODO(erikchen): Remove ScopedTracker below once crbug.com/465796 is
   // fixed.
@@ -3020,9 +2959,9 @@
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
           "465796 WebContentsImpl::OnDidStopLoading::NotifyRenderManager"));
   // Notify the RenderFrameHostManager of the event.
-  rfh->frame_tree_node()->render_manager()->OnDidStopLoading();
+  ftn->render_manager()->OnDidStopLoading();
 
-  if (!IsFrameTreeLoading(frame_tree_)) {
+  if (!frame_tree_.IsLoading()) {
     // TODO(erikchen): Remove ScopedTracker below once crbug.com/465796 is
     // fixed.
     tracked_objects::ScopedTracker tracking_profile4(
@@ -3044,8 +2983,9 @@
 
   RenderFrameHostImpl* rfh =
       static_cast<RenderFrameHostImpl*>(render_frame_message_source_);
+  FrameTreeNode* ftn = rfh->frame_tree_node();
 
-  rfh->set_loading_progress(load_progress);
+  ftn->set_loading_progress(load_progress);
 
   // We notify progress change immediately for the first and last updates.
   // Also, since the message loop may be pretty busy when a page is loaded, it
@@ -3351,12 +3291,16 @@
     if (!video_power_save_blocker_ && !IsHidden())
       CreateVideoPowerSaveBlocker();
   }
+
+  FOR_EACH_OBSERVER(WebContentsObserver, observers_, MediaStartedPlaying());
 }
 
 void WebContentsImpl::OnMediaPausedNotification(int64 player_cookie) {
   RemoveMediaPlayerEntry(player_cookie, &active_audio_players_);
   RemoveMediaPlayerEntry(player_cookie, &active_video_players_);
   MaybeReleasePowerSaveBlockers();
+
+  FOR_EACH_OBSERVER(WebContentsObserver, observers_, MediaPaused());
 }
 
 void WebContentsImpl::OnFirstVisuallyNonEmptyPaint() {
@@ -3519,13 +3463,8 @@
 
 void WebContentsImpl::SendLoadProgressChanged() {
   loading_last_progress_update_ = base::TimeTicks::Now();
-  double progress = 0.0;
-  int frame_count = 0;
+  double progress = frame_tree_.GetLoadProgress();
 
-  frame_tree_.ForEach(
-      base::Bind(&CollectLoadProgress, &progress, &frame_count));
-  if (frame_count != 0)
-    progress /= frame_count;
   DCHECK_LE(progress, 1.0);
 
   if (progress <= loading_total_progress_)
@@ -3537,7 +3476,7 @@
 }
 
 void WebContentsImpl::ResetLoadProgressState() {
-  frame_tree_.ForEach(base::Bind(&ResetLoadProgress));
+  frame_tree_.ResetLoadProgress();
   loading_total_progress_ = 0.0;
   loading_weak_factory_.InvalidateWeakPtrs();
   loading_last_progress_update_ = base::TimeTicks();
@@ -4209,8 +4148,7 @@
       rfhi->IsWaitingForUnloadACK()) {
     // Hang occurred while firing the beforeunload/unload handler.
     // Pretend the handler fired so tab closing continues as if it had.
-    frame_tree_.ForEach(base::Bind(
-          &EnableSuddenTermination, rvhi->GetSiteInstance()->GetId()));
+    rvhi->set_sudden_termination_allowed(true);
 
     if (!GetRenderManager()->ShouldCloseTabOnUnresponsiveRenderer())
       return;
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 67e8e40..f8e1e38 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -3062,50 +3062,4 @@
   EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
 }
 
-// Test that sudden termination status is properly tracked for a frame.
-TEST_F(WebContentsImplTest, SuddenTerminationForFrame) {
-  const GURL url("http://www.chromium.org");
-  contents()->NavigateAndCommit(url);
-
-  TestRenderFrameHost* frame = contents()->GetMainFrame();
-  EXPECT_TRUE(frame->SuddenTerminationAllowed());
-
-  // Register a BeforeUnload handler.
-  frame->SendBeforeUnloadHandlersPresent(true);
-  EXPECT_FALSE(frame->SuddenTerminationAllowed());
-
-  // Unregister the BeforeUnload handler.
-  frame->SendBeforeUnloadHandlersPresent(false);
-  EXPECT_TRUE(frame->SuddenTerminationAllowed());
-
-  // Register an Unload handler.
-  frame->SendUnloadHandlersPresent(true);
-  EXPECT_FALSE(frame->SuddenTerminationAllowed());
-
-  // Unregister the Unload handler.
-  frame->SendUnloadHandlersPresent(false);
-  EXPECT_TRUE(frame->SuddenTerminationAllowed());
-
-  // Register a BeforeUnload handler and an Unload handler.
-  frame->SendBeforeUnloadHandlersPresent(true);
-  frame->SendUnloadHandlersPresent(true);
-  EXPECT_FALSE(frame->SuddenTerminationAllowed());
-
-  // Override the sudden termination status.
-  frame->set_override_sudden_termination_status(true);
-  EXPECT_TRUE(frame->SuddenTerminationAllowed());
-  frame->set_override_sudden_termination_status(false);
-  EXPECT_FALSE(frame->SuddenTerminationAllowed());
-
-  // Sudden termination should not be allowed unless there are no BeforeUnload
-  // handlers and no Unload handlers in the RenderFrame.
-  frame->SendBeforeUnloadHandlersPresent(false);
-  EXPECT_FALSE(frame->SuddenTerminationAllowed());
-  frame->SendBeforeUnloadHandlersPresent(true);
-  frame->SendUnloadHandlersPresent(false);
-  EXPECT_FALSE(frame->SuddenTerminationAllowed());
-  frame->SendBeforeUnloadHandlersPresent(false);
-  EXPECT_TRUE(frame->SuddenTerminationAllowed());
-}
-
 }  // namespace content
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 92bef2e..db0ba133 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -738,95 +738,95 @@
 };
 
 const DataResource kDataResources[] = {
-  { "missingImage", IDR2_BROKENIMAGE, ui::SCALE_FACTOR_100P },
-  { "missingImage@2x", IDR2_BROKENIMAGE, ui::SCALE_FACTOR_200P },
-  { "mediaplayerPause", IDR2_MEDIAPLAYER_PAUSE_BUTTON, ui::SCALE_FACTOR_100P },
+  { "missingImage", IDR_BROKENIMAGE, ui::SCALE_FACTOR_100P },
+  { "missingImage@2x", IDR_BROKENIMAGE, ui::SCALE_FACTOR_200P },
+  { "mediaplayerPause", IDR_MEDIAPLAYER_PAUSE_BUTTON, ui::SCALE_FACTOR_100P },
   { "mediaplayerPauseHover",
-    IDR2_MEDIAPLAYER_PAUSE_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_PAUSE_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
   { "mediaplayerPauseDown",
-    IDR2_MEDIAPLAYER_PAUSE_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
-  { "mediaplayerPlay", IDR2_MEDIAPLAYER_PLAY_BUTTON, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_PAUSE_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
+  { "mediaplayerPlay", IDR_MEDIAPLAYER_PLAY_BUTTON, ui::SCALE_FACTOR_100P },
   { "mediaplayerPlayHover",
-    IDR2_MEDIAPLAYER_PLAY_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_PLAY_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
   { "mediaplayerPlayDown",
-    IDR2_MEDIAPLAYER_PLAY_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_PLAY_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
   { "mediaplayerPlayDisabled",
-    IDR2_MEDIAPLAYER_PLAY_BUTTON_DISABLED, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_PLAY_BUTTON_DISABLED, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel3",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL3_BUTTON, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL3_BUTTON, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel3Hover",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL3_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL3_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel3Down",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL3_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL3_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel2",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL2_BUTTON, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL2_BUTTON, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel2Hover",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL2_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL2_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel2Down",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL2_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL2_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel1",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL1_BUTTON, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL1_BUTTON, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel1Hover",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL1_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL1_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel1Down",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL1_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL1_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel0",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL0_BUTTON, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL0_BUTTON, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel0Hover",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL0_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL0_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundLevel0Down",
-    IDR2_MEDIAPLAYER_SOUND_LEVEL0_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_LEVEL0_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
   { "mediaplayerSoundDisabled",
-    IDR2_MEDIAPLAYER_SOUND_DISABLED, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SOUND_DISABLED, ui::SCALE_FACTOR_100P },
   { "mediaplayerSliderThumb",
-    IDR2_MEDIAPLAYER_SLIDER_THUMB, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SLIDER_THUMB, ui::SCALE_FACTOR_100P },
   { "mediaplayerSliderThumbHover",
-    IDR2_MEDIAPLAYER_SLIDER_THUMB_HOVER, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SLIDER_THUMB_HOVER, ui::SCALE_FACTOR_100P },
   { "mediaplayerSliderThumbDown",
-    IDR2_MEDIAPLAYER_SLIDER_THUMB_DOWN, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_SLIDER_THUMB_DOWN, ui::SCALE_FACTOR_100P },
   { "mediaplayerVolumeSliderThumb",
-    IDR2_MEDIAPLAYER_VOLUME_SLIDER_THUMB, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_VOLUME_SLIDER_THUMB, ui::SCALE_FACTOR_100P },
   { "mediaplayerVolumeSliderThumbHover",
-    IDR2_MEDIAPLAYER_VOLUME_SLIDER_THUMB_HOVER, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_VOLUME_SLIDER_THUMB_HOVER, ui::SCALE_FACTOR_100P },
   { "mediaplayerVolumeSliderThumbDown",
-    IDR2_MEDIAPLAYER_VOLUME_SLIDER_THUMB_DOWN, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_VOLUME_SLIDER_THUMB_DOWN, ui::SCALE_FACTOR_100P },
   { "mediaplayerVolumeSliderThumbDisabled",
-    IDR2_MEDIAPLAYER_VOLUME_SLIDER_THUMB_DISABLED, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_VOLUME_SLIDER_THUMB_DISABLED, ui::SCALE_FACTOR_100P },
   { "mediaplayerClosedCaption",
-    IDR2_MEDIAPLAYER_CLOSEDCAPTION_BUTTON, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_CLOSEDCAPTION_BUTTON, ui::SCALE_FACTOR_100P },
   { "mediaplayerClosedCaptionHover",
-    IDR2_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
   { "mediaplayerClosedCaptionDown",
-    IDR2_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
   { "mediaplayerClosedCaptionDisabled",
-    IDR2_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_DISABLED, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_DISABLED, ui::SCALE_FACTOR_100P },
   { "mediaplayerFullscreen",
-    IDR2_MEDIAPLAYER_FULLSCREEN_BUTTON, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_FULLSCREEN_BUTTON, ui::SCALE_FACTOR_100P },
   { "mediaplayerFullscreenHover",
-    IDR2_MEDIAPLAYER_FULLSCREEN_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_FULLSCREEN_BUTTON_HOVER, ui::SCALE_FACTOR_100P },
   { "mediaplayerFullscreenDown",
-    IDR2_MEDIAPLAYER_FULLSCREEN_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_FULLSCREEN_BUTTON_DOWN, ui::SCALE_FACTOR_100P },
   { "mediaplayerCastOff",
-    IDR2_MEDIAPLAYER_CAST_BUTTON_OFF, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_CAST_BUTTON_OFF, ui::SCALE_FACTOR_100P },
   { "mediaplayerCastOn",
-    IDR2_MEDIAPLAYER_CAST_BUTTON_ON, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_CAST_BUTTON_ON, ui::SCALE_FACTOR_100P },
   { "mediaplayerFullscreenDisabled",
-    IDR2_MEDIAPLAYER_FULLSCREEN_BUTTON_DISABLED, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_FULLSCREEN_BUTTON_DISABLED, ui::SCALE_FACTOR_100P },
   { "mediaplayerOverlayCastOff",
-    IDR2_MEDIAPLAYER_OVERLAY_CAST_BUTTON_OFF, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_OVERLAY_CAST_BUTTON_OFF, ui::SCALE_FACTOR_100P },
   { "mediaplayerOverlayPlay",
-    IDR2_MEDIAPLAYER_OVERLAY_PLAY_BUTTON, ui::SCALE_FACTOR_100P },
-  { "panIcon", IDR2_PAN_SCROLL_ICON, ui::SCALE_FACTOR_100P },
-  { "searchCancel", IDR2_SEARCH_CANCEL, ui::SCALE_FACTOR_100P },
-  { "searchCancelPressed", IDR2_SEARCH_CANCEL_PRESSED, ui::SCALE_FACTOR_100P },
-  { "searchMagnifier", IDR2_SEARCH_MAGNIFIER, ui::SCALE_FACTOR_100P },
+    IDR_MEDIAPLAYER_OVERLAY_PLAY_BUTTON, ui::SCALE_FACTOR_100P },
+  { "panIcon", IDR_PAN_SCROLL_ICON, ui::SCALE_FACTOR_100P },
+  { "searchCancel", IDR_SEARCH_CANCEL, ui::SCALE_FACTOR_100P },
+  { "searchCancelPressed", IDR_SEARCH_CANCEL_PRESSED, ui::SCALE_FACTOR_100P },
+  { "searchMagnifier", IDR_SEARCH_MAGNIFIER, ui::SCALE_FACTOR_100P },
   { "searchMagnifierResults",
-    IDR2_SEARCH_MAGNIFIER_RESULTS, ui::SCALE_FACTOR_100P },
-  { "textAreaResizeCorner", IDR2_TEXTAREA_RESIZER, ui::SCALE_FACTOR_100P },
-  { "textAreaResizeCorner@2x", IDR2_TEXTAREA_RESIZER, ui::SCALE_FACTOR_200P },
-  { "generatePassword", IDR2_PASSWORD_GENERATION_ICON, ui::SCALE_FACTOR_100P },
+    IDR_SEARCH_MAGNIFIER_RESULTS, ui::SCALE_FACTOR_100P },
+  { "textAreaResizeCorner", IDR_TEXTAREA_RESIZER, ui::SCALE_FACTOR_100P },
+  { "textAreaResizeCorner@2x", IDR_TEXTAREA_RESIZER, ui::SCALE_FACTOR_200P },
+  { "generatePassword", IDR_PASSWORD_GENERATION_ICON, ui::SCALE_FACTOR_100P },
   { "generatePasswordHover",
-    IDR2_PASSWORD_GENERATION_ICON_HOVER, ui::SCALE_FACTOR_100P },
+    IDR_PASSWORD_GENERATION_ICON_HOVER, ui::SCALE_FACTOR_100P },
   { "html.css", IDR_UASTYLE_HTML_CSS, ui::SCALE_FACTOR_NONE },
   { "quirks.css", IDR_UASTYLE_QUIRKS_CSS, ui::SCALE_FACTOR_NONE },
   { "view-source.css", IDR_UASTYLE_VIEW_SOURCE_CSS, ui::SCALE_FACTOR_NONE },
diff --git a/content/common/OWNERS b/content/common/OWNERS
index 4bc5132b..c035cf2 100644
--- a/content/common/OWNERS
+++ b/content/common/OWNERS
@@ -72,3 +72,7 @@
 # Compositor
 per-file cc_messages_unittest.cc=danakj@chromium.org
 per-file cc_messages_perftest.cc=danakj@chromium.org
+
+# DirectWrite
+per-file dwrite_font_platform_win*=shrikant@chromium.org
+per-file dwrite_font_platform_win*=scottmg@chromium.org
diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc
index b58475a..69a63507 100644
--- a/content/common/child_process_host_impl.cc
+++ b/content/common/child_process_host_impl.cc
@@ -254,9 +254,8 @@
                           OnShutdownRequest)
       IPC_MESSAGE_HANDLER(ChildProcessHostMsg_SyncAllocateSharedMemory,
                           OnAllocateSharedMemory)
-      IPC_MESSAGE_HANDLER_DELAY_REPLY(
-          ChildProcessHostMsg_SyncAllocateGpuMemoryBuffer,
-          OnAllocateGpuMemoryBuffer)
+      IPC_MESSAGE_HANDLER(ChildProcessHostMsg_SyncAllocateGpuMemoryBuffer,
+                          OnAllocateGpuMemoryBuffer)
       IPC_MESSAGE_HANDLER(ChildProcessHostMsg_DeletedGpuMemoryBuffer,
                           OnDeletedGpuMemoryBuffer)
       IPC_MESSAGE_UNHANDLED(handled = false)
@@ -317,25 +316,20 @@
     uint32 height,
     gfx::GpuMemoryBuffer::Format format,
     gfx::GpuMemoryBuffer::Usage usage,
-    IPC::Message* reply) {
+    gfx::GpuMemoryBufferHandle* handle) {
   // TODO(reveman): Add support for other types of GpuMemoryBuffers.
 
-  gfx::GpuMemoryBufferHandle handle;
   // AllocateForChildProcess() will check if |width| and |height| are valid
   // and handle failure in a controlled way when not. We just need to make
   // sure |format| and |usage| are supported here.
   if (GpuMemoryBufferImplSharedMemory::IsFormatSupported(format) &&
       usage == gfx::GpuMemoryBuffer::MAP) {
-    handle = GpuMemoryBufferImplSharedMemory::AllocateForChildProcess(
+    *handle = GpuMemoryBufferImplSharedMemory::AllocateForChildProcess(
         g_next_gpu_memory_buffer_id.GetNext(),
         gfx::Size(width, height),
         format,
         peer_process_.Handle());
   }
-
-  ChildProcessHostMsg_SyncAllocateGpuMemoryBuffer::WriteReplyParams(reply,
-                                                                    handle);
-  Send(reply);
 }
 
 void ChildProcessHostImpl::OnDeletedGpuMemoryBuffer(
diff --git a/content/common/child_process_host_impl.h b/content/common/child_process_host_impl.h
index 1578b17..93ff0ebe 100644
--- a/content/common/child_process_host_impl.h
+++ b/content/common/child_process_host_impl.h
@@ -84,7 +84,7 @@
                                  uint32 height,
                                  gfx::GpuMemoryBuffer::Format format,
                                  gfx::GpuMemoryBuffer::Usage usage,
-                                 IPC::Message* reply);
+                                 gfx::GpuMemoryBufferHandle* handle);
   void OnDeletedGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
                                 uint32 sync_point);
 
diff --git a/content/common/dwrite_font_platform_win.cc b/content/common/dwrite_font_platform_win.cc
index aa9d98f..648bc5e 100644
--- a/content/common/dwrite_font_platform_win.cc
+++ b/content/common/dwrite_font_platform_win.cc
@@ -746,6 +746,12 @@
   return reg_fonts_[idx];
 }
 
+const base::FilePath::CharType* kFontExtensionsToIgnore[] {
+  FILE_PATH_LITERAL(".FON"),  // Bitmap or vector
+  FILE_PATH_LITERAL(".PFM"),  // Adobe Type 1
+  FILE_PATH_LITERAL(".PFB"),  // Adobe Type 1
+};
+
 const wchar_t* kFontsToIgnore[] = {
   // "Gill Sans Ultra Bold" turns into an Ultra Bold weight "Gill Sans" in
   // DirectWrite, but most users don't have any other weights. The regular
@@ -798,6 +804,18 @@
             break;
           }
         }
+        // DirectWrite doesn't support bitmap/vector fonts and Adobe type 1
+        // fonts, we will ignore those font extensions.
+        // MSDN article: http://goo.gl/TfCOA
+        if (!should_ignore) {
+          for (const auto& ignore : kFontExtensionsToIgnore) {
+            if (path.MatchesExtension(ignore)) {
+              should_ignore = true;
+              break;
+            }
+          }
+        }
+
         if (!should_ignore)
           reg_fonts_.push_back(value.c_str());
       }
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 127daf0..72eb2f45 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -68,6 +68,7 @@
   IPC_STRUCT_TRAITS_MEMBER(selection_text)
   IPC_STRUCT_TRAITS_MEMBER(suggested_filename)
   IPC_STRUCT_TRAITS_MEMBER(misspelled_word)
+  IPC_STRUCT_TRAITS_MEMBER(misspelling_hash)
   IPC_STRUCT_TRAITS_MEMBER(dictionary_suggestions)
   IPC_STRUCT_TRAITS_MEMBER(spellcheck_enabled)
   IPC_STRUCT_TRAITS_MEMBER(is_editable)
@@ -820,14 +821,6 @@
 // See the comment in chrome/browser/ui/browser.h for more details.
 IPC_MESSAGE_ROUTED1(FrameHostMsg_ToggleFullscreen, bool /* enter_fullscreen */)
 
-// Messages to signal the presence or absence of BeforeUnload or Unload handlers
-// for a frame. |present| is true if there is at least one of the handlers for
-// the frame.
-IPC_MESSAGE_ROUTED1(FrameHostMsg_BeforeUnloadHandlersPresent,
-                    bool /* present */)
-IPC_MESSAGE_ROUTED1(FrameHostMsg_UnloadHandlersPresent,
-                    bool /* present */)
-
 // Dispatch a load event for this frame in the iframe element of an
 // out-of-process parent frame.
 IPC_MESSAGE_ROUTED0(FrameHostMsg_DispatchLoad)
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl.cc b/content/common/gpu/client/gpu_memory_buffer_impl.cc
index 0c0426f..208261d 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl.cc
@@ -111,6 +111,25 @@
   return false;
 }
 
+// static
+size_t GpuMemoryBufferImpl::NumberOfPlanesForGpuMemoryBufferFormat(
+    gfx::GpuMemoryBuffer::Format format) {
+  switch (format) {
+    case gfx::GpuMemoryBuffer::Format::ATC:
+    case gfx::GpuMemoryBuffer::Format::ATCIA:
+    case gfx::GpuMemoryBuffer::Format::DXT1:
+    case gfx::GpuMemoryBuffer::Format::DXT5:
+    case gfx::GpuMemoryBuffer::Format::ETC1:
+    case gfx::GpuMemoryBuffer::Format::RGBA_8888:
+    case gfx::GpuMemoryBuffer::Format::RGBX_8888:
+    case gfx::GpuMemoryBuffer::Format::BGRA_8888:
+      return 1;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
 gfx::GpuMemoryBuffer::Format GpuMemoryBufferImpl::GetFormat() const {
   return format_;
 }
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl.h b/content/common/gpu/client/gpu_memory_buffer_impl.h
index 61800c80..cd8c108 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl.h
+++ b/content/common/gpu/client/gpu_memory_buffer_impl.h
@@ -39,6 +39,10 @@
                             Format format,
                             size_t* stride_in_bytes);
 
+  // Returns the number of planes based on the format of the buffer.
+  static size_t NumberOfPlanesForGpuMemoryBufferFormat(
+      gfx::GpuMemoryBuffer::Format format);
+
   // Overridden from gfx::GpuMemoryBuffer:
   bool IsMapped() const override;
   Format GetFormat() const override;
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.cc b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.cc
index e8ac048e..fef592f 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.cc
@@ -35,11 +35,12 @@
       handle.id, size, format, callback, io_surface.release()));
 }
 
-void* GpuMemoryBufferImplIOSurface::Map() {
+bool GpuMemoryBufferImplIOSurface::Map(void** data) {
   DCHECK(!mapped_);
   IOSurfaceLock(io_surface_, 0, NULL);
   mapped_ = true;
-  return IOSurfaceGetBaseAddress(io_surface_);
+  *data = IOSurfaceGetBaseAddress(io_surface_);
+  return true;
 }
 
 void GpuMemoryBufferImplIOSurface::Unmap() {
@@ -48,8 +49,8 @@
   mapped_ = false;
 }
 
-uint32 GpuMemoryBufferImplIOSurface::GetStride() const {
-  return IOSurfaceGetBytesPerRow(io_surface_);
+void GpuMemoryBufferImplIOSurface::GetStride(uint32* stride) const {
+  *stride = IOSurfaceGetBytesPerRow(io_surface_);
 }
 
 gfx::GpuMemoryBufferHandle GpuMemoryBufferImplIOSurface::GetHandle() const {
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h
index d9db52f..ff16c15 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h
@@ -22,9 +22,9 @@
       const DestructionCallback& callback);
 
   // Overridden from gfx::GpuMemoryBuffer:
-  void* Map() override;
+  bool Map(void** data) override;
   void Unmap() override;
-  uint32 GetStride() const override;
+  void GetStride(uint32* stride) const override;
   gfx::GpuMemoryBufferHandle GetHandle() const override;
 
  private:
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_buffer.cc b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_buffer.cc
index 24dfb3f..1dc9df1 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_buffer.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_buffer.cc
@@ -31,18 +31,17 @@
           handle.id, size, format, callback));
 }
 
-void* GpuMemoryBufferImplOzoneNativeBuffer::Map() {
+bool GpuMemoryBufferImplOzoneNativeBuffer::Map(void** data) {
   NOTREACHED();
-  return NULL;
+  return false;
 }
 
 void GpuMemoryBufferImplOzoneNativeBuffer::Unmap() {
   NOTREACHED();
 }
 
-uint32 GpuMemoryBufferImplOzoneNativeBuffer::GetStride() const {
+void GpuMemoryBufferImplOzoneNativeBuffer::GetStride(uint32* stride) const {
   NOTREACHED();
-  return 0;
 }
 
 gfx::GpuMemoryBufferHandle GpuMemoryBufferImplOzoneNativeBuffer::GetHandle()
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_buffer.h b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_buffer.h
index d88e070..f82bc97 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_buffer.h
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_buffer.h
@@ -19,9 +19,9 @@
       const DestructionCallback& callback);
 
   // Overridden from gfx::GpuMemoryBuffer:
-  void* Map() override;
+  bool Map(void** data) override;
   void Unmap() override;
-  uint32 GetStride() const override;
+  void GetStride(uint32* stride) const override;
   gfx::GpuMemoryBufferHandle GetHandle() const override;
 
  private:
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc
index 8daff2a..3d3aee96 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc
@@ -156,10 +156,11 @@
   return false;
 }
 
-void* GpuMemoryBufferImplSharedMemory::Map() {
+bool GpuMemoryBufferImplSharedMemory::Map(void** data) {
   DCHECK(!mapped_);
   mapped_ = true;
-  return shared_memory_->memory();
+  *data = shared_memory_->memory();
+  return true;
 }
 
 void GpuMemoryBufferImplSharedMemory::Unmap() {
@@ -167,11 +168,11 @@
   mapped_ = false;
 }
 
-uint32 GpuMemoryBufferImplSharedMemory::GetStride() const {
+void GpuMemoryBufferImplSharedMemory::GetStride(uint32* stride) const {
   size_t stride_in_bytes = 0;
   bool valid_stride = StrideInBytes(size_.width(), format_, &stride_in_bytes);
   DCHECK(valid_stride);
-  return stride_in_bytes;
+  *stride = stride_in_bytes;
 }
 
 gfx::GpuMemoryBufferHandle GpuMemoryBufferImplSharedMemory::GetHandle() const {
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h
index 022c320e..c7eb80ca 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h
@@ -34,9 +34,9 @@
   static bool IsSizeValidForFormat(const gfx::Size& size, Format format);
 
   // Overridden from gfx::GpuMemoryBuffer:
-  void* Map() override;
+  bool Map(void** data) override;
   void Unmap() override;
-  uint32 GetStride() const override;
+  void GetStride(uint32* stride) const override;
   gfx::GpuMemoryBufferHandle GetHandle() const override;
 
  private:
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.cc b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.cc
index 440dbfc..a1d0e5c0 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.cc
@@ -68,7 +68,7 @@
           handle.id, size, format, callback, native_window));
 }
 
-void* GpuMemoryBufferImplSurfaceTexture::Map() {
+bool GpuMemoryBufferImplSurfaceTexture::Map(void** data) {
   TRACE_EVENT0("gpu", "GpuMemoryBufferImplSurfaceTexture::Map");
 
   DCHECK(!mapped_);
@@ -77,17 +77,18 @@
   int status = ANativeWindow_lock(native_window_, &buffer, NULL);
   if (status) {
     VLOG(1) << "ANativeWindow_lock failed with error code: " << status;
-    return NULL;
+    return false;
   }
 
   size_t stride_in_bytes = 0;
   if (!StrideInBytes(buffer.stride, format_, &stride_in_bytes))
-    return NULL;
+    return false;
 
   DCHECK_LE(size_.width(), buffer.stride);
   stride_ = stride_in_bytes;
   mapped_ = true;
-  return buffer.bits;
+  *data = buffer.bits;
+  return true;
 }
 
 void GpuMemoryBufferImplSurfaceTexture::Unmap() {
@@ -98,8 +99,8 @@
   mapped_ = false;
 }
 
-uint32 GpuMemoryBufferImplSurfaceTexture::GetStride() const {
-  return stride_;
+void GpuMemoryBufferImplSurfaceTexture::GetStride(uint32* stride) const {
+  *stride = stride_;
 }
 
 gfx::GpuMemoryBufferHandle GpuMemoryBufferImplSurfaceTexture::GetHandle()
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h
index 683aea7..111ef17 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h
@@ -21,10 +21,10 @@
       const DestructionCallback& callback);
 
   // Overridden from gfx::GpuMemoryBuffer:
-  void* Map() override;
+  bool Map(void** data) override;
   void Unmap() override;
   gfx::GpuMemoryBufferHandle GetHandle() const override;
-  uint32 GetStride() const override;
+  void GetStride(uint32* stride) const override;
 
  private:
   GpuMemoryBufferImplSurfaceTexture(gfx::GpuMemoryBufferId id,
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_unittest.cc b/content/common/gpu/client/gpu_memory_buffer_impl_unittest.cc
index 419dfee..ba55d78 100644
--- a/content/common/gpu/client/gpu_memory_buffer_impl_unittest.cc
+++ b/content/common/gpu/client/gpu_memory_buffer_impl_unittest.cc
@@ -106,10 +106,12 @@
     ASSERT_TRUE(buffer);
     EXPECT_FALSE(buffer->IsMapped());
 
-    void* memory = buffer->Map();
-    ASSERT_TRUE(memory);
+    void* memory;
+    bool rv = buffer->Map(&memory);
+    ASSERT_TRUE(rv);
     EXPECT_TRUE(buffer->IsMapped());
-    uint32 stride = buffer->GetStride();
+    uint32 stride;
+    buffer->GetStride(&stride);
     EXPECT_GE(stride, width_in_bytes);
     memcpy(memory, data.get(), width_in_bytes);
     EXPECT_EQ(memcmp(memory, data.get(), width_in_bytes), 0);
diff --git a/content/common/gpu/media/dxva_video_decode_accelerator.cc b/content/common/gpu/media/dxva_video_decode_accelerator.cc
index 25aae5c..801ff57 100644
--- a/content/common/gpu/media/dxva_video_decode_accelerator.cc
+++ b/content/common/gpu/media/dxva_video_decode_accelerator.cc
@@ -940,9 +940,16 @@
     codec_ = media::kCodecH264;
   } else if (profile == media::VP8PROFILE_ANY ||
              profile == media::VP9PROFILE_ANY) {
+    int program_files_key = base::DIR_PROGRAM_FILES;
+    if (base::win::OSInfo::GetInstance()->wow64_status() ==
+        base::win::OSInfo::WOW64_ENABLED) {
+      program_files_key = base::DIR_PROGRAM_FILES6432;
+    }
+
     base::FilePath dll_path;
-    RETURN_ON_FAILURE(PathService::Get(base::DIR_PROGRAM_FILES6432, &dll_path),
-        "failed to get path for DIR_PROGRAM_FILES6432", false);
+    RETURN_ON_FAILURE(PathService::Get(program_files_key, &dll_path),
+        "failed to get path for Program Files", false);
+
     dll_path = dll_path.Append(kVPXDecoderDLLPath);
     if (profile == media::VP8PROFILE_ANY) {
       codec_ = media::kCodecVP8;
diff --git a/content/common/host_discardable_shared_memory_manager.cc b/content/common/host_discardable_shared_memory_manager.cc
index 49c24f81..4c5c220 100644
--- a/content/common/host_discardable_shared_memory_manager.cc
+++ b/content/common/host_discardable_shared_memory_manager.cc
@@ -359,12 +359,8 @@
     scoped_refptr<MemorySegment> segment = segments_.back();
     segments_.pop_back();
 
-    // Attempt to purge and truncate LRU segment. When successful, as much
-    // memory as possible will be released to the OS. How much memory is
-    // released depends on the platform. The child process should perform
-    // periodic cleanup to ensure that all memory is release within a
-    // reasonable amount of time.
-    if (segment->memory()->PurgeAndTruncate(current_time)) {
+    // Attempt to purge LRU segment. When successful, released the memory.
+    if (segment->memory()->Purge(current_time)) {
       ReleaseMemory(segment->memory());
       continue;
     }
@@ -387,6 +383,11 @@
   DCHECK_GE(bytes_allocated_, size);
   bytes_allocated_ -= size;
 
+#if defined(DISCARDABLE_SHARED_MEMORY_SHRINKING)
+  // Shrink memory segment. This will immediately release the memory to the OS.
+  memory->Shrink();
+#endif
+
   // This will unmap the memory segment and drop our reference. The result
   // is that the memory will be released to the OS if the child process is
   // no longer referencing it.
diff --git a/content/common/presentation/presentation_service.mojom b/content/common/presentation/presentation_service.mojom
index 99ab552..36756e2 100644
--- a/content/common/presentation/presentation_service.mojom
+++ b/content/common/presentation/presentation_service.mojom
@@ -9,6 +9,11 @@
     string id;
 };
 
+enum PresentationSessionState {
+    CONNECTED,
+    DISCONNECTED
+};
+
 enum PresentationErrorType {
     NO_AVAILABLE_SCREENS,
     SESSION_REQUEST_CANCELLED,
@@ -33,15 +38,14 @@
     // handling the result (provided via Mojo callback) to get the next update
     // about the availability status.
     // May start discovery of the presentation screens. The implementation might
-    // stop discovery once there are no active calls to GetScreenAvailability.
-    // |presentation_url| can be specified to help the implementation to filter
-    // out incompatible screens.
-    GetScreenAvailability(string? presentation_url) =>
+    // stop discovery once there are no active calls to
+    // ListenForScreenAvailability. |presentation_url| can be specified to help
+    // the implementation to filter out incompatible screens.
+    ListenForScreenAvailability(string? presentation_url) =>
         (string? presentation_url, bool available);
 
-    // Called when the frame no longer listens to the
-    // |availablechange| event.
-    OnScreenAvailabilityListenerRemoved(string? presentation_url);
+    // Called when the frame no longer listens to the |availablechange| event.
+    RemoveScreenAvailabilityListener(string? presentation_url);
 
     // Called when the renderer is ready to receive the browser initiated
     // session. If the default session is started by the embedder before this
@@ -72,4 +76,11 @@
 
     // Called when closeSession() is called by the frame.
     CloseSession(string presentation_url, string presentation_id);
+
+    // Called when the frame is ready to process the next state change. Returns
+    // the last session state if it’s changed since the last time the callback
+    // was called. Might cause the event fired with the initial state change.
+    ListenForSessionStateChange()
+        => (PresentationSessionInfo sessionInfo,
+            PresentationSessionState newState);
 };
diff --git a/content/common/sandbox_win.cc b/content/common/sandbox_win.cc
index 8849a86a..7749d7b 100644
--- a/content/common/sandbox_win.cc
+++ b/content/common/sandbox_win.cc
@@ -736,15 +736,17 @@
   if (sandbox::SBOX_ALL_OK != result) {
     if (result == sandbox::SBOX_ERROR_GENERIC)
       DPLOG(ERROR) << "Failed to launch process";
-    else
+    else if (result == sandbox::SBOX_ERROR_CREATE_PROCESS) {
+      // TODO(shrikant): Remove this special case handling after determining
+      // cause for lowbox/createprocess errors.
+      sandbox::PolicyBase* policy_base =
+          static_cast<sandbox::PolicyBase*>(policy);
+      if (policy_base->GetLowBoxSid()) {
+        UMA_HISTOGRAM_SPARSE_SLOWLY("Process.Sandbox.Lowbox.Launch.Error",
+                                    last_error);
+      }
+    } else
       DLOG(ERROR) << "Failed to launch process. Error: " << result;
-
-    sandbox::PolicyBase* policy_base =
-        static_cast<sandbox::PolicyBase*>(policy);
-    if (policy_base->GetLowBoxSid()) {
-      UMA_HISTOGRAM_SPARSE_SLOWLY("Process.Sandbox.Lowbox.Launch.Error",
-                                  last_error);
-    }
     return base::Process();
   }
 
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index f060ae88..0412880c 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -29,6 +29,7 @@
     '../ui/gfx/gfx.gyp:gfx_geometry',
     '../ui/resources/ui_resources.gyp:ui_resources',
     '../ui/snapshot/snapshot.gyp:snapshot',
+    'browser/background_sync/background_sync_proto.gyp:background_sync_proto',
     'browser/notifications/notification_proto.gyp:notification_proto',
     'browser/service_worker/service_worker_proto.gyp:service_worker_proto',
     'browser/speech/proto/speech_proto.gyp:speech_proto',
@@ -428,6 +429,8 @@
       'browser/appcache/chrome_appcache_service.h',
       'browser/appcache/view_appcache_internals_job.cc',
       'browser/appcache/view_appcache_internals_job.h',
+      'browser/background_sync/background_sync_manager.cc',
+      'browser/background_sync/background_sync_manager.h',
       'browser/bad_message.cc',
       'browser/bad_message.h',
       'browser/bluetooth/bluetooth_dispatcher_host.cc',
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index 7280846..6ad22239 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -320,8 +320,6 @@
       'renderer/presentation/presentation_dispatcher.h',
       'renderer/presentation/presentation_session_client.cc',
       'renderer/presentation/presentation_session_client.h',
-      'renderer/presentation/presentation_session_dispatcher.cc',
-      'renderer/presentation/presentation_session_dispatcher.h',
       'renderer/push_messaging/push_messaging_dispatcher.cc',
       'renderer/push_messaging/push_messaging_dispatcher.h',
       'renderer/render_font_warmup_win.cc',
diff --git a/content/content_shell.gypi b/content/content_shell.gypi
index 73394901..c592ede 100644
--- a/content/content_shell.gypi
+++ b/content/content_shell.gypi
@@ -500,7 +500,7 @@
           'action_name': 'repack_content_shell_pack',
           'variables': {
             'pak_inputs': [
-              '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources.pak',
+              '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources_100_percent.pak',
               '<(SHARED_INTERMEDIATE_DIR)/content/app/resources/content_resources_100_percent.pak',
               '<(SHARED_INTERMEDIATE_DIR)/content/app/strings/content_strings_en-US.pak',
               '<(SHARED_INTERMEDIATE_DIR)/content/browser/tracing/tracing_resources.pak',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 7f628e6..bb327de0 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -333,6 +333,7 @@
       'browser/appcache/mock_appcache_storage.cc',
       'browser/appcache/mock_appcache_storage.h',
       'browser/appcache/mock_appcache_storage_unittest.cc',
+      'browser/background_sync/background_sync_manager_unittest.cc',
       'browser/browser_thread_unittest.cc',
       'browser/browser_url_handler_impl_unittest.cc',
       'browser/byte_stream_unittest.cc',
@@ -973,6 +974,7 @@
       'target_name': 'content_unittests',
       'type': '<(gtest_target_type)',
       'dependencies': [
+        'browser/background_sync/background_sync_proto.gyp:background_sync_proto',
         'browser/notifications/notification_proto.gyp:notification_proto',
         'browser/service_worker/service_worker_proto.gyp:service_worker_proto',
         'browser/speech/proto/speech_proto.gyp:speech_proto',
@@ -2024,6 +2026,26 @@
           },
           'includes': [ '../build/java_apk.gypi' ],
         },
+        {
+          # GN: //content/public/android:content_shell_unit_tests
+          'target_name': 'content_shell_unit_tests',
+          'type': 'none',
+          'dependencies': [
+            'content.gyp:content_java',
+            '../base/base.gyp:base_java',
+            '../base/base.gyp:base_java_test_support',
+            '../testing/android/junit/junit_test.gyp:junit_test_support',
+          ],
+          'variables': {
+            'main_class': 'org.chromium.testing.local.JunitTestMain',
+            'src_paths': [
+              'public/android/junit/',
+            ],
+          },
+          'includes': [
+            '../build/host_jar.gypi',
+          ],
+        },
       ],
     }],
     ['OS!="android" and OS!="ios" and OS!="linux"', {
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 98c67be3..1114671f 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -178,4 +178,17 @@
 
   DEPRECATED_java_in_dir = "javatests/src"
 }
+
+# GYP: //content/content_tests.gypi:content_shell_unit_tests
+java_binary("chrome_shell_unit_tests") {
+  testonly = true
+  java_files = [ "junit/src/org/chromium/content/browser/input/GamepadMappingsTest.java" ]
+  main_class = "org.chromium.testing.local.JunitTestMain"
+  deps = [
+    ":content_java",
+    "//base:base_java",
+    "//base:base_java_test_support",
+    "//testing/android/junit:junit_test_support",
+  ]
+}
 # TODO(GYP): content_icudata
diff --git a/content/public/android/junit/src/org/chromium/content/browser/input/GamepadMappingsTest.java b/content/public/android/junit/src/org/chromium/content/browser/input/GamepadMappingsTest.java
new file mode 100644
index 0000000..58e16e7
--- /dev/null
+++ b/content/public/android/junit/src/org/chromium/content/browser/input/GamepadMappingsTest.java
@@ -0,0 +1,309 @@
+// 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.content.browser.input;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import org.chromium.base.test.util.Feature;
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.BitSet;
+
+/**
+ * Verify no regressions in gamepad mappings.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class GamepadMappingsTest {
+    private static final float ERROR_TOLERANCE = 0.000001f;
+    /**
+     * Set bits indicate that we don't expect the button at mMappedButtons[index] to be mapped.
+     */
+    private BitSet mUnmappedButtons = new BitSet(CanonicalButtonIndex.COUNT);
+    /**
+     * Set bits indicate that we don't expect the axis at mMappedAxes[index] to be mapped.
+     */
+    private BitSet mUnmappedAxes = new BitSet(CanonicalAxisIndex.COUNT);
+    private float[] mMappedButtons = new float[CanonicalButtonIndex.COUNT];
+    private float[] mMappedAxes = new float[CanonicalAxisIndex.COUNT];
+    private float[] mRawButtons = new float[GamepadDevice.MAX_RAW_BUTTON_VALUES];
+    private float[] mRawAxes = new float[GamepadDevice.MAX_RAW_AXIS_VALUES];
+
+    @Before
+    public void setUp() throws Exception {
+
+        // By default, we expect every button and axis to be mapped.
+        mUnmappedButtons.clear();
+        mUnmappedAxes.clear();
+
+        // Start with all the mapped values as unmapped.
+        Arrays.fill(mMappedButtons, Float.NaN);
+        Arrays.fill(mMappedAxes, Float.NaN);
+
+        // Set each raw value to something unique.
+        for (int i = 0; i < GamepadDevice.MAX_RAW_AXIS_VALUES; i++) {
+            mRawAxes[i] = -i - 1.0f;
+        }
+        for (int i = 0; i < GamepadDevice.MAX_RAW_BUTTON_VALUES; i++) {
+            mRawButtons[i] = i + 1.0f;
+        }
+    }
+
+    @Test
+    @Feature({"Gamepad"})
+    public void testShieldGamepadMappings() throws Exception {
+        GamepadMappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons,
+                GamepadMappings.NVIDIA_SHIELD_DEVICE_NAME_PREFIX);
+
+        assertShieldGamepadMappings();
+    }
+
+    @Test
+    @Feature({"Gamepad"})
+    public void testXBox360GamepadMappings() throws Exception {
+        GamepadMappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons,
+                GamepadMappings.MICROSOFT_XBOX_PAD_DEVICE_NAME);
+
+        assertShieldGamepadMappings();
+    }
+
+    @Test
+    @Feature({"Gamepad"})
+    public void testPS3SixAxisGamepadMappings() throws Exception {
+        GamepadMappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons,
+                GamepadMappings.PS3_SIXAXIS_DEVICE_NAME);
+
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.PRIMARY],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_X], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.SECONDARY],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_Y], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.TERTIARY],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_A], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.QUATERNARY],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_B], ERROR_TOLERANCE);
+
+        assertMappedCommonTriggerButtons();
+        assertMappedCommonThumbstickButtons();
+        assertMappedCommonDpadButtons();
+        assertMappedCommonStartSelectMetaButtons();
+        assertMappedTriggerAxexToShoulderButtons();
+        assertMappedXYAxes();
+        assertMappedZAndRZAxesToRightStick();
+
+        assertMapping();
+    }
+
+    @Test
+    @Feature({"Gamepad"})
+    public void testSamsungEIGP20GamepadMappings() throws Exception {
+        GamepadMappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons,
+                GamepadMappings.SAMSUNG_EI_GP20_DEVICE_NAME);
+
+        assertMappedCommonXYABButtons();
+        assertMappedCommonTriggerButtons();
+        assertMappedCommonThumbstickButtons();
+        assertMappedCommonStartSelectMetaButtons();
+        assertMappedHatAxisToDpadButtons();
+        assertMappedXYAxes();
+        assertMappedRXAndRYAxesToRightStick();
+
+        expectNoShoulderButtons();
+        assertMapping();
+    }
+
+    @Test
+    @Feature({"Gamepad"})
+    public void testAmazonFireGamepadMappings() throws Exception {
+        GamepadMappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons,
+                GamepadMappings.AMAZON_FIRE_DEVICE_NAME);
+
+        assertMappedCommonXYABButtons();
+        assertMappedPedalAxesToBottomShoulder();
+        assertMappedCommonThumbstickButtons();
+        assertMappedCommonStartSelectMetaButtons();
+        assertMappedTriggerButtonsToTopShoulder();
+        assertMappedHatAxisToDpadButtons();
+        assertMappedXYAxes();
+        assertMappedZAndRZAxesToRightStick();
+
+        assertMapping();
+    }
+
+    @Test
+    @Feature({"Gamepad"})
+    public void testUnknownGamepadMappings() throws Exception {
+        GamepadMappings.mapToStandardGamepad(
+                mMappedAxes, mMappedButtons, mRawAxes, mRawButtons, "");
+
+        assertMappedCommonXYABButtons();
+        assertMappedCommonTriggerButtons();
+        assertMappedCommonThumbstickButtons();
+        assertMappedCommonStartSelectMetaButtons();
+        assertMappedTriggerAxexToShoulderButtons();
+        assertMappedCommonDpadButtons();
+        assertMappedXYAxes();
+        assertMappedRXAndRYAxesToRightStick();
+
+        assertMapping();
+    }
+
+    /**
+     * Asserts that the current gamepad mapping being tested matches the shield mappings.
+     */
+    public void assertShieldGamepadMappings() {
+        assertMappedCommonXYABButtons();
+        assertMappedTriggerButtonsToTopShoulder();
+        assertMappedCommonThumbstickButtons();
+        assertMappedCommonStartSelectMetaButtons();
+        assertMappedTriggerAxesToBottomShoulder();
+        assertMappedHatAxisToDpadButtons();
+        assertMappedXYAxes();
+        assertMappedZAndRZAxesToRightStick();
+
+        assertMapping();
+    }
+
+    public void expectNoShoulderButtons() {
+        mUnmappedButtons.set(CanonicalButtonIndex.LEFT_SHOULDER);
+        mUnmappedButtons.set(CanonicalButtonIndex.RIGHT_SHOULDER);
+    }
+
+    public void assertMapping() {
+        for (int i = 0; i < mMappedAxes.length; i++) {
+            if (mUnmappedAxes.get(i)) {
+                Assert.assertTrue(
+                        "An unexpected axis was mapped at index " + i, Float.isNaN(mMappedAxes[i]));
+            } else {
+                Assert.assertFalse(
+                        "An axis was not mapped at index " + i, Float.isNaN(mMappedAxes[i]));
+            }
+        }
+        for (int i = 0; i < mMappedButtons.length; i++) {
+            if (mUnmappedButtons.get(i)) {
+                Assert.assertTrue("An unexpected button was mapped at index " + i,
+                        Float.isNaN(mMappedButtons[i]));
+            } else {
+                Assert.assertFalse(
+                        "A button was not mapped at index " + i, Float.isNaN(mMappedButtons[i]));
+            }
+        }
+    }
+
+    private void assertMappedCommonTriggerButtons() {
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_TRIGGER],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_L1], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.RIGHT_TRIGGER],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_R1], ERROR_TOLERANCE);
+    }
+
+    private void assertMappedCommonDpadButtons() {
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_DOWN],
+                mRawButtons[KeyEvent.KEYCODE_DPAD_DOWN], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_UP],
+                mRawButtons[KeyEvent.KEYCODE_DPAD_UP], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_LEFT],
+                mRawButtons[KeyEvent.KEYCODE_DPAD_LEFT], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_RIGHT],
+                mRawButtons[KeyEvent.KEYCODE_DPAD_RIGHT], ERROR_TOLERANCE);
+    }
+
+    private void assertMappedTriggerAxexToShoulderButtons() {
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_SHOULDER],
+                mRawAxes[MotionEvent.AXIS_LTRIGGER], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.RIGHT_SHOULDER],
+                mRawAxes[MotionEvent.AXIS_RTRIGGER], ERROR_TOLERANCE);
+    }
+
+    private void assertMappedTriggerButtonsToTopShoulder() {
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_SHOULDER],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_L1], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.RIGHT_SHOULDER],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_R1], ERROR_TOLERANCE);
+    }
+
+    private void assertMappedCommonXYABButtons() {
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.PRIMARY],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_A], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.SECONDARY],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_B], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.TERTIARY],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_X], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.QUATERNARY],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_Y], ERROR_TOLERANCE);
+    }
+
+    private void assertMappedCommonThumbstickButtons() {
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_THUMBSTICK],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_THUMBL], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.RIGHT_THUMBSTICK],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_THUMBR], ERROR_TOLERANCE);
+    }
+
+    private void assertMappedCommonStartSelectMetaButtons() {
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.START],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_START], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.BACK_SELECT],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_SELECT], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.META],
+                mRawButtons[KeyEvent.KEYCODE_BUTTON_MODE], ERROR_TOLERANCE);
+    }
+
+    private void assertMappedPedalAxesToBottomShoulder() {
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_TRIGGER],
+                mRawAxes[MotionEvent.AXIS_BRAKE], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.RIGHT_TRIGGER],
+                mRawAxes[MotionEvent.AXIS_GAS], ERROR_TOLERANCE);
+    }
+
+    private void assertMappedTriggerAxesToBottomShoulder() {
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_TRIGGER],
+                mRawAxes[MotionEvent.AXIS_LTRIGGER], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.RIGHT_TRIGGER],
+                mRawAxes[MotionEvent.AXIS_RTRIGGER], ERROR_TOLERANCE);
+    }
+
+    private void assertMappedHatAxisToDpadButtons() {
+        float hatX = mRawAxes[MotionEvent.AXIS_HAT_X];
+        float hatY = mRawAxes[MotionEvent.AXIS_HAT_Y];
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_LEFT],
+                GamepadMappings.negativeAxisValueAsButton(hatX), ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_RIGHT],
+                GamepadMappings.positiveAxisValueAsButton(hatX), ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_UP],
+                GamepadMappings.negativeAxisValueAsButton(hatY), ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_DOWN],
+                GamepadMappings.positiveAxisValueAsButton(hatY), ERROR_TOLERANCE);
+    }
+
+    private void assertMappedXYAxes() {
+        Assert.assertEquals(mMappedAxes[CanonicalAxisIndex.LEFT_STICK_X],
+                mRawAxes[MotionEvent.AXIS_X], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedAxes[CanonicalAxisIndex.LEFT_STICK_Y],
+                mRawAxes[MotionEvent.AXIS_Y], ERROR_TOLERANCE);
+    }
+
+    private void assertMappedRXAndRYAxesToRightStick() {
+        Assert.assertEquals(mMappedAxes[CanonicalAxisIndex.RIGHT_STICK_X],
+                mRawAxes[MotionEvent.AXIS_RX], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedAxes[CanonicalAxisIndex.RIGHT_STICK_Y],
+                mRawAxes[MotionEvent.AXIS_RY], ERROR_TOLERANCE);
+    }
+
+    private void assertMappedZAndRZAxesToRightStick() {
+        Assert.assertEquals(mMappedAxes[CanonicalAxisIndex.RIGHT_STICK_X],
+                mRawAxes[MotionEvent.AXIS_Z], ERROR_TOLERANCE);
+        Assert.assertEquals(mMappedAxes[CanonicalAxisIndex.RIGHT_STICK_Y],
+                mRawAxes[MotionEvent.AXIS_RZ], ERROR_TOLERANCE);
+    }
+}
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 180e819..6d437f7 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -16,7 +16,6 @@
 #include "base/memory/scoped_vector.h"
 #include "base/values.h"
 #include "content/public/browser/certificate_request_result_type.h"
-#include "content/public/browser/permission_type.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/media_stream_request.h"
 #include "content/public/common/permission_status.mojom.h"
@@ -81,6 +80,7 @@
 
 namespace content {
 
+enum class PermissionType;
 class AccessTokenStore;
 class BrowserChildProcessHost;
 class BrowserContext;
diff --git a/content/public/browser/permission_type.h b/content/public/browser/permission_type.h
index c6e5c65..4fb062c 100644
--- a/content/public/browser/permission_type.h
+++ b/content/public/browser/permission_type.h
@@ -11,16 +11,16 @@
 // the UMA guidelines.
 // Make sure you update histograms.xml if you add new permission types.
 // Never delete or reorder an entry; only add new entries
-// immediately before PERMISSION_NUM
-enum PermissionType {
-  PERMISSION_MIDI_SYSEX = 1,
-  PERMISSION_PUSH_MESSAGING = 2,
-  PERMISSION_NOTIFICATIONS = 3,
-  PERMISSION_GEOLOCATION = 4,
-  PERMISSION_PROTECTED_MEDIA_IDENTIFIER = 5,
+// immediately before PermissionType::NUM
+enum class PermissionType {
+  MIDI_SYSEX = 1,
+  PUSH_MESSAGING = 2,
+  NOTIFICATIONS = 3,
+  GEOLOCATION = 4,
+  PROTECTED_MEDIA_IDENTIFIER = 5,
 
   // Always keep this at the end.
-  PERMISSION_NUM,
+  NUM,
 };
 
 }  // namespace content
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h
index ed3724e..7f747e8 100644
--- a/content/public/browser/render_process_host.h
+++ b/content/public/browser/render_process_host.h
@@ -189,7 +189,9 @@
   virtual void AddPendingView() = 0;
   virtual void RemovePendingView() = 0;
 
-  // Returns true if the process can be immediately terminated.
+  // Sets a flag indicating that the process can be abnormally terminated.
+  virtual void SetSuddenTerminationAllowed(bool allowed) = 0;
+  // Returns true if the process can be abnormally terminated.
   virtual bool SuddenTerminationAllowed() const = 0;
 
   // Returns how long the child has been idle. The definition of idle
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h
index a176d18..737b16a 100644
--- a/content/public/browser/web_contents_observer.h
+++ b/content/public/browser/web_contents_observer.h
@@ -343,6 +343,12 @@
   // Invoked when theme color is changed to |theme_color|.
   virtual void DidChangeThemeColor(SkColor theme_color) {}
 
+  // Invoked when media is playing.
+  virtual void MediaStartedPlaying() {}
+
+  // Invoked when media is paused.
+  virtual void MediaPaused() {}
+
   // Invoked if an IPC message is coming from a specific RenderFrameHost.
   virtual bool OnMessageReceived(const IPC::Message& message,
                                  RenderFrameHost* render_frame_host);
diff --git a/content/public/common/OWNERS b/content/public/common/OWNERS
index 86e4104..95aabc6 100644
--- a/content/public/common/OWNERS
+++ b/content/public/common/OWNERS
@@ -13,6 +13,10 @@
 per-file *param_traits*.h=tsepez@chromium.org
 per-file *param_traits*.h=wfh@chromium.org
 
+# DirectWrite
+per-file dwrite_font_platform_win.h=shrikant@chromium.org
+per-file dwrite_font_platform_win.h=scottmg@chromium.org
+
 # Changes to Mojo interfaces require a security review to avoid
 # introducing new sandbox escapes.
 per-file *.mojom=set noparent
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 69d0941..18b8884 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -916,6 +916,9 @@
 // Disable the pull-to-refresh effect when vertically overscrolling content.
 const char kDisablePullToRefreshEffect[]   = "disable-pull-to-refresh-effect";
 
+// Disable the locking feature of the screen orientation API.
+const char kDisableScreenOrientationLock[]  = "disable-screen-orientation-lock";
+
 // WebRTC is enabled by default on Android.
 const char kDisableWebRTC[]                 = "disable-webrtc";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 77a28c2..1449dbfa 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -259,6 +259,7 @@
 CONTENT_EXPORT extern const char kDisableClickDelay[];
 CONTENT_EXPORT extern const char kDisableOverscrollEdgeEffect[];
 CONTENT_EXPORT extern const char kDisablePullToRefreshEffect[];
+CONTENT_EXPORT extern const char kDisableScreenOrientationLock[];
 CONTENT_EXPORT extern const char kDisableWebRTC[];
 CONTENT_EXPORT extern const char kHideScrollbars[];
 extern const char kNetworkCountryIso[];
diff --git a/content/public/common/context_menu_params.cc b/content/public/common/context_menu_params.cc
index 5381702b..54b48c20 100644
--- a/content/public/common/context_menu_params.cc
+++ b/content/public/common/context_menu_params.cc
@@ -20,6 +20,7 @@
       y(0),
       has_image_contents(true),
       media_flags(0),
+      misspelling_hash(0),
       spellcheck_enabled(false),
       is_editable(false),
       writing_direction_default(
diff --git a/content/public/common/context_menu_params.h b/content/public/common/context_menu_params.h
index ea8d90fe..9fe170a0 100644
--- a/content/public/common/context_menu_params.h
+++ b/content/public/common/context_menu_params.h
@@ -112,6 +112,9 @@
   // |dictionary_suggestions| list.
   base::string16 misspelled_word;
 
+  // The identifier of the misspelling under the cursor, if any.
+  uint32 misspelling_hash;
+
   // Suggested replacements for a misspelled word under the cursor.
   // This vector gets populated in the render process host
   // by intercepting ViewHostMsg_ContextMenu in ResourceMessageFilter
diff --git a/content/public/common/renderer_preferences.h b/content/public/common/renderer_preferences.h
index b76b37b..4170932 100644
--- a/content/public/common/renderer_preferences.h
+++ b/content/public/common/renderer_preferences.h
@@ -54,7 +54,7 @@
   bool use_bitmaps;
 
   // The type of subpixel rendering to use for text.
-  // Currently only used by Linux.
+  // Currently only used by Linux and Windows.
   gfx::FontRenderParams::SubpixelRendering subpixel_rendering;
 
   // Whether subpixel positioning should be used, permitting fractional X
diff --git a/content/public/renderer/plugin_instance_throttler.h b/content/public/renderer/plugin_instance_throttler.h
index f9c5b92..d21acaf 100644
--- a/content/public/renderer/plugin_instance_throttler.h
+++ b/content/public/renderer/plugin_instance_throttler.h
@@ -64,7 +64,7 @@
     virtual void OnThrottlerDestroyed() {}
   };
 
-  static scoped_ptr<PluginInstanceThrottler> Create(bool power_saver_enabled);
+  static scoped_ptr<PluginInstanceThrottler> Create();
 
   static void RecordUnthrottleMethodMetric(PowerSaverUnthrottleMethod method);
 
diff --git a/content/public/renderer/render_view_observer.h b/content/public/renderer/render_view_observer.h
index 6952735..966a72c 100644
--- a/content/public/renderer/render_view_observer.h
+++ b/content/public/renderer/render_view_observer.h
@@ -73,8 +73,6 @@
       blink::WebLocalFrame* frame,
       const blink::WebVector<blink::WebString>& newly_matching_selectors,
       const blink::WebVector<blink::WebString>& stopped_matching_selectors) {}
-  virtual void DidCreateDataSource(blink::WebLocalFrame* frame,
-                                   blink::WebDataSource* ds) {}
   virtual void PrintPage(blink::WebLocalFrame* frame, bool user_initiated) {}
   virtual void FocusedNodeChanged(const blink::WebNode& node) {}
   virtual void DraggableRegionsChanged(blink::WebFrame* frame) {}
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index 6eca39a..a8d3afb4 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -172,6 +172,9 @@
 void MockRenderProcessHost::RemovePendingView() {
 }
 
+void MockRenderProcessHost::SetSuddenTerminationAllowed(bool allowed) {
+}
+
 bool MockRenderProcessHost::SuddenTerminationAllowed() const {
   return true;
 }
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h
index d382acf..a2618b7 100644
--- a/content/public/test/mock_render_process_host.h
+++ b/content/public/test/mock_render_process_host.h
@@ -59,6 +59,7 @@
   void Cleanup() override;
   void AddPendingView() override;
   void RemovePendingView() override;
+  void SetSuddenTerminationAllowed(bool allowed) override;
   bool SuddenTerminationAllowed() const override;
   BrowserContext* GetBrowserContext() const override;
   bool InSameStoragePartition(StoragePartition* partition) const override;
diff --git a/content/public/test/mock_special_storage_policy.cc b/content/public/test/mock_special_storage_policy.cc
index 6704f61..ff3c6c4 100644
--- a/content/public/test/mock_special_storage_policy.cc
+++ b/content/public/test/mock_special_storage_policy.cc
@@ -30,10 +30,6 @@
   return ContainsKey(can_query_disk_size_, origin);
 }
 
-bool MockSpecialStoragePolicy::IsFileHandler(const std::string& extension_id) {
-  return ContainsKey(file_handlers_, extension_id);
-}
-
 bool MockSpecialStoragePolicy::HasIsolatedStorage(const GURL& origin) {
   return ContainsKey(isolated_, origin);
 }
diff --git a/content/public/test/mock_special_storage_policy.h b/content/public/test/mock_special_storage_policy.h
index 2d7ce62f..1220f65 100644
--- a/content/public/test/mock_special_storage_policy.h
+++ b/content/public/test/mock_special_storage_policy.h
@@ -23,7 +23,6 @@
   bool IsStorageUnlimited(const GURL& origin) override;
   bool IsStorageSessionOnly(const GURL& origin) override;
   bool CanQueryDiskSize(const GURL& origin) override;
-  bool IsFileHandler(const std::string& extension_id) override;
   bool HasIsolatedStorage(const GURL& origin) override;
   bool HasSessionOnlyOrigins() override;
 
@@ -47,10 +46,6 @@
     can_query_disk_size_.insert(origin);
   }
 
-  void AddFileHandler(const std::string& id) {
-    file_handlers_.insert(id);
-  }
-
   void AddIsolated(const GURL& origin) {
     isolated_.insert(origin);
   }
diff --git a/content/renderer/context_menu_params_builder.cc b/content/renderer/context_menu_params_builder.cc
index a4a37d5..63dcd4d 100644
--- a/content/renderer/context_menu_params_builder.cc
+++ b/content/renderer/context_menu_params_builder.cc
@@ -32,6 +32,7 @@
   params.media_flags = data.mediaFlags;
   params.selection_text = data.selectedText;
   params.misspelled_word = data.misspelledWord;
+  params.misspelling_hash = data.misspellingHash;
   params.spellcheck_enabled = data.isSpellCheckingEnabled;
   params.is_editable = data.isEditable;
   params.writing_direction_default = data.writingDirectionDefault;
diff --git a/content/renderer/devtools/devtools_agent.cc b/content/renderer/devtools/devtools_agent.cc
index dca9b863..357a47f 100644
--- a/content/renderer/devtools/devtools_agent.cc
+++ b/content/renderer/devtools/devtools_agent.cc
@@ -8,25 +8,20 @@
 
 #include "base/lazy_instance.h"
 #include "base/message_loop/message_loop.h"
-#include "base/process/process_handle.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "content/common/devtools_messages.h"
 #include "content/common/frame_messages.h"
 #include "content/common/view_messages.h"
-#include "content/renderer/devtools/devtools_agent_filter.h"
 #include "content/renderer/devtools/devtools_client.h"
-#include "content/renderer/render_view_impl.h"
+#include "content/renderer/render_frame_impl.h"
+#include "content/renderer/render_widget.h"
 #include "ipc/ipc_channel.h"
 #include "third_party/WebKit/public/platform/WebPoint.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
-#include "third_party/WebKit/public/web/WebConsoleMessage.h"
 #include "third_party/WebKit/public/web/WebDevToolsAgent.h"
-#include "third_party/WebKit/public/web/WebDeviceEmulationParams.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
-#include "third_party/WebKit/public/web/WebSettings.h"
-#include "third_party/WebKit/public/web/WebView.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 
 #if defined(USE_TCMALLOC)
 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
@@ -35,20 +30,15 @@
 using blink::WebConsoleMessage;
 using blink::WebDevToolsAgent;
 using blink::WebDevToolsAgentClient;
-using blink::WebFrame;
+using blink::WebLocalFrame;
 using blink::WebPoint;
 using blink::WebString;
-using blink::WebCString;
-using blink::WebVector;
-using blink::WebView;
 
 using base::trace_event::TraceLog;
 using base::trace_event::TraceOptions;
 
 namespace content {
 
-base::subtle::AtomicWord DevToolsAgent::event_callback_;
-
 namespace {
 
 const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
@@ -75,21 +65,18 @@
 
 } //  namespace
 
-DevToolsAgent::DevToolsAgent(RenderFrame* main_render_frame)
-    : RenderFrameObserver(main_render_frame),
+DevToolsAgent::DevToolsAgent(RenderFrameImpl* frame)
+    : RenderFrameObserver(frame),
       is_attached_(false),
       is_devtools_client_(false),
       paused_in_mouse_move_(false),
-      main_render_frame_(main_render_frame) {
+      frame_(frame) {
   g_agent_for_routing_id.Get()[routing_id()] = this;
-
-  main_render_frame_->GetRenderView()->GetWebView()->setDevToolsAgentClient(
-      this);
+  frame_->GetWebFrame()->setDevToolsAgentClient(this);
 }
 
 DevToolsAgent::~DevToolsAgent() {
   g_agent_for_routing_id.Get().erase(routing_id());
-  resetTraceEventCallback();
 }
 
 // Called on the Renderer thread.
@@ -123,51 +110,25 @@
       this, routing_id(), call_id, message.utf8(), state_cookie.utf8());
 }
 
-long DevToolsAgent::processId() {
-  return base::GetCurrentProcId();
-}
-
-int DevToolsAgent::debuggerId() {
-  return routing_id();
-}
-
 blink::WebDevToolsAgentClient::WebKitClientMessageLoop*
     DevToolsAgent::createClientMessageLoop() {
   return new WebKitClientMessageLoopImpl();
 }
 
 void DevToolsAgent::willEnterDebugLoop() {
-  paused_in_mouse_move_ =
-      GetRenderViewImpl()->SendAckForMouseMoveFromDebugger();
+  if (RenderWidget* widget = frame_->GetRenderWidget())
+    paused_in_mouse_move_ = widget->SendAckForMouseMoveFromDebugger();
 }
 
 void DevToolsAgent::didExitDebugLoop() {
-  if (paused_in_mouse_move_) {
-    GetRenderViewImpl()->IgnoreAckForMouseMoveFromDebugger();
+  if (!paused_in_mouse_move_)
+    return;
+  if (RenderWidget* widget = frame_->GetRenderWidget()) {
+    widget->IgnoreAckForMouseMoveFromDebugger();
     paused_in_mouse_move_ = false;
   }
 }
 
-void DevToolsAgent::resetTraceEventCallback()
-{
-  TraceLog::GetInstance()->SetEventCallbackDisabled();
-  base::subtle::NoBarrier_Store(&event_callback_, 0);
-}
-
-void DevToolsAgent::setTraceEventCallback(const WebString& category_filter,
-                                          TraceEventCallback cb) {
-  TraceLog* trace_log = TraceLog::GetInstance();
-  base::subtle::NoBarrier_Store(&event_callback_,
-                                reinterpret_cast<base::subtle::AtomicWord>(cb));
-  if (!!cb) {
-    trace_log->SetEventCallbackEnabled(
-        base::trace_event::CategoryFilter(category_filter.utf8()),
-        TraceEventCallbackWrapper);
-  } else {
-    trace_log->SetEventCallbackDisabled();
-  }
-}
-
 void DevToolsAgent::enableTracing(const WebString& category_filter) {
   TraceLog* trace_log = TraceLog::GetInstance();
   trace_log->SetEnabled(
@@ -180,28 +141,6 @@
 }
 
 // static
-void DevToolsAgent::TraceEventCallbackWrapper(
-    base::TimeTicks timestamp,
-    char phase,
-    const unsigned char* category_group_enabled,
-    const char* name,
-    unsigned long long id,
-    int num_args,
-    const char* const arg_names[],
-    const unsigned char arg_types[],
-    const unsigned long long arg_values[],
-    unsigned char flags) {
-  TraceEventCallback callback =
-      reinterpret_cast<TraceEventCallback>(
-          base::subtle::NoBarrier_Load(&event_callback_));
-  if (callback) {
-    double timestamp_seconds = (timestamp - base::TimeTicks()).InSecondsF();
-    callback(phase, category_group_enabled, name, id, num_args,
-             arg_names, arg_types, arg_values, flags, timestamp_seconds);
-  }
-}
-
-// static
 DevToolsAgent* DevToolsAgent::FromRoutingId(int routing_id) {
   IdToAgentMap::iterator it = g_agent_for_routing_id.Get().find(routing_id);
   if (it != g_agent_for_routing_id.Get().end()) {
@@ -288,12 +227,8 @@
 
 void DevToolsAgent::OnAddMessageToConsole(ConsoleMessageLevel level,
                                           const std::string& message) {
-  WebView* web_view = main_render_frame_->GetRenderView()->GetWebView();
-  if (!web_view)
-    return;
-
-  WebFrame* main_frame = web_view->mainFrame();
-  if (!main_frame)
+  WebLocalFrame* web_frame = frame_->GetWebFrame();
+  if (!web_frame)
     return;
 
   WebConsoleMessage::Level target_level = WebConsoleMessage::LevelLog;
@@ -311,7 +246,7 @@
       target_level = WebConsoleMessage::LevelError;
       break;
   }
-  main_frame->addMessageToConsole(
+  web_frame->addMessageToConsole(
       WebConsoleMessage(target_level, WebString::fromUTF8(message)));
 }
 
@@ -322,22 +257,17 @@
 }
 
 void DevToolsAgent::OnSetupDevToolsClient() {
-  // We only want to register once per render view.
+  // We only want to register once; and only in main frame.
+  DCHECK(!frame_->GetWebFrame() || !frame_->GetWebFrame()->parent());
   if (is_devtools_client_)
     return;
   is_devtools_client_ = true;
-  new DevToolsClient(main_render_frame_);
+  new DevToolsClient(frame_);
 }
 
 WebDevToolsAgent* DevToolsAgent::GetWebAgent() {
-  WebView* web_view = main_render_frame_->GetRenderView()->GetWebView();
-  if (!web_view)
-    return NULL;
-  return web_view->devToolsAgent();
-}
-
-RenderViewImpl* DevToolsAgent::GetRenderViewImpl() {
-  return static_cast<RenderViewImpl*>(main_render_frame_->GetRenderView());
+  WebLocalFrame* web_frame = frame_->GetWebFrame();
+  return web_frame ? web_frame->devToolsAgent() : nullptr;
 }
 
 bool DevToolsAgent::IsAttached() {
diff --git a/content/renderer/devtools/devtools_agent.h b/content/renderer/devtools/devtools_agent.h
index e2f9531..9db85c4 100644
--- a/content/renderer/devtools/devtools_agent.h
+++ b/content/renderer/devtools/devtools_agent.h
@@ -7,9 +7,6 @@
 
 #include <string>
 
-#include "base/atomicops.h"
-#include "base/basictypes.h"
-#include "base/time/time.h"
 #include "content/public/common/console_message_level.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "third_party/WebKit/public/web/WebDevToolsAgentClient.h"
@@ -20,16 +17,15 @@
 
 namespace content {
 
-class RenderViewImpl;
+class RenderFrameImpl;
 
-// DevToolsAgent belongs to the inspectable RenderView and provides Glue's
-// agents with the communication capabilities. All messages from/to Glue's
-// agents infrastructure are flowing through this communication agent.
-// There is a corresponding DevToolsClient object on the client side.
+// DevToolsAgent belongs to the inspectable RenderFrameImpl and communicates
+// with WebDevToolsAgent. There is a corresponding DevToolsAgentHost
+// on the browser side.
 class DevToolsAgent : public RenderFrameObserver,
                       public blink::WebDevToolsAgentClient {
  public:
-  explicit DevToolsAgent(RenderFrame* main_render_frame);
+  explicit DevToolsAgent(RenderFrameImpl* frame);
   ~DevToolsAgent() override;
 
   // Returns agent instance for its routing id.
@@ -47,28 +43,18 @@
   bool IsAttached();
 
  private:
-  // RenderView::Observer implementation.
+  // RenderFrameObserver implementation.
   bool OnMessageReceived(const IPC::Message& message) override;
 
-  // WebDevToolsAgentClient implementation
+  // WebDevToolsAgentClient implementation.
   void sendProtocolMessage(int call_id,
                            const blink::WebString& response,
                            const blink::WebString& state) override;
-  long processId() override;
-  int debuggerId() override;
   blink::WebDevToolsAgentClient::WebKitClientMessageLoop*
       createClientMessageLoop() override;
   void willEnterDebugLoop() override;
   void didExitDebugLoop() override;
 
-  typedef void (*TraceEventCallback)(
-      char phase, const unsigned char*, const char* name, unsigned long long id,
-      int numArgs, const char* const* argNames, const unsigned char* argTypes,
-      const unsigned long long* argValues,
-      unsigned char flags, double timestamp);
-  void resetTraceEventCallback() override;
-  void setTraceEventCallback(const blink::WebString& category_filter,
-                             TraceEventCallback cb) override;
   void enableTracing(const blink::WebString& category_filter) override;
   void disableTracing() override;
 
@@ -83,26 +69,10 @@
   void ContinueProgram();
   void OnSetupDevToolsClient();
 
-  RenderViewImpl* GetRenderViewImpl();
-
-  static void TraceEventCallbackWrapper(
-      base::TimeTicks timestamp,
-      char phase,
-      const unsigned char* category_group_enabled,
-      const char* name,
-      unsigned long long id,
-      int num_args,
-      const char* const arg_names[],
-      const unsigned char arg_types[],
-      const unsigned long long arg_values[],
-      unsigned char flags);
-
   bool is_attached_;
   bool is_devtools_client_;
   bool paused_in_mouse_move_;
-  RenderFrame* main_render_frame_;
-
-  static base::subtle::AtomicWord /* TraceEventCallback */ event_callback_;
+  RenderFrameImpl* frame_;
 
   DISALLOW_COPY_AND_ASSIGN(DevToolsAgent);
 };
diff --git a/content/renderer/devtools/v8_sampling_profiler.cc b/content/renderer/devtools/v8_sampling_profiler.cc
index 8f3821b..df8fdd2 100644
--- a/content/renderer/devtools/v8_sampling_profiler.cc
+++ b/content/renderer/devtools/v8_sampling_profiler.cc
@@ -9,15 +9,16 @@
 #define USE_SIGNALS
 #endif
 
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
 #include "base/format_macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/cancellation_flag.h"
 #include "base/threading/platform_thread.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_argument.h"
-#if defined(OS_WIN)
-#include "base/win/scoped_handle.h"
-#endif
 #include "content/renderer/devtools/lock_free_circular_queue.h"
 #include "content/renderer/render_thread_impl.h"
 #include "v8/include/v8.h"
@@ -31,10 +32,55 @@
 
 namespace {
 
-#if defined(OS_WIN)
-typedef base::win::ScopedHandle UniversalThreadHandle;
-#else
-typedef base::PlatformThreadHandle UniversalThreadHandle;
+class PlatformDataCommon {
+ public:
+  base::PlatformThreadId thread_id() { return thread_id_; }
+
+ protected:
+  PlatformDataCommon() : thread_id_(base::PlatformThread::CurrentId()) {}
+
+ private:
+  base::PlatformThreadId thread_id_;
+};
+
+#if defined(USE_SIGNALS)
+
+class PlatformData : public PlatformDataCommon {
+ public:
+  PlatformData() : vm_tid_(pthread_self()) {}
+  pthread_t vm_tid() const { return vm_tid_; }
+
+ private:
+  pthread_t vm_tid_;
+};
+
+#elif defined(OS_WIN)
+
+class PlatformData : public PlatformDataCommon {
+ public:
+  // Get a handle to the calling thread. This is the thread that we are
+  // going to profile. We need to make a copy of the handle because we are
+  // going to use it in the sampler thread. Using GetThreadHandle() will
+  // not work in this case. We're using OpenThread because DuplicateHandle
+  // doesn't work in Chrome's sandbox.
+  PlatformData()
+      : thread_handle_(::OpenThread(THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME |
+                                        THREAD_QUERY_INFORMATION,
+                                    false,
+                                    ::GetCurrentThreadId())) {}
+
+  ~PlatformData() {
+    if (thread_handle_ == NULL)
+      return;
+    ::CloseHandle(thread_handle_);
+    thread_handle_ = NULL;
+  }
+
+  HANDLE thread_handle() { return thread_handle_; }
+
+ private:
+  HANDLE thread_handle_;
+};
 #endif
 
 std::string PtrToString(const void* value) {
@@ -145,15 +191,13 @@
   static void HandleJitCodeEvent(const v8::JitCodeEvent* event);
   static scoped_refptr<ConvertableToTraceFormat> JitCodeEventToTraceFormat(
       const v8::JitCodeEvent* event);
-  static UniversalThreadHandle GetCurrentThreadHandle();
 
   void InjectPendingEvents();
 
   static const unsigned kNumberOfSamples = 10;
   typedef LockFreeCircularQueue<SampleRecord, kNumberOfSamples> SamplingQueue;
 
-  base::PlatformThreadId thread_id_;
-  UniversalThreadHandle thread_handle_;
+  PlatformData platform_data_;
   Isolate* isolate_;
   scoped_ptr<SamplingQueue> samples_data_;
   base::subtle::Atomic32 code_added_events_count_;
@@ -169,9 +213,7 @@
     Sampler::tls_instance_ = LAZY_INSTANCE_INITIALIZER;
 
 Sampler::Sampler()
-    : thread_id_(base::PlatformThread::CurrentId()),
-      thread_handle_(Sampler::GetCurrentThreadHandle()),
-      isolate_(Isolate::GetCurrent()),
+    : isolate_(Isolate::GetCurrent()),
       code_added_events_count_(0),
       samples_count_(0),
       code_added_events_to_collect_for_test_(0),
@@ -191,17 +233,6 @@
   return scoped_ptr<Sampler>(new Sampler());
 }
 
-// static
-UniversalThreadHandle Sampler::GetCurrentThreadHandle() {
-#ifdef OS_WIN
-  return base::win::ScopedHandle(::OpenThread(
-      THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
-      false, base::PlatformThread::CurrentId()));
-#else
-  return base::PlatformThread::CurrentHandle();
-#endif
-}
-
 void Sampler::Start() {
   samples_data_.reset(new SamplingQueue());
   v8::JitCodeEventHandler handler = &HandleJitCodeEvent;
@@ -223,12 +254,12 @@
 void Sampler::Sample() {
 #if defined(OS_WIN)
   const DWORD kSuspendFailed = static_cast<DWORD>(-1);
-  if (::SuspendThread(thread_handle_.Get()) == kSuspendFailed)
+  if (::SuspendThread(platform_data_.thread_handle()) == kSuspendFailed)
     return;
   CONTEXT context;
   memset(&context, 0, sizeof(context));
   context.ContextFlags = CONTEXT_FULL;
-  if (::GetThreadContext(thread_handle_.Get(), &context) != 0) {
+  if (::GetThreadContext(platform_data_.thread_handle(), &context) != 0) {
     v8::RegisterState state;
     state.pc = reinterpret_cast<void*>(context.REG_64_32(Rip, Eip));
     state.sp = reinterpret_cast<void*>(context.REG_64_32(Rsp, Esp));
@@ -237,9 +268,9 @@
     // We can just collect and fire trace event right away.
     DoSample(state);
   }
-  ::ResumeThread(thread_handle_.Get());
+  ::ResumeThread(platform_data_.thread_handle());
 #elif defined(USE_SIGNALS)
-  int error = pthread_kill(thread_handle_.platform_handle(), SIGPROF);
+  int error = pthread_kill(platform_data_.vm_tid(), SIGPROF);
   if (error) {
     LOG(ERROR) << "pthread_kill failed with error " << error << " "
                << strerror(error);
@@ -263,7 +294,8 @@
   SampleRecord* record = samples_data_->Peek();
   while (record) {
     TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP1(
-        TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), "V8Sample", thread_id_,
+        TRACE_DISABLED_BY_DEFAULT("v8.cpu_profile"), "V8Sample",
+        platform_data_.thread_id(),
         (record->timestamp() - base::TimeTicks()).InMicroseconds(), "data",
         record->ToTraceFormat());
     samples_data_->Remove();
diff --git a/content/renderer/history_controller.cc b/content/renderer/history_controller.cc
index d6f1abd..81a78e45 100644
--- a/content/renderer/history_controller.cc
+++ b/content/renderer/history_controller.cc
@@ -35,6 +35,7 @@
 
 #include "content/renderer/history_controller.h"
 
+#include "content/common/navigation_params.h"
 #include "content/renderer/render_frame_impl.h"
 #include "content/renderer/render_view_impl.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
@@ -53,12 +54,15 @@
 HistoryController::~HistoryController() {
 }
 
-void HistoryController::GoToEntry(scoped_ptr<HistoryEntry> target_entry,
-                                  WebURLRequest::CachePolicy cache_policy) {
+void HistoryController::GoToEntry(
+    scoped_ptr<HistoryEntry> target_entry,
+    scoped_ptr<NavigationParams> navigation_params,
+    WebURLRequest::CachePolicy cache_policy) {
   HistoryFrameLoadVector same_document_loads;
   HistoryFrameLoadVector different_document_loads;
 
   provisional_entry_ = target_entry.Pass();
+  navigation_params_ = navigation_params.Pass();
 
   WebFrame* main_frame = render_view_->GetMainRenderFrame()->GetWebFrame();
   if (current_entry_) {
@@ -78,19 +82,25 @@
         std::make_pair(main_frame, provisional_entry_->root()));
   }
 
-  for (size_t i = 0; i < same_document_loads.size(); ++i) {
-    WebFrame* frame = same_document_loads[i].first;
-    if (!RenderFrameImpl::FromWebFrame(frame))
+  for (const auto& item : same_document_loads) {
+    WebFrame* frame = item.first;
+    RenderFrameImpl* render_frame = RenderFrameImpl::FromWebFrame(frame);
+    if (!render_frame)
       continue;
-    frame->loadHistoryItem(same_document_loads[i].second,
+    render_frame->SetPendingNavigationParams(make_scoped_ptr(
+        new NavigationParams(*navigation_params_.get())));
+    frame->loadHistoryItem(item.second,
                            blink::WebHistorySameDocumentLoad,
                            cache_policy);
   }
-  for (size_t i = 0; i < different_document_loads.size(); ++i) {
-    WebFrame* frame = different_document_loads[i].first;
-    if (!RenderFrameImpl::FromWebFrame(frame))
+  for (const auto& item : different_document_loads) {
+    WebFrame* frame = item.first;
+    RenderFrameImpl* render_frame = RenderFrameImpl::FromWebFrame(frame);
+    if (!render_frame)
       continue;
-    frame->loadHistoryItem(different_document_loads[i].second,
+    render_frame->SetPendingNavigationParams(make_scoped_ptr(
+        new NavigationParams(*navigation_params_.get())));
+    frame->loadHistoryItem(item.second,
                            blink::WebHistoryDifferentDocumentLoad,
                            cache_policy);
   }
@@ -166,6 +176,11 @@
 
 WebHistoryItem HistoryController::GetItemForNewChildFrame(
     RenderFrameImpl* frame) const {
+  if (navigation_params_.get()) {
+    frame->SetPendingNavigationParams(make_scoped_ptr(
+        new NavigationParams(*navigation_params_.get())));
+  }
+
   if (!current_entry_)
     return WebHistoryItem();
   return current_entry_->GetItemForFrame(frame);
diff --git a/content/renderer/history_controller.h b/content/renderer/history_controller.h
index 97969318..0a46eef 100644
--- a/content/renderer/history_controller.h
+++ b/content/renderer/history_controller.h
@@ -51,6 +51,7 @@
 namespace content {
 class RenderFrameImpl;
 class RenderViewImpl;
+struct NavigationParams;
 
 // A guide to history state in the renderer:
 //
@@ -110,6 +111,7 @@
   ~HistoryController();
 
   void GoToEntry(scoped_ptr<HistoryEntry> entry,
+                 scoped_ptr<NavigationParams> navigation_params,
                  blink::WebURLRequest::CachePolicy cache_policy);
 
   void UpdateForCommit(RenderFrameImpl* frame,
@@ -136,8 +138,17 @@
 
   RenderViewImpl* render_view_;
 
+  // A HistoryEntry representing the currently-loaded page.
   scoped_ptr<HistoryEntry> current_entry_;
+  // A HistoryEntry representing the page that is being loaded, or an empty
+  // scoped_ptr if no page is being loaded.
   scoped_ptr<HistoryEntry> provisional_entry_;
+  // The NavigationParams corresponding to the last load that was initiated by
+  // |GoToEntry|. This is kept around so that it can be passed into existing
+  // frames modified during a history navigation in GoToEntry(), and can be
+  // passed into frames created after the commit that resulted from the
+  // navigation in GetItemForNewChildFrame().
+  scoped_ptr<NavigationParams> navigation_params_;
 
   DISALLOW_COPY_AND_ASSIGN(HistoryController);
 };
diff --git a/content/renderer/media/media_stream_audio_processor_unittest.cc b/content/renderer/media/media_stream_audio_processor_unittest.cc
index bbdf794..a2f1b4e8 100644
--- a/content/renderer/media/media_stream_audio_processor_unittest.cc
+++ b/content/renderer/media/media_stream_audio_processor_unittest.cc
@@ -492,7 +492,14 @@
   audio_processor = NULL;
 }
 
-TEST_F(MediaStreamAudioProcessorTest, TestWithKeyboardMicChannel) {
+// Disabled on android clang builds due to crbug.com/470499
+#if defined(__clang__) && defined(OS_ANDROID)
+#define MAYBE_TestWithKeyboardMicChannel DISABLED_TestWithKeyboardMicChannel
+#else
+#define MAYBE_TestWithKeyboardMicChannel TestWithKeyboardMicChannel
+#endif
+
+TEST_F(MediaStreamAudioProcessorTest, MAYBE_TestWithKeyboardMicChannel) {
   MockMediaConstraintFactory constraint_factory;
   constraint_factory.AddMandatory(
       MediaAudioConstraints::kGoogExperimentalNoiseSuppression, true);
diff --git a/content/renderer/media/render_media_client_unittest.cc b/content/renderer/media/render_media_client_unittest.cc
index 2366156..7ac253e 100644
--- a/content/renderer/media/render_media_client_unittest.cc
+++ b/content/renderer/media/render_media_client_unittest.cc
@@ -27,6 +27,8 @@
     // TODO(sandersd): Was this supposed to be added to the list?
     media::KeySystemInfo key_system_info;
     key_system_info.key_system = "test.keysystem";
+    key_system_info.max_audio_robustness = media::EmeRobustness::EMPTY;
+    key_system_info.max_video_robustness = media::EmeRobustness::EMPTY;
     key_system_info.persistent_license_support =
         media::EME_SESSION_TYPE_NOT_SUPPORTED;
     key_system_info.persistent_release_message_support =
@@ -40,6 +42,8 @@
     if (is_extra_key_system_enabled_) {
       media::KeySystemInfo wv_key_system_info;
       wv_key_system_info.key_system = kWidevineKeySystem;
+      wv_key_system_info.max_audio_robustness = media::EmeRobustness::EMPTY;
+      wv_key_system_info.max_video_robustness = media::EmeRobustness::EMPTY;
       wv_key_system_info.persistent_license_support =
           media::EME_SESSION_TYPE_NOT_SUPPORTED;
       wv_key_system_info.persistent_release_message_support =
diff --git a/content/renderer/media/render_media_log.cc b/content/renderer/media/render_media_log.cc
index d20f622e..83666aa2 100644
--- a/content/renderer/media/render_media_log.cc
+++ b/content/renderer/media/render_media_log.cc
@@ -27,6 +27,16 @@
     return;
   }
 
+  if (event->type == media::MediaLogEvent::PIPELINE_ERROR) {
+    LOG(ERROR) << "MediaEvent: "
+               << media::MediaLog::MediaEventToLogString(*event.get());
+  } else if (event->type != media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED &&
+             event->type != media::MediaLogEvent::PROPERTY_CHANGE &&
+             event->type != media::MediaLogEvent::NETWORK_ACTIVITY_SET) {
+    DVLOG(1) << "MediaEvent: "
+             << media::MediaLog::MediaEventToLogString(*event.get());
+  }
+
   // Keep track of the latest buffered extents properties to avoid sending
   // thousands of events over IPC. See http://crbug.com/352585 for details.
   //
diff --git a/content/renderer/media/rtc_data_channel_handler.cc b/content/renderer/media/rtc_data_channel_handler.cc
index 5a21184d..91602d7 100644
--- a/content/renderer/media/rtc_data_channel_handler.cc
+++ b/content/renderer/media/rtc_data_channel_handler.cc
@@ -293,10 +293,10 @@
 
   if (buffer->binary) {
     webkit_client_->didReceiveRawData(buffer->data.data(),
-                                      buffer->data.length());
+                                      buffer->data.size());
   } else {
     base::string16 utf16;
-    if (!base::UTF8ToUTF16(buffer->data.data(), buffer->data.length(),
+    if (!base::UTF8ToUTF16(buffer->data.data(), buffer->data.size(),
                            &utf16)) {
       LOG(ERROR) << "Failed convert received data to UTF16";
       return;
diff --git a/content/renderer/media/video_capture_impl.cc b/content/renderer/media/video_capture_impl.cc
index 9c5c5344..9b104d2 100644
--- a/content/renderer/media/video_capture_impl.cc
+++ b/content/renderer/media/video_capture_impl.cc
@@ -52,27 +52,31 @@
       state_(VIDEO_CAPTURE_STATE_STOPPED),
       weak_factory_(this) {
   DCHECK(filter);
-  render_io_thread_checker_.DetachFromThread();
 }
 
 VideoCaptureImpl::~VideoCaptureImpl() {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
 }
 
 void VideoCaptureImpl::Init() {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  // For creating callbacks in unittest, this class may be constructed from a
+  // different thread than the IO thread, e.g. wherever unittest runs on.
+  // Therefore, this function should define the thread ownership.
+#if DCHECK_IS_ON()
+  io_message_loop_ = base::MessageLoopProxy::current();
+#endif
   message_filter_->AddDelegate(this);
 }
 
 void VideoCaptureImpl::DeInit() {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   if (state_ == VIDEO_CAPTURE_STATE_STARTED)
     Send(new VideoCaptureHostMsg_Stop(device_id_));
   message_filter_->RemoveDelegate(this);
 }
 
 void VideoCaptureImpl::SuspendCapture(bool suspend) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   Send(suspend ?
        static_cast<IPC::Message*>(new VideoCaptureHostMsg_Pause(device_id_)) :
        static_cast<IPC::Message*>(
@@ -84,7 +88,7 @@
     const media::VideoCaptureParams& params,
     const VideoCaptureStateUpdateCB& state_update_cb,
     const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   ClientInfo client_info;
   client_info.params = params;
   client_info.state_update_cb = state_update_cb;
@@ -132,7 +136,7 @@
 }
 
 void VideoCaptureImpl::StopCapture(int client_id) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
 
   // A client ID can be in only one client list.
   // If this ID is in any client list, we can just remove it from
@@ -153,7 +157,7 @@
 
 void VideoCaptureImpl::GetDeviceSupportedFormats(
     const VideoCaptureDeviceFormatsCB& callback) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   device_formats_cb_queue_.push_back(callback);
   if (device_formats_cb_queue_.size() == 1)
     Send(new VideoCaptureHostMsg_GetDeviceSupportedFormats(device_id_,
@@ -162,7 +166,7 @@
 
 void VideoCaptureImpl::GetDeviceFormatsInUse(
     const VideoCaptureDeviceFormatsCB& callback) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   device_formats_in_use_cb_queue_.push_back(callback);
   if (device_formats_in_use_cb_queue_.size() == 1)
     Send(
@@ -172,7 +176,7 @@
 void VideoCaptureImpl::OnBufferCreated(
     base::SharedMemoryHandle handle,
     int length, int buffer_id) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
 
   // In case client calls StopCapture before the arrival of created buffer,
   // just close this buffer and return.
@@ -196,7 +200,7 @@
 }
 
 void VideoCaptureImpl::OnBufferDestroyed(int buffer_id) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
 
   const ClientBufferMap::iterator iter = client_buffers_.find(buffer_id);
   if (iter == client_buffers_.end())
@@ -212,7 +216,7 @@
                                         const gfx::Rect& visible_rect,
                                         base::TimeTicks timestamp,
                                         const base::DictionaryValue& metadata) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
 
   if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) {
     Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id, 0));
@@ -261,7 +265,7 @@
     const gfx::Size& packed_frame_size,
     base::TimeTicks timestamp,
     const base::DictionaryValue& metadata) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
 
   if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) {
     Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id, 0));
@@ -288,13 +292,13 @@
     int buffer_id,
     const scoped_refptr<ClientBuffer>& /* ignored_buffer */,
     uint32 release_sync_point) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   Send(new VideoCaptureHostMsg_BufferReady(
       device_id_, buffer_id, release_sync_point));
 }
 
 void VideoCaptureImpl::OnStateChanged(VideoCaptureState state) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
 
   switch (state) {
     case VIDEO_CAPTURE_STATE_STARTED:
@@ -336,7 +340,7 @@
 
 void VideoCaptureImpl::OnDeviceSupportedFormatsEnumerated(
     const media::VideoCaptureFormats& supported_formats) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   for (size_t i = 0; i < device_formats_cb_queue_.size(); ++i)
     device_formats_cb_queue_[i].Run(supported_formats);
   device_formats_cb_queue_.clear();
@@ -344,14 +348,14 @@
 
 void VideoCaptureImpl::OnDeviceFormatsInUseReceived(
     const media::VideoCaptureFormats& formats_in_use) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   for (size_t i = 0; i < device_formats_in_use_cb_queue_.size(); ++i)
     device_formats_in_use_cb_queue_[i].Run(formats_in_use);
   device_formats_in_use_cb_queue_.clear();
 }
 
 void VideoCaptureImpl::OnDelegateAdded(int32 device_id) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   DVLOG(1) << "OnDelegateAdded: device_id " << device_id;
 
   device_id_ = device_id;
@@ -366,7 +370,7 @@
 }
 
 void VideoCaptureImpl::StopDevice() {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
 
   if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
     state_ = VIDEO_CAPTURE_STATE_STOPPING;
@@ -376,7 +380,7 @@
 }
 
 void VideoCaptureImpl::RestartCapture() {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPED);
 
   int width = 0;
@@ -397,7 +401,7 @@
 }
 
 void VideoCaptureImpl::StartCaptureInternal() {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   DCHECK(device_id_);
 
   Send(new VideoCaptureHostMsg_Start(device_id_, session_id_, params_));
@@ -405,12 +409,12 @@
 }
 
 void VideoCaptureImpl::Send(IPC::Message* message) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   message_filter_->Send(message);
 }
 
 bool VideoCaptureImpl::RemoveClient(int client_id, ClientInfoMap* clients) {
-  DCHECK(render_io_thread_checker_.CalledOnValidThread());
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
   bool found = false;
 
   const ClientInfoMap::iterator it = clients->find(client_id);
diff --git a/content/renderer/media/video_capture_impl.h b/content/renderer/media/video_capture_impl.h
index e6f7138..38fc4fc 100644
--- a/content/renderer/media/video_capture_impl.h
+++ b/content/renderer/media/video_capture_impl.h
@@ -2,21 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// VideoCaptureImpl represents a capture device in renderer process. It provides
-// interfaces for clients to Start/Stop capture. It also communicates to clients
-// when buffer is ready, state of capture device is changed.
-
-// VideoCaptureImpl is also a delegate of VideoCaptureMessageFilter which relays
-// operation of a capture device to the browser process and receives responses
-// from browser process.
-//
-// VideoCaptureImpl is an IO thread only object. See the comments in
-// video_capture_impl_manager.cc for the lifetime of this object.
-// All methods must be called on the IO thread.
-//
-// This is an internal class used by VideoCaptureImplManager only. Do not access
-// this directly.
-
 #ifndef CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_
 #define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_
 
@@ -45,6 +30,20 @@
 
 namespace content {
 
+// VideoCaptureImpl represents a capture device in renderer process. It provides
+// interfaces for clients to Start/Stop capture. It also communicates to clients
+// when buffer is ready, state of capture device is changed.
+
+// VideoCaptureImpl is also a delegate of VideoCaptureMessageFilter which relays
+// operation of a capture device to the browser process and receives responses
+// from browser process.
+//
+// VideoCaptureImpl is an IO thread only object. See the comments in
+// video_capture_impl_manager.cc for the lifetime of this object.
+// All methods must be called on the IO thread.
+//
+// This is an internal class used by VideoCaptureImplManager only. Do not access
+// this directly.
 class CONTENT_EXPORT VideoCaptureImpl
     : public VideoCaptureMessageFilter::Delegate {
  public:
@@ -170,8 +169,8 @@
   bool suspended_;
   VideoCaptureState state_;
 
-  // |weak_factory_| and |thread_checker_| are bound to the IO thread.
-  base::ThreadChecker render_io_thread_checker_;
+  // IO message loop reference for checking correct class operation.
+  scoped_refptr<base::MessageLoopProxy> io_message_loop_;
 
   // WeakPtrFactory pointing back to |this| object, for use with
   // media::VideoFrames constructed in OnBufferReceived() from buffers cached
diff --git a/content/renderer/media/video_capture_impl_manager.cc b/content/renderer/media/video_capture_impl_manager.cc
index 1592331..f72f313 100644
--- a/content/renderer/media/video_capture_impl_manager.cc
+++ b/content/renderer/media/video_capture_impl_manager.cc
@@ -36,11 +36,12 @@
 VideoCaptureImplManager::VideoCaptureImplManager()
     : next_client_id_(0),
       filter_(new VideoCaptureMessageFilter()),
+      render_main_message_loop_(base::MessageLoopProxy::current()),
       weak_factory_(this) {
 }
 
 VideoCaptureImplManager::~VideoCaptureImplManager() {
-  DCHECK(render_main_thread_checker_.CalledOnValidThread());
+  DCHECK(render_main_message_loop_->BelongsToCurrentThread());
   if (devices_.empty())
     return;
   // Forcibly release all video capture resources.
@@ -58,7 +59,7 @@
 
 base::Closure VideoCaptureImplManager::UseDevice(
     media::VideoCaptureSessionId id) {
-  DCHECK(render_main_thread_checker_.CalledOnValidThread());
+  DCHECK(render_main_message_loop_->BelongsToCurrentThread());
 
   VideoCaptureImpl* impl = NULL;
   const VideoCaptureDeviceMap::iterator it = devices_.find(id);
@@ -83,7 +84,7 @@
     const media::VideoCaptureParams& params,
     const VideoCaptureStateUpdateCB& state_update_cb,
     const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
-  DCHECK(render_main_thread_checker_.CalledOnValidThread());
+  DCHECK(render_main_message_loop_->BelongsToCurrentThread());
   const VideoCaptureDeviceMap::const_iterator it = devices_.find(id);
   DCHECK(it != devices_.end());
   VideoCaptureImpl* const impl = it->second.second;
@@ -107,7 +108,7 @@
 void VideoCaptureImplManager::GetDeviceSupportedFormats(
     media::VideoCaptureSessionId id,
     const VideoCaptureDeviceFormatsCB& callback) {
-  DCHECK(render_main_thread_checker_.CalledOnValidThread());
+  DCHECK(render_main_message_loop_->BelongsToCurrentThread());
   const VideoCaptureDeviceMap::const_iterator it = devices_.find(id);
   DCHECK(it != devices_.end());
   VideoCaptureImpl* const impl = it->second.second;
@@ -120,7 +121,7 @@
 void VideoCaptureImplManager::GetDeviceFormatsInUse(
     media::VideoCaptureSessionId id,
     const VideoCaptureDeviceFormatsCB& callback) {
-  DCHECK(render_main_thread_checker_.CalledOnValidThread());
+  DCHECK(render_main_message_loop_->BelongsToCurrentThread());
   const VideoCaptureDeviceMap::const_iterator it = devices_.find(id);
   DCHECK(it != devices_.end());
   VideoCaptureImpl* const impl = it->second.second;
@@ -137,9 +138,9 @@
   return NULL;
 }
 
-void VideoCaptureImplManager::StopCapture(
-    int client_id, media::VideoCaptureSessionId id) {
-  DCHECK(render_main_thread_checker_.CalledOnValidThread());
+void VideoCaptureImplManager::StopCapture(int client_id,
+                                          media::VideoCaptureSessionId id) {
+  DCHECK(render_main_message_loop_->BelongsToCurrentThread());
   const VideoCaptureDeviceMap::const_iterator it = devices_.find(id);
   DCHECK(it != devices_.end());
   VideoCaptureImpl* const impl = it->second.second;
@@ -151,7 +152,7 @@
 
 void VideoCaptureImplManager::UnrefDevice(
     media::VideoCaptureSessionId id) {
-  DCHECK(render_main_thread_checker_.CalledOnValidThread());
+  DCHECK(render_main_message_loop_->BelongsToCurrentThread());
   const VideoCaptureDeviceMap::iterator it = devices_.find(id);
   DCHECK(it != devices_.end());
   VideoCaptureImpl* const impl = it->second.second;
@@ -171,13 +172,12 @@
 }
 
 void VideoCaptureImplManager::SuspendDevices(bool suspend) {
-  DCHECK(render_main_thread_checker_.CalledOnValidThread());
+  DCHECK(render_main_message_loop_->BelongsToCurrentThread());
   for (const auto& device : devices_) {
     VideoCaptureImpl* const impl = device.second.second;
     ChildProcess::current()->io_message_loop_proxy()->PostTask(
-        FROM_HERE,
-        base::Bind(&VideoCaptureImpl::SuspendCapture,
-                   base::Unretained(impl), suspend));
+        FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture,
+                              base::Unretained(impl), suspend));
   }
 }
 
diff --git a/content/renderer/media/video_capture_impl_manager.h b/content/renderer/media/video_capture_impl_manager.h
index 72f7d39..4fce3c7a 100644
--- a/content/renderer/media/video_capture_impl_manager.h
+++ b/content/renderer/media/video_capture_impl_manager.h
@@ -2,20 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// TODO(hclam): This class should be renamed to VideoCaptureService.
-
-// This class provides access to a video capture device in the browser
-// process through IPC. The main function is to deliver video frames
-// to a client.
-//
-// THREADING
-//
-// VideoCaptureImplManager lives only on the render thread. All methods
-// must be called on this thread.
-//
-// VideoFrames are delivered on the IO thread. Callbacks provided by
-// a client are also called on the IO thread.
-
 #ifndef CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_MANAGER_H_
 #define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_MANAGER_H_
 
@@ -39,6 +25,19 @@
 class VideoCaptureImpl;
 class VideoCaptureMessageFilter;
 
+// TODO(hclam): This class should be renamed to VideoCaptureService.
+
+// This class provides access to a video capture device in the browser
+// process through IPC. The main function is to deliver video frames
+// to a client.
+//
+// THREADING
+//
+// VideoCaptureImplManager lives only on the Render Main thread. All methods
+// must be called on this thread.
+//
+// VideoFrames are delivered on the IO thread. Callbacks provided by
+// a client are also called on the IO thread.
 class CONTENT_EXPORT VideoCaptureImplManager {
  public:
   VideoCaptureImplManager();
@@ -116,8 +115,9 @@
 
   const scoped_refptr<VideoCaptureMessageFilter> filter_;
 
-  // Bound to the render thread.
-  base::ThreadChecker render_main_thread_checker_;
+  // Hold a pointer to the Render Main message loop to check we operate on the
+  // right thread.
+  const scoped_refptr<base::MessageLoopProxy> render_main_message_loop_;
 
   // Bound to the render thread.
   // NOTE: Weak pointers must be invalidated before all other member variables.
diff --git a/content/renderer/media/video_capture_impl_manager_unittest.cc b/content/renderer/media/video_capture_impl_manager_unittest.cc
index 1258c76..39c9e8e4f 100644
--- a/content/renderer/media/video_capture_impl_manager_unittest.cc
+++ b/content/renderer/media/video_capture_impl_manager_unittest.cc
@@ -137,10 +137,8 @@
   base::Closure stop_cb1, stop_cb2;
   {
     base::RunLoop run_loop;
-    base::Closure quit_closure = BindToCurrentLoop(
-        run_loop.QuitClosure());
-    EXPECT_CALL(*this, OnStarted()).WillOnce(
-        RunClosure(quit_closure));
+    base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure());
+    EXPECT_CALL(*this, OnStarted()).WillOnce(RunClosure(quit_closure));
     EXPECT_CALL(*this, OnStarted()).RetiresOnSaturation();
     stop_cb1 = StartCapture(params_);
     stop_cb2 = StartCapture(params_);
@@ -150,10 +148,8 @@
 
   {
     base::RunLoop run_loop;
-    base::Closure quit_closure = BindToCurrentLoop(
-        run_loop.QuitClosure());
-    EXPECT_CALL(*this, OnStopped()).WillOnce(
-        RunClosure(quit_closure));
+    base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure());
+    EXPECT_CALL(*this, OnStopped()).WillOnce(RunClosure(quit_closure));
     EXPECT_CALL(*this, OnStopped()).RetiresOnSaturation();
     stop_cb1.Run();
     stop_cb2.Run();
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index bc1436ab..c7e4005 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -1365,16 +1365,6 @@
   return PP_ERROR_FAILED;
 }
 
-int32_t PepperPluginInstanceImpl::RegisterMessageHandler_1_1_Deprecated(
-    PP_Instance instance,
-    void* user_data,
-    const PPP_MessageHandler_0_1_Deprecated* handler,
-    PP_Resource message_loop) {
-  // Not supported in-process.
-  NOTIMPLEMENTED();
-  return PP_ERROR_FAILED;
-}
-
 void PepperPluginInstanceImpl::UnregisterMessageHandler(PP_Instance instance) {
   // Not supported in-process.
   NOTIMPLEMENTED();
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.h b/content/renderer/pepper/pepper_plugin_instance_impl.h
index f77bd04..e322cfb 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.h
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.h
@@ -434,11 +434,6 @@
                                  void* user_data,
                                  const PPP_MessageHandler_0_2* handler,
                                  PP_Resource message_loop) override;
-  int32_t RegisterMessageHandler_1_1_Deprecated(
-      PP_Instance instance,
-      void* user_data,
-      const PPP_MessageHandler_0_1_Deprecated* handler,
-      PP_Resource message_loop) override;
   void UnregisterMessageHandler(PP_Instance instance) override;
   PP_Bool SetCursor(PP_Instance instance,
                     PP_MouseCursor_Type type,
diff --git a/content/renderer/pepper/plugin_instance_throttler_impl.cc b/content/renderer/pepper/plugin_instance_throttler_impl.cc
index 6a48a4d..a5648172 100644
--- a/content/renderer/pepper/plugin_instance_throttler_impl.cc
+++ b/content/renderer/pepper/plugin_instance_throttler_impl.cc
@@ -37,9 +37,8 @@
 const int PluginInstanceThrottlerImpl::kMaximumFramesToExamine = 150;
 
 // static
-scoped_ptr<PluginInstanceThrottler> PluginInstanceThrottler::Create(
-    bool power_saver_enabled) {
-  return make_scoped_ptr(new PluginInstanceThrottlerImpl(power_saver_enabled));
+scoped_ptr<PluginInstanceThrottler> PluginInstanceThrottler::Create() {
+  return make_scoped_ptr(new PluginInstanceThrottlerImpl);
 }
 
 // static
@@ -50,10 +49,8 @@
       PluginInstanceThrottler::UNTHROTTLE_METHOD_NUM_ITEMS);
 }
 
-PluginInstanceThrottlerImpl::PluginInstanceThrottlerImpl(
-    bool power_saver_enabled)
-    : state_(power_saver_enabled ? THROTTLER_STATE_AWAITING_KEYFRAME
-                                 : THROTTLER_STATE_POWER_SAVER_DISABLED),
+PluginInstanceThrottlerImpl::PluginInstanceThrottlerImpl()
+    : state_(THROTTLER_STATE_AWAITING_KEYFRAME),
       is_hidden_for_placeholder_(false),
       web_plugin_(nullptr),
       consecutive_interesting_frames_(0),
diff --git a/content/renderer/pepper/plugin_instance_throttler_impl.h b/content/renderer/pepper/plugin_instance_throttler_impl.h
index 0bcad2f6..54f17cf 100644
--- a/content/renderer/pepper/plugin_instance_throttler_impl.h
+++ b/content/renderer/pepper/plugin_instance_throttler_impl.h
@@ -26,7 +26,7 @@
 class CONTENT_EXPORT PluginInstanceThrottlerImpl
     : public PluginInstanceThrottler {
  public:
-  PluginInstanceThrottlerImpl(bool power_saver_enabled);
+  PluginInstanceThrottlerImpl();
 
   ~PluginInstanceThrottlerImpl() override;
 
@@ -47,8 +47,7 @@
   }
 
   bool power_saver_enabled() const {
-    return state_ == THROTTLER_STATE_AWAITING_KEYFRAME ||
-           state_ == THROTTLER_STATE_PLUGIN_THROTTLED;
+    return state_ != THROTTLER_STATE_MARKED_ESSENTIAL;
   }
 
   // Throttler needs to be initialized with the real plugin's view bounds.
@@ -68,8 +67,6 @@
   friend class PluginInstanceThrottlerImplTest;
 
   enum ThrottlerState {
-    // Power saver is disabled, but the plugin instance is still peripheral.
-    THROTTLER_STATE_POWER_SAVER_DISABLED,
     // Plugin has been found to be peripheral, Plugin Power Saver is enabled,
     // and throttler is awaiting a representative keyframe.
     THROTTLER_STATE_AWAITING_KEYFRAME,
diff --git a/content/renderer/pepper/plugin_instance_throttler_impl_unittest.cc b/content/renderer/pepper/plugin_instance_throttler_impl_unittest.cc
index 0b3e398..ccf6c97 100644
--- a/content/renderer/pepper/plugin_instance_throttler_impl_unittest.cc
+++ b/content/renderer/pepper/plugin_instance_throttler_impl_unittest.cc
@@ -39,8 +39,7 @@
     blink::WebRect rect;
     rect.width = 100;
     rect.height = 100;
-    throttler_.reset(
-        new PluginInstanceThrottlerImpl(true /* power_saver_enabled */));
+    throttler_.reset(new PluginInstanceThrottlerImpl);
     throttler_->Initialize(nullptr, GURL("http://example.com"),
                            "Shockwave Flash", rect);
     throttler_->AddObserver(this);
diff --git a/content/renderer/presentation/presentation_dispatcher.cc b/content/renderer/presentation/presentation_dispatcher.cc
index eec933b8..c050a8be 100644
--- a/content/renderer/presentation/presentation_dispatcher.cc
+++ b/content/renderer/presentation/presentation_dispatcher.cc
@@ -33,6 +33,19 @@
   }
 }
 
+blink::WebPresentationSessionState GetWebPresentationSessionStateFromMojo(
+        presentation::PresentationSessionState mojoSessionState) {
+  switch (mojoSessionState) {
+    case presentation::PRESENTATION_SESSION_STATE_CONNECTED:
+      return blink::WebPresentationSessionState::Connected;
+    case presentation::PRESENTATION_SESSION_STATE_DISCONNECTED:
+      return blink::WebPresentationSessionState::Disconnected;
+  }
+
+  NOTREACHED();
+  return blink::WebPresentationSessionState::Disconnected;
+}
+
 GURL GetPresentationURLFromFrame(content::RenderFrame* frame) {
   DCHECK(frame);
 
@@ -74,13 +87,12 @@
     const std::string& presentation_url, bool watched) {
   ConnectToPresentationServiceIfNeeded();
   if (watched) {
-    presentation_service_->GetScreenAvailability(
+    presentation_service_->ListenForScreenAvailability(
         presentation_url,
         base::Bind(&PresentationDispatcher::OnScreenAvailabilityChanged,
                  base::Unretained(this)));
   } else {
-    presentation_service_->OnScreenAvailabilityListenerRemoved(
-        presentation_url);
+    presentation_service_->RemoveScreenAvailabilityListener(presentation_url);
   }
 }
 
@@ -161,11 +173,8 @@
       base::Unretained(this)));
 
   DCHECK(!session_info.is_null());
-  PresentationSessionDispatcher* session_dispatcher =
-      new PresentationSessionDispatcher(session_info.Pass());
-  presentation_session_dispatchers_.push_back(session_dispatcher);
   controller_->didStartDefaultSession(
-      new PresentationSessionClient(session_dispatcher));
+      new PresentationSessionClient(session_info.Pass()));
 }
 
 void PresentationDispatcher::OnSessionCreated(
@@ -182,10 +191,23 @@
   }
 
   DCHECK(!session_info.is_null());
-  PresentationSessionDispatcher* session_dispatcher =
-      new PresentationSessionDispatcher(session_info.Pass());
-  presentation_session_dispatchers_.push_back(session_dispatcher);
-  callback->onSuccess(new PresentationSessionClient(session_dispatcher));
+  callback->onSuccess(new PresentationSessionClient(session_info.Pass()));
+}
+
+void PresentationDispatcher::OnSessionStateChange(
+    presentation::PresentationSessionInfoPtr session_info,
+    presentation::PresentationSessionState session_state) {
+  if (!controller_)
+    return;
+
+  presentation_service_->ListenForSessionStateChange(base::Bind(
+      &PresentationDispatcher::OnSessionStateChange,
+      base::Unretained(this)));
+
+  DCHECK(!session_info.is_null());
+  controller_->didChangeSessionState(
+      new PresentationSessionClient(session_info.Pass()),
+      GetWebPresentationSessionStateFromMojo(session_state));
 }
 
 void PresentationDispatcher::ConnectToPresentationServiceIfNeeded() {
@@ -197,6 +219,9 @@
   presentation_service_->ListenForDefaultSessionStart(base::Bind(
       &PresentationDispatcher::OnDefaultSessionStarted,
       base::Unretained(this)));
+  presentation_service_->ListenForSessionStateChange(base::Bind(
+      &PresentationDispatcher::OnSessionStateChange,
+      base::Unretained(this)));
 }
 
 }  // namespace content
diff --git a/content/renderer/presentation/presentation_dispatcher.h b/content/renderer/presentation/presentation_dispatcher.h
index 0099a0f..fb6b4d9a 100644
--- a/content/renderer/presentation/presentation_dispatcher.h
+++ b/content/renderer/presentation/presentation_dispatcher.h
@@ -6,11 +6,9 @@
 #define CONTENT_RENDERER_PRESENTATION_PRESENTATION_DISPATCHER_H_
 
 #include "base/compiler_specific.h"
-#include "base/memory/scoped_vector.h"
 #include "content/common/content_export.h"
 #include "content/common/presentation/presentation_service.mojom.h"
 #include "content/public/renderer/render_frame_observer.h"
-#include "content/renderer/presentation/presentation_session_dispatcher.h"
 #include "third_party/WebKit/public/platform/modules/presentation/WebPresentationClient.h"
 
 namespace blink {
@@ -57,6 +55,9 @@
       presentation::PresentationErrorPtr error);
   void OnDefaultSessionStarted(
       presentation::PresentationSessionInfoPtr session_info);
+  void OnSessionStateChange(
+      presentation::PresentationSessionInfoPtr session_info,
+      presentation::PresentationSessionState session_state);
 
   void ConnectToPresentationServiceIfNeeded();
 
@@ -67,14 +68,6 @@
   // Used as a weak reference. Can be null since lifetime is bound to the frame.
   blink::WebPresentationController* controller_;
   presentation::PresentationServicePtr presentation_service_;
-
-  // Wrappers around the PresentationSessionPtr corresponding to each Javascript
-  // PresentationSession object passed back to the frame.
-  // TODO(avayvod): Switch to base::IDMap when the lookup by presentation id is
-  // needed.
-  using PresentationSessionDispatchers =
-      ScopedVector<PresentationSessionDispatcher>;
-  PresentationSessionDispatchers presentation_session_dispatchers_;
 };
 
 }  // namespace content
diff --git a/content/renderer/presentation/presentation_session_client.cc b/content/renderer/presentation/presentation_session_client.cc
index e1f468f..62b2852 100644
--- a/content/renderer/presentation/presentation_session_client.cc
+++ b/content/renderer/presentation/presentation_session_client.cc
@@ -5,15 +5,14 @@
 #include "content/renderer/presentation/presentation_session_client.h"
 
 #include "base/logging.h"
-#include "content/renderer/presentation/presentation_session_dispatcher.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 
 namespace content {
 
 PresentationSessionClient::PresentationSessionClient(
-    PresentationSessionDispatcher* dispatcher)
-    : url_(blink::WebString::fromUTF8(dispatcher->GetUrl())),
-      id_(blink::WebString::fromUTF8(dispatcher->GetId())) {
+    presentation::PresentationSessionInfoPtr session_info)
+    : url_(blink::WebString::fromUTF8(session_info->url)),
+      id_(blink::WebString::fromUTF8(session_info->id)) {
 }
 
 PresentationSessionClient::~PresentationSessionClient() {
diff --git a/content/renderer/presentation/presentation_session_client.h b/content/renderer/presentation/presentation_session_client.h
index c6cc259..1bdcf15b 100644
--- a/content/renderer/presentation/presentation_session_client.h
+++ b/content/renderer/presentation/presentation_session_client.h
@@ -7,18 +7,18 @@
 
 #include "base/compiler_specific.h"
 #include "content/common/content_export.h"
+#include "content/common/presentation/presentation_service.mojom.h"
 #include "third_party/WebKit/public/platform/modules/presentation/WebPresentationSessionClient.h"
 
 namespace content {
 
-class PresentationSessionDispatcher;
-
 // PresentationSessionClient is passed to the Blink layer if presentation
 // session has been created successfully. Owned by the callback.
 class CONTENT_EXPORT PresentationSessionClient
     : public NON_EXPORTED_BASE(blink::WebPresentationSessionClient) {
  public:
-  explicit PresentationSessionClient(PresentationSessionDispatcher* dispatcher);
+  explicit PresentationSessionClient(
+        presentation::PresentationSessionInfoPtr session_info);
   ~PresentationSessionClient() override;
 
   // WebPresentationSessionClient implementation.
diff --git a/content/renderer/presentation/presentation_session_dispatcher.cc b/content/renderer/presentation/presentation_session_dispatcher.cc
deleted file mode 100644
index 26660bd..0000000
--- a/content/renderer/presentation/presentation_session_dispatcher.cc
+++ /dev/null
@@ -1,29 +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 "content/renderer/presentation/presentation_session_dispatcher.h"
-
-#include "base/logging.h"
-
-namespace content {
-
-PresentationSessionDispatcher::PresentationSessionDispatcher(
-    presentation::PresentationSessionInfoPtr session_info)
-    : session_info_(session_info.Pass()) {
-}
-
-PresentationSessionDispatcher::~PresentationSessionDispatcher() {
-}
-
-const mojo::String& PresentationSessionDispatcher::GetUrl() const {
-  DCHECK(!session_info_.is_null());
-  return session_info_->url;
-}
-
-const mojo::String& PresentationSessionDispatcher::GetId() const {
-  DCHECK(!session_info_.is_null());
-  return session_info_->id;
-}
-
-}  // namespace content
diff --git a/content/renderer/presentation/presentation_session_dispatcher.h b/content/renderer/presentation/presentation_session_dispatcher.h
deleted file mode 100644
index 0a89c56..0000000
--- a/content/renderer/presentation/presentation_session_dispatcher.h
+++ /dev/null
@@ -1,30 +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.
-
-#ifndef CONTENT_RENDERER_PRESENTATION_PRESENTATION_SESSION_DISPATCHER_H_
-#define CONTENT_RENDERER_PRESENTATION_PRESENTATION_SESSION_DISPATCHER_H_
-
-#include "base/compiler_specific.h"
-#include "content/common/content_export.h"
-#include "content/common/presentation/presentation_service.mojom.h"
-
-namespace content {
-
-// A wrapper around the Mojo PresentationSession remote object.
-class CONTENT_EXPORT PresentationSessionDispatcher {
- public:
-  explicit PresentationSessionDispatcher(
-      presentation::PresentationSessionInfoPtr session_info);
-  ~PresentationSessionDispatcher();
-
-  const mojo::String& GetUrl() const;
-  const mojo::String& GetId() const;
-
- private:
-  presentation::PresentationSessionInfoPtr session_info_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_PRESENTATION_PRESENTATION_SESSION_DISPATCHER_H_
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 193a1157..e5dced9b 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -38,6 +38,7 @@
 #include "content/common/frame_messages.h"
 #include "content/common/frame_replication_state.h"
 #include "content/common/input_messages.h"
+#include "content/common/navigation_params.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/common/swapped_out_messages.h"
 #include "content/common/view_messages.h"
@@ -488,6 +489,12 @@
 }
 #endif
 
+bool IsReload(FrameMsg_Navigate_Type::Value navigation_type) {
+  return navigation_type == FrameMsg_Navigate_Type::RELOAD ||
+         navigation_type == FrameMsg_Navigate_Type::RELOAD_IGNORING_CACHE ||
+         navigation_type == FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL;
+}
+
 RenderFrameImpl::CreateRenderFrameImplFunction g_create_render_frame_impl =
     nullptr;
 
@@ -640,6 +647,7 @@
 // RenderFrameImpl ----------------------------------------------------------
 RenderFrameImpl::RenderFrameImpl(RenderViewImpl* render_view, int routing_id)
     : frame_(NULL),
+      is_local_root_(false),
       render_view_(render_view->AsWeakPtr()),
       routing_id_(routing_id),
       is_swapped_out_(false),
@@ -666,6 +674,7 @@
 #if defined(VIDEO_HOLE)
       contains_media_player_(false),
 #endif
+      devtools_agent_(nullptr),
       geolocation_dispatcher_(NULL),
       push_messaging_dispatcher_(NULL),
       presentation_dispatcher_(NULL),
@@ -723,6 +732,8 @@
 }
 
 void RenderFrameImpl::Initialize() {
+  is_local_root_ = !frame_->parent() || frame_->parent()->isWebRemoteFrame();
+
 #if defined(ENABLE_PLUGINS)
   new PepperBrowserConnection(this);
 #endif
@@ -731,6 +742,12 @@
   if (!frame_->parent())
     new ImageLoadingHelper(this);
 
+  if (is_local_root_ && !render_frame_proxy_) {
+    // DevToolsAgent is a RenderFrameObserver, and will destruct itself
+    // when |this| is deleted.
+    devtools_agent_ = new DevToolsAgent(this);
+  }
+
   // We delay calling this until we have the WebFrame so that any observer or
   // embedder can call GetWebFrame on any RenderFrame.
   GetContentClient()->renderer()->RenderFrameCreated(this);
@@ -1046,7 +1063,7 @@
   TRACE_EVENT2("navigation", "RenderFrameImpl::OnNavigate", "id", routing_id_,
                "url", common_params.url.possibly_invalid_spec());
 
-  bool is_reload = RenderViewImpl::IsReload(common_params.navigation_type);
+  bool is_reload = IsReload(common_params.navigation_type);
   bool is_history_navigation = request_params.page_state.IsValid();
   WebURLRequest::CachePolicy cache_policy =
       WebURLRequest::UseProtocolCachePolicy;
@@ -1075,7 +1092,7 @@
     cache_policy = WebURLRequest::ReloadIgnoringCacheData;
   }
 
-  render_view_->pending_navigation_params_.reset(
+  pending_navigation_params_.reset(
       new NavigationParams(common_params, start_params, request_params));
 
   // If we are reloading, then WebKit will use the history state of the current
@@ -1102,7 +1119,10 @@
       // Ensure we didn't save the swapped out URL in UpdateState, since the
       // browser should never be telling us to navigate to swappedout://.
       CHECK(entry->root().urlString() != WebString::fromUTF8(kSwappedOutURL));
-      render_view_->history_controller()->GoToEntry(entry.Pass(), cache_policy);
+      scoped_ptr<NavigationParams> navigation_params(
+          new NavigationParams(*pending_navigation_params_.get()));
+      render_view_->history_controller()->GoToEntry(
+          entry.Pass(), navigation_params.Pass(), cache_policy);
     }
   } else if (!common_params.base_url_for_data_url.is_empty()) {
     LoadDataURL(common_params, frame);
@@ -1150,8 +1170,8 @@
                                 renderer_navigation_start);
   }
 
-  // In case LoadRequest failed before DidCreateDataSource was called.
-  render_view_->pending_navigation_params_.reset();
+  // In case LoadRequest failed before didCreateDataSource was called.
+  pending_navigation_params_.reset();
 }
 
 void RenderFrameImpl::NavigateToSwappedOutURL() {
@@ -1178,6 +1198,11 @@
   return manifest_manager_;
 }
 
+void RenderFrameImpl::SetPendingNavigationParams(
+    scoped_ptr<NavigationParams> navigation_params) {
+  pending_navigation_params_ = navigation_params.Pass();
+}
+
 void RenderFrameImpl::OnBeforeUnload() {
   TRACE_EVENT1("navigation", "RenderFrameImpl::OnBeforeUnload",
                "id", routing_id_);
@@ -2289,13 +2314,98 @@
                                           blink::WebDataSource* datasource) {
   DCHECK(!frame_ || frame_ == frame);
 
-  // TODO(nasko): Move implementation here. Needed state:
-  // * pending_navigation_params_
-  // * webview
-  // Needed methods:
-  // * PopulateDocumentStateFromPending
-  // * CreateNavigationStateFromPending
-  render_view_->didCreateDataSource(frame, datasource);
+  bool content_initiated = !pending_navigation_params_.get();
+
+  // Make sure any previous redirect URLs end up in our new data source.
+  if (pending_navigation_params_.get()) {
+    for (const auto& i :
+         pending_navigation_params_->request_params.redirects) {
+      datasource->appendRedirect(i);
+    }
+  }
+
+  DocumentState* document_state = DocumentState::FromDataSource(datasource);
+  if (!document_state) {
+    document_state = new DocumentState;
+    datasource->setExtraData(document_state);
+    if (!content_initiated)
+      PopulateDocumentStateFromPending(document_state);
+  }
+
+  // Carry over the user agent override flag, if it exists.
+  blink::WebView* webview = render_view_->webview();
+  if (content_initiated && webview && webview->mainFrame() &&
+      webview->mainFrame()->isWebLocalFrame() &&
+      webview->mainFrame()->dataSource()) {
+    DocumentState* old_document_state =
+        DocumentState::FromDataSource(webview->mainFrame()->dataSource());
+    if (old_document_state) {
+      InternalDocumentStateData* internal_data =
+          InternalDocumentStateData::FromDocumentState(document_state);
+      InternalDocumentStateData* old_internal_data =
+          InternalDocumentStateData::FromDocumentState(old_document_state);
+      internal_data->set_is_overriding_user_agent(
+          old_internal_data->is_overriding_user_agent());
+    }
+  }
+
+  // The rest of RenderView assumes that a WebDataSource will always have a
+  // non-null NavigationState.
+  if (content_initiated) {
+    document_state->set_navigation_state(
+        NavigationStateImpl::CreateContentInitiated());
+  } else {
+    document_state->set_navigation_state(CreateNavigationStateFromPending());
+    pending_navigation_params_.reset();
+  }
+
+  // DocumentState::referred_by_prefetcher_ is true if we are
+  // navigating from a page that used prefetching using a link on that
+  // page.  We are early enough in the request process here that we
+  // can still see the DocumentState of the previous page and set
+  // this value appropriately.
+  // TODO(gavinp): catch the important case of navigation in a new
+  // renderer process.
+  if (webview) {
+    if (WebFrame* old_frame = webview->mainFrame()) {
+      const WebURLRequest& original_request = datasource->originalRequest();
+      const GURL referrer(
+          original_request.httpHeaderField(WebString::fromUTF8("Referer")));
+      if (!referrer.is_empty() && old_frame->isWebLocalFrame() &&
+          DocumentState::FromDataSource(old_frame->dataSource())
+              ->was_prefetcher()) {
+        for (; old_frame; old_frame = old_frame->traverseNext(false)) {
+          WebDataSource* old_frame_datasource = old_frame->dataSource();
+          if (old_frame_datasource &&
+              referrer == GURL(old_frame_datasource->request().url())) {
+            document_state->set_was_referred_by_prefetcher(true);
+            break;
+          }
+        }
+      }
+    }
+  }
+
+  if (content_initiated) {
+    const WebURLRequest& request = datasource->request();
+    switch (request.cachePolicy()) {
+      case WebURLRequest::UseProtocolCachePolicy:  // normal load.
+        document_state->set_load_type(DocumentState::LINK_LOAD_NORMAL);
+        break;
+      case WebURLRequest::ReloadIgnoringCacheData:  // reload.
+      case WebURLRequest::ReloadBypassingCache:     // end-to-end reload.
+        document_state->set_load_type(DocumentState::LINK_LOAD_RELOAD);
+        break;
+      case WebURLRequest::ReturnCacheDataElseLoad:  // allow stale data.
+        document_state->set_load_type(DocumentState::LINK_LOAD_CACHE_STALE_OK);
+        break;
+      case WebURLRequest::ReturnCacheDataDontLoad:  // Don't re-post.
+        document_state->set_load_type(DocumentState::LINK_LOAD_CACHE_ONLY);
+        break;
+      default:
+        NOTREACHED();
+    }
+  }
 
   // Create the serviceworker's per-document network observing object if it
   // does not exist (When navigation happens within a page, the provider already
@@ -2453,10 +2563,10 @@
   // If we failed on a browser initiated request, then make sure that our error
   // page load is regarded as the same browser initiated request.
   if (!navigation_state->IsContentInitiated()) {
-    render_view_->pending_navigation_params_.reset(new NavigationParams(
+    pending_navigation_params_.reset(new NavigationParams(
         navigation_state->common_params(), navigation_state->start_params(),
         navigation_state->request_params()));
-    render_view_->pending_navigation_params_->request_params.request_time =
+    pending_navigation_params_->request_params.request_time =
         document_state->request_time();
   }
 
@@ -2541,8 +2651,7 @@
     // reload, the page ID doesn't change, and UpdateSessionHistory gets the
     // previous URL and the current page ID, which would be wrong.
     if (navigation_state->request_params().page_id != -1 &&
-        navigation_state->request_params().page_id != render_view_->page_id_ &&
-        !navigation_state->request_committed()) {
+        navigation_state->request_params().page_id != render_view_->page_id_) {
       // This is a successful session history navigation!
       render_view_->page_id_ = navigation_state->request_params().page_id;
 
@@ -2769,7 +2878,7 @@
   // ExtraData will get the new NavigationState.  Similarly, if we did not
   // initiate this navigation, then we need to take care to reset any pre-
   // existing navigation state to a content-initiated navigation state.
-  // DidCreateDataSource conveniently takes care of this for us.
+  // didCreateDataSource conveniently takes care of this for us.
   didCreateDataSource(frame, frame->dataSource());
 
   DocumentState* document_state =
@@ -3219,10 +3328,14 @@
     return;
 
   // Do not show error page when DevTools is attached.
-  if (render_view_->devtools_agent_ &&
-      render_view_->devtools_agent_->IsAttached()) {
-    return;
+  RenderFrameImpl* localRoot = this;
+  while (localRoot->frame_ && localRoot->frame_->parent() &&
+      localRoot->frame_->parent()->isWebLocalFrame()) {
+    localRoot = RenderFrameImpl::FromWebFrame(localRoot->frame_->parent());
+    DCHECK(localRoot);
   }
+  if (localRoot->devtools_agent_ && localRoot->devtools_agent_->IsAttached())
+    return;
 
   // Display error page, if appropriate.
   std::string error_domain = "http";
@@ -3620,23 +3733,6 @@
   return true;
 }
 
-void RenderFrameImpl::suddenTerminationDisablerChanged(
-    bool present,
-    blink::WebFrameClient::SuddenTerminationDisablerType type) {
-  switch (type) {
-    case blink::WebFrameClient::BeforeUnloadHandler:
-      Send(new FrameHostMsg_BeforeUnloadHandlersPresent(
-          routing_id_, present));
-      break;
-    case blink::WebFrameClient::UnloadHandler:
-      Send(new FrameHostMsg_UnloadHandlersPresent(
-          routing_id_, present));
-      break;
-    default:
-      NOTREACHED();
-  }
-}
-
 blink::WebPermissionClient* RenderFrameImpl::permissionClient() {
   if (!permission_client_)
     permission_client_.reset(new PermissionManager(GetServiceRegistry()));
@@ -3941,7 +4037,7 @@
 
   GetContentClient()->SetActiveURL(common_params.url);
 
-  render_view_->pending_navigation_params_.reset(new NavigationParams(
+  pending_navigation_params_.reset(new NavigationParams(
       common_params, StartNavigationParams(), request_params));
 
   if (!common_params.base_url_for_data_url.is_empty() ||
@@ -4482,6 +4578,61 @@
   return request.url();
 }
 
+void RenderFrameImpl::PopulateDocumentStateFromPending(
+    DocumentState* document_state) {
+  document_state->set_request_time(
+      pending_navigation_params_->request_params.request_time);
+
+  InternalDocumentStateData* internal_data =
+      InternalDocumentStateData::FromDocumentState(document_state);
+
+  if (!pending_navigation_params_->common_params.url.SchemeIs(
+          url::kJavaScriptScheme) &&
+      pending_navigation_params_->common_params.navigation_type ==
+          FrameMsg_Navigate_Type::RESTORE) {
+    // We're doing a load of a page that was restored from the last session. By
+    // default this prefers the cache over loading (LOAD_PREFERRING_CACHE) which
+    // can result in stale data for pages that are set to expire. We explicitly
+    // override that by setting the policy here so that as necessary we load
+    // from the network.
+    //
+    // TODO(davidben): Remove this in favor of passing a cache policy to the
+    // loadHistoryItem call in OnNavigate. That requires not overloading
+    // UseProtocolCachePolicy to mean both "normal load" and "determine cache
+    // policy based on load type, etc".
+    internal_data->set_cache_policy_override(
+        WebURLRequest::UseProtocolCachePolicy);
+  }
+
+  if (IsReload(pending_navigation_params_->common_params.navigation_type))
+    document_state->set_load_type(DocumentState::RELOAD);
+  else if (pending_navigation_params_->request_params.page_state.IsValid())
+    document_state->set_load_type(DocumentState::HISTORY_LOAD);
+  else
+    document_state->set_load_type(DocumentState::NORMAL_LOAD);
+
+  internal_data->set_is_overriding_user_agent(
+      pending_navigation_params_->request_params.is_overriding_user_agent);
+  internal_data->set_must_reset_scroll_and_scale_state(
+      pending_navigation_params_->common_params.navigation_type ==
+      FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL);
+  document_state->set_can_load_local_resources(
+      pending_navigation_params_->request_params.can_load_local_resources);
+}
+
+NavigationState* RenderFrameImpl::CreateNavigationStateFromPending() {
+  // A navigation resulting from loading a javascript URL should not be treated
+  // as a browser initiated event.  Instead, we want it to look as if the page
+  // initiated any load resulting from JS execution.
+  if (!pending_navigation_params_->common_params.url.SchemeIs(
+          url::kJavaScriptScheme)) {
+    return NavigationStateImpl::CreateBrowserInitiated(
+        pending_navigation_params_->common_params,
+        pending_navigation_params_->start_params,
+        pending_navigation_params_->request_params);
+  }
+  return NavigationStateImpl::CreateContentInitiated();
+}
 #if defined(OS_ANDROID)
 
 WebMediaPlayer* RenderFrameImpl::CreateAndroidWebMediaPlayer(
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index fb2c524..0494da6 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -74,6 +74,8 @@
 
 class ChildFrameCompositingHelper;
 class CompositorDependencies;
+class DevToolsAgent;
+class DocumentState;
 class ExternalPopupMenu;
 class GeolocationDispatcher;
 class ManifestManager;
@@ -81,6 +83,7 @@
 class MediaStreamRendererFactory;
 class MediaPermissionDispatcher;
 class MidiDispatcher;
+class NavigationState;
 class NotificationPermissionDispatcher;
 class PageState;
 class PepperPluginInstanceImpl;
@@ -101,6 +104,7 @@
 struct CommonNavigationParams;
 struct CustomContextMenuContext;
 struct FrameReplicationState;
+struct NavigationParams;
 struct RequestNavigationParams;
 struct ResourceResponseHead;
 struct StartNavigationParams;
@@ -517,9 +521,6 @@
   virtual void didChangeDefaultPresentation(blink::WebLocalFrame*);
   virtual bool enterFullscreen();
   virtual bool exitFullscreen();
-  virtual void suddenTerminationDisablerChanged(
-      bool present,
-      blink::WebFrameClient::SuddenTerminationDisablerType type);
   virtual blink::WebPermissionClient* permissionClient();
 
   // WebMediaPlayerDelegate implementation:
@@ -544,6 +545,11 @@
 
   ManifestManager* manifest_manager();
 
+  // TODO(creis): Remove when the only caller, the HistoryController, is no
+  // more.
+  void SetPendingNavigationParams(
+      scoped_ptr<NavigationParams> navigation_params);
+
  protected:
   RenderFrameImpl(RenderViewImpl* render_view, int32 routing_id);
 
@@ -722,6 +728,14 @@
   // Returns the URL being loaded by the |frame_|'s request.
   GURL GetLoadingUrl() const;
 
+  // If we initiated a navigation, this function will populate |document_state|
+  // with the navigation information saved in OnNavigate().
+  void PopulateDocumentStateFromPending(DocumentState* document_state);
+
+  // Returns a new NavigationState populated with the navigation information
+  // saved in OnNavigate().
+  NavigationState* CreateNavigationStateFromPending();
+
 #if defined(OS_ANDROID)
   blink::WebMediaPlayer* CreateAndroidWebMediaPlayer(
       const blink::WebURL& url,
@@ -742,6 +756,10 @@
   // case of the main frame, but not subframes).
   blink::WebLocalFrame* frame_;
 
+  // Frame is a local root if it is rendered in a process different than parent
+  // or it is a main frame.
+  bool is_local_root_;
+
   base::WeakPtr<RenderViewImpl> render_view_;
   int routing_id_;
   bool is_swapped_out_;
@@ -764,6 +782,11 @@
   // own RenderWidgets.
   scoped_refptr<RenderWidget> render_widget_;
 
+  // Temporarily holds state pertaining to a navigation that has been initiated
+  // until the NavigationState corresponding to the new navigation is created in
+  // didCreateDataSource().
+  scoped_ptr<NavigationParams> pending_navigation_params_;
+
 #if defined(ENABLE_PLUGINS)
   // Current text input composition text. Empty if no composition is in
   // progress.
@@ -849,6 +872,10 @@
   bool contains_media_player_;
 #endif
 
+  // The devtools agent for this frame; only created for main frame and
+  // local roots.
+  DevToolsAgent* devtools_agent_;
+
   // The geolocation dispatcher attached to this frame, lazily initialized.
   GeolocationDispatcher* geolocation_dispatcher_;
 
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index be5a892f..6e3672a 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -626,11 +626,7 @@
   } else if (command_line.HasSwitch(switches::kEnableDistanceFieldText)) {
     is_distance_field_text_enabled_ = true;
   } else {
-#if defined(OS_ANDROID)
-    is_distance_field_text_enabled_ = true;
-#else
     is_distance_field_text_enabled_ = false;
-#endif
   }
 
   // Note that under Linux, the media library will normally already have
@@ -668,7 +664,7 @@
           new CompositorRasterThread(
               compositor_task_graph_runner_.get(),
               base::StringPrintf(
-                  "CompositorWorker%u",
+                  "CompositorTileWorker%u",
                   static_cast<unsigned>(compositor_raster_threads_.size() + 1))
                   .c_str()));
       raster_thread->Start();
@@ -788,6 +784,7 @@
 
   if (gpu_channel_.get())
     gpu_channel_->DestroyChannel();
+  gpu_va_context_provider_ = nullptr;
 
   // TODO(port)
 #if defined(OS_WIN)
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index d9a9d02..7e31a9f 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -71,7 +71,6 @@
 #include "content/public/renderer/render_view_visitor.h"
 #include "content/renderer/browser_plugin/browser_plugin.h"
 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
-#include "content/renderer/devtools/devtools_agent.h"
 #include "content/renderer/disambiguation_popup_helper.h"
 #include "content/renderer/dom_storage/webstoragenamespace_impl.h"
 #include "content/renderer/drop_data_builder.h"
@@ -330,13 +329,6 @@
     NULL;
 
 // static
-bool RenderViewImpl::IsReload(FrameMsg_Navigate_Type::Value navigation_type) {
-  return navigation_type == FrameMsg_Navigate_Type::RELOAD ||
-         navigation_type == FrameMsg_Navigate_Type::RELOAD_IGNORING_CACHE ||
-         navigation_type == FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL;
-}
-
-// static
 Referrer RenderViewImpl::GetReferrerFromRequest(
     WebFrame* frame,
     const WebURLRequest& request) {
@@ -416,6 +408,12 @@
     sizes->push_back(gfx::Size(web_sizes[i]));
 }
 
+static blink::WebDevToolsAgent* GetWebDevToolsAgent(WebView* webview) {
+  if (!webview || !webview->mainFrame()->isWebLocalFrame())
+    return nullptr;
+  return webview->mainFrame()->toWebLocalFrame()->devToolsAgent();
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 struct RenderViewImpl::PendingFileChooser {
@@ -637,7 +635,6 @@
 #endif
       has_scrolled_focused_editable_node_into_rect_(false),
       speech_recognition_dispatcher_(NULL),
-      devtools_agent_(NULL),
       mouse_lock_dispatcher_(NULL),
 #if defined(OS_ANDROID)
       expected_content_intent_id_(0),
@@ -775,14 +772,14 @@
   new TextInputClientObserver(this);
 #endif  // defined(OS_MACOSX)
 
-  // The next group of objects all implement RenderViewObserver, so are deleted
-  // along with the RenderView automatically.
-  if (!proxy) {
-    devtools_agent_ = new DevToolsAgent(main_render_frame_.get());
+  // TODO(dgozman): do this not for main frame, but for local frame roots.
+  if (blink::WebDevToolsAgent* devToolsAgent = GetWebDevToolsAgent(webview())) {
     if (RenderWidgetCompositor* rwc = compositor())
-      webview()->devToolsAgent()->setLayerTreeId(rwc->GetLayerTreeId());
+      devToolsAgent->setLayerTreeId(rwc->GetLayerTreeId());
   }
 
+  // The next group of objects all implement RenderViewObserver, so are deleted
+  // along with the RenderView automatically.
   mouse_lock_dispatcher_ = new RenderViewMouseLockDispatcher(this);
 
   history_controller_.reset(new HistoryController(this));
@@ -2021,8 +2018,9 @@
   RenderWidgetCompositor* rwc = compositor();
   if (!rwc)
     return;
-  if (webview() && webview()->devToolsAgent())
-    webview()->devToolsAgent()->setLayerTreeId(rwc->GetLayerTreeId());
+  // TODO(dgozman): do this not for main frame, but for local frame roots.
+  if (blink::WebDevToolsAgent* devToolsAgent = GetWebDevToolsAgent(webview()))
+    devToolsAgent->setLayerTreeId(rwc->GetLayerTreeId());
 
   bool use_threaded_event_handling = true;
 #if defined(OS_MACOSX) && !defined(OS_IOS)
@@ -2081,161 +2079,6 @@
   return renderer_preferences_.accept_languages;
 }
 
-void RenderViewImpl::didCreateDataSource(WebLocalFrame* frame,
-                                         WebDataSource* ds) {
-  bool content_initiated = !pending_navigation_params_.get();
-
-  // Make sure any previous redirect URLs end up in our new data source.
-  if (pending_navigation_params_.get()) {
-    for (std::vector<GURL>::const_iterator i =
-             pending_navigation_params_->request_params.redirects.begin();
-         i != pending_navigation_params_->request_params.redirects.end(); ++i) {
-      ds->appendRedirect(*i);
-    }
-  }
-
-  DocumentState* document_state = DocumentState::FromDataSource(ds);
-  if (!document_state) {
-    document_state = new DocumentState;
-    ds->setExtraData(document_state);
-    if (!content_initiated)
-      PopulateDocumentStateFromPending(document_state);
-  }
-
-  // Carry over the user agent override flag, if it exists.
-  if (content_initiated && webview() && webview()->mainFrame() &&
-      webview()->mainFrame()->isWebLocalFrame() &&
-      webview()->mainFrame()->dataSource()) {
-    DocumentState* old_document_state =
-        DocumentState::FromDataSource(webview()->mainFrame()->dataSource());
-    if (old_document_state) {
-      InternalDocumentStateData* internal_data =
-          InternalDocumentStateData::FromDocumentState(document_state);
-      InternalDocumentStateData* old_internal_data =
-          InternalDocumentStateData::FromDocumentState(old_document_state);
-      internal_data->set_is_overriding_user_agent(
-          old_internal_data->is_overriding_user_agent());
-    }
-  }
-
-  // The rest of RenderView assumes that a WebDataSource will always have a
-  // non-null NavigationState.
-  if (content_initiated) {
-    document_state->set_navigation_state(
-        NavigationStateImpl::CreateContentInitiated());
-  } else {
-    document_state->set_navigation_state(CreateNavigationStateFromPending());
-    pending_navigation_params_.reset();
-  }
-
-  // DocumentState::referred_by_prefetcher_ is true if we are
-  // navigating from a page that used prefetching using a link on that
-  // page.  We are early enough in the request process here that we
-  // can still see the DocumentState of the previous page and set
-  // this value appropriately.
-  // TODO(gavinp): catch the important case of navigation in a new
-  // renderer process.
-  if (webview()) {
-    if (WebFrame* old_frame = webview()->mainFrame()) {
-      const WebURLRequest& original_request = ds->originalRequest();
-      const GURL referrer(
-          original_request.httpHeaderField(WebString::fromUTF8("Referer")));
-      if (!referrer.is_empty() && old_frame->isWebLocalFrame() &&
-          DocumentState::FromDataSource(old_frame->dataSource())
-              ->was_prefetcher()) {
-        for (; old_frame; old_frame = old_frame->traverseNext(false)) {
-          WebDataSource* old_frame_ds = old_frame->dataSource();
-          if (old_frame_ds && referrer == GURL(old_frame_ds->request().url())) {
-            document_state->set_was_referred_by_prefetcher(true);
-            break;
-          }
-        }
-      }
-    }
-  }
-
-  if (content_initiated) {
-    const WebURLRequest& request = ds->request();
-    switch (request.cachePolicy()) {
-      case WebURLRequest::UseProtocolCachePolicy:  // normal load.
-        document_state->set_load_type(DocumentState::LINK_LOAD_NORMAL);
-        break;
-      case WebURLRequest::ReloadIgnoringCacheData:  // reload.
-      case WebURLRequest::ReloadBypassingCache:  // end-to-end reload.
-        document_state->set_load_type(DocumentState::LINK_LOAD_RELOAD);
-        break;
-      case WebURLRequest::ReturnCacheDataElseLoad:  // allow stale data.
-        document_state->set_load_type(
-            DocumentState::LINK_LOAD_CACHE_STALE_OK);
-        break;
-      case WebURLRequest::ReturnCacheDataDontLoad:  // Don't re-post.
-        document_state->set_load_type(DocumentState::LINK_LOAD_CACHE_ONLY);
-        break;
-      default:
-        NOTREACHED();
-    }
-  }
-
-  FOR_EACH_OBSERVER(
-      RenderViewObserver, observers_, DidCreateDataSource(frame, ds));
-}
-
-void RenderViewImpl::PopulateDocumentStateFromPending(
-    DocumentState* document_state) {
-  document_state->set_request_time(
-      pending_navigation_params_->request_params.request_time);
-
-  InternalDocumentStateData* internal_data =
-      InternalDocumentStateData::FromDocumentState(document_state);
-
-  if (!pending_navigation_params_->common_params.url.SchemeIs(
-          url::kJavaScriptScheme) &&
-      pending_navigation_params_->common_params.navigation_type ==
-          FrameMsg_Navigate_Type::RESTORE) {
-    // We're doing a load of a page that was restored from the last session. By
-    // default this prefers the cache over loading (LOAD_PREFERRING_CACHE) which
-    // can result in stale data for pages that are set to expire. We explicitly
-    // override that by setting the policy here so that as necessary we load
-    // from the network.
-    //
-    // TODO(davidben): Remove this in favor of passing a cache policy to the
-    // loadHistoryItem call in OnNavigate. That requires not overloading
-    // UseProtocolCachePolicy to mean both "normal load" and "determine cache
-    // policy based on load type, etc".
-    internal_data->set_cache_policy_override(
-        WebURLRequest::UseProtocolCachePolicy);
-  }
-
-  if (IsReload(pending_navigation_params_->common_params.navigation_type))
-    document_state->set_load_type(DocumentState::RELOAD);
-  else if (pending_navigation_params_->request_params.page_state.IsValid())
-    document_state->set_load_type(DocumentState::HISTORY_LOAD);
-  else
-    document_state->set_load_type(DocumentState::NORMAL_LOAD);
-
-  internal_data->set_is_overriding_user_agent(
-      pending_navigation_params_->request_params.is_overriding_user_agent);
-  internal_data->set_must_reset_scroll_and_scale_state(
-      pending_navigation_params_->common_params.navigation_type ==
-      FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL);
-  document_state->set_can_load_local_resources(
-      pending_navigation_params_->request_params.can_load_local_resources);
-}
-
-NavigationState* RenderViewImpl::CreateNavigationStateFromPending() {
-  // A navigation resulting from loading a javascript URL should not be treated
-  // as a browser initiated event.  Instead, we want it to look as if the page
-  // initiated any load resulting from JS execution.
-  if (!pending_navigation_params_->common_params.url.SchemeIs(
-          url::kJavaScriptScheme)) {
-    return NavigationStateImpl::CreateBrowserInitiated(
-        pending_navigation_params_->common_params,
-        pending_navigation_params_->start_params,
-        pending_navigation_params_->request_params);
-  }
-  return NavigationStateImpl::CreateContentInitiated();
-}
-
 void RenderViewImpl::didChangeIcon(WebLocalFrame* frame,
                                    WebIconURL::Type icon_type) {
   if (frame->parent())
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 44c21e34..3377f2f 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -29,7 +29,6 @@
 #include "content/common/frame_message_enums.h"
 #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
 #include "content/common/navigation_gesture.h"
-#include "content/common/navigation_params.h"
 #include "content/common/view_message_enums.h"
 #include "content/public/common/page_zoom.h"
 #include "content/public/common/referrer.h"
@@ -120,12 +119,9 @@
 
 namespace content {
 
-class DevToolsAgent;
-class DocumentState;
 class HistoryController;
 class HistoryEntry;
 class MouseLockDispatcher;
-class NavigationState;
 class PageState;
 class PepperPluginInstanceImpl;
 class RenderViewImplTest;
@@ -576,14 +572,10 @@
   // still live here and are called from RenderFrameImpl. These implementations
   // are to be moved to RenderFrameImpl <http://crbug.com/361761>.
 
-  void didCreateDataSource(blink::WebLocalFrame* frame,
-                           blink::WebDataSource* datasource);
   void didChangeIcon(blink::WebLocalFrame*, blink::WebIconURL::Type);
   void didUpdateCurrentHistoryItem(blink::WebLocalFrame* frame);
   void didChangeScrollOffset(blink::WebLocalFrame* frame);
 
-  static bool IsReload(FrameMsg_Navigate_Type::Value navigation_type);
-
   static Referrer GetReferrerFromRequest(
       blink::WebFrame* frame,
       const blink::WebURLRequest& request);
@@ -709,14 +701,6 @@
   // Returns NULL if there is no such WebPlugin.
   blink::WebPlugin* GetWebPluginForFind();
 
-  // If we initiated a navigation, this function will populate |document_state|
-  // with the navigation information saved in OnNavigate().
-  void PopulateDocumentStateFromPending(DocumentState* document_state);
-
-  // Returns a new NavigationState populated with the navigation information
-  // saved in OnNavigate().
-  NavigationState* CreateNavigationStateFromPending();
-
 #if defined(OS_ANDROID)
   // Launch an Android content intent with the given URL.
   void LaunchAndroidContentIntent(const GURL& intent_url, size_t request_id);
@@ -830,13 +814,6 @@
   // PageGroupLoadDeferrer on the stack that interferes with swapping out.
   bool suppress_dialogs_until_swap_out_;
 
-  // Holds state pertaining to a navigation that we initiated.  This is held by
-  // the WebDataSource::ExtraData attribute.  We use pending_navigation_params_
-  // as a temporary holder for the state until the WebDataSource corresponding
-  // to the new navigation is created.  See DidCreateDataSource.
-  // TODO(nasko): Move to RenderFrame, as this is per-frame state.
-  scoped_ptr<NavigationParams> pending_navigation_params_;
-
   // Timer used to delay the updating of nav state (see SyncNavigationState).
   base::OneShotTimer<RenderViewImpl> nav_state_sync_timer_;
 
@@ -930,8 +907,6 @@
   // initialized.
   SpeechRecognitionDispatcher* speech_recognition_dispatcher_;
 
-  DevToolsAgent* devtools_agent_;
-
   // Mouse Lock dispatcher attached to this view.
   MouseLockDispatcher* mouse_lock_dispatcher_;
 
diff --git a/content/renderer/render_view_linux.cc b/content/renderer/render_view_linux.cc
index f4ef02e7..7f3ab8db 100644
--- a/content/renderer/render_view_linux.cc
+++ b/content/renderer/render_view_linux.cc
@@ -6,6 +6,7 @@
 
 #include "content/public/common/renderer_preferences.h"
 #include "third_party/WebKit/public/web/linux/WebFontRendering.h"
+#include "ui/gfx/font_render_params.h"
 
 using blink::WebFontRendering;
 
@@ -43,38 +44,6 @@
     }
 }
 
-SkFontHost::LCDOrder RendererPreferencesToSkiaLCDOrder(
-    const RendererPreferences& prefs) {
-  switch (prefs.subpixel_rendering) {
-    case gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE:
-    case gfx::FontRenderParams::SUBPIXEL_RENDERING_RGB:
-    case gfx::FontRenderParams::SUBPIXEL_RENDERING_VRGB:
-      return SkFontHost::kRGB_LCDOrder;
-    case gfx::FontRenderParams::SUBPIXEL_RENDERING_BGR:
-    case gfx::FontRenderParams::SUBPIXEL_RENDERING_VBGR:
-      return SkFontHost::kBGR_LCDOrder;
-    default:
-      NOTREACHED();
-      return SkFontHost::kRGB_LCDOrder;
-  }
-}
-
-SkFontHost::LCDOrientation RendererPreferencesToSkiaLCDOrientation(
-    const RendererPreferences& prefs) {
-  switch (prefs.subpixel_rendering) {
-    case gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE:
-    case gfx::FontRenderParams::SUBPIXEL_RENDERING_RGB:
-    case gfx::FontRenderParams::SUBPIXEL_RENDERING_BGR:
-      return SkFontHost::kHorizontal_LCDOrientation;
-    case gfx::FontRenderParams::SUBPIXEL_RENDERING_VRGB:
-    case gfx::FontRenderParams::SUBPIXEL_RENDERING_VBGR:
-      return SkFontHost::kVertical_LCDOrientation;
-    default:
-      NOTREACHED();
-      return SkFontHost::kHorizontal_LCDOrientation;
-  }
-}
-
 }  // namespace
 
 void RenderViewImpl::UpdateFontRenderingFromRendererPrefs() {
@@ -82,9 +51,12 @@
   WebFontRendering::setHinting(RendererPreferencesToSkiaHinting(prefs));
   WebFontRendering::setAutoHint(prefs.use_autohinter);
   WebFontRendering::setUseBitmaps(prefs.use_bitmaps);
-  WebFontRendering::setLCDOrder(RendererPreferencesToSkiaLCDOrder(prefs));
+  WebFontRendering::setLCDOrder(
+      gfx::FontRenderParams::SubpixelRenderingToSkiaLCDOrder(
+          prefs.subpixel_rendering));
   WebFontRendering::setLCDOrientation(
-      RendererPreferencesToSkiaLCDOrientation(prefs));
+      gfx::FontRenderParams::SubpixelRenderingToSkiaLCDOrientation(
+          prefs.subpixel_rendering));
   WebFontRendering::setAntiAlias(prefs.should_antialias_text);
   WebFontRendering::setSubpixelRendering(
       prefs.subpixel_rendering !=
diff --git a/content/renderer/render_view_win.cc b/content/renderer/render_view_win.cc
index b52d8e9..301d289 100644
--- a/content/renderer/render_view_win.cc
+++ b/content/renderer/render_view_win.cc
@@ -5,6 +5,7 @@
 #include "content/public/common/renderer_preferences.h"
 #include "content/renderer/render_view_impl.h"
 #include "third_party/WebKit/public/web/win/WebFontRendering.h"
+#include "ui/gfx/font_render_params.h"
 
 using blink::WebFontRendering;
 
@@ -23,6 +24,13 @@
 
   blink::WebFontRendering::setStatusFontMetrics(
       prefs.status_font_family_name.c_str(), prefs.status_font_height);
+
+  blink::WebFontRendering::setLCDOrder(
+      gfx::FontRenderParams::SubpixelRenderingToSkiaLCDOrder(
+          prefs.subpixel_rendering));
+  blink::WebFontRendering::setLCDOrientation(
+      gfx::FontRenderParams::SubpixelRenderingToSkiaLCDOrientation(
+          prefs.subpixel_rendering));
 }
 
 }  // namespace content
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 2ac75e6..be9b4d6 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -54,6 +54,7 @@
 #include "content/renderer/scheduler/web_scheduler_impl.h"
 #include "content/renderer/scheduler/webthread_impl_for_scheduler.h"
 #include "content/renderer/screen_orientation/screen_orientation_observer.h"
+#include "content/renderer/service_worker/webserviceworkercachestorage_impl.h"
 #include "content/renderer/webclipboard_impl.h"
 #include "content/renderer/webgraphicscontext3d_provider_impl.h"
 #include "content/renderer/webpublicsuffixlist_impl.h"
@@ -66,6 +67,7 @@
 #include "media/filters/stream_parser_factory.h"
 #include "net/base/mime_util.h"
 #include "net/base/net_util.h"
+#include "storage/common/database/database_identifier.h"
 #include "storage/common/quota/quota_types.h"
 #include "third_party/WebKit/public/platform/WebBatteryStatusListener.h"
 #include "third_party/WebKit/public/platform/WebBlobRegistry.h"
@@ -391,6 +393,16 @@
 
 //------------------------------------------------------------------------------
 
+blink::WebServiceWorkerCacheStorage* RendererBlinkPlatformImpl::cacheStorage(
+    const WebString& origin_identifier) {
+  const GURL origin =
+      storage::GetOriginFromIdentifier(origin_identifier.utf8());
+  return new WebServiceWorkerCacheStorageImpl(thread_safe_sender_.get(),
+                                              origin);
+}
+
+//------------------------------------------------------------------------------
+
 WebFileSystem* RendererBlinkPlatformImpl::fileSystem() {
   return WebFileSystemImpl::ThreadSpecificInstance(default_task_runner_);
 }
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index c084751..a9e39ad 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -34,6 +34,7 @@
 class WebDeviceMotionData;
 class WebDeviceOrientationData;
 class WebGraphicsContext3DProvider;
+class WebServiceWorkerCacheStorage;
 }
 
 namespace content {
@@ -101,6 +102,8 @@
   virtual void screenColorProfile(blink::WebVector<char>* to_profile);
   virtual blink::WebScrollbarBehavior* scrollbarBehavior();
   virtual blink::WebIDBFactory* idbFactory();
+  virtual blink::WebServiceWorkerCacheStorage* cacheStorage(
+      const blink::WebString& origin_identifier);
   virtual blink::WebFileSystem* fileSystem();
   virtual bool canAccelerate2dCanvas();
   virtual bool isThreadedCompositingEnabled();
diff --git a/content/renderer/scheduler/web_scheduler_impl.cc b/content/renderer/scheduler/web_scheduler_impl.cc
index efab3e24..53122bc 100644
--- a/content/renderer/scheduler/web_scheduler_impl.cc
+++ b/content/renderer/scheduler/web_scheduler_impl.cc
@@ -29,9 +29,8 @@
   return renderer_scheduler_->CanExceedIdleDeadlineIfRequired();
 }
 
-void WebSchedulerImpl::runIdleTask(
-    scoped_ptr<blink::WebScheduler::IdleTask> task,
-    base::TimeTicks deadline) {
+void WebSchedulerImpl::runIdleTask(scoped_ptr<blink::WebThread::IdleTask> task,
+                                   base::TimeTicks deadline) {
   task->run((deadline - base::TimeTicks()).InSecondsF());
 }
 
@@ -40,9 +39,9 @@
 }
 
 void WebSchedulerImpl::postIdleTask(const blink::WebTraceLocation& web_location,
-                                    blink::WebScheduler::IdleTask* task) {
+                                    blink::WebThread::IdleTask* task) {
   DCHECK(idle_task_runner_);
-  scoped_ptr<blink::WebScheduler::IdleTask> scoped_task(task);
+  scoped_ptr<blink::WebThread::IdleTask> scoped_task(task);
   tracked_objects::Location location(web_location.functionName(),
                                      web_location.fileName(), -1, nullptr);
   idle_task_runner_->PostIdleTask(
@@ -52,9 +51,9 @@
 
 void WebSchedulerImpl::postNonNestableIdleTask(
     const blink::WebTraceLocation& web_location,
-    blink::WebScheduler::IdleTask* task) {
+    blink::WebThread::IdleTask* task) {
   DCHECK(idle_task_runner_);
-  scoped_ptr<blink::WebScheduler::IdleTask> scoped_task(task);
+  scoped_ptr<blink::WebThread::IdleTask> scoped_task(task);
   tracked_objects::Location location(web_location.functionName(),
                                      web_location.fileName(), -1, nullptr);
   idle_task_runner_->PostNonNestableIdleTask(
@@ -64,9 +63,9 @@
 
 void WebSchedulerImpl::postIdleTaskAfterWakeup(
     const blink::WebTraceLocation& web_location,
-    blink::WebScheduler::IdleTask* task) {
+    blink::WebThread::IdleTask* task) {
   DCHECK(idle_task_runner_);
-  scoped_ptr<blink::WebScheduler::IdleTask> scoped_task(task);
+  scoped_ptr<blink::WebThread::IdleTask> scoped_task(task);
   tracked_objects::Location location(web_location.functionName(),
                                      web_location.fileName(), -1, nullptr);
   idle_task_runner_->PostIdleTaskAfterWakeup(
diff --git a/content/renderer/scheduler/web_scheduler_impl.h b/content/renderer/scheduler/web_scheduler_impl.h
index b0b60b7..c74430e 100644
--- a/content/renderer/scheduler/web_scheduler_impl.h
+++ b/content/renderer/scheduler/web_scheduler_impl.h
@@ -29,11 +29,11 @@
   virtual bool shouldYieldForHighPriorityWork();
   virtual bool canExceedIdleDeadlineIfRequired();
   virtual void postIdleTask(const blink::WebTraceLocation& location,
-                            blink::WebScheduler::IdleTask* task);
+                            blink::WebThread::IdleTask* task);
   virtual void postNonNestableIdleTask(const blink::WebTraceLocation& location,
-                                       blink::WebScheduler::IdleTask* task);
+                                       blink::WebThread::IdleTask* task);
   virtual void postIdleTaskAfterWakeup(const blink::WebTraceLocation& location,
-                                       blink::WebScheduler::IdleTask* task);
+                                       blink::WebThread::IdleTask* task);
   virtual void postLoadingTask(const blink::WebTraceLocation& location,
                                blink::WebThread::Task* task);
   virtual void postTimerTask(const blink::WebTraceLocation& location,
@@ -41,7 +41,7 @@
                              long long delayMs);
 
  private:
-  static void runIdleTask(scoped_ptr<blink::WebScheduler::IdleTask> task,
+  static void runIdleTask(scoped_ptr<blink::WebThread::IdleTask> task,
                           base::TimeTicks deadline);
   static void runTask(scoped_ptr<blink::WebThread::Task> task);
 
diff --git a/content/renderer/service_worker/embedded_worker_context_client.cc b/content/renderer/service_worker/embedded_worker_context_client.cc
index e2c50d84..c393502 100644
--- a/content/renderer/service_worker/embedded_worker_context_client.cc
+++ b/content/renderer/service_worker/embedded_worker_context_client.cc
@@ -140,11 +140,6 @@
   return service_worker_scope_;
 }
 
-blink::WebServiceWorkerCacheStorage*
-    EmbeddedWorkerContextClient::cacheStorage() {
-  return script_context_->cache_storage();
-}
-
 void EmbeddedWorkerContextClient::didPauseAfterDownload() {
   Send(new EmbeddedWorkerHostMsg_DidPauseAfterDownload(embedded_worker_id_));
 }
diff --git a/content/renderer/service_worker/embedded_worker_context_client.h b/content/renderer/service_worker/embedded_worker_context_client.h
index 5dcacd9..248591e 100644
--- a/content/renderer/service_worker/embedded_worker_context_client.h
+++ b/content/renderer/service_worker/embedded_worker_context_client.h
@@ -21,7 +21,6 @@
 namespace blink {
 class WebDataSource;
 class WebServiceWorkerProvider;
-class WebServiceWorkerCacheStorage;
 }
 
 namespace content {
@@ -62,7 +61,6 @@
   // WebServiceWorkerContextClient overrides, some of them are just dispatched
   // on to script_context_.
   virtual blink::WebURL scope() const;
-  virtual blink::WebServiceWorkerCacheStorage* cacheStorage();
   virtual void didPauseAfterDownload();
   virtual void getClients(const blink::WebServiceWorkerClientQueryOptions&,
                           blink::WebServiceWorkerClientsCallbacks*);
@@ -137,11 +135,6 @@
   }
   ThreadSafeSender* thread_safe_sender() { return sender_.get(); }
 
-  // Only needed by ServiceWorkerScriptContext when creating a
-  // WebServiceWorkerCacheStorageImpl
-  // TODO(jsbell): Remove when no longer needed. crbug.com/439389
-  const GURL origin() const { return service_worker_scope_.GetOrigin(); }
-
  private:
   void OnMessageToWorker(int thread_id,
                          int embedded_worker_id,
diff --git a/content/renderer/service_worker/service_worker_script_context.cc b/content/renderer/service_worker/service_worker_script_context.cc
index a0eaa1e8..37205205 100644
--- a/content/renderer/service_worker/service_worker_script_context.cc
+++ b/content/renderer/service_worker/service_worker_script_context.cc
@@ -16,7 +16,6 @@
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/public/common/referrer.h"
 #include "content/renderer/service_worker/embedded_worker_context_client.h"
-#include "content/renderer/service_worker/webserviceworkercachestorage_impl.h"
 #include "ipc/ipc_message.h"
 #include "third_party/WebKit/public/platform/WebCrossOriginServiceWorkerClient.h"
 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
@@ -99,9 +98,6 @@
     EmbeddedWorkerContextClient* embedded_context,
     blink::WebServiceWorkerContextProxy* proxy)
     : embedded_context_(embedded_context),
-      cache_storage_(new WebServiceWorkerCacheStorageImpl(
-          embedded_context->thread_safe_sender(),
-          embedded_context->origin())),
       proxy_(proxy) {
 }
 
diff --git a/content/renderer/service_worker/service_worker_script_context.h b/content/renderer/service_worker/service_worker_script_context.h
index 07b58ca0..2def78f 100644
--- a/content/renderer/service_worker/service_worker_script_context.h
+++ b/content/renderer/service_worker/service_worker_script_context.h
@@ -16,11 +16,8 @@
 #include "base/time/time.h"
 #include "content/child/webmessageportchannel_impl.h"
 #include "content/common/service_worker/service_worker_types.h"
-#include "content/renderer/service_worker/service_worker_cache_storage_dispatcher.h"
-#include "content/renderer/service_worker/webserviceworkercachestorage_impl.h"
 #include "third_party/WebKit/public/platform/WebGeofencingEventType.h"
 #include "third_party/WebKit/public/platform/WebMessagePortChannel.h"
-#include "third_party/WebKit/public/platform/WebServiceWorkerCacheStorage.h"
 #include "third_party/WebKit/public/platform/WebServiceWorkerClientsClaimCallbacks.h"
 #include "third_party/WebKit/public/platform/WebServiceWorkerClientsInfo.h"
 #include "third_party/WebKit/public/platform/WebServiceWorkerError.h"
@@ -102,11 +99,6 @@
   // in the browser process.
   int GetRoutingID() const;
 
-  // TODO(jsbell): Remove when CacheStorage creation is moved. crbug.com/439389
-  blink::WebServiceWorkerCacheStorage* cache_storage() const {
-    return cache_storage_.get();
-  }
-
  private:
   typedef IDMap<blink::WebServiceWorkerClientsCallbacks, IDMapOwnPointer>
       ClientsCallbacksMap;
@@ -158,8 +150,6 @@
   // Not owned; embedded_context_ owns this.
   EmbeddedWorkerContextClient* embedded_context_;
 
-  scoped_ptr<WebServiceWorkerCacheStorageImpl> cache_storage_;
-
   // Not owned; this object is destroyed when proxy_ becomes invalid.
   blink::WebServiceWorkerContextProxy* proxy_;
 
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 96cee7c0..87d31bf 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -452,7 +452,7 @@
 
 repack("pak") {
   sources = [
-    "$root_gen_dir/blink/public/resources/blink_resources.pak",
+    "$root_gen_dir/blink/public/resources/blink_resources_100_percent.pak",
     "$root_gen_dir/content/app/resources/content_resources_100_percent.pak",
     "$root_gen_dir/content/app/strings/content_strings_en-US.pak",
     "$root_gen_dir/content/browser/tracing/tracing_resources.pak",
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
index bc460bf..a5a94719 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
@@ -71,7 +71,8 @@
 
         setContentView(R.layout.content_shell_activity);
         mShellManager = (ShellManager) findViewById(R.id.shell_container);
-        mWindowAndroid = new ActivityWindowAndroid(this);
+        final boolean listenToActivityState = true;
+        mWindowAndroid = new ActivityWindowAndroid(this, listenToActivityState);
         mWindowAndroid.restoreInstanceState(savedInstanceState);
         mShellManager.setWindow(mWindowAndroid);
         // Set up the animation placeholder to be the SurfaceView. This disables the
@@ -183,14 +184,6 @@
     }
 
     @Override
-    protected void onStop() {
-        super.onStop();
-
-        ContentViewCore contentViewCore = getActiveContentViewCore();
-        if (contentViewCore != null) contentViewCore.onHide();
-    }
-
-    @Override
     protected void onStart() {
         super.onStart();
 
diff --git a/content/shell/browser/layout_test/layout_test_content_browser_client.cc b/content/shell/browser/layout_test/layout_test_content_browser_client.cc
index 82a04df..93899453 100644
--- a/content/shell/browser/layout_test/layout_test_content_browser_client.cc
+++ b/content/shell/browser/layout_test/layout_test_content_browser_client.cc
@@ -7,6 +7,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigator_connect_context.h"
+#include "content/public/browser/permission_type.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/shell/browser/layout_test/layout_test_browser_context.h"
@@ -88,7 +89,7 @@
     bool user_gesture,
     const base::Callback<void(PermissionStatus)>& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (permission == content::PERMISSION_NOTIFICATIONS) {
+  if (permission == PermissionType::NOTIFICATIONS) {
     BrowserThread::PostTask(
         BrowserThread::IO,
         FROM_HERE,
diff --git a/content/shell/browser/layout_test/layout_test_content_browser_client.h b/content/shell/browser/layout_test/layout_test_content_browser_client.h
index 60c0495..328ce61 100644
--- a/content/shell/browser/layout_test/layout_test_content_browser_client.h
+++ b/content/shell/browser/layout_test/layout_test_content_browser_client.h
@@ -5,11 +5,11 @@
 #ifndef CONTENT_SHELL_BROWSER_LAYOUT_TEST_LAYOUT_TEST_CONTENT_BROWSER_CLIENT_H_
 #define CONTENT_SHELL_BROWSER_LAYOUT_TEST_LAYOUT_TEST_CONTENT_BROWSER_CLIENT_H_
 
-#include "content/public/browser/permission_type.h"
 #include "content/shell/browser/shell_content_browser_client.h"
 
 namespace content {
 
+enum class PermissionType;
 class LayoutTestBrowserContext;
 class LayoutTestNotificationManager;
 
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 90cc70c..f7fa9db1 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -12,6 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "content/public/browser/client_certificate_delegate.h"
 #include "content/public/browser/page_navigator.h"
+#include "content/public/browser/permission_type.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/storage_partition.h"
@@ -305,7 +306,7 @@
     const base::Callback<void(PermissionStatus)>& callback) {
   // Some Geolocation tests on Android are still expecting to have the
   // permission granted. See https://crbug.com/463514.
-  if (permission == PERMISSION_GEOLOCATION) {
+  if (permission == PermissionType::GEOLOCATION) {
     callback.Run(PERMISSION_STATUS_GRANTED);
     return;
   }
diff --git a/content/shell/browser/shell_network_delegate.cc b/content/shell/browser/shell_network_delegate.cc
index 135bba3..633fb46 100644
--- a/content/shell/browser/shell_network_delegate.cc
+++ b/content/shell/browser/shell_network_delegate.cc
@@ -4,8 +4,6 @@
 
 #include "content/shell/browser/shell_network_delegate.h"
 
-#include "base/command_line.h"
-#include "content/public/common/content_switches.h"
 #include "net/base/net_errors.h"
 #include "net/base/static_cookie_policy.h"
 #include "net/url_request/url_request.h"
@@ -116,9 +114,4 @@
   return false;
 }
 
-bool ShellNetworkDelegate::OnFirstPartyOnlyCookieExperimentEnabled() const {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableExperimentalWebPlatformFeatures);
-}
-
 }  // namespace content
diff --git a/content/shell/browser/shell_network_delegate.h b/content/shell/browser/shell_network_delegate.h
index a23f5ff..75faa9a 100644
--- a/content/shell/browser/shell_network_delegate.h
+++ b/content/shell/browser/shell_network_delegate.h
@@ -54,7 +54,6 @@
   bool OnCanAccessFile(const net::URLRequest& request,
                        const base::FilePath& path) const override;
   bool OnCanThrottleRequest(const net::URLRequest& request) const override;
-  bool OnFirstPartyOnlyCookieExperimentEnabled() const override;
 
   DISALLOW_COPY_AND_ASSIGN(ShellNetworkDelegate);
 };
diff --git a/content/shell/common/shell_content_client.cc b/content/shell/common/shell_content_client.cc
index 9b9781d..a438823 100644
--- a/content/shell/common/shell_content_client.cc
+++ b/content/shell/common/shell_content_client.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
+#include "blink/public/resources/grit/blink_resources.h"
 #include "content/app/resources/grit/content_resources.h"
 #include "content/app/strings/grit/content_strings.h"
 #include "content/public/common/content_switches.h"
@@ -64,7 +65,7 @@
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kRunLayoutTest)) {
     switch (resource_id) {
-      case IDR2_BROKENIMAGE:
+      case IDR_BROKENIMAGE:
 #if defined(OS_MACOSX)
         resource_id = IDR_CONTENT_SHELL_MISSING_IMAGE_PNG;
 #else
@@ -72,7 +73,7 @@
 #endif
         break;
 
-      case IDR2_TEXTAREA_RESIZER:
+      case IDR_TEXTAREA_RESIZER:
         resource_id = IDR_CONTENT_SHELL_TEXT_AREA_RESIZE_CORNER_PNG;
         break;
     }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 9dc01e9a..85f23be2 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -448,6 +448,7 @@
       ":test_support",
       "//base/allocator",
       "//base/test:test_support",
+      "//content/browser/background_sync:background_sync_proto",
       "//content/browser/notifications:notification_proto",
       "//content/browser/service_worker:service_worker_proto",
       "//content/browser/speech/proto",
diff --git a/content/test/data/media/peerconnection-call.html b/content/test/data/media/peerconnection-call.html
index 2912cd4..68fd5c0 100644
--- a/content/test/data/media/peerconnection-call.html
+++ b/content/test/data/media/peerconnection-call.html
@@ -156,50 +156,28 @@
     });
   }
 
-  // First makes a call between pc1 and pc2, and then makes a call between pc3
-  // and pc4. The stream sent from pc3 to pc4 is the stream received on pc1.
-  // The stream sent from pc4 to pc3 is cloned from the stream received on pc2
-  // to test that cloning of remote video tracks works as intended.
+  // First makes a call between pc1 and pc2 where a stream is sent from pc1 to
+  // pc2. The stream sent from pc1 to pc2 is cloned from the stream received on 
+  // pc2  to test that cloning of remote video tracks works as intended and is 
+  // sent back to pc1.
   function callAndForwardRemoteStream(constraints) {
     createConnections(null);
     navigator.webkitGetUserMedia(constraints,
-                                 addStreamToBothConnectionsAndNegotiate,
+                                 addStreamToTheFirstConnectionAndNegotiate,
                                  printGetUserMediaError);
-    var gotRemoteStream1 = false;
-    var gotRemoteStream2 = false;
-
-    var onRemoteStream1 = function() {
-      gotRemoteStream1 = true;
-      maybeCallEstablished();
-    }
-
     var onRemoteStream2 = function() {
-      gotRemoteStream2 = true;
-      maybeCallEstablished();
+      // Video has been detected to be playing in pc2. Clone the received 
+      // stream and send it back to pc1.
+      gSecondConnection.addStream(gRemoteStreams['remote-view-2'].clone());
+      negotiate();
     }
 
-    var maybeCallEstablished = function() {
-      if (gotRemoteStream1 && gotRemoteStream2) {
-        onCallEstablished();
-      }
-    }
-
-    var onCallEstablished = function() {
-      thirdConnection = createConnection(null, 'remote-view-3');
-      thirdConnection.addStream(gRemoteStreams['remote-view-1']);
-
-      fourthConnection = createConnection(null, 'remote-view-4');
-      fourthConnection.addStream(gRemoteStreams['remote-view-2'].clone());
-
-      negotiateBetween(thirdConnection, fourthConnection);
-
-      waitForVideo('remote-view-3');
-      waitForVideo('remote-view-4');
-    }
-
-    // Do the forwarding after we have received video.
-    detectVideoPlaying('remote-view-1', onRemoteStream1);
+    // Wait for remove video to be playing in pc2. Once video is playing,
+    // forward the remove stream from pc2 to pc1.
     detectVideoPlaying('remote-view-2', onRemoteStream2);
+
+    // Wait for video to be forwarded back to connection 1.
+    waitForVideo('remote-view-1');
   }
 
   // First makes a call between pc1 and pc2, and then construct a new media
diff --git a/content/test/data/screen_orientation/screen_orientation_lock_disabled.html b/content/test/data/screen_orientation/screen_orientation_lock_disabled.html
new file mode 100644
index 0000000..e7faecf
--- /dev/null
+++ b/content/test/data/screen_orientation/screen_orientation_lock_disabled.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<script>
+  screen.orientation.lock('portrait-secondary').then(function() {
+    document.location.hash = '#success';
+  }, function(e) {
+    document.location.hash = '#' + e.name;
+  });
+</script>
+<body>
+  <div>Starting...</div>
+</body>
+</html>
diff --git a/content/test/gpu/gpu_tests/memory_test.py b/content/test/gpu/gpu_tests/memory_test.py
index 576f13f..e8fe31a 100644
--- a/content/test/gpu/gpu_tests/memory_test.py
+++ b/content/test/gpu/gpu_tests/memory_test.py
@@ -5,9 +5,9 @@
 import page_sets
 
 from telemetry import benchmark
-from telemetry.page import page_test
 from telemetry.core.platform import tracing_category_filter
 from telemetry.core.platform import tracing_options
+from telemetry.page import page_test
 from telemetry.timeline import counter
 from telemetry.timeline import model
 
diff --git a/content/test/gpu/gpu_tests/trace_test.py b/content/test/gpu/gpu_tests/trace_test.py
index 0238045..72126c5 100644
--- a/content/test/gpu/gpu_tests/trace_test.py
+++ b/content/test/gpu/gpu_tests/trace_test.py
@@ -1,13 +1,13 @@
 # Copyright (c) 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.
-import trace_test_expectations
 import page_sets
+import trace_test_expectations
 
 from telemetry import benchmark
-from telemetry.page import page_test
 from telemetry.core.platform import tracing_category_filter
 from telemetry.core.platform import tracing_options
+from telemetry.page import page_test
 from telemetry.timeline import model as model_module
 
 TOPLEVEL_GL_CATEGORY = 'gpu_toplevel'
diff --git a/content/test/gpu/gpu_tests/webgl_conformance.py b/content/test/gpu/gpu_tests/webgl_conformance.py
index 1de2fbb..247ed84 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance.py
@@ -10,8 +10,8 @@
 
 from telemetry import benchmark as benchmark_module
 from telemetry.core import util
-from telemetry.page import page_set
 from telemetry.page import page as page_module
+from telemetry.page import page_set
 from telemetry.page import page_test
 
 
diff --git a/content/test/gpu/gpu_tests/webgl_robustness.py b/content/test/gpu/gpu_tests/webgl_robustness.py
index 0612760..464bbf7f 100644
--- a/content/test/gpu/gpu_tests/webgl_robustness.py
+++ b/content/test/gpu/gpu_tests/webgl_robustness.py
@@ -6,9 +6,9 @@
 from telemetry.page import page_set
 from telemetry.page import page_test
 
-from webgl_conformance import WebglConformanceValidator
 from webgl_conformance import conformance_harness_script
 from webgl_conformance import conformance_path
+from webgl_conformance import WebglConformanceValidator
 
 
 robustness_harness_script = conformance_harness_script + r"""
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index 6ef20c1..93bc6025 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -268,12 +268,4 @@
   url_loader->CallOnResponseStarted(response, MakeEmptyStream());
 }
 
-void TestRenderFrameHost::SendBeforeUnloadHandlersPresent(bool present) {
-  OnBeforeUnloadHandlersPresent(present);
-}
-
-void TestRenderFrameHost::SendUnloadHandlersPresent(bool present) {
-  OnUnloadHandlersPresent(present);
-}
-
 }  // namespace content
diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h
index 333c2d7..ebc79575 100644
--- a/content/test/test_render_frame_host.h
+++ b/content/test/test_render_frame_host.h
@@ -114,12 +114,6 @@
   // redirect step is ignored.
   void PrepareForCommitWithServerRedirect(const GURL& redirect_url);
 
-  // Simulate receiving a FrameHostMsg_BeforeUnloadHandlersPresent.
-  void SendBeforeUnloadHandlersPresent(bool present);
-
-  // Simulate receiving a FrameHostMsg_UnloadHandlersPresent.
-  void SendUnloadHandlersPresent(bool present);
-
  private:
   TestRenderFrameHostCreationObserver child_creation_observer_;
 
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index a8ea64b..48c8f82 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -103,6 +103,8 @@
     "bluetooth_task_manager_win.h",
     "bluetooth_uuid.cc",
     "bluetooth_uuid.h",
+    "uribeacon/uri_encoder.cc",
+    "uribeacon/uri_encoder.h",
   ]
 
   defines = [ "DEVICE_BLUETOOTH_IMPLEMENTATION" ]
diff --git a/device/bluetooth/bluetooth.gyp b/device/bluetooth/bluetooth.gyp
index f4d3f1fa..5b3bcfd4 100644
--- a/device/bluetooth/bluetooth.gyp
+++ b/device/bluetooth/bluetooth.gyp
@@ -106,6 +106,8 @@
         'bluetooth_task_manager_win.h',
         'bluetooth_uuid.cc',
         'bluetooth_uuid.h',
+        'uribeacon/uri_encoder.cc',
+        'uribeacon/uri_encoder.h',
       ],
       'conditions': [
         ['chromeos==1', {
diff --git a/device/bluetooth/bluetooth_audio_sink.h b/device/bluetooth/bluetooth_audio_sink.h
index c7de6072..37359256 100644
--- a/device/bluetooth/bluetooth_audio_sink.h
+++ b/device/bluetooth/bluetooth_audio_sink.h
@@ -75,12 +75,14 @@
 
     // Called when there is audio data available. |audio_sink| indicates the
     // object being changed. |data| is the pointer to the audio data and |size|
-    // is the number of bytes in |data|. This method provides the raw audio data
-    // which hasn't been processed, so RTP assembling and SBC decoding need to
-    // be handled in order to get PCM data.
+    // is the number of bytes in |data|. |read_mtu| is the max size of the RTP
+    // packet. This method provides the raw audio data which hasn't been
+    // processed, so RTP assembling and SBC decoding need to be handled in order
+    // to get PCM data.
     virtual void BluetoothAudioSinkDataAvailable(BluetoothAudioSink* audio_sink,
                                                  char* data,
-                                                 size_t size) = 0;
+                                                 size_t size,
+                                                 uint16_t read_mtu) = 0;
   };
 
   // The ErrorCallback is used for the methods that can fail in which case it
diff --git a/device/bluetooth/bluetooth_audio_sink_chromeos.cc b/device/bluetooth/bluetooth_audio_sink_chromeos.cc
index 0f76be8ab..dcd751bc 100644
--- a/device/bluetooth/bluetooth_audio_sink_chromeos.cc
+++ b/device/bluetooth/bluetooth_audio_sink_chromeos.cc
@@ -400,8 +400,9 @@
   }
 
   VLOG(1) << "ReadFromFile - read " << size << " bytes";
-  FOR_EACH_OBSERVER(BluetoothAudioSink::Observer, observers_,
-                    BluetoothAudioSinkDataAvailable(this, data_.get(), size));
+  FOR_EACH_OBSERVER(
+      BluetoothAudioSink::Observer, observers_,
+      BluetoothAudioSinkDataAvailable(this, data_.get(), size, read_mtu_));
 }
 
 void BluetoothAudioSinkChromeOS::StateChanged(
diff --git a/device/bluetooth/bluetooth_audio_sink_chromeos_unittest.cc b/device/bluetooth/bluetooth_audio_sink_chromeos_unittest.cc
index b415e73..9781dec1 100644
--- a/device/bluetooth/bluetooth_audio_sink_chromeos_unittest.cc
+++ b/device/bluetooth/bluetooth_audio_sink_chromeos_unittest.cc
@@ -58,10 +58,12 @@
 
   void BluetoothAudioSinkDataAvailable(BluetoothAudioSink* audio_sink,
                                        char* data,
-                                       size_t size) override {
+                                       size_t size,
+                                       uint16_t read_mtu) override {
     total_read_ += size;
     data_.clear();
     data_.insert(data_.begin(), data, data + size);
+    read_mtu_ = read_mtu;
   }
 
   int state_changed_count_;
@@ -69,6 +71,7 @@
   int data_available_count_;
   size_t total_read_;
   std::vector<char> data_;
+  uint16_t read_mtu_;
   BluetoothAudioSink::State state_;
 
  private:
@@ -838,6 +841,8 @@
   EXPECT_EQ(observer_->state_changed_count_, 3);
   EXPECT_EQ(observer_->total_read_, data_one.size());
   EXPECT_EQ(observer_->data_, data_one);
+  EXPECT_EQ(observer_->read_mtu_,
+            FakeBluetoothMediaTransportClient::kDefaultReadMtu);
 }
 
 // Tests the case where the remote device pauses and resume audio streaming.
@@ -873,6 +878,8 @@
   message_loop_.RunUntilIdle();
 
   EXPECT_EQ(observer_->data_, data_one);
+  EXPECT_EQ(observer_->read_mtu_,
+            FakeBluetoothMediaTransportClient::kDefaultReadMtu);
   EXPECT_EQ(observer_->state_changed_count_, 3);
   EXPECT_EQ(observer_->total_read_, data_one.size());
 
@@ -891,6 +898,8 @@
   message_loop_.RunUntilIdle();
 
   EXPECT_EQ(observer_->data_, data_two);
+  EXPECT_EQ(observer_->read_mtu_,
+            FakeBluetoothMediaTransportClient::kDefaultReadMtu);
   EXPECT_EQ(observer_->state_changed_count_, 6);
   EXPECT_EQ(observer_->total_read_, data_two.size());
 }
@@ -927,6 +936,8 @@
   message_loop_.RunUntilIdle();
 
   EXPECT_EQ(observer_->data_, data_one);
+  EXPECT_EQ(observer_->read_mtu_,
+            FakeBluetoothMediaTransportClient::kDefaultReadMtu);
   EXPECT_EQ(observer_->state_changed_count_, 3);
   EXPECT_EQ(observer_->total_read_, data_one.size());
 
@@ -936,6 +947,8 @@
   message_loop_.RunUntilIdle();
 
   EXPECT_EQ(observer_->data_, data_two);
+  EXPECT_EQ(observer_->read_mtu_,
+            FakeBluetoothMediaTransportClient::kDefaultReadMtu);
   EXPECT_EQ(observer_->state_changed_count_, 3);
   EXPECT_EQ(observer_->total_read_, data_one.size() + data_two.size());
 }
diff --git a/device/bluetooth/uribeacon/uri_encoder.cc b/device/bluetooth/uribeacon/uri_encoder.cc
new file mode 100644
index 0000000..4c40d65a
--- /dev/null
+++ b/device/bluetooth/uribeacon/uri_encoder.cc
@@ -0,0 +1,131 @@
+// 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 "uri_encoder.h"
+
+using base::StringPiece;
+
+namespace {
+
+struct expansion {
+  uint8_t code;
+  const char* value;
+};
+
+// The two following data structures are the expansions code tables for URI
+// encoding described in the following specification:
+// https://github.com/google/uribeacon/blob/master/specification/AdvertisingMode.md
+
+// For the prefix of the URI.
+struct expansion prefix_expansions_list[] = {
+    {0, "http://www."},
+    {1, "https://www."},
+    {2, "http://"},
+    {3, "https://"},
+    {4, "urn:uuid:"},
+};
+
+// For the remaining part of the URI.
+struct expansion expansions_list[] = {
+    {0, ".com/"},
+    {1, ".org/"},
+    {2, ".edu/"},
+    {3, ".net/"},
+    {4, ".info/"},
+    {5, ".biz/"},
+    {6, ".gov/"},
+    {7, ".com"},
+    {8, ".org"},
+    {9, ".edu"},
+    {10, ".net"},
+    {11, ".info"},
+    {12, ".biz"},
+    {13, ".gov"},
+};
+
+struct expansion* CommonLookupExpansionByValue(struct expansion* table,
+                                               int table_length,
+                                               const std::string& input,
+                                               int input_index) {
+  int found = -1;
+  int found_length = -1;
+
+  for (int k = 0; k < table_length; k++) {
+    const char* value = table[k].value;
+    int len = static_cast<int>(strlen(table[k].value));
+    if (input_index + len <= static_cast<int>(input.size())) {
+      if (len > found_length && strncmp(&input[input_index], value, len) == 0) {
+        found = k;
+        found_length = len;
+      }
+    }
+  }
+  if (found == -1)
+    return NULL;
+  return &table[found];
+}
+
+struct expansion* LookupExpansionByValue(const std::string& input,
+                                         int input_index) {
+  return CommonLookupExpansionByValue(
+      expansions_list, arraysize(expansions_list), input, input_index);
+}
+
+struct expansion* LookupPrefixExpansionByValue(const std::string& input,
+                                               int input_index) {
+  return CommonLookupExpansionByValue(prefix_expansions_list,
+                                      arraysize(prefix_expansions_list), input,
+                                      input_index);
+}
+
+struct expansion* LookupExpansionByCode(const std::vector<uint8_t>& input,
+                                        int input_index) {
+  if (input[input_index] >= arraysize(expansions_list))
+    return NULL;
+  return &expansions_list[input[input_index]];
+}
+
+struct expansion* LookupPrefixExpansionByCode(const std::vector<uint8_t>& input,
+                                              int input_index) {
+  if (input[input_index] >= arraysize(prefix_expansions_list))
+    return NULL;
+  return &prefix_expansions_list[input[input_index]];
+}
+
+}  // namespace
+
+void device::EncodeUriBeaconUri(const std::string& input,
+                                std::vector<uint8_t>& output) {
+  int i = 0;
+  while (i < static_cast<int>(input.size())) {
+    struct expansion* exp;
+    if (i == 0)
+      exp = LookupPrefixExpansionByValue(input, i);
+    else
+      exp = LookupExpansionByValue(input, i);
+    if (exp == NULL) {
+      output.push_back(static_cast<uint8_t>(input[i]));
+      i++;
+    } else {
+      output.push_back(exp->code);
+      i += static_cast<int>(strlen(exp->value));
+    }
+  }
+}
+
+void device::DecodeUriBeaconUri(const std::vector<uint8_t>& input,
+                                std::string& output) {
+  int length = static_cast<int>(input.size());
+  for (int i = 0; i < length; i++) {
+    struct expansion* exp;
+    if (i == 0)
+      exp = LookupPrefixExpansionByCode(input, i);
+    else
+      exp = LookupExpansionByCode(input, i);
+    if (exp == NULL)
+      output.push_back(static_cast<char>(input[i]));
+    else
+      output.append(exp->value);
+  }
+}
diff --git a/device/bluetooth/uribeacon/uri_encoder.h b/device/bluetooth/uribeacon/uri_encoder.h
new file mode 100644
index 0000000..30d27d6
--- /dev/null
+++ b/device/bluetooth/uribeacon/uri_encoder.h
@@ -0,0 +1,42 @@
+// 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 DEVICE_BLUETOOTH_URIBEACON_URI_ENCODER_H_
+#define DEVICE_BLUETOOTH_URIBEACON_URI_ENCODER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+
+namespace device {
+
+// The following functions EncodeUriBeaconUri() and DecodeUriBeaconUri() helps
+// encoding/decoding URI with the UriBeacon encoding.
+//
+// Example usage:
+//
+//   std::vector<uint8_t> encoded;
+//   EncodeUriBeaconUri("http://web.mit.edu/", encoded)
+//   // encoded -> {2, 'w', 'e', 'b', '.', 'm', 'i', 't', 2}
+//
+//   const char encodedUri[] = {0, 'u', 'r', 'i', 'b', 'e', 'a', 'c', 'o', 'n',
+//       8};
+//   const std::vector<uint8_t> kEncodedUri(encodedUri, encodedUri +
+//   sizeof(encodedUri));
+//   std::string decoded;
+//   DecodeUriBeaconUri(kEncodedUri, decoded)
+//   // decoded -> "http://uribeacon.org"
+
+// Encodes the input string using URI encoding described in UriBeacon
+// specifications. |input| must be ASCII characters.
+void EncodeUriBeaconUri(const std::string& input, std::vector<uint8_t>& output);
+
+// Decodes the input string using URI encoding described in UriBeacon
+// specifications.
+void DecodeUriBeaconUri(const std::vector<uint8_t>& input, std::string& output);
+
+}  // namespace device
+
+#endif
diff --git a/device/bluetooth/uribeacon/uri_encoder_unittest.cc b/device/bluetooth/uribeacon/uri_encoder_unittest.cc
new file mode 100644
index 0000000..2e4fa9c
--- /dev/null
+++ b/device/bluetooth/uribeacon/uri_encoder_unittest.cc
@@ -0,0 +1,93 @@
+// 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 "device/bluetooth/uribeacon/uri_encoder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+TEST(UriEncoderTest, Url1) {
+  const std::string kUri = "http://123.com";
+  const char encoded_uri[] = {2, '1', '2', '3', 7};
+  const std::vector<uint8_t> kEncodedUri(encoded_uri,
+                                         encoded_uri + sizeof(encoded_uri));
+
+  std::vector<uint8_t> encoded;
+  std::string decoded;
+
+  EncodeUriBeaconUri(kUri, encoded);
+  EXPECT_EQ(kEncodedUri, encoded);
+
+  DecodeUriBeaconUri(encoded, decoded);
+  EXPECT_EQ(kUri, decoded);
+}
+
+TEST(UriEncoderTest, Url2) {
+  const std::string kUri = "http://www.abcdefghijklmnop.org";
+  const char encoded_uri[] = {
+      0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+      'o', 'p', 8};
+  const std::vector<uint8_t> kEncodedUri(encoded_uri,
+                                         encoded_uri + sizeof(encoded_uri));
+
+  std::vector<uint8_t> encoded;
+  std::string decoded;
+
+  EncodeUriBeaconUri(kUri, encoded);
+  EXPECT_EQ(kEncodedUri, encoded);
+
+  DecodeUriBeaconUri(encoded, decoded);
+  EXPECT_EQ(kUri, decoded);
+}
+
+TEST(UriEncoderTest, Url3) {
+  const std::string kUri = "https://123.com/123";
+  const char encoded_uri[] = {3, '1', '2', '3', 0, '1', '2', '3'};
+  const std::vector<uint8_t> kEncodedUri(encoded_uri,
+                                         encoded_uri + sizeof(encoded_uri));
+
+  std::vector<uint8_t> encoded;
+  std::string decoded;
+
+  EncodeUriBeaconUri(kUri, encoded);
+  EXPECT_EQ(kEncodedUri, encoded);
+
+  DecodeUriBeaconUri(encoded, decoded);
+  EXPECT_EQ(kUri, decoded);
+}
+
+TEST(UriEncoderTest, Url4) {
+  const std::string kUri = "http://www.uribeacon.org";
+  const char encoded_uri[] = {
+      0, 'u', 'r', 'i', 'b', 'e', 'a', 'c', 'o', 'n', 8};
+  const std::vector<uint8_t> kEncodedUri(encoded_uri,
+                                         encoded_uri + sizeof(encoded_uri));
+
+  std::vector<uint8_t> encoded;
+  std::string decoded;
+
+  EncodeUriBeaconUri(kUri, encoded);
+  EXPECT_EQ(kEncodedUri, encoded);
+
+  DecodeUriBeaconUri(encoded, decoded);
+  EXPECT_EQ(kUri, decoded);
+}
+
+TEST(UriEncoderTest, Url5) {
+  const std::string kUri = "http://web.mit.edu/";
+  const char encoded_uri[] = {2, 'w', 'e', 'b', '.', 'm', 'i', 't', 2};
+  const std::vector<uint8_t> kEncodedUri(encoded_uri,
+                                         encoded_uri + sizeof(encoded_uri));
+
+  std::vector<uint8_t> encoded;
+  std::string decoded;
+
+  EncodeUriBeaconUri(kUri, encoded);
+  EXPECT_EQ(kEncodedUri, encoded);
+
+  DecodeUriBeaconUri(encoded, decoded);
+  EXPECT_EQ(kUri, decoded);
+}
+
+}  // namespace device
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index ad3960a5..73879f55 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -99,7 +99,7 @@
 repack("shell_and_test_pak") {
   sources = [
     "$root_gen_dir/blink/devtools_resources.pak",
-    "$root_gen_dir/blink/public/resources/blink_resources.pak",
+    "$root_gen_dir/blink/public/resources/blink_resources_100_percent.pak",
     "$root_gen_dir/content/app/strings/content_strings_en-US.pak",
     "$root_gen_dir/content/content_resources.pak",
     "$root_gen_dir/content/shell/shell_resources.pak",
diff --git a/extensions/browser/api/bluetooth/bluetooth_api.cc b/extensions/browser/api/bluetooth/bluetooth_api.cc
index 2e1e4bc7..048bd118 100644
--- a/extensions/browser/api/bluetooth/bluetooth_api.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_api.cc
@@ -35,7 +35,7 @@
 
 extensions::BluetoothEventRouter* GetEventRouter(BrowserContext* context) {
   // Note: |context| is valid on UI thread only.
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return extensions::BluetoothAPI::Get(context)->event_router();
 }
 
@@ -54,13 +54,13 @@
 
 // static
 BluetoothAPI* BluetoothAPI::Get(BrowserContext* context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return GetFactoryInstance()->Get(context);
 }
 
 BluetoothAPI::BluetoothAPI(content::BrowserContext* context)
     : browser_context_(context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   EventRouter* event_router = EventRouter::Get(browser_context_);
   event_router->RegisterObserver(this,
                                  bluetooth::OnAdapterStateChanged::kEventName);
@@ -72,7 +72,7 @@
 BluetoothAPI::~BluetoothAPI() {}
 
 BluetoothEventRouter* BluetoothAPI::event_router() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!event_router_) {
     event_router_.reset(new BluetoothEventRouter(browser_context_));
   }
@@ -80,18 +80,18 @@
 }
 
 void BluetoothAPI::Shutdown() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   EventRouter::Get(browser_context_)->UnregisterObserver(this);
 }
 
 void BluetoothAPI::OnListenerAdded(const EventListenerInfo& details) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (event_router()->IsBluetoothSupported())
     event_router()->OnListenerAdded();
 }
 
 void BluetoothAPI::OnListenerRemoved(const EventListenerInfo& details) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (event_router()->IsBluetoothSupported())
     event_router()->OnListenerRemoved();
 }
@@ -113,7 +113,7 @@
 
 bool BluetoothGetDevicesFunction::DoWork(
     scoped_refptr<BluetoothAdapter> adapter) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   base::ListValue* device_list = new base::ListValue;
   SetResult(device_list);
@@ -140,7 +140,7 @@
 
 bool BluetoothGetDeviceFunction::DoWork(
     scoped_refptr<BluetoothAdapter> adapter) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   scoped_ptr<GetDevice::Params> params(GetDevice::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
diff --git a/extensions/browser/api/bluetooth/bluetooth_extension_function.cc b/extensions/browser/api/bluetooth/bluetooth_extension_function.cc
index 7a5eefe5..b51b647a 100644
--- a/extensions/browser/api/bluetooth/bluetooth_extension_function.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_extension_function.cc
@@ -20,18 +20,18 @@
 
 extensions::BluetoothEventRouter* GetEventRouter(
     content::BrowserContext* context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return extensions::BluetoothAPI::Get(context)->event_router();
 }
 
 bool IsBluetoothSupported(content::BrowserContext* context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return GetEventRouter(context)->IsBluetoothSupported();
 }
 
 void GetAdapter(const device::BluetoothAdapterFactory::AdapterCallback callback,
                 content::BrowserContext* context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   GetEventRouter(context)->GetAdapter(callback);
 }
 
@@ -47,7 +47,7 @@
 }
 
 bool BluetoothExtensionFunction::RunAsync() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!IsBluetoothSupported(browser_context())) {
     SetError(kPlatformNotSupported);
@@ -61,7 +61,7 @@
 
 void BluetoothExtensionFunction::RunOnAdapterReady(
     scoped_refptr<device::BluetoothAdapter> adapter) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DoWork(adapter);
 }
 
diff --git a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc
index face89a..71529ad 100644
--- a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc
+++ b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc
@@ -91,7 +91,7 @@
 
 extensions::BluetoothLowEnergyEventRouter* GetEventRouter(
     BrowserContext* context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return extensions::BluetoothLowEnergyAPI::Get(context)->event_router();
 }
 
@@ -114,20 +114,20 @@
 
 // static
 BluetoothLowEnergyAPI* BluetoothLowEnergyAPI::Get(BrowserContext* context) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return GetFactoryInstance()->Get(context);
 }
 
 BluetoothLowEnergyAPI::BluetoothLowEnergyAPI(BrowserContext* context)
     : event_router_(new BluetoothLowEnergyEventRouter(context)) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 BluetoothLowEnergyAPI::~BluetoothLowEnergyAPI() {
 }
 
 void BluetoothLowEnergyAPI::Shutdown() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
 namespace core_api {
@@ -139,7 +139,7 @@
 }
 
 bool BluetoothLowEnergyExtensionFunction::RunAsync() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!BluetoothManifestData::CheckLowEnergyPermitted(extension())) {
     error_ = kErrorPermissionDenied;
@@ -165,7 +165,7 @@
 }
 
 bool BluetoothLowEnergyConnectFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -208,7 +208,7 @@
 }
 
 bool BluetoothLowEnergyDisconnectFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -245,7 +245,7 @@
 }
 
 bool BluetoothLowEnergyGetServiceFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -278,7 +278,7 @@
 }
 
 bool BluetoothLowEnergyGetServicesFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -309,7 +309,7 @@
 }
 
 bool BluetoothLowEnergyGetCharacteristicFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -346,7 +346,7 @@
 }
 
 bool BluetoothLowEnergyGetCharacteristicsFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -390,7 +390,7 @@
 }
 
 bool BluetoothLowEnergyGetIncludedServicesFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -423,7 +423,7 @@
 }
 
 bool BluetoothLowEnergyGetDescriptorFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -459,7 +459,7 @@
 }
 
 bool BluetoothLowEnergyGetDescriptorsFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -502,7 +502,7 @@
 }
 
 bool BluetoothLowEnergyReadCharacteristicValueFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -560,7 +560,7 @@
 }
 
 bool BluetoothLowEnergyWriteCharacteristicValueFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -604,7 +604,7 @@
 }
 
 bool BluetoothLowEnergyStartCharacteristicNotificationsFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -652,7 +652,7 @@
 }
 
 bool BluetoothLowEnergyStopCharacteristicNotificationsFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -694,7 +694,7 @@
 }
 
 bool BluetoothLowEnergyReadDescriptorValueFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
@@ -751,7 +751,7 @@
 }
 
 bool BluetoothLowEnergyWriteDescriptorValueFunction::DoWork() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
diff --git a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
index 727532c..e1f59fb 100644
--- a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
+++ b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
@@ -187,7 +187,7 @@
 BluetoothLowEnergyEventRouter::BluetoothLowEnergyEventRouter(
     content::BrowserContext* context)
     : adapter_(NULL), browser_context_(context), weak_ptr_factory_(this) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(browser_context_);
   VLOG(1) << "Initializing BluetoothLowEnergyEventRouter.";
 
@@ -198,7 +198,7 @@
 }
 
 BluetoothLowEnergyEventRouter::~BluetoothLowEnergyEventRouter() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!adapter_.get())
     return;
 
@@ -207,7 +207,7 @@
 }
 
 bool BluetoothLowEnergyEventRouter::IsBluetoothSupported() const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return adapter_.get() ||
          BluetoothAdapterFactory::IsBluetoothAdapterAvailable();
 }
@@ -239,7 +239,7 @@
     const std::string& device_address,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!adapter_.get()) {
     VLOG(1) << "BluetoothAdapter not ready.";
     error_callback.Run(kStatusErrorFailed);
@@ -294,7 +294,7 @@
     const std::string& device_address,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!adapter_.get()) {
     VLOG(1) << "BluetoothAdapter not ready.";
     error_callback.Run(kStatusErrorFailed);
@@ -329,7 +329,7 @@
 bool BluetoothLowEnergyEventRouter::GetServices(
     const std::string& device_address,
     ServiceList* out_services) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(out_services);
   if (!adapter_.get()) {
     VLOG(1) << "BluetoothAdapter not ready.";
@@ -364,7 +364,7 @@
 BluetoothLowEnergyEventRouter::Status BluetoothLowEnergyEventRouter::GetService(
     const std::string& instance_id,
     apibtle::Service* out_service) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(out_service);
   if (!adapter_.get()) {
     VLOG(1) << "BluetoothAdapter not ready.";
@@ -385,7 +385,7 @@
 BluetoothLowEnergyEventRouter::GetIncludedServices(
     const std::string& instance_id,
     ServiceList* out_services) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(out_services);
   if (!adapter_.get()) {
     VLOG(1) << "BluetoothAdapter not ready.";
@@ -422,7 +422,7 @@
     const Extension* extension,
     const std::string& instance_id,
     CharacteristicList* out_characteristics) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(extension);
   DCHECK(out_characteristics);
   if (!adapter_.get()) {
@@ -468,7 +468,7 @@
     const Extension* extension,
     const std::string& instance_id,
     apibtle::Characteristic* out_characteristic) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(extension);
   DCHECK(out_characteristic);
   if (!adapter_.get()) {
@@ -500,7 +500,7 @@
     const Extension* extension,
     const std::string& instance_id,
     DescriptorList* out_descriptors) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(extension);
   DCHECK(out_descriptors);
   if (!adapter_.get()) {
@@ -547,7 +547,7 @@
     const Extension* extension,
     const std::string& instance_id,
     core_api::bluetooth_low_energy::Descriptor* out_descriptor) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(extension);
   DCHECK(out_descriptor);
   if (!adapter_.get()) {
@@ -578,7 +578,7 @@
     const std::string& instance_id,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(extension);
   if (!adapter_.get()) {
     VLOG(1) << "BluetoothAdapter not ready.";
@@ -618,7 +618,7 @@
     const std::vector<uint8>& value,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(extension);
   if (!adapter_.get()) {
     VLOG(1) << "BluetoothAdapter not ready.";
@@ -657,7 +657,7 @@
     const std::string& instance_id,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!adapter_.get()) {
     VLOG(1) << "BluetoothAdapter not ready.";
     error_callback.Run(kStatusErrorFailed);
@@ -722,7 +722,7 @@
     const std::string& instance_id,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!adapter_.get()) {
     VLOG(1) << "BluetoothAdapter not ready.";
     error_callback.Run(kStatusErrorFailed);
@@ -753,7 +753,7 @@
     const std::string& instance_id,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(extension);
   if (!adapter_.get()) {
     VLOG(1) << "BluetoothAdapter not ready.";
@@ -792,7 +792,7 @@
     const std::vector<uint8>& value,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(extension);
   if (!adapter_.get()) {
     VLOG(1) << "BluetoothAdapter not ready.";
@@ -834,7 +834,7 @@
     BluetoothAdapter* adapter,
     BluetoothDevice* device,
     BluetoothGattService* service) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(adapter, adapter_.get());
   VLOG(2) << "GATT service added: " << service->GetIdentifier();
 
@@ -849,7 +849,7 @@
     BluetoothAdapter* adapter,
     BluetoothDevice* device,
     BluetoothGattService* service) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(adapter, adapter_.get());
   VLOG(2) << "GATT service removed: " << service->GetIdentifier();
 
@@ -874,7 +874,7 @@
 void BluetoothLowEnergyEventRouter::GattDiscoveryCompleteForService(
     BluetoothAdapter* adapter,
     BluetoothGattService* service) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(adapter, adapter_.get());
   VLOG(2) << "GATT service discovery complete: " << service->GetIdentifier();
 
@@ -895,7 +895,7 @@
 void BluetoothLowEnergyEventRouter::GattServiceChanged(
     BluetoothAdapter* adapter,
     BluetoothGattService* service) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(adapter, adapter_.get());
   VLOG(2) << "GATT service changed: " << service->GetIdentifier();
   DCHECK(service_id_to_device_address_.find(service->GetIdentifier()) !=
@@ -915,7 +915,7 @@
 void BluetoothLowEnergyEventRouter::GattCharacteristicAdded(
     BluetoothAdapter* adapter,
     BluetoothGattCharacteristic* characteristic) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(adapter, adapter_.get());
   VLOG(2) << "GATT characteristic added: " << characteristic->GetIdentifier();
 
@@ -934,7 +934,7 @@
 void BluetoothLowEnergyEventRouter::GattCharacteristicRemoved(
     BluetoothAdapter* adapter,
     BluetoothGattCharacteristic* characteristic) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(adapter, adapter_.get());
   VLOG(2) << "GATT characteristic removed: " << characteristic->GetIdentifier();
 
@@ -952,7 +952,7 @@
 void BluetoothLowEnergyEventRouter::GattDescriptorAdded(
     BluetoothAdapter* adapter,
     BluetoothGattDescriptor* descriptor) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(adapter, adapter_.get());
   VLOG(2) << "GATT descriptor added: " << descriptor->GetIdentifier();
 
@@ -971,7 +971,7 @@
 void BluetoothLowEnergyEventRouter::GattDescriptorRemoved(
     BluetoothAdapter* adapter,
     BluetoothGattDescriptor* descriptor) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(adapter, adapter_.get());
   VLOG(2) << "GATT descriptor removed: " << descriptor->GetIdentifier();
 
@@ -990,7 +990,7 @@
     BluetoothAdapter* adapter,
     BluetoothGattCharacteristic* characteristic,
     const std::vector<uint8>& value) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(adapter, adapter_.get());
   VLOG(2) << "GATT characteristic value changed: "
           << characteristic->GetIdentifier();
@@ -1024,7 +1024,7 @@
     BluetoothAdapter* adapter,
     BluetoothGattDescriptor* descriptor,
     const std::vector<uint8>& value) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(adapter, adapter_.get());
   VLOG(2) << "GATT descriptor value changed: " << descriptor->GetIdentifier();
 
@@ -1065,7 +1065,7 @@
 }
 
 void BluetoothLowEnergyEventRouter::InitializeIdentifierMappings() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(service_id_to_device_address_.empty());
   DCHECK(chrc_id_to_service_id_.empty());
 
diff --git a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc
index 77d19d1a..c9c70b9 100644
--- a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc
+++ b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc
@@ -183,7 +183,7 @@
 BluetoothSocketCreateFunction::~BluetoothSocketCreateFunction() {}
 
 bool BluetoothSocketCreateFunction::Prepare() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   params_ = bluetooth_socket::Create::Params::Create(*args_);
   EXTENSION_FUNCTION_VALIDATE(params_.get());
@@ -264,7 +264,7 @@
 BluetoothSocketListenFunction::~BluetoothSocketListenFunction() {}
 
 bool BluetoothSocketListenFunction::Prepare() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!CreateParams())
     return false;
   socket_event_dispatcher_ = GetSocketEventDispatcher(browser_context());
@@ -443,7 +443,7 @@
     ~BluetoothSocketAbstractConnectFunction() {}
 
 bool BluetoothSocketAbstractConnectFunction::Prepare() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   params_ = bluetooth_socket::Connect::Params::Create(*args_);
   EXTENSION_FUNCTION_VALIDATE(params_.get());
 
@@ -540,7 +540,7 @@
 BluetoothSocketDisconnectFunction::~BluetoothSocketDisconnectFunction() {}
 
 bool BluetoothSocketDisconnectFunction::Prepare() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   params_ = bluetooth_socket::Disconnect::Params::Create(*args_);
   EXTENSION_FUNCTION_VALIDATE(params_.get());
   return true;
@@ -592,7 +592,7 @@
 BluetoothSocketSendFunction::~BluetoothSocketSendFunction() {}
 
 bool BluetoothSocketSendFunction::Prepare() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   params_ = bluetooth_socket::Send::Params::Create(*args_);
   EXTENSION_FUNCTION_VALIDATE(params_.get());
 
diff --git a/extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.cc b/extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.cc
index 3daadf3..662b9b7c 100644
--- a/extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.cc
+++ b/extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.cc
@@ -355,7 +355,7 @@
     void* browser_context_id,
     const std::string& extension_id,
     scoped_ptr<Event> event) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   content::BrowserContext* context =
       reinterpret_cast<content::BrowserContext*>(browser_context_id);
diff --git a/extensions/browser/api/cast_channel/cast_socket.cc b/extensions/browser/api/cast_channel/cast_socket.cc
index f0bd6940..b6348f4 100644
--- a/extensions/browser/api/cast_channel/cast_socket.cc
+++ b/extensions/browser/api/cast_channel/cast_socket.cc
@@ -45,7 +45,7 @@
 
 namespace {
 
-const int kMaxSelfSignedCertLifetimeInDays = 2;
+const int kMaxSelfSignedCertLifetimeInDays = 4;
 
 std::string FormatTimeForLogging(base::Time time) {
   base::Time::Exploded exploded_time;
@@ -197,7 +197,7 @@
   logger_->LogSocketEvent(channel_id_, proto::SSL_INFO_OBTAINED);
 
   // Ensure that the peer cert (which is self-signed) doesn't have an excessive
-  // life-time (i.e. no more than 2 days).
+  // remaining life-time.
   base::Time expiry = ssl_info.cert->valid_expiry();
   base::Time lifetimeLimit =
       base::Time::Now() +
diff --git a/extensions/browser/api/networking_private/networking_private_api.cc b/extensions/browser/api/networking_private/networking_private_api.cc
index ea4cebd..92270e9 100644
--- a/extensions/browser/api/networking_private/networking_private_api.cc
+++ b/extensions/browser/api/networking_private/networking_private_api.cc
@@ -200,6 +200,35 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// NetworkingPrivateForgetNetworkFunction
+
+NetworkingPrivateForgetNetworkFunction::
+    ~NetworkingPrivateForgetNetworkFunction() {
+}
+
+bool NetworkingPrivateForgetNetworkFunction::RunAsync() {
+  scoped_ptr<private_api::ForgetNetwork::Params> params =
+      private_api::ForgetNetwork::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  GetDelegate(browser_context())
+      ->ForgetNetwork(
+          params->network_guid,
+          base::Bind(&NetworkingPrivateForgetNetworkFunction::Success, this),
+          base::Bind(&NetworkingPrivateForgetNetworkFunction::Failure, this));
+  return true;
+}
+
+void NetworkingPrivateForgetNetworkFunction::Success() {
+  SendResponse(true);
+}
+
+void NetworkingPrivateForgetNetworkFunction::Failure(const std::string& error) {
+  error_ = error;
+  SendResponse(false);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // NetworkingPrivateGetNetworksFunction
 
 NetworkingPrivateGetNetworksFunction::~NetworkingPrivateGetNetworksFunction() {
diff --git a/extensions/browser/api/networking_private/networking_private_api.h b/extensions/browser/api/networking_private/networking_private_api.h
index 0c8b6d6..31606d0 100644
--- a/extensions/browser/api/networking_private/networking_private_api.h
+++ b/extensions/browser/api/networking_private/networking_private_api.h
@@ -125,6 +125,26 @@
   DISALLOW_COPY_AND_ASSIGN(NetworkingPrivateCreateNetworkFunction);
 };
 
+// Implements the chrome.networkingPrivate.createNetwork method.
+class NetworkingPrivateForgetNetworkFunction : public AsyncExtensionFunction {
+ public:
+  NetworkingPrivateForgetNetworkFunction() {}
+  DECLARE_EXTENSION_FUNCTION("networkingPrivate.forgetNetwork",
+                             NETWORKINGPRIVATE_FORGETNETWORK);
+
+ protected:
+  ~NetworkingPrivateForgetNetworkFunction() override;
+
+  // AsyncExtensionFunction overrides.
+  bool RunAsync() override;
+
+ private:
+  void Success();
+  void Failure(const std::string& error);
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkingPrivateForgetNetworkFunction);
+};
+
 // Implements the chrome.networkingPrivate.getNetworks method.
 class NetworkingPrivateGetNetworksFunction : public AsyncExtensionFunction {
  public:
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos.cc b/extensions/browser/api/networking_private/networking_private_chromeos.cc
index 351bf0d..1179f4c 100644
--- a/extensions/browser/api/networking_private/networking_private_chromeos.cc
+++ b/extensions/browser/api/networking_private/networking_private_chromeos.cc
@@ -203,6 +203,21 @@
       base::Bind(&NetworkHandlerFailureCallback, failure_callback));
 }
 
+void NetworkingPrivateChromeOS::ForgetNetwork(
+    const std::string& guid,
+    const VoidCallback& success_callback,
+    const FailureCallback& failure_callback) {
+  std::string service_path, error;
+  if (!GetServicePathFromGuid(guid, &service_path, &error)) {
+    failure_callback.Run(error);
+    return;
+  }
+
+  GetManagedConfigurationHandler()->RemoveConfiguration(
+      service_path, success_callback,
+      base::Bind(&NetworkHandlerFailureCallback, failure_callback));
+}
+
 void NetworkingPrivateChromeOS::GetNetworks(
     const std::string& network_type,
     bool configured_only,
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos.h b/extensions/browser/api/networking_private/networking_private_chromeos.h
index d5afc510..f92221c 100644
--- a/extensions/browser/api/networking_private/networking_private_chromeos.h
+++ b/extensions/browser/api/networking_private/networking_private_chromeos.h
@@ -40,6 +40,9 @@
                      scoped_ptr<base::DictionaryValue> properties,
                      const StringCallback& success_callback,
                      const FailureCallback& failure_callback) override;
+  void ForgetNetwork(const std::string& guid,
+                     const VoidCallback& success_callback,
+                     const FailureCallback& failure_callback) override;
   void GetNetworks(const std::string& network_type,
                    bool configured_only,
                    bool visible_only,
diff --git a/extensions/browser/api/networking_private/networking_private_delegate.h b/extensions/browser/api/networking_private/networking_private_delegate.h
index fb29d04c..5f8f68f 100644
--- a/extensions/browser/api/networking_private/networking_private_delegate.h
+++ b/extensions/browser/api/networking_private/networking_private_delegate.h
@@ -99,6 +99,9 @@
                              scoped_ptr<base::DictionaryValue> properties,
                              const StringCallback& success_callback,
                              const FailureCallback& failure_callback) = 0;
+  virtual void ForgetNetwork(const std::string& guid,
+                             const VoidCallback& success_callback,
+                             const FailureCallback& failure_callback) = 0;
   virtual void GetNetworks(const std::string& network_type,
                            bool configured_only,
                            bool visible_only,
diff --git a/extensions/browser/api/networking_private/networking_private_linux.cc b/extensions/browser/api/networking_private/networking_private_linux.cc
index 5980a9b..1d9ad879 100644
--- a/extensions/browser/api/networking_private/networking_private_linux.cc
+++ b/extensions/browser/api/networking_private/networking_private_linux.cc
@@ -269,6 +269,14 @@
   ReportNotSupported("CreateNetwork", failure_callback);
 }
 
+void NetworkingPrivateLinux::ForgetNetwork(
+    const std::string& guid,
+    const VoidCallback& success_callback,
+    const FailureCallback& failure_callback) {
+  // TODO(zentaro): Implement for Linux.
+  ReportNotSupported("ForgetNetwork", failure_callback);
+}
+
 void NetworkingPrivateLinux::GetNetworks(
     const std::string& network_type,
     bool configured_only,
diff --git a/extensions/browser/api/networking_private/networking_private_linux.h b/extensions/browser/api/networking_private/networking_private_linux.h
index 80ee615..8bee05f 100644
--- a/extensions/browser/api/networking_private/networking_private_linux.h
+++ b/extensions/browser/api/networking_private/networking_private_linux.h
@@ -56,6 +56,9 @@
                      scoped_ptr<base::DictionaryValue> properties,
                      const StringCallback& success_callback,
                      const FailureCallback& failure_callback) override;
+  void ForgetNetwork(const std::string& guid,
+                     const VoidCallback& success_callback,
+                     const FailureCallback& failure_callback) override;
   void GetNetworks(const std::string& network_type,
                    bool configured_only,
                    bool visible_only,
diff --git a/extensions/browser/api/networking_private/networking_private_service_client.cc b/extensions/browser/api/networking_private/networking_private_service_client.cc
index 6528709..9859b5e 100644
--- a/extensions/browser/api/networking_private/networking_private_service_client.cc
+++ b/extensions/browser/api/networking_private/networking_private_service_client.cc
@@ -219,6 +219,14 @@
                  base::Owned(network_guid), base::Owned(error)));
 }
 
+void NetworkingPrivateServiceClient::ForgetNetwork(
+    const std::string& guid,
+    const VoidCallback& success_callback,
+    const FailureCallback& failure_callback) {
+  // TODO(mef): Implement for Win/Mac
+  failure_callback.Run(networking_private::kErrorNotSupported);
+}
+
 void NetworkingPrivateServiceClient::GetNetworks(
     const std::string& network_type,
     bool configured_only,
diff --git a/extensions/browser/api/networking_private/networking_private_service_client.h b/extensions/browser/api/networking_private/networking_private_service_client.h
index d8ae48e..364f3ae 100644
--- a/extensions/browser/api/networking_private/networking_private_service_client.h
+++ b/extensions/browser/api/networking_private/networking_private_service_client.h
@@ -65,6 +65,9 @@
                      scoped_ptr<base::DictionaryValue> properties_dict,
                      const StringCallback& success_callback,
                      const FailureCallback& failure_callback) override;
+  void ForgetNetwork(const std::string& guid,
+                     const VoidCallback& success_callback,
+                     const FailureCallback& failure_callback) override;
   void GetNetworks(const std::string& network_type,
                    bool configured_only,
                    bool visible_only,
diff --git a/extensions/browser/api/printer_provider/printer_provider_api.cc b/extensions/browser/api/printer_provider/printer_provider_api.cc
index f993f81..cf1fda6 100644
--- a/extensions/browser/api/printer_provider/printer_provider_api.cc
+++ b/extensions/browser/api/printer_provider/printer_provider_api.cc
@@ -16,6 +16,7 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/scoped_observer.h"
 #include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "extensions/browser/api/printer_provider/printer_provider_print_job.h"
 #include "extensions/browser/api/printer_provider_internal/printer_provider_internal_api.h"
@@ -510,6 +511,7 @@
   }
 
   print_job.content_type = job.content_type;
+  print_job.title = base::UTF16ToUTF8(job.job_title);
   int request_id = pending_print_requests_[extension_id].Add(job, callback);
 
   scoped_ptr<base::ListValue> internal_args(new base::ListValue);
diff --git a/extensions/browser/api/printer_provider/printer_provider_apitest.cc b/extensions/browser/api/printer_provider/printer_provider_apitest.cc
index bd494d7b..dfe5086 100644
--- a/extensions/browser/api/printer_provider/printer_provider_apitest.cc
+++ b/extensions/browser/api/printer_provider/printer_provider_apitest.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "extensions/browser/api/printer_provider/printer_provider_api.h"
 #include "extensions/browser/api/printer_provider/printer_provider_api_factory.h"
 #include "extensions/browser/api/printer_provider/printer_provider_print_job.h"
@@ -106,6 +107,7 @@
       const PrinterProviderAPI::PrintCallback& callback) {
     extensions::PrinterProviderPrintJob job;
     job.printer_id = extension_id + ":printer_id";
+    job.job_title = base::ASCIIToUTF16("Print job");
     job.ticket_json = "{}";
     job.content_type = "application/pdf";
     const unsigned char kDocumentBytes[] = {'b', 'y', 't', 'e', 's'};
@@ -130,6 +132,7 @@
     }
 
     job.printer_id = extension_id + ":printer_id";
+    job.job_title = base::ASCIIToUTF16("Print job");
     job.ticket_json = "{}";
     job.content_type = "image/pwg-raster";
 
diff --git a/extensions/browser/api/printer_provider/printer_provider_print_job.h b/extensions/browser/api/printer_provider/printer_provider_print_job.h
index 81fc9b8..4148d0d 100644
--- a/extensions/browser/api/printer_provider/printer_provider_print_job.h
+++ b/extensions/browser/api/printer_provider/printer_provider_print_job.h
@@ -11,6 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/strings/string16.h"
 
 namespace extensions {
 
@@ -30,6 +31,9 @@
   // chrome.printerProvider.onGetPrintersRequested event callback).
   std::string printer_id;
 
+  // The print job title.
+  base::string16 job_title;
+
   // The print job ticket.
   std::string ticket_json;
 
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 88d74b7..7c3d34e 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1043,6 +1043,13 @@
   DEVELOPERPRIVATE_DELETEEXTENSIONERRORS,
   FILEMANAGERPRIVATE_ISPIEXLOADERENABLED,
   HOTWORDPRIVATE_SPEAKERMODELEXISTSRESULT,
+  SETTINGSPRIVATE_SETBOOLEANPREF,
+  SETTINGSPRIVATE_SETNUMERICPREF,
+  SETTINGSPRIVATE_SETSTRINGPREF,
+  SETTINGSPRIVATE_SETURLPREF,
+  SETTINGSPRIVATE_GETALLPREFS,
+  SETTINGSPRIVATE_GETPREF,
+  NETWORKINGPRIVATE_FORGETNETWORK,
   // Last entry: Add new entries above and ensure to update
   // tools/metrics/histograms/histograms.xml.
   ENUM_BOUNDARY
diff --git a/extensions/browser/extension_icon_image.cc b/extensions/browser/extension_icon_image.cc
index 5ebc400..31ddd61 100644
--- a/extensions/browser/extension_icon_image.cc
+++ b/extensions/browser/extension_icon_image.cc
@@ -11,6 +11,7 @@
 #include "extensions/browser/image_loader.h"
 #include "extensions/browser/notification_types.h"
 #include "extensions/common/extension.h"
+#include "ui/base/layout.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/size_conversions.h"
@@ -223,7 +224,21 @@
   DCHECK(!rep.is_null());
   DCHECK_EQ(scale, rep.scale());
 
-  // Remove old representation if there is one.
+  // Remove fractional scale image representations as they may have become
+  // stale here. These images are generated by ImageSkia on request from
+  // supported scales like 1x, 2x, etc.
+  // TODO(oshima)
+  // A better approach might be to set the |image_| member using the ImageSkia
+  // copy from the image passed in and set the |image_skia_| member using
+  // image_.ToImageSkia(). However that does not work correctly as the
+  // ImageSkia from the image does not contain a source which breaks requests
+  // for scaled images.
+  std::vector<gfx::ImageSkiaRep> reps = image_skia_.image_reps();
+  for (const auto rep : reps) {
+    if (!ui::IsSupportedScale(rep.scale()))
+      image_skia_.RemoveRepresentation(rep.scale());
+  }
+
   image_skia_.RemoveRepresentation(scale);
   image_skia_.AddRepresentation(rep);
 
diff --git a/extensions/browser/extension_registry_observer.h b/extensions/browser/extension_registry_observer.h
index 31573b6..9520570 100644
--- a/extensions/browser/extension_registry_observer.h
+++ b/extensions/browser/extension_registry_observer.h
@@ -31,7 +31,8 @@
       const Extension* extension) {}
 
   // Called after an extension is unloaded. The extension no longer exists in
-  // any of the ExtensionRegistry sets (enabled, disabled, etc.).
+  // the set |ExtensionRegistry::enabled_extensions()|, but it can still be a
+  // member of one of the other sets, like disabled, blacklisted or terminated.
   virtual void OnExtensionUnloaded(content::BrowserContext* browser_context,
                                    const Extension* extension,
                                    UnloadedExtensionInfo::Reason reason) {}
diff --git a/extensions/browser/guest_view/extension_options/extension_options_constants.cc b/extensions/browser/guest_view/extension_options/extension_options_constants.cc
index b5d245f9..7ce80e5 100644
--- a/extensions/browser/guest_view/extension_options/extension_options_constants.cc
+++ b/extensions/browser/guest_view/extension_options/extension_options_constants.cc
@@ -9,6 +9,6 @@
 // API namespace.
 extern const char kAPINamespace[] = "extensionOptionsInternal";
 
-const char kExtensionId[] = "extensionId";
+const char kExtensionId[] = "extension";
 
 }  // namespace extensionoptions
diff --git a/extensions/browser/guest_view/extension_options/extension_options_guest.cc b/extensions/browser/guest_view/extension_options/extension_options_guest.cc
index 6041be6a..5ec8cb4 100644
--- a/extensions/browser/guest_view/extension_options/extension_options_guest.cc
+++ b/extensions/browser/guest_view/extension_options/extension_options_guest.cc
@@ -223,9 +223,17 @@
 void ExtensionOptionsGuest::DidNavigateMainFrame(
     const content::LoadCommittedDetails& details,
     const content::FrameNavigateParams& params) {
-  if (attached() && (params.url.GetOrigin() != options_page_.GetOrigin())) {
-    bad_message::ReceivedBadMessage(web_contents()->GetRenderProcessHost(),
-                                    bad_message::EOG_BAD_ORIGIN);
+  if (attached()) {
+    auto guest_zoom_controller =
+        ui_zoom::ZoomController::FromWebContents(web_contents());
+    guest_zoom_controller->SetZoomMode(
+        ui_zoom::ZoomController::ZOOM_MODE_ISOLATED);
+    SetGuestZoomLevelToMatchEmbedder();
+
+    if (params.url.GetOrigin() != options_page_.GetOrigin()) {
+      bad_message::ReceivedBadMessage(web_contents()->GetRenderProcessHost(),
+                                      bad_message::EOG_BAD_ORIGIN);
+    }
   }
 }
 
diff --git a/extensions/browser/guest_view/guest_view_base.cc b/extensions/browser/guest_view/guest_view_base.cc
index eefe4bb..cf518e4 100644
--- a/extensions/browser/guest_view/guest_view_base.cc
+++ b/extensions/browser/guest_view/guest_view_base.cc
@@ -12,6 +12,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/page_zoom.h"
 #include "content/public/common/url_constants.h"
@@ -233,6 +234,17 @@
     content::WebContents* guest_web_contents) {
   DCHECK(guest_web_contents);
 
+  // Create a ZoomController to allow the guest's contents to be zoomed.
+  // Do this before adding the GuestView as a WebContents Observer so that
+  // the GuestView and its derived classes can re-configure the ZoomController
+  // after the latter has handled WebContentsObserver events (observers are
+  // notified of events in the same order they are added as observers). For
+  // example, GuestViewBase may wish to put its guest into isolated zoom mode
+  // in DidNavigateMainFrame, but since ZoomController always resets to default
+  // zoom mode on this event, GuestViewBase would need to do so after
+  // ZoomController::DidNavigateMainFrame has completed.
+  ui_zoom::ZoomController::CreateForWebContents(guest_web_contents);
+
   // At this point, we have just created the guest WebContents, we need to add
   // an observer to the owner WebContents. This observer will be responsible
   // for destroying the guest WebContents if the owner goes away.
@@ -246,9 +258,6 @@
   GuestViewManager::FromBrowserContext(browser_context_)->
       AddGuest(guest_instance_id_, guest_web_contents);
 
-  // Create a ZoomController to allow the guest's contents to be zoomed.
-  ui_zoom::ZoomController::CreateForWebContents(guest_web_contents);
-
   // Populate the view instance ID if we have it on creation.
   create_params.GetInteger(guestview::kParameterInstanceId,
                            &view_instance_id_);
@@ -576,6 +585,13 @@
   delete this;
 }
 
+void GuestViewBase::DidNavigateMainFrame(
+    const content::LoadCommittedDetails& details,
+    const content::FrameNavigateParams& params) {
+  if (attached())
+    SetGuestZoomLevelToMatchEmbedder();
+}
+
 void GuestViewBase::ActivateContents(WebContents* web_contents) {
   if (!attached() || !embedder_web_contents()->GetDelegate())
     return;
@@ -782,26 +798,36 @@
   params.GetInteger(guestview::kAttributeMinHeight, &min_height);
   params.GetInteger(guestview::kAttributeMinWidth, &min_width);
 
-  // Set the normal size to the element size so that the guestview will fit the
-  // element initially if autosize is disabled.
-  double element_height = 0.0;
-  double element_width = 0.0;
-  params.GetDouble(guestview::kElementHeight, &element_height);
-  params.GetDouble(guestview::kElementWidth, &element_width);
-
-  // If the element size was provided in logical units (versus physical), then
-  // it will be converted to physical units.
-  bool element_size_is_logical = false;
-  params.GetBoolean(guestview::kElementSizeIsLogical, &element_size_is_logical);
   int normal_height = 0;
   int normal_width = 0;
-  if (element_size_is_logical) {
-    // Convert the element size from logical pixels to physical pixels.
-    normal_height = LogicalPixelsToPhysicalPixels(element_height);
-    normal_width = LogicalPixelsToPhysicalPixels(element_width);
+  if (is_full_page_plugin()) {
+    // The initial size of a full page plugin should be set to fill the
+    // owner's visible viewport.
+    auto owner_size = owner_web_contents()->GetRenderWidgetHostView()->
+        GetVisibleViewportSize();
+    normal_height = owner_size.height();
+    normal_width = owner_size.width();
   } else {
-    normal_height = lround(element_height);
-    normal_width = lround(element_width);
+    // Set the normal size to the element size so that the guestview will fit
+    // the element initially if autosize is disabled.
+    double element_height = 0.0;
+    double element_width = 0.0;
+    params.GetDouble(guestview::kElementHeight, &element_height);
+    params.GetDouble(guestview::kElementWidth, &element_width);
+
+    // If the element size was provided in logical units (versus physical), then
+    // it will be converted to physical units.
+    bool element_size_is_logical = false;
+    params.GetBoolean(guestview::kElementSizeIsLogical,
+                      &element_size_is_logical);
+    if (element_size_is_logical) {
+      // Convert the element size from logical pixels to physical pixels.
+      normal_height = LogicalPixelsToPhysicalPixels(element_height);
+      normal_width = LogicalPixelsToPhysicalPixels(element_width);
+    } else {
+      normal_height = lround(element_height);
+      normal_width = lround(element_width);
+    }
   }
 
   SetSizeParams set_size_params;
@@ -815,6 +841,16 @@
   SetSize(set_size_params);
 }
 
+void GuestViewBase::SetGuestZoomLevelToMatchEmbedder() {
+  auto embedder_zoom_controller =
+      ui_zoom::ZoomController::FromWebContents(owner_web_contents());
+  if (!embedder_zoom_controller)
+    return;
+
+  ui_zoom::ZoomController::FromWebContents(web_contents())
+      ->SetZoomLevel(embedder_zoom_controller->GetZoomLevel());
+}
+
 void GuestViewBase::StartTrackingEmbedderZoomLevel() {
   if (!ZoomPropagatesFromEmbedderToGuest())
     return;
@@ -826,9 +862,9 @@
     return;
   // Listen to the embedder's zoom changes.
   embedder_zoom_controller->AddObserver(this);
+
   // Set the guest's initial zoom level to be equal to the embedder's.
-  ui_zoom::ZoomController::FromWebContents(web_contents())
-      ->SetZoomLevel(embedder_zoom_controller->GetZoomLevel());
+  SetGuestZoomLevelToMatchEmbedder();
 }
 
 void GuestViewBase::StopTrackingEmbedderZoomLevel() {
diff --git a/extensions/browser/guest_view/guest_view_base.h b/extensions/browser/guest_view/guest_view_base.h
index 17eac48..a7ea60a 100644
--- a/extensions/browser/guest_view/guest_view_base.h
+++ b/extensions/browser/guest_view/guest_view_base.h
@@ -327,6 +327,9 @@
   void DidStopLoading() final;
   void RenderViewReady() final;
   void WebContentsDestroyed() final;
+  void DidNavigateMainFrame(
+      const content::LoadCommittedDetails& details,
+      const content::FrameNavigateParams& params) override;
 
   // WebContentsDelegate implementation.
   void ActivateContents(content::WebContents* contents) final;
@@ -353,6 +356,8 @@
                            const gfx::Size& pref_size) final;
   void UpdateTargetURL(content::WebContents* source, const GURL& url) override;
 
+  void SetGuestZoomLevelToMatchEmbedder();
+
  private:
   class OwnerContentsObserver;
 
diff --git a/extensions/browser/guest_view/guest_view_message_filter.cc b/extensions/browser/guest_view/guest_view_message_filter.cc
index 52f8d6b..e7c505c4 100644
--- a/extensions/browser/guest_view/guest_view_message_filter.cc
+++ b/extensions/browser/guest_view/guest_view_message_filter.cc
@@ -70,7 +70,7 @@
     int element_instance_id,
     int guest_instance_id,
     const base::DictionaryValue& params) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto manager = GuestViewManager::FromBrowserContext(browser_context_);
   if (!manager)
     return;
@@ -86,7 +86,7 @@
     const std::string& view_id,
     int element_instance_id,
     const gfx::Size& element_size) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto manager = GuestViewManager::FromBrowserContext(browser_context_);
   if (!manager)
     return;
diff --git a/extensions/browser/guest_view/web_view/web_view_constants.cc b/extensions/browser/guest_view/web_view/web_view_constants.cc
index 7502e6e..f156db6 100644
--- a/extensions/browser/guest_view/web_view/web_view_constants.cc
+++ b/extensions/browser/guest_view/web_view/web_view_constants.cc
@@ -24,7 +24,7 @@
 const char kEventClose[] = "webViewInternal.onClose";
 const char kEventConsoleMessage[] = "webViewInternal.onConsoleMessage";
 const char kEventContentLoad[] = "webViewInternal.onContentLoad";
-const char kEventContextMenu[] = "chromeWebViewInternal.contextmenu";
+const char kEventContextMenuShow[] = "chromeWebViewInternal.onContextMenuShow";
 const char kEventDialog[] = "webViewInternal.onDialog";
 const char kEventDropLink[] = "webViewInternal.onDropLink";
 const char kEventExit[] = "webViewInternal.onExit";
diff --git a/extensions/browser/guest_view/web_view/web_view_constants.h b/extensions/browser/guest_view/web_view/web_view_constants.h
index eaa2f0c..8572399c 100644
--- a/extensions/browser/guest_view/web_view/web_view_constants.h
+++ b/extensions/browser/guest_view/web_view/web_view_constants.h
@@ -28,7 +28,7 @@
 extern const char kEventClose[];
 extern const char kEventConsoleMessage[];
 extern const char kEventContentLoad[];
-extern const char kEventContextMenu[];
+extern const char kEventContextMenuShow[];
 extern const char kEventDialog[];
 extern const char kEventDropLink[];
 extern const char kEventExit[];
diff --git a/extensions/browser/value_store/leveldb_value_store.cc b/extensions/browser/value_store/leveldb_value_store.cc
index 58cf32fe..56fd265 100644
--- a/extensions/browser/value_store/leveldb_value_store.cc
+++ b/extensions/browser/value_store/leveldb_value_store.cc
@@ -13,6 +13,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/value_store/value_store_util.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
 
@@ -328,6 +329,7 @@
   leveldb::Options options;
   options.max_open_files = 0;  // Use minimum.
   options.create_if_missing = true;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
 
   leveldb::DB* db = NULL;
   leveldb::Status status =
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index 736f0e3..20f01b5 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -124,7 +124,6 @@
       "whitelist": [
         // http://crbug.com/292856
         "3F50C3A83839D9C76334BCE81CDEC06174F266AF",
-        "09FDCB5851B8F3378DB630D06E316076E89C95A6",
         "39BE69F11F68E4EED080DA3DC2394F7885B7AFF9",
         "FF78670081967CE21DB86A04AD94A0498F01E20A",
         // Hotword component extension
@@ -222,7 +221,7 @@
     "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "networking.config": {
-    "channel": "dev",
+    "channel": "stable",
     "platforms": ["chromeos"],
     "extension_types": ["extension"]
   },
@@ -411,7 +410,6 @@
       "extension_types": ["extension"],
       "whitelist": [
         // http://crbug.com/292856
-        "09FDCB5851B8F3378DB630D06E316076E89C95A6",
         "A434B90223C3C52F2B69DB494736B63C612C774D"
       ]
     }
diff --git a/extensions/common/api/networking_private.json b/extensions/common/api/networking_private.json
index 158017f..ed2943b 100644
--- a/extensions/common/api/networking_private.json
+++ b/extensions/common/api/networking_private.json
@@ -189,6 +189,24 @@
         ]
       },
       {
+        "name": "forgetNetwork",
+        "description": "Forgets a network configuration by clearing any configured properties for the network with GUID 'networkGuid'. This may also include any other networks with matching identifiers (e.g. WiFi SSID and Security). If no such configuration exists, an error will be set and the operation will fail.",
+        "parameters": [
+          {
+            "name": "networkGuid",
+            "type": "string",
+            "description": "The unique identifier of the network to forget."
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "optional": true,
+            "parameters": [],
+            "description": "Called when the operation has completed."
+          }
+        ]
+      },
+      {
         "name": "getNetworks",
         "description": "Returns a list of network objects with the same properties provided by getState. A filter is provided to specify the type of networks returned and to limit the number of networks. Networks are ordered by the system based on their priority, with connected or connecting networks listed first.",
         "parameters": [
diff --git a/extensions/common/api/printer_provider.idl b/extensions/common/api/printer_provider.idl
index b3e4faf9..ba4d5737 100644
--- a/extensions/common/api/printer_provider.idl
+++ b/extensions/common/api/printer_provider.idl
@@ -43,6 +43,9 @@
     // ID of the printer which should handle the job.
     DOMString printerId;
 
+    // The print job title.
+    DOMString title;
+
     // Print ticket in
     // <a href="https://developers.google.com/cloud-print/docs/cdd#cjt">
     // CJT format</a>.
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index 1c51334..d4506bd 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -361,6 +361,9 @@
     "Invalid value for 'file_access[*]'.";
 const char kInvalidFileBrowserHandler[] =
     "Invalid value for 'file_browser_handlers'.";
+const char kInvalidFileBrowserHandlerMissingPermission[] =
+    "Declaring file browser handlers requires the fileBrowserHandler manifest "
+    "permission.";
 const char kInvalidFileFiltersList[] =
     "Invalid value for 'file_filters'.";
 const char kInvalidFileFilterValue[] =
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index 8791396..36edf89 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -302,7 +302,7 @@
 extern const char kInvalidFileAccessList[];
 extern const char kInvalidFileAccessValue[];
 extern const char kInvalidFileBrowserHandler[];
-extern const char kInvalidFileBrowserHandlerMIMETypes[];
+extern const char kInvalidFileBrowserHandlerMissingPermission[];
 extern const char kInvalidFileFiltersList[];
 extern const char kInvalidFileFilterValue[];
 extern const char kInvalidFileHandlers[];
diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h
index fbcf584..c07cc3d 100644
--- a/extensions/common/permissions/api_permission.h
+++ b/extensions/common/permissions/api_permission.h
@@ -166,6 +166,7 @@
     kSearchProvider,
     kSerial,
     kSessions,
+    kSettingsPrivate,
     kSignedInDevices,
     kSocket,
     kStartupPages,
diff --git a/extensions/common/permissions/permission_message.h b/extensions/common/permissions/permission_message.h
index 432321e..8a8aa6e 100644
--- a/extensions/common/permissions/permission_message.h
+++ b/extensions/common/permissions/permission_message.h
@@ -92,17 +92,18 @@
     kCopresence,
     kTopSites,
     kU2fDevices,
-    kVpnProvider,
     kDocumentScan,
+    kNetworkingConfig,
+    kPlatformKeys,
+    kMDns,
+    kVpnProvider,
     kHosts1ReadOnly,
     kHosts2ReadOnly,
     kHosts3ReadOnly,
     kHosts4OrMoreReadOnly,
     kHostsAllReadOnly,
     kInterceptAllKeys,
-    kNetworkingConfig,
-    kPlatformKeys,
-    kMDns,
+    kSettingsPrivate,
     // Last entry: Add new entries above and ensure to update the
     // "ExtensionPermission2" enum in tools/metrics/histograms/histograms.xml.
     kEnumBoundary,
diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp
index 797d4ccf..e52dfe6 100644
--- a/extensions/extensions.gyp
+++ b/extensions/extensions.gyp
@@ -289,7 +289,7 @@
           'action_name': 'repack_extensions_shell_and_test_pak',
           'variables': {
             'pak_inputs': [
-              '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources.pak',
+              '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources_100_percent.pak',
               '<(SHARED_INTERMEDIATE_DIR)/content/app/strings/content_strings_en-US.pak',
               '<(SHARED_INTERMEDIATE_DIR)/content/content_resources.pak',
               '<(SHARED_INTERMEDIATE_DIR)/content/shell_resources.pak',
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 67ed82df..55a9b14 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -478,6 +478,10 @@
   resources.push_back(std::make_pair(kEventBindings, IDR_EVENT_BINDINGS_JS));
   resources.push_back(std::make_pair("extensionOptions",
                                      IDR_EXTENSION_OPTIONS_JS));
+  resources.push_back(std::make_pair("extensionOptionsAttributes",
+                                     IDR_EXTENSION_OPTIONS_ATTRIBUTES_JS));
+  resources.push_back(std::make_pair("extensionOptionsConstants",
+                                     IDR_EXTENSION_OPTIONS_CONSTANTS_JS));
   resources.push_back(std::make_pair("extensionOptionsEvents",
                                      IDR_EXTENSION_OPTIONS_EVENTS_JS));
   resources.push_back(std::make_pair("extensionView", IDR_EXTENSION_VIEW_JS));
@@ -1409,6 +1413,7 @@
   // Require ExtensionOptions.
   if (context->GetAvailability("extensionOptionsInternal").is_available()) {
     module_system->Require("extensionOptions");
+    module_system->Require("extensionOptionsAttributes");
   }
 
   // Require ExtensionView.
diff --git a/extensions/renderer/resources/extensions_renderer_resources.grd b/extensions/renderer/resources/extensions_renderer_resources.grd
index 7dc1708..d5c1d67 100644
--- a/extensions/renderer/resources/extensions_renderer_resources.grd
+++ b/extensions/renderer/resources/extensions_renderer_resources.grd
@@ -19,6 +19,8 @@
       <include name="IDR_ENTRY_ID_MANAGER" file="entry_id_manager.js" type="BINDATA" />
       <include name="IDR_EVENT_BINDINGS_JS" file="event.js" type="BINDATA" />
       <include name="IDR_EXTENSION_OPTIONS_JS" file="guest_view/extension_options/extension_options.js" type="BINDATA"/>
+      <include name="IDR_EXTENSION_OPTIONS_ATTRIBUTES_JS" file="guest_view/extension_options/extension_options_attributes.js" type="BINDATA"/>
+      <include name="IDR_EXTENSION_OPTIONS_CONSTANTS_JS" file="guest_view/extension_options/extension_options_constants.js" type="BINDATA"/>
       <include name="IDR_EXTENSION_OPTIONS_EVENTS_JS" file="guest_view/extension_options/extension_options_events.js" type="BINDATA"/>
       <include name="IDR_EXTENSION_VIEW_JS" file="guest_view/extension_view/extension_view.js" type="BINDATA" />
       <include name="IDR_EXTENSION_VIEW_API_METHODS_JS" file="guest_view/extension_view/extension_view_api_methods.js" type="BINDATA" />
diff --git a/extensions/renderer/resources/guest_view/app_view/app_view.js b/extensions/renderer/resources/guest_view/app_view/app_view.js
index 17f142ba..301c827 100644
--- a/extensions/renderer/resources/guest_view/app_view/app_view.js
+++ b/extensions/renderer/resources/guest_view/app_view/app_view.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 var DocumentNatives = requireNative('document_natives');
-var GuestView = require('guestView').GuestView;
 var GuestViewContainer = require('guestViewContainer').GuestViewContainer;
 var IdGenerator = requireNative('id_generator');
 
diff --git a/extensions/renderer/resources/guest_view/extension_options/extension_options.js b/extensions/renderer/resources/guest_view/extension_options/extension_options.js
index 0a92096..d96a130 100644
--- a/extensions/renderer/resources/guest_view/extension_options/extension_options.js
+++ b/extensions/renderer/resources/guest_view/extension_options/extension_options.js
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var ExtensionOptionsConstants =
+    require('extensionOptionsConstants').ExtensionOptionsConstants;
 var ExtensionOptionsEvents =
     require('extensionOptionsEvents').ExtensionOptionsEvents;
-var GuestView = require('guestView').GuestView;
 var GuestViewContainer = require('guestViewContainer').GuestViewContainer;
 
 function ExtensionOptionsImpl(extensionoptionsElement) {
   GuestViewContainer.call(this, extensionoptionsElement, 'extensionoptions');
 
   new ExtensionOptionsEvents(this);
-  this.setupElementProperties();
 };
 
 ExtensionOptionsImpl.prototype.__proto__ = GuestViewContainer.prototype;
@@ -23,16 +23,14 @@
 }
 
 ExtensionOptionsImpl.prototype.buildContainerParams = function() {
-  return {
-    'extensionId': this.element.getAttribute('extension')
-  };
+  var params = {};
+  for (var i in this.attributes) {
+    params[i] = this.attributes[i].getValue();
+  }
+  return params;
 };
 
 ExtensionOptionsImpl.prototype.createGuest = function() {
-  if (!this.elementAttached) {
-    return;
-  }
-
   // Destroy the old guest if one exists.
   this.guest.destroy();
 
@@ -48,31 +46,7 @@
   }.bind(this));
 };
 
-ExtensionOptionsImpl.prototype.handleAttributeMutation =
-    function(name, oldValue, newValue) {
-  // We treat null attribute (attribute removed) and the empty string as
-  // one case.
-  oldValue = oldValue || '';
-  newValue = newValue || '';
-
-  if (oldValue === newValue)
-    return;
-
-  if (name == 'extension') {
-    this.createGuest();
-  }
-};
-
-ExtensionOptionsImpl.prototype.setupElementProperties = function() {
-  $Object.defineProperty(this.element, 'extension', {
-    get: function() {
-      return this.element.getAttribute('extension');
-    }.bind(this),
-    set: function(value) {
-      this.element.setAttribute('extension', value);
-    }.bind(this),
-    enumerable: true
-  });
-};
-
 GuestViewContainer.registerElement(ExtensionOptionsImpl);
+
+// Exports.
+exports.ExtensionOptionsImpl = ExtensionOptionsImpl;
diff --git a/extensions/renderer/resources/guest_view/extension_options/extension_options_attributes.js b/extensions/renderer/resources/guest_view/extension_options/extension_options_attributes.js
new file mode 100644
index 0000000..5206f1dc
--- /dev/null
+++ b/extensions/renderer/resources/guest_view/extension_options/extension_options_attributes.js
@@ -0,0 +1,43 @@
+// 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.
+
+// This module implements the attributes of the <extensionoptions> tag.
+
+var GuestViewAttributes = require('guestViewAttributes').GuestViewAttributes;
+var ExtensionOptionsConstants =
+    require('extensionOptionsConstants').ExtensionOptionsConstants;
+var ExtensionOptionsImpl = require('extensionOptions').ExtensionOptionsImpl;
+
+// -----------------------------------------------------------------------------
+// ExtensionAttribute object.
+
+// Attribute that handles extension binded to the extensionoptions.
+function ExtensionAttribute(view) {
+  GuestViewAttributes.Attribute.call(
+      this, ExtensionOptionsConstants.ATTRIBUTE_EXTENSION, view);
+}
+
+ExtensionAttribute.prototype.__proto__ =
+    GuestViewAttributes.Attribute.prototype;
+
+ExtensionAttribute.prototype.handleMutation = function(oldValue, newValue) {
+  // Once this attribute has been set, it cannot be unset.
+  if (!newValue && oldValue) {
+    this.setValueIgnoreMutation(oldValue);
+    return;
+  }
+
+  if (!newValue || !this.elementAttached)
+    return;
+
+  this.view.createGuest();
+};
+
+// -----------------------------------------------------------------------------
+
+// Sets up all of the extensionoptions attributes.
+ExtensionOptionsImpl.prototype.setupAttributes = function() {
+  this.attributes[ExtensionOptionsConstants.ATTRIBUTE_EXTENSION] =
+      new ExtensionAttribute(this);
+};
diff --git a/extensions/renderer/resources/guest_view/extension_options/extension_options_constants.js b/extensions/renderer/resources/guest_view/extension_options/extension_options_constants.js
new file mode 100644
index 0000000..f9b29ae
--- /dev/null
+++ b/extensions/renderer/resources/guest_view/extension_options/extension_options_constants.js
@@ -0,0 +1,13 @@
+// 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.
+
+// This module contains constants used in extensionoptions.
+
+// Container for the extensionview constants.
+var ExtensionOptionsConstants = {
+  // Attributes.
+  ATTRIBUTE_EXTENSION: 'extension'
+};
+
+exports.ExtensionOptionsConstants = $Object.freeze(ExtensionOptionsConstants);
diff --git a/extensions/renderer/resources/guest_view/extension_view/extension_view.js b/extensions/renderer/resources/guest_view/extension_view/extension_view.js
index 9a132548..331f508 100644
--- a/extensions/renderer/resources/guest_view/extension_view/extension_view.js
+++ b/extensions/renderer/resources/guest_view/extension_view/extension_view.js
@@ -13,7 +13,6 @@
 
 function ExtensionViewImpl(extensionviewElement) {
   GuestViewContainer.call(this, extensionviewElement, 'extensionview');
-  this.setupExtensionViewAttributes();
 
   new ExtensionViewEvents(this, this.viewInstanceId);
 }
@@ -42,16 +41,6 @@
   return params;
 };
 
-// This observer monitors mutations to attributes of the <extensionview>.
-ExtensionViewImpl.prototype.handleAttributeMutation = function(
-    attributeName, oldValue, newValue) {
-  if (!this.attributes[attributeName])
-    return;
-
-  // Let the changed attribute handle its own mutation.
-  this.attributes[attributeName].maybeHandleMutation(oldValue, newValue);
-};
-
 ExtensionViewImpl.prototype.onElementDetached = function() {
   this.guest.destroy();
 
diff --git a/extensions/renderer/resources/guest_view/extension_view/extension_view_attributes.js b/extensions/renderer/resources/guest_view/extension_view/extension_view_attributes.js
index d0786ed7..59792b8 100644
--- a/extensions/renderer/resources/guest_view/extension_view/extension_view_attributes.js
+++ b/extensions/renderer/resources/guest_view/extension_view/extension_view_attributes.js
@@ -5,9 +5,9 @@
 // This module implements the attributes of the <extensionview> tag.
 
 var GuestViewAttributes = require('guestViewAttributes').GuestViewAttributes;
-var ExtensionViewImpl = require('extensionView').ExtensionViewImpl;
 var ExtensionViewConstants =
     require('extensionViewConstants').ExtensionViewConstants;
+var ExtensionViewImpl = require('extensionView').ExtensionViewImpl;
 var ExtensionViewInternal =
     require('extensionViewInternal').ExtensionViewInternal;
 
@@ -54,8 +54,7 @@
 // -----------------------------------------------------------------------------
 
 // Sets up all of the extensionview attributes.
-ExtensionViewImpl.prototype.setupExtensionViewAttributes = function() {
-  this.attributes = {};
+ExtensionViewImpl.prototype.setupAttributes = function() {
   this.attributes[ExtensionViewConstants.ATTRIBUTE_EXTENSION] =
       new ExtensionAttribute(this);
   this.attributes[ExtensionViewConstants.ATTRIBUTE_SRC] =
diff --git a/extensions/renderer/resources/guest_view/guest_view_container.js b/extensions/renderer/resources/guest_view/guest_view_container.js
index 3a3f837c..5b6a7367 100644
--- a/extensions/renderer/resources/guest_view/guest_view_container.js
+++ b/extensions/renderer/resources/guest_view/guest_view_container.js
@@ -12,6 +12,7 @@
 
 function GuestViewContainer(element, viewType) {
   privates(element).internal = this;
+  this.attributes = {};
   this.element = element;
   this.elementAttached = false;
   this.viewInstanceId = IdGenerator.GetNextId();
@@ -19,10 +20,10 @@
 
   this.setupGuestProperty();
   this.guest = new GuestView(viewType);
+  this.setupAttributes();
 
   privates(this).browserPluginElement = this.createBrowserPluginElement();
   this.setupFocusPropagation();
-
   var shadowRoot = this.element.createShadowRoot();
   shadowRoot.appendChild(privates(this).browserPluginElement);
 }
@@ -156,9 +157,17 @@
 GuestViewContainer.prototype.buildParams = function() {
   var params = this.buildContainerParams();
   params['instanceId'] = this.viewInstanceId;
+  // When the GuestViewContainer is not participating in layout (display:none)
+  // then getBoundingClientRect() would report a width and height of 0.
+  // However, in the case where the GuestViewContainer has a fixed size we can
+  // use that value to initially size the guest so as to avoid a relayout of the
+  // on display:block.
+  var css = window.getComputedStyle(this.element, null);
   var elementRect = this.element.getBoundingClientRect();
-  params['elementWidth'] = parseInt(elementRect.width);
-  params['elementHeight'] = parseInt(elementRect.height);
+  params['elementWidth'] = parseInt(elementRect.width) ||
+      parseInt(css.getPropertyValue('width'));
+  params['elementHeight'] = parseInt(elementRect.height) ||
+      parseInt(css.getPropertyValue('height'));
   return params;
 };
 
@@ -168,11 +177,9 @@
 
 // Implemented by the specific view type, if needed.
 GuestViewContainer.prototype.buildContainerParams = function() { return {}; };
-// TODO(paulmeyer): remove once all view types use attribute objects.
-GuestViewContainer.prototype.handleAttributeMutation = function(
-    attributeName, oldValue, newValue) {};
 GuestViewContainer.prototype.onElementAttached = function() {};
 GuestViewContainer.prototype.onElementDetached = function() {};
+GuestViewContainer.prototype.setupAttributes = function() {};
 
 // Registers the browser plugin <object> custom element. |viewType| is the
 // name of the specific guestview container (e.g. 'webview').
@@ -230,10 +237,12 @@
 
   proto.attributeChangedCallback = function(name, oldValue, newValue) {
     var internal = privates(this).internal;
-    if (!internal) {
+    if (!internal || !internal.attributes[name]) {
       return;
     }
-    internal.handleAttributeMutation(name, oldValue, newValue);
+
+    // Let the changed attribute handle its own mutation.
+    internal.attributes[name].maybeHandleMutation(oldValue, newValue);
   };
 
   proto.detachedCallback = function() {
diff --git a/extensions/renderer/resources/guest_view/web_view/web_view.js b/extensions/renderer/resources/guest_view/web_view/web_view.js
index c061c83..1b40d15 100644
--- a/extensions/renderer/resources/guest_view/web_view/web_view.js
+++ b/extensions/renderer/resources/guest_view/web_view/web_view.js
@@ -18,9 +18,7 @@
 function WebViewImpl(webviewElement) {
   GuestViewContainer.call(this, webviewElement, 'webview');
 
-  this.setupWebViewAttributes();
   this.setupElementProperties();
-
   new WebViewEvents(this, this.viewInstanceId);
 }
 
@@ -98,17 +96,6 @@
   });
 };
 
-// This observer monitors mutations to attributes of the <webview>.
-WebViewImpl.prototype.handleAttributeMutation = function(
-    attributeName, oldValue, newValue) {
-  if (!this.attributes[attributeName]) {
-    return;
-  }
-
-  // Let the changed attribute handle its own mutation;
-  this.attributes[attributeName].maybeHandleMutation(oldValue, newValue);
-};
-
 WebViewImpl.prototype.onSizeChanged = function(webViewEvent) {
   var newWidth = webViewEvent.newWidth;
   var newHeight = webViewEvent.newHeight;
diff --git a/extensions/renderer/resources/guest_view/web_view/web_view_attributes.js b/extensions/renderer/resources/guest_view/web_view/web_view_attributes.js
index 3b666a2..ce538fdc 100644
--- a/extensions/renderer/resources/guest_view/web_view/web_view_attributes.js
+++ b/extensions/renderer/resources/guest_view/web_view/web_view_attributes.js
@@ -5,8 +5,8 @@
 // This module implements the attributes of the <webview> tag.
 
 var GuestViewAttributes = require('guestViewAttributes').GuestViewAttributes;
-var WebViewImpl = require('webView').WebViewImpl;
 var WebViewConstants = require('webViewConstants').WebViewConstants;
+var WebViewImpl = require('webView').WebViewImpl;
 var WebViewInternal = require('webViewInternal').WebViewInternal;
 
 // -----------------------------------------------------------------------------
@@ -252,9 +252,7 @@
 // -----------------------------------------------------------------------------
 
 // Sets up all of the webview attributes.
-WebViewImpl.prototype.setupWebViewAttributes = function() {
-  this.attributes = {};
-
+WebViewImpl.prototype.setupAttributes = function() {
   this.attributes[WebViewConstants.ATTRIBUTE_ALLOWSCALING] =
       new AllowScalingAttribute(this);
   this.attributes[WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY] =
diff --git a/extensions/renderer/script_injector.h b/extensions/renderer/script_injector.h
index 33922b6..1c98aa0 100644
--- a/extensions/renderer/script_injector.h
+++ b/extensions/renderer/script_injector.h
@@ -49,7 +49,7 @@
   // Returns true if the script is running inside a user gesture.
   virtual bool IsUserGesture() const = 0;
 
-  // Returns ture if the script expects results.
+  // Returns true if the script expects results.
   virtual bool ExpectsResults() const = 0;
 
   // Returns true if the script should inject JS source at the given
diff --git a/extensions/shell/browser/shell_browsertest.cc b/extensions/shell/browser/shell_browsertest.cc
index baf75d9f7..93e6b39d 100644
--- a/extensions/shell/browser/shell_browsertest.cc
+++ b/extensions/shell/browser/shell_browsertest.cc
@@ -17,8 +17,14 @@
 
 namespace extensions {
 
+// Flaky on linux. See http://crbug.com/470873.
+#if defined(OS_LINUX)
+#define MAYBE_Basic DISABLED_Basic
+#else
+#define MAYBE_Basic Basic
+#endif
 // Test that we can open an app window and wait for it to load.
-IN_PROC_BROWSER_TEST_F(ShellApiTest, Basic) {
+IN_PROC_BROWSER_TEST_F(ShellApiTest, MAYBE_Basic) {
   ASSERT_TRUE(RunAppTest("platform_app")) << message_;
 
   // A window was created.
diff --git a/extensions/shell/browser/shell_special_storage_policy.cc b/extensions/shell/browser/shell_special_storage_policy.cc
index a5f4120..5ed1b50f 100644
--- a/extensions/shell/browser/shell_special_storage_policy.cc
+++ b/extensions/shell/browser/shell_special_storage_policy.cc
@@ -32,10 +32,6 @@
   return false;
 }
 
-bool ShellSpecialStoragePolicy::IsFileHandler(const std::string& extension_id) {
-  return true;
-}
-
 bool ShellSpecialStoragePolicy::HasIsolatedStorage(const GURL& origin) {
   return false;
 }
diff --git a/extensions/shell/browser/shell_special_storage_policy.h b/extensions/shell/browser/shell_special_storage_policy.h
index 344a97b..e59602f2 100644
--- a/extensions/shell/browser/shell_special_storage_policy.h
+++ b/extensions/shell/browser/shell_special_storage_policy.h
@@ -20,7 +20,6 @@
   bool IsStorageUnlimited(const GURL& origin) override;
   bool IsStorageSessionOnly(const GURL& origin) override;
   bool CanQueryDiskSize(const GURL& origin) override;
-  bool IsFileHandler(const std::string& extension_id) override;
   bool HasIsolatedStorage(const GURL& origin) override;
   bool HasSessionOnlyOrigins() override;
 
diff --git a/extensions/shell/browser/shell_speech_recognition_manager_delegate.cc b/extensions/shell/browser/shell_speech_recognition_manager_delegate.cc
index 7a96874..9fc2f65 100644
--- a/extensions/shell/browser/shell_speech_recognition_manager_delegate.cc
+++ b/extensions/shell/browser/shell_speech_recognition_manager_delegate.cc
@@ -72,7 +72,7 @@
 void ShellSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed(
     int session_id,
     base::Callback<void(bool ask_user, bool is_allowed)> callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   const content::SpeechRecognitionSessionContext& context =
       SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
@@ -104,7 +104,7 @@
     base::Callback<void(bool ask_user, bool is_allowed)> callback,
     int render_process_id,
     int render_view_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   const content::RenderViewHost* render_view_host =
       content::RenderViewHost::FromID(render_process_id, render_view_id);
   bool allowed = false;
diff --git a/extensions/test/data/api_test/printer_provider/request_print/test.js b/extensions/test/data/api_test/printer_provider/request_print/test.js
index c0942eaf..86baf08 100644
--- a/extensions/test/data/api_test/printer_provider/request_print/test.js
+++ b/extensions/test/data/api_test/printer_provider/request_print/test.js
@@ -64,6 +64,8 @@
             if (content)
               chrome.test.assertEq('bytes', content);
 
+            chrome.test.assertEq('Print job', job.title);
+
             chrome.test.succeed();
           });
 
diff --git a/google_apis/gcm/engine/gcm_store_impl.cc b/google_apis/gcm/engine/gcm_store_impl.cc
index 8f701a145..bc526c4 100644
--- a/google_apis/gcm/engine/gcm_store_impl.cc
+++ b/google_apis/gcm/engine/gcm_store_impl.cc
@@ -23,6 +23,7 @@
 #include "google_apis/gcm/base/mcs_message.h"
 #include "google_apis/gcm/base/mcs_util.h"
 #include "google_apis/gcm/protocol/mcs.pb.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
 
@@ -239,6 +240,7 @@
 
   leveldb::Options options;
   options.create_if_missing = true;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   leveldb::DB* db;
   leveldb::Status status =
       leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index c7f70eb..c27cb8f 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -291,25 +291,3 @@
     "//third_party/angle:translator_static",
   ]
 }
-
-if (is_linux && !is_chromeos && target_cpu != "arm" && use_x11) {
-  executable("compositor_model_bench") {
-    sources = [
-      "tools/compositor_model_bench/compositor_model_bench.cc",
-      "tools/compositor_model_bench/forward_render_model.cc",
-      "tools/compositor_model_bench/render_model_utils.cc",
-      "tools/compositor_model_bench/render_models.cc",
-      "tools/compositor_model_bench/render_tree.cc",
-      "tools/compositor_model_bench/shaders.cc",
-    ]
-
-    libs = [ "GL" ]
-
-    configs += [ "//build/config/linux:x11" ]
-
-    deps = [
-      "//base",
-      "//ui/gl",
-    ]
-  }
-}
diff --git a/gpu/angle_end2end_tests_main.cc b/gpu/angle_end2end_tests_main.cc
index 24bd9b5..aed7f74 100644
--- a/gpu/angle_end2end_tests_main.cc
+++ b/gpu/angle_end2end_tests_main.cc
@@ -10,7 +10,7 @@
 #include "base/test/test_suite.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/angle/tests/angle_tests/ANGLETest.h"
+#include "third_party/angle/src/tests/end2end_tests/ANGLETest.h"
 
 namespace {
 
diff --git a/gpu/angle_perftests_main.cc b/gpu/angle_perftests_main.cc
new file mode 100644
index 0000000..c608a3e
--- /dev/null
+++ b/gpu/angle_perftests_main.cc
@@ -0,0 +1,29 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+
+namespace {
+
+int RunHelper(base::TestSuite* test_suite) {
+  base::MessageLoopForIO message_loop;
+  return test_suite->Run();
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  base::CommandLine::Init(argc, argv);
+  base::TestSuite test_suite(argc, argv);
+  int rt = base::LaunchUnitTestsSerially(
+      argc,
+      argv,
+      base::Bind(&RunHelper, base::Unretained(&test_suite)));
+  return rt;
+}
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index af238131..aa7be79 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -4220,20 +4220,9 @@
     file.Write("%s MojoGLES2Impl::%s(%s) {\n" %
                (func.return_type, func.original_name,
                 func.MakeTypedOriginalArgString("")))
-    # TODO(alhaad): Add Mojo C thunk for each of the following methods and
-    # remove this.
-    func_list = ["GenQueriesEXT", "BeginQueryEXT", "MapTexSubImage2DCHROMIUM",
-                 "UnmapTexSubImage2DCHROMIUM", "DeleteQueriesEXT",
-                 "EndQueryEXT", "GetQueryObjectuivEXT", "ShallowFlushCHROMIUM"]
-    if func.original_name in func_list:
-      file.Write("return static_cast<gpu::gles2::GLES2Interface*>"
-                 "(MojoGLES2GetGLES2Interface(context_))->" +
-                 func.original_name + "(" + func.MakeOriginalArgString("") +
-                 ");")
-      file.Write("}")
-      return
-
-    extensions = ["CHROMIUM_sync_point", "CHROMIUM_texture_mailbox"]
+    extensions = ["CHROMIUM_sync_point", "CHROMIUM_texture_mailbox",
+                  "CHROMIUM_sub_image", "CHROMIUM_miscellaneous",
+                  "occlusion_query_EXT"]
     if func.IsCoreGLFunction() or func.GetInfo("extension") in extensions:
       file.Write("MojoGLES2MakeCurrent(context_);");
       func_return = "gl" + func.original_name + "(" + \
@@ -10055,9 +10044,12 @@
 #include "mojo/gpu/mojo_gles2_impl_autogen.h"
 
 #include "base/logging.h"
+#include "third_party/mojo/src/mojo/public/c/gles2/chromium_miscellaneous.h"
+#include "third_party/mojo/src/mojo/public/c/gles2/chromium_sub_image.h"
 #include "third_party/mojo/src/mojo/public/c/gles2/chromium_sync_point.h"
 #include "third_party/mojo/src/mojo/public/c/gles2/chromium_texture_mailbox.h"
 #include "third_party/mojo/src/mojo/public/c/gles2/gles2.h"
+#include "third_party/mojo/src/mojo/public/c/gles2/occlusion_query_ext.h"
 
 namespace mojo {
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 0886768..3abe199 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -3755,6 +3755,12 @@
   // by the context group.
   async_pixel_transfer_manager_.reset();
 
+  // Destroy the GPU Tracer which may own some in process GPU Timings.
+  if (gpu_tracer_) {
+    gpu_tracer_->Destroy(have_context);
+    gpu_tracer_.reset();
+  }
+
   if (group_.get()) {
     framebuffer_manager()->RemoveObserver(this);
     group_->Destroy(this, have_context);
@@ -4492,6 +4498,9 @@
 void GLES2DecoderImpl::DoDiscardFramebufferEXT(GLenum target,
                                                GLsizei numAttachments,
                                                const GLenum* attachments) {
+  if (workarounds().disable_fbo_invalidations)
+    return;
+
   Framebuffer* framebuffer =
       GetFramebufferInfoForTarget(GL_FRAMEBUFFER);
 
diff --git a/gpu/command_buffer/service/gpu_tracer.cc b/gpu/command_buffer/service/gpu_tracer.cc
index d00deb0..a633959 100644
--- a/gpu/command_buffer/service/gpu_tracer.cc
+++ b/gpu/command_buffer/service/gpu_tracer.cc
@@ -99,6 +99,12 @@
 GPUTrace::~GPUTrace() {
 }
 
+void GPUTrace::Destroy(bool have_context) {
+  if (gpu_timer_.get()) {
+    gpu_timer_->Destroy(have_context);
+  }
+}
+
 void GPUTrace::Start(bool trace_service) {
   if (trace_service) {
     outputter_->TraceServiceBegin(category_, name_);
@@ -152,6 +158,20 @@
 GPUTracer::~GPUTracer() {
 }
 
+void GPUTracer::Destroy(bool have_context) {
+  for (int n = 0; n < NUM_TRACER_SOURCES; n++) {
+    for (size_t i = 0; i < markers_[n].size(); i++) {
+      TraceMarker& marker = markers_[n][i];
+      if (marker.trace_.get()) {
+        marker.trace_->Destroy(have_context);
+        marker.trace_ = 0;
+      }
+    }
+  }
+
+  ClearFinishedTraces(have_context);
+}
+
 bool GPUTracer::BeginDecoding() {
   if (gpu_executing_)
     return false;
@@ -194,10 +214,9 @@
         TraceMarker& marker = markers_[n][i];
         if (marker.trace_.get()) {
           marker.trace_->End(*gpu_trace_srv_category != 0);
-          if (marker.trace_->IsEnabled())
-            traces_.push_back(marker.trace_);
 
-          markers_[n][i].trace_ = 0;
+          finished_traces_.push_back(marker.trace_);
+          marker.trace_ = 0;
         }
       }
     }
@@ -241,14 +260,14 @@
 
   // Pop last marker with matching 'source'
   if (!markers_[source].empty()) {
-    if (IsTracing()) {
-      scoped_refptr<GPUTrace> trace = markers_[source].back().trace_;
-      if (trace.get()) {
+    scoped_refptr<GPUTrace> trace = markers_[source].back().trace_;
+    if (trace.get()) {
+      if (IsTracing()) {
         trace->End(*gpu_trace_srv_category != 0);
-        if (trace->IsEnabled())
-          traces_.push_back(trace);
-        IssueProcessTask();
       }
+
+      finished_traces_.push_back(trace);
+      IssueProcessTask();
     }
 
     markers_[source].pop_back();
@@ -298,7 +317,7 @@
 
 void GPUTracer::ProcessTraces() {
   if (!gpu_timing_client_->IsAvailable()) {
-    traces_.clear();
+    ClearFinishedTraces(false);
     return;
   }
 
@@ -307,28 +326,42 @@
   // Make owning decoder's GL context current
   if (!decoder_->MakeCurrent()) {
     // Skip subsequent GL calls if MakeCurrent fails
-    traces_.clear();
+    ClearFinishedTraces(false);
     return;
   }
 
   // Check if timers are still valid (e.g: a disjoint operation
   // might have occurred.)
-  if (gpu_timing_client_->CheckAndResetTimerErrors())
-    traces_.clear();
+  if (gpu_timing_client_->CheckAndResetTimerErrors()) {
+    ClearFinishedTraces(true);
+  }
 
-  while (!traces_.empty() && traces_.front()->IsAvailable()) {
-    traces_.front()->Process();
-    traces_.pop_front();
+  while (!finished_traces_.empty()) {
+    scoped_refptr<GPUTrace>& trace = finished_traces_.front();
+    if (trace->IsEnabled()) {
+      if (!finished_traces_.front()->IsAvailable())
+        break;
+      finished_traces_.front()->Process();
+    }
+    finished_traces_.front()->Destroy(true);
+    finished_traces_.pop_front();
   }
 
   // Clear pending traces if there were are any errors
   GLenum err = glGetError();
   if (err != GL_NO_ERROR)
-    traces_.clear();
+    ClearFinishedTraces(true);
+}
+
+void GPUTracer::ClearFinishedTraces(bool have_context) {
+   while (!finished_traces_.empty()) {
+    finished_traces_.front()->Destroy(have_context);
+    finished_traces_.pop_front();
+   }
 }
 
 void GPUTracer::IssueProcessTask() {
-  if (traces_.empty() || process_posted_)
+  if (finished_traces_.empty() || process_posted_)
     return;
 
   process_posted_ = true;
diff --git a/gpu/command_buffer/service/gpu_tracer.h b/gpu/command_buffer/service/gpu_tracer.h
index 1b2c315..3f8ec84 100644
--- a/gpu/command_buffer/service/gpu_tracer.h
+++ b/gpu/command_buffer/service/gpu_tracer.h
@@ -56,6 +56,8 @@
   explicit GPUTracer(gles2::GLES2Decoder* decoder);
   virtual ~GPUTracer();
 
+  void Destroy(bool have_context);
+
   // Scheduled processing in decoder begins.
   bool BeginDecoding();
 
@@ -83,13 +85,14 @@
 
   void Process();
   void ProcessTraces();
+  void ClearFinishedTraces(bool have_context);
 
   void IssueProcessTask();
 
   scoped_refptr<gfx::GPUTimingClient> gpu_timing_client_;
   scoped_refptr<Outputter> outputter_;
   std::vector<TraceMarker> markers_[NUM_TRACER_SOURCES];
-  std::deque<scoped_refptr<GPUTrace> > traces_;
+  std::deque<scoped_refptr<GPUTrace> > finished_traces_;
 
   const unsigned char* gpu_trace_srv_category;
   const unsigned char* gpu_trace_dev_category;
@@ -155,6 +158,8 @@
            const std::string& name,
            const bool enabled);
 
+  void Destroy(bool have_context);
+
   void Start(bool trace_service);
   void End(bool tracing_service);
   bool IsAvailable();
diff --git a/gpu/command_buffer/service/gpu_tracer_unittest.cc b/gpu/command_buffer/service/gpu_tracer_unittest.cc
index 8aed1c6..2a6896ef 100644
--- a/gpu/command_buffer/service/gpu_tracer_unittest.cc
+++ b/gpu/command_buffer/service/gpu_tracer_unittest.cc
@@ -228,6 +228,7 @@
 
   void TearDown() override {
     outputter_ref_ = NULL;
+    gpu_timing_client_ = NULL;
 
     gl_fake_queries_.Reset();
     GpuServiceTest::TearDown();
@@ -374,6 +375,9 @@
     // Proces should output expected Trace results to MockOutputter
     trace->Process();
 
+    // Destroy trace after we are done.
+    trace->Destroy(true);
+
     outputter_ref_ = NULL;
   }
 };
diff --git a/gpu/command_buffer/tests/gl_gpu_memory_buffer_unittest.cc b/gpu/command_buffer/tests/gl_gpu_memory_buffer_unittest.cc
index 9e37d55b..48f8ba8 100644
--- a/gpu/command_buffer/tests/gl_gpu_memory_buffer_unittest.cc
+++ b/gpu/command_buffer/tests/gl_gpu_memory_buffer_unittest.cc
@@ -78,7 +78,11 @@
       gfx::Size(kImageWidth, kImageHeight), gfx::GpuMemoryBuffer::RGBA_8888));
 
   // Map buffer for writing.
-  uint8* mapped_buffer = static_cast<uint8*>(buffer->Map());
+  void* data;
+  bool rv = buffer->Map(&data);
+  DCHECK(rv);
+
+  uint8* mapped_buffer = static_cast<uint8*>(data);
   ASSERT_TRUE(mapped_buffer != NULL);
 
   // Assign a value to each pixel.
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index 4e4eccb..67bdbb8 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -72,15 +72,16 @@
   }
 
   // Overridden from gfx::GpuMemoryBuffer:
-  void* Map() override {
+  bool Map(void** data) override {
     mapped_ = true;
-    return &bytes_->data().front();
+    *data = &bytes_->data().front();
+    return true;
   }
   void Unmap() override { mapped_ = false; }
   bool IsMapped() const override { return mapped_; }
   Format GetFormat() const override { return format_; }
-  uint32 GetStride() const override {
-    return StrideInBytes(size_.width(), format_);
+  void GetStride(uint32* stride) const override {
+    *stride = StrideInBytes(size_.width(), format_);
   }
   gfx::GpuMemoryBufferHandle GetHandle() const override {
     NOTREACHED();
diff --git a/gpu/config/gpu_driver_bug_list_json.cc b/gpu/config/gpu_driver_bug_list_json.cc
index dfebf2b6..6e3f940 100644
--- a/gpu/config/gpu_driver_bug_list_json.cc
+++ b/gpu/config/gpu_driver_bug_list_json.cc
@@ -19,7 +19,7 @@
 {
   "name": "gpu driver bug list",
   // Please update the version number whenever you change this file.
-  "version": "7.17",
+  "version": "7.18",
   "entries": [
     {
       "id": 1,
@@ -601,9 +601,13 @@
     },
     {
       "id": 52,
-      "description": "ES3 MSAA is broken on Qualcomm",
+      "description": "ES3 MSAA is broken on Qualcomm driver prior to Lollipop",
       "os": {
-        "type": "android"
+        "type": "android",
+        "version": {
+          "op": "<",
+          "value": "5.0.0"
+        }
       },
       "gl_vendor": "Qualcomm.*",
       "features": [
@@ -1176,6 +1180,19 @@
       "features": [
         "unbind_attachments_on_bound_render_fbo_delete"
       ]
+    },
+    {
+      "id": 103,
+      "description": "Adreno 420 driver drops draw calls after FBO invalidation",
+      "cr_bugs": [443060],
+      "os": {
+        "type": "android"
+      },
+      "gl_vendor": "Qualcomm.*",
+      "gl_renderer": ".*420",
+      "features": [
+        "disable_fbo_invalidations"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_driver_bug_workaround_type.h b/gpu/config/gpu_driver_bug_workaround_type.h
index 9476bf2..462cb83b 100644
--- a/gpu/config/gpu_driver_bug_workaround_type.h
+++ b/gpu/config/gpu_driver_bug_workaround_type.h
@@ -36,6 +36,8 @@
          disable_ext_draw_buffers)                           \
   GPU_OP(DISABLE_EXT_OCCLUSION_QUERY,                        \
          disable_ext_occlusion_query)                        \
+  GPU_OP(DISABLE_FBO_INVALIDATIONS,                          \
+         disable_fbo_invalidations)                          \
   GPU_OP(DISABLE_MULTIMONITOR_MULTISAMPLING,                 \
          disable_multimonitor_multisampling)                 \
   GPU_OP(DISABLE_MULTISAMPLING,                              \
diff --git a/gpu/config/software_rendering_list_json.cc b/gpu/config/software_rendering_list_json.cc
index fb7d12d..eb3ddc4 100644
--- a/gpu/config/software_rendering_list_json.cc
+++ b/gpu/config/software_rendering_list_json.cc
@@ -18,7 +18,7 @@
 {
   "name": "software rendering list",
   // Please update the version number whenever you change this file.
-  "version": "10.1",
+  "version": "10.2",
   "entries": [
     {
       "id": 1,
@@ -552,6 +552,10 @@
       },
       "vendor_id": "0x10de",
       "driver_vendor": "NVIDIA",
+      "driver_version": {
+        "op": "<",
+        "value": "331.38"
+      },
       "features": [
         "accelerated_video_decode",
         "flash_3d",
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp
index 0e31fb04b..b84806a 100644
--- a/gpu/gpu.gyp
+++ b/gpu/gpu.gyp
@@ -130,7 +130,7 @@
       'type': '<(gtest_target_type)',
       'includes': [
         '../third_party/angle/build/common_defines.gypi',
-        '../third_party/angle/tests/angle_unittests.gypi',
+        '../third_party/angle/src/tests/angle_unittests.gypi',
       ],
       'dependencies': [
         '../base/base.gyp:base',
@@ -140,8 +140,7 @@
         '..',
         '../third_party/angle/include',
       ],
-      'sources':
-      [
+      'sources': [
         'angle_unittest_main.cc',
       ],
     },
@@ -711,16 +710,30 @@
             '../base/base.gyp:base',
             '../base/base.gyp:test_support_base',
           ],
-          'includes':
-          [
+          'includes': [
             '../third_party/angle/build/common_defines.gypi',
-            '../third_party/angle/tests/angle_end2end_tests.gypi',
+            '../third_party/angle/src/tests/angle_end2end_tests.gypi',
           ],
-          'sources':
-          [
+          'sources': [
             'angle_end2end_tests_main.cc',
           ],
         },
+        {
+          # TODO(jmadill): port this target to the GN build.
+          'target_name': 'angle_perftests',
+          'type': '<(gtest_target_type)',
+          'dependencies': [
+            '../base/base.gyp:base',
+            '../base/base.gyp:test_support_base',
+          ],
+          'includes': [
+            '../third_party/angle/build/common_defines.gypi',
+            '../third_party/angle/src/tests/angle_perftests.gypi',
+          ],
+          'sources': [
+            'angle_perftests_main.cc',
+          ],
+        },
       ],
     }],
     ['test_isolation_mode != "noop"', {
diff --git a/gpu/perftests/measurements.cc b/gpu/perftests/measurements.cc
index 0f463906..47ef28e2 100644
--- a/gpu/perftests/measurements.cc
+++ b/gpu/perftests/measurements.cc
@@ -96,6 +96,9 @@
 }
 
 MeasurementTimers::~MeasurementTimers() {
+  if (gpu_timer_.get()) {
+    gpu_timer_->Destroy(true);
+  }
 }
 
 }  // namespace gpu
diff --git a/gpu/perftests/texture_upload_perftest.cc b/gpu/perftests/texture_upload_perftest.cc
index c14a151..b425272 100644
--- a/gpu/perftests/texture_upload_perftest.cc
+++ b/gpu/perftests/texture_upload_perftest.cc
@@ -349,8 +349,11 @@
     UploadTexture(texture_id, size, pixels, format, subimage);
     tex_timers.Record();
 
-    MeasurementTimers draw_timers(gpu_timing_client_.get());
+    MeasurementTimers first_draw_timers(gpu_timing_client_.get());
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    first_draw_timers.Record();
 
+    MeasurementTimers draw_timers(gpu_timing_client_.get());
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
     draw_timers.Record();
 
@@ -374,6 +377,8 @@
     if (!gpu_timer_errors) {
       measurements.push_back(tex_timers.GetAsMeasurement(
           subimage ? "texsubimage2d" : "teximage2d"));
+      measurements.push_back(
+          first_draw_timers.GetAsMeasurement("firstdrawarrays"));
       measurements.push_back(draw_timers.GetAsMeasurement("drawarrays"));
       measurements.push_back(finish_timers.GetAsMeasurement("finish"));
     }
diff --git a/gpu/tools/compositor_model_bench/BUILD.gn b/gpu/tools/compositor_model_bench/BUILD.gn
new file mode 100644
index 0000000..8eadc6b9
--- /dev/null
+++ b/gpu/tools/compositor_model_bench/BUILD.gn
@@ -0,0 +1,28 @@
+# 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.
+
+import("//build/config/ui.gni")
+
+if (is_linux && !is_chromeos && target_cpu != "arm" && use_x11) {
+  # GYP version: //gpu/tools/tools.gyp:compositor_model_bench
+  executable("compositor_model_bench") {
+    sources = [
+      "compositor_model_bench.cc",
+      "forward_render_model.cc",
+      "render_model_utils.cc",
+      "render_models.cc",
+      "render_tree.cc",
+      "shaders.cc",
+    ]
+
+    libs = [ "GL" ]
+
+    configs += [ "//build/config/linux:x11" ]
+
+    deps = [
+      "//base",
+      "//ui/gl",
+    ]
+  }
+}
diff --git a/gpu/tools/tools.gyp b/gpu/tools/tools.gyp
index 4ea41b71..342a769 100644
--- a/gpu/tools/tools.gyp
+++ b/gpu/tools/tools.gyp
@@ -12,7 +12,7 @@
     ['OS == "linux" and target_arch != "arm" and use_x11==1', {
       'targets': [
         {
-          # GN: //gpu:compositor_model_bench
+          # GN version: //gpu/tools/compositor_model_bench
           'target_name': 'compositor_model_bench',
           'type': 'executable',
           'dependencies': [
diff --git a/ios/chrome/browser/find_in_page/find_in_page_controller.h b/ios/chrome/browser/find_in_page/find_in_page_controller.h
new file mode 100644
index 0000000..cdd7a36
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/find_in_page_controller.h
@@ -0,0 +1,68 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_FIND_IN_PAGE_FIND_IN_PAGE_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_FIND_IN_PAGE_FIND_IN_PAGE_CONTROLLER_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/ios/block_types.h"
+
+namespace web {
+class WebState;
+}
+
+@class FindInPageModel;
+
+extern NSString* const kFindBarTextFieldWillBecomeFirstResponderNotification;
+extern NSString* const kFindBarTextFieldDidResignFirstResponderNotification;
+
+@protocol FindInPageControllerDelegate<NSObject>
+// Informs the delegate when the scroll position is about to be changed on the
+// page.
+- (void)willAdjustScrollPosition;
+@end
+
+@interface FindInPageController : NSObject
+
+// Designated initializer.
+- (id)initWithWebState:(web::WebState*)webState
+              delegate:(id<FindInPageControllerDelegate>)delegate;
+// Inject the find in page scripts into the web state.
+- (void)initFindInPage;
+// Find In Page model. TODO(justincohen) consider using find_tab_helper.cc.
+- (FindInPageModel*)findInPageModel;
+// Is Find In Page available right now (given the state of the WebState)?
+- (BOOL)canFindInPage;
+// Find |query| in page, update model with results of find. Calls
+// |completionHandler| after the find operation is complete. |completionHandler|
+// can be nil.
+- (void)findStringInPage:(NSString*)query
+       completionHandler:(ProceduralBlock)completionHandler;
+// Move to the next find result based on |-findInPageModel|, and scroll to
+// match. Calls |completionHandler| when the next string has been found.
+// |completionHandler| can be nil.
+- (void)findNextStringInPageWithCompletionHandler:
+    (ProceduralBlock)completionHandler;
+// Move to the previous find result based on |-findInPageModel|. Calls
+// |completionHandler| when the previous string has been found.
+// |completionHandler| can be nil.
+- (void)findPreviousStringInPageWithCompletionHandler:
+    (ProceduralBlock)completionHandler;
+// Disable find in page script and model. Calls |completionHandler| once the
+// model has been disabled and cleanup is complete. |completionHandler| can be
+// nil.
+- (void)disableFindInPageWithCompletionHandler:
+    (ProceduralBlock)completionHandler;
+
+// Save search term to Paste UIPasteboard.
+- (void)saveSearchTerm;
+// Restore search term from Paste UIPasteboard, updating findInPageModel.
+- (void)restoreSearchTerm;
+
+// Instructs the controller to detach itself from the web state.
+- (void)detachFromWebState;
+@end
+
+#endif  // IOS_CHROME_BROWSER_FIND_IN_PAGE_FIND_IN_PAGE_CONTROLLER_H_
diff --git a/ios/chrome/browser/find_in_page/find_in_page_controller.mm b/ios/chrome/browser/find_in_page/find_in_page_controller.mm
new file mode 100644
index 0000000..0d1621e
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/find_in_page_controller.mm
@@ -0,0 +1,357 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
+
+#import <UIKit/UIKit.h>
+
+#include "base/ios/ios_util.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_nsobject.h"
+#import "ios/chrome/browser/find_in_page/find_in_page_model.h"
+#import "ios/chrome/browser/find_in_page/js_findinpage_manager.h"
+#import "ios/chrome/browser/web/dom_altering_lock.h"
+#import "ios/web/public/web_state/crw_web_view_proxy.h"
+#import "ios/web/public/web_state/crw_web_view_scroll_view_proxy.h"
+#import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
+#import "ios/web/public/web_state/web_state.h"
+#import "ios/web/public/web_state/web_state_observer_bridge.h"
+
+NSString* const kFindBarTextFieldWillBecomeFirstResponderNotification =
+    @"kFindBarTextFieldWillBecomeFirstResponderNotification";
+NSString* const kFindBarTextFieldDidResignFirstResponderNotification =
+    @"kFindBarTextFieldDidResignFirstResponderNotification";
+
+namespace {
+// The delay (in secs) after which the find in page string will be pumped again.
+const NSTimeInterval kRecurringPumpDelay = .01;
+}
+
+@interface FindInPageController () <DOMAltering, CRWWebStateObserver>
+// The find in page controller delegate.
+@property(nonatomic, readonly) id<FindInPageControllerDelegate> delegate;
+// The web view's scroll view.
+@property(nonatomic, readonly) CRWWebViewScrollViewProxy* webViewScrollView;
+
+// Convenience method to obtain UIPasteboardNameFind from UIPasteBoard.
+- (UIPasteboard*)findPasteboard;
+// Find in Page text field listeners.
+- (void)findBarTextFieldWillBecomeFirstResponder:(NSNotification*)note;
+- (void)findBarTextFieldDidResignFirstResponder:(NSNotification*)note;
+// Keyboard listeners.
+- (void)keyboardDidShow:(NSNotification*)note;
+- (void)keyboardWillHide:(NSNotification*)note;
+// Constantly injects the find string in page until
+// |disableFindInPageWithCompletionHandler:| is called or the find operation is
+// complete. Calls |completionHandler| if the find operation is complete.
+// |completionHandler| can be nil.
+- (void)startPumpingWithCompletionHandler:(ProceduralBlock)completionHandler;
+// Gives find in page more time to complete. Calls |completionHandler| with
+// a BOOL indicating if the find operation was successfull. |completionHandler|
+// can be nil.
+- (void)pumpFindStringInPageWithCompletionHandler:
+    (void (^)(BOOL))completionHandler;
+// Processes the result of a single find in page pump. Calls |completionHandler|
+// if pumping is done. Re-pumps if necessary.
+- (void)processPumpResult:(BOOL)finished
+              scrollPoint:(CGPoint)scrollPoint
+        completionHandler:(ProceduralBlock)completionHandler;
+// Returns the associated web state. May be null.
+- (web::WebState*)webState;
+@end
+
+@implementation FindInPageController {
+ @private
+  // Object that manages find_in_page.js injection into the web view.
+  JsFindinpageManager* _findInPageJsManager;
+  id<FindInPageControllerDelegate> _delegate;
+
+  // Access to the web view from the web state.
+  base::scoped_nsprotocol<id<CRWWebViewProxy>> _webViewProxy;
+
+  // True when a find is in progress. Used to avoid running JavaScript during
+  // disable when there is nothing to clear.
+  BOOL _findStringStarted;
+
+  // Bridge to observe the web state from Objective-C.
+  scoped_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
+}
+
+@synthesize delegate = _delegate;
+
+- (id)initWithWebState:(web::WebState*)webState
+              delegate:(id<FindInPageControllerDelegate>)delegate {
+  self = [super init];
+  if (self) {
+    DCHECK(delegate);
+    _findInPageJsManager = base::mac::ObjCCastStrict<JsFindinpageManager>(
+        [webState->GetJSInjectionReceiver()
+            instanceOfClass:[JsFindinpageManager class]]);
+    _delegate = delegate;
+    _webStateObserverBridge.reset(
+        new web::WebStateObserverBridge(webState, self));
+    _webViewProxy.reset([webState->GetWebViewProxy() retain]);
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(findBarTextFieldWillBecomeFirstResponder:)
+               name:kFindBarTextFieldWillBecomeFirstResponderNotification
+             object:nil];
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(findBarTextFieldDidResignFirstResponder:)
+               name:kFindBarTextFieldDidResignFirstResponderNotification
+             object:nil];
+    DOMAlteringLock::CreateForWebState(webState);
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [[NSNotificationCenter defaultCenter] removeObserver:self];
+  [super dealloc];
+}
+
+- (FindInPageModel*)findInPageModel {
+  return [_findInPageJsManager findInPageModel];
+}
+
+- (BOOL)canFindInPage {
+  return [_webViewProxy hasSearchableTextContent];
+}
+
+- (void)initFindInPage {
+  [_findInPageJsManager inject];
+
+  // Initialize the module with our frame size.
+  CGRect frame = [_webViewProxy bounds];
+  [_findInPageJsManager setWidth:frame.size.width height:frame.size.height];
+}
+
+- (CRWWebViewScrollViewProxy*)webViewScrollView {
+  return [_webViewProxy scrollViewProxy];
+}
+
+- (void)processPumpResult:(BOOL)finished
+              scrollPoint:(CGPoint)scrollPoint
+        completionHandler:(ProceduralBlock)completionHandler {
+  if (finished) {
+    [_delegate willAdjustScrollPosition];
+    [[_webViewProxy scrollViewProxy] setContentOffset:scrollPoint animated:YES];
+    if (completionHandler)
+      completionHandler();
+  } else {
+    [self performSelector:@selector(startPumpingWithCompletionHandler:)
+               withObject:completionHandler
+               afterDelay:kRecurringPumpDelay];
+  }
+}
+
+- (void)findStringInPage:(NSString*)query
+       completionHandler:(ProceduralBlock)completionHandler {
+  ProceduralBlockWithBool lockAction = ^(BOOL hasLock) {
+    if (!hasLock) {
+      if (completionHandler) {
+        completionHandler();
+      }
+      return;
+    }
+    // Cancel any previous pumping.
+    [NSObject cancelPreviousPerformRequestsWithTarget:self];
+    [self initFindInPage];
+    // Keep track of whether a find is in progress so to avoid running
+    // JavaScript during disable if unnecessary.
+    _findStringStarted = YES;
+    base::WeakNSObject<FindInPageController> weakSelf(self);
+    [_findInPageJsManager findString:query
+                   completionHandler:^(BOOL finished, CGPoint point) {
+                     [weakSelf processPumpResult:finished
+                                     scrollPoint:point
+                               completionHandler:completionHandler];
+                   }];
+  };
+  DOMAlteringLock::FromWebState([self webState])->Acquire(self, lockAction);
+}
+
+- (void)startPumpingWithCompletionHandler:(ProceduralBlock)completionHandler {
+  base::WeakNSObject<FindInPageController> weakSelf(self);
+  id completionHandlerBlock = ^void(BOOL findFinished) {
+    if (findFinished) {
+      // Pumping complete. Nothing else to do.
+      if (completionHandler)
+        completionHandler();
+      return;
+    }
+    // Further pumping is required.
+    [weakSelf performSelector:@selector(startPumpingWithCompletionHandler:)
+                   withObject:completionHandler
+                   afterDelay:kRecurringPumpDelay];
+  };
+  [self pumpFindStringInPageWithCompletionHandler:completionHandlerBlock];
+}
+
+- (void)pumpFindStringInPageWithCompletionHandler:
+    (void (^)(BOOL))completionHandler {
+  base::WeakNSObject<FindInPageController> weakSelf(self);
+  [_findInPageJsManager pumpWithCompletionHandler:^(BOOL finished,
+                                                    CGPoint point) {
+    base::scoped_nsobject<FindInPageController> strongSelf([weakSelf retain]);
+    if (finished) {
+      [[strongSelf delegate] willAdjustScrollPosition];
+      [[strongSelf webViewScrollView] setContentOffset:point animated:YES];
+    }
+    completionHandler(finished);
+  }];
+}
+
+- (void)findNextStringInPageWithCompletionHandler:
+    (ProceduralBlock)completionHandler {
+  [self initFindInPage];
+  base::WeakNSObject<FindInPageController> weakSelf(self);
+  [_findInPageJsManager nextMatchWithCompletionHandler:^(CGPoint point) {
+    base::scoped_nsobject<FindInPageController> strongSelf([weakSelf retain]);
+    [[strongSelf delegate] willAdjustScrollPosition];
+    CGFloat contentHeight = [strongSelf webViewScrollView].contentSize.height;
+    CGFloat frameHeight = [strongSelf webViewScrollView].frame.size.height;
+    CGFloat maxScroll = fmax(0, contentHeight - frameHeight);
+    if (point.y > maxScroll) {
+      point.y = maxScroll;
+    }
+    [[strongSelf webViewScrollView] setContentOffset:point animated:YES];
+    if (completionHandler)
+      completionHandler();
+  }];
+}
+
+// Highlight the previous search match, update model and scroll to match.
+- (void)findPreviousStringInPageWithCompletionHandler:
+    (ProceduralBlock)completionHandler {
+  [self initFindInPage];
+  base::WeakNSObject<FindInPageController> weakSelf(self);
+  [_findInPageJsManager previousMatchWithCompletionHandler:^(CGPoint point) {
+    base::scoped_nsobject<FindInPageController> strongSelf([weakSelf retain]);
+    [[strongSelf delegate] willAdjustScrollPosition];
+    [[strongSelf webViewScrollView] setContentOffset:point animated:YES];
+    if (completionHandler)
+      completionHandler();
+  }];
+}
+
+// Remove highlights from the page and disable the model.
+- (void)disableFindInPageWithCompletionHandler:
+    (ProceduralBlock)completionHandler {
+  if (![self canFindInPage])
+    return;
+  // Cancel any queued calls to |recurringPumpWithCompletionHandler|.
+  [NSObject cancelPreviousPerformRequestsWithTarget:self];
+  base::WeakNSObject<FindInPageController> weakSelf(self);
+  ProceduralBlock handler = ^{
+    web::WebState* webState = [self webState];
+    if (webState)
+      DOMAlteringLock::FromWebState(webState)->Release(self);
+    if (completionHandler)
+      completionHandler();
+  };
+  // Only run JSFindInPageManager disable if there is a string in progress to
+  // avoid WKWebView crash on deallocation due to outstanding completion
+  // handler.
+  if (_findStringStarted) {
+    [_findInPageJsManager disableWithCompletionHandler:handler];
+    _findStringStarted = NO;
+  } else {
+    handler();
+  }
+}
+
+- (void)saveSearchTerm {
+  [self findPasteboard].string = [[self findInPageModel] text];
+}
+
+- (void)restoreSearchTerm {
+  NSString* term = [self findPasteboard].string;
+  [[self findInPageModel] updateQuery:(term ? term : @"") matches:0];
+}
+
+- (UIPasteboard*)findPasteboard {
+  return [UIPasteboard pasteboardWithName:UIPasteboardNameFind create:NO];
+}
+
+- (web::WebState*)webState {
+  return _webStateObserverBridge ? _webStateObserverBridge->web_state()
+                                 : nullptr;
+}
+
+#pragma mark - Notification listeners
+
+- (void)findBarTextFieldWillBecomeFirstResponder:(NSNotification*)note {
+  // Listen to the keyboard appearance notifications.
+  NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
+  [defaultCenter addObserver:self
+                    selector:@selector(keyboardDidShow:)
+                        name:UIKeyboardDidShowNotification
+                      object:nil];
+  [defaultCenter addObserver:self
+                    selector:@selector(keyboardWillHide:)
+                        name:UIKeyboardWillHideNotification
+                      object:nil];
+}
+
+- (void)findBarTextFieldDidResignFirstResponder:(NSNotification*)note {
+  // Resign from the keyboard appearance notifications on the next turn of the
+  // runloop.
+  dispatch_async(dispatch_get_main_queue(), ^{
+    NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
+    [defaultCenter removeObserver:self
+                             name:UIKeyboardDidShowNotification
+                           object:nil];
+    [defaultCenter removeObserver:self
+                             name:UIKeyboardWillHideNotification
+                           object:nil];
+  });
+}
+
+- (void)keyboardDidShow:(NSNotification*)note {
+  NSDictionary* info = [note userInfo];
+  CGSize kbSize =
+      [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
+  UIInterfaceOrientation orientation =
+      [[UIApplication sharedApplication] statusBarOrientation];
+  CGFloat kbHeight = kbSize.height;
+  // Prior to iOS 8, the keyboard frame was not dependent on interface
+  // orientation, so height and width need to be swapped in landscape mode.
+  if (UIInterfaceOrientationIsLandscape(orientation) &&
+      !base::ios::IsRunningOnIOS8OrLater()) {
+    kbHeight = kbSize.width;
+  }
+
+  UIEdgeInsets insets = UIEdgeInsetsZero;
+  insets.bottom = kbHeight;
+  [_webViewProxy registerInsets:insets forCaller:self];
+}
+
+- (void)keyboardWillHide:(NSNotification*)note {
+  [_webViewProxy unregisterInsetsForCaller:self];
+}
+
+- (void)detachFromWebState {
+  _webStateObserverBridge.reset();
+}
+
+#pragma mark - CRWWebStateObserver Methods
+
+- (void)webStateDestroyed:(web::WebState*)webState {
+  [self detachFromWebState];
+}
+
+#pragma mark - DOMAltering Methods
+
+- (BOOL)canReleaseDOMLock {
+  return NO;
+}
+
+- (void)releaseDOMLockWithCompletionHandler:(ProceduralBlock)completionHandler {
+  NOTREACHED();
+}
+
+@end
diff --git a/ios/chrome/browser/find_in_page/find_in_page_model.h b/ios/chrome/browser/find_in_page/find_in_page_model.h
new file mode 100644
index 0000000..3199b64
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/find_in_page_model.h
@@ -0,0 +1,39 @@
+// Copyright 2011 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_FIND_IN_PAGE_FIND_IN_PAGE_MODEL_H_
+#define IOS_CHROME_BROWSER_FIND_IN_PAGE_FIND_IN_PAGE_MODEL_H_
+
+#import <UIKit/UIKit.h>
+#include "base/mac/scoped_nsobject.h"
+
+// This is a simplified version of find_tab_helper.cc.
+@interface FindInPageModel : NSObject {
+ @private
+  // Should find in page be displayed.
+  BOOL enabled_;
+  // The current search string.
+  base::scoped_nsobject<NSString> text_;
+  // The number of matches for |text_|
+  NSUInteger matches_;
+  // The currently higlighted index.
+  NSUInteger currentIndex_;
+  // The content offset needed to display the |currentIndex_| match.
+  CGPoint currentPoint_;
+}
+
+@property(nonatomic, readwrite, assign) BOOL enabled;
+@property(nonatomic, readonly) NSString* text;
+@property(nonatomic, readonly) NSUInteger matches;
+@property(nonatomic, readonly) NSUInteger currentIndex;
+@property(nonatomic, readonly) CGPoint currentPoint;
+
+// Update the query string and the number of matches.
+- (void)updateQuery:(NSString*)query matches:(NSUInteger)matches;
+// Update the current match index and its found position.
+- (void)updateIndex:(NSInteger)index atPoint:(CGPoint)point;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_FIND_IN_PAGE_FIND_IN_PAGE_MODEL_H_
diff --git a/ios/chrome/browser/find_in_page/find_in_page_model.mm b/ios/chrome/browser/find_in_page/find_in_page_model.mm
new file mode 100644
index 0000000..ea107eb
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/find_in_page_model.mm
@@ -0,0 +1,37 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/find_in_page/find_in_page_model.h"
+
+@implementation FindInPageModel
+
+@synthesize enabled = enabled_;
+@synthesize matches = matches_;
+@synthesize currentIndex = currentIndex_;
+@synthesize currentPoint = currentPoint_;
+
+- (NSString*)text {
+  return text_;
+}
+
+- (void)setEnabled:(BOOL)enabled {
+  enabled_ = enabled;
+  matches_ = 0;
+  currentIndex_ = 0;
+  currentPoint_ = CGPointZero;
+}
+
+- (void)updateQuery:(NSString*)query matches:(NSUInteger)matches {
+  if (query)
+    text_.reset([query copy]);
+  matches_ = matches;
+  currentIndex_ = 0;
+}
+
+- (void)updateIndex:(NSInteger)index atPoint:(CGPoint)point {
+  currentIndex_ = index;
+  currentPoint_ = point;
+}
+
+@end
diff --git a/ios/chrome/browser/find_in_page/js_findinpage_manager.h b/ios/chrome/browser/find_in_page/js_findinpage_manager.h
new file mode 100644
index 0000000..77591ada
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/js_findinpage_manager.h
@@ -0,0 +1,70 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_FIND_IN_PAGE_JS_FINDINPAGE_MANAGER_H_
+#define IOS_CHROME_BROWSER_FIND_IN_PAGE_JS_FINDINPAGE_MANAGER_H_
+
+#include <CoreGraphics/CGBase.h>
+#include <CoreGraphics/CGGeometry.h>
+
+#include "base/ios/block_types.h"
+#import "base/mac/scoped_nsobject.h"
+#import "ios/web/public/web_state/js/crw_js_injection_manager.h"
+
+// Data from find in page.
+typedef struct FindInPageEntry {
+  CGPoint point;    // Scroll offset required to center the highlighted item.
+  NSInteger index;  // Currently higlighted search term.
+} FindInPageEntry;
+
+// Constant for "not found".
+extern FindInPageEntry FindInPageEntryZero;
+
+@class CRWJSInjectionReceiver;
+@class FindInPageModel;
+
+// Manager for the injection of the Find In Page JavaScript.
+@interface JsFindinpageManager : CRWJSInjectionManager {
+ @private
+  // Model for find in page.
+  base::scoped_nsobject<FindInPageModel> findInPageModel_;
+}
+
+// Find In Page model. TODO(justincohen) consider using find_tab_helper.cc.
+@property(nonatomic, readonly) FindInPageModel* findInPageModel;
+
+// Sets the width and height of the window.
+- (void)setWidth:(CGFloat)width height:(CGFloat)height;
+
+// Runs injected JavaScript to find |query| string. Calls |completionHandler|
+// with YES if the find operation completed, it is called with NO otherwise.
+// If the find operation was successfiul the first match to scroll to is
+// also called with. If the |completionHandler| is called with NO, another
+// call to |pumpWithCompletionHandler:| is required. |completionHandler| cannot
+// be nil.
+- (void)findString:(NSString*)query
+    completionHandler:(void (^)(BOOL, CGPoint))completionHandler;
+
+// Searches for more matches. Calls |completionHandler| with a success BOOL and
+// scroll position if pumping was successfull. If the pumping was unsuccessfull
+// another pumping call maybe required. |completionHandler| cannot be nil.
+// TODO(shreyasv): Consider folding the logic for re-pumping into this class
+// instead of having clients having to do it.
+- (void)pumpWithCompletionHandler:(void (^)(BOOL, CGPoint))completionHandler;
+
+// Moves to the next matched location and executes the completion handler with
+// the new scroll position passed in. The |completionHandler| can be nil.
+- (void)nextMatchWithCompletionHandler:(void (^)(CGPoint))completionHandler;
+
+// Moves to the previous matched location and executes the completion handle
+// with the new scroll position passed in. The |completionHandler| can be nil.
+- (void)previousMatchWithCompletionHandler:(void (^)(CGPoint))completionHandler;
+
+// Stops find in page and calls |completionHandler| once find in page is
+// stopped. |completionHandler| cannot be nil.
+- (void)disableWithCompletionHandler:(ProceduralBlock)completionHandler;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_FIND_IN_PAGE_JS_FINDINPAGE_MANAGER_H_
diff --git a/ios/chrome/browser/find_in_page/js_findinpage_manager.mm b/ios/chrome/browser/find_in_page/js_findinpage_manager.mm
new file mode 100644
index 0000000..e718aa66
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/js_findinpage_manager.mm
@@ -0,0 +1,278 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/find_in_page/js_findinpage_manager.h"
+
+#include <string>
+
+#import "base/ios/weak_nsobject.h"
+#include "base/json/json_reader.h"
+#include "base/json/string_escape.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/values.h"
+#import "ios/chrome/browser/find_in_page/find_in_page_model.h"
+#import "ios/web/public/web_state/js/crw_js_early_script_manager.h"
+
+namespace {
+
+// Global variable defined in find_in_page.js that can be used for testing
+// whether JavaScript bas heen loaded.
+NSString* const kFindInPageBeacon = @"window.__gCrWeb.findInPage";
+
+// Initializes Find In Page JavaScript with the width and height of the window.
+NSString* const kFindInPageInit = @"window.__gCrWeb.findInPage && "
+                                   "window.__gCrWeb.findInPage.init(%.f, %.f);";
+
+// This will only do verbatim matches.
+// The timeout of 100ms is hardcoded into this string so we don't have
+// to spend any time at runtime to format this constant into another constant.
+NSString* const kFindInPageVerbatim =
+    @"window.__gCrWeb.findInPage && "
+     "window.__gCrWeb.findInPage.highlightWord(%@, false, 100.0);";
+
+// The timeout of 100ms is hardcoded into this string so we don't have
+// to spend any time at runtime to format this constant into another constant.
+NSString* const kFindInPagePump =
+    @"window.__gCrWeb.findInPage && "
+     "window.__gCrWeb.findInPage.pumpSearch(100.0);";
+
+NSString* const kFindInPagePrev = @"window.__gCrWeb.findInPage && "
+                                   "window.__gCrWeb.findInPage.goPrev();";
+
+NSString* const kFindInPageNext = @"window.__gCrWeb.findInPage && "
+                                   "window.__gCrWeb.findInPage.goNext();";
+
+NSString* const kFindInPageDisable = @"window.__gCrWeb.findInPage && "
+                                      "window.__gCrWeb.findInPage.disable();";
+
+NSString* const kFindInPagePending = @"[false]";
+
+const FindInPageEntry kFindInPageEntryZero = {{0.0, 0.0}, 0};
+
+}  // namespace
+
+@interface JsFindinpageManager ()
+// Update find in page model with results, return true if fip completes or
+// false if still pending and requires pumping. If |point| is not nil, it will
+// contain the scroll position upon return.
+- (BOOL)processFindInPageResult:(NSString*)result
+                 scrollPosition:(CGPoint*)point;
+// Updates find in page model with results. Calls |completionHandler| with the
+// the result of the processing and the new scroll position if successfull. If
+// |completionHandler| is called with NO, further pumping is required.
+// |completionHandler| cannot be nil.
+- (void)processFindInPagePumpResult:(NSString*)result
+                  completionHandler:(void (^)(BOOL, CGPoint))completionHandler;
+// Helper functions to extract FindInPageEntry from JSON.
+- (FindInPageEntry)findInPageEntryForJson:(NSString*)jsonStr;
+- (FindInPageEntry)entryForListValue:(base::ListValue*)position;
+// Executes |script| which is a piece of JavaScript to move to the next or
+// previous element in the page and executes |completionHandler| after moving
+// with the new scroll position passed in.
+- (void)moveHighlightByEvaluatingJavaScript:(NSString*)script
+                          completionHandler:
+                              (void (^)(CGPoint))completionHandler;
+// Updates the current match index and its found position in the model.
+- (void)updateIndex:(NSInteger)index atPoint:(CGPoint)point;
+@end
+
+@implementation JsFindinpageManager
+
+- (FindInPageModel*)findInPageModel {
+  if (!findInPageModel_)
+    findInPageModel_.reset([[FindInPageModel alloc] init]);
+  return findInPageModel_.get();
+}
+
+- (void)setWidth:(CGFloat)width height:(CGFloat)height {
+  NSString* javaScript =
+      [NSString stringWithFormat:kFindInPageInit, width, height];
+  [self evaluate:javaScript stringResultHandler:nil];
+}
+
+- (void)findString:(NSString*)query
+    completionHandler:(void (^)(BOOL, CGPoint))completionHandler {
+  DCHECK(completionHandler);
+  // Save the query in the model before searching.
+  [findInPageModel_ updateQuery:query matches:0];
+
+  // Escape |query| before passing to js.
+  std::string escapedJson;
+  base::EscapeJSONString(base::SysNSStringToUTF16(query), true, &escapedJson);
+  NSString* jsonQuery =
+      [NSString stringWithFormat:kFindInPageVerbatim,
+                                 base::SysUTF8ToNSString(escapedJson.c_str())];
+  base::WeakNSObject<JsFindinpageManager> weakSelf(self);
+  [self evaluate:jsonQuery
+      stringResultHandler:^(NSString* result, NSError* error) {
+        [weakSelf processFindInPagePumpResult:result
+                            completionHandler:completionHandler];
+      }];
+}
+
+- (void)pumpWithCompletionHandler:(void (^)(BOOL, CGPoint))completionHandler {
+  DCHECK(completionHandler);
+  base::WeakNSObject<JsFindinpageManager> weakSelf(self);
+  [self evaluate:kFindInPagePump
+      stringResultHandler:^(NSString* result, NSError* error) {
+        // TODO(shreyasv): What to do here if this returns an NSError in the
+        // WKWebView version.
+        [weakSelf processFindInPagePumpResult:result
+                            completionHandler:completionHandler];
+      }];
+}
+
+- (void)nextMatchWithCompletionHandler:(void (^)(CGPoint))completionHandler {
+  [self moveHighlightByEvaluatingJavaScript:kFindInPageNext
+                          completionHandler:completionHandler];
+}
+
+- (void)previousMatchWithCompletionHandler:
+        (void (^)(CGPoint))completionHandler {
+  [self moveHighlightByEvaluatingJavaScript:kFindInPagePrev
+                          completionHandler:completionHandler];
+}
+
+- (void)moveHighlightByEvaluatingJavaScript:(NSString*)script
+                          completionHandler:
+                              (void (^)(CGPoint))completionHandler {
+  base::WeakNSObject<JsFindinpageManager> weakSelf(self);
+  [self evaluate:script
+      stringResultHandler:^(NSString* result, NSError* error) {
+        base::WeakNSObject<JsFindinpageManager> strongSelf([weakSelf retain]);
+        if (!strongSelf)
+          return;
+        DCHECK(!error);
+        FindInPageEntry entry = kFindInPageEntryZero;
+        if (![result isEqualToString:kFindInPagePending])
+          entry = [strongSelf findInPageEntryForJson:result];
+        CGPoint newPoint = entry.point;
+        [strongSelf updateIndex:entry.index atPoint:newPoint];
+        if (completionHandler)
+          completionHandler(newPoint);
+      }];
+}
+
+- (void)disableWithCompletionHandler:(ProceduralBlock)completionHandler {
+  DCHECK(completionHandler);
+  base::WeakNSObject<FindInPageModel> weakFindInPageModel(findInPageModel_);
+  [self evaluate:kFindInPageDisable
+      stringResultHandler:^(NSString* result, NSError* error) {
+        [weakFindInPageModel setEnabled:NO];
+        completionHandler();
+      }];
+}
+
+#pragma mark -
+#pragma mark FindInPageEntry
+
+- (BOOL)processFindInPageResult:(NSString*)result
+                 scrollPosition:(CGPoint*)point {
+  if (!result)
+    return NO;
+
+  // Parse JSONs.
+  std::string json([result UTF8String]);
+  scoped_ptr<base::Value> root(base::JSONReader::Read(json, false));
+  if (!root.get())
+    return YES;
+  if (!root->IsType(base::Value::TYPE_LIST))
+    return YES;
+
+  base::ListValue* resultList = static_cast<base::ListValue*>(root.get());
+  DCHECK(resultList);
+  if (resultList) {
+    if (resultList->GetSize() == 2) {
+      int numHighlighted = 0;
+      if (resultList->GetInteger(0, &numHighlighted)) {
+        if (numHighlighted > 0) {
+          base::ListValue* position;
+          if (resultList->GetList(1, &position)) {
+            [findInPageModel_ updateQuery:nil matches:numHighlighted];
+            // Scroll to first match.
+            FindInPageEntry entry = [self entryForListValue:position];
+            [findInPageModel_ updateIndex:entry.index atPoint:entry.point];
+            if (point)
+              *point = entry.point;
+          }
+        }
+      }
+    }
+  }
+  return YES;
+}
+
+- (void)processFindInPagePumpResult:(NSString*)result
+                  completionHandler:(void (^)(BOOL, CGPoint))completionHandler {
+  CGPoint point = CGPointZero;
+  if ([result isEqualToString:kFindInPagePending]) {
+    completionHandler(NO, point);
+  }
+  // TODO(shreyasv): Inline this call from the logic from the above function
+  // and remove the above function.
+  BOOL processFIPResult =
+      [self processFindInPageResult:result scrollPosition:&point];
+  completionHandler(processFIPResult, point);
+}
+
+- (void)updateIndex:(NSInteger)index atPoint:(CGPoint)point {
+  [findInPageModel_ updateIndex:index atPoint:point];
+}
+
+- (FindInPageEntry)findInPageEntryForJson:(NSString*)jsonStr {
+  std::string json([jsonStr UTF8String]);
+  scoped_ptr<base::Value> root(base::JSONReader::Read(json, false));
+  if (!root.get())
+    return kFindInPageEntryZero;
+
+  if (!root->IsType(base::Value::TYPE_LIST))
+    return kFindInPageEntryZero;
+
+  base::ListValue* position = static_cast<base::ListValue*>(root.get());
+  return [self entryForListValue:position];
+}
+
+- (FindInPageEntry)entryForListValue:(base::ListValue*)position {
+  if (!position)
+    return kFindInPageEntryZero;
+
+  // Position should always be of length 3, from [index,x,y].
+  DCHECK(position->GetSize() == 3);
+  if (position->GetSize() != 3)
+    return kFindInPageEntryZero;
+
+  // The array position comes from the JSON string [index, x, y], which
+  // represents the index of the currently found string, and the x and y
+  // position necessary to center that string.  Pull out that data into a
+  // FindInPageEntry struct.
+  int index;
+  double x = 0, y = 0;
+  position->GetInteger(0, &index);
+  position->GetDouble(1, &x);
+  position->GetDouble(2, &y);
+  FindInPageEntry entry;
+  entry.index = index;
+  entry.point.x = x;
+  entry.point.y = y;
+  return entry;
+}
+
+#pragma mark -
+#pragma mark ProtectedMethods
+
+- (NSString*)scriptPath {
+  return @"find_in_page";
+}
+
+- (NSString*)presenceBeacon {
+  return kFindInPageBeacon;
+}
+
+- (NSArray*)directDependencies {
+  return @[ [CRWJSEarlyScriptManager class] ];
+}
+
+@end
diff --git a/ios/chrome/browser/find_in_page/resources/find_in_page.js b/ios/chrome/browser/find_in_page/resources/find_in_page.js
new file mode 100644
index 0000000..c107582
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/resources/find_in_page.js
@@ -0,0 +1,964 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * This file is lifted from the GoogleMobile find tool.
+ *
+ * @fileoverview A find in page tool.  It scans the DOM for elements with the
+ * text being search for, and wraps them with a span that highlights them.
+ *
+ * @author bmcmahan@google.com (Benjamin McMahan)
+ *
+ */
+
+/**
+ * Namespace for this file.  Depends on __gCrWeb having already been injected.
+ */
+__gCrWeb['findInPage'] = {};
+
+/**
+ * Index of the current highlighted choice.  -1 means none.
+ * @type {number}
+ */
+__gCrWeb['findInPage']['index'] = -1;
+
+/**
+ * The list of found searches in span form.
+ * @type {Array.<Element>}
+ */
+__gCrWeb['findInPage']['spans'] = [];
+
+/**
+ * The list of frame documents.
+ * TODO(justincohen): x-domain frames won't work.
+ * @type {Array.<Document>}
+ */
+__gCrWeb['findInPage'].frameDocs = [];
+
+/**
+ * Associate array to stash element styles while the element is highlighted.
+ * @type {Object.<Element,Object<string,string>>}
+ */
+__gCrWeb['findInPage'].savedElementStyles = {};
+
+/**
+ * The style DOM element that we add.
+ * @type {Element}
+ */
+__gCrWeb['findInPage'].style = null;
+
+/**
+ * Width we expect the page to be.  For example (320/480) for iphone,
+ * (1024/768) for ipad.
+ * @type {number}
+ */
+__gCrWeb['findInPage'].pageWidth = 320;
+
+/**
+ * Height we expect the page to be.
+ * @type {number}
+ */
+__gCrWeb['findInPage'].pageHeight = 480;
+
+/**
+ * Maximum number of visible elements to count
+ * @type {number}
+ */
+__gCrWeb['findInPage'].maxVisibleElements = 100;
+
+/**
+ * A search is in progress.
+ * @type {boolean}
+ */
+__gCrWeb['findInPage'].searchInProgress = false;
+
+/**
+ * Node names that are not going to be processed.
+ * @type {Object}
+ */
+__gCrWeb['findInPage'].ignoreNodeNames = {
+ 'SCRIPT': 1,
+ 'STYLE': 1,
+ 'EMBED': 1,
+ 'OBJECT': 1
+};
+
+/**
+ * Class name of CSS element.
+ * @type {string}
+ */
+__gCrWeb['findInPage'].CSS_CLASS_NAME = 'find_in_page';
+
+/**
+ * ID of CSS style.
+ * @type {string}
+ */
+__gCrWeb['findInPage'].CSS_STYLE_ID = '__gCrWeb.findInPageStyle';
+
+/**
+ * Result passed back to app to indicate no results for the query.
+ * @type {string}
+ */
+__gCrWeb['findInPage'].NO_RESULTS = '[0,[0,0,0]]';
+
+/**
+ * Regex to escape regex special characters in a string.
+ * @type {RegExp}
+ */
+__gCrWeb['findInPage'].REGEX_ESCAPER = /([.?*+^$[\]\\(){}|-])/g;
+
+__gCrWeb['findInPage'].getCurrentSpan = function() {
+  return __gCrWeb['findInPage']['spans'][__gCrWeb['findInPage']['index']];
+};
+
+/**
+ * Creates the regex needed to find the text.
+ * @param {string} findText Phrase to look for.
+ * @param {boolean} opt_split True to split up the phrase.
+ * @return {RegExp} regex needed to find the text.
+ */
+__gCrWeb['findInPage'].getRegex = function(findText, opt_split) {
+  var regexString = '';
+  if (opt_split) {
+    var words = [];
+    var split = findText.split(' ');
+    for (var i = 0; i < split.length; i++) {
+      words.push(__gCrWeb['findInPage'].escapeRegex(split[i]));
+    }
+    var joinedWords = words.join('|');
+    regexString = '(' +
+        // Match at least one word.
+        '\\b(?:' + joinedWords + ')' +
+        // Include zero or more additional words separated by whitespace.
+        '(?:\\s*\\b(?:' + joinedWords + '))*' +
+        ')';
+  } else {
+    regexString = '(' + __gCrWeb['findInPage'].escapeRegex(findText) + ')';
+  }
+  return new RegExp(regexString, 'ig');
+};
+
+/**
+ * Get current timestamp.
+ * @return {number} timestamp.
+ */
+__gCrWeb['findInPage'].time = function() {
+  return (new Date).getTime();
+};
+
+/**
+ * After |timeCheck| iterations, return true if |now| - |start| is greater than
+ * |timeout|.
+ * @return {boolean} Find in page needs to return.
+ */
+__gCrWeb['findInPage'].overTime = function() {
+  return (__gCrWeb['findInPage'].time() - __gCrWeb['findInPage'].startTime >
+          __gCrWeb['findInPage'].timeout);
+};
+
+/**
+ * Looks for a phrase in the DOM.
+ * @param {string} findText Phrase to look for like "ben franklin".
+ * @param {boolean} opt_split True to split up the words and look for any
+ *     of them.  False to require the full phrase to be there.
+ *     Undefined will try the full phrase, and if nothing is found do the split.
+ * @param {number} timeout Maximum time to run.
+ * @return {number} How many results there are in the page.
+ */
+__gCrWeb['findInPage']['highlightWord'] =
+    function(findText, opt_split, timeout) {
+  if (__gCrWeb['findInPage']['spans'] &&
+      __gCrWeb['findInPage']['spans'].length) {
+    // Clean up a previous run.
+    __gCrWeb['findInPage']['clearHighlight']();
+  }
+  if (!findText || !findText.replace(/\u00a0|\s/g, '')) {
+    // No searching for emptyness.
+    return __gCrWeb['findInPage'].NO_RESULTS;
+  }
+
+  // Store all DOM modifications to do them in a tight loop at once.
+  __gCrWeb['findInPage'].replacements = [];
+
+  // Node is what we are currently looking at.
+  __gCrWeb['findInPage'].node = document.body;
+
+  // Holds what nodes we have not processed yet.
+  __gCrWeb['findInPage'].stack = [];
+
+  // Push frames into stack too.
+  for (var i = __gCrWeb['findInPage'].frameDocs.length - 1; i >= 0; i--) {
+    var doc = __gCrWeb['findInPage'].frameDocs[i];
+    __gCrWeb['findInPage'].stack.push(doc);
+  }
+
+  // Number of visible elements found.
+  __gCrWeb['findInPage'].visibleFound = 0;
+
+  // Index tracking variables so search can be broken up into multiple calls.
+  __gCrWeb['findInPage'].visibleIndex = 0;
+  __gCrWeb['findInPage'].replacementsIndex = 0;
+  __gCrWeb['findInPage'].replacementNewNodesIndex = 0;
+
+  __gCrWeb['findInPage'].regex =
+      __gCrWeb['findInPage'].getRegex(findText, opt_split);
+
+  __gCrWeb['findInPage'].searchInProgress = true;
+
+  return __gCrWeb['findInPage']['pumpSearch'](timeout);
+};
+
+/**
+ * Break up find in page DOM regex, DOM manipulation and visibility check
+ * into sections that can be stopped and restarted later.  Because the js runs
+ * in the main UI thread, anything over timeout will cause the UI to lock up.
+ * @param {number} timeout Only run find in page until timeout.
+ * @return {boolean} Whether find in page completed.
+ */
+__gCrWeb['findInPage']['pumpSearch'] = function(timeout) {
+  var opt_split = false;
+  // TODO(justincohen): It would be better if this DCHECKed.
+  if (__gCrWeb['findInPage'].searchInProgress == false)
+    return __gCrWeb['findInPage'].NO_RESULTS;
+
+  __gCrWeb['findInPage'].timeout = timeout;
+  __gCrWeb['findInPage'].startTime = __gCrWeb['findInPage'].time();
+
+  var regex = __gCrWeb['findInPage'].regex;
+  // Go through every node in DFS fashion.
+  while (__gCrWeb['findInPage'].node) {
+    var node = __gCrWeb['findInPage'].node;
+    var children = node.childNodes;
+    if (children && children.length) {
+      // add all (reasonable) children
+      for (var i = children.length - 1; i >= 0; --i) {
+        var child = children[i];
+        if ((child.nodeType == 1 || child.nodeType == 3) &&
+            !__gCrWeb['findInPage'].ignoreNodeNames[child.nodeName]) {
+          __gCrWeb['findInPage'].stack.push(children[i]);
+        }
+      }
+    }
+    if (node.nodeType == 3 && node.parentNode) {
+      var strIndex = 0;
+      var nodes = [];
+      var match;
+      while (match = regex.exec(node.textContent)) {
+        try {
+          var matchText = match[0];
+
+          // If there is content before this match, add it to a new text node.
+          if (match.index > 0) {
+            var nodeSubstr = node.textContent.substring(strIndex,
+                                                        match.index);
+            nodes.push(node.ownerDocument.createTextNode(nodeSubstr));
+          }
+
+          // Now create our matched element.
+          var element = node.ownerDocument.createElement('chrome_find');
+          element.setAttribute('class', __gCrWeb['findInPage'].CSS_CLASS_NAME);
+          element.innerHTML = __gCrWeb['findInPage'].escapeHTML(matchText);
+          nodes.push(element);
+
+          strIndex = match.index + matchText.length;
+        } catch (e) {
+          // Do nothing.
+        }
+      }
+      if (nodes.length) {
+        // Add any text after our matches to a new text node.
+        if (strIndex < node.textContent.length) {
+          var substr = node.textContent.substring(strIndex,
+                                                  node.textContent.length);
+          nodes.push(node.ownerDocument.createTextNode(substr));
+        }
+        __gCrWeb['findInPage'].replacements.push(
+            {oldNode: node, newNodes: nodes});
+        regex.lastIndex = 0;
+      }
+
+    }
+
+    if (__gCrWeb['findInPage'].overTime())
+      return '[false]';
+
+    if (__gCrWeb['findInPage'].stack.length > 0) {
+      __gCrWeb['findInPage'].node = __gCrWeb['findInPage'].stack.pop();
+    } else {
+      __gCrWeb['findInPage'].node = null;
+    }
+  }
+
+  // Insert each of the replacement nodes into the old node's parent, then
+  // remove the old node.
+  var replacements = __gCrWeb['findInPage'].replacements;
+
+  // Last position in replacements array.
+  var rIndex = __gCrWeb['findInPage'].replacementsIndex;
+  var rMax = replacements.length;
+  for (; rIndex < rMax; rIndex++) {
+    var replacement = replacements[rIndex];
+    var parent = replacement.oldNode.parentNode;
+    if (parent == null)
+      continue;
+    var rNodesMax = replacement.newNodes.length;
+    for (var rNodesIndex = __gCrWeb['findInPage'].replacementNewNodesIndex;
+         rNodesIndex < rNodesMax; rNodesIndex++) {
+      if (__gCrWeb['findInPage'].overTime()) {
+        __gCrWeb['findInPage'].replacementsIndex = rIndex;
+        __gCrWeb['findInPage'].replacementNewNodesIndex = rNodesIndex;
+        return __gCrWeb.stringify([false]);
+      }
+      parent.insertBefore(replacement.newNodes[rNodesIndex],
+                          replacement.oldNode);
+    }
+    parent.removeChild(replacement.oldNode);
+    __gCrWeb['findInPage'].replacementNewNodesIndex = 0;
+  }
+  // Save last position in replacements array.
+  __gCrWeb['findInPage'].replacementsIndex = rIndex;
+
+  __gCrWeb['findInPage']['spans'] =
+      __gCrWeb['findInPage'].getAllElementsByClassName(
+          __gCrWeb['findInPage'].CSS_CLASS_NAME);
+
+  // Count visible elements.
+  var max = __gCrWeb['findInPage']['spans'].length;
+  var maxVisible = __gCrWeb['findInPage'].maxVisibleElements;
+  for (var index = __gCrWeb['findInPage'].visibleIndex; index < max; index++) {
+    var elem = __gCrWeb['findInPage']['spans'][index];
+    if (__gCrWeb['findInPage'].overTime()) {
+      __gCrWeb['findInPage'].visibleIndex = index;
+      return __gCrWeb.stringify([false]);
+    }
+
+    // Stop after |maxVisible| elements.
+    if (__gCrWeb['findInPage'].visibleFound > maxVisible) {
+      __gCrWeb['findInPage']['spans'][index].visibleIndex = maxVisible;
+      continue;
+    }
+
+    if (__gCrWeb['findInPage'].isVisible(elem)) {
+      __gCrWeb['findInPage'].visibleFound++;
+      __gCrWeb['findInPage']['spans'][index].visibleIndex =
+          __gCrWeb['findInPage'].visibleFound;
+    }
+  }
+
+  __gCrWeb['findInPage'].searchInProgress = false;
+
+  var pos;
+  // Try again flow.
+  // If opt_split is true, we are done since we won't do any better.
+  // If opt_split is false, they were explicit about wanting the full thing
+  // so do not try with a split.
+  // If opt_split is undefined and we did not find an answer, go ahead and try
+  // splitting the terms.
+  if (__gCrWeb['findInPage']['spans'].length == 0 && opt_split === undefined) {
+    // Try to be more aggressive:
+    return __gCrWeb['findInPage']['highlightWord'](findText, true);
+  } else {
+    pos = __gCrWeb['findInPage']['goNext']();
+    if (pos) {
+      return '[' + __gCrWeb['findInPage'].visibleFound + ',' + pos + ']';
+    } else if (opt_split === undefined) {
+      // Nothing visible, go ahead and be more aggressive.
+      return __gCrWeb['findInPage']['highlightWord'](findText, true);
+    } else {
+      return __gCrWeb['findInPage'].NO_RESULTS;
+    }
+  }
+};
+
+/**
+ * Converts a node list to an array.
+ * @param {object} nodeList DOM node list.
+ * @return {object} array.
+ */
+__gCrWeb['findInPage'].toArray = function(nodeList) {
+  var array = [];
+  for (var i = 0; i < nodeList.length; i++)
+    array[i] = nodeList[i];
+  return array;
+};
+
+/**
+ * Return all elements of class name, spread out over various frames.
+ * @param {string} name of class.
+ * @return {object} array of elements matching class name.
+ */
+__gCrWeb['findInPage'].getAllElementsByClassName = function(name) {
+  var nodeList = document.getElementsByClassName(name);
+  var elements = __gCrWeb['findInPage'].toArray(nodeList);
+  for (var i = __gCrWeb['findInPage'].frameDocs.length - 1; i >= 0; i--) {
+    var doc = __gCrWeb['findInPage'].frameDocs[i];
+    nodeList = doc.getElementsByClassName(name);
+    elements = elements.concat(__gCrWeb['findInPage'].toArray(nodeList));
+  }
+  return elements;
+};
+
+/**
+ * Removes all currently highlighted spans.
+ * Note: It does not restore previous state, just removes the class name.
+ */
+__gCrWeb['findInPage']['clearHighlight'] = function() {
+  if (__gCrWeb['findInPage']['index'] >= 0) {
+    __gCrWeb['findInPage'].removeSelectHighlight(
+        __gCrWeb['findInPage'].getCurrentSpan());
+  }
+  // Store all DOM modifications to do them in a tight loop.
+  var modifications = [];
+  var length = __gCrWeb['findInPage']['spans'].length;
+  var prevParent = null;
+  for (var i = length - 1; i >= 0; i--) {
+    var elem = __gCrWeb['findInPage']['spans'][i];
+    var parentNode = elem.parentNode;
+    // Safari has an occasional |elem.innerText| bug that drops the trailing
+    // space.  |elem.innerText| would be more correct in this situation, but
+    // since we only allow text in this element, grabbing the HTML value should
+    // not matter.
+    var nodeText = elem.innerHTML;
+    // If this element has the same parent as the previous, check if we should
+    // add this node to the previous one.
+    if (prevParent && prevParent.isSameNode(parentNode) &&
+        elem.nextSibling.isSameNode(
+            __gCrWeb['findInPage']['spans'][i + 1].previousSibling)) {
+      var prevMod = modifications[modifications.length - 1];
+      prevMod.nodesToRemove.push(elem);
+      var elemText = elem.innerText;
+      if (elem.previousSibling) {
+        prevMod.nodesToRemove.push(elem.previousSibling);
+        elemText = elem.previousSibling.textContent + elemText;
+      }
+      prevMod.replacement.textContent =
+          elemText + prevMod.replacement.textContent;
+    }
+    else { // Element isn't attached to previous, so create a new modification.
+      var nodesToRemove = [elem];
+      if (elem.previousSibling && elem.previousSibling.nodeType == 3) {
+        nodesToRemove.push(elem.previousSibling);
+        nodeText = elem.previousSibling.textContent + nodeText;
+      }
+      if (elem.nextSibling && elem.nextSibling.nodeType == 3) {
+        nodesToRemove.push(elem.nextSibling);
+        nodeText = nodeText + elem.nextSibling.textContent;
+      }
+      var textNode = elem.ownerDocument.createTextNode(nodeText);
+      modifications.push({nodesToRemove: nodesToRemove, replacement: textNode});
+    }
+    prevParent = parentNode;
+  }
+  var numMods = modifications.length;
+  for (i = numMods - 1; i >= 0; i--) {
+    var mod = modifications[i];
+    for (var j = 0; j < mod.nodesToRemove.length; j++) {
+      var existing = mod.nodesToRemove[j];
+      if (j == 0) {
+        existing.parentNode.replaceChild(mod.replacement, existing);
+      } else {
+        existing.parentNode.removeChild(existing);
+      }
+    }
+  }
+
+  __gCrWeb['findInPage']['spans'] = [];
+  __gCrWeb['findInPage']['index'] = -1;
+};
+
+/**
+ * Increments the index of the current highlighted span or, if the index is
+ * already at the end, sets it to the index of the first span in the page.
+ */
+__gCrWeb['findInPage']['incrementIndex'] = function() {
+  if (__gCrWeb['findInPage']['index'] >=
+      __gCrWeb['findInPage']['spans'].length - 1) {
+    __gCrWeb['findInPage']['index'] = 0;
+  } else {
+    __gCrWeb['findInPage']['index']++;
+  }
+};
+
+/**
+ * Switches to the next result, animating a little highlight in the process.
+ * @return {string} JSON encoded array of coordinates to scroll to, or blank if
+ *     nothing happened.
+ */
+__gCrWeb['findInPage']['goNext'] = function() {
+  if (!__gCrWeb['findInPage']['spans'] ||
+      __gCrWeb['findInPage']['spans'].length == 0) {
+    return '';
+  }
+  if (__gCrWeb['findInPage']['index'] >= 0) {
+    // Remove previous highlight.
+    __gCrWeb['findInPage'].removeSelectHighlight(
+        __gCrWeb['findInPage'].getCurrentSpan());
+  }
+  // Iterate through to the next index, but because they might not be visible,
+  // keep trying until you find one that is.  Make sure we don't loop forever by
+  // stopping on what we are currently highlighting.
+  var oldIndex = __gCrWeb['findInPage']['index'];
+  __gCrWeb['findInPage']['incrementIndex']();
+  while (!__gCrWeb['findInPage'].isVisible(
+              __gCrWeb['findInPage'].getCurrentSpan())) {
+    if (oldIndex === __gCrWeb['findInPage']['index']) {
+      // Checked all spans but didn't find anything else visible.
+      return '';
+    }
+    __gCrWeb['findInPage']['incrementIndex']();
+    if (0 === __gCrWeb['findInPage']['index'] && oldIndex < 0) {
+      // Didn't find anything visible and haven't highlighted anything yet.
+      return '';
+    }
+  }
+  // Return scroll dimensions.
+  return __gCrWeb['findInPage'].findScrollDimensions();
+};
+
+/**
+ * Decrements the index of the current highlighted span or, if the index is
+ * already at the beginning, sets it to the index of the last span in the page.
+ */
+__gCrWeb['findInPage']['decrementIndex'] = function() {
+  if (__gCrWeb['findInPage']['index'] <= 0) {
+    __gCrWeb['findInPage']['index'] =
+        __gCrWeb['findInPage']['spans'].length - 1;
+  } else {
+    __gCrWeb['findInPage']['index']--;
+  }
+};
+
+/**
+ * Switches to the previous result, animating a little highlight in the process.
+ * @return {string} JSON encoded array of coordinates to scroll to, or blank if
+ *     nothing happened.
+ */
+__gCrWeb['findInPage']['goPrev'] = function() {
+  if (!__gCrWeb['findInPage']['spans'] ||
+      __gCrWeb['findInPage']['spans'].length == 0) {
+    return '';
+  }
+  if (__gCrWeb['findInPage']['index'] >= 0) {
+    // Remove previous highlight.
+    __gCrWeb['findInPage'].removeSelectHighlight(
+        __gCrWeb['findInPage'].getCurrentSpan());
+  }
+  // Iterate through to the next index, but because they might not be visible,
+  // keep trying until you find one that is.  Make sure we don't loop forever by
+  // stopping on what we are currently highlighting.
+  var old = __gCrWeb['findInPage']['index'];
+  __gCrWeb['findInPage']['decrementIndex']();
+  while (!__gCrWeb['findInPage'].isVisible(
+             __gCrWeb['findInPage'].getCurrentSpan())) {
+    __gCrWeb['findInPage']['decrementIndex']();
+    if (old == __gCrWeb['findInPage']['index']) {
+      // Checked all spans but didn't find anything.
+      return '';
+    }
+  }
+
+  // Return scroll dimensions.
+  return __gCrWeb['findInPage'].findScrollDimensions();
+};
+
+/**
+ * Adds the special highlighting to the result at the index.
+ * @param {number} opt_index Index to replace __gCrWeb['findInPage']['index']
+ *                 with.
+ */
+__gCrWeb['findInPage'].addHighlightToIndex = function(opt_index) {
+  if (opt_index !== undefined) {
+    __gCrWeb['findInPage']['index'] = opt_index;
+  }
+  __gCrWeb['findInPage'].addSelectHighlight(
+      __gCrWeb['findInPage'].getCurrentSpan());
+};
+
+/**
+ * Updates the elements style, while saving the old style in
+ * __gCrWeb['findInPage'].savedElementStyles.
+ * @param {Element} element Element to update.
+ * @param {string} style Name of the style to update.
+ * @param {string} value New style value.
+ */
+__gCrWeb['findInPage'].updateElementStyle = function(element, style, value) {
+  if (!__gCrWeb['findInPage'].savedElementStyles[element]) {
+    __gCrWeb['findInPage'].savedElementStyles[element] = {};
+  }
+  // We need to keep the original style setting for this element, so if we've
+  // already saved a value for this style don't update it.
+  if (!__gCrWeb['findInPage'].savedElementStyles[element][style]) {
+    __gCrWeb['findInPage'].savedElementStyles[element][style] =
+        element.style[style];
+  }
+  element.style[style] = value;
+};
+
+/**
+ * Adds selected highlight style to the specified element.
+ * @param {Element} element Element to highlight.
+ */
+__gCrWeb['findInPage'].addSelectHighlight = function(element) {
+  element.className = (element.className || '') + ' findysel';
+};
+
+/**
+ * Removes selected highlight style from the specified element.
+ * @param {Element} element Element to remove highlighting from.
+ */
+__gCrWeb['findInPage'].removeSelectHighlight = function(element) {
+  element.className = (element.className || '').replace(/\sfindysel/g, '');
+
+  // Restore any styles we may have saved when adding the select highlighting.
+  var savedStyles = __gCrWeb['findInPage'].savedElementStyles[element];
+  if (savedStyles) {
+    for (var style in savedStyles) {
+      element.style[style] = savedStyles[style];
+    }
+    delete __gCrWeb['findInPage'].savedElementStyles[element];
+  }
+};
+
+/**
+ * Normalize coordinates according to the current document dimensions. Don't go
+ * too far off the screen in either direction. Try to center if possible.
+ * @param {Element} elem Element to find normalized coordinates for.
+ * @return {Array.<number>} Normalized coordinates.
+ */
+__gCrWeb['findInPage'].getNormalizedCoordinates = function(elem) {
+  var fip = __gCrWeb['findInPage'];
+  var pos = fip.findAbsolutePosition(elem);
+  var maxX =
+      Math.max(fip.getBodyWidth(), pos[0] + elem.offsetWidth);
+  var maxY =
+      Math.max(fip.getBodyHeight(), pos[1] + elem.offsetHeight);
+  // Don't go too far off the screen in either direction.  Try to center if
+  // possible.
+  var xPos = Math.max(0,
+      Math.min(maxX - window.innerWidth,
+               pos[0] - (window.innerWidth / 2)));
+  var yPos = Math.max(0,
+      Math.min(maxY - window.innerHeight,
+               pos[1] - (window.innerHeight / 2)));
+  return [xPos, yPos];
+};
+
+/**
+ * Scale coordinates according to the width of the screen, in case the screen
+ * is zoomed out.
+ * @param {Array.<number>} coordinates Coordinates to scale.
+ * @return {Array.<number>} Scaled coordinates.
+ */
+__gCrWeb['findInPage'].scaleCoordinates = function(coordinates) {
+  var scaleFactor = __gCrWeb['findInPage'].pageWidth / window.innerWidth;
+  return [coordinates[0] * scaleFactor, coordinates[1] * scaleFactor];
+};
+
+/**
+ * Finds the position of the result and scrolls to it.
+ * @param {number} opt_index Index to replace __gCrWeb['findInPage']['index']
+ *                 with.
+ * @return {string} JSON encoded array of the scroll coordinates "[x, y]".
+ */
+__gCrWeb['findInPage'].findScrollDimensions = function(opt_index) {
+  if (opt_index !== undefined) {
+    __gCrWeb['findInPage']['index'] = opt_index;
+  }
+  var elem = __gCrWeb['findInPage'].getCurrentSpan();
+  if (!elem) {
+    return '';
+  }
+  var normalized = __gCrWeb['findInPage'].getNormalizedCoordinates(elem);
+  var xPos = normalized[0];
+  var yPos = normalized[1];
+
+  // Perform the scroll.
+  //window.scrollTo(xPos, yPos);
+
+  if (xPos < window.pageXOffset ||
+      xPos >= (window.pageXOffset + window.innerWidth) ||
+      yPos < window.pageYOffset ||
+      yPos >= (window.pageYOffset + window.innerHeight)) {
+    // If it's off the screen.  Wait a bit to start the highlight animation so
+    // that scrolling can get there first.
+    window.setTimeout(__gCrWeb['findInPage'].addHighlightToIndex, 250);
+  } else {
+    __gCrWeb['findInPage'].addHighlightToIndex();
+  }
+  var scaled = __gCrWeb['findInPage'].scaleCoordinates(normalized);
+  var index = __gCrWeb['findInPage'].getCurrentSpan().visibleIndex;
+  scaled.unshift(index);
+  return __gCrWeb.stringify(scaled);
+};
+
+/**
+ * Initialize the __gCrWeb['findInPage'] module.
+ * @param {number} width Width of page.
+ * @param {number} height Height of page.
+
+ */
+__gCrWeb['findInPage']['init'] = function(width, height) {
+  if (__gCrWeb['findInPage'].hasInitialized) {
+    return;
+  }
+  __gCrWeb['findInPage'].pageWidth = width;
+  __gCrWeb['findInPage'].pageHeight = height;
+  __gCrWeb['findInPage'].frameDocs = __gCrWeb['findInPage'].frameDocuments();
+  __gCrWeb['findInPage'].enable();
+  __gCrWeb['findInPage'].hasInitialized = true;
+};
+
+/**
+ * When the GSA app detects a zoom change, we need to update our css.
+ * @param {number} width Width of page.
+ * @param {number} height Height of page.
+ */
+__gCrWeb['findInPage']['fixZoom'] = function(width, height) {
+  __gCrWeb['findInPage'].pageWidth = width;
+  __gCrWeb['findInPage'].pageHeight = height;
+  if (__gCrWeb['findInPage'].style) {
+    __gCrWeb['findInPage'].removeStyle();
+    __gCrWeb['findInPage'].addStyle();
+  }
+};
+
+/**
+ * Enable the __gCrWeb['findInPage'] module.
+ * Mainly just adds the style for the classes.
+ */
+__gCrWeb['findInPage'].enable = function() {
+  if (__gCrWeb['findInPage'].style) {
+    // Already enabled.
+    return;
+  }
+  __gCrWeb['findInPage'].addStyle();
+};
+
+/**
+ * Gets the scale ratio between the application window and the web document.
+ * @return {number} Scale.
+ */
+__gCrWeb['findInPage'].getPageScale = function() {
+  return (__gCrWeb['findInPage'].pageWidth /
+      __gCrWeb['findInPage'].getBodyWidth());
+};
+
+/**
+ * Maximum padding added to a highlighted item when selected.
+ * @type {number}
+ */
+__gCrWeb['findInPage'].MAX_HIGHLIGHT_PADDING = 10;
+
+/**
+ * Adds the appropriate style element to the page.
+ */
+__gCrWeb['findInPage'].addStyle = function() {
+  __gCrWeb['findInPage'].addDocumentStyle(document);
+  for (var i = __gCrWeb['findInPage'].frameDocs.length - 1; i >= 0; i--) {
+    var doc = __gCrWeb['findInPage'].frameDocs[i];
+    __gCrWeb['findInPage'].addDocumentStyle(doc);
+  }
+};
+
+__gCrWeb['findInPage'].addDocumentStyle = function(thisDocument) {
+  var styleContent = [];
+  function addCSSRule(name, style) {
+    styleContent.push(name, '{', style, '}');
+  };
+  var scale = __gCrWeb['findInPage'].getPageScale();
+  var zoom = (1.0 / scale);
+  var left = ((1 - scale) / 2 * 100);
+  addCSSRule('.' + __gCrWeb['findInPage'].CSS_CLASS_NAME,
+             'background-color:#ffff00 !important;' +
+             'padding:0px;margin:0px;' +
+             'overflow:visible !important;');
+  addCSSRule('.findysel',
+             'background-color:#ff9632 !important;' +
+             'padding:0px;margin:0px;' +
+             'overflow:visible !important;');
+  __gCrWeb['findInPage'].style = thisDocument.createElement('style');
+  __gCrWeb['findInPage'].style.id = __gCrWeb['findInPage'].CSS_STYLE_ID;
+  __gCrWeb['findInPage'].style.setAttribute('type', 'text/css');
+  __gCrWeb['findInPage'].style.appendChild(
+      thisDocument.createTextNode(styleContent.join('')));
+  thisDocument.body.appendChild(__gCrWeb['findInPage'].style);
+};
+
+/**
+ * Removes the style element from the page.
+ */
+__gCrWeb['findInPage'].removeStyle = function() {
+  if (__gCrWeb['findInPage'].style) {
+    __gCrWeb['findInPage'].removeDocumentStyle(document);
+    for (var i = __gCrWeb['findInPage'].frameDocs.length - 1; i >= 0; i--) {
+      var doc = __gCrWeb['findInPage'].frameDocs[i];
+      __gCrWeb['findInPage'].removeDocumentStyle(doc);
+    }
+    __gCrWeb['findInPage'].style = null;
+  }
+};
+
+__gCrWeb['findInPage'].removeDocumentStyle = function(thisDocument) {
+  var style = thisDocument.getElementById(__gCrWeb['findInPage'].CSS_STYLE_ID);
+  thisDocument.body.removeChild(style);
+};
+
+/**
+ * Disables the __gCrWeb['findInPage'] module.
+ * Basically just removes the style and class names.
+ */
+__gCrWeb['findInPage']['disable'] = function() {
+  if (__gCrWeb['findInPage'].style) {
+    __gCrWeb['findInPage'].removeStyle();
+    window.setTimeout(__gCrWeb['findInPage']['clearHighlight'], 0);
+  }
+  __gCrWeb['findInPage'].hasInitialized = false;
+};
+
+/**
+* Returns the width of the document.body.  Sometimes though the body lies to
+* try to make the page not break rails, so attempt to find those as well.
+* An example: wikipedia pages for the ipad.
+* @return {number} Width of the document body.
+*/
+__gCrWeb['findInPage'].getBodyWidth = function() {
+  var body = document.body;
+  var documentElement = document.documentElement;
+  return Math.max(body.scrollWidth, documentElement.scrollWidth,
+                  body.offsetWidth, documentElement.offsetWidth,
+                  body.clientWidth, documentElement.clientWidth);
+};
+
+/**
+ * Returns the height of the document.body.  Sometimes though the body lies to
+ * try to make the page not break rails, so attempt to find those as well.
+ * An example: wikipedia pages for the ipad.
+ * @return {number} Height of the document body.
+ */
+__gCrWeb['findInPage'].getBodyHeight = function() {
+  var body = document.body;
+  var documentElement = document.documentElement;
+  return Math.max(body.scrollHeight, documentElement.scrollHeight,
+                  body.offsetHeight, documentElement.offsetHeight,
+                  body.clientHeight, documentElement.clientHeight);
+};
+
+/**
+ * Helper function that determines if an element is visible.
+ * @param {Element} elem Element to check.
+ * @return {boolean} Whether elem is visible or not.
+ */
+__gCrWeb['findInPage'].isVisible = function(elem) {
+  if (!elem) {
+    return false;
+  }
+  var top = 0;
+  var left = 0;
+  var bottom = Infinity;
+  var right = Infinity;
+
+  var originalElement = elem;
+  var nextOffsetParent = originalElement.offsetParent;
+  var computedStyle =
+      elem.ownerDocument.defaultView.getComputedStyle(elem, null);
+
+  // We are currently handling all scrolling through the app, which means we can
+  // only scroll the window, not any scrollable containers in the DOM itself. So
+  // for now this function returns false if the element is scrolled outside the
+  // viewable area of its ancestors.
+  // TODO (jonwall): handle scrolling within the DOM.
+  var pageHeight = __gCrWeb['findInPage'].getBodyHeight();
+  var pageWidth = __gCrWeb['findInPage'].getBodyWidth();
+
+  while (elem && elem.nodeName != 'BODY') {
+    if (elem.style.display === 'none' ||
+        elem.style.visibility === 'hidden' ||
+        elem.style.opacity === 0 ||
+        computedStyle.display === 'none' ||
+        computedStyle.visibility === 'hidden' ||
+        computedStyle.opacity === 0) {
+      return false;
+    }
+
+    // For the original element and all ancestor offsetParents, trim down the
+    // visible area of the original element.
+    if (elem.isSameNode(originalElement) || elem.isSameNode(nextOffsetParent)) {
+      var visible = elem.getBoundingClientRect();
+      if (elem.style.overflow === 'hidden' &&
+          (visible.width === 0 || visible.height === 0))
+        return false;
+
+      top = Math.max(top, visible.top + window.pageYOffset);
+      bottom = Math.min(bottom, visible.bottom + window.pageYOffset);
+      left = Math.max(left, visible.left + window.pageXOffset);
+      right = Math.min(right, visible.right + window.pageXOffset);
+
+      // The element is not within the original viewport.
+      var notWithinViewport = top < 0 || left < 0;
+
+      // The element is flowing off the boundary of the page. Note this is
+      // not comparing to the size of the window, but the calculated offset
+      // size of the document body. This can happen if the element is within
+      // a scrollable container in the page.
+      var offPage = right > pageWidth || bottom > pageHeight;
+      if (notWithinViewport || offPage) {
+        return false;
+      }
+      nextOffsetParent = elem.offsetParent;
+    }
+
+    elem = elem.parentNode;
+    computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, null);
+  }
+  return true;
+};
+
+/**
+ * Helper function to find the absolute position of an element on the page.
+ * @param {Element} elem Element to check.
+ * @return {Array.<number>} [x, y] positions.
+ */
+__gCrWeb['findInPage'].findAbsolutePosition = function(elem) {
+  var boundingRect = elem.getBoundingClientRect();
+  return [boundingRect.left + window.pageXOffset,
+      boundingRect.top + window.pageYOffset];
+};
+
+/**
+ * @param {string} text Text to escape.
+ * @return {string} escaped text.
+ */
+__gCrWeb['findInPage'].escapeHTML = function(text) {
+  var unusedDiv = document.createElement('div');
+  unusedDiv.innerText = text;
+  return unusedDiv.innerHTML;
+};
+
+/**
+ * Escapes regexp special characters.
+ * @param {string} text Text to escape.
+ * @return {string} escaped text.
+ */
+__gCrWeb['findInPage'].escapeRegex = function(text) {
+  return text.replace(__gCrWeb['findInPage'].REGEX_ESCAPER, '\\$1');
+};
+
+/**
+ * Gather all iframes in the main window.
+ * @return {Array.<Document>} frames.
+ */
+__gCrWeb['findInPage'].frameDocuments = function() {
+  var windowsToSearch = [window];
+  var documents = [];
+  while (windowsToSearch.length != 0) {
+    var win = windowsToSearch.pop();
+    for (var i = win.frames.length - 1; i >= 0; i--) {
+      if (win.frames[i].document) {
+        documents.push(win.frames[i].document);
+        windowsToSearch.push(win.frames[i]);
+      }
+    }
+  }
+  return documents;
+};
diff --git a/ios/chrome/browser/infobars/infobar_container_ios.mm b/ios/chrome/browser/infobars/infobar_container_ios.mm
index bd1223b..2919449 100644
--- a/ios/chrome/browser/infobars/infobar_container_ios.mm
+++ b/ios/chrome/browser/infobars/infobar_container_ios.mm
@@ -22,6 +22,7 @@
 }
 
 InfoBarContainerIOS::~InfoBarContainerIOS() {
+  delegate_ = nullptr;
   RemoveAllInfoBarsForDestruction();
 }
 
@@ -44,7 +45,7 @@
   // Otherwise, the infobar is being replaced by another one. Do not call the
   // delegate in this case, as the delegate will be updated when the new infobar
   // is added.
-  if (infobar->total_height() == 0)
+  if (infobar->total_height() == 0 && delegate_)
     delegate_->InfoBarContainerStateChanged(false);
 
   // TODO(rohitrao, jif): [Merge 239355] Upstream InfoBarContainer deletes the
diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp
index 5334f35..f2e8122 100644
--- a/ios/chrome/ios_chrome.gyp
+++ b/ios/chrome/ios_chrome.gyp
@@ -37,6 +37,7 @@
         '../../url/url.gyp:url_lib',
         '../provider/ios_provider_chrome.gyp:ios_provider_chrome_browser',
         '../web/ios_web.gyp:ios_web',
+        'injected_js',
         'ios_chrome_resources.gyp:ios_theme_resources_gen',
       ],
       'link_settings': {
@@ -82,6 +83,12 @@
         'browser/dom_distiller/dom_distiller_service_factory.h',
         'browser/experimental_flags.h',
         'browser/experimental_flags.mm',
+        'browser/find_in_page/find_in_page_controller.h',
+        'browser/find_in_page/find_in_page_controller.mm',
+        'browser/find_in_page/find_in_page_model.h',
+        'browser/find_in_page/find_in_page_model.mm',
+        'browser/find_in_page/js_findinpage_manager.h',
+        'browser/find_in_page/js_findinpage_manager.mm',
         'browser/infobars/confirm_infobar_controller.h',
         'browser/infobars/confirm_infobar_controller.mm',
         'browser/infobars/infobar.h',
@@ -161,5 +168,20 @@
         'browser/web_resource/ios_web_resource_service.h',
       ],
     },
+    {
+      'target_name': 'injected_js',
+      'type': 'none',
+      'sources': [
+        'browser/find_in_page/resources/find_in_page.js',
+      ],
+      'includes': [
+        '../../ios/web/js_compile.gypi',
+      ],
+      'link_settings': {
+        'mac_bundle_resources': [
+          '<(SHARED_INTERMEDIATE_DIR)/find_in_page.js',
+        ],
+      },
+    },
   ],
 }
diff --git a/ios/third_party/blink/LICENSE b/ios/third_party/blink/LICENSE
new file mode 100644
index 0000000..cc3ab97
--- /dev/null
+++ b/ios/third_party/blink/LICENSE
@@ -0,0 +1,24 @@
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/
+Copyright (C) 2010 Google, Inc. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+ *
+THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ios/third_party/blink/OWNERS b/ios/third_party/blink/OWNERS
new file mode 100644
index 0000000..1b3348e7
--- /dev/null
+++ b/ios/third_party/blink/OWNERS
@@ -0,0 +1,2 @@
+rohitrao@chromium.org
+stuartmorgan@chromium.org
diff --git a/ios/third_party/blink/README.chromium b/ios/third_party/blink/README.chromium
new file mode 100644
index 0000000..89a3454
--- /dev/null
+++ b/ios/third_party/blink/README.chromium
@@ -0,0 +1,14 @@
+Name: blink HTMLTokenizer
+Short Name: blink
+URL: http://www.chromium.org/blink
+Version: commit 2e924d852a814dee5efde8deb412a70a4d90c5c7
+License: BSD License
+Security Critical: yes
+
+Description:
+The HTMLTokenizer and associated classes from blink handle tokenizing HTML
+content into runtime usable data structures.
+
+Local Modifications:
+The blink code was used as a starting point and heavily modified to remove
+unnecessary code, dependancies on WTF, and dependencies on GPL code.
diff --git a/ios/third_party/blink/blink_html_tokenizer.gyp b/ios/third_party/blink/blink_html_tokenizer.gyp
new file mode 100644
index 0000000..d6fbcf2
--- /dev/null
+++ b/ios/third_party/blink/blink_html_tokenizer.gyp
@@ -0,0 +1,27 @@
+# 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.
+{
+  'targets': [
+    {
+      'target_name': 'blink_html_tokenizer',
+      'type': 'static_library',
+      'dependencies': [
+        '../../../base/base.gyp:base',
+      ],
+      'include_dirs': [
+        '../../..',
+      ],
+      'sources': [
+        'src/html_character_provider.h',
+        'src/html_input_stream_preprocessor.h',
+        'src/html_markup_tokenizer_inlines.h',
+        'src/html_token.h',
+        'src/html_token.mm',
+        'src/html_tokenizer.h',
+        'src/html_tokenizer.mm',
+        'src/html_tokenizer_adapter.h',
+      ],
+    },
+  ],
+}
diff --git a/ios/third_party/blink/src/html_character_provider.h b/ios/third_party/blink/src/html_character_provider.h
new file mode 100644
index 0000000..f6dd427
--- /dev/null
+++ b/ios/third_party/blink/src/html_character_provider.h
@@ -0,0 +1,166 @@
+// 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 IOS_THIRD_PARTY_BLINK_SRC_HTML_CHARACTER_PROVIDER_H_
+#define IOS_THIRD_PARTY_BLINK_SRC_HTML_CHARACTER_PROVIDER_H_
+
+#include "ios/third_party/blink/src/html_tokenizer_adapter.h"
+
+namespace WebCore {
+
+const LChar kEndOfFileMarker = 0;
+
+// CharacterProvider provides input characters to WebCore::HTMLTokenizer.
+// It replaces WebCore::SegmentedString (which sits ontop of WTF::String).
+class CharacterProvider {
+    WTF_MAKE_NONCOPYABLE(CharacterProvider);
+
+public:
+    CharacterProvider()
+        : _totalBytes(0)
+        , _remainingBytes(0)
+        , _singleBytePtr(nullptr)
+        , _doubleBytePtr(nullptr)
+        , _littleEndian(false)
+    {
+    }
+
+    void setContents(const LChar* str, size_t numberOfBytes)
+    {
+        _totalBytes = numberOfBytes;
+        _remainingBytes = numberOfBytes;
+        _singleBytePtr = str;
+        _doubleBytePtr = nullptr;
+        _littleEndian = false;
+    }
+
+    void setContents(const UChar* str, size_t numberOfBytes)
+    {
+        _totalBytes = numberOfBytes;
+        _remainingBytes = numberOfBytes;
+        _singleBytePtr = nullptr;
+        _doubleBytePtr = str;
+        _littleEndian = false;
+    }
+
+    void clear()
+    {
+        _totalBytes = 0;
+        _remainingBytes = 0;
+        _singleBytePtr = nullptr;
+        _doubleBytePtr = nullptr;
+        _littleEndian = false;
+    }
+
+    bool startsWith(const LChar* str,
+                    size_t byteCount,
+                    bool caseInsensitive = false) const
+    {
+        if (!str || byteCount > _remainingBytes)
+            return false;
+
+        for (size_t index = 0; index < byteCount; ++index) {
+            UChar lhs = characterAtIndex(index);
+            UChar rhs = str[index];
+
+            if (caseInsensitive) {
+                if (isASCIIUpper(lhs))
+                    lhs = toLowerCase(lhs);
+
+                if (isASCIIUpper(rhs))
+                    rhs = toLowerCase(rhs);
+            }
+
+            if (lhs != rhs)
+                return false;
+        }
+
+        return true;
+    }
+
+    inline UChar currentCharacter() const
+    {
+        return characterAtIndex(0);
+    }
+
+    inline UChar nextCharacter()
+    {
+        advanceBytePointer();
+        return characterAtIndex(0);
+    }
+
+    inline void next()
+    {
+        advanceBytePointer();
+    }
+
+    inline bool isEmpty() const
+    {
+        return !_remainingBytes;
+    }
+
+    inline size_t remainingBytes() const
+    {
+        return _remainingBytes;
+    }
+
+    inline size_t bytesProvided() const
+    {
+        return _totalBytes - _remainingBytes;
+    }
+
+    inline void setLittleEndian()
+    {
+        _littleEndian = true;
+    }
+
+private:
+    void advanceBytePointer()
+    {
+        --_remainingBytes;
+        if (!_remainingBytes)
+            return;
+
+        if (_singleBytePtr)
+            ++_singleBytePtr;
+        else {
+            DCHECK(_doubleBytePtr);
+            ++_doubleBytePtr;
+        }
+    }
+
+    UChar characterAtIndex(size_t index) const
+    {
+        if (!_remainingBytes) {
+            // There is a quirk in the blink implementation wherein the empty state
+            // is not set on the source until next() has been called when
+            // _remainingBytes is zero. In this case, return kEndOfFileMarker.
+            return kEndOfFileMarker;
+        }
+
+        ASSERT(_singleBytePtr || _doubleBytePtr);
+
+        UChar character = kEndOfFileMarker;
+        if (_singleBytePtr)
+            character = _singleBytePtr[index];
+        else
+            character = _doubleBytePtr[index];
+
+        if (_littleEndian)
+            character = ByteSwap(character);
+
+        return character;
+    }
+
+private:
+    size_t _totalBytes;
+    size_t _remainingBytes;
+    const LChar* _singleBytePtr;
+    const UChar* _doubleBytePtr;
+    bool _littleEndian;
+};
+
+}
+
+#endif // IOS_THIRD_PARTY_BLINK_SRC_HTML_CHARACTER_PROVIDER_H_
diff --git a/ios/third_party/blink/src/html_input_stream_preprocessor.h b/ios/third_party/blink/src/html_input_stream_preprocessor.h
new file mode 100644
index 0000000..d46ed643
--- /dev/null
+++ b/ios/third_party/blink/src/html_input_stream_preprocessor.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/
+ * Copyright (C) 2013 Google, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef InputStreamPreprocessor_h
+#define InputStreamPreprocessor_h
+
+#include "html_character_provider.h"
+
+namespace WebCore {
+
+// http://www.whatwg.org/specs/web-apps/current-work/#preprocessing-the-input-stream
+template <typename Tokenizer>
+class InputStreamPreprocessor {
+    WTF_MAKE_NONCOPYABLE(InputStreamPreprocessor);
+public:
+    InputStreamPreprocessor(Tokenizer* tokenizer)
+        : m_tokenizer(tokenizer)
+    {
+        reset();
+    }
+
+    ALWAYS_INLINE UChar nextInputCharacter() const { return m_nextInputCharacter; }
+
+    // Returns whether we succeeded in peeking at the next character.
+    // The only way we can fail to peek is if there are no more
+    // characters in |source| (after collapsing \r\n, etc).
+    ALWAYS_INLINE bool peek(CharacterProvider& source)
+    {
+        m_nextInputCharacter = source.currentCharacter();
+
+        // Every branch in this function is expensive, so we have a
+        // fast-reject branch for characters that don't require special
+        // handling. Please run the parser benchmark whenever you touch
+        // this function. It's very hot.
+        static const UChar specialCharacterMask = '\n' | '\r' | '\0';
+        if (m_nextInputCharacter & ~specialCharacterMask) {
+            m_skipNextNewLine = false;
+            return true;
+        }
+        return processNextInputCharacter(source);
+    }
+
+    // Returns whether there are more characters in |source| after advancing.
+    ALWAYS_INLINE bool advance(CharacterProvider& source)
+    {
+        source.next();
+        if (source.isEmpty())
+            return false;
+        return peek(source);
+    }
+
+    void reset(bool skipNextNewLine = false)
+    {
+        m_nextInputCharacter = '\0';
+        m_skipNextNewLine = skipNextNewLine;
+    }
+
+private:
+    bool processNextInputCharacter(CharacterProvider& source)
+    {
+    ProcessAgain:
+        ASSERT(m_nextInputCharacter == source.currentCharacter());
+
+        if (m_nextInputCharacter == '\n' && m_skipNextNewLine) {
+            m_skipNextNewLine = false;
+            source.next();
+            if (source.isEmpty())
+                return false;
+            m_nextInputCharacter = source.currentCharacter();
+        }
+        if (m_nextInputCharacter == '\r') {
+            m_nextInputCharacter = '\n';
+            m_skipNextNewLine = true;
+        } else {
+            m_skipNextNewLine = false;
+            // FIXME: The spec indicates that the surrogate pair range as well as
+            // a number of specific character values are parse errors and should be replaced
+            // by the replacement character. We suspect this is a problem with the spec as doing
+            // that filtering breaks surrogate pair handling and causes us not to match Minefield.
+            if (m_nextInputCharacter == '\0' && !shouldTreatNullAsEndOfFileMarker(source)) {
+                if (m_tokenizer->shouldSkipNullCharacters()) {
+                    source.next();
+                    if (source.isEmpty())
+                        return false;
+                    m_nextInputCharacter = source.currentCharacter();
+                    goto ProcessAgain;
+                }
+                m_nextInputCharacter = 0xFFFD;
+            }
+        }
+        return true;
+    }
+
+    bool shouldTreatNullAsEndOfFileMarker(CharacterProvider& source) const
+    {
+        return source.remainingBytes() == 1;
+    }
+
+    Tokenizer* m_tokenizer;
+
+    // http://www.whatwg.org/specs/web-apps/current-work/#next-input-character
+    UChar m_nextInputCharacter;
+    bool m_skipNextNewLine;
+};
+
+}
+
+#endif // InputStreamPreprocessor_h
+
diff --git a/ios/third_party/blink/src/html_markup_tokenizer_inlines.h b/ios/third_party/blink/src/html_markup_tokenizer_inlines.h
new file mode 100644
index 0000000..f510fd1
--- /dev/null
+++ b/ios/third_party/blink/src/html_markup_tokenizer_inlines.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/
+ * Copyright (C) 2010 Google, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MarkupTokenizerInlines_h
+#define MarkupTokenizerInlines_h
+
+#include "html_character_provider.h"
+
+namespace WebCore {
+
+template <typename CharType>
+inline bool isTokenizerWhitespace(CharType cc)
+{
+    return cc == ' ' || cc == '\x0A' || cc == '\x09' || cc == '\x0C';
+}
+
+void advanceStringAndASSERTIgnoringCase(CharacterProvider& source, const LChar* expectedCharacters)
+{
+    while (*expectedCharacters) {
+#ifndef NDEBUG
+        ASSERT(isASCIILower(*expectedCharacters));
+
+        UChar currentCharacter = source.currentCharacter();
+        if (isASCIIUpper(currentCharacter))
+            currentCharacter = toLowerCase(currentCharacter);
+
+        ASSERT(currentCharacter == *expectedCharacters);
+#endif
+
+        ++expectedCharacters;
+        source.next();
+    }
+}
+
+inline void advanceAndASSERT(CharacterProvider& source, UChar expectedCharacter)
+{
+    ASSERT(source.currentCharacter() == expectedCharacter);
+    source.next();
+}
+
+#define BEGIN_STATE(prefix, stateName) case prefix::stateName: stateName:
+#define END_STATE() ASSERT_NOT_REACHED(); break;
+
+// We use this macro when the HTML5 spec says "reconsume the current input
+// character in the <mumble> state."
+#define RECONSUME_IN(prefix, stateName)                                    \
+    do {                                                                   \
+        m_state = prefix::stateName;                                       \
+        goto stateName;                                                    \
+    } while (false)
+
+// We use this macro when the HTML5 spec says "consume the next input
+// character ... and switch to the <mumble> state."
+#define ADVANCE_TO(prefix, stateName)                                      \
+    do {                                                                   \
+        m_state = prefix::stateName;                                       \
+        if (!m_inputStreamPreprocessor.advance(source))                    \
+            return haveBufferedCharacterToken();                           \
+        cc = m_inputStreamPreprocessor.nextInputCharacter();               \
+        goto stateName;                                                    \
+    } while (false)
+
+// Sometimes there's more complicated logic in the spec that separates when
+// we consume the next input character and when we switch to a particular
+// state. We handle those cases by advancing the source directly and using
+// this macro to switch to the indicated state.
+#define SWITCH_TO(prefix, stateName)                                       \
+    do {                                                                   \
+        m_state = prefix::stateName;                                       \
+        if (source.isEmpty() || !m_inputStreamPreprocessor.peek(source))   \
+            return haveBufferedCharacterToken();                           \
+        cc = m_inputStreamPreprocessor.nextInputCharacter();               \
+        goto stateName;                                                    \
+    } while (false)
+
+}
+
+#endif // MarkupTokenizerInlines_h
diff --git a/ios/third_party/blink/src/html_token.h b/ios/third_party/blink/src/html_token.h
new file mode 100644
index 0000000..103f7b7b
--- /dev/null
+++ b/ios/third_party/blink/src/html_token.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2013 Google, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HTMLToken_h
+#define HTMLToken_h
+
+#include <vector>
+
+#include "ios/third_party/blink/src/html_tokenizer_adapter.h"
+
+namespace WebCore {
+
+class HTMLToken {
+    WTF_MAKE_NONCOPYABLE(HTMLToken);
+
+public:
+    enum Type {
+        Uninitialized,
+        DOCTYPE,
+        StartTag,
+        EndTag,
+        Comment,
+        Character,
+        EndOfFile,
+    };
+
+    HTMLToken();
+    ~HTMLToken();
+
+    void clear()
+    {
+        m_type = Uninitialized;
+        m_data.clear();
+    }
+
+    Type type() const { return m_type; }
+
+    void makeEndOfFile()
+    {
+        ASSERT(m_type == Uninitialized);
+        m_type = EndOfFile;
+    }
+
+    void appendToName(LChar character)
+    {
+        ASSERT(m_type == StartTag || m_type == EndTag || m_type == DOCTYPE);
+        ASSERT(character);
+        m_data.push_back(character);
+    }
+
+    bool nameEquals(const LChar* name, size_t length)
+    {
+        ASSERT(m_type == StartTag || m_type == EndTag || m_type == DOCTYPE);
+        if (length != m_data.size())
+            return false;
+
+        for (size_t index = 0; index < length; ++index) {
+            if (m_data.at(index) != name[index])
+                return false;
+        }
+
+        return true;
+    }
+
+    /* DOCTYPE Tokens */
+
+    void beginDOCTYPE()
+    {
+        ASSERT(m_type == Uninitialized);
+        m_type = DOCTYPE;
+    }
+
+    /* Start/End Tag Tokens */
+
+    void beginStartTag(LChar character)
+    {
+        ASSERT(character);
+        ASSERT(m_type == Uninitialized);
+        m_type = StartTag;
+
+        m_data.push_back(character);
+    }
+
+    void beginEndTag(LChar character)
+    {
+        ASSERT(m_type == Uninitialized);
+        m_type = EndTag;
+
+        m_data.push_back(character);
+    }
+
+    /* Character Tokens */
+
+    // Starting a character token works slightly differently than starting
+    // other types of tokens because we want to save a per-character branch.
+    void ensureIsCharacterToken()
+    {
+        ASSERT(m_type == Uninitialized || m_type == Character);
+        m_type = Character;
+    }
+
+    /* Comment Tokens */
+
+    void beginComment()
+    {
+        ASSERT(m_type == Uninitialized);
+        m_type = Comment;
+    }
+
+private:
+    Type m_type;
+    std::vector<LChar> m_data;
+};
+}
+
+#endif
diff --git a/ios/third_party/blink/src/html_token.mm b/ios/third_party/blink/src/html_token.mm
new file mode 100644
index 0000000..f05f24e
--- /dev/null
+++ b/ios/third_party/blink/src/html_token.mm
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 Google, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ios/third_party/blink/src/html_token.h"
+
+namespace {
+size_t kDefaultDataSize = 256;
+}
+
+namespace WebCore {
+
+HTMLToken::HTMLToken()
+    : m_type(Uninitialized)
+{
+  m_data.reserve(kDefaultDataSize);
+}
+
+HTMLToken::~HTMLToken()
+{
+}
+}
diff --git a/ios/third_party/blink/src/html_tokenizer.h b/ios/third_party/blink/src/html_tokenizer.h
new file mode 100644
index 0000000..70623ae
--- /dev/null
+++ b/ios/third_party/blink/src/html_tokenizer.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2010 Google, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HTMLTokenizer_h
+#define HTMLTokenizer_h
+
+#include "ios/third_party/blink/src/html_input_stream_preprocessor.h"
+#include "ios/third_party/blink/src/html_token.h"
+
+namespace WebCore {
+
+class HTMLTokenizer {
+    WTF_MAKE_NONCOPYABLE(HTMLTokenizer);
+
+public:
+    HTMLTokenizer();
+    ~HTMLTokenizer();
+
+    void reset();
+
+    enum State {
+        DataState,
+        TagOpenState,
+        EndTagOpenState,
+        TagNameState,
+        BeforeAttributeNameState,
+        AttributeNameState,
+        AfterAttributeNameState,
+        BeforeAttributeValueState,
+        AttributeValueDoubleQuotedState,
+        AttributeValueSingleQuotedState,
+        AttributeValueUnquotedState,
+        AfterAttributeValueQuotedState,
+        SelfClosingStartTagState,
+        BogusCommentState,
+        // The ContinueBogusCommentState is not in the HTML5 spec, but we use
+        // it internally to keep track of whether we've started the bogus
+        // comment token yet.
+        ContinueBogusCommentState,
+        MarkupDeclarationOpenState,
+        CommentStartState,
+        CommentStartDashState,
+        CommentState,
+        CommentEndDashState,
+        CommentEndState,
+        CommentEndBangState,
+        DOCTYPEState,
+        BeforeDOCTYPENameState,
+        DOCTYPENameState,
+        AfterDOCTYPENameState,
+        AfterDOCTYPEPublicKeywordState,
+        BeforeDOCTYPEPublicIdentifierState,
+        DOCTYPEPublicIdentifierDoubleQuotedState,
+        DOCTYPEPublicIdentifierSingleQuotedState,
+        AfterDOCTYPEPublicIdentifierState,
+        BetweenDOCTYPEPublicAndSystemIdentifiersState,
+        AfterDOCTYPESystemKeywordState,
+        BeforeDOCTYPESystemIdentifierState,
+        DOCTYPESystemIdentifierDoubleQuotedState,
+        DOCTYPESystemIdentifierSingleQuotedState,
+        AfterDOCTYPESystemIdentifierState,
+        BogusDOCTYPEState,
+        CDATASectionState,
+        // These CDATA states are not in the HTML5 spec, but we use them internally.
+        CDATASectionRightSquareBracketState,
+        CDATASectionDoubleRightSquareBracketState,
+    };
+
+    // This function returns true if it emits a token. Otherwise, callers
+    // must provide the same (in progress) token on the next call (unless
+    // they call reset() first).
+    bool nextToken(CharacterProvider&, HTMLToken&);
+
+    State state() const { return m_state; }
+    void setState(State state) { m_state = state; }
+
+    inline bool shouldSkipNullCharacters() const
+    {
+        return m_state == HTMLTokenizer::DataState;
+    }
+
+private:
+    inline void parseError();
+
+    inline bool emitAndResumeIn(CharacterProvider& source, State state)
+    {
+        ASSERT(m_token->type() != HTMLToken::Uninitialized);
+        m_state = state;
+        source.next();
+        return true;
+    }
+
+    inline bool emitAndReconsumeIn(CharacterProvider&, State state)
+    {
+        ASSERT(m_token->type() != HTMLToken::Uninitialized);
+        m_state = state;
+        return true;
+    }
+
+    inline bool emitEndOfFile(CharacterProvider& source)
+    {
+        if (haveBufferedCharacterToken())
+            return true;
+        m_state = HTMLTokenizer::DataState;
+        source.next();
+        m_token->clear();
+        m_token->makeEndOfFile();
+        return true;
+    }
+
+    // Return whether we need to emit a character token before dealing with
+    // the buffered end tag.
+    inline bool flushBufferedEndTag(CharacterProvider&);
+
+    inline bool haveBufferedCharacterToken()
+    {
+        return m_token->type() == HTMLToken::Character;
+    }
+
+    State m_state;
+
+    // m_token is owned by the caller. If nextToken is not on the stack,
+    // this member might be pointing to unallocated memory.
+    HTMLToken* m_token;
+
+    // http://www.whatwg.org/specs/web-apps/current-work/#additional-allowed-character
+    LChar m_additionalAllowedCharacter;
+
+    // http://www.whatwg.org/specs/web-apps/current-work/#preprocessing-the-input-stream
+    InputStreamPreprocessor<HTMLTokenizer> m_inputStreamPreprocessor;
+};
+}
+
+#endif
diff --git a/ios/third_party/blink/src/html_tokenizer.mm b/ios/third_party/blink/src/html_tokenizer.mm
new file mode 100644
index 0000000..709f4d13
--- /dev/null
+++ b/ios/third_party/blink/src/html_tokenizer.mm
@@ -0,0 +1,787 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/
+ * Copyright (C) 2010 Google, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ios/third_party/blink/src/html_tokenizer.h"
+
+#include "html_markup_tokenizer_inlines.h"
+
+namespace WebCore {
+
+#define HTML_BEGIN_STATE(stateName) BEGIN_STATE(HTMLTokenizer, stateName)
+#define HTML_RECONSUME_IN(stateName) RECONSUME_IN(HTMLTokenizer, stateName)
+#define HTML_ADVANCE_TO(stateName) ADVANCE_TO(HTMLTokenizer, stateName)
+#define HTML_SWITCH_TO(stateName) SWITCH_TO(HTMLTokenizer, stateName)
+
+HTMLTokenizer::HTMLTokenizer()
+    : m_state(HTMLTokenizer::DataState)
+    , m_token(nullptr)
+    , m_additionalAllowedCharacter('\0')
+    , m_inputStreamPreprocessor(this)
+{
+}
+
+HTMLTokenizer::~HTMLTokenizer()
+{
+}
+
+void HTMLTokenizer::reset()
+{
+    m_state = HTMLTokenizer::DataState;
+    m_token = 0;
+    m_additionalAllowedCharacter = '\0';
+}
+
+bool HTMLTokenizer::flushBufferedEndTag(CharacterProvider& source)
+{
+    ASSERT(m_token->type() == HTMLToken::Character || m_token->type() == HTMLToken::Uninitialized);
+    source.next();
+    if (m_token->type() == HTMLToken::Character)
+        return true;
+
+    return false;
+}
+
+#define FLUSH_AND_ADVANCE_TO(stateName)                                    \
+    do {                                                                   \
+        m_state = HTMLTokenizer::stateName;                           \
+        if (flushBufferedEndTag(source))                                   \
+            return true;                                                   \
+        if (source.isEmpty()                                               \
+            || !m_inputStreamPreprocessor.peek(source))                    \
+            return haveBufferedCharacterToken();                           \
+        cc = m_inputStreamPreprocessor.nextInputCharacter();               \
+        goto stateName;                                                    \
+    } while (false)
+
+bool HTMLTokenizer::nextToken(CharacterProvider& source, HTMLToken& token)
+{
+    // If we have a token in progress, then we're supposed to be called back
+    // with the same token so we can finish it.
+    ASSERT(!m_token || m_token == &token || token.type() == HTMLToken::Uninitialized);
+    m_token = &token;
+
+    if (source.isEmpty() || !m_inputStreamPreprocessor.peek(source))
+        return haveBufferedCharacterToken();
+    UChar cc = m_inputStreamPreprocessor.nextInputCharacter();
+
+    // Source: http://www.whatwg.org/specs/web-apps/current-work/#tokenisation0
+    switch (m_state) {
+    HTML_BEGIN_STATE(DataState) {
+        if (cc == '<') {
+            if (m_token->type() == HTMLToken::Character) {
+                // We have a bunch of character tokens queued up that we
+                // are emitting lazily here.
+                return true;
+            }
+            HTML_ADVANCE_TO(TagOpenState);
+        } else if (cc == kEndOfFileMarker)
+            return emitEndOfFile(source);
+        else {
+            m_token->ensureIsCharacterToken();
+            HTML_ADVANCE_TO(DataState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(TagOpenState) {
+        if (cc == '!')
+            HTML_ADVANCE_TO(MarkupDeclarationOpenState);
+        else if (cc == '/')
+            HTML_ADVANCE_TO(EndTagOpenState);
+        else if (isASCIIUpper(cc)) {
+            m_token->beginStartTag(toLowerCase(cc));
+            HTML_ADVANCE_TO(TagNameState);
+        } else if (isASCIILower(cc)) {
+            m_token->beginStartTag(cc);
+            HTML_ADVANCE_TO(TagNameState);
+        } else if (cc == '?') {
+            parseError();
+            // The spec consumes the current character before switching
+            // to the bogus comment state, but it's easier to implement
+            // if we reconsume the current character.
+            HTML_RECONSUME_IN(BogusCommentState);
+        } else {
+            parseError();
+            m_token->ensureIsCharacterToken();
+            HTML_RECONSUME_IN(DataState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(EndTagOpenState) {
+        if (isASCIIUpper(cc)) {
+            m_token->beginEndTag(static_cast<LChar>(toLowerCase(cc)));
+            HTML_ADVANCE_TO(TagNameState);
+        } else if (isASCIILower(cc)) {
+            m_token->beginEndTag(static_cast<LChar>(cc));
+            HTML_ADVANCE_TO(TagNameState);
+        } else if (cc == '>') {
+            parseError();
+            HTML_ADVANCE_TO(DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            m_token->ensureIsCharacterToken();
+            HTML_RECONSUME_IN(DataState);
+        } else {
+            parseError();
+            HTML_RECONSUME_IN(BogusCommentState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(TagNameState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BeforeAttributeNameState);
+        else if (cc == '/')
+            HTML_ADVANCE_TO(SelfClosingStartTagState);
+        else if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (isASCIIUpper(cc)) {
+            m_token->appendToName(toLowerCase(cc));
+            HTML_ADVANCE_TO(TagNameState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            HTML_RECONSUME_IN(DataState);
+        } else {
+            m_token->appendToName(cc);
+            HTML_ADVANCE_TO(TagNameState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(BeforeAttributeNameState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BeforeAttributeNameState);
+        else if (cc == '/')
+            HTML_ADVANCE_TO(SelfClosingStartTagState);
+        else if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (isASCIIUpper(cc)) {
+            HTML_ADVANCE_TO(AttributeNameState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            HTML_RECONSUME_IN(DataState);
+        } else {
+            if (cc == '"' || cc == '\'' || cc == '<' || cc == '=')
+                parseError();
+            HTML_ADVANCE_TO(AttributeNameState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(AttributeNameState) {
+        if (isTokenizerWhitespace(cc)) {
+            HTML_ADVANCE_TO(AfterAttributeNameState);
+        } else if (cc == '/') {
+            HTML_ADVANCE_TO(SelfClosingStartTagState);
+        } else if (cc == '=') {
+            HTML_ADVANCE_TO(BeforeAttributeValueState);
+        } else if (cc == '>') {
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (isASCIIUpper(cc)) {
+            HTML_ADVANCE_TO(AttributeNameState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            HTML_RECONSUME_IN(DataState);
+        } else {
+            if (cc == '"' || cc == '\'' || cc == '<' || cc == '=')
+                parseError();
+            HTML_ADVANCE_TO(AttributeNameState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(AfterAttributeNameState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(AfterAttributeNameState);
+        else if (cc == '/')
+            HTML_ADVANCE_TO(SelfClosingStartTagState);
+        else if (cc == '=')
+            HTML_ADVANCE_TO(BeforeAttributeValueState);
+        else if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (isASCIIUpper(cc)) {
+            HTML_ADVANCE_TO(AttributeNameState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            HTML_RECONSUME_IN(DataState);
+        } else {
+            if (cc == '"' || cc == '\'' || cc == '<')
+                parseError();
+            HTML_ADVANCE_TO(AttributeNameState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(BeforeAttributeValueState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BeforeAttributeValueState);
+        else if (cc == '"') {
+            HTML_ADVANCE_TO(AttributeValueDoubleQuotedState);
+        } else if (cc == '&') {
+            HTML_RECONSUME_IN(AttributeValueUnquotedState);
+        } else if (cc == '\'') {
+            HTML_ADVANCE_TO(AttributeValueSingleQuotedState);
+        } else if (cc == '>') {
+            parseError();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            HTML_RECONSUME_IN(DataState);
+        } else {
+            if (cc == '<' || cc == '=' || cc == '`')
+                parseError();
+            HTML_ADVANCE_TO(AttributeValueUnquotedState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(AttributeValueDoubleQuotedState) {
+        if (cc == '"') {
+            HTML_ADVANCE_TO(AfterAttributeValueQuotedState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            HTML_RECONSUME_IN(DataState);
+        } else {
+            HTML_ADVANCE_TO(AttributeValueDoubleQuotedState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(AttributeValueSingleQuotedState) {
+        if (cc == '\'') {
+            HTML_ADVANCE_TO(AfterAttributeValueQuotedState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            HTML_RECONSUME_IN(DataState);
+        } else {
+            HTML_ADVANCE_TO(AttributeValueSingleQuotedState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(AttributeValueUnquotedState) {
+        if (isTokenizerWhitespace(cc)) {
+            HTML_ADVANCE_TO(BeforeAttributeNameState);
+        } else if (cc == '>') {
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            HTML_RECONSUME_IN(DataState);
+        } else {
+            if (cc == '"' || cc == '\'' || cc == '<' || cc == '=' || cc == '`')
+                parseError();
+            HTML_ADVANCE_TO(AttributeValueUnquotedState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(AfterAttributeValueQuotedState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BeforeAttributeNameState);
+        else if (cc == '/')
+            HTML_ADVANCE_TO(SelfClosingStartTagState);
+        else if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (cc == kEndOfFileMarker) {
+            parseError();
+            HTML_RECONSUME_IN(DataState);
+        } else {
+            parseError();
+            HTML_RECONSUME_IN(BeforeAttributeNameState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(SelfClosingStartTagState) {
+        if (cc == '>') {
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            HTML_RECONSUME_IN(DataState);
+        } else {
+            parseError();
+            HTML_RECONSUME_IN(BeforeAttributeNameState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(BogusCommentState) {
+        m_token->beginComment();
+        HTML_RECONSUME_IN(ContinueBogusCommentState);
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(ContinueBogusCommentState) {
+        if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (cc == kEndOfFileMarker)
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        else {
+            HTML_ADVANCE_TO(ContinueBogusCommentState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(MarkupDeclarationOpenState) {
+        DEFINE_STATIC_LOCAL_STRING(dashDashString, "--");
+        DEFINE_STATIC_LOCAL_STRING(doctypeString, "doctype");
+        if (cc == '-') {
+            if (source.startsWith(dashDashString, dashDashStringLength)) {
+                advanceAndASSERT(source, '-');
+                advanceAndASSERT(source, '-');
+                m_token->beginComment();
+                HTML_SWITCH_TO(CommentStartState);
+            } else if (source.remainingBytes() < dashDashStringLength)
+                return haveBufferedCharacterToken();
+        } else if (cc == 'D' || cc == 'd') {
+            if (source.startsWith(doctypeString, doctypeStringLength, true)) {
+                advanceStringAndASSERTIgnoringCase(source, doctypeString);
+                HTML_SWITCH_TO(DOCTYPEState);
+            } else if (source.remainingBytes() < doctypeStringLength)
+                return haveBufferedCharacterToken();
+        }
+        parseError();
+        HTML_RECONSUME_IN(BogusCommentState);
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(CommentStartState) {
+        if (cc == '-')
+            HTML_ADVANCE_TO(CommentStartDashState);
+        else if (cc == '>') {
+            parseError();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            HTML_ADVANCE_TO(CommentState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(CommentStartDashState) {
+        if (cc == '-')
+            HTML_ADVANCE_TO(CommentEndState);
+        else if (cc == '>') {
+            parseError();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            HTML_ADVANCE_TO(CommentState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(CommentState) {
+        if (cc == '-')
+            HTML_ADVANCE_TO(CommentEndDashState);
+        else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            HTML_ADVANCE_TO(CommentState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(CommentEndDashState) {
+        if (cc == '-')
+            HTML_ADVANCE_TO(CommentEndState);
+        else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            HTML_ADVANCE_TO(CommentState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(CommentEndState) {
+        if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (cc == '!') {
+            parseError();
+            HTML_ADVANCE_TO(CommentEndBangState);
+        } else if (cc == '-') {
+            parseError();
+            HTML_ADVANCE_TO(CommentEndState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            parseError();
+            HTML_ADVANCE_TO(CommentState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(CommentEndBangState) {
+        if (cc == '-') {
+            HTML_ADVANCE_TO(CommentEndDashState);
+        } else if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            HTML_ADVANCE_TO(CommentState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(DOCTYPEState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BeforeDOCTYPENameState);
+        else if (cc == kEndOfFileMarker) {
+            parseError();
+            m_token->beginDOCTYPE();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            parseError();
+            HTML_RECONSUME_IN(BeforeDOCTYPENameState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(BeforeDOCTYPENameState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BeforeDOCTYPENameState);
+        else if (cc == '>') {
+            parseError();
+            m_token->beginDOCTYPE();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            m_token->beginDOCTYPE();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            m_token->beginDOCTYPE();
+            HTML_ADVANCE_TO(DOCTYPENameState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(DOCTYPENameState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(AfterDOCTYPENameState);
+        else if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            HTML_ADVANCE_TO(DOCTYPENameState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(AfterDOCTYPENameState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(AfterDOCTYPENameState);
+        if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            DEFINE_STATIC_LOCAL_STRING(publicString, "public");
+            DEFINE_STATIC_LOCAL_STRING(systemString, "system");
+            if (cc == 'P' || cc == 'p') {
+                if (source.startsWith(publicString, publicStringLength, true)) {
+                    advanceStringAndASSERTIgnoringCase(source, publicString);
+                    HTML_SWITCH_TO(AfterDOCTYPEPublicKeywordState);
+                } else if (source.remainingBytes() < publicStringLength)
+                    return haveBufferedCharacterToken();
+            } else if (cc == 'S' || cc == 's') {
+                if (source.startsWith(systemString, systemStringLength, true)) {
+                    advanceStringAndASSERTIgnoringCase(source, systemString);
+                    HTML_SWITCH_TO(AfterDOCTYPESystemKeywordState);
+                } else if (source.remainingBytes() < systemStringLength)
+                    return haveBufferedCharacterToken();
+            }
+            parseError();
+            HTML_ADVANCE_TO(BogusDOCTYPEState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(AfterDOCTYPEPublicKeywordState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BeforeDOCTYPEPublicIdentifierState);
+        else if (cc == '"') {
+            parseError();
+            HTML_ADVANCE_TO(DOCTYPEPublicIdentifierDoubleQuotedState);
+        } else if (cc == '\'') {
+            parseError();
+            HTML_ADVANCE_TO(DOCTYPEPublicIdentifierSingleQuotedState);
+        } else if (cc == '>') {
+            parseError();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            parseError();
+            HTML_ADVANCE_TO(BogusDOCTYPEState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(BeforeDOCTYPEPublicIdentifierState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BeforeDOCTYPEPublicIdentifierState);
+        else if (cc == '"') {
+            HTML_ADVANCE_TO(DOCTYPEPublicIdentifierDoubleQuotedState);
+        } else if (cc == '\'') {
+            HTML_ADVANCE_TO(DOCTYPEPublicIdentifierSingleQuotedState);
+        } else if (cc == '>') {
+            parseError();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            parseError();
+            HTML_ADVANCE_TO(BogusDOCTYPEState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(DOCTYPEPublicIdentifierDoubleQuotedState) {
+        if (cc == '"')
+            HTML_ADVANCE_TO(AfterDOCTYPEPublicIdentifierState);
+        else if (cc == '>') {
+            parseError();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            HTML_ADVANCE_TO(DOCTYPEPublicIdentifierDoubleQuotedState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(DOCTYPEPublicIdentifierSingleQuotedState) {
+        if (cc == '\'')
+            HTML_ADVANCE_TO(AfterDOCTYPEPublicIdentifierState);
+        else if (cc == '>') {
+            parseError();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            HTML_ADVANCE_TO(DOCTYPEPublicIdentifierSingleQuotedState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(AfterDOCTYPEPublicIdentifierState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BetweenDOCTYPEPublicAndSystemIdentifiersState);
+        else if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (cc == '"') {
+            parseError();
+            HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState);
+        } else if (cc == '\'') {
+            parseError();
+            HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            parseError();
+            HTML_ADVANCE_TO(BogusDOCTYPEState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(BetweenDOCTYPEPublicAndSystemIdentifiersState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BetweenDOCTYPEPublicAndSystemIdentifiersState);
+        else if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (cc == '"') {
+            HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState);
+        } else if (cc == '\'') {
+            HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            parseError();
+            HTML_ADVANCE_TO(BogusDOCTYPEState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(AfterDOCTYPESystemKeywordState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BeforeDOCTYPESystemIdentifierState);
+        else if (cc == '"') {
+            parseError();
+            HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState);
+        } else if (cc == '\'') {
+            parseError();
+            HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState);
+        } else if (cc == '>') {
+            parseError();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            parseError();
+            HTML_ADVANCE_TO(BogusDOCTYPEState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(BeforeDOCTYPESystemIdentifierState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(BeforeDOCTYPESystemIdentifierState);
+        if (cc == '"') {
+            HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState);
+        } else if (cc == '\'') {
+            HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState);
+        } else if (cc == '>') {
+            parseError();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            parseError();
+            HTML_ADVANCE_TO(BogusDOCTYPEState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(DOCTYPESystemIdentifierDoubleQuotedState) {
+        if (cc == '"')
+            HTML_ADVANCE_TO(AfterDOCTYPESystemIdentifierState);
+        else if (cc == '>') {
+            parseError();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(DOCTYPESystemIdentifierSingleQuotedState) {
+        if (cc == '\'')
+            HTML_ADVANCE_TO(AfterDOCTYPESystemIdentifierState);
+        else if (cc == '>') {
+            parseError();
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        } else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(AfterDOCTYPESystemIdentifierState) {
+        if (isTokenizerWhitespace(cc))
+            HTML_ADVANCE_TO(AfterDOCTYPESystemIdentifierState);
+        else if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (cc == kEndOfFileMarker) {
+            parseError();
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        } else {
+            parseError();
+            HTML_ADVANCE_TO(BogusDOCTYPEState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(BogusDOCTYPEState) {
+        if (cc == '>')
+            return emitAndResumeIn(source, HTMLTokenizer::DataState);
+        else if (cc == kEndOfFileMarker)
+            return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
+        HTML_ADVANCE_TO(BogusDOCTYPEState);
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(CDATASectionState) {
+        if (cc == ']')
+            HTML_ADVANCE_TO(CDATASectionRightSquareBracketState);
+        else if (cc == kEndOfFileMarker)
+            HTML_RECONSUME_IN(DataState);
+        else {
+            m_token->ensureIsCharacterToken();
+            HTML_ADVANCE_TO(CDATASectionState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(CDATASectionRightSquareBracketState) {
+        if (cc == ']')
+            HTML_ADVANCE_TO(CDATASectionDoubleRightSquareBracketState);
+        else {
+            m_token->ensureIsCharacterToken();
+            HTML_RECONSUME_IN(CDATASectionState);
+        }
+    }
+    END_STATE()
+
+    HTML_BEGIN_STATE(CDATASectionDoubleRightSquareBracketState) {
+        if (cc == '>')
+            HTML_ADVANCE_TO(DataState);
+        else {
+            m_token->ensureIsCharacterToken();
+            HTML_RECONSUME_IN(CDATASectionState);
+        }
+    }
+    END_STATE()
+
+    }
+
+    ASSERT_NOT_REACHED();
+    return false;
+}
+
+inline void HTMLTokenizer::parseError()
+{
+    notImplemented();
+}
+
+}
diff --git a/ios/third_party/blink/src/html_tokenizer_adapter.h b/ios/third_party/blink/src/html_tokenizer_adapter.h
new file mode 100644
index 0000000..a16c5c09
--- /dev/null
+++ b/ios/third_party/blink/src/html_tokenizer_adapter.h
@@ -0,0 +1,51 @@
+// 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 IOS_THIRD_PARTY_BLINK_SRC_TOKENIZER_ADAPTER_H_
+#define IOS_THIRD_PARTY_BLINK_SRC_TOKENIZER_ADAPTER_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+#define ALWAYS_INLINE inline __attribute__((always_inline))
+
+#define DEFINE_STATIC_LOCAL_STRING(name, arguments)                       \
+    static const WebCore::LChar* name = (const WebCore::LChar*)arguments; \
+    static const size_t name##Length = (arraysize(arguments) - 1); \
+    DCHECK(name##Length == strlen((const char*)name))
+
+#define WTF_MAKE_NONCOPYABLE(x) DISALLOW_COPY_AND_ASSIGN(x)
+
+#define ASSERT(x) DCHECK(x)
+#define ASSERT_NOT_REACHED NOTREACHED
+
+#define notImplemented()
+
+namespace WebCore {
+typedef uint16 UChar;
+typedef uint8 LChar;
+
+template <typename CharType>
+inline bool isASCIIUpper(CharType c) {
+  return c >= 'A' && c <= 'Z';
+}
+
+template <typename CharType>
+inline bool isASCIILower(CharType c) {
+  return c >= 'a' && c <= 'z';
+}
+
+template <typename CharType>
+inline CharType toLowerCase(CharType c) {
+  ASSERT(isASCIIUpper(c));
+  const int lowerCaseOffset = 0x20;
+  return c + lowerCaseOffset;
+}
+
+inline UChar ByteSwap(UChar c) {
+  return ((c & 0x00ff) << 8) | ((c & 0xff00) >> 8);
+}
+}
+
+#endif  // IOS_THIRD_PARTY_BLINK_SRC_TOKENIZER_ADAPTER_H_
diff --git a/ios/web/DEPS b/ios/web/DEPS
index 2f189bf..abbd333 100644
--- a/ios/web/DEPS
+++ b/ios/web/DEPS
@@ -1,8 +1,13 @@
 include_rules = [
+  "+ios/public/provider/web",
+  "+ios/net",
   "+ios/web",
   "+net",
   "+third_party/libwebp/webp",
   "+ui",
+
+  # For tests.
+  "+ios/testing",
 ]
 
 specific_include_rules = {
diff --git a/ios/web/ios_web.gyp b/ios/web/ios_web.gyp
index 5621e41..f661363 100644
--- a/ios/web/ios_web.gyp
+++ b/ios/web/ios_web.gyp
@@ -67,12 +67,15 @@
         'public/web_state/crw_web_delegate.h',
         'public/web_state/crw_web_user_interface_delegate.h'
         'public/web_state/crw_web_view_proxy.h'
+        'public/web_state/crw_web_view_scroll_view_proxy.h'
         'public/web_state/js/crw_js_base_manager.h',
         'public/web_state/js/crw_js_early_script_manager.h',
         'public/web_state/js/crw_js_injection_evaluator.h',
         'public/web_state/js/crw_js_injection_manager.h',
         'public/web_state/js/crw_js_injection_receiver.h',
         'public/web_state/js/crw_js_message_manager.h',
+        'public/web_state/page_scroll_state.h',
+        'public/web_state/page_scroll_state.mm',
         'public/web_state/url_verification_constants.h',
         'public/web_state/web_state_observer.h',
         'public/web_state/web_state_observer_bridge.h',
diff --git a/ios/web/navigation/crw_session_certificate_policy_manager.h b/ios/web/navigation/crw_session_certificate_policy_manager.h
new file mode 100644
index 0000000..a97e913
--- /dev/null
+++ b/ios/web/navigation/crw_session_certificate_policy_manager.h
@@ -0,0 +1,43 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_NAVIGATION_CRW_SESSION_CERTIFICATE_POLICY_MANAGER_H_
+#define IOS_WEB_NAVIGATION_CRW_SESSION_CERTIFICATE_POLICY_MANAGER_H_
+
+#import <Foundation/Foundation.h>
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "net/cert/cert_status_flags.h"
+
+@class CRWSessionEntry;
+
+namespace net {
+class X509Certificate;
+}
+
+namespace web {
+class CertificatePolicyCache;
+}
+
+// The CRWSessionCertificatePolicyManager keeps track of the certificates that
+// have been manually allowed by the user despite the errors.
+// The CRWSessionCertificatePolicyManager lives on the main thread.
+@interface CRWSessionCertificatePolicyManager : NSObject <NSCoding, NSCopying>
+
+- (void)registerAllowedCertificate:(net::X509Certificate*)certificate
+                           forHost:(const std::string&)host
+                            status:(net::CertStatus)status;
+
+// Removes all the certificates associated with this session. Note that this has
+// no effect on the policy cache service.
+- (void)clearCertificates;
+
+// Copies the certificate polices for the session into |cache|.
+- (void)updateCertificatePolicyCache:
+    (const scoped_refptr<web::CertificatePolicyCache>&)cache;
+
+@end
+
+#endif  // IOS_WEB_NAVIGATION_CRW_SESSION_CERTIFICATE_POLICY_MANAGER_H_
diff --git a/ios/web/navigation/crw_session_certificate_policy_manager.mm b/ios/web/navigation/crw_session_certificate_policy_manager.mm
new file mode 100644
index 0000000..3547536
--- /dev/null
+++ b/ios/web/navigation/crw_session_certificate_policy_manager.mm
@@ -0,0 +1,182 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web/navigation/crw_session_certificate_policy_manager.h"
+
+#include <map>
+#include <set>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/strings/sys_string_conversions.h"
+#include "ios/web/public/certificate_policy_cache.h"
+#include "ios/web/public/web_thread.h"
+#include "net/cert/x509_certificate.h"
+
+// Break if we detect that CertStatus values changed, because we persist them on
+// disk and thus require them to be consistent.
+COMPILE_ASSERT(net::CERT_STATUS_ALL_ERRORS == 0xFFFF,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_COMMON_NAME_INVALID == 1 << 0,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_DATE_INVALID == 1 << 1,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_AUTHORITY_INVALID == 1 << 2,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_NO_REVOCATION_MECHANISM == 1 << 4,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION == 1 << 5,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_REVOKED == 1 << 6,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_INVALID == 1 << 7,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM == 1 << 8,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_NON_UNIQUE_NAME == 1 << 10,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_WEAK_KEY == 1 << 11,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_IS_EV == 1 << 16,
+               cert_status_value_changed);
+COMPILE_ASSERT(net::CERT_STATUS_REV_CHECKING_ENABLED == 1 << 17,
+               cert_status_value_changed);
+
+namespace {
+
+NSString* const kAllowedCertificatesKey = @"allowedCertificates";
+
+struct AllowedCertificate {
+  scoped_refptr<net::X509Certificate> certificate;
+  std::string host;
+};
+
+class LessThan {
+ public:
+  bool operator() (const AllowedCertificate& lhs,
+                   const AllowedCertificate& rhs) const {
+    if (lhs.host != rhs.host)
+      return lhs.host < rhs.host;
+    return certificateCompare_(lhs.certificate, rhs.certificate);
+  }
+ private:
+  net::X509Certificate::LessThan certificateCompare_;
+};
+
+typedef std::map<AllowedCertificate, net::CertStatus, LessThan>
+    AllowedCertificates;
+
+NSData* CertificateToNSData(net::X509Certificate* certificate) {
+  std::string s;
+  bool success =
+      net::X509Certificate::GetDEREncoded(certificate->os_cert_handle(), &s);
+  DCHECK(success);
+  return [NSData dataWithBytes:s.c_str() length:s.length()];
+}
+
+net::X509Certificate* NSDataToCertificate(NSData* data) {
+  return net::X509Certificate::CreateFromBytes((const char *)[data bytes],
+                                               [data length]);
+}
+
+void AddToCertificatePolicyCache(
+    scoped_refptr<web::CertificatePolicyCache> policy_cache,
+    AllowedCertificates certs) {
+  DCHECK(policy_cache);
+  AllowedCertificates::iterator it;
+  for (it = certs.begin(); it != certs.end(); ++it) {
+    policy_cache->AllowCertForHost(
+        it->first.certificate.get(), it->first.host, it->second);
+  }
+}
+
+}  // namespace
+
+@implementation CRWSessionCertificatePolicyManager {
+ @private
+  AllowedCertificates allowed_;
+}
+
+- (void)registerAllowedCertificate:(net::X509Certificate*)certificate
+                           forHost:(const std::string&)host
+                            status:(net::CertStatus)status {
+  DCHECK([NSThread isMainThread]);
+  DCHECK(certificate);
+  AllowedCertificate allowedCertificate = {certificate, host};
+  allowed_[allowedCertificate] = status;
+}
+
+- (void)clearCertificates {
+  DCHECK([NSThread isMainThread]);
+  allowed_.clear();
+}
+
+- (void)updateCertificatePolicyCache:
+    (const scoped_refptr<web::CertificatePolicyCache>&)cache {
+  DCHECK([NSThread isMainThread]);
+  DCHECK(cache);
+  // Make a copy of allowed_ and access the policy cache from the IOThread.
+  web::WebThread::PostTask(
+      web::WebThread::IO, FROM_HERE,
+      base::Bind(&AddToCertificatePolicyCache, cache, allowed_));
+}
+
+#pragma mark -
+#pragma mark NSCoding and NSCopying methods
+
+- (id)initWithCoder:(NSCoder*)aDecoder {
+  DCHECK([NSThread isMainThread]);
+  self = [super init];
+  if (self) {
+    NSMutableSet* allowed = [aDecoder
+        decodeObjectForKey:kAllowedCertificatesKey];
+    for (NSArray* fields in allowed) {
+      if ([fields count] == 2) {
+        DVLOG(2) << "Dropping cached certificate policy (old format).";
+        continue;
+      } else if ([fields count] != 3) {
+        NOTREACHED();
+        continue;
+      }
+      net::X509Certificate* c = NSDataToCertificate([fields objectAtIndex:0]);
+      std::string host = base::SysNSStringToUTF8([fields objectAtIndex:1]);
+      net::CertStatus status = (net::CertStatus)[[fields objectAtIndex:2]
+          unsignedIntegerValue];
+      [self registerAllowedCertificate:c forHost:host status:status];
+    }
+  }
+  return self;
+}
+
+- (void)encodeWithCoder:(NSCoder*)aCoder {
+  if (allowed_.size() == 0)
+    return;
+
+  // Simple serialization of the set. If a same certificate is duplicated in the
+  // set (for a different host), the serialization will be duplicated as well.
+  NSMutableSet* allowedToEncode = [NSMutableSet set];
+  AllowedCertificates::iterator it;
+  for (it = allowed_.begin(); it != allowed_.end(); ++it) {
+    NSData* c = CertificateToNSData(it->first.certificate.get());
+    NSString* h = base::SysUTF8ToNSString(it->first.host);
+    DCHECK(c);
+    DCHECK(h);
+    if (!c || !h)
+      continue;
+    const NSUInteger status = (NSUInteger)it->second;
+    NSArray* fields = [NSArray arrayWithObjects:c, h, @(status), nil];
+    [allowedToEncode addObject:fields];
+  }
+  [aCoder encodeObject:allowedToEncode forKey:kAllowedCertificatesKey];
+}
+
+- (id)copyWithZone:(NSZone*)zone {
+  DCHECK([NSThread isMainThread]);
+  CRWSessionCertificatePolicyManager* copy = [[[self class] alloc] init];
+  copy->allowed_ = allowed_;
+  return copy;
+}
+
+@end
diff --git a/ios/web/navigation/crw_session_controller+private_constructors.h b/ios/web/navigation/crw_session_controller+private_constructors.h
new file mode 100644
index 0000000..dd7a119
--- /dev/null
+++ b/ios/web/navigation/crw_session_controller+private_constructors.h
@@ -0,0 +1,37 @@
+// 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 IOS_WEB_NAVIGATION_CRW_SESSION_CONTROLLER_PRIVATE_CONSTRUCTORS_H_
+#define IOS_WEB_NAVIGATION_CRW_SESSION_CONTROLLER_PRIVATE_CONSTRUCTORS_H_
+
+#import "ios/web/navigation/crw_session_controller.h"
+
+#include "base/memory/scoped_vector.h"
+
+namespace web {
+class BrowserState;
+class NavigationItem;
+}
+
+// Temporary interface for NavigationManager and tests to create
+// CRWSessionControllers. Once CRWSessionController has no users outside of
+// web/, these methods can go back into session_controller.h. crbug.com/318974
+@interface CRWSessionController (PrivateConstructors)
+// Initializes a session controller, supplying a unique textual identifier for
+// the window, or nil. |opener| is the tab id of the parent tab. It may be
+// nil or empty if there is no parent.
+- (id)initWithWindowName:(NSString*)windowName
+                openerId:(NSString*)opener
+             openedByDOM:(BOOL)openedByDOM
+   openerNavigationIndex:(NSInteger)openerIndex
+            browserState:(web::BrowserState*)browserState;
+
+// Initializes a session controller, supplying a list of NavigationItem objects
+// and the current index in the navigation history.
+- (id)initWithNavigationItems:(ScopedVector<web::NavigationItem>)scoped_items
+                 currentIndex:(NSUInteger)currentIndex
+                 browserState:(web::BrowserState*)browserState;
+@end
+
+#endif  // IOS_WEB_NAVIGATION_CRW_SESSION_CONTROLLER_PRIVATE_CONSTRUCTORS_H_
diff --git a/ios/web/navigation/crw_session_controller.h b/ios/web/navigation/crw_session_controller.h
new file mode 100644
index 0000000..b6eb972
--- /dev/null
+++ b/ios/web/navigation/crw_session_controller.h
@@ -0,0 +1,159 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_NAVIGATION_CRW_SESSION_CONTROLLER_H_
+#define IOS_WEB_NAVIGATION_CRW_SESSION_CONTROLLER_H_
+
+#import <Foundation/Foundation.h>
+#include <vector>
+
+#include "ui/base/page_transition_types.h"
+#include "url/gurl.h"
+
+@class CRWSessionEntry;
+@class CRWSessionCertificatePolicyManager;
+@class XCallbackParameters;
+
+namespace web {
+class NavigationManagerImpl;
+struct Referrer;
+struct SSLStatus;
+}
+
+// A CRWSessionController is similar to a NavigationController object in desktop
+// Chrome. It maintains information needed to save/restore a tab with its
+// complete session history. There is one of these for each tab.
+// TODO(stuartmorgan): Move under NavigationManager, and consider merging into
+// NavigationManager.
+@interface CRWSessionController : NSObject<NSCoding, NSCopying>
+
+@property(nonatomic, readonly, retain) NSString* tabId;
+@property(nonatomic, readonly, assign) NSInteger currentNavigationIndex;
+@property(nonatomic, readonly, assign) NSInteger previousNavigationIndex;
+@property(nonatomic, readonly, retain) NSArray* entries;
+@property(nonatomic, copy) NSString* windowName;
+// Indicates whether the page was opened by DOM (e.g. with |window.open|
+// JavaScript call or by clicking a link with |_blank| target).
+@property(nonatomic, readonly, getter=isOpenedByDOM) BOOL openedByDOM;
+@property(nonatomic, readonly, retain)
+    CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager;
+// Returns the current entry in the session list, or the pending entry if there
+// is a navigation in progress.
+@property(nonatomic, readonly) CRWSessionEntry* currentEntry;
+// Returns the entry that should be displayed to users (e.g., in the omnibox).
+@property(nonatomic, readonly) CRWSessionEntry* visibleEntry;
+// Returns the pending entry, if any.
+@property(nonatomic, readonly) CRWSessionEntry* pendingEntry;
+// Returns the transient entry, if any.
+@property(nonatomic, readonly) CRWSessionEntry* transientEntry;
+// Returns the last committed entry.
+@property(nonatomic, readonly) CRWSessionEntry* lastCommittedEntry;
+// Returns the previous entry in the session list, or nil if there isn't any.
+@property(nonatomic, readonly) CRWSessionEntry* previousEntry;
+@property(nonatomic, assign) NSTimeInterval lastVisitedTimestamp;
+@property(nonatomic, readonly, copy) NSString* openerId;
+@property(nonatomic, readonly, assign) NSInteger openerNavigationIndex;
+@property(nonatomic, retain) XCallbackParameters* xCallbackParameters;
+
+// CRWSessionController doesn't have public constructors. New
+// CRWSessionControllers are created by deserialization, or via a
+// NavigationManager.
+
+// Sets the corresponding NavigationManager.
+- (void)setNavigationManager:(web::NavigationManagerImpl*)navigationManager;
+
+// Add a new entry with the given url, referrer, and navigation type, making it
+// the current entry. If |url| is the same as the current entry's url, this
+// does nothing. |referrer| may be nil if there isn't one. The entry starts
+// out as pending, and will be lost unless |-commitPendingEntry| is called.
+- (void)addPendingEntry:(const GURL&)url
+               referrer:(const web::Referrer&)referrer
+             transition:(ui::PageTransition)type
+      rendererInitiated:(BOOL)rendererInitiated;
+
+// Updates the URL of the yet to be committed pending entry. Useful for page
+// redirects. Does nothing if there is no pending entry.
+- (void)updatePendingEntry:(const GURL&)url;
+
+// Commits the current pending entry. No changes are made to the entry during
+// this process, it is just moved from pending to committed.
+// TODO(pinkerton): Desktop Chrome broadcasts a notification here, should we?
+- (void)commitPendingEntry;
+
+// Adds a transient entry with the given information. A transient entry will be
+// discarded on any navigation.
+// TODO(stuartmorgan): Make this work more like upstream, where the entry can
+// be made from outside and then handed in, instead of having to pass a bunch
+// of construction params here.
+- (void)addTransientEntry:(const GURL&)url
+                    title:(const base::string16&)title
+                sslStatus:(const web::SSLStatus*)status;
+
+// Creates a new CRWSessionEntry with the given URL and state object. A state
+// object is a serialized generic JavaScript object that contains details of the
+// UI's state for a given CRWSessionEntry/URL. The current entry's URL is the
+// new entry's referrer.
+- (void)pushNewEntryWithURL:(const GURL&)url stateObject:(NSString*)stateObject;
+// Updates the URL and state object for the current entry.
+- (void)updateCurrentEntryWithURL:(const GURL&)url
+                      stateObject:(NSString*)stateObject;
+
+- (void)discardNonCommittedEntries;
+
+// Returns YES if there is a pending entry.
+- (BOOL)hasPendingEntry;
+
+// Copies history state from the given CRWSessionController and adds it to this
+// controller. If |replaceState|, replaces the state of this controller with
+// the state of |otherSession|, instead of appending.
+- (void)copyStateFromAndPrune:(CRWSessionController*)otherSession
+                 replaceState:(BOOL)replaceState;
+
+// Returns YES if there are entries to go back or forward to, given the
+// current entry.
+- (BOOL)canGoBack;
+- (BOOL)canGoForward;
+// Adjusts the current entry to reflect the navigation in the corresponding
+// direction in history.
+- (void)goBack;
+- (void)goForward;
+// Calls goBack or goForward the appropriate number of times to adjust
+// currentNavigationIndex_ by delta.
+- (void)goDelta:(int)delta;
+// Sets |currentNavigationIndex_| to the index of |entry| if |entries_| contains
+// |entry|.
+- (void)goToEntry:(CRWSessionEntry*)entry;
+
+// Removes the entry at |index| after discarding any noncomitted entries.
+// |index| must not be the index of the last committed entry, or a noncomitted
+// entry.
+- (void)removeEntryAtIndex:(NSInteger)index;
+
+// Returns an array containing all of the non-redirected CRWSessionEntry objects
+// whose index in |entries_| is less than |currentNavigationIndex_|.
+- (NSArray*)backwardEntries;
+
+// Returns an array containing all of the non-redirected CRWSessionEntry objects
+// whose index in |entries_| is greater than |currentNavigationIndex_|.
+- (NSArray*)forwardEntries;
+
+// Returns the URLs in the entries that are redirected to the current entry.
+- (std::vector<GURL>)currentRedirectedUrls;
+
+// Determines if navigation between the two given entries is a push state
+// navigation. Entries can be passed in in any order.
+- (BOOL)isPushStateNavigationBetweenEntry:(CRWSessionEntry*)firstEntry
+                                 andEntry:(CRWSessionEntry*)secondEntry;
+
+// Find the most recent session entry that is not a redirect. Returns nil if
+// |entries_| is empty.
+- (CRWSessionEntry*)lastUserEntry;
+
+// Set |useDesktopUserAgentForNextPendingEntry_| to YES if there is no pending
+// entry, otherwise set |useDesktopUserAgent| in the pending entry.
+- (void)useDesktopUserAgentForNextPendingEntry;
+
+@end
+
+#endif  // IOS_WEB_NAVIGATION_CRW_SESSION_CONTROLLER_H_
diff --git a/ios/web/navigation/crw_session_controller.mm b/ios/web/navigation/crw_session_controller.mm
new file mode 100644
index 0000000..5407ec31
--- /dev/null
+++ b/ios/web/navigation/crw_session_controller.mm
@@ -0,0 +1,874 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web/navigation/crw_session_controller.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/mac/objc_property_releaser.h"
+#import "base/mac/scoped_nsobject.h"
+#include "base/metrics/user_metrics_action.h"
+#include "base/strings/sys_string_conversions.h"
+#import "ios/web/history_state_util.h"
+#import "ios/web/navigation/crw_session_certificate_policy_manager.h"
+#import "ios/web/navigation/crw_session_controller+private_constructors.h"
+#import "ios/web/navigation/crw_session_entry.h"
+#include "ios/web/navigation/navigation_item_impl.h"
+#import "ios/web/navigation/navigation_manager_facade_delegate.h"
+#import "ios/web/navigation/navigation_manager_impl.h"
+#include "ios/web/navigation/time_smoother.h"
+#include "ios/web/public/browser_state.h"
+#include "ios/web/public/browser_url_rewriter.h"
+#include "ios/web/public/referrer.h"
+#include "ios/web/public/ssl_status.h"
+#include "ios/web/public/user_metrics.h"
+
+using base::UserMetricsAction;
+
+namespace {
+NSString* const kCertificatePolicyManagerKey = @"certificatePolicyManager";
+NSString* const kCurrentNavigationIndexKey = @"currentNavigationIndex";
+NSString* const kEntriesKey = @"entries";
+NSString* const kLastVisitedTimestampKey = @"lastVisitedTimestamp";
+NSString* const kOpenerIdKey = @"openerId";
+NSString* const kOpenedByDOMKey = @"openedByDOM";
+NSString* const kOpenerNavigationIndexKey = @"openerNavigationIndex";
+NSString* const kPreviousNavigationIndexKey = @"previousNavigationIndex";
+NSString* const kTabIdKey = @"tabId";
+NSString* const kWindowNameKey = @"windowName";
+NSString* const kXCallbackParametersKey = @"xCallbackParameters";
+}  // anonymous namespace
+
+@interface CRWSessionController () {
+  // Weak pointer back to the owning NavigationManager. This is to facilitate
+  // the incremental merging of the two classes.
+  web::NavigationManagerImpl* _navigationManager;
+
+  NSString* _tabId;  // Unique id of the tab.
+  NSString* _openerId;  // Id of tab who opened this tab, empty/nil if none.
+  // Navigation index of the tab which opened this tab. Do not rely on the
+  // value of this member variable to indicate whether or not this tab has
+  // an opener, as both 0 and -1 are used as navigationIndex values.
+  NSInteger _openerNavigationIndex;
+  // Identifies the index of the current navigation in the CRWSessionEntry
+  // array.
+  NSInteger _currentNavigationIndex;
+  // Identifies the index of the previous navigation in the CRWSessionEntry
+  // array.
+  NSInteger _previousNavigationIndex;
+  // Ordered array of |CRWSessionEntry| objects, one for each site in session
+  // history. End of the list is the most recent load.
+  NSMutableArray* _entries;
+
+  // An entry we haven't gotten a response for yet. This will be discarded
+  // when we navigate again. It's used only so we know what the currently
+  // displayed tab is.  It backs the property of the same name and should only
+  // be set through its setter.
+  base::scoped_nsobject<CRWSessionEntry> _pendingEntry;
+
+  // The transient entry, if any. A transient entry is discarded on any
+  // navigation, and is used for representing interstitials that need to be
+  // represented in the session.  It backs the property of the same name and
+  // should only be set through its setter.
+  base::scoped_nsobject<CRWSessionEntry> _transientEntry;
+
+  // The window name associated with the session.
+  NSString* _windowName;
+
+   // Stores the certificate policies decided by the user.
+  CRWSessionCertificatePolicyManager* _sessionCertificatePolicyManager;
+
+  // The timestamp of the last time this tab is visited, represented in time
+  // interval since 1970.
+  NSTimeInterval _lastVisitedTimestamp;
+
+  // If |YES|, override |currentEntry.useDesktopUserAgent| and create the
+  // pending entry using the desktop user agent.
+  BOOL _useDesktopUserAgentForNextPendingEntry;
+
+  // The browser state associated with this CRWSessionController;
+  __weak web::BrowserState* _browserState;
+
+  // Time smoother for navigation entry timestamps; see comment in
+  // navigation_controller_impl.h
+  web::TimeSmoother _timeSmoother;
+
+  // XCallback parameters used to create (or clobber) the tab. Can be nil.
+  XCallbackParameters* _xCallbackParameters;
+
+  base::mac::ObjCPropertyReleaser _propertyReleaser_CRWSessionController;
+}
+
+// TODO(rohitrao): These properties must be redefined readwrite to work around a
+// clang bug. crbug.com/228650
+@property(nonatomic, readwrite, retain) NSString* tabId;
+@property(nonatomic, readwrite, retain) NSArray* entries;
+@property(nonatomic, readwrite, retain)
+    CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager;
+
+- (NSString*)uniqueID;
+// Removes all entries after currentNavigationIndex_.
+- (void)clearForwardEntries;
+// Discards the transient entry, if any.
+- (void)discardTransientEntry;
+// Create a new autoreleased session entry.
+- (CRWSessionEntry*)sessionEntryWithURL:(const GURL&)url
+                               referrer:(const web::Referrer&)referrer
+                             transition:(ui::PageTransition)transition
+                    useDesktopUserAgent:(BOOL)useDesktopUserAgent
+                      rendererInitiated:(BOOL)rendererInitiated;
+// Return the PageTransition for the underlying navigationItem at |index| in
+// |entries_|
+- (ui::PageTransition)transitionForIndex:(NSUInteger)index;
+@end
+
+@implementation CRWSessionController
+
+@synthesize tabId = _tabId;
+@synthesize currentNavigationIndex = _currentNavigationIndex;
+@synthesize previousNavigationIndex = _previousNavigationIndex;
+@synthesize entries = _entries;
+@synthesize windowName = _windowName;
+@synthesize lastVisitedTimestamp = _lastVisitedTimestamp;
+@synthesize openerId = _openerId;
+@synthesize openedByDOM = _openedByDOM;
+@synthesize openerNavigationIndex = _openerNavigationIndex;
+@synthesize sessionCertificatePolicyManager = _sessionCertificatePolicyManager;
+@synthesize xCallbackParameters = _xCallbackParameters;
+
+- (id)initWithWindowName:(NSString*)windowName
+                openerId:(NSString*)openerId
+             openedByDOM:(BOOL)openedByDOM
+   openerNavigationIndex:(NSInteger)openerIndex
+            browserState:(web::BrowserState*)browserState {
+  self = [super init];
+  if (self) {
+    _propertyReleaser_CRWSessionController.Init(self,
+                                                [CRWSessionController class]);
+    self.windowName = windowName;
+    _tabId = [[self uniqueID] retain];
+    _openerId = [openerId copy];
+    _openedByDOM = openedByDOM;
+    _openerNavigationIndex = openerIndex;
+    _browserState = browserState;
+    _entries = [[NSMutableArray array] retain];
+    _lastVisitedTimestamp = [[NSDate date] timeIntervalSince1970];
+    _currentNavigationIndex = -1;
+    _previousNavigationIndex = -1;
+    _sessionCertificatePolicyManager =
+        [[CRWSessionCertificatePolicyManager alloc] init];
+  }
+  return self;
+}
+
+- (id)initWithNavigationItems:(ScopedVector<web::NavigationItem>)scoped_items
+                 currentIndex:(NSUInteger)currentIndex
+                 browserState:(web::BrowserState*)browserState {
+  self = [super init];
+  if (self) {
+    _propertyReleaser_CRWSessionController.Init(self,
+                                                [CRWSessionController class]);
+    _tabId = [[self uniqueID] retain];
+    _openerId = nil;
+    _browserState = browserState;
+
+    // Create entries array from list of navigations.
+    _entries = [[NSMutableArray alloc] initWithCapacity:scoped_items.size()];
+    std::vector<web::NavigationItem*> items;
+    scoped_items.release(&items);
+
+    for (size_t i = 0; i < items.size(); ++i) {
+      scoped_ptr<web::NavigationItem> item(items[i]);
+      base::scoped_nsobject<CRWSessionEntry> entry(
+          [[CRWSessionEntry alloc] initWithNavigationItem:item.Pass() index:i]);
+      [_entries addObject:entry];
+    }
+    _currentNavigationIndex = currentIndex;
+    // Prior to M34, 0 was used as "no index" instead of -1; adjust for that.
+    if (![_entries count])
+      _currentNavigationIndex = -1;
+    if (_currentNavigationIndex >= static_cast<NSInteger>(items.size())) {
+      _currentNavigationIndex = static_cast<NSInteger>(items.size()) - 1;
+    }
+    _previousNavigationIndex = -1;
+    _lastVisitedTimestamp = [[NSDate date] timeIntervalSince1970];
+    _sessionCertificatePolicyManager =
+        [[CRWSessionCertificatePolicyManager alloc] init];
+  }
+  return self;
+}
+
+- (id)initWithCoder:(NSCoder*)aDecoder {
+  self = [super init];
+  if (self) {
+    _propertyReleaser_CRWSessionController.Init(self,
+                                                [CRWSessionController class]);
+    NSString* uuid = [aDecoder decodeObjectForKey:kTabIdKey];
+    if (!uuid)
+      uuid = [self uniqueID];
+
+    self.windowName = [aDecoder decodeObjectForKey:kWindowNameKey];
+    _tabId = [uuid retain];
+    _openerId = [[aDecoder decodeObjectForKey:kOpenerIdKey] copy];
+    _openedByDOM = [aDecoder decodeBoolForKey:kOpenedByDOMKey];
+    _openerNavigationIndex =
+        [aDecoder decodeIntForKey:kOpenerNavigationIndexKey];
+    _currentNavigationIndex =
+        [aDecoder decodeIntForKey:kCurrentNavigationIndexKey];
+    _previousNavigationIndex =
+        [aDecoder decodeIntForKey:kPreviousNavigationIndexKey];
+    _lastVisitedTimestamp =
+       [aDecoder decodeDoubleForKey:kLastVisitedTimestampKey];
+    NSMutableArray* temp =
+        [NSMutableArray arrayWithArray:
+            [aDecoder decodeObjectForKey:kEntriesKey]];
+    _entries = [temp retain];
+    // Prior to M34, 0 was used as "no index" instead of -1; adjust for that.
+    if (![_entries count])
+      _currentNavigationIndex = -1;
+    _sessionCertificatePolicyManager =
+       [[aDecoder decodeObjectForKey:kCertificatePolicyManagerKey] retain];
+    if (!_sessionCertificatePolicyManager) {
+      _sessionCertificatePolicyManager =
+          [[CRWSessionCertificatePolicyManager alloc] init];
+    }
+
+    _xCallbackParameters =
+        [[aDecoder decodeObjectForKey:kXCallbackParametersKey] retain];
+  }
+  return self;
+}
+
+- (void)encodeWithCoder:(NSCoder*)aCoder {
+  [aCoder encodeObject:_tabId forKey:kTabIdKey];
+  [aCoder encodeObject:_openerId forKey:kOpenerIdKey];
+  [aCoder encodeBool:_openedByDOM forKey:kOpenedByDOMKey];
+  [aCoder encodeInt:_openerNavigationIndex forKey:kOpenerNavigationIndexKey];
+  [aCoder encodeObject:_windowName forKey:kWindowNameKey];
+  [aCoder encodeInt:_currentNavigationIndex forKey:kCurrentNavigationIndexKey];
+  [aCoder encodeInt:_previousNavigationIndex
+             forKey:kPreviousNavigationIndexKey];
+  [aCoder encodeDouble:_lastVisitedTimestamp forKey:kLastVisitedTimestampKey];
+  [aCoder encodeObject:_entries forKey:kEntriesKey];
+  [aCoder encodeObject:_sessionCertificatePolicyManager
+                forKey:kCertificatePolicyManagerKey];
+  [aCoder encodeObject:_xCallbackParameters forKey:kXCallbackParametersKey];
+  // rendererInitiated is deliberately not preserved, as upstream.
+}
+
+- (id)copyWithZone:(NSZone*)zone {
+  CRWSessionController* copy = [[[self class] alloc] init];
+  copy->_propertyReleaser_CRWSessionController.Init(
+      copy, [CRWSessionController class]);
+  copy->_tabId = [_tabId copy];
+  copy->_openerId = [_openerId copy];
+  copy->_openedByDOM = _openedByDOM;
+  copy->_openerNavigationIndex = _openerNavigationIndex;
+  copy.windowName = self.windowName;
+  copy->_currentNavigationIndex = _currentNavigationIndex;
+  copy->_previousNavigationIndex = _previousNavigationIndex;
+  copy->_lastVisitedTimestamp = _lastVisitedTimestamp;
+  copy->_entries = [_entries copy];
+  copy->_sessionCertificatePolicyManager =
+      [_sessionCertificatePolicyManager copy];
+  copy->_xCallbackParameters = [_xCallbackParameters copy];
+  return copy;
+}
+
+- (void)setNavigationManager:(web::NavigationManagerImpl*)navigationManager {
+  _navigationManager = navigationManager;
+  if (_navigationManager) {
+    // _browserState will be nullptr if CRWSessionController has been
+    // initialized with -initWithCoder: method. Take _browserState from
+    // NavigationManagerImpl if that's the case.
+    if (!_browserState) {
+      _browserState = _navigationManager->GetBrowserState();
+    }
+    DCHECK_EQ(_browserState, _navigationManager->GetBrowserState());
+  }
+}
+
+- (NSString*)description {
+  return [NSString
+      stringWithFormat:
+          @"id: %@\nname: %@\nlast visit: %f\ncurrent index: %" PRIdNS
+          @"\nprevious index: %" PRIdNS "\n%@\npending: %@\nxCallback:\n%@\n",
+          _tabId,
+          self.windowName,
+          _lastVisitedTimestamp,
+          _currentNavigationIndex,
+          _previousNavigationIndex,
+          _entries,
+          _pendingEntry.get(),
+          _xCallbackParameters];
+}
+
+// Returns the current entry in the session list, or the pending entry if there
+// is a navigation in progress.
+- (CRWSessionEntry*)currentEntry {
+  if (_transientEntry)
+    return _transientEntry.get();
+  if (_pendingEntry)
+    return _pendingEntry.get();
+  return [self lastCommittedEntry];
+}
+
+// See NavigationController::GetVisibleEntry for the motivation for this
+// distinction.
+- (CRWSessionEntry*)visibleEntry {
+  if (_transientEntry)
+    return _transientEntry.get();
+  // Only return the pending_entry for:
+  //   (a) new (non-history), browser-initiated navigations, and
+  //   (b) pending unsafe navigations (while showing the interstitial)
+  // in order to prevent URL spoof attacks.
+  web::NavigationItemImpl* pendingItemImpl =
+      static_cast<web::NavigationItemImpl*>([_pendingEntry navigationItem]);
+  if (_pendingEntry &&
+      (!pendingItemImpl->is_renderer_initiated() ||
+       pendingItemImpl->IsUnsafe())) {
+    return _pendingEntry.get();
+  }
+  return [self lastCommittedEntry];
+}
+
+- (CRWSessionEntry*)pendingEntry {
+  return _pendingEntry.get();
+}
+
+- (CRWSessionEntry*)transientEntry {
+  return _transientEntry.get();
+}
+
+- (CRWSessionEntry*)lastCommittedEntry {
+  if (_currentNavigationIndex == -1)
+    return nil;
+  return [_entries objectAtIndex:_currentNavigationIndex];
+}
+
+// Returns the previous entry in the session list, or nil if there isn't any.
+- (CRWSessionEntry*)previousEntry {
+  if ((_previousNavigationIndex < 0) || (![_entries count]))
+    return nil;
+  return [_entries objectAtIndex:_previousNavigationIndex];
+}
+
+- (void)addPendingEntry:(const GURL&)url
+               referrer:(const web::Referrer&)ref
+             transition:(ui::PageTransition)trans
+      rendererInitiated:(BOOL)rendererInitiated {
+  [self discardTransientEntry];
+
+  // Don't create a new entry if it's already the same as the current entry,
+  // allowing this routine to be called multiple times in a row without issue.
+  // Note: CRWSessionController currently has the responsibility to distinguish
+  // between new navigations and history stack navigation, hence the inclusion
+  // of specific transiton type logic here, in order to make it reliable with
+  // real-world observed behavior.
+  // TODO(stuartmorgan): Fix the way changes are detected/reported elsewhere
+  // in the web layer so that this hack can be removed.
+  // Remove the workaround code from -presentSafeBrowsingWarningForResource:.
+  CRWSessionEntry* currentEntry = self.currentEntry;
+  if (currentEntry) {
+    // If the current entry is known-unsafe (and thus not visible and likely to
+    // be removed), ignore any renderer-initated updates and don't worry about
+    // sending a notification.
+    web::NavigationItem* item = [currentEntry navigationItem];
+    if (item->IsUnsafe() && rendererInitiated) {
+      return;
+    }
+    if (item->GetURL() == url &&
+        (!PageTransitionCoreTypeIs(trans, ui::PAGE_TRANSITION_FORM_SUBMIT) ||
+         PageTransitionCoreTypeIs(item->GetTransitionType(),
+                                  ui::PAGE_TRANSITION_FORM_SUBMIT) ||
+         item->IsUnsafe())) {
+      // Send the notification anyway, to preserve old behavior. It's unknown
+      // whether anything currently relies on this, but since both this whole
+      // hack and the content facade will both be going away, it's not worth
+      // trying to unwind.
+      if (_navigationManager && _navigationManager->GetFacadeDelegate()) {
+        _navigationManager->GetFacadeDelegate()->OnNavigationItemPending();
+      }
+      return;
+    }
+  }
+
+  BOOL useDesktopUserAgent = _useDesktopUserAgentForNextPendingEntry ||
+      self.currentEntry.useDesktopUserAgent;
+  _useDesktopUserAgentForNextPendingEntry = NO;
+  _pendingEntry.reset([[self sessionEntryWithURL:url
+                                        referrer:ref
+                                      transition:trans
+                             useDesktopUserAgent:useDesktopUserAgent
+                               rendererInitiated:rendererInitiated] retain]);
+
+  if (_navigationManager && _navigationManager->GetFacadeDelegate()) {
+    _navigationManager->GetFacadeDelegate()->OnNavigationItemPending();
+  }
+}
+
+- (void)updatePendingEntry:(const GURL&)url {
+  [self discardTransientEntry];
+
+  // If there is no pending entry, navigation is probably happening within the
+  // session history. Don't modify the entry list.
+  if (!_pendingEntry)
+    return;
+  web::NavigationItem* item = [_pendingEntry navigationItem];
+  if (url != item->GetURL()) {
+    item->SetURL(url);
+    item->SetVirtualURL(url);
+    // Since updates are caused by page redirects, they are renderer-initiated.
+    web::NavigationItemImpl* pendingItemImpl =
+        static_cast<web::NavigationItemImpl*>([_pendingEntry navigationItem]);
+    pendingItemImpl->set_is_renderer_initiated(true);
+    // Redirects (3xx response code), or client side navigation must change
+    // POST requests to GETs.
+    [_pendingEntry setPOSTData:nil];
+    [_pendingEntry resetHTTPHeaders];
+  }
+
+  // This should probably not be sent if the URLs matched, but that's what was
+  // done before, so preserve behavior in case something relies on it.
+  if (_navigationManager && _navigationManager->GetFacadeDelegate()) {
+    _navigationManager->GetFacadeDelegate()->OnNavigationItemPending();
+  }
+}
+
+- (void)clearForwardEntries {
+  [self discardTransientEntry];
+
+  NSInteger forwardEntryStartIndex = _currentNavigationIndex + 1;
+  DCHECK(forwardEntryStartIndex >= 0);
+
+  if (forwardEntryStartIndex >= static_cast<NSInteger>([_entries count]))
+    return;
+
+  NSRange remove = NSMakeRange(forwardEntryStartIndex,
+                               [_entries count] - forwardEntryStartIndex);
+  // Store removed items in temporary NSArray so they can be deallocated after
+  // their facades.
+  base::scoped_nsobject<NSArray> removedItems(
+      [[_entries subarrayWithRange:remove] retain]);
+  [_entries removeObjectsInRange:remove];
+  if (_previousNavigationIndex >= forwardEntryStartIndex)
+    _previousNavigationIndex = -1;
+  if (_navigationManager && _navigationManager->GetFacadeDelegate()) {
+    _navigationManager->GetFacadeDelegate()->OnNavigationItemsPruned(
+        remove.length);
+  }
+}
+
+- (void)commitPendingEntry {
+  if (_pendingEntry) {
+    [self clearForwardEntries];
+    // Add the new entry at the end.
+    [_entries addObject:_pendingEntry];
+    _previousNavigationIndex = _currentNavigationIndex;
+    _currentNavigationIndex = [_entries count] - 1;
+    // Once an entry is committed it's not renderer-initiated any more. (Matches
+    // the implementation in NavigationController.)
+    web::NavigationItemImpl* pendingItemImpl =
+        static_cast<web::NavigationItemImpl*>([_pendingEntry navigationItem]);
+    pendingItemImpl->ResetForCommit();
+    _pendingEntry.reset();
+  }
+
+  CRWSessionEntry* currentEntry = self.currentEntry;
+  web::NavigationItem* item = currentEntry.navigationItem;
+  // Update the navigation timestamp now that it's actually happened.
+  if (item)
+    item->SetTimestamp(_timeSmoother.GetSmoothedTime(base::Time::Now()));
+
+  if (_navigationManager && item)
+    _navigationManager->OnNavigationItemCommitted();
+}
+
+- (void)addTransientEntry:(const GURL&)url
+                    title:(const base::string16&)title
+                sslStatus:(const web::SSLStatus*)status {
+  // TODO(stuartmorgan): Don't do this; this is here only to preserve the old
+  // behavior from when transient entries were faked with pending entries, so
+  // any actual pending entry had to be committed. This shouldn't be necessary
+  // now, but things may rely on the old behavior and need to be fixed.
+  [self commitPendingEntry];
+
+  _transientEntry.reset(
+      [[self sessionEntryWithURL:url
+                        referrer:web::Referrer()
+                      transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
+             useDesktopUserAgent:NO
+               rendererInitiated:NO] retain]);
+
+  web::NavigationItem* navigationItem = [_transientEntry navigationItem];
+  DCHECK(navigationItem);
+  if (status)
+    navigationItem->GetSSL() = *status;
+  navigationItem->SetTitle(title);
+  navigationItem->SetTimestamp(
+      _timeSmoother.GetSmoothedTime(base::Time::Now()));
+
+  // This doesn't match upstream, but matches what we've traditionally done and
+  // will hopefully continue to be good enough for as long as we need the
+  // facade.
+  if (_navigationManager)
+    _navigationManager->OnNavigationItemChanged();
+}
+
+- (void)pushNewEntryWithURL:(const GURL&)url
+                stateObject:(NSString*)stateObject {
+  DCHECK([self currentEntry]);
+  web::NavigationItem* item = [self currentEntry].navigationItem;
+  CHECK(
+      web::history_state_util::IsHistoryStateChangeValid(item->GetURL(), url));
+  web::Referrer referrer(item->GetURL(), web::ReferrerPolicyDefault);
+  base::scoped_nsobject<CRWSessionEntry> pushedEntry(
+      [[self sessionEntryWithURL:url
+                        referrer:referrer
+                      transition:ui::PAGE_TRANSITION_LINK
+             useDesktopUserAgent:self.currentEntry.useDesktopUserAgent
+               rendererInitiated:NO] retain]);
+  pushedEntry.get().serializedStateObject = stateObject;
+  pushedEntry.get().createdFromPushState = YES;
+  web::SSLStatus& sslStatus = [self currentEntry].navigationItem->GetSSL();
+  pushedEntry.get().navigationItem->GetSSL() = sslStatus;
+
+  [self clearForwardEntries];
+  // Add the new entry at the end.
+  [_entries addObject:pushedEntry];
+  _previousNavigationIndex = _currentNavigationIndex;
+  _currentNavigationIndex = [_entries count] - 1;
+
+  if (_navigationManager)
+    _navigationManager->OnNavigationItemCommitted();
+}
+
+- (void)updateCurrentEntryWithURL:(const GURL&)url
+                      stateObject:(NSString*)stateObject {
+  DCHECK(!_transientEntry);
+  CRWSessionEntry* currentEntry = self.currentEntry;
+  currentEntry.navigationItem->SetURL(url);
+  currentEntry.serializedStateObject = stateObject;
+  // If the change is to a committed entry, notify interested parties.
+  if (currentEntry != self.pendingEntry && _navigationManager)
+    _navigationManager->OnNavigationItemChanged();
+}
+
+- (void)discardNonCommittedEntries {
+  [self discardTransientEntry];
+  _pendingEntry.reset();
+}
+
+- (void)discardTransientEntry {
+  // Keep the entry alive temporarily. There are flows that get the current
+  // entry, do some navigation operation, and then try to use that old current
+  // entry; since navigations clear the transient entry, these flows might
+  // crash. (This should be removable once more session management is handled
+  // within this class and/or NavigationManager).
+  [[_transientEntry retain] autorelease];
+  _transientEntry.reset();
+}
+
+- (BOOL)hasPendingEntry {
+  return _pendingEntry != nil;
+}
+
+- (void)copyStateFromAndPrune:(CRWSessionController*)otherSession
+                 replaceState:(BOOL)replaceState {
+  DCHECK(otherSession);
+  if (replaceState) {
+    [_entries removeAllObjects];
+    _currentNavigationIndex = -1;
+    _previousNavigationIndex = -1;
+  }
+  self.xCallbackParameters =
+      [[otherSession.xCallbackParameters copy] autorelease];
+  self.windowName = otherSession.windowName;
+  NSInteger numInitialEntries = [_entries count];
+
+  // Cycle through the entries from the other session and insert them before any
+  // entries from this session.  Do not copy anything that comes after the other
+  // session's current entry unless replaceState is true.
+  NSArray* otherEntries = [otherSession entries];
+
+  // The other session may not have any entries, in which case there is nothing
+  // to copy or prune.  The other session's currentNavigationEntry will be bogus
+  // in such cases, so ignore it and return early.
+  // TODO(rohitrao): Do we need to copy over any pending entries?  We might not
+  // add the prerendered page into the back/forward history if we don't copy
+  // pending entries.
+  if (![otherEntries count])
+    return;
+
+  NSInteger maxCopyIndex = replaceState ? [otherEntries count] - 1 :
+                                          [otherSession currentNavigationIndex];
+  for (NSInteger i = 0; i <= maxCopyIndex; ++i) {
+    [_entries insertObject:[otherEntries objectAtIndex:i] atIndex:i];
+    ++_currentNavigationIndex;
+    _previousNavigationIndex = -1;
+  }
+
+  // If this CRWSessionController has no entries initially, reset
+  // |currentNavigationIndex_| to be in bounds.
+  if (!numInitialEntries) {
+    if (replaceState) {
+      _currentNavigationIndex = [otherSession currentNavigationIndex];
+      _previousNavigationIndex = [otherSession previousNavigationIndex];
+    } else {
+      _currentNavigationIndex = maxCopyIndex;
+    }
+  }
+  DCHECK_LT((NSUInteger)_currentNavigationIndex, [_entries count]);
+}
+
+- (ui::PageTransition)transitionForIndex:(NSUInteger)index {
+  return [[_entries objectAtIndex:index] navigationItem]->GetTransitionType();
+}
+
+- (BOOL)canGoBack {
+  if ([_entries count] == 0)
+    return NO;
+
+  NSInteger lastNonRedirectedIndex = _currentNavigationIndex;
+  while (lastNonRedirectedIndex >= 0 &&
+         ui::PageTransitionIsRedirect(
+            [self transitionForIndex:lastNonRedirectedIndex])) {
+    --lastNonRedirectedIndex;
+  }
+
+  return lastNonRedirectedIndex > 0;
+}
+
+- (BOOL)canGoForward {
+  // In case there are pending entries return no since when the entry will be
+  // committed the history will be cleared from that point forward.
+  if (_pendingEntry)
+    return NO;
+  // If the current index is less than the last element, there are entries to
+  // go forward to.
+  const NSInteger count = [_entries count];
+  return count && _currentNavigationIndex < (count - 1);
+}
+
+- (void)goBack {
+  if (![self canGoBack])
+    return;
+
+  [self discardTransientEntry];
+
+  web::RecordAction(UserMetricsAction("Back"));
+  _previousNavigationIndex = _currentNavigationIndex;
+  // To stop the user getting 'stuck' on redirecting pages they weren't even
+  // aware existed, it is necessary to pass over pages that would immediately
+  // result in a redirect (the entry *before* the redirected page).
+  while (_currentNavigationIndex &&
+         [self transitionForIndex:_currentNavigationIndex] &
+             ui::PAGE_TRANSITION_IS_REDIRECT_MASK) {
+    --_currentNavigationIndex;
+  }
+
+  if (_currentNavigationIndex)
+    --_currentNavigationIndex;
+}
+
+- (void)goForward {
+  [self discardTransientEntry];
+
+  web::RecordAction(UserMetricsAction("Forward"));
+  if (_currentNavigationIndex + 1 < static_cast<NSInteger>([_entries count])) {
+    _previousNavigationIndex = _currentNavigationIndex;
+    ++_currentNavigationIndex;
+  }
+  // To reduce the chance of a redirect kicking in (truncating the history
+  // stack) we skip over any pages that might do this; we detect this by
+  // looking for when the *next* page had rediection transition type (was
+  // auto redirected to).
+  while (_currentNavigationIndex + 1 <
+         (static_cast<NSInteger>([_entries count])) &&
+         ([self transitionForIndex:_currentNavigationIndex + 1] &
+          ui::PAGE_TRANSITION_IS_REDIRECT_MASK)) {
+    ++_currentNavigationIndex;
+  }
+}
+
+- (void)goDelta:(int)delta {
+  if (delta < 0) {
+    while ([self canGoBack] && delta < 0) {
+      [self goBack];
+      ++delta;
+    }
+  } else {
+    while ([self canGoForward] && delta > 0) {
+      [self goForward];
+      --delta;
+    }
+  }
+}
+
+- (void)goToEntry:(CRWSessionEntry*)entry {
+  DCHECK(entry);
+
+  [self discardTransientEntry];
+
+  // Check that |entries_| still contains |entry|. |entry| could have been
+  // removed by -clearForwardEntries.
+  if ([_entries containsObject:entry])
+    _currentNavigationIndex = [_entries indexOfObject:entry];
+}
+
+- (void)removeEntryAtIndex:(NSInteger)index {
+  DCHECK(index < static_cast<NSInteger>([_entries count]));
+  DCHECK(index != _currentNavigationIndex);
+  DCHECK(index >= 0);
+
+  [self discardNonCommittedEntries];
+
+  [_entries removeObjectAtIndex:index];
+  if (_currentNavigationIndex > index)
+    _currentNavigationIndex--;
+  if (_previousNavigationIndex >= index)
+    _previousNavigationIndex--;
+}
+
+- (NSArray*)backwardEntries {
+  NSMutableArray* entries = [NSMutableArray array];
+  NSInteger lastNonRedirectedIndex = _currentNavigationIndex;
+  while (lastNonRedirectedIndex >= 0) {
+    CRWSessionEntry* entry = [_entries objectAtIndex:lastNonRedirectedIndex];
+    if (!ui::PageTransitionIsRedirect(
+            entry.navigationItem->GetTransitionType())) {
+      [entries addObject:entry];
+    }
+    --lastNonRedirectedIndex;
+  }
+  // Remove the currently displayed entry.
+  [entries removeObjectAtIndex:0];
+  return entries;
+}
+
+- (NSArray*)forwardEntries {
+  NSMutableArray* entries = [NSMutableArray array];
+  NSUInteger lastNonRedirectedIndex = _currentNavigationIndex + 1;
+  while (lastNonRedirectedIndex < [_entries count]) {
+    CRWSessionEntry* entry = [_entries objectAtIndex:lastNonRedirectedIndex];
+    if (!ui::PageTransitionIsRedirect(
+            entry.navigationItem->GetTransitionType())) {
+      [entries addObject:entry];
+    }
+    ++lastNonRedirectedIndex;
+  }
+  return entries;
+}
+
+- (std::vector<GURL>)currentRedirectedUrls {
+  std::vector<GURL> results;
+  if (_pendingEntry) {
+    web::NavigationItem* item = [_pendingEntry navigationItem];
+    results.push_back(item->GetURL());
+
+    if (!ui::PageTransitionIsRedirect(item->GetTransitionType()))
+      return results;
+  }
+
+  if (![_entries count])
+    return results;
+
+  NSInteger index = _currentNavigationIndex;
+  // Add urls in the redirected entries.
+  while (index >= 0) {
+    web::NavigationItem* item = [[_entries objectAtIndex:index] navigationItem];
+    if (!ui::PageTransitionIsRedirect(item->GetTransitionType()))
+      break;
+    results.push_back(item->GetURL());
+    --index;
+  }
+  // Add the last non-redirected entry.
+  if (index >= 0) {
+    web::NavigationItem* item = [[_entries objectAtIndex:index] navigationItem];
+    results.push_back(item->GetURL());
+  }
+  return results;
+}
+
+- (BOOL)isPushStateNavigationBetweenEntry:(CRWSessionEntry*)firstEntry
+                                 andEntry:(CRWSessionEntry*)secondEntry {
+  DCHECK(firstEntry);
+  DCHECK(secondEntry);
+  if (firstEntry == secondEntry)
+    return NO;
+  NSUInteger firstIndex = [_entries indexOfObject:firstEntry];
+  NSUInteger secondIndex = [_entries indexOfObject:secondEntry];
+  if (firstIndex == NSNotFound || secondIndex == NSNotFound)
+    return NO;
+  NSUInteger startIndex = firstIndex < secondIndex ? firstIndex : secondIndex;
+  NSUInteger endIndex = firstIndex < secondIndex ? secondIndex : firstIndex;
+
+  for (NSUInteger i = startIndex + 1; i <= endIndex; i++) {
+    CRWSessionEntry* entry = [_entries objectAtIndex:i];
+    // Every entry in the sequence has to be created from a pushState() call.
+    if (!entry.createdFromPushState)
+      return NO;
+    // Every entry in the sequence has to have a URL that could have been
+    // created from a pushState() call.
+    if (!web::history_state_util::IsHistoryStateChangeValid(
+            firstEntry.navigationItem->GetURL(),
+            entry.navigationItem->GetURL()))
+      return NO;
+  }
+  return YES;
+}
+
+- (CRWSessionEntry*)lastUserEntry {
+  if (![_entries count])
+    return nil;
+
+  NSInteger index = _currentNavigationIndex;
+  // This will return the first session entry if all other entries are
+  // redirects, regardless of the transition state of the first entry.
+  while (index > 0 &&
+         [self transitionForIndex:index] &
+         ui::PAGE_TRANSITION_IS_REDIRECT_MASK) {
+    --index;
+  }
+  return [_entries objectAtIndex:index];
+}
+
+- (void)useDesktopUserAgentForNextPendingEntry {
+  if (_pendingEntry)
+    [_pendingEntry setUseDesktopUserAgent:YES];
+  else
+    _useDesktopUserAgentForNextPendingEntry = YES;
+}
+
+#pragma mark -
+#pragma mark Private methods
+
+- (NSString*)uniqueID {
+  CFUUIDRef uuidRef = CFUUIDCreate(NULL);
+  CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
+  CFRelease(uuidRef);
+  NSString* uuid = [NSString stringWithString:(NSString*)uuidStringRef];
+  CFRelease(uuidStringRef);
+  return uuid;
+}
+
+- (CRWSessionEntry*)sessionEntryWithURL:(const GURL&)url
+                               referrer:(const web::Referrer&)referrer
+                             transition:(ui::PageTransition)transition
+                    useDesktopUserAgent:(BOOL)useDesktopUserAgent
+                      rendererInitiated:(BOOL)rendererInitiated {
+  GURL loaded_url(url);
+  web::BrowserURLRewriter::GetInstance()->RewriteURLIfNecessary(&loaded_url,
+                                                                _browserState);
+  return [[[CRWSessionEntry alloc] initWithUrl:loaded_url
+                                      referrer:referrer
+                                    transition:transition
+                           useDesktopUserAgent:useDesktopUserAgent
+                             rendererInitiated:rendererInitiated] autorelease];
+}
+
+@end
diff --git a/ios/web/navigation/crw_session_controller_unittest.mm b/ios/web/navigation/crw_session_controller_unittest.mm
new file mode 100644
index 0000000..79a6dff
--- /dev/null
+++ b/ios/web/navigation/crw_session_controller_unittest.mm
@@ -0,0 +1,1022 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Foundation/Foundation.h>
+
+#include "base/logging.h"
+#import "base/mac/scoped_nsobject.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/sys_string_conversions.h"
+#import "ios/web/navigation/crw_session_controller+private_constructors.h"
+#import "ios/web/navigation/crw_session_controller.h"
+#include "ios/web/navigation/crw_session_entry.h"
+#include "ios/web/navigation/navigation_item_impl.h"
+#include "ios/web/public/referrer.h"
+#include "ios/web/public/test/test_browser_state.h"
+#import "net/base/mac/url_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+@interface CRWSessionController (Testing)
+- (const GURL&)URLForSessionAtIndex:(NSUInteger)index;
+- (const GURL&)currentURL;
+@end
+
+@implementation CRWSessionController (Testing)
+- (const GURL&)URLForSessionAtIndex:(NSUInteger)index {
+  CRWSessionEntry* entry =
+      static_cast<CRWSessionEntry*>([self.entries objectAtIndex:index]);
+  return entry.navigationItem->GetURL();
+}
+
+- (const GURL&)currentURL {
+  DCHECK([self currentEntry]);
+  return [self currentEntry].navigationItem->GetURL();
+}
+@end
+
+namespace {
+
+class CRWSessionControllerTest : public PlatformTest {
+ protected:
+  void SetUp() override {
+    session_controller_.reset(
+        [[CRWSessionController alloc] initWithWindowName:@"test window"
+                                                openerId:@"opener"
+                                             openedByDOM:NO
+                                   openerNavigationIndex:0
+                                            browserState:&browser_state_]);
+  }
+
+  web::Referrer MakeReferrer(std::string url) {
+    return web::Referrer(GURL(url), web::ReferrerPolicyDefault);
+  }
+
+  web::TestBrowserState browser_state_;
+  base::scoped_nsobject<CRWSessionController> session_controller_;
+};
+
+TEST_F(CRWSessionControllerTest, InitWithWindowName) {
+  EXPECT_NSEQ(@"test window", [session_controller_ windowName]);
+  EXPECT_NSEQ(@"opener", [session_controller_ openerId]);
+  EXPECT_FALSE([session_controller_ isOpenedByDOM]);
+  EXPECT_EQ(0U, [[session_controller_ entries] count]);
+  EXPECT_EQ(nil, [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, AddPendingEntry) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+
+  EXPECT_EQ(0U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ currentURL]);
+}
+
+TEST_F(CRWSessionControllerTest, AddPendingEntryWithCommittedEntries) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.committed.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+
+  EXPECT_EQ(1U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.committed.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ currentURL]);
+}
+
+TEST_F(CRWSessionControllerTest, AddPendingEntryOverriding) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_
+        addPendingEntry:GURL("http://www.another.url.com")
+               referrer:MakeReferrer("http://www.another.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+
+  EXPECT_EQ(0U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.another.url.com/"),
+      [session_controller_ currentURL]);
+}
+
+TEST_F(CRWSessionControllerTest, AddPendingEntryAndCommit) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_EQ(1U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:0U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, AddPendingEntryOverridingAndCommit) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_
+        addPendingEntry:GURL("http://www.another.url.com")
+               referrer:MakeReferrer("http://www.another.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_EQ(1U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.another.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:0U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, AddPendingEntryAndCommitMultiple) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_
+        addPendingEntry:GURL("http://www.another.url.com")
+               referrer:MakeReferrer("http://www.another.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_EQ(2U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      GURL("http://www.another.url.com/"),
+      [session_controller_ URLForSessionAtIndex:1U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:1U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, AddPendingEntryAndDiscard) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ discardNonCommittedEntries];
+
+  EXPECT_EQ(0U, [[session_controller_ entries] count]);
+  EXPECT_EQ(nil, [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, AddPendingEntryAndDiscardAndAddAndCommit) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ discardNonCommittedEntries];
+
+  [session_controller_
+        addPendingEntry:GURL("http://www.another.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_EQ(1U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.another.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:0U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, AddPendingEntryAndCommitAndAddAndDiscard) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_
+        addPendingEntry:GURL("http://www.another.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ discardNonCommittedEntries];
+
+  EXPECT_EQ(1U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:0U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest,
+       CommitPendingEntryWithoutPendingOrCommittedEntry) {
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_EQ(0U, [[session_controller_ entries] count]);
+  EXPECT_EQ(nil, [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest,
+       CommitPendingEntryWithoutPendingEntryWithCommittedEntry) {
+  // Setup committed entry
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  // Commit pending entry when there is no such one
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_EQ(1U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:0U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest,
+       DiscardPendingEntryWithoutPendingOrCommittedEntry) {
+  [session_controller_ discardNonCommittedEntries];
+
+  EXPECT_EQ(0U, [[session_controller_ entries] count]);
+  EXPECT_EQ(nil, [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest,
+       DiscardPendingEntryWithoutPendingEntryWithCommittedEntry) {
+  // Setup committed entry
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  // Discard noncommitted entries when there is no such one
+  [session_controller_ discardNonCommittedEntries];
+
+  EXPECT_EQ(1U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:0U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, UpdatePendingEntryWithoutPendingEntry) {
+  [session_controller_
+       updatePendingEntry:GURL("http://www.another.url.com")];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_EQ(0U, [[session_controller_ entries] count]);
+  EXPECT_EQ(nil, [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, UpdatePendingEntryWithPendingEntry) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_
+       updatePendingEntry:GURL("http://www.another.url.com")];
+
+  EXPECT_EQ(
+      GURL("http://www.another.url.com/"),
+      [session_controller_ currentURL]);
+}
+
+TEST_F(CRWSessionControllerTest,
+       UpdatePendingEntryWithPendingEntryAlreadyCommited) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+       updatePendingEntry:GURL("http://www.another.url.com")];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_EQ(1U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:0U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, GoBackWithoutCommitedEntry) {
+  [session_controller_ goBack];
+
+  EXPECT_EQ(0U, [[session_controller_ entries] count]);
+  EXPECT_EQ(nil, [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, GoBackWithSingleCommitedEntry) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_ goBack];
+
+  EXPECT_EQ(1U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:0U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, GoBackFromTheEnd) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url2.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_ goBack];
+
+  EXPECT_EQ(2U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      GURL("http://www.url2.com/"),
+      [session_controller_ URLForSessionAtIndex:1U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:0U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, GoBackFromTheBeginning) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url2.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_ goBack];
+  [session_controller_ goBack];
+
+  EXPECT_EQ(2U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      GURL("http://www.url2.com/"),
+      [session_controller_ URLForSessionAtIndex:1U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:0U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, GoBackFromTheMiddle) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url2.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url3.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url4.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_ goBack];
+  [session_controller_ goBack];
+
+  EXPECT_EQ(4U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      GURL("http://www.url2.com/"),
+      [session_controller_ URLForSessionAtIndex:1U]);
+  EXPECT_EQ(
+      GURL("http://www.url3.com/"),
+      [session_controller_ URLForSessionAtIndex:2U]);
+  EXPECT_EQ(
+      GURL("http://www.url4.com/"),
+      [session_controller_ URLForSessionAtIndex:3U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:1U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, GoBackAndRemove) {
+  [session_controller_
+   addPendingEntry:GURL("http://www.url.com")
+   referrer:MakeReferrer("http://www.referer.com")
+   transition:ui::PAGE_TRANSITION_TYPED
+   rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+   addPendingEntry:GURL("http://www.url2.com")
+   referrer:MakeReferrer("http://www.referer.com")
+   transition:ui::PAGE_TRANSITION_TYPED
+   rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_ goBack];
+  [session_controller_ removeEntryAtIndex:1];
+
+  EXPECT_EQ(1U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+            GURL("http://www.url.com/"),
+            [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+            [[session_controller_ entries] objectAtIndex:0U],
+            [session_controller_ currentEntry]);
+  EXPECT_EQ([session_controller_ currentEntry],
+            [session_controller_ previousEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, GoForwardWithoutCommitedEntry) {
+  [session_controller_ goForward];
+
+  EXPECT_EQ(0U, [[session_controller_ entries] count]);
+  EXPECT_EQ(nil, [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, GoForwardWithSingleCommitedEntry) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_ goForward];
+
+  EXPECT_EQ(1U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:0U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, GoForewardFromTheEnd) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url2.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_ goForward];
+
+  EXPECT_EQ(2U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      GURL("http://www.url2.com/"),
+      [session_controller_ URLForSessionAtIndex:1U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:1U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, GoForewardFromTheBeginning) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url2.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_ goBack];
+  [session_controller_ goForward];
+
+  EXPECT_EQ(2U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      GURL("http://www.url2.com/"),
+      [session_controller_ URLForSessionAtIndex:1U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:1U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, GoForwardFromTheMiddle) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url2.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url3.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url4.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  [session_controller_ goBack];
+  [session_controller_ goBack];
+  [session_controller_ goForward];
+
+  EXPECT_EQ(4U, [[session_controller_ entries] count]);
+  EXPECT_EQ(
+      GURL("http://www.url.com/"),
+      [session_controller_ URLForSessionAtIndex:0U]);
+  EXPECT_EQ(
+      GURL("http://www.url2.com/"),
+      [session_controller_ URLForSessionAtIndex:1U]);
+  EXPECT_EQ(
+      GURL("http://www.url3.com/"),
+      [session_controller_ URLForSessionAtIndex:2U]);
+  EXPECT_EQ(
+      GURL("http://www.url4.com/"),
+      [session_controller_ URLForSessionAtIndex:3U]);
+  EXPECT_EQ(
+      [[session_controller_ entries] objectAtIndex:2U],
+      [session_controller_ currentEntry]);
+}
+
+TEST_F(CRWSessionControllerTest, CanGoBackWithoutCommitedEntry) {
+  EXPECT_FALSE([session_controller_ canGoBack]);
+}
+
+TEST_F(CRWSessionControllerTest, CanGoBackWithSingleCommitedEntry) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_FALSE([session_controller_ canGoBack]);
+}
+
+TEST_F(CRWSessionControllerTest, CanGoBackWithMultipleCommitedEntries) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url1.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url2.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_TRUE([session_controller_ canGoBack]);
+
+  [session_controller_ goBack];
+  EXPECT_TRUE([session_controller_ canGoBack]);
+
+  [session_controller_ goBack];
+  EXPECT_FALSE([session_controller_ canGoBack]);
+
+  [session_controller_ goBack];
+  EXPECT_FALSE([session_controller_ canGoBack]);
+
+  [session_controller_ goForward];
+  EXPECT_TRUE([session_controller_ canGoBack]);
+}
+
+TEST_F(CRWSessionControllerTest, CanGoForwardWithoutCommitedEntry) {
+  EXPECT_FALSE([session_controller_ canGoBack]);
+}
+
+TEST_F(CRWSessionControllerTest, CanGoForwardWithSingleCommitedEntry) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_FALSE([session_controller_ canGoBack]);
+}
+
+TEST_F(CRWSessionControllerTest, CanGoForwardWithMultipleCommitedEntries) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url1.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url2.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_FALSE([session_controller_ canGoForward]);
+
+  [session_controller_ goBack];
+  EXPECT_TRUE([session_controller_ canGoForward]);
+
+  [session_controller_ goBack];
+  EXPECT_TRUE([session_controller_ canGoForward]);
+
+  [session_controller_ goForward];
+  EXPECT_TRUE([session_controller_ canGoForward]);
+
+  [session_controller_ goForward];
+  EXPECT_FALSE([session_controller_ canGoForward]);
+}
+
+// Helper to create a NavigationItem. Caller is responsible for freeing
+// the memory.
+web::NavigationItem* CreateNavigationItem(const std::string& url,
+                                          const std::string& referrer,
+                                          NSString* title) {
+  web::Referrer referrer_object(GURL(referrer),
+                                web::ReferrerPolicyDefault);
+  web::NavigationItemImpl* navigation_item = new web::NavigationItemImpl();
+  navigation_item->SetURL(GURL(url));
+  navigation_item->SetReferrer(referrer_object);
+  navigation_item->SetTitle(base::SysNSStringToUTF16(title));
+  navigation_item->SetTransitionType(ui::PAGE_TRANSITION_TYPED);
+
+  return navigation_item;
+}
+
+TEST_F(CRWSessionControllerTest, CreateWithEmptyNavigations) {
+  ScopedVector<web::NavigationItem> items;
+  base::scoped_nsobject<CRWSessionController> controller(
+      [[CRWSessionController alloc] initWithNavigationItems:items.Pass()
+                                               currentIndex:0
+                                               browserState:&browser_state_]);
+  EXPECT_EQ(controller.get().entries.count, 0U);
+  EXPECT_EQ(controller.get().currentNavigationIndex, -1);
+  EXPECT_EQ(controller.get().previousNavigationIndex, -1);
+  EXPECT_FALSE(controller.get().currentEntry);
+}
+
+TEST_F(CRWSessionControllerTest, CreateWithNavList) {
+  ScopedVector<web::NavigationItem> items;
+  items.push_back(CreateNavigationItem("http://www.google.com",
+                                       "http://www.referrer.com", @"Google"));
+  items.push_back(CreateNavigationItem("http://www.yahoo.com",
+                                       "http://www.google.com", @"Yahoo"));
+  items.push_back(CreateNavigationItem("http://www.espn.com",
+                                       "http://www.nothing.com", @"ESPN"));
+  base::scoped_nsobject<CRWSessionController> controller(
+      [[CRWSessionController alloc] initWithNavigationItems:items.Pass()
+                                               currentIndex:1
+                                               browserState:&browser_state_]);
+
+  EXPECT_EQ(controller.get().entries.count, 3U);
+  EXPECT_EQ(controller.get().currentNavigationIndex, 1);
+  EXPECT_EQ(controller.get().previousNavigationIndex, -1);
+  // Sanity check the current entry, the CRWSessionEntry unit test will ensure
+  // the entire object is created properly.
+  CRWSessionEntry* current_entry = controller.get().currentEntry;
+  EXPECT_EQ(current_entry.navigationItem->GetURL(),
+            GURL("http://www.yahoo.com"));
+  EXPECT_EQ([[controller openerId] length], 0UL);
+}
+
+TEST_F(CRWSessionControllerTest, PreviousNavigationEntry) {
+  [session_controller_
+        addPendingEntry:GURL("http://www.url.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url1.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_
+        addPendingEntry:GURL("http://www.url2.com")
+               referrer:MakeReferrer("http://www.referer.com")
+             transition:ui::PAGE_TRANSITION_TYPED
+      rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_EQ(session_controller_.get().previousNavigationIndex, 1);
+
+  [session_controller_ goBack];
+  EXPECT_EQ(session_controller_.get().previousNavigationIndex, 2);
+
+  [session_controller_ goBack];
+  EXPECT_EQ(session_controller_.get().previousNavigationIndex, 1);
+
+  [session_controller_ goForward];
+  EXPECT_EQ(session_controller_.get().previousNavigationIndex, 0);
+
+  [session_controller_ goForward];
+  EXPECT_EQ(session_controller_.get().previousNavigationIndex, 1);
+}
+
+TEST_F(CRWSessionControllerTest, PushNewEntry) {
+  ScopedVector<web::NavigationItem> items;
+  items.push_back(CreateNavigationItem("http://www.firstpage.com",
+                                       "http://www.starturl.com", @"First"));
+  items.push_back(CreateNavigationItem("http://www.secondpage.com",
+                                       "http://www.firstpage.com", @"Second"));
+  items.push_back(CreateNavigationItem("http://www.thirdpage.com",
+                                       "http://www.secondpage.com", @"Third"));
+  base::scoped_nsobject<CRWSessionController> controller(
+      [[CRWSessionController alloc] initWithNavigationItems:items.Pass()
+                                               currentIndex:0
+                                               browserState:&browser_state_]);
+
+  GURL pushPageGurl1("http://www.firstpage.com/#push1");
+  NSString* stateObject1 = @"{'foo': 1}";
+  [controller pushNewEntryWithURL:pushPageGurl1 stateObject:stateObject1];
+  CRWSessionEntry* pushedEntry = [controller currentEntry];
+  NSUInteger expectedCount = 2;
+  EXPECT_EQ(expectedCount, controller.get().entries.count);
+  EXPECT_EQ(pushPageGurl1, pushedEntry.navigationItem->GetURL());
+  EXPECT_TRUE(pushedEntry.createdFromPushState);
+  EXPECT_NSEQ(stateObject1, pushedEntry.serializedStateObject);
+  EXPECT_EQ(GURL("http://www.firstpage.com/"),
+            pushedEntry.navigationItem->GetReferrer().url);
+
+  // Add another new entry and check size and fields again.
+  GURL pushPageGurl2("http://www.firstpage.com/push2");
+  [controller pushNewEntryWithURL:pushPageGurl2 stateObject:nil];
+  pushedEntry = [controller currentEntry];
+  expectedCount = 3;
+  EXPECT_EQ(expectedCount, controller.get().entries.count);
+  EXPECT_EQ(pushPageGurl2, pushedEntry.navigationItem->GetURL());
+  EXPECT_TRUE(pushedEntry.createdFromPushState);
+  EXPECT_EQ(nil, pushedEntry.serializedStateObject);
+  EXPECT_EQ(pushPageGurl1, pushedEntry.navigationItem->GetReferrer().url);
+}
+
+TEST_F(CRWSessionControllerTest, IsPushStateNavigation) {
+  ScopedVector<web::NavigationItem> items;
+  items.push_back(
+      CreateNavigationItem("http://foo.com", "http://google.com", @"First"));
+  // Push state navigation.
+  items.push_back(
+      CreateNavigationItem("http://foo.com#bar", "http://foo.com", @"Second"));
+  items.push_back(CreateNavigationItem("http://google.com",
+                                       "http://foo.com#bar", @"Third"));
+  items.push_back(
+      CreateNavigationItem("http://foo.com", "http://google.com", @"Fourth"));
+  // Push state navigation.
+  items.push_back(
+      CreateNavigationItem("http://foo.com/bar", "http://foo.com", @"Fifth"));
+  // Push state navigation.
+  items.push_back(CreateNavigationItem("http://foo.com/bar#bar",
+                                       "http://foo.com/bar", @"Sixth"));
+  base::scoped_nsobject<CRWSessionController> controller(
+      [[CRWSessionController alloc] initWithNavigationItems:items.Pass()
+                                               currentIndex:0
+                                               browserState:&browser_state_]);
+  CRWSessionEntry* entry0 = [controller.get().entries objectAtIndex:0];
+  CRWSessionEntry* entry1 = [controller.get().entries objectAtIndex:1];
+  CRWSessionEntry* entry2 = [controller.get().entries objectAtIndex:2];
+  CRWSessionEntry* entry3 = [controller.get().entries objectAtIndex:3];
+  CRWSessionEntry* entry4 = [controller.get().entries objectAtIndex:4];
+  CRWSessionEntry* entry5 = [controller.get().entries objectAtIndex:5];
+  entry1.createdFromPushState = YES;
+  entry4.createdFromPushState = YES;
+  entry5.createdFromPushState = YES;
+
+  EXPECT_TRUE(
+      [controller isPushStateNavigationBetweenEntry:entry0 andEntry:entry1]);
+  EXPECT_TRUE(
+      [controller isPushStateNavigationBetweenEntry:entry5 andEntry:entry3]);
+  EXPECT_TRUE(
+      [controller isPushStateNavigationBetweenEntry:entry4 andEntry:entry3]);
+  EXPECT_FALSE(
+      [controller isPushStateNavigationBetweenEntry:entry1 andEntry:entry2]);
+  EXPECT_FALSE(
+      [controller isPushStateNavigationBetweenEntry:entry0 andEntry:entry5]);
+  EXPECT_FALSE(
+      [controller isPushStateNavigationBetweenEntry:entry2 andEntry:entry4]);
+}
+
+TEST_F(CRWSessionControllerTest, UpdateCurrentEntry) {
+  ScopedVector<web::NavigationItem> items;
+  items.push_back(CreateNavigationItem("http://www.firstpage.com",
+                                       "http://www.starturl.com", @"First"));
+  items.push_back(CreateNavigationItem("http://www.secondpage.com",
+                                       "http://www.firstpage.com", @"Second"));
+  items.push_back(CreateNavigationItem("http://www.thirdpage.com",
+                                       "http://www.secondpage.com", @"Third"));
+  base::scoped_nsobject<CRWSessionController> controller(
+      [[CRWSessionController alloc] initWithNavigationItems:items.Pass()
+                                               currentIndex:0
+                                               browserState:&browser_state_]);
+
+  GURL replacePageGurl1("http://www.firstpage.com/#replace1");
+  NSString* stateObject1 = @"{'foo': 1}";
+
+  // Replace current entry and check the size of history and fields of the
+  // modified entry.
+  [controller updateCurrentEntryWithURL:replacePageGurl1
+                            stateObject:stateObject1];
+  CRWSessionEntry* replacedEntry = [controller currentEntry];
+  NSUInteger expectedCount = 3;
+  EXPECT_EQ(expectedCount, controller.get().entries.count);
+  EXPECT_EQ(replacePageGurl1, replacedEntry.navigationItem->GetURL());
+  EXPECT_FALSE(replacedEntry.createdFromPushState);
+  EXPECT_NSEQ(stateObject1, replacedEntry.serializedStateObject);
+  EXPECT_EQ(GURL("http://www.starturl.com/"),
+            replacedEntry.navigationItem->GetReferrer().url);
+
+  // Replace current entry and check size and fields again.
+  GURL replacePageGurl2("http://www.firstpage.com/#replace2");
+  [controller.get() updateCurrentEntryWithURL:replacePageGurl2 stateObject:nil];
+  replacedEntry = [controller currentEntry];
+  EXPECT_EQ(expectedCount, controller.get().entries.count);
+  EXPECT_EQ(replacePageGurl2, replacedEntry.navigationItem->GetURL());
+  EXPECT_FALSE(replacedEntry.createdFromPushState);
+  EXPECT_NSEQ(nil, replacedEntry.serializedStateObject);
+  EXPECT_EQ(GURL("http://www.starturl.com/"),
+            replacedEntry.navigationItem->GetReferrer().url);
+}
+
+TEST_F(CRWSessionControllerTest, TestBackwardForwardEntries) {
+  [session_controller_ addPendingEntry:GURL("http://www.example.com/0")
+                             referrer:MakeReferrer("http://www.example.com/a")
+                           transition:ui::PAGE_TRANSITION_LINK
+                    rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_ addPendingEntry:GURL("http://www.example.com/1")
+                             referrer:MakeReferrer("http://www.example.com/b")
+                           transition:ui::PAGE_TRANSITION_LINK
+                    rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_ addPendingEntry:GURL("http://www.example.com/redirect")
+                             referrer:MakeReferrer("http://www.example.com/r")
+                           transition:ui::PAGE_TRANSITION_IS_REDIRECT_MASK
+                    rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_ addPendingEntry:GURL("http://www.example.com/2")
+                             referrer:MakeReferrer("http://www.example.com/c")
+                           transition:ui::PAGE_TRANSITION_LINK
+                    rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+
+  EXPECT_EQ(3, session_controller_.get().currentNavigationIndex);
+  NSArray* backEntries = [session_controller_ backwardEntries];
+  EXPECT_EQ(2U, [backEntries count]);
+  EXPECT_EQ(0U, [[session_controller_ forwardEntries] count]);
+  EXPECT_EQ("http://www.example.com/1",
+            [[backEntries objectAtIndex:0] navigationItem]->GetURL().spec());
+
+  [session_controller_ goBack];
+  EXPECT_EQ(1U, [[session_controller_ backwardEntries] count]);
+  EXPECT_EQ(1U, [[session_controller_ forwardEntries] count]);
+
+  [session_controller_ goBack];
+  NSArray* forwardEntries = [session_controller_ forwardEntries];
+  EXPECT_EQ(0U, [[session_controller_ backwardEntries] count]);
+  EXPECT_EQ(2U, [forwardEntries count]);
+  EXPECT_EQ("http://www.example.com/2",
+            [[forwardEntries objectAtIndex:1] navigationItem]->GetURL().spec());
+}
+
+TEST_F(CRWSessionControllerTest, GoToEntry) {
+  [session_controller_ addPendingEntry:GURL("http://www.example.com/0")
+                             referrer:MakeReferrer("http://www.example.com/a")
+                           transition:ui::PAGE_TRANSITION_LINK
+                    rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_ addPendingEntry:GURL("http://www.example.com/1")
+                             referrer:MakeReferrer("http://www.example.com/b")
+                           transition:ui::PAGE_TRANSITION_LINK
+                    rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_ addPendingEntry:GURL("http://www.example.com/redirect")
+                             referrer:MakeReferrer("http://www.example.com/r")
+                           transition:ui::PAGE_TRANSITION_IS_REDIRECT_MASK
+                    rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  [session_controller_ addPendingEntry:GURL("http://www.example.com/2")
+                             referrer:MakeReferrer("http://www.example.com/c")
+                           transition:ui::PAGE_TRANSITION_LINK
+                    rendererInitiated:NO];
+  [session_controller_ commitPendingEntry];
+  EXPECT_EQ(3, session_controller_.get().currentNavigationIndex);
+
+  CRWSessionEntry* entry1 = [session_controller_.get().entries objectAtIndex:1];
+  [session_controller_ goToEntry:entry1];
+  EXPECT_EQ(1, session_controller_.get().currentNavigationIndex);
+
+  // Remove an entry and attempt to go it. Ensure it outlives the removal.
+  base::scoped_nsobject<CRWSessionEntry> entry3(
+      [[session_controller_.get().entries objectAtIndex:3] retain]);
+  [session_controller_ removeEntryAtIndex:3];
+  [session_controller_ goToEntry:entry3];
+  EXPECT_EQ(1, session_controller_.get().currentNavigationIndex);
+}
+
+}  // anonymous namespace
diff --git a/ios/web/navigation/crw_session_entry.h b/ios/web/navigation/crw_session_entry.h
new file mode 100644
index 0000000..00d8ec830
--- /dev/null
+++ b/ios/web/navigation/crw_session_entry.h
@@ -0,0 +1,65 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_NAVIGATION_CRW_SESSION_ENTRY_H_
+#define IOS_WEB_NAVIGATION_CRW_SESSION_ENTRY_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "ui/base/page_transition_types.h"
+#include "url/gurl.h"
+
+namespace web {
+class NavigationItem;
+struct Referrer;
+}
+
+// A CRWSessionEntry is similar to a NavigationEntry object in desktop Chrome.
+// It maintains the information needed to save/restore a single entry in session
+// history (i.e., one site) for a tab. A tab may have multiple of these objects
+// comprising its entire session history.
+// TODO(rohitrao): Fold CRWSessionEntry's logic into NavigationItem.
+@interface CRWSessionEntry : NSObject<NSCoding, NSCopying>
+
+@property(nonatomic, assign) NSInteger index;
+@property(nonatomic, readonly) const GURL& originalUrl;
+@property(nonatomic, assign) BOOL useDesktopUserAgent;
+@property(nonatomic, assign) BOOL usedDataReductionProxy;
+@property(nonatomic, retain) NSString* serializedStateObject;
+@property(nonatomic, assign) BOOL createdFromPushState;
+@property(nonatomic, retain) NSData* POSTData;
+@property(nonatomic, readonly) NSDictionary* httpHeaders;
+@property(nonatomic, assign) BOOL skipResubmitDataConfirmation;
+
+// Initialize the session entry with the given url.
+- (id)initWithUrl:(const GURL&)url
+               referrer:(const web::Referrer&)referrer
+             transition:(ui::PageTransition)transition
+    useDesktopUserAgent:(BOOL)useDesktopUserAgent
+      rendererInitiated:(BOOL)rendererInitiated;
+
+// Initialize the session entry with the given NavigationItem.
+- (id)initWithNavigationItem:(scoped_ptr<web::NavigationItem>)item
+                       index:(int)index;
+
+// Returns a pointer to the NavigationItem associated with this CRWSessionEntry.
+// Eventually, this will replace CRWSessionEntry entirely.
+- (web::NavigationItem*)navigationItem;
+
+// Adds headers from |moreHTTPHeaders| to |httpHeaders|; existing headers with
+// the same key will be overridden.
+- (void)addHTTPHeaders:(NSDictionary*)moreHTTPHeaders;
+
+// Removes the header for the given key from |httpHeaders|.
+- (void)removeHTTPHeaderForKey:(NSString*)key;
+
+// Resets |httpHeaders| to nil.
+- (void)resetHTTPHeaders;
+
+@end
+
+#endif  // IOS_WEB_NAVIGATION_CRW_SESSION_ENTRY_H_
diff --git a/ios/web/navigation/crw_session_entry.mm b/ios/web/navigation/crw_session_entry.mm
new file mode 100644
index 0000000..c786499
--- /dev/null
+++ b/ios/web/navigation/crw_session_entry.mm
@@ -0,0 +1,318 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web/navigation/crw_session_entry.h"
+
+#include "base/mac/objc_property_releaser.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/sys_string_conversions.h"
+#include "ios/web/navigation/navigation_item_impl.h"
+#include "ios/web/navigation/nscoder_util.h"
+#include "ios/web/public/navigation_item.h"
+#include "ios/web/public/web_state/page_scroll_state.h"
+#import "net/base/mac/url_conversions.h"
+
+namespace {
+// Keys used to serialize web::PageScrollState properties.
+NSString* const kScrollOffsetXKey = @"scrollX";
+NSString* const kScrollOffsetYKey = @"scrollY";
+NSString* const kMinimumZoomScaleKey = @"minZoom";
+NSString* const kMaximumZoomScaleKey = @"maxZoom";
+NSString* const kZoomScaleKey = @"zoom";
+}
+
+@interface CRWSessionEntry () {
+  // The index in the CRWSessionController.
+  //
+  // This is used when determining the selected CRWSessionEntry and only useful
+  // to the SessionServiceIOS.
+  NSInteger _index;
+
+  // The original URL of the page.  In cases where a redirect occurred, |url_|
+  // will contain the final post-redirect URL, and |originalUrl_| will contain
+  // the pre-redirect URL.  This field is not persisted to disk.
+  GURL _originalUrl;
+
+  // Headers passed along with the request. For POST requests, these are
+  // persisted, to be able to resubmit them. Some specialized non-POST requests
+  // may also pass custom headers.
+  base::scoped_nsobject<NSMutableDictionary> _httpHeaders;
+
+  // Data submitted with a POST request, persisted for resubmits.
+  NSData* _POSTData;
+
+  // Serialized representation of the state object that was used in conjunction
+  // with a JavaScript window.history.pushState() or
+  // window.history.replaceState() call that created or modified this
+  // CRWSessionEntry. Intended to be used for JavaScript history operations and
+  // will be nil in most cases.
+  NSString* _serializedStateObject;
+
+  // Whether or not this entry was created by calling history.pushState().
+  BOOL _createdFromPushState;
+
+  // If |YES| use a desktop user agent in HTTP requests and UIWebView.
+  BOOL _useDesktopUserAgent;
+
+  // If |YES| the page was last fetched through the data reduction proxy.
+  BOOL _usedDataReductionProxy;
+
+  // Whether or not to bypass showing the resubmit data confirmation when
+  // loading a POST request. Set to YES for browser-generated POST requests such
+  // as search-by-image requests.
+  BOOL _skipResubmitDataConfirmation;
+
+  // The NavigationItemImpl corresponding to this CRWSessionEntry.
+  // TODO(stuartmorgan): Move ownership to NavigationManagerImpl.
+  scoped_ptr<web::NavigationItemImpl> _navigationItem;
+
+  base::mac::ObjCPropertyReleaser _propertyReleaser_CRWSessionEntry;
+}
+// Redefine originalUrl to be read-write.
+@property(nonatomic, readwrite) const GURL& originalUrl;
+
+// Converts a serialized NSDictionary to a web::PageScrollState.
++ (web::PageScrollState)scrollStateFromDictionary:(NSDictionary*)dictionary;
+// Serializes a web::PageScrollState to an NSDictionary.
++ (NSDictionary*)dictionaryFromScrollState:
+    (const web::PageScrollState&)scrollState;
+// Returns a readable description of |scrollState|.
++ (NSString*)scrollStateDescription:(const web::PageScrollState&)scrollState;
+@end
+
+@implementation CRWSessionEntry
+
+@synthesize POSTData = _POSTData;
+@synthesize originalUrl = _originalUrl;
+@synthesize useDesktopUserAgent = _useDesktopUserAgent;
+@synthesize usedDataReductionProxy = _usedDataReductionProxy;
+@synthesize index = _index;
+@synthesize serializedStateObject = _serializedStateObject;
+@synthesize createdFromPushState = _createdFromPushState;
+@synthesize skipResubmitDataConfirmation = _skipResubmitDataConfirmation;
+
+// Creates a new session entry. These may be nil.
+- (instancetype)initWithUrl:(const GURL&)url
+                   referrer:(const web::Referrer&)referrer
+                 transition:(ui::PageTransition)transition
+        useDesktopUserAgent:(BOOL)useDesktopUserAgent
+          rendererInitiated:(BOOL)rendererInitiated {
+  self = [super init];
+  if (self) {
+    _propertyReleaser_CRWSessionEntry.Init(self, [CRWSessionEntry class]);
+    _navigationItem.reset(new web::NavigationItemImpl());
+
+    _navigationItem->SetURL(url);
+    _navigationItem->SetReferrer(referrer);
+    _navigationItem->SetTransitionType(transition);
+    _navigationItem->set_is_renderer_initiated(rendererInitiated);
+
+    self.originalUrl = url;
+    self.useDesktopUserAgent = useDesktopUserAgent;
+  }
+  return self;
+}
+
+- (instancetype)initWithNavigationItem:(scoped_ptr<web::NavigationItem>)item
+                                 index:(int)index {
+  self = [super init];
+  if (self) {
+    _propertyReleaser_CRWSessionEntry.Init(self, [CRWSessionEntry class]);
+    _navigationItem.reset(
+        static_cast<web::NavigationItemImpl*>(item.release()));
+
+    self.index = index;
+    self.originalUrl = _navigationItem->GetURL();
+    self.useDesktopUserAgent = NO;
+  }
+  return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder*)aDecoder {
+  self = [super init];
+  if (self) {
+    _propertyReleaser_CRWSessionEntry.Init(self, [CRWSessionEntry class]);
+    _navigationItem.reset(new web::NavigationItemImpl());
+
+    // Desktop chrome only persists virtualUrl_ and uses it to feed the url
+    // when creating a NavigationEntry.
+    GURL url;
+    if ([aDecoder containsValueForKey:@"virtualUrlString"]) {
+      url = GURL(
+          web::nscoder_util::DecodeString(aDecoder, @"virtualUrlString"));
+    } else {
+      // Backward compatibility.
+      url = net::GURLWithNSURL([aDecoder decodeObjectForKey:@"virtualUrl"]);
+    }
+    _navigationItem->SetURL(url);
+    self.originalUrl = url;
+
+    if ([aDecoder containsValueForKey:@"referrerUrlString"]) {
+      const std::string referrerString(web::nscoder_util::DecodeString(
+          aDecoder, @"referrerUrlString"));
+      web::ReferrerPolicy referrerPolicy =
+          static_cast<web::ReferrerPolicy>(
+              [aDecoder decodeIntForKey:@"referrerPolicy"]);
+      _navigationItem->SetReferrer(
+          web::Referrer(GURL(referrerString), referrerPolicy));
+    } else {
+      // Backward compatibility.
+      NSURL* referrer = [aDecoder decodeObjectForKey:@"referrer"];
+      _navigationItem->SetReferrer(web::Referrer(
+          net::GURLWithNSURL(referrer), web::ReferrerPolicyDefault));
+    }
+
+    if ([aDecoder containsValueForKey:@"timestamp"]) {
+      int64 us = [aDecoder decodeInt64ForKey:@"timestamp"];
+      _navigationItem->SetTimestamp(base::Time::FromInternalValue(us));
+    }
+
+    NSString* title = [aDecoder decodeObjectForKey:@"title"];
+    // Use a transition type of reload so that we don't incorrectly increase
+    // the typed count.  This is what desktop chrome does.
+    _navigationItem->SetPageID(-1);
+    _navigationItem->SetTitle(base::SysNSStringToUTF16(title));
+    _navigationItem->SetTransitionType(ui::PAGE_TRANSITION_RELOAD);
+    _navigationItem->SetPageScrollState([[self class]
+        scrollStateFromDictionary:[aDecoder decodeObjectForKey:@"state"]]);
+    self.index = [aDecoder decodeIntForKey:@"index"];
+    self.useDesktopUserAgent =
+        [aDecoder decodeBoolForKey:@"useDesktopUserAgent"];
+    self.usedDataReductionProxy =
+        [aDecoder decodeBoolForKey:@"usedDataReductionProxy"];
+    [self addHTTPHeaders:[aDecoder decodeObjectForKey:@"httpHeaders"]];
+    self.POSTData = [aDecoder decodeObjectForKey:@"POSTData"];
+    self.skipResubmitDataConfirmation =
+        [aDecoder decodeBoolForKey:@"skipResubmitDataConfirmation"];
+  }
+  return self;
+}
+
+- (void)encodeWithCoder:(NSCoder*)aCoder {
+  // Desktop Chrome doesn't persist |url_| or |originalUrl_|, only
+  // |virtualUrl_|.
+  [aCoder encodeInt:self.index forKey:@"index"];
+  web::nscoder_util::EncodeString(aCoder, @"virtualUrlString",
+                                  _navigationItem->GetVirtualURL().spec());
+  web::nscoder_util::EncodeString(aCoder, @"referrerUrlString",
+                                  _navigationItem->GetReferrer().url.spec());
+  [aCoder encodeInt:_navigationItem->GetReferrer().policy
+             forKey:@"referrerPolicy"];
+  [aCoder encodeInt64:_navigationItem->GetTimestamp().ToInternalValue()
+               forKey:@"timestamp"];
+
+  [aCoder encodeObject:base::SysUTF16ToNSString(_navigationItem->GetTitle())
+                forKey:@"title"];
+  [aCoder encodeObject:[[self class] dictionaryFromScrollState:
+                                         _navigationItem->GetPageScrollState()]
+                forKey:@"state"];
+  [aCoder encodeBool:self.useDesktopUserAgent forKey:@"useDesktopUserAgent"];
+  [aCoder encodeBool:self.usedDataReductionProxy
+              forKey:@"usedDataReductionProxy"];
+  [aCoder encodeObject:self.httpHeaders forKey:@"httpHeaders"];
+  [aCoder encodeObject:self.POSTData forKey:@"POSTData"];
+  [aCoder encodeBool:self.skipResubmitDataConfirmation
+              forKey:@"skipResubmitDataConfirmation"];
+}
+
+// TODO(ios): Shall we overwrite EqualTo:?
+
+- (instancetype)copyWithZone:(NSZone*)zone {
+  CRWSessionEntry* copy = [[[self class] alloc] init];
+  copy->_propertyReleaser_CRWSessionEntry.Init(copy, [CRWSessionEntry class]);
+  copy->_navigationItem.reset(
+      new web::NavigationItemImpl(*_navigationItem.get()));
+  copy->_index = _index;
+  copy->_originalUrl = _originalUrl;
+  copy->_useDesktopUserAgent = _useDesktopUserAgent;
+  copy->_usedDataReductionProxy = _usedDataReductionProxy;
+  copy->_POSTData = [_POSTData copy];
+  copy->_httpHeaders.reset([_httpHeaders mutableCopy]);
+  copy->_skipResubmitDataConfirmation = _skipResubmitDataConfirmation;
+  return copy;
+}
+
+- (NSString*)description {
+  return [NSString
+      stringWithFormat:
+          @"url:%@ originalurl:%@ title:%@ transition:%d scrollState:%@ "
+          @"desktopUA:%d " @"proxy:%d",
+          base::SysUTF8ToNSString(_navigationItem->GetURL().spec()),
+          base::SysUTF8ToNSString(self.originalUrl.spec()),
+          base::SysUTF16ToNSString(_navigationItem->GetTitle()),
+          _navigationItem->GetTransitionType(),
+          [[self class]
+              scrollStateDescription:_navigationItem->GetPageScrollState()],
+          _useDesktopUserAgent, _usedDataReductionProxy];
+}
+
+- (web::NavigationItem*)navigationItem {
+  return _navigationItem.get();
+}
+
+- (NSDictionary*)httpHeaders {
+  return _httpHeaders ? [NSDictionary dictionaryWithDictionary:_httpHeaders]
+                      : nil;
+}
+
+- (void)addHTTPHeaders:(NSDictionary*)moreHTTPHeaders {
+  if (_httpHeaders)
+    [_httpHeaders addEntriesFromDictionary:moreHTTPHeaders];
+  else
+    _httpHeaders.reset([moreHTTPHeaders mutableCopy]);
+}
+
+- (void)removeHTTPHeaderForKey:(NSString*)key {
+  [_httpHeaders removeObjectForKey:key];
+  if (![_httpHeaders count])
+    _httpHeaders.reset();
+}
+
+- (void)resetHTTPHeaders {
+  _httpHeaders.reset();
+}
+
+#pragma mark - Serialization helpers
+
++ (web::PageScrollState)scrollStateFromDictionary:(NSDictionary*)dictionary {
+  web::PageScrollState scrollState;
+  NSNumber* serializedValue = nil;
+  if ((serializedValue = dictionary[kScrollOffsetXKey]))
+    scrollState.set_scroll_offset_x([serializedValue doubleValue]);
+  if ((serializedValue = dictionary[kScrollOffsetYKey]))
+    scrollState.set_scroll_offset_y([serializedValue doubleValue]);
+  if ((serializedValue = dictionary[kMinimumZoomScaleKey]))
+    scrollState.set_minimum_zoom_scale([serializedValue doubleValue]);
+  if ((serializedValue = dictionary[kMaximumZoomScaleKey]))
+    scrollState.set_maximum_zoom_scale([serializedValue doubleValue]);
+  if ((serializedValue = dictionary[kZoomScaleKey]))
+    scrollState.set_zoom_scale([serializedValue doubleValue]);
+  return scrollState;
+}
+
++ (NSDictionary*)dictionaryFromScrollState:
+    (const web::PageScrollState&)scrollState {
+  return @{
+    kScrollOffsetXKey : @(scrollState.scroll_offset_x()),
+    kScrollOffsetYKey : @(scrollState.scroll_offset_y()),
+    kMinimumZoomScaleKey : @(scrollState.minimum_zoom_scale()),
+    kMaximumZoomScaleKey : @(scrollState.maximum_zoom_scale()),
+    kZoomScaleKey : @(scrollState.zoom_scale())
+  };
+}
+
++ (NSString*)scrollStateDescription:(const web::PageScrollState&)scrollState {
+  NSString* const kPageScrollStateDescriptionFormat =
+      @"{ scrollOffset:(%0.2f, %0.2f), zoomScaleRange:(%0.2f, %0.2f), "
+      @"zoomScale:%0.2f }";
+  return [NSString stringWithFormat:kPageScrollStateDescriptionFormat,
+                                    scrollState.scroll_offset_x(),
+                                    scrollState.scroll_offset_y(),
+                                    scrollState.minimum_zoom_scale(),
+                                    scrollState.maximum_zoom_scale(),
+                                    scrollState.zoom_scale()];
+}
+
+@end
diff --git a/ios/web/navigation/crw_session_entry_unittest.mm b/ios/web/navigation/crw_session_entry_unittest.mm
new file mode 100644
index 0000000..210afc6
--- /dev/null
+++ b/ios/web/navigation/crw_session_entry_unittest.mm
@@ -0,0 +1,400 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Foundation/Foundation.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+#import "ios/testing/ocmock_complex_type_helper.h"
+#import "ios/web/navigation/crw_session_entry.h"
+#include "ios/web/navigation/navigation_item_impl.h"
+#include "ios/web/public/referrer.h"
+#import "net/base/mac/url_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#include "third_party/ocmock/gtest_support.h"
+#include "ui/base/page_transition_types.h"
+
+@interface CRWSessionEntry (ExposedForTesting)
++ (web::PageScrollState)scrollStateFromDictionary:(NSDictionary*)dictionary;
++ (NSDictionary*)dictionaryFromScrollState:
+    (const web::PageScrollState&)scrollState;
+@end
+
+static NSString* const kHTTPHeaderKey1 = @"key1";
+static NSString* const kHTTPHeaderKey2 = @"key2";
+static NSString* const kHTTPHeaderValue1 = @"value1";
+static NSString* const kHTTPHeaderValue2 = @"value2";
+
+class CRWSessionEntryTest : public PlatformTest {
+ public:
+  static void expectEqualSessionEntries(CRWSessionEntry* entry1,
+                                        CRWSessionEntry* entry2,
+                                        ui::PageTransition transition);
+
+ protected:
+  void SetUp() override {
+    GURL url("http://init.test");
+    ui::PageTransition transition =
+        ui::PAGE_TRANSITION_AUTO_BOOKMARK;
+    sessionEntry_.reset([[CRWSessionEntry alloc] initWithUrl:url
+                                                    referrer:web::Referrer()
+                                                  transition:transition
+                                         useDesktopUserAgent:NO
+                                           rendererInitiated:NO]);
+    [sessionEntry_ navigationItem]->SetTimestamp(base::Time::Now());
+    [sessionEntry_ addHTTPHeaders:@{ kHTTPHeaderKey1 : kHTTPHeaderValue1 }];
+    [sessionEntry_
+        setPOSTData:[@"Test data" dataUsingEncoding:NSUTF8StringEncoding]];
+  }
+  void TearDown() override { sessionEntry_.reset(); }
+
+ protected:
+  base::scoped_nsobject<CRWSessionEntry> sessionEntry_;
+};
+
+@implementation OCMockComplexTypeHelper (CRWSessionEntryTest)
+typedef void (^encodeBytes_length_forKey_block)(
+    const uint8_t*, NSUInteger, NSString*);
+- (void)encodeBytes:(const uint8_t*)bytes
+             length:(NSUInteger)length
+             forKey:(NSString*)key {
+  static_cast<encodeBytes_length_forKey_block>([self blockForSelector:_cmd])(
+      bytes, length, key);
+}
+
+typedef const uint8_t* (^decodeBytesForKeyBlock)(NSString*, NSUInteger*);
+- (const uint8_t*)decodeBytesForKey:(NSString*)key
+                      returnedLength:(NSUInteger*)lengthp {
+  return static_cast<decodeBytesForKeyBlock>([self blockForSelector:_cmd])(
+      key, lengthp);
+}
+@end
+
+void CRWSessionEntryTest::expectEqualSessionEntries(
+    CRWSessionEntry* entry1,
+    CRWSessionEntry* entry2,
+    ui::PageTransition transition) {
+  EXPECT_EQ(entry1.index, entry2.index);
+  web::NavigationItem* navItem1 = entry1.navigationItem;
+  web::NavigationItem* navItem2 = entry2.navigationItem;
+  // url is not compared because it could differ after copy or archive.
+  EXPECT_EQ(navItem1->GetVirtualURL(), navItem2->GetVirtualURL());
+  EXPECT_EQ(navItem1->GetReferrer().url, navItem2->GetReferrer().url);
+  EXPECT_EQ(navItem1->GetTimestamp(), navItem2->GetTimestamp());
+  EXPECT_EQ(navItem1->GetTitle(), navItem2->GetTitle());
+  EXPECT_EQ(navItem1->GetPageScrollState(), navItem2->GetPageScrollState());
+  EXPECT_EQ(entry1.useDesktopUserAgent, entry2.useDesktopUserAgent);
+  EXPECT_EQ(entry1.usedDataReductionProxy, entry2.usedDataReductionProxy);
+  EXPECT_EQ(navItem2->GetTransitionType(), transition);
+  EXPECT_NSEQ(entry1.httpHeaders, entry2.httpHeaders);
+  EXPECT_TRUE((!entry1.POSTData && !entry2.POSTData) ||
+              [entry1.POSTData isEqualToData:entry2.POSTData]);
+  EXPECT_EQ(entry1.skipResubmitDataConfirmation,
+            entry2.skipResubmitDataConfirmation);
+}
+
+TEST_F(CRWSessionEntryTest, Description) {
+  [sessionEntry_ navigationItem]->SetTitle(base::SysNSStringToUTF16(@"Title"));
+  EXPECT_NSEQ([sessionEntry_ description], @"url:http://init.test/ "
+              @"originalurl:http://init.test/ " @"title:Title " @"transition:2 "
+              @"scrollState:{ scrollOffset:(nan, nan), zoomScaleRange:(nan, "
+              @"nan), zoomScale:nan } " @"desktopUA:0 " @"proxy:0");
+}
+
+TEST_F(CRWSessionEntryTest, InitWithCoder) {
+  web::NavigationItem* item = [sessionEntry_ navigationItem];
+  item->SetVirtualURL(GURL("http://user.friendly"));
+  item->SetTitle(base::SysNSStringToUTF16(@"Title"));
+  int index = sessionEntry_.get().index;
+  // Old serialized entries have no timestamp.
+  item->SetTimestamp(base::Time::FromInternalValue(0));
+
+  NSURL* virtualUrl = net::NSURLWithGURL(item->GetVirtualURL());
+  NSURL* referrer = net::NSURLWithGURL(item->GetReferrer().url);
+  NSString* title = base::SysUTF16ToNSString(item->GetTitle());
+  base::scoped_nsobject<id> decoder([[OCMockComplexTypeHelper alloc]
+      initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
+
+  decodeBytesForKeyBlock block = ^ const uint8_t* (NSString* key,
+                                                   NSUInteger* length) {
+      *length = 0;
+      return NULL;
+  };
+
+  [[[decoder stub] andReturnValue:[NSNumber numberWithBool:NO]]
+      containsValueForKey:[OCMArg any]];
+
+  [decoder onSelector:@selector(decodeBytesForKey:returnedLength:)
+      callBlockExpectation:block];
+  [[[decoder expect] andReturnValue:OCMOCK_VALUE(index)]
+      decodeIntForKey:@"index"];
+  [[[decoder expect] andReturn:virtualUrl]
+      decodeObjectForKey:@"virtualUrl"];
+  [[[decoder expect] andReturn:referrer]
+      decodeObjectForKey:@"referrer"];
+  [[[decoder expect] andReturn:title]
+      decodeObjectForKey:@"title"];
+  const web::PageScrollState& scrollState =
+      [sessionEntry_ navigationItem]->GetPageScrollState();
+  NSDictionary* serializedScrollState =
+      [CRWSessionEntry dictionaryFromScrollState:scrollState];
+  [[[decoder expect] andReturn:serializedScrollState]
+      decodeObjectForKey:@"state"];
+  BOOL useDesktopUserAgent = sessionEntry_.get().useDesktopUserAgent;
+  [[[decoder expect] andReturnValue:OCMOCK_VALUE(useDesktopUserAgent)]
+      decodeBoolForKey:@"useDesktopUserAgent"];
+  BOOL usedDataReductionProxy = sessionEntry_.get().usedDataReductionProxy;
+  [[[decoder expect] andReturnValue:OCMOCK_VALUE(usedDataReductionProxy)]
+      decodeBoolForKey:@"usedDataReductionProxy"];
+  [[[decoder expect] andReturn:sessionEntry_.get().httpHeaders]
+      decodeObjectForKey:@"httpHeaders"];
+  [[[decoder expect] andReturn:sessionEntry_.get().POSTData]
+      decodeObjectForKey:@"POSTData"];
+  BOOL skipResubmitDataConfirmation =
+      sessionEntry_.get().skipResubmitDataConfirmation;
+  [[[decoder expect] andReturnValue:OCMOCK_VALUE(skipResubmitDataConfirmation)]
+      decodeBoolForKey:@"skipResubmitDataConfirmation"];
+
+  base::scoped_nsobject<CRWSessionEntry> newSessionEntry(
+      [[CRWSessionEntry alloc] initWithCoder:decoder]);
+  web::NavigationItem* newItem = [newSessionEntry navigationItem];
+
+  EXPECT_OCMOCK_VERIFY(decoder);
+  expectEqualSessionEntries(sessionEntry_, newSessionEntry,
+                            ui::PAGE_TRANSITION_RELOAD);
+  EXPECT_NE(item->GetURL(), newItem->GetURL());
+  EXPECT_EQ(item->GetVirtualURL(), newItem->GetURL());
+}
+
+TEST_F(CRWSessionEntryTest, InitWithCoderNewStyle) {
+  web::NavigationItem* item = [sessionEntry_ navigationItem];
+  item->SetVirtualURL(GURL("http://user.friendly"));
+  item->SetTitle(base::SysNSStringToUTF16(@"Title"));
+  int index = sessionEntry_.get().index;
+  int64 timestamp = item->GetTimestamp().ToInternalValue();
+
+  std::string virtualUrl = item->GetVirtualURL().spec();
+  std::string referrerUrl = item->GetReferrer().url.spec();
+  NSString* title = base::SysUTF16ToNSString(item->GetTitle());
+  base::scoped_nsobject<id> decoder([[OCMockComplexTypeHelper alloc]
+      initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
+
+  const std::string emptyString;
+  decodeBytesForKeyBlock block =  ^ const uint8_t* (NSString* key,
+                                                    NSUInteger* length) {
+      const std::string *value = &emptyString;
+      if ([key isEqualToString:@"virtualUrlString"])
+        value = &virtualUrl;
+      else if ([key isEqualToString:@"referrerUrlString"])
+        value = &referrerUrl;
+      else
+        EXPECT_TRUE(false);
+
+      *length = value->size();
+      return reinterpret_cast<const uint8_t*>(value->data());
+  };
+
+  [decoder onSelector:@selector(decodeBytesForKey:returnedLength:)
+      callBlockExpectation:block];
+  [[[decoder stub] andReturnValue:[NSNumber numberWithBool:YES]]
+      containsValueForKey:[OCMArg any]];
+  [[[decoder expect] andReturnValue:OCMOCK_VALUE(index)]
+      decodeIntForKey:@"index"];
+  web::ReferrerPolicy expectedPolicy = item->GetReferrer().policy;
+  [[[decoder expect]
+      andReturnValue:OCMOCK_VALUE(expectedPolicy)]
+      decodeIntForKey:@"referrerPolicy"];
+  [[[decoder expect] andReturnValue:OCMOCK_VALUE(timestamp)]
+      decodeInt64ForKey:@"timestamp"];
+  [[[decoder expect] andReturn:title]
+      decodeObjectForKey:@"title"];
+  const web::PageScrollState& scrollState =
+      [sessionEntry_ navigationItem]->GetPageScrollState();
+  NSDictionary* serializedScrollState =
+      [CRWSessionEntry dictionaryFromScrollState:scrollState];
+  [[[decoder expect] andReturn:serializedScrollState]
+      decodeObjectForKey:@"state"];
+  BOOL useDesktopUserAgent = sessionEntry_.get().useDesktopUserAgent;
+  [[[decoder expect] andReturnValue:OCMOCK_VALUE(useDesktopUserAgent)]
+      decodeBoolForKey:@"useDesktopUserAgent"];
+  BOOL usedDataReductionProxy = sessionEntry_.get().usedDataReductionProxy;
+  [[[decoder expect] andReturnValue:OCMOCK_VALUE(usedDataReductionProxy)]
+      decodeBoolForKey:@"usedDataReductionProxy"];
+  [[[decoder expect] andReturn:sessionEntry_.get().httpHeaders]
+      decodeObjectForKey:@"httpHeaders"];
+  [[[decoder expect] andReturn:sessionEntry_.get().POSTData]
+      decodeObjectForKey:@"POSTData"];
+  BOOL skipResubmitDataConfirmation =
+      sessionEntry_.get().skipResubmitDataConfirmation;
+  [[[decoder expect] andReturnValue:OCMOCK_VALUE(skipResubmitDataConfirmation)]
+      decodeBoolForKey:@"skipResubmitDataConfirmation"];
+
+  base::scoped_nsobject<CRWSessionEntry> newSessionEntry(
+      [[CRWSessionEntry alloc] initWithCoder:decoder]);
+  web::NavigationItem* newItem = [newSessionEntry navigationItem];
+
+  EXPECT_OCMOCK_VERIFY(decoder);
+  expectEqualSessionEntries(sessionEntry_, newSessionEntry,
+                            ui::PAGE_TRANSITION_RELOAD);
+  EXPECT_NE(item->GetURL(), newItem->GetURL());
+  EXPECT_EQ(item->GetVirtualURL(), newItem->GetVirtualURL());
+}
+
+TEST_F(CRWSessionEntryTest, EncodeDecode) {
+  NSData *data =
+      [NSKeyedArchiver archivedDataWithRootObject:sessionEntry_];
+  id decoded = [NSKeyedUnarchiver unarchiveObjectWithData:data];
+
+  expectEqualSessionEntries(sessionEntry_, decoded,
+                            ui::PAGE_TRANSITION_RELOAD);
+}
+
+TEST_F(CRWSessionEntryTest, EncodeWithCoder) {
+  web::NavigationItem* item = [sessionEntry_ navigationItem];
+  NSString* title = base::SysUTF16ToNSString(item->GetTitle());
+
+  base::scoped_nsobject<id> coder([[OCMockComplexTypeHelper alloc]
+      initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
+
+  encodeBytes_length_forKey_block block = ^(const uint8_t* bytes,
+                                            NSUInteger length,
+                                            NSString* key) {
+      if ([key isEqualToString:@"virtualUrlString"]) {
+        ASSERT_EQ(item->GetVirtualURL().spec(),
+                  std::string(reinterpret_cast<const char*>(bytes), length));
+        return;
+      } else if ([key isEqualToString:@"referrerUrlString"]) {
+        ASSERT_EQ(item->GetReferrer().url.spec(),
+                  std::string(reinterpret_cast<const char*>(bytes), length));
+        return;
+      }
+      FAIL();
+  };
+  [coder onSelector:@selector(encodeBytes:length:forKey:)
+      callBlockExpectation:block];
+  [[coder expect] encodeInt:[sessionEntry_ index] forKey:@"index"];
+  [[coder expect] encodeInt:item->GetReferrer().policy
+                     forKey:@"referrerPolicy"];
+  [[coder expect] encodeInt64:item->GetTimestamp().ToInternalValue()
+                       forKey:@"timestamp"];
+  [[coder expect] encodeObject:title forKey:@"title"];
+  const web::PageScrollState& scrollState =
+      [sessionEntry_ navigationItem]->GetPageScrollState();
+  NSDictionary* serializedScrollState =
+      [CRWSessionEntry dictionaryFromScrollState:scrollState];
+  [[coder expect] encodeObject:serializedScrollState forKey:@"state"];
+  [[coder expect] encodeBool:[sessionEntry_ useDesktopUserAgent]
+                      forKey:@"useDesktopUserAgent"];
+  [[coder expect] encodeBool:[sessionEntry_ usedDataReductionProxy]
+                      forKey:@"usedDataReductionProxy"];
+  [[coder expect] encodeObject:[sessionEntry_ httpHeaders]
+                        forKey:@"httpHeaders"];
+  [[coder expect] encodeObject:[sessionEntry_ POSTData] forKey:@"POSTData"];
+  [[coder expect] encodeBool:[sessionEntry_ skipResubmitDataConfirmation]
+                      forKey:@"skipResubmitDataConfirmation"];
+  [sessionEntry_ encodeWithCoder:coder];
+  EXPECT_OCMOCK_VERIFY(coder);
+}
+
+TEST_F(CRWSessionEntryTest, CodingEncoding) {
+  web::NavigationItem* item = [sessionEntry_ navigationItem];
+  item->SetVirtualURL(GURL("http://user.friendly"));
+  NSData* data = [NSKeyedArchiver archivedDataWithRootObject:sessionEntry_];
+  EXPECT_TRUE(data != nil);
+  CRWSessionEntry* unarchivedSessionEntry =
+      [NSKeyedUnarchiver unarchiveObjectWithData:data];
+  ASSERT_TRUE(unarchivedSessionEntry != nil);
+  web::NavigationItem* unarchivedItem = [unarchivedSessionEntry navigationItem];
+  expectEqualSessionEntries(sessionEntry_, unarchivedSessionEntry,
+                            ui::PAGE_TRANSITION_RELOAD);
+  EXPECT_EQ(unarchivedItem->GetURL(), item->GetVirtualURL());
+  EXPECT_NE(unarchivedItem->GetURL(), item->GetURL());
+}
+
+TEST_F(CRWSessionEntryTest, CopyWithZone) {
+  CRWSessionEntry* sessionEntry2 = [sessionEntry_ copy];
+  EXPECT_NE(sessionEntry_, sessionEntry2);
+  expectEqualSessionEntries(
+      sessionEntry_, sessionEntry2,
+      [sessionEntry_ navigationItem]->GetTransitionType());
+}
+
+TEST_F(CRWSessionEntryTest, EmptyVirtualUrl) {
+  EXPECT_EQ(GURL("http://init.test/"),
+            [sessionEntry_ navigationItem]->GetURL());
+}
+
+TEST_F(CRWSessionEntryTest, NonEmptyVirtualUrl) {
+  web::NavigationItem* item = [sessionEntry_ navigationItem];
+  item->SetVirtualURL(GURL("http://user.friendly"));
+  EXPECT_EQ(GURL("http://user.friendly/"), item->GetVirtualURL());
+  EXPECT_EQ(GURL("http://init.test/"), item->GetURL());
+}
+
+TEST_F(CRWSessionEntryTest, EmptyDescription) {
+  EXPECT_GT([[sessionEntry_ description] length], 0U);
+}
+
+TEST_F(CRWSessionEntryTest, CreateWithNavigationItem) {
+  int index = 5;  // Just pick something non-zero.
+  GURL url("http://www.virtualurl.com");
+  web::Referrer referrer(GURL("http://www.referrer.com"),
+                         web::ReferrerPolicyDefault);
+  base::string16 title = base::SysNSStringToUTF16(@"Title");
+  std::string state;
+  ui::PageTransition transition = ui::PAGE_TRANSITION_GENERATED;
+
+  scoped_ptr<web::NavigationItem> navigation_item(
+      new web::NavigationItemImpl());
+  navigation_item->SetURL(url);
+  navigation_item->SetReferrer(referrer);
+  navigation_item->SetTitle(title);
+  navigation_item->SetTransitionType(transition);
+
+  base::scoped_nsobject<CRWSessionEntry> sessionEntry(
+      [[CRWSessionEntry alloc] initWithNavigationItem:navigation_item.Pass()
+                                                index:index]);
+  web::NavigationItem* item = [sessionEntry navigationItem];
+  // Validate everything was set correctly.
+  EXPECT_EQ(sessionEntry.get().index, index);
+  // Desktop only persists the virtual url, all three fields are initialized
+  // by it.
+  EXPECT_EQ(item->GetURL(), url);
+  EXPECT_EQ(item->GetVirtualURL(), url);
+  EXPECT_EQ(sessionEntry.get().originalUrl, url);
+  EXPECT_EQ(item->GetReferrer().url, referrer.url);
+  EXPECT_EQ(item->GetTitle(), title);
+  EXPECT_EQ(item->GetTransitionType(), transition);
+}
+
+TEST_F(CRWSessionEntryTest, AddHTTPHeaders) {
+  EXPECT_NSEQ(@{ kHTTPHeaderKey1 : kHTTPHeaderValue1 },
+              [sessionEntry_ httpHeaders]);
+
+  [sessionEntry_ addHTTPHeaders:@{ kHTTPHeaderKey1 : kHTTPHeaderValue2 }];
+  EXPECT_NSEQ(@{ kHTTPHeaderKey1 : kHTTPHeaderValue2 },
+              [sessionEntry_ httpHeaders]);
+
+  [sessionEntry_ addHTTPHeaders:@{ kHTTPHeaderKey2 : kHTTPHeaderValue1 }];
+  NSDictionary* expected = @{ kHTTPHeaderKey1 : kHTTPHeaderValue2,
+                              kHTTPHeaderKey2 : kHTTPHeaderValue1 };
+  EXPECT_NSEQ(expected, [sessionEntry_ httpHeaders]);
+}
+
+TEST_F(CRWSessionEntryTest, RemoveHTTPHeaderForKey) {
+  NSDictionary* httpHeaders = @{ kHTTPHeaderKey1 : kHTTPHeaderValue1,
+                                 kHTTPHeaderKey2 : kHTTPHeaderValue2 };
+  [sessionEntry_ addHTTPHeaders:httpHeaders];
+  EXPECT_NSEQ(httpHeaders, [sessionEntry_ httpHeaders]);
+
+  [sessionEntry_ removeHTTPHeaderForKey:kHTTPHeaderKey1];
+  EXPECT_NSEQ(@{ kHTTPHeaderKey2 : kHTTPHeaderValue2 },
+              [sessionEntry_ httpHeaders]);
+
+  [sessionEntry_ removeHTTPHeaderForKey:kHTTPHeaderKey2];
+  EXPECT_TRUE([sessionEntry_ httpHeaders] == nil);
+}
diff --git a/ios/web/navigation/navigation_item_facade_delegate.h b/ios/web/navigation/navigation_item_facade_delegate.h
new file mode 100644
index 0000000..8ccd6c0a
--- /dev/null
+++ b/ios/web/navigation/navigation_item_facade_delegate.h
@@ -0,0 +1,31 @@
+// 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 IOS_WEB_NAVIGATION_NAVIGATION_ITEM_FACADE_DELEGATE_H_
+#define IOS_WEB_NAVIGATION_NAVIGATION_ITEM_FACADE_DELEGATE_H_
+
+namespace content {
+class NavigationEntry;
+}
+
+namespace web {
+
+class NavigationItemImpl;
+
+// Interface used by NavigationItems to interact with their NavigationEntry
+// facades.  This pushes references to NavigationEntry out of the web-layer.
+// Note that since communication is one-directional, this interface is primarily
+// to add hooks for creation/deletion of facades.
+class NavigationItemFacadeDelegate {
+ public:
+  NavigationItemFacadeDelegate() {}
+  virtual ~NavigationItemFacadeDelegate() {}
+
+  // Returns the facade object being driven by this delegate.
+  virtual content::NavigationEntry* GetNavigationEntryFacade() = 0;
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_NAVIGATION_NAVIGATION_ITEM_FACADE_DELEGATE_H_
diff --git a/ios/web/navigation/navigation_item_impl.h b/ios/web/navigation/navigation_item_impl.h
index 26cc2bc..9abd992 100644
--- a/ios/web/navigation/navigation_item_impl.h
+++ b/ios/web/navigation/navigation_item_impl.h
@@ -8,14 +8,18 @@
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string16.h"
+#include "ios/web/navigation/navigation_item_facade_delegate.h"
 #include "ios/web/public/favicon_status.h"
 #include "ios/web/public/navigation_item.h"
 #include "ios/web/public/referrer.h"
 #include "ios/web/public/ssl_status.h"
 #include "url/gurl.h"
 
+
 namespace web {
 
+class NavigationItemFacadeDelegate;
+
 // Implementation of NavigationItem.
 class NavigationItemImpl : public web::NavigationItem {
  public:
@@ -23,6 +27,17 @@
   NavigationItemImpl();
   ~NavigationItemImpl() override;
 
+  // Since NavigationItemImpls own their facade delegates, there is no implicit
+  // copy constructor (scoped_ptrs can't be copied), so one is defined here.
+  NavigationItemImpl(const NavigationItemImpl& item);
+
+  // Accessors for the delegate used to drive the navigation entry facade.
+  // NOTE: to minimize facade synchronization code, NavigationItems take
+  // ownership of their facade delegates.
+  void SetFacadeDelegate(
+      scoped_ptr<NavigationItemFacadeDelegate> facade_delegate);
+  NavigationItemFacadeDelegate* GetFacadeDelegate() const;
+
   // NavigationItem implementation:
   int GetUniqueID() const override;
   void SetURL(const GURL& url) override;
@@ -35,6 +50,8 @@
   const base::string16& GetTitle() const override;
   void SetPageID(int page_id) override;
   int32 GetPageID() const override;
+  void SetPageScrollState(const PageScrollState& scroll_state) override;
+  const PageScrollState& GetPageScrollState() const override;
   const base::string16& GetTitleForDisplay(
       const std::string& languages) const override;
   void SetTransitionType(ui::PageTransition transition_type) override;
@@ -45,6 +62,19 @@
   SSLStatus& GetSSL() override;
   void SetTimestamp(base::Time timestamp) override;
   base::Time GetTimestamp() const override;
+  void SetUnsafe(bool is_unsafe) override;
+  bool IsUnsafe() const override;
+
+  // Once a navigation item is committed, we should no longer track
+  // non-persisted state, as documented on the members below.
+  void ResetForCommit();
+
+  // Whether this (pending) navigation is renderer-initiated.  Resets to false
+  // for all types of navigations after commit.
+  void set_is_renderer_initiated(bool is_renderer_initiated) {
+    is_renderer_initiated_ = is_renderer_initiated;
+  }
+  bool is_renderer_initiated() const { return is_renderer_initiated_; }
 
  private:
   int unique_id_;
@@ -53,15 +83,27 @@
   GURL virtual_url_;
   base::string16 title_;
   int32 page_id_;
+  PageScrollState page_scroll_state_;
   ui::PageTransition transition_type_;
   FaviconStatus favicon_;
   SSLStatus ssl_;
   base::Time timestamp_;
 
+  // Whether the item, while loading, was created for a renderer-initiated
+  // navigation.  This dictates whether the URL should be displayed before the
+  // navigation commits.  It is cleared in |ResetForCommit| and not persisted.
+  bool is_renderer_initiated_;
+
+  // Whether the navigation contains unsafe resources.
+  bool is_unsafe_;
+
   // This is a cached version of the result of GetTitleForDisplay. When the URL,
   // virtual URL, or title is set, this should be cleared to force a refresh.
   mutable base::string16 cached_display_title_;
 
+  // Weak pointer to the facade delegate.
+  scoped_ptr<NavigationItemFacadeDelegate> facade_delegate_;
+
   // Copy and assignment is explicitly allowed for this class.
 };
 
diff --git a/ios/web/navigation/navigation_item_impl.mm b/ios/web/navigation/navigation_item_impl.mm
index 1f86d72b..56edd853 100644
--- a/ios/web/navigation/navigation_item_impl.mm
+++ b/ios/web/navigation/navigation_item_impl.mm
@@ -31,12 +31,42 @@
 NavigationItemImpl::NavigationItemImpl()
     : unique_id_(GetUniqueIDInConstructor()),
       page_id_(-1),
-      transition_type_(ui::PAGE_TRANSITION_LINK) {
+      transition_type_(ui::PAGE_TRANSITION_LINK),
+      is_renderer_initiated_(false),
+      is_unsafe_(false),
+      facade_delegate_(nullptr) {
 }
 
 NavigationItemImpl::~NavigationItemImpl() {
 }
 
+NavigationItemImpl::NavigationItemImpl(const NavigationItemImpl& item)
+    : unique_id_(item.unique_id_),
+      url_(item.url_),
+      referrer_(item.referrer_),
+      virtual_url_(item.virtual_url_),
+      title_(item.title_),
+      page_id_(item.page_id_),
+      page_scroll_state_(item.page_scroll_state_),
+      transition_type_(item.transition_type_),
+      favicon_(item.favicon_),
+      ssl_(item.ssl_),
+      timestamp_(item.timestamp_),
+      is_renderer_initiated_(item.is_renderer_initiated_),
+      is_unsafe_(item.is_unsafe_),
+      cached_display_title_(item.cached_display_title_),
+      facade_delegate_(nullptr) {
+}
+
+void NavigationItemImpl::SetFacadeDelegate(
+    scoped_ptr<NavigationItemFacadeDelegate> facade_delegate) {
+  facade_delegate_ = facade_delegate.Pass();
+}
+
+NavigationItemFacadeDelegate* NavigationItemImpl::GetFacadeDelegate() const {
+  return facade_delegate_.get();
+}
+
 int NavigationItemImpl::GetUniqueID() const {
   return unique_id_;
 }
@@ -84,6 +114,15 @@
   return page_id_;
 }
 
+void NavigationItemImpl::SetPageScrollState(
+    const web::PageScrollState& scroll_state) {
+  page_scroll_state_ = scroll_state;
+}
+
+const PageScrollState& NavigationItemImpl::GetPageScrollState() const {
+  return page_scroll_state_;
+}
+
 const base::string16& NavigationItemImpl::GetTitleForDisplay(
     const std::string& languages) const {
   // Most pages have real titles. Don't even bother caching anything if this is
@@ -148,4 +187,18 @@
   return timestamp_;
 }
 
+void NavigationItemImpl::ResetForCommit() {
+  // Any state that only matters when a navigation item is pending should be
+  // cleared here.
+  set_is_renderer_initiated(false);
+}
+
+void NavigationItemImpl::SetUnsafe(bool is_unsafe) {
+  is_unsafe_ = is_unsafe;
+}
+
+bool NavigationItemImpl::IsUnsafe() const {
+  return is_unsafe_;
+}
+
 }  // namespace web
diff --git a/ios/web/navigation/navigation_manager_delegate.h b/ios/web/navigation/navigation_manager_delegate.h
new file mode 100644
index 0000000..bc1e058
--- /dev/null
+++ b/ios/web/navigation/navigation_manager_delegate.h
@@ -0,0 +1,34 @@
+// 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_WEB_NAVIGATION_NAVIGATION_MANAGER_DELEGATE_H_
+#define IOS_WEB_NAVIGATION_NAVIGATION_MANAGER_DELEGATE_H_
+
+namespace web {
+
+struct LoadCommittedDetails;
+class WebState;
+
+// Delegate for NavigationManager to hand off parts of the navigation flow.
+// TODO(stuartmorgan): See if this can be eliminated by moving more
+class NavigationManagerDelegate {
+ public:
+  virtual ~NavigationManagerDelegate() {}
+
+  // Instructs the delegate to begin navigating to the pending entry.
+  // TODO(stuartmorgan): Remove this once more navigation logic moves to
+  // NavigationManagerImpl.
+  virtual void NavigateToPendingEntry() = 0;
+
+  // Informs the delegate that a navigation item has been commited.
+  virtual void OnNavigationItemCommitted(
+      const LoadCommittedDetails& load_details) = 0;
+
+  // Returns the WebState associated with this delegate.
+  virtual WebState* GetWebState() = 0;
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_NAVIGATION_NAVIGATION_MANAGER_DELEGATE_H_
diff --git a/ios/web/navigation/navigation_manager_facade_delegate.h b/ios/web/navigation/navigation_manager_facade_delegate.h
new file mode 100644
index 0000000..b405f04
--- /dev/null
+++ b/ios/web/navigation/navigation_manager_facade_delegate.h
@@ -0,0 +1,47 @@
+// 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 IOS_WEB_NAVIGATION_NAVIGATION_MANAGER_FACADE_DELEGATE_H_
+#define IOS_WEB_NAVIGATION_NAVIGATION_MANAGER_FACADE_DELEGATE_H_
+
+namespace content {
+class NavigationController;
+}
+
+namespace web {
+
+class NavigationItemImpl;
+class NavigationManagerImpl;
+
+// Interface used by the NavigationManager to drive the NavigationController
+// facade. This pushes the ownership of the facade out of the web-layer to
+// simplify upstreaming efforts.  Once upstream features are componentized and
+// use NavigationManager, this class will no longer be necessary.
+class NavigationManagerFacadeDelegate {
+ public:
+  NavigationManagerFacadeDelegate() {}
+  virtual ~NavigationManagerFacadeDelegate() {}
+
+  // Sets the NavigationManagerImpl that backs the NavigationController facade.
+  virtual void SetNavigationManager(
+      NavigationManagerImpl* navigation_manager) = 0;
+  // Returns the facade object being driven by this delegate.
+  virtual content::NavigationController* GetNavigationControllerFacade() = 0;
+
+  // Callbacks for triggering notifications:
+
+  // Called when the NavigationManager has a pending NavigationItem.
+  virtual void OnNavigationItemPending() = 0;
+  // Called when the NavigationManager has updated a NavigationItem.
+  virtual void OnNavigationItemChanged() = 0;
+  // Called when the NavigationManager has committed a pending NavigationItem.
+  virtual void OnNavigationItemCommitted(int previous_item_index,
+                                         bool is_in_page) = 0;
+  // Called when the NavigationManager has pruned committed NavigationItems.
+  virtual void OnNavigationItemsPruned(size_t pruned_item_count) = 0;
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_NAVIGATION_NAVIGATION_MANAGER_FACADE_DELEGATE_H_
diff --git a/ios/web/navigation/navigation_manager_impl.h b/ios/web/navigation/navigation_manager_impl.h
new file mode 100644
index 0000000..92f65d4
--- /dev/null
+++ b/ios/web/navigation/navigation_manager_impl.h
@@ -0,0 +1,147 @@
+// 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 IOS_WEB_NAVIGATION_NAVIGATION_MANAGER_IMPL_H_
+#define IOS_WEB_NAVIGATION_NAVIGATION_MANAGER_IMPL_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/memory/scoped_vector.h"
+#include "ios/web/public/navigation_manager.h"
+#include "ui/base/page_transition_types.h"
+#include "url/gurl.h"
+
+@class CRWSessionController;
+@class CRWSessionEntry;
+
+namespace web {
+class BrowserState;
+class NavigationItem;
+struct Referrer;
+class NavigationManagerDelegate;
+class NavigationManagerFacadeDelegate;
+
+// Implementation of NavigationManager.
+// Generally mirrors upstream's NavigationController.
+class NavigationManagerImpl : public web::NavigationManager {
+ public:
+  NavigationManagerImpl(NavigationManagerDelegate* delegate,
+                        BrowserState* browser_state);
+  ~NavigationManagerImpl() override;
+
+  // Sets the CRWSessionController that backs this object.
+  // Keeps a strong reference to |session_controller|.
+  // This method should only be called when deserializing |session_controller|
+  // and joining it with its NavigationManager. Other cases should call
+  // InitializeSession() or ReplaceSessionHistory().
+  // TODO(stuartmorgan): Also move deserialization of CRWSessionControllers
+  // under the control of this class, and move the bulk of CRWSessionController
+  // logic into it.
+  void SetSessionController(CRWSessionController* session_controller);
+
+  // Initializes a new session history, supplying a unique |window_name| for the
+  // window (or nil). |opener_id| is the id of opener, or nil if there is none.
+  // |opened_by_dom| is YES if the page was opened by DOM.
+  // |opener_index| is the navigation index of the opener, or -1 if there is
+  // none.
+  void InitializeSession(NSString* window_name,
+                         NSString* opener_id,
+                         BOOL opened_by_dom,
+                         int opener_navigation_index);
+
+  // Replace the session history with a new one, where |items| is the
+  // complete set of navigation items in the new history, and |current_index|
+  // is the index of the currently active item.
+  void ReplaceSessionHistory(ScopedVector<web::NavigationItem> items,
+                             int current_index);
+
+  // Sets the delegate used to drive the navigation controller facade.
+  void SetFacadeDelegate(NavigationManagerFacadeDelegate* facade_delegate);
+  NavigationManagerFacadeDelegate* GetFacadeDelegate() const;
+
+  // Helper functions for communicating with the facade layer.
+  // TODO(stuartmorgan): Make these private once the logic triggering them moves
+  // into this layer.
+  void OnNavigationItemChanged();
+  void OnNavigationItemCommitted();
+
+  // Returns the pending entry corresponding to the navigation that is
+  // currently in progress, or nullptr if there is none.
+  NavigationItem* GetPendingItem() const;
+
+  // Returns the transient item if any. This is an item which is removed and
+  // discarded if any navigation occurs. Note that the returned item is owned
+  // by the navigation manager and may be deleted at any time.
+  NavigationItem* GetTransientItem() const;
+
+  // Returns the last committed NavigationItem, which may be NULL if there
+  // are no committed entries.
+  NavigationItem* GetLastCommittedItem() const;
+
+  // Returns the committed NavigationItem at |index|.
+  NavigationItem* GetItemAtIndex(size_t index) const;
+
+  // Temporary accessors and content/ class pass-throughs.
+  // TODO(stuartmorgan): Re-evaluate this list once the refactorings have
+  // settled down.
+  CRWSessionController* GetSessionController();
+  int GetCurrentEntryIndex() const;
+  int GetLastCommittedEntryIndex() const;
+  int GetEntryCount() const;
+  bool RemoveEntryAtIndex(int index);
+  void DiscardNonCommittedEntries();
+  int GetPendingEntryIndex() const;
+  void LoadURL(const GURL& url,
+               const web::Referrer& referrer,
+               ui::PageTransition type);
+  bool CanGoBack() const;
+  bool CanGoForward() const;
+  void GoBack();
+  void GoForward();
+
+  // Convenience accessors to get the underlying NavigationItems from the
+  // SessionEntries returned from |session_controller_|'s -lastUserEntry and
+  // -previousEntry methods.
+  // TODO(marq):Evaluate the long-term utility of these methods.
+  NavigationItem* GetLastUserItem() const;
+  NavigationItem* GetPreviousItem() const;
+
+  // Temporary method. Returns a vector of NavigationItems corresponding to
+  // the SessionEntries of the uderlying CRWSessionController.
+  // TOOD(marq): Remove this method and unfork its caller,
+  //     TabRestoreServiceHelper::PopulateTab
+  std::vector<NavigationItem*> GetItems();
+
+  // NavigationManager:
+  BrowserState* GetBrowserState() const override;
+  WebState* GetWebState() const override;
+  web::NavigationItem* GetVisibleItem() const override;
+
+  // Copy state from |navigation_manager|, including a copy of that object's
+  // CRWSessionController.
+  // TODO(marq): This doesn't deep-copy the SessionEntries in the
+  // CRWSessionController.
+  void CopyState(NavigationManagerImpl* navigation_manager);
+ private:
+  // The primary delegate for this manager.
+  NavigationManagerDelegate* delegate_;
+
+  // The BrowserState that is associated with this instance.
+  BrowserState* browser_state_;
+
+  // CRWSessionController that backs this instance.
+  // TODO(stuartmorgan): Fold CRWSessionController into this class.
+  base::scoped_nsobject<CRWSessionController> session_controller_;
+
+  // Weak pointer to the facade delegate.
+  NavigationManagerFacadeDelegate* facade_delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigationManagerImpl);
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_NAVIGATION_NAVIGATION_MANAGER_IMPL_H_
diff --git a/ios/web/navigation/navigation_manager_impl.mm b/ios/web/navigation/navigation_manager_impl.mm
new file mode 100644
index 0000000..f445104
--- /dev/null
+++ b/ios/web/navigation/navigation_manager_impl.mm
@@ -0,0 +1,251 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/navigation/navigation_manager_impl.h"
+
+#include "base/logging.h"
+#import "ios/web/navigation/crw_session_controller+private_constructors.h"
+#import "ios/web/navigation/crw_session_controller.h"
+#import "ios/web/navigation/crw_session_entry.h"
+#include "ios/web/navigation/navigation_item_impl.h"
+#include "ios/web/navigation/navigation_manager_delegate.h"
+#import "ios/web/navigation/navigation_manager_facade_delegate.h"
+#include "ios/web/public/load_committed_details.h"
+#include "ios/web/public/navigation_item.h"
+#include "ios/web/public/web_state/web_state.h"
+#include "ui/base/page_transition_types.h"
+
+namespace {
+
+// Checks whether or not two URL are an in-page navigation (differing only
+// in the fragment).
+bool AreURLsInPageNavigation(const GURL& existing_url, const GURL& new_url) {
+  if (existing_url == new_url || !new_url.has_ref())
+    return false;
+
+  url::Replacements<char> replacements;
+  replacements.ClearRef();
+  return existing_url.ReplaceComponents(replacements) ==
+      new_url.ReplaceComponents(replacements);
+}
+
+}  // anonymous namespace
+
+namespace web {
+
+NavigationManagerImpl::NavigationManagerImpl(
+    NavigationManagerDelegate* delegate,
+    BrowserState* browser_state)
+    : delegate_(delegate),
+      browser_state_(browser_state),
+      facade_delegate_(nullptr) {
+  DCHECK(browser_state_);
+}
+
+NavigationManagerImpl::~NavigationManagerImpl() {
+  // The facade layer should be deleted before this object.
+  DCHECK(!facade_delegate_);
+
+  [session_controller_ setNavigationManager:nullptr];
+}
+
+CRWSessionController* NavigationManagerImpl::GetSessionController() {
+  return session_controller_;
+}
+
+void NavigationManagerImpl::SetSessionController(
+    CRWSessionController* session_controller) {
+  session_controller_.reset([session_controller retain]);
+  [session_controller_ setNavigationManager:this];
+}
+
+void NavigationManagerImpl::InitializeSession(NSString* window_name,
+                                              NSString* opener_id,
+                                              BOOL opened_by_dom,
+                                              int opener_navigation_index) {
+  SetSessionController([[[CRWSessionController alloc]
+         initWithWindowName:window_name
+                   openerId:opener_id
+                openedByDOM:opened_by_dom
+      openerNavigationIndex:opener_navigation_index
+               browserState:browser_state_] autorelease]);
+}
+
+void NavigationManagerImpl::ReplaceSessionHistory(
+    ScopedVector<web::NavigationItem> items,
+    int current_index) {
+  SetSessionController([[[CRWSessionController alloc]
+      initWithNavigationItems:items.Pass()
+                 currentIndex:current_index
+                 browserState:browser_state_] autorelease]);
+}
+
+void NavigationManagerImpl::SetFacadeDelegate(
+    NavigationManagerFacadeDelegate* facade_delegate) {
+  facade_delegate_ = facade_delegate;
+}
+
+NavigationManagerFacadeDelegate* NavigationManagerImpl::GetFacadeDelegate()
+    const {
+  return facade_delegate_;
+}
+
+
+void NavigationManagerImpl::OnNavigationItemChanged() {
+  if (facade_delegate_)
+    facade_delegate_->OnNavigationItemChanged();
+}
+
+void NavigationManagerImpl::OnNavigationItemCommitted() {
+  LoadCommittedDetails details;
+  details.item = GetLastCommittedItem();
+  DCHECK(details.item);
+  details.previous_item_index = [session_controller_ previousNavigationIndex];
+  if (details.previous_item_index >= 0) {
+    DCHECK([session_controller_ previousEntry]);
+    details.previous_url =
+        [session_controller_ previousEntry].navigationItem->GetURL();
+    details.is_in_page =
+        AreURLsInPageNavigation(details.previous_url, details.item->GetURL());
+  } else {
+    details.previous_url = GURL();
+    details.is_in_page = NO;
+  }
+
+  delegate_->OnNavigationItemCommitted(details);
+
+  if (facade_delegate_) {
+    facade_delegate_->OnNavigationItemCommitted(details.previous_item_index,
+                                                details.is_in_page);
+  }
+}
+
+NavigationItem* NavigationManagerImpl::GetVisibleItem() const {
+  CRWSessionEntry* entry = [session_controller_ visibleEntry];
+  return [entry navigationItem];
+}
+
+NavigationItem* NavigationManagerImpl::GetPendingItem() const {
+  return [[session_controller_ pendingEntry] navigationItem];
+}
+
+NavigationItem* NavigationManagerImpl::GetTransientItem() const {
+  return [[session_controller_ transientEntry] navigationItem];
+}
+
+NavigationItem* NavigationManagerImpl::GetLastCommittedItem() const {
+  CRWSessionEntry* entry = [session_controller_ lastCommittedEntry];
+  return [entry navigationItem];
+}
+
+NavigationItem* NavigationManagerImpl::GetItemAtIndex(size_t index) const {
+  NSArray* entries =  [session_controller_ entries];
+  return index < entries.count ? [entries[index] navigationItem] : nullptr;
+}
+
+int NavigationManagerImpl::GetCurrentEntryIndex() const {
+  return [session_controller_ currentNavigationIndex];
+}
+
+int NavigationManagerImpl::GetLastCommittedEntryIndex() const {
+  if (![[session_controller_ entries] count])
+    return -1;
+  return [session_controller_ currentNavigationIndex];
+}
+
+int NavigationManagerImpl::GetEntryCount() const {
+  return [[session_controller_ entries] count];
+}
+
+bool NavigationManagerImpl::RemoveEntryAtIndex(int index) {
+  if (index == GetLastCommittedEntryIndex() ||
+      index == GetPendingEntryIndex())
+    return false;
+
+  NSUInteger idx = static_cast<NSUInteger>(index);
+  NSArray* entries = [session_controller_ entries];
+  if (idx >= entries.count)
+    return false;
+
+  [session_controller_ removeEntryAtIndex:index];
+  return true;
+}
+
+void NavigationManagerImpl::DiscardNonCommittedEntries() {
+  [session_controller_ discardNonCommittedEntries];
+}
+
+NavigationItem* NavigationManagerImpl::GetLastUserItem() const {
+  CRWSessionEntry* entry = [session_controller_ lastUserEntry];
+  return [entry navigationItem];
+}
+
+NavigationItem* NavigationManagerImpl::GetPreviousItem() const {
+  CRWSessionEntry* entry = [session_controller_ previousEntry];
+  return [entry navigationItem];
+}
+
+int NavigationManagerImpl::GetPendingEntryIndex() const {
+  if ([session_controller_ hasPendingEntry])
+    return GetCurrentEntryIndex();
+  return -1;
+}
+
+void NavigationManagerImpl::LoadURL(const GURL& url,
+                                    const web::Referrer& referrer,
+                                    ui::PageTransition type) {
+  WebState::OpenURLParams params(url, referrer, CURRENT_TAB, type, NO);
+  delegate_->GetWebState()->OpenURL(params);
+}
+
+bool NavigationManagerImpl::CanGoBack() const {
+  return [session_controller_ canGoBack];
+}
+
+bool NavigationManagerImpl::CanGoForward() const {
+  return [session_controller_ canGoForward];
+}
+
+void NavigationManagerImpl::GoBack() {
+  if (CanGoBack()) {
+    [session_controller_ goBack];
+    // Signal the delegate to load the old page.
+    delegate_->NavigateToPendingEntry();
+  }
+}
+
+void NavigationManagerImpl::GoForward() {
+  if (CanGoForward()) {
+    [session_controller_ goForward];
+    // Signal the delegate to load the new page.
+    delegate_->NavigateToPendingEntry();
+  }
+}
+
+std::vector<NavigationItem*> NavigationManagerImpl::GetItems() {
+  std::vector<NavigationItem*> items;
+  size_t i = 0;
+  items.resize([session_controller_ entries].count);
+  for (CRWSessionEntry* entry in [session_controller_ entries]) {
+    items[i++] = entry.navigationItem;
+  }
+  return items;
+}
+
+BrowserState* NavigationManagerImpl::GetBrowserState() const {
+  return browser_state_;
+}
+
+WebState* NavigationManagerImpl::GetWebState() const {
+  return delegate_->GetWebState();
+}
+
+void NavigationManagerImpl::CopyState(
+    NavigationManagerImpl* navigation_manager) {
+  SetSessionController(
+      [[navigation_manager->GetSessionController() copy] autorelease]);
+}
+
+}  // namespace web
+
diff --git a/ios/web/navigation/navigation_manager_impl_unittest.mm b/ios/web/navigation/navigation_manager_impl_unittest.mm
new file mode 100644
index 0000000..9460fb0
--- /dev/null
+++ b/ios/web/navigation/navigation_manager_impl_unittest.mm
@@ -0,0 +1,32 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "ios/web/navigation/navigation_manager_impl.h"
+#include "ios/web/public/test/test_browser_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+namespace web {
+namespace {
+
+class NavigationManagerTest : public PlatformTest {
+ protected:
+  void SetUp() override {
+    manager_.reset(new NavigationManagerImpl(NULL, &browser_state_));
+  }
+  scoped_ptr<NavigationManagerImpl> manager_;
+  TestBrowserState browser_state_;
+};
+
+// TODO(stuartmorgan): Remove this once NavigationManager has actual
+// functionality to test, instead of just being pass-throughs.
+TEST_F(NavigationManagerTest, Dummy) {
+  EXPECT_FALSE(manager_->CanGoBack());
+  EXPECT_FALSE(manager_->CanGoForward());
+}
+
+}  // namespace
+}  // namespace web
diff --git a/ios/web/navigation/web_load_params.h b/ios/web/navigation/web_load_params.h
new file mode 100644
index 0000000..0fea4df
--- /dev/null
+++ b/ios/web/navigation/web_load_params.h
@@ -0,0 +1,60 @@
+// 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 IOS_WEB_NAVIGATION_WEB_LOAD_PARAMS_H_
+#define IOS_WEB_NAVIGATION_WEB_LOAD_PARAMS_H_
+
+#import <Foundation/Foundation.h>
+
+#import "base/mac/scoped_nsobject.h"
+#include "ios/net/request_tracker.h"
+#include "ios/web/public/referrer.h"
+#include "ui/base/page_transition_types.h"
+#include "url/gurl.h"
+
+namespace web {
+
+// Parameters for creating a tab. Most paramaters are optional, and
+// can be left at the default values set by the constructor.
+// TODO(stuartmorgan): Remove the dependency on RequestTracker, then move
+// this into NavigationManager to parallel NavigationController's
+// LoadURLParams (which this is modeled after).
+struct WebLoadParams {
+ public:
+  // The URL to load. Must be set.
+  GURL url;
+
+  // The referrer for the load. May be empty.
+  Referrer referrer;
+
+  // The transition type for the load. Defaults to PAGE_TRANSITION_LINK.
+  ui::PageTransition transition_type;
+
+  // True for renderer-initiated navigations. This is
+  // important for tracking whether to display pending URLs.
+  bool is_renderer_initiated;
+
+  // The cache mode to load with. Defaults to CACHE_NORMAL.
+  net::RequestTracker::CacheMode cache_mode;
+
+  // Any extra HTTP headers to add to the load.
+  base::scoped_nsobject<NSDictionary> extra_headers;
+
+  // Any post data to send with the load. When setting this, you should
+  // generally set a Content-Type header as well.
+  base::scoped_nsobject<NSData> post_data;
+
+  // Create a new WebLoadParams with the given URL and defaults for all other
+  // parameters.
+  explicit WebLoadParams(const GURL& url);
+  ~WebLoadParams();
+
+  // Allow copying WebLoadParams.
+  WebLoadParams(const WebLoadParams& other);
+  WebLoadParams& operator=(const WebLoadParams& other);
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_NAVIGATION_WEB_LOAD_PARAMS_H_
diff --git a/ios/web/navigation/web_load_params.mm b/ios/web/navigation/web_load_params.mm
new file mode 100644
index 0000000..2c5a82c3
--- /dev/null
+++ b/ios/web/navigation/web_load_params.mm
@@ -0,0 +1,42 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/navigation/web_load_params.h"
+
+namespace web {
+
+WebLoadParams::WebLoadParams(const GURL& url)
+    : url(url),
+      transition_type(ui::PAGE_TRANSITION_LINK),
+      is_renderer_initiated(false),
+      cache_mode(net::RequestTracker::CACHE_NORMAL),
+      post_data(NULL) {
+}
+
+WebLoadParams::~WebLoadParams() {}
+
+WebLoadParams::WebLoadParams(const WebLoadParams& other)
+    : url(other.url),
+      referrer(other.referrer),
+      transition_type(other.transition_type),
+      is_renderer_initiated(other.is_renderer_initiated),
+      cache_mode(other.cache_mode),
+      extra_headers([other.extra_headers copy]),
+      post_data([other.post_data copy]) {
+}
+
+WebLoadParams& WebLoadParams::operator=(
+    const WebLoadParams& other) {
+  url = other.url;
+  referrer = other.referrer;
+  is_renderer_initiated = other.is_renderer_initiated;
+  transition_type = other.transition_type;
+  cache_mode = other.cache_mode;
+  extra_headers.reset([other.extra_headers copy]);
+  post_data.reset([other.post_data copy]);
+
+  return *this;
+}
+
+}  // namespace web
diff --git a/ios/web/public/navigation_item.h b/ios/web/public/navigation_item.h
index a17a7e44..e067a82e 100644
--- a/ios/web/public/navigation_item.h
+++ b/ios/web/public/navigation_item.h
@@ -8,6 +8,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
+#include "ios/web/public/web_state/page_scroll_state.h"
 #include "ui/base/page_transition_types.h"
 
 class GURL;
@@ -68,6 +69,10 @@
   virtual void SetPageID(int page_id) = 0;
   virtual int32 GetPageID() const = 0;
 
+  // Stores the NavigationItem's last recorded scroll offset and zoom scale.
+  virtual void SetPageScrollState(const PageScrollState& scroll_state) = 0;
+  virtual const PageScrollState& GetPageScrollState() const = 0;
+
   // Page-related helpers ------------------------------------------------------
 
   // Returns the title to be displayed on the tab. This could be the title of
@@ -104,6 +109,11 @@
   //   - or this navigation was copied from a foreign session.
   virtual void SetTimestamp(base::Time timestamp) = 0;
   virtual base::Time GetTimestamp() const = 0;
+
+  // |true| if this item contains unsafe resources and will be removed. This
+  // property doesn't get serialized.
+  virtual void SetUnsafe(bool is_unsafe) = 0;
+  virtual bool IsUnsafe() const = 0;
 };
 
 }  // namespace web
diff --git a/ios/web/public/web_state/crw_web_view_scroll_view_proxy.h b/ios/web/public/web_state/crw_web_view_scroll_view_proxy.h
new file mode 100644
index 0000000..725577d
--- /dev/null
+++ b/ios/web/public/web_state/crw_web_view_scroll_view_proxy.h
@@ -0,0 +1,95 @@
+// 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 IOS_WEB_PUBLIC_WEB_STATE_CRW_WEB_VIEW_SCROLL_VIEW_PROXY_H_
+#define IOS_WEB_PUBLIC_WEB_STATE_CRW_WEB_VIEW_SCROLL_VIEW_PROXY_H_
+
+#import <UIKit/UIKit.h>
+
+@protocol CRWWebViewScrollViewProxyObserver;
+
+// Provides an interface for web state observers to access the WebState's
+// UIScrollView in a limited and controlled manner.
+// This class is designed to limit lifetime of the UIScrollView such that it is
+// not retained beyond WebState. It is also a way to tunnel UIScrollViewDelegate
+// callbacks.
+// NOTE: The API exposed by the proxy class isn't intended to be restrictive.
+// The features needing to access other UIScrollView properties and methods
+// needed to drive the UIScrollView are free to extend the proxy class as
+// needed.
+// The class forwards some of the methods onto the UIScrollView. For more
+// information look at the UIScrollView documentation.
+@interface CRWWebViewScrollViewProxy : NSObject<UIScrollViewDelegate>
+@property(nonatomic, assign) CGPoint contentOffset;
+@property(nonatomic, assign) UIEdgeInsets contentInset;
+@property(nonatomic, readonly) BOOL isZooming;
+@property(nonatomic, assign) UIEdgeInsets scrollIndicatorInsets;
+@property(nonatomic, assign) CGSize contentSize;
+@property(nonatomic, readonly) CGRect frame;
+@property(nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;
+@property(nonatomic, assign) BOOL bounces;
+@property(nonatomic, readonly) UIPanGestureRecognizer* panGestureRecognizer;
+// Returns the scrollview's gesture recognizers.
+@property(nonatomic, readonly) NSArray* gestureRecognizers;
+
+// Calls UIScrollView's implementation of setContentInset: directly. This
+// bypasses a very slow update path in UIWebView.
+- (void)setContentInsetFast:(UIEdgeInsets)contentInset;
+
+- (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer;
+- (void)removeGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer;
+- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;
+
+// Used by the CRWWebViewProxy to set the UIScrollView to be managed.
+- (void)setScrollView:(UIScrollView*)scrollView;
+
+// Copies all display properties that can be set on a CRWWebViewScrollViewProxy
+// from the underlying UIScrollView into |scrollView|.
+- (void)copyPropertiesToScrollView:(UIScrollView*)scrollView;
+
+// Adds |observer| to subscribe to change notifications.
+- (void)addObserver:(id<CRWWebViewScrollViewProxyObserver>)observer;
+
+// Removes |observer| as a subscriber for change notifications.
+- (void)removeObserver:(id<CRWWebViewScrollViewProxyObserver>)observer;
+
+@end
+
+// A protocol to be implemented by objects to listen for changes to the
+// UIScrollView.
+// This is an exact mirror of the UIScrollViewDelegate callbacks. For more
+// information look at the UIScrollViewDelegate documentation.
+@protocol CRWWebViewScrollViewObserver<NSObject>
+@optional
+- (void)webViewScrollViewDidScroll:
+        (CRWWebViewScrollViewProxy*)webViewScrollViewProxy;
+- (void)webViewScrollViewWillBeginDragging:
+        (CRWWebViewScrollViewProxy*)webViewScrollViewProxy;
+- (void)webViewScrollViewWillEndDragging:
+            (CRWWebViewScrollViewProxy*)webViewScrollViewProxy
+                            withVelocity:(CGPoint)velocity
+                     targetContentOffset:(inout CGPoint*)targetContentOffset;
+- (void)webViewScrollViewDidEndDragging:
+            (CRWWebViewScrollViewProxy*)webViewScrollViewProxy
+                         willDecelerate:(BOOL)decelerate;
+- (void)webViewScrollViewDidEndScrollingAnimation:
+        (CRWWebViewScrollViewProxy*)webViewScrollViewProxy;
+- (void)webViewScrollViewDidEndDecelerating:
+        (CRWWebViewScrollViewProxy*)webViewScrollViewProxy;
+- (BOOL)webViewScrollViewShouldScrollToTop:
+        (CRWWebViewScrollViewProxy*)webViewScrollViewProxy;
+@end
+
+// A protocol to be implemented by objects to listen for changes to the
+// CRWWebViewScrollViewProxyObserver.
+// It inherit from CRWWebViewScrollViewScrollViewObserver which only implements
+// methods for listening to scrollview changes.
+@protocol CRWWebViewScrollViewProxyObserver<CRWWebViewScrollViewObserver>
+@optional
+// Called when the underlying scrollview of the proxy is set.
+- (void)webViewScrollViewProxyDidSetScrollView:
+        (CRWWebViewScrollViewProxy*)webViewScrollViewProxy;
+@end
+
+#endif  // IOS_WEB_PUBLIC_WEB_STATE_CRW_WEB_VIEW_SCROLL_VIEW_PROXY_H_
diff --git a/ios/web/public/web_state/page_scroll_state.h b/ios/web/public/web_state/page_scroll_state.h
new file mode 100644
index 0000000..9984019
--- /dev/null
+++ b/ios/web/public/web_state/page_scroll_state.h
@@ -0,0 +1,75 @@
+// 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_WEB_PUBLIC_WEB_STATE_PAGE_SCROLL_STATE_H_
+#define IOS_WEB_PUBLIC_WEB_STATE_PAGE_SCROLL_STATE_H_
+
+namespace web {
+
+// Class used to represent the scrolling offset and the zoom scale of a webview.
+class PageScrollState {
+ public:
+  // Default constructor.  Initializes scroll offsets and zoom scales to NAN.
+  PageScrollState();
+  // Constructor with initial values.
+  PageScrollState(double scroll_offset_x,
+                  double scroll_offset_y,
+                  double minimum_zoom_scale,
+                  double maximum_zoom_scale,
+                  double zoom_scale);
+  ~PageScrollState();
+
+  // PageScrollStates cannot be applied until the scroll offset and zoom scale
+  // are both valid.
+  bool IsValid() const;
+
+  // The scroll offset is valid if its x and y values are both non-NAN.
+  bool IsScrollOffsetValid() const;
+
+  // Non-legacy zoom scales are valid if all three values are non-NAN and the
+  // zoom scale is within the minimum and maximum scales.  Legacy-format
+  // PageScrollStates are considered valid if the minimum and maximum scales
+  // are NAN and the zoom scale is greater than zero.
+  bool IsZoomScaleValid() const;
+
+  // PageScrollStates restored from the legacy serialization format make
+  // assumptions about the web view's implementation of zooming, and contain a
+  // non-NAN zoom scale and a NAN minimum and maximum scale.  Legacy zoom scales
+  // can only be applied to CRWUIWebViewWebControllers.
+  bool IsZoomScaleLegacyFormat() const;
+
+  // Accessors for scroll offsets and zoom scale.
+  double scroll_offset_x() const { return scroll_offset_x_; }
+  void set_scroll_offset_x(double scroll_offset_x) {
+    scroll_offset_x_ = scroll_offset_x;
+  }
+  double scroll_offset_y() const { return scroll_offset_y_; }
+  void set_scroll_offset_y(double scroll_offset_y) {
+    scroll_offset_y_ = scroll_offset_y;
+  }
+  double minimum_zoom_scale() const { return minimum_zoom_scale_; }
+  void set_minimum_zoom_scale(double minimum_zoom_scale) {
+    minimum_zoom_scale_ = minimum_zoom_scale;
+  }
+  double maximum_zoom_scale() const { return maximum_zoom_scale_; }
+  void set_maximum_zoom_scale(double maximum_zoom_scale) {
+    maximum_zoom_scale_ = maximum_zoom_scale;
+  }
+  double zoom_scale() const { return zoom_scale_; }
+  void set_zoom_scale(double zoom_scale) { zoom_scale_ = zoom_scale; }
+
+  // Comparator operator.
+  bool operator==(const PageScrollState& other) const;
+
+ private:
+  double scroll_offset_x_;
+  double scroll_offset_y_;
+  double minimum_zoom_scale_;
+  double maximum_zoom_scale_;
+  double zoom_scale_;
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_PUBLIC_WEB_STATE_PAGE_SCROLL_STATE_H_
diff --git a/ios/web/public/web_state/page_scroll_state.mm b/ios/web/public/web_state/page_scroll_state.mm
new file mode 100644
index 0000000..b7479fd4
--- /dev/null
+++ b/ios/web/public/web_state/page_scroll_state.mm
@@ -0,0 +1,72 @@
+// 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/web/public/web_state/page_scroll_state.h"
+
+#include <cmath>
+
+namespace web {
+
+namespace {
+// Returns true if:
+// - both |value1| and |value2| are NAN, or
+// - |value1| and |value2| are equal non-NAN values.
+inline bool StateValuesAreEqual(double value1, double value2) {
+  return std::isnan(value1) ? std::isnan(value2) : value1 == value2;
+}
+}  // namespace
+
+PageScrollState::PageScrollState()
+    : scroll_offset_x_(NAN),
+      scroll_offset_y_(NAN),
+      minimum_zoom_scale_(NAN),
+      maximum_zoom_scale_(NAN),
+      zoom_scale_(NAN) {
+}
+
+PageScrollState::PageScrollState(double scroll_offset_x,
+                                 double scroll_offset_y,
+                                 double minimum_zoom_scale,
+                                 double maximum_zoom_scale,
+                                 double zoom_scale)
+    : scroll_offset_x_(scroll_offset_x),
+      scroll_offset_y_(scroll_offset_y),
+      minimum_zoom_scale_(minimum_zoom_scale),
+      maximum_zoom_scale_(maximum_zoom_scale),
+      zoom_scale_(zoom_scale) {
+}
+
+PageScrollState::~PageScrollState() {
+}
+
+bool PageScrollState::IsValid() const {
+  return IsScrollOffsetValid() && IsZoomScaleValid();
+}
+
+bool PageScrollState::IsScrollOffsetValid() const {
+  return !std::isnan(scroll_offset_x_) && !std::isnan(scroll_offset_y_);
+}
+
+bool PageScrollState::IsZoomScaleValid() const {
+  return IsZoomScaleLegacyFormat() ||
+         (!std::isnan(minimum_zoom_scale_) &&
+          !std::isnan(maximum_zoom_scale_) && !std::isnan(zoom_scale_) &&
+          zoom_scale_ >= minimum_zoom_scale_ &&
+          zoom_scale_ <= maximum_zoom_scale_);
+}
+
+bool PageScrollState::IsZoomScaleLegacyFormat() const {
+  return std::isnan(minimum_zoom_scale_) && std::isnan(maximum_zoom_scale_) &&
+         zoom_scale_ > 0.0;
+}
+
+bool PageScrollState::operator==(const PageScrollState& other) const {
+  return StateValuesAreEqual(scroll_offset_x_, other.scroll_offset_x_) &&
+         StateValuesAreEqual(scroll_offset_y_, other.scroll_offset_y_) &&
+         StateValuesAreEqual(minimum_zoom_scale_, other.minimum_zoom_scale_) &&
+         StateValuesAreEqual(maximum_zoom_scale_, other.maximum_zoom_scale_) &&
+         StateValuesAreEqual(zoom_scale_, other.zoom_scale_);
+}
+
+}  // namespace web
diff --git a/ios/web/web_state/js/resources/base.js b/ios/web/web_state/js/resources/base.js
index 97b9622..216ddafb 100644
--- a/ios/web/web_state/js/resources/base.js
+++ b/ios/web/web_state/js/resources/base.js
@@ -8,6 +8,8 @@
 // this.style because member identifiers are minified by default.
 // See http://goo.gl/FwOgy
 
+goog.provide('__crweb.base');
+
 // This object is checked on the main app to know when to inject (or not).
 window['__gCrWeb'] = {};
 
diff --git a/ios/web/web_state/js/resources/common.js b/ios/web/web_state/js/resources/common.js
index 1cea022f..7e84350 100644
--- a/ios/web/web_state/js/resources/common.js
+++ b/ios/web/web_state/js/resources/common.js
@@ -4,6 +4,8 @@
 
 // This file provides common methods that can be shared by other JavaScripts.
 
+goog.provide('__crweb.common');
+
 /**
  * Namespace for this file. It depends on |__gCrWeb| having already been
  * injected. String 'common' is used in |__gCrWeb['common']| as it needs to be
@@ -576,4 +578,115 @@
       element.dispatchEvent(changeEvent);
     }, 0);
   };
+
+  /**
+   * Retrieves favicon information.
+   *
+   * @return {Object} Object containing favicon data.
+   */
+  __gCrWeb.common.getFavicons = function() {
+    var favicons = [];
+    var hasFavicon = false;
+    favicons.toJSON = null;  // Never inherit Array.prototype.toJSON.
+    var links = document.getElementsByTagName('link');
+    var linkCount = links.length;
+    for (var i = 0; i < linkCount; ++i) {
+      if (links[i].rel) {
+        var rel = links[i].rel.toLowerCase();
+        if (rel == 'shortcut icon' ||
+            rel == 'icon' ||
+            rel == 'apple-touch-icon' ||
+            rel == 'apple-touch-icon-precomposed') {
+          var favicon = {
+            rel: links[i].rel.toLowerCase(),
+            href: links[i].href
+          };
+          favicons.push(favicon);
+          if (rel == 'icon' || rel == 'shortcut icon') {
+            hasFavicon = true;
+          }
+        }
+      }
+    }
+    if (!hasFavicon) {
+      // If an HTTP(S)? webpage does not reference a "favicon" then search
+      // for a file named "favicon.ico" at the root of the website (legacy).
+      // http://en.wikipedia.org/wiki/Favicon
+      var location = document.location;
+      if (location.protocol == 'http:' || location.protocol == 'https:') {
+        var favicon = {
+          rel: 'icon',
+          href: location.origin + '/favicon.ico'
+        };
+        favicons.push(favicon);
+      }
+    }
+    return favicons;
+  };
+
+  /**
+   * Checks whether an <object> node is plugin content (as <object> can also be
+   * used to embed images).
+   * @param {HTMLElement} node The <object> node to check.
+   * @return {Boolean} Whether the node appears to be a plugin.
+   * @private
+   */
+  var objectNodeIsPlugin_ = function(node) {
+    return node.hasAttribute('classid') ||
+    (node.hasAttribute('type') && node.type.indexOf('image/') != 0);
+  };
+
+  /**
+   * Checks whether plugin a node has fallback content.
+   * @param {HTMLElement} node The node to check.
+   * @return {Boolean} Whether the node has fallback.
+   * @private
+   */
+  var pluginHasFallbackContent_ = function(node) {
+    return node.textContent.trim().length > 0 ||
+           node.getElementsByTagName('img').length > 0;
+  };
+
+  /**
+   * Returns a list of plugin elements in the document that have no fallback
+   * content. For nested plugins, only the innermost plugin element is returned.
+   * @return {Array} A list of plugin elements.
+   * @private
+   */
+  var findPluginNodesWithoutFallback_ = function() {
+    var pluginNodes = [];
+    var objects = document.getElementsByTagName('object');
+    var objectCount = objects.length;
+    for (var i = 0; i < objectCount; i++) {
+      var object = objects[i];
+      if (objectNodeIsPlugin_(object) &&
+          !pluginHasFallbackContent_(object)) {
+        pluginNodes.push(object);
+      }
+    }
+    var applets = document.getElementsByTagName('applet');
+    var appletsCount = applets.length;
+    for (var i = 0; i < appletsCount; i++) {
+      var applet = applets[i];
+      if (!pluginHasFallbackContent_(applet)) {
+        pluginNodes.push(applet);
+      }
+    }
+    return pluginNodes;
+  };
+
+  /**
+   * Finds and stores any plugins that don't have placeholders.
+   * Returns true if any plugins without placeholders are found.
+   */
+  __gCrWeb.common.updatePluginPlaceholders = function() {
+    var plugins = findPluginNodesWithoutFallback_();
+    if (plugins.length > 0) {
+      // Store the list of plugins in a known place for the replacement script
+      // to use, then trigger it.
+      __gCrWeb['placeholderTargetPlugins'] = plugins;
+      return true;
+    }
+    return false;
+  };
 }  // End of anonymous object
diff --git a/ios/web/web_state/js/resources/console.js b/ios/web/web_state/js/resources/console.js
new file mode 100644
index 0000000..0e67644
--- /dev/null
+++ b/ios/web/web_state/js/resources/console.js
@@ -0,0 +1,44 @@
+// 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.
+
+// Scripts to allow page console.log() etc. output to be seen on the console
+// of the host application.
+
+goog.provide('__crweb.console');
+
+/**
+ * Namespace for this module.
+ */
+__gCrWeb.console = {};
+
+/* Beginning of anonymous object. */
+new function() {
+  function sendConsoleMessage(method, originalArguments) {
+    message = Array.prototype.slice.call(originalArguments).join(' ');
+    __gCrWeb.message.invokeOnHost({'command': 'console',
+                                    'method': method,
+                                   'message': message,
+                                    'origin': document.location.origin});
+  }
+
+  console.log = function() {
+    sendConsoleMessage('log', arguments);
+  };
+
+  console.debug = function() {
+    sendConsoleMessage('debug', arguments);
+  };
+
+  console.info = function() {
+    sendConsoleMessage('info', arguments);
+  };
+
+  console.warn = function() {
+    sendConsoleMessage('warn', arguments);
+  };
+
+  console.error = function() {
+    sendConsoleMessage('error', arguments);
+  };
+}
diff --git a/ios/web/web_state/js/resources/core.js b/ios/web/web_state/js/resources/core.js
new file mode 100644
index 0000000..5e5d0dd
--- /dev/null
+++ b/ios/web/web_state/js/resources/core.js
@@ -0,0 +1,646 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file adheres to closure-compiler conventions in order to enable
+// compilation with ADVANCED_OPTIMIZATIONS. In particular, members that are to
+// be accessed externally should be specified in this['style'] as opposed to
+// this.style because member identifiers are minified by default.
+// See http://goo.gl/FwOgy
+
+goog.provide('__crweb.core');
+
+/**
+ * The Chrome object is populated in an anonymous object defined at
+ * initialization to prevent polluting the global namespace.
+ */
+
+/* Beginning of anonymous object. */
+new function() {
+  // TODO(jimblackler): use this namespace as a wrapper for all externally-
+  // visible functions, to be consistent with other JS scripts. crbug.com/380390
+  __gCrWeb['core'] = {};
+
+  // JavaScript errors are logged on the main application side. The handler is
+  // added ASAP to catch any errors in startup. Note this does not appear to
+  // work in iOS < 5.
+  window.addEventListener('error', function(event) {
+    // Sadly, event.filename and event.lineno are always 'undefined' and '0'
+    // with UIWebView.
+    invokeOnHost_({'command': 'window.error',
+                   'message': event.message.toString()});
+  });
+
+  /**
+   * Margin in points around touchable elements (e.g. links for custom context
+   * menu).
+   * @type {number}
+   */
+  var touchMargin_ = 25;
+
+  __gCrWeb['innerSizeAsString'] = function() {
+    return window.innerWidth + '/' + window.innerHeight;
+  };
+
+  // Implementation of document.elementFromPoint that is working for iOS4 and
+  // iOS5 and that also goes into frames and iframes.
+  var elementFromPoint_ = function(x, y) {
+    var elementFromPointIsUsingViewPortCoordinates = function(win) {
+      if (win.pageYOffset > 0) {  // Page scrolled down.
+        return (win.document.elementFromPoint(
+            0, win.pageYOffset + win.innerHeight - 1) === null);
+      }
+      if (win.pageXOffset > 0) {  // Page scrolled to the right.
+        return (win.document.elementFromPoint(
+            win.pageXOffset + win.innerWidth - 1, 0) === null);
+      }
+      return false;  // No scrolling, don't care.
+    };
+
+    var newCoordinate = function(x, y) {
+      var coordinates = {
+          x: x, y: y,
+          viewPortX: x - window.pageXOffset, viewPortY: y - window.pageYOffset,
+          useViewPortCoordinates: false,
+          window: window
+      };
+      return coordinates;
+    };
+
+    // Returns the coordinates of the upper left corner of |obj| in the
+    // coordinates of the window that |obj| is in.
+    var getPositionInWindow = function(obj) {
+      var coord = { x: 0, y: 0 };
+      while (obj.offsetParent) {
+        coord.x += obj.offsetLeft;
+        coord.y += obj.offsetTop;
+        obj = obj.offsetParent;
+      }
+      return coord;
+    };
+
+    var elementsFromCoordinates = function(coordinates) {
+      coordinates.useViewPortCoordinates = coordinates.useViewPortCoordinates ||
+          elementFromPointIsUsingViewPortCoordinates(coordinates.window);
+
+      var currentElement = null;
+      if (coordinates.useViewPortCoordinates) {
+        currentElement = coordinates.window.document.elementFromPoint(
+            coordinates.viewPortX, coordinates.viewPortY);
+      } else {
+        currentElement = coordinates.window.document.elementFromPoint(
+            coordinates.x, coordinates.y);
+      }
+      // We have to check for tagName, because if a selection is made by the
+      // UIWebView, the element we will get won't have one.
+      if (!currentElement || !currentElement.tagName) {
+        return null;
+      }
+      if (currentElement.tagName.toLowerCase() === 'iframe' ||
+          currentElement.tagName.toLowerCase() === 'frame') {
+        // The following condition is true if the iframe is in a different
+        // domain; no further information is accessible.
+        if (typeof(currentElement.contentWindow.document) == 'undefined') {
+          invokeOnHost_({
+              'command': 'window.error',
+              'message': 'iframe contentWindow.document is not accessible.'});
+          return currentElement;
+        }
+        var framePosition = getPositionInWindow(currentElement);
+        coordinates.viewPortX -=
+            framePosition.x - coordinates.window.pageXOffset;
+        coordinates.viewPortY -=
+            framePosition.y - coordinates.window.pageYOffset;
+        coordinates.window = currentElement.contentWindow;
+        coordinates.x -= framePosition.x + coordinates.window.pageXOffset;
+        coordinates.y -= framePosition.y + coordinates.window.pageYOffset;
+        return elementsFromCoordinates(coordinates);
+      }
+      return currentElement;
+    };
+
+    return elementsFromCoordinates(newCoordinate(x, y));
+  };
+
+  var spiralCoordinates = function(x, y) {
+    var coordinates = [];
+
+    var maxAngle = Math.PI * 2.0 * 3.0;
+    var pointCount = 30;
+    var angleStep = maxAngle / pointCount;
+    var speed = touchMargin_ / maxAngle;
+
+    for (var index = 0; index < pointCount; index++) {
+      var angle = angleStep * index;
+      var radius = angle * speed;
+
+      coordinates.push({x: x + Math.round(Math.cos(angle) * radius),
+                        y: y + Math.round(Math.sin(angle) * radius)});
+    }
+
+    return coordinates;
+  };
+
+  // Returns the url of the image or link under the selected point. Returns an
+  // empty string if no links or images are found.
+  __gCrWeb['getElementFromPoint'] = function(x, y) {
+    var hitCoordinates = spiralCoordinates(x, y);
+    for (var index = 0; index < hitCoordinates.length; index++) {
+      var coordinates = hitCoordinates[index];
+
+      var element = elementFromPoint_(coordinates.x, coordinates.y);
+      if (!element || !element.tagName) {
+        // Nothing under the hit point. Try the next hit point.
+        continue;
+      }
+
+      if (getComputedWebkitTouchCallout_(element) === 'none')
+        continue;
+      // Also check element's ancestors. A bound on the level is used here to
+      // avoid large overhead when no links or images are found.
+      var level = 0;
+      while (++level < 8 && element && element != document) {
+        var tagName = element.tagName;
+        if (!tagName)
+          continue;
+        tagName = tagName.toLowerCase();
+
+        if (tagName === 'input' || tagName === 'textarea' ||
+            tagName === 'select' || tagName === 'option') {
+          // If the element is a known input element, stop the spiral search and
+          // return empty results.
+          return '{}';
+        }
+
+        if (tagName === 'a' && element.href) {
+          // Found a link.
+          return __gCrWeb.common.JSONStringify(
+              {href: element.href,
+               referrerPolicy: getReferrerPolicy_(element)});
+        }
+
+        if (tagName === 'img' && element.src) {
+          // Found an image.
+          var result = {src: element.src,
+                        referrerPolicy: getReferrerPolicy_()};
+          // Copy the title, if any.
+          if (element.title) {
+            result.title = element.title;
+          }
+          // Check if the image is also a link.
+          var parent = element.parentNode;
+          while (parent) {
+            if (parent.tagName &&
+                parent.tagName.toLowerCase() === 'a' &&
+                parent.href) {
+              // This regex identifies strings like void(0),
+              // void(0)  ;void(0);, ;;;;
+              // which result in a NOP when executed as JavaScript.
+              var regex = RegExp("^javascript:(?:(?:void\\(0\\)|;)\\s*)+$");
+              if (parent.href.match(regex)) {
+                parent = parent.parentNode;
+                continue;
+              }
+              result.href = parent.href;
+              result.referrerPolicy = getReferrerPolicy_(parent);
+              break;
+            }
+            parent = parent.parentNode;
+          }
+          return __gCrWeb.common.JSONStringify(result);
+        }
+        element = element.parentNode;
+      }
+    }
+    return '{}';
+  };
+
+  // Returns true if the top window or any frames inside contain an input
+  // field of type 'password'.
+  __gCrWeb['hasPasswordField'] = function() {
+    return hasPasswordField_(window);
+  };
+
+  // Returns a string that is formatted according to the JSON syntax rules.
+  // This is equivalent to the built-in JSON.stringify() function, but is
+  // less likely to be overridden by the website itself.  This public function
+  // should not be used if spoofing it would create a security vulnerability.
+  // The |__gCrWeb| object itself does not use it; it uses its private
+  // counterpart instead.
+  // Prevents websites from changing stringify's behavior by adding the
+  // method toJSON() by temporarily removing it.
+  __gCrWeb['stringify'] = function(value) {
+    if (value === null)
+      return 'null';
+    if (value === undefined)
+      return undefined;
+    if (typeof(value.toJSON) == 'function') {
+      var originalToJSON = value.toJSON;
+      value.toJSON = undefined;
+      var stringifiedValue = __gCrWeb.common.JSONStringify(value);
+      value.toJSON = originalToJSON;
+      return stringifiedValue;
+    }
+    return __gCrWeb.common.JSONStringify(value);
+  };
+
+  /*
+   * Adds the listeners that are used to handle forms, enabling autofill and
+   * the replacement method to dismiss the keyboard needed because of the
+   * Autofill keyboard accessory.
+   */
+  function addFormEventListeners_() {
+    // Focus and input events for form elements are messaged to the main
+    // application for broadcast to CRWWebControllerObservers.
+    // This is done with a single event handler for each type being added to the
+    // main document element which checks the source element of the event; this
+    // is much easier to manage than adding handlers to individual elements.
+    var formActivity = function(evt) {
+      var srcElement = evt.srcElement;
+      var fieldName = srcElement.name || '';
+      var value = srcElement.value || '';
+
+      var msg = {
+        'command': 'form.activity',
+        'formName': __gCrWeb.common.getFormIdentifier(evt.srcElement.form),
+        'fieldName': fieldName,
+        'type': evt.type,
+        'value': value
+      };
+      if (evt.keyCode)
+        msg.keyCode = evt.keyCode;
+      invokeOnHost_(msg);
+    };
+
+    // Focus events performed on the 'capture' phase otherwise they are often
+    // not received.
+    document.addEventListener('focus', formActivity, true);
+    document.addEventListener('blur', formActivity, true);
+    document.addEventListener('change', formActivity, true);
+
+    // Text input is watched at the bubbling phase as this seems adequate in
+    // practice and it is less obtrusive to page scripts than capture phase.
+    document.addEventListener('input', formActivity, false);
+    document.addEventListener('keyup', formActivity, false);
+  };
+
+  // Returns true if the supplied window or any frames inside contain an input
+  // field of type 'password'.
+  // @private
+  var hasPasswordField_ = function(win) {
+    var doc = win.document;
+
+    // We may will not be allowed to read the 'document' property from a frame
+    // that is in a different domain.
+    if (!doc) {
+      return false;
+    }
+
+    if (doc.querySelector('input[type=password]')) {
+      return true;
+    }
+
+    var frames = win.frames;
+    for (var i = 0; i < frames.length; i++) {
+      if (hasPasswordField_(frames[i])) {
+        return true;
+      }
+    }
+
+    return false;
+  };
+
+  function invokeOnHost_(command) {
+    __gCrWeb.message.invokeOnHost(command);
+  };
+
+  function invokeOnHostImmediate_(command) {
+    __gCrWeb.message.invokeOnHostImmediate(command);
+  };
+
+  /**
+   * Gets the referrer policy to use for navigations away from the current page.
+   * If a link element is passed, and it includes a rel=noreferrer tag, that
+   * will override the page setting.
+   * @param {HTMLElement} linkElement The link triggering the navigation.
+   * @return {String} The policy string.
+   * @private
+   */
+  var getReferrerPolicy_ = function(linkElement) {
+    if (linkElement) {
+      var rel = linkElement.getAttribute('rel');
+      if (rel && rel.toLowerCase() == 'noreferrer') {
+        return 'never';
+      }
+    }
+
+    var metaTags = document.getElementsByTagName('meta');
+    for (var i = 0; i < metaTags.length; ++i) {
+      if (metaTags[i].name.toLowerCase() == 'referrer') {
+        return metaTags[i].content.toLowerCase();
+      }
+    }
+    return 'default';
+  };
+
+  // Provides a way for other injected javascript to access the page's referrer
+  // policy.
+  __gCrWeb['getPageReferrerPolicy'] = function() {
+    return getReferrerPolicy_();
+  };
+
+  // Various aspects of global DOM behavior are overridden here.
+
+  // A popstate event needs to be fired anytime the active history entry
+  // changes. Either via back, forward, go navigation or by loading the URL,
+  // clicking on a link, etc.
+  __gCrWeb['dispatchPopstateEvent'] = function(stateObject) {
+    var popstateEvent = window.document.createEvent('HTMLEvents');
+    popstateEvent.initEvent('popstate', true, false);
+    if (stateObject)
+      popstateEvent.state = JSON.parse(stateObject);
+
+    // setTimeout() is used in order to return immediately. Otherwise the
+    // dispatchEvent call waits for all event handlers to return, which could
+    // cause a ReentryGuard failure.
+    window.setTimeout(function() {
+      window.dispatchEvent(popstateEvent);
+    }, 0);
+  };
+
+  // Keep the original replaceState() method. It's needed to update UIWebView's
+  // URL and window.history.state property during history navigations that don't
+  // cause a page load.
+  var originalWindowHistoryReplaceState = window.history.replaceState;
+  __gCrWeb['replaceWebViewURL'] = function(url, stateObject) {
+    originalWindowHistoryReplaceState.call(history, stateObject, null, url);
+  };
+
+  // Intercept window.history methods to call back/forward natively.
+  window.history.back = function() {
+    invokeOnHost_({'command': 'window.history.back'});
+  };
+  window.history.forward = function() {
+    invokeOnHost_({'command': 'window.history.forward'});
+  };
+  window.history.go = function(delta) {
+    invokeOnHost_({'command': 'window.history.go', 'value': delta});
+  };
+  window.history.pushState = function(stateObject, pageTitle, pageUrl) {
+    __gCrWeb.core_dynamic.historyWillChangeState();
+    // Calling stringify() on undefined causes a JSON parse error.
+    var serializedState =
+        typeof(stateObject) == 'undefined' ? '' :
+            __gCrWeb.common.JSONStringify(stateObject);
+    pageUrl = pageUrl || window.location.href;
+    originalWindowHistoryReplaceState.call(history, stateObject, null, pageUrl);
+    invokeOnHost_({'command': 'window.history.didPushState',
+                   'stateObject': serializedState,
+                   'baseUrl': document.baseURI,
+                   'pageUrl': pageUrl.toString()});
+  };
+  window.history.replaceState = function(stateObject, pageTitle, pageUrl) {
+    __gCrWeb.core_dynamic.historyWillChangeState();
+    // Calling stringify() on undefined causes a JSON parse error.
+    var serializedState =
+        typeof(stateObject) == 'undefined' ? '' :
+            __gCrWeb.common.JSONStringify(stateObject);
+    pageUrl = pageUrl || window.location.href;
+    originalWindowHistoryReplaceState.call(history, stateObject, null, pageUrl);
+    invokeOnHost_({'command': 'window.history.didReplaceState',
+                   'stateObject': serializedState,
+                   'baseUrl': document.baseURI,
+                   'pageUrl': pageUrl.toString()});
+  };
+
+  __gCrWeb['getFullyQualifiedURL'] = function(originalURL) {
+    // A dummy anchor (never added to the document) is used to obtain the
+    // fully-qualified URL of |originalURL|.
+    var anchor = document.createElement('a');
+    anchor.href = originalURL;
+    return anchor.href;
+  };
+
+  // Intercept window.close calls.
+  window.close = function() {
+    invokeOnHost_({'command': 'window.close.self'});
+  };
+
+  window.addEventListener('hashchange', function(evt) {
+    invokeOnHost_({'command': 'window.hashchange'});
+  });
+
+  __gCrWeb.core_dynamic.addEventListeners();
+
+  // Returns if a frame with |name| is found in |currentWindow|.
+  // Note frame.name is undefined for cross domain frames.
+  var hasFrame_ = function(currentWindow, name) {
+    if (currentWindow.name === name)
+      return true;
+
+    var frames = currentWindow.frames;
+    for (var index = 0; index < frames.length; ++index) {
+      var frame = frames[index];
+      if (frame === undefined)
+        continue;
+      if (hasFrame_(frame, name))
+        return true;
+    }
+    return false;
+  };
+
+  // Checks if |node| is an anchor to be opened in the current tab.
+  var isInternaLink_ = function(node) {
+    if (!node instanceof HTMLAnchorElement)
+      return false;
+
+    // Anchor with href='javascript://.....' will be opened in the current tab
+    // for simplicity.
+    if (node.href.indexOf('javascript:') == 0)
+      return true;
+
+    // UIWebView will take care of the following cases.
+    //
+    // - If the given browsing context name is the empty string or '_self', then
+    //   the chosen browsing context must be the current one.
+    //
+    // - If the given browsing context name is '_parent', then the chosen
+    //   browsing context must be the parent browsing context of the current
+    //   one, unless there is no one, in which case the chosen browsing context
+    //   must be the current browsing context.
+    //
+    // - If the given browsing context name is '_top', then the chosen browsing
+    //   context must be the top-level browsing context of the current one, if
+    //   there is one, or else the current browsing context.
+    //
+    // Here an undefined target is considered in the same way as an empty
+    // target.
+    if (node.target === undefined || node.target === '' ||
+        node.target === '_self' || node.target === '_parent' ||
+        node.target === '_top') {
+      return true;
+    }
+
+    // A new browsing context is being requested for an '_blank' target.
+    if (node.target === '_blank')
+      return false;
+
+    // Otherwise UIWebView will take care of the case where there exists a
+    // browsing context whose name is the same as the given browsing context
+    // name. If there is no such a browsing context, a new browsing context is
+    // being requested.
+    return hasFrame_(window, node.target);
+  };
+
+  var getTargetLink_ = function(target) {
+    var node = target;
+    // Find the closest ancester that is a link.
+    while (node) {
+      if (node instanceof HTMLAnchorElement)
+        break;
+      node = node.parentNode;
+    }
+    return node;
+  };
+
+  var setExternalRequest_ = function(href, target) {
+    if (typeof(target) == 'undefined' || target == '_blank' || target == '') {
+      target = '' + Date.now() + '-' + Math.random();
+    }
+    if (typeof(href) == 'undefined') {
+      // W3C recommended behavior.
+      href = 'about:blank';
+    }
+    // ExternalRequest messages need to be handled before the expected
+    // shouldStartLoadWithRequest, as such we cannot wait for the regular
+    // message queue invoke which delays to avoid illegal recursion into
+    // UIWebView. This immediate class of messages is handled ASAP by
+    // CRWWebController.
+    invokeOnHostImmediate_({'command': 'externalRequest',
+                               'href': href,
+                             'target': target,
+                     'referrerPolicy': getReferrerPolicy_()});
+  };
+
+  var resetExternalRequest_ = function() {
+    invokeOnHost_({'command': 'resetExternalRequest'});
+  };
+
+  var clickBubbleListener_ = function(evt) {
+    if (evt['defaultPrevented']) {
+      resetExternalRequest_();
+    }
+    // Remove the listener.
+    evt.currentTarget.removeEventListener(
+        'click', clickBubbleListener_, false);
+  };
+
+  var getComputedWebkitTouchCallout_ = function(element) {
+    return window.getComputedStyle(element, null)['webkitTouchCallout'];
+  };
+
+  /**
+   * This method applies the various document-level overrides. Sometimes the
+   * document object gets reset in the early stages of the page lifecycle, so
+   * this is exposed as a method for the application to invoke later. That way
+   * the window-level overrides can be applied as soon as possible.
+   */
+  __gCrWeb.core.documentInject = function() {
+    // Perform web view specific operations requiring document.body presence.
+    // If necessary returns and waits for document to be present.
+    if (!__gCrWeb.core_dynamic.documentInject())
+      return;
+
+    document.addEventListener('click', function(evt) {
+      var node = getTargetLink_(evt.target);
+
+      if (!node)
+        return;
+
+      if (isInternaLink_(node)) {
+        return;
+      }
+      setExternalRequest_(node.href, node.target);
+      // Add listener to the target and its immediate ancesters. These event
+      // listeners will be removed if they get called. The listeners for some
+      // elements might never be removed, but if multiple identical event
+      // listeners are registered on the same event target with the same
+      // parameters the duplicate instances are discarded.
+      for (var level = 0; level < 5; ++level) {
+        if (node && node != document) {
+          node.addEventListener('click', clickBubbleListener_, false);
+          node = node.parentNode;
+        } else {
+          break;
+        }
+      }
+    }, true);
+
+    // Intercept clicks on anchors (links) during bubbling phase so that the
+    // browser can handle target type appropriately.
+    document.addEventListener('click', function(evt) {
+      var node = getTargetLink_(evt.target);
+
+      if (!node)
+        return;
+
+      if (isInternaLink_(node)) {
+        if (evt['defaultPrevented'])
+          return;
+        // Internal link. The web view will handle navigation, but register
+        // the anchor for UIWebView to start the progress indicator ASAP and
+        // notify web controller as soon as possible of impending navigation.
+        if (__gCrWeb.core_dynamic.handleInternalClickEvent) {
+          __gCrWeb.core_dynamic.handleInternalClickEvent(node);
+        }
+        return;
+      } else {
+        // Resets the external request if it has been canceled, otherwise
+        // updates the href in case it has been changed.
+        if (evt['defaultPrevented'])
+          resetExternalRequest_();
+        else
+          setExternalRequest_(node.href, node.target);
+      }
+    }, false);
+
+    // Capture form submit actions.
+    document.addEventListener('submit', function(evt) {
+      if (evt['defaultPrevented'])
+        return;
+
+      var form = evt.target;
+      var targetsFrame = form.target && hasFrame_(window, form.target);
+      // TODO(stuartmorgan): Handle external targets. crbug.com/233543
+
+      var action = form.getAttribute('action');
+      // Default action is to re-submit to same page.
+      if (!action)
+        action = document.location.href;
+      invokeOnHost_({
+               'command': 'document.submit',
+              'formName': __gCrWeb.common.getFormIdentifier(evt.srcElement),
+                  'href': __gCrWeb['getFullyQualifiedURL'](action),
+          'targetsFrame': targetsFrame
+      });
+    }, false);
+
+    addFormEventListeners_();
+
+   // Handle or wait for and handle document load completion, if applicable.
+   if (__gCrWeb.core_dynamic.handleDocumentLoaded)
+     __gCrWeb.core_dynamic.handleDocumentLoaded();
+
+    return true;
+  };
+
+  __gCrWeb.core.documentInject();
+
+  // Form prototype loaded with event to supply Autocomplete API
+  // functionality.
+  HTMLFormElement.prototype.requestAutocomplete = function() {
+    invokeOnHost_(
+         {'command': 'form.requestAutocomplete',
+         'formName': __gCrWeb.common.getFormIdentifier(this)});
+  };
+}  // End of anonymous object
diff --git a/ios/web/web_state/js/resources/core_dynamic_ui.js b/ios/web/web_state/js/resources/core_dynamic_ui.js
new file mode 100644
index 0000000..e8988b7
--- /dev/null
+++ b/ios/web/web_state/js/resources/core_dynamic_ui.js
@@ -0,0 +1,157 @@
+// 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.
+
+// Scripts that are conceptually part of core.js, but have UIWebView-specific
+// details/behaviors.
+
+goog.provide('__crweb.core_dynamic_ui');
+
+/**
+ * Namespace for this module.
+ */
+__gCrWeb.core_dynamic = {};
+
+/* Beginning of anonymous object. */
+new function() {
+  /**
+   * Resets common.JSONStringify to a clean copy. This can be called to ensure
+   * that its copy is not of an override injected earlier by the page. This
+   * must be called after document.body is present.
+   */
+  var resetJsonStringify_ = function() {
+    var frame = document.createElement('iframe');
+    // crwebnull protocol returns NO immediately to reject the load attempt.
+    // A new frame is still created with an independent window object.
+    frame.src = 'crwebnull://';
+    document.body.appendChild(frame);
+    // One some occasions the contentWindow is not available, or the JSON object
+    // is not available. It is not clear why, but this defense still has value
+    // when it can be applied.
+    if (frame.contentWindow && frame.contentWindow.JSON) {
+      // Refresh original stringify object from new window reference (if
+      // available) in case the originally retained version was not the native
+      // version.
+      __gCrWeb.common.JSONStringify = frame.contentWindow.JSON.stringify;
+    }
+    document.body.removeChild(frame);
+  };
+
+  /**
+   * Adds UIWebView specific event listeners.
+   */
+  __gCrWeb.core_dynamic.addEventListeners = function() {
+    window.addEventListener('unload', function(evt) {
+      // In the case of a newly-created UIWebView, the URL starts as
+      // about:blank, the window.unload event fires, and then the URL changes.
+      // However, at this point the window object is *not* reset. After this,
+      // when the page changes for any reason the window object *is* reset.
+      // For this reason, we do not report the window.unload event from the
+      // default page to the first URL.
+      // This is sent as an immediate command because if the message arrives
+      // after the page change has taken effect, false positive security errors
+      // can occur.
+      if (!document._defaultPage)
+        __gCrWeb.message.invokeOnHostImmediate({'command': 'window.unload'});
+    });
+  };
+
+  /**
+   * Applies UIWebView specific document-level overrides. These overrides
+   * require the document body to be present; therefore the method sets event
+   * listeners and timers to retry upon document body load if document body is
+   * not yet present. Returns false if on default page or document is not
+   * present.
+   */
+  __gCrWeb.core_dynamic.documentInject = function() {
+    // The default page gets the injections to the window object, but not the
+    // document object. On the first occasion the page changes (from the default
+    // about:blank) the window.unload event fires but the window object does not
+    // actually reset. However by the time the DOMContentLoaded event fires the
+    // document object will have been reset (to a non-default page).
+    if (document && document['_defaultPage']) {
+      window.addEventListener('DOMContentLoaded', __gCrWeb.core.documentInject);
+      return false;
+    }
+    if (!document || !document.body) {
+      // Either the document or document body is not yet available... retest in
+      // 1 / 4 of a second.
+      window.setTimeout(__gCrWeb.core.documentInject, 250);
+      return false;
+    }
+
+    // Try to guarantee a clean copy of common.JSONStringify.
+    resetJsonStringify_();
+
+    // Flush the message queue and send document.present message, if load has
+    // not been aborted.
+    if (!document._cancelled) {
+      if (__gCrWeb.message) {
+        __gCrWeb.message.invokeQueues();
+      }
+      __gCrWeb.message.invokeOnHost({'command': 'document.present'});
+    }
+
+    // Add event listener for title changes.
+    var lastSeenTitle = document.title;
+    document.addEventListener('DOMSubtreeModified', function(evt) {
+      if (document.title !== lastSeenTitle) {
+        lastSeenTitle = document.title;
+        __gCrWeb.message.invokeOnHost({'command': 'document.retitled'});
+      }
+    });
+
+    return true;
+  };
+
+  /**
+   * Notifies client and handles post-document load tasks when document has
+   * finished loading.
+   */
+  __gCrWeb.core_dynamic.handleDocumentLoaded = function() {
+    var invokeOnHost_ = __gCrWeb.message.invokeOnHost;
+    var loaded_ = function() {
+      invokeOnHost_({'command': 'document.loaded'});
+      // Send the favicons to the browser.
+      invokeOnHost_({'command': 'document.favicons',
+                    'favicons': __gCrWeb.common.getFavicons()});
+      // Add placeholders for plugin content.
+      if (__gCrWeb.common.updatePluginPlaceholders())
+        __gCrWeb.message.invokeOnHost({'command': 'addPluginPlaceholders'});
+    };
+
+    if (document.readyState === 'loaded' || document.readyState === 'complete')
+      loaded_();
+    else
+      window.addEventListener('load', loaded_);
+  }
+
+
+  /**
+   * Sends anchor.click message.
+   */
+  __gCrWeb.core_dynamic.handleInternalClickEvent = function(node) {
+    __gCrWeb.message.invokeOnHost({'command': 'anchor.click',
+                                   'href': node.href});
+  }
+
+  /**
+   * Called when history.pushState and history.replaceState are invoked.
+   */
+  __gCrWeb.core_dynamic.historyWillChangeState = function () {
+    // UIWebViewWebController does not need to be notified prior to
+    // history.pushState or history.replaceState calls.
+  };
+
+  /**
+   * Exits Fullscreen video by calling webkitExitFullScreen on every video
+   * element.
+   */
+  __gCrWeb['exitFullscreenVideo'] = function() {
+    var videos = document.getElementsByTagName('video');
+    var videosLength = videos.length;
+    for (var i = 0; i < videosLength; ++i) {
+      videos[i].webkitExitFullScreen();
+    }
+  };
+}
diff --git a/ios/web/web_state/js/resources/core_dynamic_wk.js b/ios/web/web_state/js/resources/core_dynamic_wk.js
new file mode 100644
index 0000000..dd91478
--- /dev/null
+++ b/ios/web/web_state/js/resources/core_dynamic_wk.js
@@ -0,0 +1,59 @@
+// 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.
+
+// Scripts that are conceptually part of core.js, but have WKWebView-specific
+// details/behaviors.
+
+goog.provide('__crweb.core_dynamic_wk');
+
+/**
+ * Namespace for this module.
+ */
+__gCrWeb.core_dynamic = {};
+
+/* Beginning of anonymous object. */
+new function() {
+  /**
+   * Adds WKWebView specific event listeners.
+   */
+  __gCrWeb.core_dynamic.addEventListeners = function() {
+    // So far there are no WKWebView specific event listeners.
+  };
+
+  /**
+   * Applies WKWebView specific document-level overrides. Script injection for
+   * WKWebView always happens after the document is presented; therefore, there
+   * is no need to provide for invoking documentInject at a later time.
+   */
+  __gCrWeb.core_dynamic.documentInject = function() {
+    // Flush the message queue.
+    if (__gCrWeb.message) {
+      __gCrWeb.message.invokeQueues();
+    }
+    return true;
+  };
+
+  /**
+   * Handles document load completion tasks. Invoked from
+   * [WKNavigationDelegate webView:didFinishNavigation:], when document load is
+   * complete.
+   */
+  __gCrWeb.didFinishNavigation = function() {
+    // Send the favicons to the browser.
+    __gCrWeb.message.invokeOnHost({'command': 'document.favicons',
+                                   'favicons': __gCrWeb.common.getFavicons()});
+    // Add placeholders for plugin content.
+    if (__gCrWeb.common.updatePluginPlaceholders())
+      __gCrWeb.message.invokeOnHost({'command': 'addPluginPlaceholders'});
+  }
+
+  /**
+   * Sends window.history.willChangeState message. Called when
+   * history.pushState and history.replaceState are invoked.
+   */
+  __gCrWeb.core_dynamic.historyWillChangeState = function() {
+    __gCrWeb.message.invokeOnHost(
+        {'command': 'window.history.willChangeState'});
+  };
+}
diff --git a/ios/web/web_state/js/resources/dialog_overrides.js b/ios/web/web_state/js/resources/dialog_overrides.js
new file mode 100644
index 0000000..d5c582ef
--- /dev/null
+++ b/ios/web/web_state/js/resources/dialog_overrides.js
@@ -0,0 +1,114 @@
+// 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.
+
+goog.provide('__crweb.dialog_overrides');
+
+// Namespace for this module.
+__gCrWeb.dialogOverrides = {};
+
+// Beginning of anonymous object.
+new function() {
+  /*
+   * Install a wrapper around functions displaying dialogs in order to catch
+   * code displaying dialog.
+   *
+   * Since the Javascript on the page may cache the value of those functions
+   * and invoke them later, we must only install the wrapper once and change
+   * their behaviour when required.
+   *
+   * Returns a function that allows changing the value of the two booleans
+   * |suppressDialogs| and |notifyAboutDialogs| that are tested by the wrappers.
+   */
+  var installDialogOverridesMethods = function() {
+    var suppressDialogs = false;
+    var notifyAboutDialogs = false;
+
+    // Returns a wrapper function around |originalDialog|. The wrapper may
+    // suppress the dialog and notify host about show/suppress.
+    var makeDialogWrapper = function(originalDialogGetter) {
+      return function() {
+        if (!suppressDialogs) {
+          if (notifyAboutDialogs) {
+            __gCrWeb.message.invokeOnHost({'command': 'dialog.willShow'});
+          }
+          return originalDialogGetter().apply(null, arguments);
+        } else if (notifyAboutDialogs) {
+          __gCrWeb.message.invokeOnHost({'command': 'dialog.suppressed'});
+        }
+      };
+    };
+
+    // Install wrapper around the following properties of |window|.
+    var wrappedFunctionNames = ['alert', 'confirm', 'prompt', 'open'];
+    var len = wrappedFunctionNames.length;
+    for (var i = 0; i < len; i++) {
+      (function(wrappedFunctionName) {
+        var wrappedDialogMethod = window[wrappedFunctionName];
+        window[wrappedFunctionName] = makeDialogWrapper(
+          function() { return wrappedDialogMethod; });
+      })(wrappedFunctionNames[i]);
+    }
+
+    // Reading or writing to the property 'geolocation' too early breaks
+    // the API. Make a copy of navigator and stub in the required methods
+    // without touching the property. See crbug.com/280818 for more
+    // details.
+    var stubNavigator = {};
+
+    // Copy all properties and functions without touching 'geolocation'.
+    var oldNavigator = navigator;
+    for (var keyName in navigator) {
+      if (keyName !== 'geolocation') {
+        var value = navigator[keyName];
+        if (typeof(value) == 'function') {
+          // Forward functions calls to real navigator.
+          stubNavigator[keyName] = function() {
+            return value.apply(oldNavigator, arguments);
+          }
+        } else {
+          Object['defineProperty'](stubNavigator, keyName, {
+            value: value,
+            configurable: false,
+            writable: false,
+            enumerable: true
+          });
+        }
+      }
+    }
+
+    // Stub in 'geolocation' if necessary, using delayed accessor for the
+    // 'geolocation' property of the original |navigator|.
+    if ('geolocation' in navigator) {
+      var geolocation = {};
+      var geoPropNames = ['getCurrentPosition', 'watchPosition', 'clearWatch'];
+      var len = geoPropNames.length;
+      for (var i = 0; i < len; i++) {
+        (function(geoPropName) {
+          geolocation[geoPropName] = makeDialogWrapper(function() {
+             return function() {
+               return oldNavigator.geolocation[geoPropName].apply(
+                   oldNavigator.geolocation, arguments);
+             };
+          });
+        })(geoPropNames[i]);
+      }
+      stubNavigator.geolocation = geolocation;
+    }
+
+    // Install |stubNavigator| as |navigator|.
+    navigator = stubNavigator;
+
+    // Returns the closure allowing to change |suppressDialogs| and
+    // |notifyAboutDialogs| variables.
+    return function(setEnabled, setNotify) {
+      suppressDialogs = setEnabled;
+      notifyAboutDialogs = setNotify;
+    };
+  };
+
+  // Override certain methods that produce dialogs. This needs to be installed
+  // after other window methods overrides.
+  __gCrWeb['setSuppressDialogs'] = installDialogOverridesMethods();
+
+}  // End of anonymous object
diff --git a/ios/web/web_state/js/resources/message.js b/ios/web/web_state/js/resources/message.js
index 40ebda4..30e4a6b 100644
--- a/ios/web/web_state/js/resources/message.js
+++ b/ios/web/web_state/js/resources/message.js
@@ -4,6 +4,8 @@
 
 // Scripts for the message handler.
 
+goog.provide('__crweb.message');
+
 /**
  * Namespace for this module.
  */
diff --git a/ios/web/web_state/js/resources/message_dynamic_ui.js b/ios/web/web_state/js/resources/message_dynamic_ui.js
index 5d179541..9ec5cdc2 100644
--- a/ios/web/web_state/js/resources/message_dynamic_ui.js
+++ b/ios/web/web_state/js/resources/message_dynamic_ui.js
@@ -4,6 +4,8 @@
 
 // Scripts for the message handler for use with UIWebView.
 
+goog.provide('__crweb.message_dynamic_ui');
+
 /**
  * Namespace for this module.
  */
diff --git a/ios/web/web_state/js/resources/message_dynamic_wk.js b/ios/web/web_state/js/resources/message_dynamic_wk.js
index 644f2fa..d8b0c246 100644
--- a/ios/web/web_state/js/resources/message_dynamic_wk.js
+++ b/ios/web/web_state/js/resources/message_dynamic_wk.js
@@ -4,6 +4,8 @@
 
 // Scripts for the message handler for use with WKWebView.
 
+goog.provide('__crweb.message_dynamic_wk');
+
 /**
  * Namespace for this module.
  */
diff --git a/ios/web/web_state/js/resources/plugin_placeholder.js b/ios/web/web_state/js/resources/plugin_placeholder.js
new file mode 100644
index 0000000..4a588fd
--- /dev/null
+++ b/ios/web/web_state/js/resources/plugin_placeholder.js
@@ -0,0 +1,199 @@
+// 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.
+
+// This file adheres to closure-compiler conventions in order to enable
+// compilation with ADVANCED_OPTIMIZATIONS. See http://goo.gl/FwOgy
+//
+// Installs and runs the plugin placeholder function on the |__gCrWeb| object.
+
+/**
+ * Namespace for this file. It depends on |__gCrWeb| having already been
+ * injected.
+ */
+__gCrWeb['plugin'] = {};
+
+/* Beginning of anonymous object. */
+new function() {
+
+  /* Data-URL version of plugin_blocked_android.png. Served this way rather
+   * than with an intercepted URL to avoid messing up https pages.
+   */
+  __gCrWeb['plugin'].imageData_ =
+      'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAQAAABLCVATAAAD' +
+      'aklEQVR4Xn2Wz2tcVRTHP/e+O28mMxONJKlF4kIkP4luXFgQuuxCBaG41IWrLupOXLur+A' +
+      'e4cmV3LiS6qujSLgq2CIKQUqS2YnWsRkzGSTIz7zyHw+EdchnkcOd+7+OeT84578tMwmet' +
+      'O1fkar1RRNAgUJuqbeEn/0RUcdS6UX7w0X54/93qw4V+m0IReBiizhAYpG52kfrO86+F9/' +
+      'YXNnukHOTpc5SHgpiOu1cT623FBELeGvgTXfppOAjN3dCKm7GIkWiY4LsBnqBPpGqAgN/z' +
+      'CDMMBsCWX+pwibd5hzdZZmLNOsxDm8VAzIkt1hX5NLucqgrZm3RlIC/XscKTNlAQpvncMi' +
+      'tAnEM33D4nqgbcosBSPT3DRTJ3+Cx+4UfV3/CQniMQQ5g2WMJkoGKHNodUCBDpsYEQ2KGm' +
+      'JBKIFPT4nYckB9ueaPxRscamWczco3qXLcR9wx4ndBsziqFSjaOCAWLm4kj0xhhSMVFli4' +
+      'opyYuLlJ7s+/xTE6IgcVBthUuW6goHZDiA5IeCAnFEhkKVxxQh+pnoqSeMCEw4Uvt5kEHP' +
+      'c8IyF3iJ5De1NYSAMOYvOtxgwBqv0wcE5rR4gcQGq9Sc5wt7bq2JtfYtI0Ys8mCmLhFg7q' +
+      'w6XKRStUHJiMJmpC8vglqypAOU/MwRiw7KYGKqxZSKqE/iTKrQAwGxv5oU4ZbzGHCTf1QN' +
+      'OTXbQhJ/gbxKjy85IPECHQSQ3EFUfM0+93iZgluM6LuzDUTJOXpc5jcWeDb3DjQrsMhj9t' +
+      'TdPcAq8mtjjunyFEtN8ohfOWaVZR88Qd2WKK15a5zoRY8ZmRaNIZ/yCZ/P1u0zY+9TASjc' +
+      'q04YMzBhqAAUBXf5iWcITGdql3aTtpIZVnxGYvSxj1VPXUB0EtHnxBoT6iwgeXEwQfwC69' +
+      'xmROAcr5DwESxa3XLGW9G9AgPGVKahzzb/UvEcq81PwCl/MyDMrUgxQeMH7tNniQW6nPKA' +
+      'e5TU3KUFjPmTRxyofUsFeFVQqyENBHDAYyodJhR0CFrnfaYECgvAjdogEwZCVySQaJ8Zeq' +
+      'AL874rsy+2ofT1ev5fkSdmihwF0jpOra/kskTHkGMckkG9Gg7Xvw9XtifXOy/GEgCr7H/r' +
+      'yepFOFy5fu1agI9XH71RbRWRrDmHOhrfLYrx9ndv3Wz98R+P7LgG2uyMvgAAAABJRU5Erk' +
+      'Jggg==';
+
+  /**
+   * Returns the first <embed> child of the given node, if any.
+   * @param {HTMLElement} node The node to check.
+   * @return {HTMLElement} The first <embed> child, or null.
+   * @private
+   */
+  __gCrWeb['plugin'].getEmbedChild_ = function(node) {
+    if (node.hasChildNodes()) {
+      for (var i = 0; i < node.childNodes.length; i++) {
+        if (node.childNodes[i].nodeName === 'EMBED') {
+          return node.childNodes[i];
+        }
+      }
+    }
+    return null;
+  };
+
+  /**
+   * Returns the size for the given plugin element. For the common
+   * pattern of an IE-specific <object> wrapping an all-other-browsers <embed>,
+   * the object doesn't have real style info (most notably size), so this uses
+   * the embed in that case.
+   * @param {HTMLElement} plugin The <object> node to check.
+   * @return {Object} The size (width and height) for the plugin element.
+   * @private
+   */
+  __gCrWeb['plugin'].getPluginSize_ = function(plugin) {
+    var style;
+    // For the common pattern of an IE-specific <object> wrapping an
+    // all-other-browsers <embed>, the object doesn't have real style info
+    // (most notably size), so this uses the embed in that case.
+    var embedChild = __gCrWeb['plugin'].getEmbedChild_(plugin);
+    if (embedChild) {
+      style = window.getComputedStyle(embedChild);
+    } else {
+      style = window.getComputedStyle(plugin);
+    }
+
+    var width = parseFloat(style.width);
+    var height = parseFloat(style.height);
+    if (plugin.tagName === 'APPLET') {
+      // Size computation doesn't always work correctly with applets in
+      // UIWebView, so use the attributes as fallbacks.
+      if (isNaN(width)) {
+        width = parseFloat(plugin.width);
+      }
+      if (isNaN(height)) {
+        height = parseFloat(plugin.height);
+      }
+    }
+
+    return {
+      'width': width,
+      'height': height
+    };
+  };
+
+  /**
+   * Checks whether an element is "significant". Whether a plugin is
+   * "significant" is a heuristic that attempts to determine if it's a critical
+   * visual element for the page (i.e., not invisible, or an incidental ad).
+   * @param {HTMLElement} plugin The <object> node to check.
+   * @return {Boolean} Whether the node is significant.
+   * @private
+   */
+  __gCrWeb['plugin'].isSignificantPlugin_ = function(plugin) {
+    var windowWidth = window.innerWidth;
+    var windowHeight = window.innerHeight;
+    var pluginSize = __gCrWeb['plugin'].getPluginSize_(plugin);
+    var pluginWidth = parseFloat(pluginSize.width);
+    var pluginHeight = parseFloat(pluginSize.height);
+    // A plugin must be at least |significantFraction| of one dimension of the
+    // page, and a minimum size in the other dimension (to weed out banners and
+    // tall side ads).
+    var minSize = Math.min(200, windowWidth / 2, windowHeight / 2);
+    var significantFraction = 0.5;
+    return (pluginWidth > windowWidth * significantFraction &&
+            pluginHeight > minSize) ||
+           (pluginHeight > windowHeight * significantFraction &&
+            pluginWidth > minSize);
+  };
+
+  /**
+   * Walks the list of detected plugin elements, adding a placeholder to any
+   * that are "significant" (see above).
+   * @param {string} message The message to show in the placeholder.
+   */
+  __gCrWeb['plugin']['addPluginPlaceholders'] = function(message) {
+    var plugins = __gCrWeb['placeholderTargetPlugins'];
+    for (i = 0; i < plugins.length; i++) {
+      var plugin = plugins[i];
+      if (!__gCrWeb['plugin'].isSignificantPlugin_(plugin)) {
+        continue;
+      }
+
+      var pluginSize = __gCrWeb['plugin'].getPluginSize_(plugin);
+      var widthStyle = pluginSize.width + 'px';
+      var heightStyle = pluginSize.height + 'px';
+
+      // The outer wrapper is a div with relative positioning, as an anchor for
+      // an inner absolute-position element, whose height is based on whether or
+      // not there's an embed. If there is, then it's zero height, to avoid
+      // affecting the layout of the (presumably-full-size) <embed> fallback. If
+      // not, it's full-height to ensure the placeholder takes up the right
+      // amount of space in the page layout. Width is full-width either way, to
+      // avoid being affected by container alignment.
+      var placeholder = document.createElement('div');
+      placeholder.style.width = widthStyle;
+      if (__gCrWeb['plugin'].getEmbedChild_(plugin)) {
+        placeholder.style.height = '0';
+      } else {
+        placeholder.style.height = heightStyle;
+      }
+      placeholder.style.position = 'relative';
+
+      // Inside is a full-plugin-size solid box.
+      var placeholderBox = document.createElement('div');
+      placeholderBox.style.position = 'absolute';
+      placeholderBox.style.boxSizing = 'border-box';
+      placeholderBox.style.width = widthStyle;
+      placeholderBox.style.height = heightStyle;
+      placeholderBox.style.border = '1px solid black';
+      placeholderBox.style.backgroundColor = '#808080';
+      placeholder.appendChild(placeholderBox);
+
+      // Inside that is the plugin placeholder image, centered.
+      var pluginImg = document.createElement('img');
+      var imageSize = 36;
+      pluginImg.width = imageSize;
+      pluginImg.height = imageSize;
+      pluginImg.style.position = 'absolute';
+      // Center vertically and horizontally.
+      var halfSize = imageSize / 2;
+      pluginImg.style.top = '50%';
+      pluginImg.style.marginTop = '-' + halfSize + 'px';
+      pluginImg.style.left = '50%';
+      pluginImg.style.marginLeft = '-' + halfSize + 'px';
+      pluginImg.src = __gCrWeb['plugin'].imageData_;
+      placeholderBox.appendChild(pluginImg);
+
+      // And below that, the message.
+      var label = document.createElement('p');
+      label.style.width = widthStyle;
+      label.style.height = '1.5em';
+      label.style.position = 'absolute';
+      // Position below the image.
+      label.style.top = '50%';
+      label.style.marginTop = imageSize + 'px';
+      // Center horizontally.
+      label.style.textAlign = 'center';
+      label.textContent = message;
+      placeholderBox.appendChild(label);
+
+      plugin.insertBefore(placeholder, plugin.firstChild);
+    }
+  };
+}  // End of anonymous object
diff --git a/ios/web/web_state/js/resources/web_bundle_ui.js b/ios/web/web_state/js/resources/web_bundle_ui.js
new file mode 100644
index 0000000..db29cb7
--- /dev/null
+++ b/ios/web/web_state/js/resources/web_bundle_ui.js
@@ -0,0 +1,16 @@
+// 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.
+
+// Set of scripts required by web layer backed up by UIWebView.
+goog.provide('__crweb.web_bundle_ui');
+
+goog.require('__crweb.base');
+goog.require('__crweb.common');
+goog.require('__crweb.core');
+goog.require('__crweb.core_dynamic_ui');
+goog.require('__crweb.console');
+goog.require('__crweb.dialog_overrides');
+goog.require('__crweb.message');
+goog.require('__crweb.message_dynamic_ui');
+goog.require('__crweb.window_open_ui');
diff --git a/ios/web/web_state/js/resources/web_bundle_wk.js b/ios/web/web_state/js/resources/web_bundle_wk.js
new file mode 100644
index 0000000..ff26a6c
--- /dev/null
+++ b/ios/web/web_state/js/resources/web_bundle_wk.js
@@ -0,0 +1,15 @@
+// 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.
+
+// Set of scripts required by web layer backed up by WKWebView.
+goog.provide('__crweb.web_bundle_wk');
+
+goog.require('__crweb.base');
+goog.require('__crweb.common');
+goog.require('__crweb.core');
+goog.require('__crweb.core_dynamic_wk');
+goog.require('__crweb.console');
+goog.require('__crweb.dialog_overrides');
+goog.require('__crweb.message');
+goog.require('__crweb.message_dynamic_wk');
diff --git a/ios/web/web_state/js/resources/window_id.js b/ios/web/web_state/js/resources/window_id.js
new file mode 100644
index 0000000..73ef670
--- /dev/null
+++ b/ios/web/web_state/js/resources/window_id.js
@@ -0,0 +1,31 @@
+// 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.
+
+// This file adheres to closure-compiler conventions in order to enable
+// compilation with ADVANCED_OPTIMIZATIONS. See http://goo.gl/FwOgy
+
+// Script to set windowId.
+
+
+// Namespace for module, used as presence beacon for injection checks.
+__gCrWeb['windowIdObject'] = {};
+
+new function() {
+  // CRWJSWindowIdManager replaces $(WINDOW_ID) with appropriate string upon
+  // injection.
+  __gCrWeb['windowId'] = '$(WINDOW_ID)';
+
+  // Wrap queues flushing in setTimeout to avoid reentrant calls.
+  // In some circumstances setTimeout does not work on iOS8 if set from
+  // injected script. There is an assumption that it's happen when the script
+  // has been injected too early. Do not place anything important to delayed
+  // function body, since there is no guarantee that it will ever be executed.
+  // TODO(eugenebut): Find out why setTimeout does not work (crbug.com/402682).
+  window.setTimeout(function() {
+    // Send messages queued since message.js injection.
+    if (__gCrWeb.message) {
+      __gCrWeb.message.invokeQueues();
+    }
+  }, 0);
+}
diff --git a/ios/web/web_state/js/resources/window_open_ui.js b/ios/web/web_state/js/resources/window_open_ui.js
new file mode 100644
index 0000000..fb418f1
--- /dev/null
+++ b/ios/web/web_state/js/resources/window_open_ui.js
@@ -0,0 +1,245 @@
+// 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.
+
+// Scripts that are conceptually part of core.js, but have UIWebView-specific
+// details/behaviors.
+
+goog.provide('__crweb.window_open_ui');
+
+// Namespace for this module.
+__gCrWeb.windowOpen = {};
+
+// Beginning of anonymous object.
+new function() {
+  // Preserve a reference to the original window.open method.
+  __gCrWeb['originalWindowOpen'] = window.open;
+
+  // Object used to keep track of all windows opened from this window.
+  var openedWindows = {};
+
+  /**
+   * Checks if a child window exists with the given name and if so, sets its
+   * closed property to true and removes it from |openedWindows|.
+   * @param {String} windowName The name of the window to mark as closed.
+   */
+  __gCrWeb['windowClosed'] = function(windowName) {
+    if (openedWindows.hasOwnProperty(windowName)) {
+      openedWindows[windowName].closed = true;
+      delete openedWindows[windowName];
+    }
+  };
+
+  function invokeOnHost_(command) {
+    __gCrWeb.message.invokeOnHost(command);
+  };
+
+  var invokeNotImplementedOnHost_ = function(methodName) {
+    invokeOnHost_({'command': 'window.error',
+                   'message': methodName + ' is not implemented'});
+  };
+
+  // Define Object watch/unwatch functions to detect assignments to
+  // certain object properties. Handles defineProperty case only because
+  // this code runs in UIWebView (i.e. Safari).
+  var objectWatch = function(obj, prop, handler) {
+    var val = obj[prop];
+    if (delete obj[prop]) {
+      Object['defineProperty'](obj, prop, {
+        'get': function() {
+          return val;
+        },
+        'set': function(newVal) {
+          return val = handler.call(obj, prop, val, newVal);
+        }
+      });
+    }
+  };
+
+  /**
+   * Creates and returns a window proxy used to represent the window object and
+   * intercept calls made on it.
+   * @param {String} target The name of the window.
+   * @return {Object} A window proxy object for intercepting window methods.
+   * @private
+   */
+  var createWindowProxy_ = function(target) {
+    // Create return window object.
+    // 'name' is always the original supplied name.
+    var windowProxy = {name: target};
+
+    // Define window object methods.
+    windowProxy.alert = function() {
+      invokeNotImplementedOnHost_('windowProxy.alert');
+    };
+
+    windowProxy.blur = function() {
+      invokeNotImplementedOnHost_('windowProxy.blur');
+    };
+
+    windowProxy.clearInterval = function() {
+      invokeNotImplementedOnHost_('windowProxy.clearInterval');
+    };
+
+    windowProxy.clearTimeout = function() {
+      invokeNotImplementedOnHost_('windowProxy.clearTimeout');
+    };
+
+    windowProxy.close = function() {
+      invokeOnHost_({'command': 'window.close',
+                     'target': target});
+    };
+
+    windowProxy.confirm = function() {
+      invokeNotImplementedOnHost_('windowProxy.confirm');
+    };
+
+    windowProxy.createPopup = function() {
+      invokeNotImplementedOnHost_('windowProxy.createPopup');
+    };
+
+    windowProxy.focus = function() {
+      // Noop as the opened window always gets focus.
+    };
+
+    windowProxy.moveBy = function() {
+      invokeNotImplementedOnHost_('windowProxy.moveBy');
+    };
+
+    windowProxy.moveTo = function() {
+      invokeNotImplementedOnHost_('windowProxy.moveTo');
+    };
+
+    windowProxy.stop = function() {
+      invokeOnHost_({'command': 'window.stop',
+                     'target': target});
+    };
+
+    windowProxy.open = function() {
+      invokeNotImplementedOnHost_('windowProxy.open');
+    };
+
+    windowProxy.print = function() {
+      invokeNotImplementedOnHost_('windowProxy.print');
+    };
+
+    windowProxy.prompt = function() {
+      invokeNotImplementedOnHost_('windowProxy.prompt');
+    };
+
+    windowProxy.resizeBy = function() {
+      invokeNotImplementedOnHost_('windowProxy.resizeBy');
+    };
+
+    windowProxy.resizeTo = function() {
+      invokeNotImplementedOnHost_('windowProxy.resizeTo');
+    };
+
+    windowProxy.scroll = function() {
+      invokeNotImplementedOnHost_('windowProxy.scroll');
+    };
+
+    windowProxy.scrollBy = function() {
+      invokeNotImplementedOnHost_('windowProxy.scrollBy');
+    };
+
+    windowProxy.scrollTo = function() {
+      invokeNotImplementedOnHost_('windowProxy.scrollTo');
+    };
+
+    windowProxy.setInterval = function() {
+      invokeNotImplementedOnHost_('windowProxy.setInterval');
+    };
+
+    windowProxy.setTimeout = function() {
+      invokeNotImplementedOnHost_('windowProxy.setTimeout');
+    };
+
+    // Define window object properties.
+    // The current window.
+    windowProxy.self = windowProxy;
+    // The topmost browser window.
+    windowProxy.top = windowProxy;
+
+    // Provide proxy document which supplies one method, document.write().
+    windowProxy.document = {};
+    windowProxy.document.title = '';
+    windowProxy.document.write = function(html) {
+      invokeOnHost_({'command': 'window.document.write',
+                       'html': html,
+                     'target': target});
+    };
+
+    windowProxy.document.open = function() {
+      // The open() method should open an output stream to collect the output
+      // from any document.write() or document.writeln() methods.
+      invokeNotImplementedOnHost_('windowProxy.document.open');
+    };
+
+    windowProxy.document.close = function() {
+      // The close() method should close the output stream previously opened
+      // with the document.open() method, and displays the collected data in
+      // this process.
+      invokeNotImplementedOnHost_('windowProxy.document.close');
+    };
+
+    windowProxy.location = {};
+    windowProxy.location.assign = function(url) {
+      windowProxy.location = url;
+    };
+    // Watch assignments to window.location and window.location.href.
+    // Invoke equivalent method in ObjC code.
+    var onWindowProxyLocationChange = function(prop, oldVal, newVal) {
+      invokeOnHost_({'command': 'window.location',
+                     'value': __gCrWeb['getFullyQualifiedURL'](newVal),
+                     'target': target});
+      return newVal;
+    };
+    objectWatch(windowProxy, 'location', onWindowProxyLocationChange);
+    objectWatch(windowProxy.location, 'href', onWindowProxyLocationChange);
+    windowProxy.closed = false;
+
+    return windowProxy;
+  };
+
+  // Intercept window.open calls.
+  window.open = function(url, target, features) {
+    if (target == '_parent' || target == '_self' || target == '_top') {
+      return __gCrWeb['originalWindowOpen'].call(window, url, target, features);
+    }
+
+    // Because of the difficulty of returning data from JS->ObjC calls, in the
+    // event of a blank window name the JS side chooses a pseudo-GUID to
+    // use as the window name which is passed to ObjC and mapped to the real
+    // Tab there.
+    var isTargetBlank = (typeof target == 'undefined' || target == '_blank' ||
+                         target == '' || target == null);
+    if (isTargetBlank) {
+      target = '' + Date.now() + '-' + Math.random();
+    }
+
+    if (typeof(url) == 'undefined') {
+      // W3C recommended behavior.
+      url = 'about:blank';
+    }
+
+    invokeOnHost_({
+      'command': 'window.open',
+      'target': target,
+      'url': url,
+      'referrerPolicy': __gCrWeb.getPageReferrerPolicy()
+    });
+
+    // Create a new |windowProxy| if none already exists with |target| as its
+    // name.
+    var windowProxy;
+    if (openedWindows.hasOwnProperty(target)) {
+      windowProxy = openedWindows[target];
+    } else {
+      windowProxy = createWindowProxy_(target);
+      openedWindows[target] = windowProxy;
+    }
+    return windowProxy;
+  };
+
+}  // End of anonymous object
diff --git a/ios/web/web_state/js/resources/window_open_wk.js b/ios/web/web_state/js/resources/window_open_wk.js
new file mode 100644
index 0000000..209628c
--- /dev/null
+++ b/ios/web/web_state/js/resources/window_open_wk.js
@@ -0,0 +1,15 @@
+// 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.
+
+goog.provide('__crweb.window_open_wk');
+
+// WKWebView natively supports window.open so there is no need to install
+// JavaScript-based fix. CRWJSWindowOpenManager always injected as multiple
+// JS managers depend on it. The script does not have any content except
+// presenceBeacon.
+
+
+// Namespace for this module.
+__gCrWeb.windowOpen = {};
+
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index 59772970..6787c8e 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -906,33 +906,9 @@
     return ok;
   }
 
-  template<typename TA>
-  static void WriteReplyParams(Message* reply, TA a) {
-    ReplyParam p(a);
-    WriteParam(reply, p);
-  }
-
-  template<typename TA, typename TB>
-  static void WriteReplyParams(Message* reply, TA a, TB b) {
-    ReplyParam p(a, b);
-    WriteParam(reply, p);
-  }
-
-  template<typename TA, typename TB, typename TC>
-  static void WriteReplyParams(Message* reply, TA a, TB b, TC c) {
-    ReplyParam p(a, b, c);
-    WriteParam(reply, p);
-  }
-
-  template<typename TA, typename TB, typename TC, typename TD>
-  static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d) {
-    ReplyParam p(a, b, c, d);
-    WriteParam(reply, p);
-  }
-
-  template<typename TA, typename TB, typename TC, typename TD, typename TE>
-  static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d, TE e) {
-    ReplyParam p(a, b, c, d, e);
+  template <typename... Ts>
+  static void WriteReplyParams(Message* reply, Ts... args) {
+    ReplyParam p(args...);
     WriteParam(reply, p);
   }
 };
diff --git a/media/audio/audio_manager.cc b/media/audio/audio_manager.cc
index 6d5d70d0..8a09415a 100644
--- a/media/audio/audio_manager.cc
+++ b/media/audio/audio_manager.cc
@@ -9,12 +9,90 @@
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
+#include "base/power_monitor/power_monitor.h"
+#include "build/build_config.h"
 #include "media/audio/fake_audio_log_factory.h"
 
 namespace media {
 namespace {
 AudioManager* g_last_created = NULL;
-static base::LazyInstance<FakeAudioLogFactory>::Leaky g_fake_log_factory =
+
+// Helper class for managing global AudioManager data and hang timers. If the
+// audio thread is unresponsive for more than a minute we want to crash the
+// process so we can catch offenders quickly in the field.
+class AudioManagerHelper : public base::PowerObserver {
+ public:
+  AudioManagerHelper()
+      : max_hung_task_time_(base::TimeDelta::FromMinutes(1)),
+        hang_detection_enabled_(true) {}
+  ~AudioManagerHelper() override {}
+
+  void StartHangTimer(
+      const scoped_refptr<base::SingleThreadTaskRunner>& monitor_task_runner) {
+    CHECK(!monitor_task_runner_);
+    monitor_task_runner_ = monitor_task_runner;
+    base::PowerMonitor::Get()->AddObserver(this);
+    UpdateLastAudioThreadTimeTick();
+    CrashOnAudioThreadHang();
+  }
+
+  // Disable hang detection when the system goes into the suspend state.
+  void OnSuspend() override {
+    base::AutoLock lock(hang_lock_);
+    hang_detection_enabled_ = false;
+  }
+
+  // Reenable hang detection once the system comes out of the suspend state.
+  void OnResume() override {
+    base::AutoLock lock(hang_lock_);
+    hang_detection_enabled_ = true;
+    last_audio_thread_timer_tick_ = base::TimeTicks::Now();
+  }
+
+  // Runs on |monitor_task_runner| typically, but may be started on any thread.
+  void CrashOnAudioThreadHang() {
+    base::AutoLock lock(hang_lock_);
+
+    // Don't attempt to verify the tick time if the system is in the process of
+    // suspending or resuming.
+    if (hang_detection_enabled_) {
+      CHECK(base::TimeTicks::Now() - last_audio_thread_timer_tick_ <=
+            max_hung_task_time_);
+    }
+
+    monitor_task_runner_->PostDelayedTask(
+        FROM_HERE, base::Bind(&AudioManagerHelper::CrashOnAudioThreadHang,
+                              base::Unretained(this)),
+        max_hung_task_time_);
+  }
+
+  // Runs on the audio thread typically, but may be started on any thread.
+  void UpdateLastAudioThreadTimeTick() {
+    base::AutoLock lock(hang_lock_);
+    last_audio_thread_timer_tick_ = base::TimeTicks::Now();
+    g_last_created->GetTaskRunner()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&AudioManagerHelper::UpdateLastAudioThreadTimeTick,
+                   base::Unretained(this)),
+        max_hung_task_time_ / 2);
+  }
+
+  AudioLogFactory* fake_log_factory() { return &fake_log_factory_; }
+
+ private:
+  FakeAudioLogFactory fake_log_factory_;
+
+  const base::TimeDelta max_hung_task_time_;
+  scoped_refptr<base::SingleThreadTaskRunner> monitor_task_runner_;
+
+  base::Lock hang_lock_;
+  bool hang_detection_enabled_;
+  base::TimeTicks last_audio_thread_timer_tick_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioManagerHelper);
+};
+
+static base::LazyInstance<AudioManagerHelper>::Leaky g_helper =
     LAZY_INSTANCE_INITIALIZER;
 }
 
@@ -36,8 +114,21 @@
 }
 
 // static
+AudioManager* AudioManager::CreateWithHangTimer(
+    AudioLogFactory* audio_log_factory,
+    const scoped_refptr<base::SingleThreadTaskRunner>& monitor_task_runner) {
+  AudioManager* manager = Create(audio_log_factory);
+  // On OSX the audio thread is the UI thread, for which a hang monitor is not
+  // necessary.
+#if !defined(OS_MACOSX)
+  g_helper.Pointer()->StartHangTimer(monitor_task_runner);
+#endif
+  return manager;
+}
+
+// static
 AudioManager* AudioManager::CreateForTesting() {
-  return Create(g_fake_log_factory.Pointer());
+  return Create(g_helper.Pointer()->fake_log_factory());
 }
 
 // static
diff --git a/media/audio/audio_manager.h b/media/audio/audio_manager.h
index 7705cdb..5e49b8b 100644
--- a/media/audio/audio_manager.h
+++ b/media/audio/audio_manager.h
@@ -34,6 +34,13 @@
   // such |audio_log_factory| must outlive the AudioManager.
   static AudioManager* Create(AudioLogFactory* audio_log_factory);
 
+  // Similar to Create() except also schedules a monitor on the given task
+  // runner to ensure the audio thread is not stuck for more than 60 seconds; if
+  // a hang is detected, the process will be crashed.
+  static AudioManager* CreateWithHangTimer(
+      AudioLogFactory* audio_log_factory,
+      const scoped_refptr<base::SingleThreadTaskRunner>& monitor_task_runner);
+
   // Similar to Create() except uses a FakeAudioLogFactory for testing.
   static AudioManager* CreateForTesting();
 
diff --git a/media/base/android/BUILD.gn b/media/base/android/BUILD.gn
index 71e9edc7..f6f910e 100644
--- a/media/base/android/BUILD.gn
+++ b/media/base/android/BUILD.gn
@@ -99,9 +99,11 @@
 java_cpp_enum("media_java_enums_srcjar") {
   sources = [
     "//media/video/capture/android/video_capture_device_android.h",
+    "//media/video/capture/video_capture_device.h",
   ]
   outputs = [
     "org/chromium/media/AndroidImageFormat.java",
+    "org/chromium/media/CaptureApiType.java",
   ]
 }
 
diff --git a/media/base/android/audio_decoder_job.h b/media/base/android/audio_decoder_job.h
index f218f3e..0a7523f 100644
--- a/media/base/android/audio_decoder_job.h
+++ b/media/base/android/audio_decoder_job.h
@@ -34,6 +34,7 @@
 
   // Sets the volume of the audio output.
   void SetVolume(double volume);
+  double volume() const { return volume_; }
 
   // Sets the base timestamp for |audio_timestamp_helper_|.
   void SetBaseTimestamp(base::TimeDelta base_timestamp);
diff --git a/media/base/android/browser_cdm_factory_android.cc b/media/base/android/browser_cdm_factory_android.cc
index e116863..614c8d1d 100644
--- a/media/base/android/browser_cdm_factory_android.cc
+++ b/media/base/android/browser_cdm_factory_android.cc
@@ -34,7 +34,7 @@
   }
 
   // TODO(xhwang/ddorwin): Pass the security level from key system.
-  // http://crbug.com/459400
+  // http://crbug.com/467779
   if (key_system == kWidevineKeySystem) {
     MediaDrmBridge::SecurityLevel security_level =
         MediaDrmBridge::SECURITY_LEVEL_3;
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java b/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java
index 1682b7fb..a6e99bb 100644
--- a/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java
+++ b/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java
@@ -51,9 +51,17 @@
         return android.hardware.Camera.getNumberOfCameras();
     }
 
+    static int getCaptureApiType(int id) {
+        if (VideoCaptureCamera.getCameraInfo(id) == null) {
+            return CaptureApiType.API_TYPE_UNKNOWN;
+        }
+        return CaptureApiType.API1;
+    }
+
     static String getName(int id) {
         android.hardware.Camera.CameraInfo cameraInfo = VideoCaptureCamera.getCameraInfo(id);
         if (cameraInfo == null) return null;
+
         return "camera " + id + ", facing " + (cameraInfo.facing
                 == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT ? "front" : "back");
     }
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java b/media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java
index a76013f..cdaca75 100644
--- a/media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java
+++ b/media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java
@@ -303,6 +303,27 @@
         }
     }
 
+    static int getCaptureApiType(int id, Context appContext) {
+        final CameraCharacteristics cameraCharacteristics =
+                getCameraCharacteristics(appContext, id);
+        if (cameraCharacteristics == null) {
+            return CaptureApiType.API_TYPE_UNKNOWN;
+        }
+
+        final int supportedHWLevel = cameraCharacteristics.get(
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+        switch (supportedHWLevel) {
+            case CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY:
+                return CaptureApiType.API2_LEGACY;
+            case CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL:
+                return CaptureApiType.API2_FULL;
+            case CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED:
+                return CaptureApiType.API2_LIMITED;
+            default:
+                return CaptureApiType.API2_LEGACY;
+        }
+    }
+
     static String getName(int id, Context appContext) {
         final CameraCharacteristics cameraCharacteristics =
                 getCameraCharacteristics(appContext, id);
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java b/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java
index ae6018df..fcc8ebf 100644
--- a/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java
+++ b/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java
@@ -117,6 +117,18 @@
     }
 
     @CalledByNative
+    static int getCaptureApiType(int id, Context appContext) {
+        if (isLReleaseOrLater()) {
+            return VideoCaptureCamera2.getCaptureApiType(id, appContext);
+        } else if (ChromiumCameraInfo.isSpecialCamera(id)) {
+            return VideoCaptureTango.getCaptureApiType(
+                    ChromiumCameraInfo.toSpecialCameraId(id));
+        } else {
+            return VideoCaptureAndroid.getCaptureApiType(id);
+        }
+    }
+
+    @CalledByNative
     static String getDeviceName(int id, Context appContext) {
         if (isLReleaseOrLater() && !VideoCaptureCamera2.isLegacyDevice(appContext, id)) {
             return VideoCaptureCamera2.getName(id, appContext);
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java b/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java
index f802f29..b3f875fc 100644
--- a/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java
+++ b/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java
@@ -67,6 +67,13 @@
         return CAM_PARAMS.length;
     }
 
+    static int getCaptureApiType(int index) {
+        if (index >= CAM_PARAMS.length) {
+            return CaptureApiType.API1;
+        }
+        return CaptureApiType.TANGO;
+    }
+
     static String getName(int index) {
         if (index >= CAM_PARAMS.length) return "";
         return CAM_PARAMS[index].mName;
diff --git a/media/base/android/media_decoder_job.h b/media/base/android/media_decoder_job.h
index 83ec5e1..329512f 100644
--- a/media/base/android/media_decoder_job.h
+++ b/media/base/android/media_decoder_job.h
@@ -108,6 +108,9 @@
 
   bool prerolling() const { return prerolling_; }
 
+  // Returns true if this object has data to decode.
+  bool HasData() const;
+
  protected:
   // Creates a new MediaDecoderJob instance.
   // |decoder_task_runner| - Thread on which the decoder task will run.
@@ -162,9 +165,6 @@
   // Queues an access unit into |media_codec_bridge_|'s input buffer.
   MediaCodecStatus QueueInputBuffer(const AccessUnit& unit);
 
-  // Returns true if this object has data to decode.
-  bool HasData() const;
-
   // Initiates a request for more data.
   // |done_cb| is called when more data is available in |received_data_|.
   void RequestData(const base::Closure& done_cb);
diff --git a/media/base/android/media_source_player.cc b/media/base/android/media_source_player.cc
index ed0352f..52ea7db 100644
--- a/media/base/android/media_source_player.cc
+++ b/media/base/android/media_source_player.cc
@@ -185,6 +185,8 @@
   playing_ = false;
 
   decoder_starvation_callback_.Cancel();
+
+  SetAudible(false);
   DetachListener();
 }
 
@@ -229,6 +231,7 @@
     const DemuxerConfigs& configs) {
   DVLOG(1) << __FUNCTION__;
   DCHECK(!HasAudio() && !HasVideo());
+
   duration_ = configs.duration;
 
   audio_decoder_job_->SetDemuxerConfigs(configs);
@@ -390,6 +393,7 @@
 
   if (IsEventPending(PREFETCH_REQUEST_EVENT_PENDING)) {
     DVLOG(1) << __FUNCTION__ << " : Handling PREFETCH_REQUEST_EVENT.";
+
     int count = (AudioFinished() ? 0 : 1) + (VideoFinished() ? 0 : 1);
 
     // It is possible that all streams have finished decode, yet starvation
@@ -484,12 +488,18 @@
     return;
   }
 
-  if (status == MEDIA_CODEC_OUTPUT_END_OF_STREAM)
+  if (status == MEDIA_CODEC_OUTPUT_END_OF_STREAM) {
+    if (is_audio)
+      SetAudible(false);
     return;
+  }
 
   if (!playing_) {
     if (is_clock_manager)
       interpolator_.StopInterpolating();
+
+    if (is_audio)
+      SetAudible(false);
     return;
   }
 
@@ -498,6 +508,9 @@
       DVLOG(2) << __FUNCTION__ << ": Key was added during decoding.";
       ResumePlaybackAfterKeyAdded();
     } else {
+      if (is_audio)
+        SetAudible(false);
+
       is_waiting_for_key_ = true;
       manager()->OnWaitingForDecryptionKey(player_id());
     }
@@ -517,8 +530,11 @@
   // If the status is MEDIA_CODEC_ABORT, stop decoding new data. The player is
   // in the middle of a seek or stop event and needs to wait for the IPCs to
   // come.
-  if (status == MEDIA_CODEC_ABORT)
+  if (status == MEDIA_CODEC_ABORT) {
+    if (is_audio)
+      SetAudible(false);
     return;
+  }
 
   if (prerolling_ && IsPrerollFinished(is_audio)) {
     if (IsPrerollFinished(!is_audio)) {
@@ -528,6 +544,13 @@
     return;
   }
 
+  // We successfully decoded a frame and going to the next one.
+  // Set the audible state.
+  if (is_audio) {
+    bool is_audible = !prerolling_ && audio_decoder_job_->volume() > 0;
+    SetAudible(is_audible);
+  }
+
   if (is_clock_manager) {
     // If we have a valid timestamp, start the starvation callback. Otherwise,
     // reset the |start_time_ticks_| so that the next frame will not suffer
@@ -639,6 +662,14 @@
 
 void MediaSourcePlayer::OnDecoderStarved() {
   DVLOG(1) << __FUNCTION__;
+
+  if (HasAudio()) {
+    // If the starvation timer fired but there are no encoded frames
+    // in the queue we believe the demuxer (i.e. renderer process) froze.
+    if (!audio_decoder_job_->HasData())
+      SetAudible(false);
+  }
+
   SetPendingEvent(PREFETCH_REQUEST_EVENT_PENDING);
   ProcessPendingEvents();
 }
diff --git a/media/base/android/media_source_player_unittest.cc b/media/base/android/media_source_player_unittest.cc
index 6349b318..f37cf205 100644
--- a/media/base/android/media_source_player_unittest.cc
+++ b/media/base/android/media_source_player_unittest.cc
@@ -46,7 +46,9 @@
         playback_completed_(false),
         num_resources_requested_(0),
         num_metadata_changes_(0),
-        timestamp_updated_(false) {}
+        timestamp_updated_(false),
+        is_audible_(false),
+        is_delay_expired_(false) {}
   ~MockMediaPlayerManager() override {}
 
   // MediaPlayerManager implementation.
@@ -75,12 +77,15 @@
                       const base::TimeDelta& current_time) override {}
   void OnError(int player_id, int error) override {}
   void OnVideoSizeChanged(int player_id, int width, int height) override {}
-  void OnAudibleStateChanged(int player_id, bool is_audible_now) override {}
   void OnWaitingForDecryptionKey(int player_id) override {}
   MediaPlayerAndroid* GetFullscreenPlayer() override { return NULL; }
   MediaPlayerAndroid* GetPlayer(int player_id) override { return NULL; }
   void RequestFullScreen(int player_id) override {}
 
+  void OnAudibleStateChanged(int player_id, bool is_audible_now) override {
+    is_audible_ = is_audible_now;
+  }
+
   bool playback_completed() const {
     return playback_completed_;
   }
@@ -105,6 +110,18 @@
     timestamp_updated_ = false;
   }
 
+  bool is_audible() const {
+    return is_audible_;
+  }
+
+  bool is_delay_expired() const {
+    return is_delay_expired_;
+  }
+
+  void SetDelayExpired(bool value) {
+    is_delay_expired_ = value;
+  }
+
  private:
   base::MessageLoop* message_loop_;
   bool playback_completed_;
@@ -114,6 +131,10 @@
   int num_metadata_changes_;
   // Playback timestamp was updated.
   bool timestamp_updated_;
+  // Audible state of the pipeline
+  bool is_audible_;
+  // Helper flag to ensure delay for WaitForDelay().
+  bool is_delay_expired_;
 
   DISALLOW_COPY_AND_ASSIGN(MockMediaPlayerManager);
 };
@@ -171,6 +192,7 @@
                 GURL()),
         decoder_callback_hook_executed_(false),
         surface_texture_a_is_next_(true) {}
+
   ~MediaSourcePlayerTest() override {}
 
  protected:
@@ -534,6 +556,37 @@
     EXPECT_LE(target_timestamp, player_.GetCurrentTime());
   }
 
+  void PlayAudioForTimeInterval(const base::TimeDelta& start_timestamp,
+                                const base::TimeDelta& target_timestamp ) {
+
+    DemuxerData data = CreateReadFromDemuxerAckForAudio(1);
+    int current_timestamp = start_timestamp.InMilliseconds();
+    int stop_timestamp = target_timestamp.InMilliseconds();
+    while (current_timestamp < stop_timestamp) {
+      data.access_units[0].timestamp =
+          base::TimeDelta::FromMilliseconds(current_timestamp);
+      player_.OnDemuxerDataAvailable(data);
+      current_timestamp += 30;
+      WaitForAudioDecodeDone();
+    }
+  }
+
+  void WaitForDelay(const base::TimeDelta& delay) {
+    // Let the message_loop_ process events.
+    // We post delayed task and RunUnitilIdle() until it signals.
+
+    manager_.SetDelayExpired(false);
+    message_loop_.PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&MockMediaPlayerManager::SetDelayExpired,
+                   base::Unretained(&manager_),
+                   true),
+        delay);
+
+    while (!manager_.is_delay_expired())
+      message_loop_.RunUntilIdle();
+  }
+
   DemuxerData CreateReadFromDemuxerAckWithConfigChanged(
       bool is_audio,
       int config_unit_index,
@@ -837,6 +890,7 @@
     return GetMediaDecoderJob(is_audio)->drain_decoder_;
   }
 
+ protected:
   base::MessageLoop message_loop_;
   MockMediaPlayerManager manager_;
   MockDemuxerAndroid* demuxer_;  // Owned by |player_|.
@@ -857,6 +911,8 @@
   bool surface_texture_a_is_next_;
   int next_texture_id_;
 
+  bool verify_not_audible_is_called_;
+
   DISALLOW_COPY_AND_ASSIGN(MediaSourcePlayerTest);
 };
 
@@ -888,6 +944,107 @@
   EXPECT_FALSE(GetMediaCodecBridge(true));
 }
 
+// timav
+TEST_F(MediaSourcePlayerTest, AudioDecoderSetsAudibleState) {
+  SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+  // No data arrived yet
+  EXPECT_FALSE(manager_.is_audible());
+
+  // Initialize decoder
+  StartAudioDecoderJob();
+  player_.SetVolume(1.0);
+
+  // Process frames until prerolling is done.
+  SeekPlayerWithAbort(true, base::TimeDelta::FromMilliseconds(100));
+  EXPECT_TRUE(IsPrerolling(true));
+  PrerollDecoderToTime(
+      true, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100), false);
+  EXPECT_TRUE(IsPrerolling(false));
+
+  // Send more packets
+  PlayAudioForTimeInterval(base::TimeDelta::FromMilliseconds(150),
+                           base::TimeDelta::FromMilliseconds(220));
+
+  // The player should trigger audible status
+  EXPECT_TRUE(manager_.is_audible());
+
+  // The player release should report a non-audible state.
+  ReleasePlayer();
+  EXPECT_FALSE(manager_.is_audible());
+}
+
+TEST_F(MediaSourcePlayerTest, AudioDecoderRemovesAudibleStateWhenPaused) {
+  SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+  // No data arrived yet
+  EXPECT_FALSE(manager_.is_audible());
+
+  // Initialize decoder
+  StartAudioDecoderJob();
+  player_.SetVolume(1.0);
+
+  // Process frames until prerolling is done.
+  SeekPlayerWithAbort(true, base::TimeDelta::FromMilliseconds(100));
+  EXPECT_TRUE(IsPrerolling(true));
+  PrerollDecoderToTime(
+      true, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100), false);
+  EXPECT_TRUE(IsPrerolling(false));
+
+  // Send more packets
+  PlayAudioForTimeInterval(base::TimeDelta::FromMilliseconds(150),
+                           base::TimeDelta::FromMilliseconds(220));
+
+  // The player should trigger audible status
+  EXPECT_TRUE(manager_.is_audible());
+
+  // Pause the player
+  player_.Pause(true);
+
+  // Send more packets
+  PlayAudioForTimeInterval(base::TimeDelta::FromMilliseconds(240),
+                           base::TimeDelta::FromMilliseconds(280));
+
+  // The player should trigger audible status again
+  EXPECT_FALSE(manager_.is_audible());
+
+  player_.Release();
+}
+
+TEST_F(MediaSourcePlayerTest, AudioDecoderRemovesAudibleStateWhenIdle) {
+  SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+  // No data arrived yet
+  EXPECT_FALSE(manager_.is_audible());
+
+  // Initialize decoder
+  StartAudioDecoderJob();
+  player_.SetVolume(1.0);
+
+  // Process frames until prerolling is done.
+  SeekPlayerWithAbort(true, base::TimeDelta::FromMilliseconds(100));
+  EXPECT_TRUE(IsPrerolling(true));
+  PrerollDecoderToTime(
+      true, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100), false);
+  EXPECT_TRUE(IsPrerolling(false));
+
+  // Send more packets
+  PlayAudioForTimeInterval(base::TimeDelta::FromMilliseconds(150),
+                           base::TimeDelta::FromMilliseconds(220));
+
+  // The player should trigger audible status
+  EXPECT_TRUE(manager_.is_audible());
+
+  // Simulate the freeze on demuxer: wait for 300 ms
+  WaitForDelay(base::TimeDelta::FromMilliseconds(300));
+
+  // By this time the player should have reported
+  // that there is no audio.
+  EXPECT_FALSE(manager_.is_audible());
+
+  ReleasePlayer();
+}
+
 TEST_F(MediaSourcePlayerTest, StartVideoCodecWithValidSurface) {
   SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
 
diff --git a/media/base/eme_constants.h b/media/base/eme_constants.h
index 2f8f033..70d58c5 100644
--- a/media/base/eme_constants.h
+++ b/media/base/eme_constants.h
@@ -9,18 +9,23 @@
 
 namespace media {
 
-// Defines bitmask values that specify registered initialization data types used
+// Defines values that specify registered Initialization Data Types used
 // in Encrypted Media Extensions (EME).
-// The mask values are stored in a SupportedInitDataTypes.
-enum EmeInitDataType {
-  EME_INIT_DATA_TYPE_NONE = 0,
-  EME_INIT_DATA_TYPE_WEBM = 1 << 0,
-#if defined(USE_PROPRIETARY_CODECS)
-  EME_INIT_DATA_TYPE_CENC = 1 << 1,
-#endif  // defined(USE_PROPRIETARY_CODECS)
-  EME_INIT_DATA_TYPE_KEYIDS = 1 << 2,
+// http://w3c.github.io/encrypted-media/initdata-format-registry.html#registry
+// The mask values are stored in a InitDataTypeMask.
+enum class EmeInitDataType {
+  UNKNOWN,
+  WEBM,
+  CENC,
+  KEYIDS
 };
 
+typedef uint32_t InitDataTypeMask;
+const InitDataTypeMask kInitDataTypeMaskNone = 0;
+const InitDataTypeMask kInitDataTypeMaskWebM = 1 << 0;
+const InitDataTypeMask kInitDataTypeMaskCenc = 1 << 1;
+const InitDataTypeMask kInitDataTypeMaskKeyIds = 1 << 2;
+
 // Defines bitmask values that specify codecs used in Encrypted Media Extension
 // (EME). Each value represents a codec within a specific container.
 // The mask values are stored in a SupportedCodecs.
@@ -41,13 +46,16 @@
   EME_CODEC_MP4_AVC1 = 1 << 5,
   EME_CODEC_MP4_VIDEO_ALL = EME_CODEC_MP4_AVC1,
   EME_CODEC_MP4_ALL = (EME_CODEC_MP4_AUDIO_ALL | EME_CODEC_MP4_VIDEO_ALL),
+  EME_CODEC_AUDIO_ALL = (EME_CODEC_WEBM_AUDIO_ALL | EME_CODEC_MP4_AUDIO_ALL),
+  EME_CODEC_VIDEO_ALL = (EME_CODEC_WEBM_VIDEO_ALL | EME_CODEC_WEBM_VIDEO_ALL),
   EME_CODEC_ALL = (EME_CODEC_WEBM_ALL | EME_CODEC_MP4_ALL),
 #else
+  EME_CODEC_AUDIO_ALL = EME_CODEC_WEBM_AUDIO_ALL,
+  EME_CODEC_VIDEO_ALL = EME_CODEC_WEBM_VIDEO_ALL,
   EME_CODEC_ALL = EME_CODEC_WEBM_ALL,
 #endif  // defined(USE_PROPRIETARY_CODECS)
 };
 
-typedef uint32_t SupportedInitDataTypes;
 typedef uint32_t SupportedCodecs;
 
 enum EmeSessionTypeSupport {
@@ -55,32 +63,75 @@
   EME_SESSION_TYPE_INVALID,
   // The session type is not supported.
   EME_SESSION_TYPE_NOT_SUPPORTED,
-  // The session type is supported if the encrypted media permission is granted.
-  EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION,
+  // The session type is supported if a distinctive identifier is available.
+  EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER,
   // The session type is always supported.
   EME_SESSION_TYPE_SUPPORTED,
 };
 
+// Used to declare support for distinctive identifier and persistent state.
 enum EmeFeatureSupport {
   // Invalid default value.
   EME_FEATURE_INVALID,
   // Access to the feature is not supported at all.
   EME_FEATURE_NOT_SUPPORTED,
-  // Access to the feature may be requested if the encrypted media permission is
-  // granted.
-  EME_FEATURE_REQUESTABLE_WITH_PERMISSION,
+  // Access to the feature may be requested if a distinctive identifier is
+  // available. (This is the correct choice for declaring support for a
+  // requestable distinctive identifier.)
+  EME_FEATURE_REQUESTABLE_WITH_IDENTIFIER,
   // Access to the feature may be requested.
   EME_FEATURE_REQUESTABLE,
   // Access to the feature cannot be blocked.
   EME_FEATURE_ALWAYS_ENABLED,
 };
 
+// Used to query support for distinctive identifier and persistent state.
 enum EmeFeatureRequirement {
   EME_FEATURE_NOT_ALLOWED,
   EME_FEATURE_OPTIONAL,
   EME_FEATURE_REQUIRED,
 };
 
+enum class EmeMediaType {
+  AUDIO,
+  VIDEO,
+};
+
+// Robustness values understood by KeySystems.
+// Note: key_systems.cc expects this ordering in GetRobustnessConfigRule(),
+// make sure to correct that code if this list changes.
+enum class EmeRobustness {
+  INVALID,
+  EMPTY,
+  SW_SECURE_CRYPTO,
+  SW_SECURE_DECODE,
+  HW_SECURE_CRYPTO,
+  HW_SECURE_DECODE,
+  HW_SECURE_ALL,
+};
+
+// Configuration rules indicate the configuration state required to support a
+// configuration option (note: a configuration option may be disallowing a
+// feature). Configuration rules are used to answer queries about distinctive
+// identifier, persistent state, and robustness requirements, as well as to
+// describe support for different session types.
+//
+// If in the future there are reasons to request user permission other than
+// access to a distinctive identifier, then additional rules should be added.
+// Rules are implemented in ConfigState and are otherwise opaque.
+enum class EmeConfigRule {
+  // The configuration option is not supported.
+  NOT_SUPPORTED,
+  // The configuration option is supported if a distinctive identifier is
+  // available.
+  IDENTIFIER_REQUIRED,
+  // The configuration option is supported, but the user experience may be
+  // improved if a distinctive identifier is available.
+  IDENTIFIER_RECOMMENDED,
+  // The configuration option is supported without conditions.
+  SUPPORTED,
+};
+
 }  // namespace media
 
 #endif  // MEDIA_BASE_EME_CONSTANTS_H_
diff --git a/media/base/key_system_info.cc b/media/base/key_system_info.cc
index b656f0f0..f5ca112 100644
--- a/media/base/key_system_info.cc
+++ b/media/base/key_system_info.cc
@@ -7,8 +7,10 @@
 namespace media {
 
 KeySystemInfo::KeySystemInfo()
-    : supported_init_data_types(EME_INIT_DATA_TYPE_NONE),
+    : supported_init_data_types(kInitDataTypeMaskNone),
       supported_codecs(EME_CODEC_NONE),
+      max_audio_robustness(EmeRobustness::INVALID),
+      max_video_robustness(EmeRobustness::INVALID),
       persistent_license_support(EME_SESSION_TYPE_INVALID),
       persistent_release_message_support(EME_SESSION_TYPE_INVALID),
       persistent_state_support(EME_FEATURE_INVALID),
diff --git a/media/base/key_system_info.h b/media/base/key_system_info.h
index e66d8be8..4c60552 100644
--- a/media/base/key_system_info.h
+++ b/media/base/key_system_info.h
@@ -35,13 +35,10 @@
 
   std::string key_system;
 
-  // Specifies registered initialization data types supported by |key_system|.
-  SupportedInitDataTypes supported_init_data_types;
-
-  // Specifies codecs supported by |key_system|.
+  InitDataTypeMask supported_init_data_types;
   SupportedCodecs supported_codecs;
-
-  // Specifies session types and features supported by |key_system|.
+  EmeRobustness max_audio_robustness;
+  EmeRobustness max_video_robustness;
   EmeSessionTypeSupport persistent_license_support;
   EmeSessionTypeSupport persistent_release_message_support;
   EmeFeatureSupport persistent_state_support;
diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc
index 92bcc38e8..85bde8b 100644
--- a/media/base/key_systems.cc
+++ b/media/base/key_systems.cc
@@ -34,14 +34,11 @@
   EmeInitDataType type;
 };
 
-// Mapping between initialization data types names and enum values. When adding
-// entries, make sure to update IsSaneInitDataTypeWithContainer().
+// Mapping between initialization data types names and enum values.
 static NamedInitDataType kInitDataTypeNames[] = {
-    {"webm", EME_INIT_DATA_TYPE_WEBM},
-#if defined(USE_PROPRIETARY_CODECS)
-    {"cenc", EME_INIT_DATA_TYPE_CENC},
-#endif  // defined(USE_PROPRIETARY_CODECS)
-    {"keyids", EME_INIT_DATA_TYPE_KEYIDS},
+    {"webm", EmeInitDataType::WEBM},
+    {"cenc", EmeInitDataType::CENC},
+    {"keyids", EmeInitDataType::KEYIDS},
 };
 
 struct NamedCodec {
@@ -52,6 +49,8 @@
 // Mapping between containers and their codecs.
 // Only audio codec can belong to a "audio/*" container. Both audio and video
 // codecs can belong to a "video/*" container.
+// TODO(sandersd): This definition only makes sense for prefixed EME. Change it
+// when prefixed EME is removed. http://crbug.com/249976
 static NamedCodec kContainerToCodecMasks[] = {
     {"audio/webm", EME_CODEC_WEBM_AUDIO_ALL},
     {"video/webm", EME_CODEC_WEBM_ALL},
@@ -76,6 +75,39 @@
 #endif  // defined(USE_PROPRIETARY_CODECS)
 };
 
+static EmeConfigRule ConvertSessionTypeSupport(
+    EmeSessionTypeSupport support) {
+  switch (support) {
+    case EME_SESSION_TYPE_INVALID:
+      NOTREACHED();
+      return EmeConfigRule::NOT_SUPPORTED;
+    case EME_SESSION_TYPE_NOT_SUPPORTED:
+      return EmeConfigRule::NOT_SUPPORTED;
+    case EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER:
+      return EmeConfigRule::IDENTIFIER_REQUIRED;
+    case EME_SESSION_TYPE_SUPPORTED:
+      return EmeConfigRule::SUPPORTED;
+  }
+  NOTREACHED();
+  return EmeConfigRule::NOT_SUPPORTED;
+}
+
+static EmeRobustness ConvertRobustness(const std::string& robustness) {
+  if (robustness.empty())
+    return EmeRobustness::EMPTY;
+  if (robustness == "SW_SECURE_CRYPTO")
+    return EmeRobustness::SW_SECURE_CRYPTO;
+  if (robustness == "SW_SECURE_DECODE")
+    return EmeRobustness::SW_SECURE_DECODE;
+  if (robustness == "HW_SECURE_CRYPTO")
+    return EmeRobustness::HW_SECURE_CRYPTO;
+  if (robustness == "HW_SECURE_DECODE")
+    return EmeRobustness::HW_SECURE_DECODE;
+  if (robustness == "HW_SECURE_ALL")
+    return EmeRobustness::HW_SECURE_ALL;
+  return EmeRobustness::INVALID;
+}
+
 static void AddClearKey(std::vector<KeySystemInfo>* concrete_key_systems) {
   KeySystemInfo info;
   info.key_system = kClearKeyKeySystem;
@@ -85,7 +117,7 @@
   // VP9 support is device dependent.
 
   info.supported_init_data_types =
-      EME_INIT_DATA_TYPE_WEBM | EME_INIT_DATA_TYPE_KEYIDS;
+      kInitDataTypeMaskWebM | kInitDataTypeMaskKeyIds;
   info.supported_codecs = EME_CODEC_WEBM_ALL;
 
 #if defined(OS_ANDROID)
@@ -99,10 +131,12 @@
 #endif  // defined(OS_ANDROID)
 
 #if defined(USE_PROPRIETARY_CODECS)
-  info.supported_init_data_types |= EME_INIT_DATA_TYPE_CENC;
+  info.supported_init_data_types |= kInitDataTypeMaskCenc;
   info.supported_codecs |= EME_CODEC_MP4_ALL;
 #endif  // defined(USE_PROPRIETARY_CODECS)
 
+  info.max_audio_robustness = EmeRobustness::EMPTY;
+  info.max_video_robustness = EmeRobustness::EMPTY;
   info.persistent_license_support = EME_SESSION_TYPE_NOT_SUPPORTED;
   info.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED;
   info.persistent_state_support = EME_FEATURE_NOT_SUPPORTED;
@@ -125,9 +159,9 @@
 // key system-specific behaviors not fully defined by the EME specification.
 // That specification should be provided by the owner of the domain that is the
 // reverse of the |key_system| string.
-// This involves more than calling a library, SDK, or platform API. KeySystems
-// must be populated appropriately, and there will likely be glue code to adapt
-// to the API of the library, SDK, or platform API.
+// This involves more than calling a library, SDK, or platform API.
+// KeySystemsImpl must be populated appropriately, and there will likely be glue
+// code to adapt to the API of the library, SDK, or platform API.
 //
 // Chromium mainline contains this data and glue code for specific key systems,
 // which should help ensure interoperability with other implementations using
@@ -136,7 +170,7 @@
 // If you need to add support for other key systems, ensure that you have
 // obtained the specification for how to integrate it with EME, implemented the
 // appropriate glue/adapter code, and added all the appropriate data to
-// KeySystems. Only then should you change this function.
+// KeySystemsImpl. Only then should you change this function.
 static bool IsPotentiallySupportedKeySystem(const std::string& key_system) {
   // Known and supported key systems.
   if (key_system == kWidevineKeySystem)
@@ -163,54 +197,64 @@
   return false;
 }
 
-class KeySystems {
+class KeySystemsImpl : public KeySystems {
  public:
-  static KeySystems& GetInstance();
+  static KeySystemsImpl& GetInstance();
 
   void UpdateIfNeeded();
 
-  bool IsConcreteSupportedKeySystem(const std::string& key_system);
-
-  bool IsSupportedKeySystem(const std::string& key_system);
+  bool IsConcreteSupportedKeySystem(const std::string& key_system) const;
 
   bool IsSupportedKeySystemWithInitDataType(
       const std::string& key_system,
-      const std::string& init_data_type);
+      const std::string& init_data_type) const;
 
-  bool IsSupportedKeySystemWithMediaMimeType(
+  bool PrefixedIsSupportedKeySystemWithMediaMimeType(
       const std::string& mime_type,
       const std::vector<std::string>& codecs,
-      const std::string& key_system,
-      bool is_prefixed);
+      const std::string& key_system);
 
   std::string GetKeySystemNameForUMA(const std::string& key_system) const;
 
-  bool UseAesDecryptor(const std::string& concrete_key_system);
+  bool UseAesDecryptor(const std::string& concrete_key_system) const;
 
 #if defined(ENABLE_PEPPER_CDMS)
-  std::string GetPepperType(const std::string& concrete_key_system);
+  std::string GetPepperType(const std::string& concrete_key_system) const;
 #endif
 
-  bool IsPersistentLicenseSessionSupported(
-      const std::string& key_system,
-      bool is_permission_granted);
-
-  bool IsPersistentReleaseMessageSessionSupported(
-      const std::string& key_system,
-      bool is_permission_granted);
-
-  bool IsPersistentStateRequirementSupported(
-      const std::string& key_system,
-      EmeFeatureRequirement requirement,
-      bool is_permission_granted);
-
-  bool IsDistinctiveIdentifierRequirementSupported(
-      const std::string& key_system,
-      EmeFeatureRequirement requirement,
-      bool is_permission_granted);
-
   void AddContainerMask(const std::string& container, uint32 mask);
-  void AddCodecMask(const std::string& codec, uint32 mask);
+  void AddCodecMask(
+      EmeMediaType media_type,
+      const std::string& codec,
+      uint32 mask);
+
+  // Implementation of KeySystems interface.
+  bool IsSupportedKeySystem(const std::string& key_system) const override;
+
+  bool IsSupportedCodecCombination(
+      const std::string& key_system,
+      EmeMediaType media_type,
+      const std::string& container_mime_type,
+      const std::vector<std::string>& codecs) const override;
+
+  EmeConfigRule GetRobustnessConfigRule(
+      const std::string& key_system,
+      EmeMediaType media_type,
+      const std::string& requested_robustness) const override;
+
+  EmeConfigRule GetPersistentLicenseSessionConfigRule(
+      const std::string& key_system) const override;
+
+  EmeConfigRule GetPersistentReleaseMessageSessionConfigRule(
+      const std::string& key_system) const override;
+
+  EmeConfigRule GetPersistentStateConfigRule(
+      const std::string& key_system,
+      EmeFeatureRequirement requirement) const override;
+
+  EmeConfigRule GetDistinctiveIdentifierConfigRule(
+      const std::string& key_system,
+      EmeFeatureRequirement requirement) const override;
 
  private:
   void InitializeUMAInfo();
@@ -220,7 +264,7 @@
   void AddConcreteSupportedKeySystems(
       const std::vector<KeySystemInfo>& concrete_key_systems);
 
-  friend struct base::DefaultLazyInstanceTraits<KeySystems>;
+  friend struct base::DefaultLazyInstanceTraits<KeySystemsImpl>;
 
   typedef base::hash_map<std::string, KeySystemInfo> KeySystemInfoMap;
   typedef base::hash_map<std::string, std::string> ParentKeySystemMap;
@@ -229,8 +273,8 @@
   typedef base::hash_map<std::string, EmeInitDataType> InitDataTypesMap;
   typedef base::hash_map<std::string, std::string> KeySystemNameForUMAMap;
 
-  KeySystems();
-  ~KeySystems() {}
+  KeySystemsImpl();
+  ~KeySystemsImpl() {}
 
   EmeInitDataType GetInitDataTypeForName(
       const std::string& init_data_type) const;
@@ -270,23 +314,29 @@
   CodecsMap codec_string_map_;
   KeySystemNameForUMAMap key_system_name_for_uma_map_;
 
+  SupportedCodecs audio_codec_mask_;
+  SupportedCodecs video_codec_mask_;
+
   // Makes sure all methods are called from the same thread.
   base::ThreadChecker thread_checker_;
 
-  DISALLOW_COPY_AND_ASSIGN(KeySystems);
+  DISALLOW_COPY_AND_ASSIGN(KeySystemsImpl);
 };
 
-static base::LazyInstance<KeySystems> g_key_systems = LAZY_INSTANCE_INITIALIZER;
+static base::LazyInstance<KeySystemsImpl> g_key_systems =
+    LAZY_INSTANCE_INITIALIZER;
 
-KeySystems& KeySystems::GetInstance() {
-  KeySystems& key_systems = g_key_systems.Get();
+KeySystemsImpl& KeySystemsImpl::GetInstance() {
+  KeySystemsImpl& key_systems = g_key_systems.Get();
   key_systems.UpdateIfNeeded();
   return key_systems;
 }
 
 // Because we use a LazyInstance, the key systems info must be populated when
 // the instance is lazily initiated.
-KeySystems::KeySystems() {
+KeySystemsImpl::KeySystemsImpl() :
+    audio_codec_mask_(EME_CODEC_AUDIO_ALL),
+    video_codec_mask_(EME_CODEC_VIDEO_ALL) {
   for (size_t i = 0; i < arraysize(kInitDataTypeNames); ++i) {
     const std::string& name = kInitDataTypeNames[i].name;
     DCHECK(!init_data_type_name_map_.count(name));
@@ -309,16 +359,16 @@
   UpdateSupportedKeySystems();
 }
 
-EmeInitDataType KeySystems::GetInitDataTypeForName(
+EmeInitDataType KeySystemsImpl::GetInitDataTypeForName(
     const std::string& init_data_type) const {
   InitDataTypesMap::const_iterator iter =
       init_data_type_name_map_.find(init_data_type);
   if (iter != init_data_type_name_map_.end())
     return iter->second;
-  return EME_INIT_DATA_TYPE_NONE;
+  return EmeInitDataType::UNKNOWN;
 }
 
-SupportedCodecs KeySystems::GetCodecMaskForContainer(
+SupportedCodecs KeySystemsImpl::GetCodecMaskForContainer(
     const std::string& container) const {
   ContainerCodecsMap::const_iterator iter =
       container_to_codec_mask_map_.find(container);
@@ -327,14 +377,14 @@
   return EME_CODEC_NONE;
 }
 
-EmeCodec KeySystems::GetCodecForString(const std::string& codec) const {
+EmeCodec KeySystemsImpl::GetCodecForString(const std::string& codec) const {
   CodecsMap::const_iterator iter = codec_string_map_.find(codec);
   if (iter != codec_string_map_.end())
     return iter->second;
   return EME_CODEC_NONE;
 }
 
-const std::string& KeySystems::PrefixedGetConcreteKeySystemNameFor(
+const std::string& KeySystemsImpl::PrefixedGetConcreteKeySystemNameFor(
     const std::string& key_system) const {
   ParentKeySystemMap::const_iterator iter =
       parent_key_system_map_.find(key_system);
@@ -343,7 +393,7 @@
   return key_system;
 }
 
-void KeySystems::InitializeUMAInfo() {
+void KeySystemsImpl::InitializeUMAInfo() {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(key_system_name_for_uma_map_.empty());
 
@@ -363,12 +413,12 @@
       kClearKeyKeySystemNameForUMA;
 }
 
-void KeySystems::UpdateIfNeeded() {
+void KeySystemsImpl::UpdateIfNeeded() {
   if (GetMediaClient() && GetMediaClient()->IsKeySystemsUpdateNeeded())
     UpdateSupportedKeySystems();
 }
 
-void KeySystems::UpdateSupportedKeySystems() {
+void KeySystemsImpl::UpdateSupportedKeySystems() {
   DCHECK(thread_checker_.CalledOnValidThread());
   concrete_key_system_map_.clear();
   parent_key_system_map_.clear();
@@ -386,7 +436,7 @@
   AddConcreteSupportedKeySystems(key_systems_info);
 }
 
-void KeySystems::AddConcreteSupportedKeySystems(
+void KeySystemsImpl::AddConcreteSupportedKeySystems(
     const std::vector<KeySystemInfo>& concrete_key_systems) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(concrete_key_system_map_.empty());
@@ -394,45 +444,71 @@
 
   for (const KeySystemInfo& info : concrete_key_systems) {
     DCHECK(!info.key_system.empty());
-    DCHECK_NE(info.persistent_license_support, EME_SESSION_TYPE_INVALID);
-    DCHECK_NE(info.persistent_release_message_support,
-              EME_SESSION_TYPE_INVALID);
-    // TODO(sandersd): Add REQUESTABLE and REQUESTABLE_WITH_PERMISSION for
-    // persistent_state_support once we can block access per-CDM-instance
-    // (http://crbug.com/457482).
-    DCHECK(info.persistent_state_support == EME_FEATURE_NOT_SUPPORTED ||
-           info.persistent_state_support == EME_FEATURE_ALWAYS_ENABLED);
-// TODO(sandersd): Allow REQUESTABLE_WITH_PERMISSION for all key systems on
-// all platforms once we have proper enforcement (http://crbug.com/457482).
-// On Chrome OS, an ID will not be used without permission, but we cannot
-// currently prevent the CDM from requesting the permission again when no
-// there was no initial prompt. Thus, we block "not-allowed" below.
-#if defined(OS_CHROMEOS)
-    DCHECK(info.distinctive_identifier_support == EME_FEATURE_NOT_SUPPORTED ||
-           (info.distinctive_identifier_support ==
-                EME_FEATURE_REQUESTABLE_WITH_PERMISSION &&
-            info.key_system == kWidevineKeySystem) ||
-           info.distinctive_identifier_support == EME_FEATURE_ALWAYS_ENABLED);
-#else
-    DCHECK(info.distinctive_identifier_support == EME_FEATURE_NOT_SUPPORTED ||
-           info.distinctive_identifier_support == EME_FEATURE_ALWAYS_ENABLED);
-#endif
+    DCHECK(info.max_audio_robustness != EmeRobustness::INVALID);
+    DCHECK(info.max_video_robustness != EmeRobustness::INVALID);
+    DCHECK(info.persistent_license_support != EME_SESSION_TYPE_INVALID);
+    DCHECK(info.persistent_release_message_support != EME_SESSION_TYPE_INVALID);
+    DCHECK(info.persistent_state_support != EME_FEATURE_INVALID);
+    DCHECK(info.distinctive_identifier_support != EME_FEATURE_INVALID);
+
+    // Supporting persistent state is a prerequsite for supporting persistent
+    // sessions.
     if (info.persistent_state_support == EME_FEATURE_NOT_SUPPORTED) {
-      DCHECK_EQ(info.persistent_license_support,
-                EME_SESSION_TYPE_NOT_SUPPORTED);
-      DCHECK_EQ(info.persistent_release_message_support,
-                EME_SESSION_TYPE_NOT_SUPPORTED);
+      DCHECK(info.persistent_license_support == EME_SESSION_TYPE_NOT_SUPPORTED);
+      DCHECK(info.persistent_release_message_support ==
+             EME_SESSION_TYPE_NOT_SUPPORTED);
     }
+    else if (info.persistent_state_support ==
+             EME_FEATURE_REQUESTABLE_WITH_IDENTIFIER) {
+      // Must be either NOT_SUPPORTED or SUPPORTED_WITH_IDENTIFIER.
+      DCHECK(info.persistent_license_support != EME_SESSION_TYPE_SUPPORTED);
+      DCHECK(info.persistent_release_message_support !=
+             EME_SESSION_TYPE_SUPPORTED);
+    }
+
+    // persistent-release-message sessions are not currently supported.
+    // http://crbug.com/448888
+    DCHECK(info.persistent_release_message_support ==
+           EME_SESSION_TYPE_NOT_SUPPORTED);
+
+    // Because an optional persistent state value is resolved after an optional
+    // distinctive identifier, persistent state requiring a distinctive
+    // identifier may not resolve correctly.
+    DCHECK(info.persistent_state_support !=
+           EME_FEATURE_REQUESTABLE_WITH_IDENTIFIER);
+
+    // If supported, distinctive identifiers always require permission.
+    DCHECK(info.distinctive_identifier_support != EME_FEATURE_REQUESTABLE);
+
+    // If distinctive identifiers are not supported, then no other features can
+    // require them.
+    if (info.distinctive_identifier_support == EME_FEATURE_NOT_SUPPORTED) {
+      DCHECK(info.persistent_license_support !=
+             EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER);
+      DCHECK(info.persistent_release_message_support !=
+             EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER);
+    }
+
+    // Distinctive identifiers and persistent state can only be reliably blocked
+    // (and therefore be safely configurable) for Pepper-hosted key systems. For
+    // other platforms, only non-configurable values are valid.
+    bool can_block = false;
+#if defined(ENABLE_PEPPER_CDMS)
+    DCHECK_EQ(info.use_aes_decryptor, info.pepper_type.empty());
+    can_block = !info.pepper_type.empty();
+#endif
+    if (!can_block) {
+      DCHECK(info.distinctive_identifier_support == EME_FEATURE_NOT_SUPPORTED ||
+             info.distinctive_identifier_support == EME_FEATURE_ALWAYS_ENABLED);
+      DCHECK(info.persistent_state_support == EME_FEATURE_NOT_SUPPORTED ||
+             info.persistent_state_support == EME_FEATURE_ALWAYS_ENABLED);
+    }
+
     DCHECK(!IsSupportedKeySystem(info.key_system))
         << "Key system '" << info.key_system << "' already registered";
     DCHECK(!parent_key_system_map_.count(info.key_system))
         <<  "'" << info.key_system << "' is already registered as a parent";
-#if defined(ENABLE_PEPPER_CDMS)
-    DCHECK_EQ(info.use_aes_decryptor, info.pepper_type.empty());
-#endif
-
     concrete_key_system_map_[info.key_system] = info;
-
     if (!info.parent_key_system.empty()) {
       DCHECK(!IsConcreteSupportedKeySystem(info.parent_key_system))
           << "Parent '" << info.parent_key_system << "' "
@@ -444,12 +520,13 @@
   }
 }
 
-bool KeySystems::IsConcreteSupportedKeySystem(const std::string& key_system) {
+bool KeySystemsImpl::IsConcreteSupportedKeySystem(
+    const std::string& key_system) const {
   DCHECK(thread_checker_.CalledOnValidThread());
   return concrete_key_system_map_.count(key_system) != 0;
 }
 
-bool KeySystems::IsSupportedContainer(
+bool KeySystemsImpl::IsSupportedContainer(
     const std::string& container,
     SupportedCodecs key_system_supported_codecs) const {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -470,7 +547,7 @@
   return (supported_codecs & key_system_supported_codecs) != 0;
 }
 
-bool KeySystems::IsSupportedContainerAndCodecs(
+bool KeySystemsImpl::IsSupportedContainerAndCodecs(
     const std::string& container,
     const std::vector<std::string>& codecs,
     SupportedCodecs key_system_supported_codecs) const {
@@ -483,8 +560,6 @@
       GetCodecMaskForContainer(container);
 
   for (size_t i = 0; i < codecs.size(); ++i) {
-    // TODO(sandersd): This should fail for isTypeSupported().
-    // http://crbug.com/417461
     if (codecs[i].empty())
       continue;
 
@@ -502,15 +577,9 @@
   return true;
 }
 
-bool KeySystems::IsSupportedKeySystem(const std::string& key_system) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  // Unprefixed EME only supports concrete key systems.
-  return concrete_key_system_map_.count(key_system) != 0;
-}
-
-bool KeySystems::IsSupportedKeySystemWithInitDataType(
+bool KeySystemsImpl::IsSupportedKeySystemWithInitDataType(
     const std::string& key_system,
-    const std::string& init_data_type) {
+    const std::string& init_data_type) const {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // Locate |key_system|. Only concrete key systems are supported in unprefixed.
@@ -519,33 +588,35 @@
   if (key_system_iter == concrete_key_system_map_.end())
     return false;
 
-  // Check |init_data_type| and |key_system| x |init_data_type|.
-  const KeySystemInfo& info = key_system_iter->second;
-  EmeInitDataType eme_init_data_type = GetInitDataTypeForName(init_data_type);
-  return (info.supported_init_data_types & eme_init_data_type) != 0;
+  // Check |init_data_type|.
+  InitDataTypeMask available_init_data_types =
+      key_system_iter->second.supported_init_data_types;
+  switch (GetInitDataTypeForName(init_data_type)) {
+    case EmeInitDataType::UNKNOWN:
+      return false;
+    case EmeInitDataType::WEBM:
+      return (available_init_data_types & kInitDataTypeMaskWebM) != 0;
+    case EmeInitDataType::CENC:
+      return (available_init_data_types & kInitDataTypeMaskCenc) != 0;
+    case EmeInitDataType::KEYIDS:
+      return (available_init_data_types & kInitDataTypeMaskKeyIds) != 0;
+  }
+  NOTREACHED();
+  return false;
 }
 
-// TODO(sandersd): Reorganize to be more similar to
-// IsKeySystemSupportedWithInitDataType(). Note that a fork may still be
-// required; http://crbug.com/417461.
-bool KeySystems::IsSupportedKeySystemWithMediaMimeType(
+bool KeySystemsImpl::PrefixedIsSupportedKeySystemWithMediaMimeType(
     const std::string& mime_type,
     const std::vector<std::string>& codecs,
-    const std::string& key_system,
-    bool is_prefixed) {
+    const std::string& key_system) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  const bool report_to_uma = is_prefixed;
 
-  // If |is_prefixed| and |key_system| is a parent key system, use its concrete
-  // child.
-  const std::string& concrete_key_system = is_prefixed ?
-      PrefixedGetConcreteKeySystemNameFor(key_system) :
-      key_system;
+  const std::string& concrete_key_system =
+      PrefixedGetConcreteKeySystemNameFor(key_system);
 
   bool has_type = !mime_type.empty();
 
-  if (report_to_uma)
-    key_systems_support_uma_.ReportKeySystemQuery(key_system, has_type);
+  key_systems_support_uma_.ReportKeySystemQuery(key_system, has_type);
 
   // Check key system support.
   KeySystemInfoMap::const_iterator key_system_iter =
@@ -553,8 +624,7 @@
   if (key_system_iter == concrete_key_system_map_.end())
     return false;
 
-  if (report_to_uma)
-    key_systems_support_uma_.ReportKeySystemSupport(key_system, false);
+  key_systems_support_uma_.ReportKeySystemSupport(key_system, false);
 
   if (!has_type) {
     DCHECK(codecs.empty());
@@ -573,12 +643,12 @@
     return false;
   }
 
-  if (report_to_uma)
-    key_systems_support_uma_.ReportKeySystemSupport(key_system, true);
+  key_systems_support_uma_.ReportKeySystemSupport(key_system, true);
+
   return true;
 }
 
-std::string KeySystems::GetKeySystemNameForUMA(
+std::string KeySystemsImpl::GetKeySystemNameForUMA(
     const std::string& key_system) const {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -590,7 +660,8 @@
   return iter->second;
 }
 
-bool KeySystems::UseAesDecryptor(const std::string& concrete_key_system) {
+bool KeySystemsImpl::UseAesDecryptor(
+    const std::string& concrete_key_system) const {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   KeySystemInfoMap::const_iterator key_system_iter =
@@ -604,7 +675,8 @@
 }
 
 #if defined(ENABLE_PEPPER_CDMS)
-std::string KeySystems::GetPepperType(const std::string& concrete_key_system) {
+std::string KeySystemsImpl::GetPepperType(
+    const std::string& concrete_key_system) const {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   KeySystemInfoMap::const_iterator key_system_iter =
@@ -620,144 +692,252 @@
 }
 #endif
 
-bool KeySystems::IsPersistentLicenseSessionSupported(
-    const std::string& key_system,
-    bool is_permission_granted) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  KeySystemInfoMap::const_iterator key_system_iter =
-      concrete_key_system_map_.find(key_system);
-  if (key_system_iter == concrete_key_system_map_.end()) {
-    NOTREACHED();
-    return false;
-  }
-
-  switch (key_system_iter->second.persistent_license_support) {
-    case EME_SESSION_TYPE_INVALID:
-      NOTREACHED();
-      return false;
-    case EME_SESSION_TYPE_NOT_SUPPORTED:
-      return false;
-    case EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION:
-      return is_permission_granted;
-    case EME_SESSION_TYPE_SUPPORTED:
-      return true;
-  }
-
-  NOTREACHED();
-  return false;
-}
-
-bool KeySystems::IsPersistentReleaseMessageSessionSupported(
-    const std::string& key_system,
-    bool is_permission_granted) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  KeySystemInfoMap::const_iterator key_system_iter =
-      concrete_key_system_map_.find(key_system);
-  if (key_system_iter == concrete_key_system_map_.end()) {
-    NOTREACHED();
-    return false;
-  }
-
-  switch (key_system_iter->second.persistent_release_message_support) {
-    case EME_SESSION_TYPE_INVALID:
-      NOTREACHED();
-      return false;
-    case EME_SESSION_TYPE_NOT_SUPPORTED:
-      return false;
-    case EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION:
-      return is_permission_granted;
-    case EME_SESSION_TYPE_SUPPORTED:
-      return true;
-  }
-
-  NOTREACHED();
-  return false;
-}
-
-bool KeySystems::IsPersistentStateRequirementSupported(
-    const std::string& key_system,
-    EmeFeatureRequirement requirement,
-    bool is_permission_granted) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  KeySystemInfoMap::const_iterator key_system_iter =
-      concrete_key_system_map_.find(key_system);
-  if (key_system_iter == concrete_key_system_map_.end()) {
-    NOTREACHED();
-    return false;
-  }
-
-  switch (key_system_iter->second.persistent_state_support) {
-    case EME_FEATURE_INVALID:
-      NOTREACHED();
-      return false;
-    case EME_FEATURE_NOT_SUPPORTED:
-      return requirement != EME_FEATURE_REQUIRED;
-    case EME_FEATURE_REQUESTABLE_WITH_PERMISSION:
-      return (requirement != EME_FEATURE_REQUIRED) || is_permission_granted;
-    case EME_FEATURE_REQUESTABLE:
-      return true;
-    case EME_FEATURE_ALWAYS_ENABLED:
-      // Persistent state does not require user permission, but the session
-      // types that use it might.
-      return requirement != EME_FEATURE_NOT_ALLOWED;
-  }
-
-  NOTREACHED();
-  return false;
-}
-
-bool KeySystems::IsDistinctiveIdentifierRequirementSupported(
-    const std::string& key_system,
-    EmeFeatureRequirement requirement,
-    bool is_permission_granted) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  KeySystemInfoMap::const_iterator key_system_iter =
-      concrete_key_system_map_.find(key_system);
-  if (key_system_iter == concrete_key_system_map_.end()) {
-    NOTREACHED();
-    return false;
-  }
-
-  switch (key_system_iter->second.distinctive_identifier_support) {
-    case EME_FEATURE_INVALID:
-      NOTREACHED();
-      return false;
-    case EME_FEATURE_NOT_SUPPORTED:
-      return requirement != EME_FEATURE_REQUIRED;
-    case EME_FEATURE_REQUESTABLE_WITH_PERMISSION:
-      // TODO(sandersd): Remove this hack once crbug.com/457482 and
-      // crbug.com/460616 are addressed.
-      // We cannot currently enforce "not-allowed", so don't allow it.
-      // Note: Removing this check will expose crbug.com/460616.
-      if (requirement == EME_FEATURE_NOT_ALLOWED)
-        return false;
-      return (requirement != EME_FEATURE_REQUIRED) || is_permission_granted;
-    case EME_FEATURE_REQUESTABLE:
-      NOTREACHED();
-      return true;
-    case EME_FEATURE_ALWAYS_ENABLED:
-      // Distinctive identifiers always require user permission.
-      return (requirement != EME_FEATURE_NOT_ALLOWED) && is_permission_granted;
-  }
-
-  NOTREACHED();
-  return false;
-}
-
-void KeySystems::AddContainerMask(const std::string& container, uint32 mask) {
+void KeySystemsImpl::AddContainerMask(
+    const std::string& container,
+    uint32 mask) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!container_to_codec_mask_map_.count(container));
   container_to_codec_mask_map_[container] = static_cast<EmeCodec>(mask);
 }
 
-void KeySystems::AddCodecMask(const std::string& codec, uint32 mask) {
+void KeySystemsImpl::AddCodecMask(
+    EmeMediaType media_type,
+    const std::string& codec,
+    uint32 mask) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!codec_string_map_.count(codec));
   codec_string_map_[codec] = static_cast<EmeCodec>(mask);
+  if (media_type == EmeMediaType::AUDIO) {
+    audio_codec_mask_ |= mask;
+  } else {
+    video_codec_mask_ |= mask;
+  }
+}
+
+bool KeySystemsImpl::IsSupportedKeySystem(const std::string& key_system) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return concrete_key_system_map_.count(key_system) != 0;
+}
+
+bool KeySystemsImpl::IsSupportedCodecCombination(
+    const std::string& key_system,
+    EmeMediaType media_type,
+    const std::string& container_mime_type,
+    const std::vector<std::string>& codecs) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Make sure the container matches |media_type|.
+  SupportedCodecs media_type_codec_mask = EME_CODEC_NONE;
+  switch (media_type) {
+    case EmeMediaType::AUDIO:
+      if (!StartsWithASCII(container_mime_type, "audio/", true))
+        return false;
+      media_type_codec_mask = audio_codec_mask_;
+      break;
+    case EmeMediaType::VIDEO:
+      if (!StartsWithASCII(container_mime_type, "video/", true))
+        return false;
+      media_type_codec_mask = video_codec_mask_;
+      break;
+  }
+
+  // Look up the key system's supported codecs.
+  KeySystemInfoMap::const_iterator key_system_iter =
+      concrete_key_system_map_.find(key_system);
+  if (key_system_iter == concrete_key_system_map_.end()) {
+    NOTREACHED();
+    return false;
+  }
+  SupportedCodecs key_system_codec_mask =
+      key_system_iter->second.supported_codecs;
+
+  // Check that the container is supported by the key system. (This check is
+  // necessary because |codecs| may be empty.)
+  SupportedCodecs container_codec_mask =
+      GetCodecMaskForContainer(container_mime_type) & media_type_codec_mask;
+  if ((key_system_codec_mask & container_codec_mask) == 0)
+    return false;
+
+  // Check that the codecs are supported by the key system and container.
+  for (size_t i = 0; i < codecs.size(); i++) {
+    SupportedCodecs codec = GetCodecForString(codecs[i]);
+    if ((codec & key_system_codec_mask & container_codec_mask) == 0)
+      return false;
+  }
+
+  return true;
+}
+
+EmeConfigRule KeySystemsImpl::GetRobustnessConfigRule(
+    const std::string& key_system,
+    EmeMediaType media_type,
+    const std::string& requested_robustness) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  EmeRobustness robustness = ConvertRobustness(requested_robustness);
+  if (robustness == EmeRobustness::INVALID)
+    return EmeConfigRule::NOT_SUPPORTED;
+  if (robustness == EmeRobustness::EMPTY)
+    return EmeConfigRule::SUPPORTED;
+
+  KeySystemInfoMap::const_iterator key_system_iter =
+      concrete_key_system_map_.find(key_system);
+  if (key_system_iter == concrete_key_system_map_.end()) {
+    NOTREACHED();
+    return EmeConfigRule::NOT_SUPPORTED;
+  }
+
+  EmeRobustness max_robustness = EmeRobustness::INVALID;
+  switch (media_type) {
+    case EmeMediaType::AUDIO:
+      max_robustness = key_system_iter->second.max_audio_robustness;
+      break;
+    case EmeMediaType::VIDEO:
+      max_robustness = key_system_iter->second.max_video_robustness;
+      break;
+  }
+
+  // We can compare robustness levels whenever they are not HW_SECURE_CRYPTO
+  // and SW_SECURE_DECODE in some order. If they are exactly those two then the
+  // robustness requirement is not supported.
+  if ((max_robustness == EmeRobustness::HW_SECURE_CRYPTO &&
+       robustness == EmeRobustness::SW_SECURE_DECODE) ||
+      (max_robustness == EmeRobustness::SW_SECURE_DECODE &&
+       robustness == EmeRobustness::HW_SECURE_CRYPTO) ||
+      robustness > max_robustness) {
+    return EmeConfigRule::NOT_SUPPORTED;
+  }
+
+#if defined(OS_CHROMEOS)
+  if (key_system == kWidevineKeySystem) {
+    // Hardware security requires remote attestation.
+    if (robustness >= EmeRobustness::HW_SECURE_CRYPTO)
+      return EmeConfigRule::IDENTIFIER_REQUIRED;
+
+    // For video, recommend remote attestation if HW_SECURE_ALL is available,
+    // because it enables hardware accelerated decoding.
+    // TODO(sandersd): Only do this when hardware accelerated decoding is
+    // available for the requested codecs.
+    if (media_type == EmeMediaType::VIDEO &&
+        max_robustness == EmeRobustness::HW_SECURE_ALL) {
+      return EmeConfigRule::IDENTIFIER_RECOMMENDED;
+    }
+  }
+#endif  // defined(OS_CHROMEOS)
+
+  return EmeConfigRule::SUPPORTED;
+}
+
+EmeConfigRule KeySystemsImpl::GetPersistentLicenseSessionConfigRule(
+    const std::string& key_system) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  KeySystemInfoMap::const_iterator key_system_iter =
+      concrete_key_system_map_.find(key_system);
+  if (key_system_iter == concrete_key_system_map_.end()) {
+    NOTREACHED();
+    return EmeConfigRule::NOT_SUPPORTED;
+  }
+  return ConvertSessionTypeSupport(
+      key_system_iter->second.persistent_license_support);
+}
+
+EmeConfigRule KeySystemsImpl::GetPersistentReleaseMessageSessionConfigRule(
+    const std::string& key_system) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  KeySystemInfoMap::const_iterator key_system_iter =
+      concrete_key_system_map_.find(key_system);
+  if (key_system_iter == concrete_key_system_map_.end()) {
+    NOTREACHED();
+    return EmeConfigRule::NOT_SUPPORTED;
+  }
+  return ConvertSessionTypeSupport(
+      key_system_iter->second.persistent_release_message_support);
+}
+
+EmeConfigRule KeySystemsImpl::GetPersistentStateConfigRule(
+    const std::string& key_system,
+    EmeFeatureRequirement requirement) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  KeySystemInfoMap::const_iterator key_system_iter =
+      concrete_key_system_map_.find(key_system);
+  if (key_system_iter == concrete_key_system_map_.end()) {
+    NOTREACHED();
+    return EmeConfigRule::NOT_SUPPORTED;
+  }
+
+  // For NOT_ALLOWED and REQUIRED, the result is as expected. For OPTIONAL, we
+  // return the least restrictive of the two rules; this guarantees that if
+  // OPTIONAL is accepted, then it can always be resolved to some value. (In
+  // fact OPTIONAL is always accepted, because the least restrictive rule is
+  // always SUPPORTED.)
+  //
+  // Note that even if permission is not required for persistent state, it may
+  // be required for specific persistent session types.
+  //
+  //                              NOT_ALLOWED    OPTIONAL   REQUIRED
+  //               NOT_SUPPORTED  SUPPORTED      SUPPORTED  NOT_SUPPORTED
+  // REQUESTABLE_WITH_IDENTIFIER  SUPPORTED      SUPPORTED  IDENTIFIER_REQ
+  //                 REQUESTABLE  SUPPORTED      SUPPORTED  SUPPORTED
+  //              ALWAYS_ENABLED  NOT_SUPPORTED  SUPPORTED  SUPPORTED
+  EmeFeatureSupport support = key_system_iter->second.persistent_state_support;
+  if (support == EME_FEATURE_NOT_SUPPORTED &&
+      requirement == EME_FEATURE_REQUIRED) {
+    return EmeConfigRule::NOT_SUPPORTED;
+  }
+  if (support == EME_FEATURE_ALWAYS_ENABLED &&
+      requirement == EME_FEATURE_NOT_ALLOWED) {
+    return EmeConfigRule::NOT_SUPPORTED;
+  }
+  if (support == EME_FEATURE_REQUESTABLE_WITH_IDENTIFIER &&
+      requirement == EME_FEATURE_REQUIRED) {
+    return EmeConfigRule::IDENTIFIER_REQUIRED;
+  }
+  return EmeConfigRule::SUPPORTED;
+}
+
+EmeConfigRule KeySystemsImpl::GetDistinctiveIdentifierConfigRule(
+    const std::string& key_system,
+    EmeFeatureRequirement requirement) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  KeySystemInfoMap::const_iterator key_system_iter =
+      concrete_key_system_map_.find(key_system);
+  if (key_system_iter == concrete_key_system_map_.end()) {
+    NOTREACHED();
+    return EmeConfigRule::NOT_SUPPORTED;
+  }
+
+  // Permission is required for REQUIRED, but not for NOT_ALLOWED. For OPTIONAL,
+  // we return the least restrictive of the two rules; this guarantees that if
+  // OPTIONAL is accepted, then it can always be resolved to some value.
+  //
+  //                              NOT_ALLOWED    OPTIONAL        REQUIRED
+  //               NOT_SUPPORTED  SUPPORTED      SUPPORTED       NOT_SUPPORTED
+  // REQUESTABLE_WITH_IDENTIFIER  SUPPORTED      SUPPORTED       IDENTIFIER_REQ
+  //              ALWAYS_ENABLED  NOT_SUPPORTED  IDENTIFIER_REQ  IDENTIFIER_REQ
+  EmeFeatureSupport support =
+      key_system_iter->second.distinctive_identifier_support;
+  DCHECK(support != EME_FEATURE_REQUESTABLE);
+  if (support == EME_FEATURE_NOT_SUPPORTED &&
+      requirement == EME_FEATURE_REQUIRED) {
+    return EmeConfigRule::NOT_SUPPORTED;
+  }
+  if (support == EME_FEATURE_ALWAYS_ENABLED &&
+      requirement == EME_FEATURE_NOT_ALLOWED) {
+    return EmeConfigRule::NOT_SUPPORTED;
+  }
+  if (support == EME_FEATURE_ALWAYS_ENABLED ||
+      requirement == EME_FEATURE_REQUIRED) {
+    return EmeConfigRule::IDENTIFIER_REQUIRED;
+  }
+  return EmeConfigRule::SUPPORTED;
+}
+
+KeySystems& KeySystems::GetInstance() {
+  return KeySystemsImpl::GetInstance();
 }
 
 //------------------------------------------------------------------------------
@@ -781,24 +961,12 @@
   return key_system;
 }
 
-bool IsSaneInitDataTypeWithContainer(
-    const std::string& init_data_type,
-    const std::string& container) {
-  if (init_data_type == "cenc") {
-    return container == "audio/mp4" || container == "video/mp4";
-  } else if (init_data_type == "webm") {
-    return  container == "audio/webm" || container == "video/webm";
-  } else {
-    return true;
-  }
-}
-
 bool PrefixedIsSupportedConcreteKeySystem(const std::string& key_system) {
-  return KeySystems::GetInstance().IsConcreteSupportedKeySystem(key_system);
+  return KeySystemsImpl::GetInstance().IsConcreteSupportedKeySystem(key_system);
 }
 
 bool IsSupportedKeySystem(const std::string& key_system) {
-  if (!KeySystems::GetInstance().IsSupportedKeySystem(key_system))
+  if (!KeySystemsImpl::GetInstance().IsSupportedKeySystem(key_system))
     return false;
 
   // TODO(ddorwin): Move this to where we add key systems when prefixed EME is
@@ -816,70 +984,33 @@
 bool IsSupportedKeySystemWithInitDataType(
     const std::string& key_system,
     const std::string& init_data_type) {
-  return KeySystems::GetInstance().IsSupportedKeySystemWithInitDataType(
+  return KeySystemsImpl::GetInstance().IsSupportedKeySystemWithInitDataType(
       key_system, init_data_type);
 }
 
-bool IsSupportedKeySystemWithMediaMimeType(
-    const std::string& mime_type,
-    const std::vector<std::string>& codecs,
-    const std::string& key_system) {
-  return KeySystems::GetInstance().IsSupportedKeySystemWithMediaMimeType(
-      mime_type, codecs, key_system, false);
-}
-
 bool PrefixedIsSupportedKeySystemWithMediaMimeType(
     const std::string& mime_type,
     const std::vector<std::string>& codecs,
     const std::string& key_system) {
-  return KeySystems::GetInstance().IsSupportedKeySystemWithMediaMimeType(
-      mime_type, codecs, key_system, true);
+  return KeySystemsImpl::GetInstance()
+      .PrefixedIsSupportedKeySystemWithMediaMimeType(mime_type, codecs,
+                                                     key_system);
 }
 
 std::string GetKeySystemNameForUMA(const std::string& key_system) {
-  return KeySystems::GetInstance().GetKeySystemNameForUMA(key_system);
+  return KeySystemsImpl::GetInstance().GetKeySystemNameForUMA(key_system);
 }
 
 bool CanUseAesDecryptor(const std::string& concrete_key_system) {
-  return KeySystems::GetInstance().UseAesDecryptor(concrete_key_system);
+  return KeySystemsImpl::GetInstance().UseAesDecryptor(concrete_key_system);
 }
 
 #if defined(ENABLE_PEPPER_CDMS)
 std::string GetPepperType(const std::string& concrete_key_system) {
-  return KeySystems::GetInstance().GetPepperType(concrete_key_system);
+  return KeySystemsImpl::GetInstance().GetPepperType(concrete_key_system);
 }
 #endif
 
-bool IsPersistentLicenseSessionSupported(
-    const std::string& key_system,
-    bool is_permission_granted) {
-  return KeySystems::GetInstance().IsPersistentLicenseSessionSupported(
-      key_system, is_permission_granted);
-}
-
-bool IsPersistentReleaseMessageSessionSupported(
-    const std::string& key_system,
-    bool is_permission_granted) {
-  return KeySystems::GetInstance().IsPersistentReleaseMessageSessionSupported(
-      key_system, is_permission_granted);
-}
-
-bool IsPersistentStateRequirementSupported(
-    const std::string& key_system,
-    EmeFeatureRequirement requirement,
-    bool is_permission_granted) {
-  return KeySystems::GetInstance().IsPersistentStateRequirementSupported(
-      key_system, requirement, is_permission_granted);
-}
-
-bool IsDistinctiveIdentifierRequirementSupported(
-    const std::string& key_system,
-    EmeFeatureRequirement requirement,
-    bool is_permission_granted) {
-  return KeySystems::GetInstance().IsDistinctiveIdentifierRequirementSupported(
-      key_system, requirement, is_permission_granted);
-}
-
 // These two functions are for testing purpose only. The declaration in the
 // header file is guarded by "#if defined(UNIT_TEST)" so that they can be used
 // by tests but not non-test code. However, this .cc file is compiled as part of
@@ -887,11 +1018,14 @@
 // "MEDIA_EXPORT" here again so that they are visible to tests.
 
 MEDIA_EXPORT void AddContainerMask(const std::string& container, uint32 mask) {
-  KeySystems::GetInstance().AddContainerMask(container, mask);
+  KeySystemsImpl::GetInstance().AddContainerMask(container, mask);
 }
 
-MEDIA_EXPORT void AddCodecMask(const std::string& codec, uint32 mask) {
-  KeySystems::GetInstance().AddCodecMask(codec, mask);
+MEDIA_EXPORT void AddCodecMask(
+    EmeMediaType media_type,
+    const std::string& codec,
+    uint32 mask) {
+  KeySystemsImpl::GetInstance().AddCodecMask(media_type, codec, mask);
 }
 
 }  // namespace media
diff --git a/media/base/key_systems.h b/media/base/key_systems.h
index 93317a1..9a4e23313 100644
--- a/media/base/key_systems.h
+++ b/media/base/key_systems.h
@@ -14,6 +14,58 @@
 
 namespace media {
 
+// Provides an interface for querying registered key systems. The exposed API is
+// only intended to support unprefixed EME.
+//
+// Many of the original static methods are still available, they should be
+// migrated into this interface over time (or removed).
+//
+// TODO(sandersd): Provide GetKeySystem() so that it is not necessary to pass
+// |key_system| to every method. http://crbug.com/457438
+class MEDIA_EXPORT KeySystems {
+ public:
+  static KeySystems& GetInstance();
+
+  // Returns whether |key_system| is a supported key system.
+  virtual bool IsSupportedKeySystem(const std::string& key_system) const = 0;
+
+  // Returns whether the list of codecs are supported together by |key_system|.
+  // TODO(sandersd): Return a rule instead of a bool so that codec selection can
+  // affect other configuration options (namely robustness).
+  virtual bool IsSupportedCodecCombination(
+      const std::string& key_system,
+      EmeMediaType media_type,
+      const std::string& container_mime_type,
+      const std::vector<std::string>& codecs) const = 0;
+
+  // Returns the configuration rule for supporting a robustness requirement.
+  virtual EmeConfigRule GetRobustnessConfigRule(
+      const std::string& key_system,
+      EmeMediaType media_type,
+      const std::string& requested_robustness) const = 0;
+
+  // Returns the configuration rule for supporting persistent-license sessions.
+  virtual EmeConfigRule GetPersistentLicenseSessionConfigRule(
+      const std::string& key_system) const = 0;
+
+  // Returns the configuration rule for supporting persistent-release-message
+  // sessions.
+  virtual EmeConfigRule GetPersistentReleaseMessageSessionConfigRule(
+      const std::string& key_system) const = 0;
+
+  // Returns the configuration rule for supporting a persistent state
+  // requirement.
+  virtual EmeConfigRule GetPersistentStateConfigRule(
+      const std::string& key_system,
+      EmeFeatureRequirement requirement) const = 0;
+
+  // Returns the configuration rule for supporting a distinctive identifier
+  // requirement.
+  virtual EmeConfigRule GetDistinctiveIdentifierConfigRule(
+      const std::string& key_system,
+      EmeFeatureRequirement requirement) const = 0;
+};
+
 // Prefixed EME API only supports prefixed (webkit-) key system name for
 // certain key systems. But internally only unprefixed key systems are
 // supported. The following two functions help convert between prefixed and
@@ -27,20 +79,9 @@
 MEDIA_EXPORT std::string GetPrefixedKeySystemName(
     const std::string& key_system);
 
-// Returns false if a container-specific |init_data_type| is specified with an
-// inappropriate container.
-// TODO(sandersd): Remove this essentially internal detail if the spec is
-// updated to not convolve the two in a single method call.
-// TODO(sandersd): Use enum values instead of strings. http://crbug.com/417440
-MEDIA_EXPORT bool IsSaneInitDataTypeWithContainer(
-    const std::string& init_data_type,
-    const std::string& container);
-
 // Use for unprefixed EME only!
 // Returns whether |key_system| is a supported key system.
 // Note: Shouldn't be used for prefixed API as the original
-// IsSupportedKeySystemWithMediaMimeType() path reports UMAs, but this path does
-// not.
 MEDIA_EXPORT bool IsSupportedKeySystem(const std::string& key_system);
 
 MEDIA_EXPORT bool IsSupportedKeySystemWithInitDataType(
@@ -51,19 +92,9 @@
 // Returns whether |key_system| is a real supported key system that can be
 // instantiated.
 // Abstract parent |key_system| strings will return false.
-// Call IsSupportedKeySystemWithMediaMimeType() to determine whether a
-// |key_system| supports a specific type of media or to check parent key
-// systems.
 MEDIA_EXPORT bool PrefixedIsSupportedConcreteKeySystem(
     const std::string& key_system);
 
-// Use for unprefixed EME only!
-// Returns whether |key_system| supports the specified media type and codec(s).
-MEDIA_EXPORT bool IsSupportedKeySystemWithMediaMimeType(
-    const std::string& mime_type,
-    const std::vector<std::string>& codecs,
-    const std::string& key_system);
-
 // Use for prefixed EME only!
 // Returns whether |key_system| supports the specified media type and codec(s).
 // To be used with prefixed EME only as it generates UMAs based on the query.
@@ -85,32 +116,13 @@
     const std::string& concrete_key_system);
 #endif
 
-// Returns whether |key_system| supports persistent-license sessions.
-MEDIA_EXPORT bool IsPersistentLicenseSessionSupported(
-    const std::string& key_system,
-    bool is_permission_granted);
-
-// Returns whether |key_system| supports persistent-release-message sessions.
-MEDIA_EXPORT bool IsPersistentReleaseMessageSessionSupported(
-    const std::string& key_system,
-    bool is_permission_granted);
-
-// Returns whether |key_system| supports persistent state as requested.
-MEDIA_EXPORT bool IsPersistentStateRequirementSupported(
-    const std::string& key_system,
-    EmeFeatureRequirement requirement,
-    bool is_permission_granted);
-
-// Returns whether |key_system| supports distinctive identifiers as requested.
-MEDIA_EXPORT bool IsDistinctiveIdentifierRequirementSupported(
-    const std::string& key_system,
-    EmeFeatureRequirement requirement,
-    bool is_permission_granted);
-
 #if defined(UNIT_TEST)
 // Helper functions to add container/codec types for testing purposes.
 MEDIA_EXPORT void AddContainerMask(const std::string& container, uint32 mask);
-MEDIA_EXPORT void AddCodecMask(const std::string& codec, uint32 mask);
+MEDIA_EXPORT void AddCodecMask(
+    EmeMediaType media_type,
+    const std::string& codec,
+    uint32 mask);
 #endif  // defined(UNIT_TEST)
 
 }  // namespace media
diff --git a/media/base/key_systems_unittest.cc b/media/base/key_systems_unittest.cc
index bb60e8b..bb0227b 100644
--- a/media/base/key_systems_unittest.cc
+++ b/media/base/key_systems_unittest.cc
@@ -2,6 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO(sandersd): Refactor to remove recomputed codec arrays, and generally
+// shorten and improve coverage.
+//   - http://crbug.com/417444
+//   - http://crbug.com/457438
+// TODO(sandersd): Add tests to cover codec vectors with empty items.
+// http://crbug.com/417461
+
 #include <string>
 #include <vector>
 
@@ -64,6 +71,24 @@
 static_assert((TEST_CODEC_FOO_ALL & EME_CODEC_ALL) == EME_CODEC_NONE,
               "test codec masks should only use invalid codec masks");
 
+// Adapt IsSupportedKeySystemWithMediaMimeType() to the new API,
+// IsSupportedCodecCombination().
+static bool IsSupportedKeySystemWithMediaMimeType(
+    const std::string& mime_type,
+    const std::vector<std::string>& codecs,
+    const std::string& key_system) {
+  return KeySystems::GetInstance().IsSupportedCodecCombination(
+      key_system, EmeMediaType::VIDEO, mime_type, codecs);
+}
+
+static bool IsSupportedKeySystemWithAudioMimeType(
+    const std::string& mime_type,
+    const std::vector<std::string>& codecs,
+    const std::string& key_system) {
+  return KeySystems::GetInstance().IsSupportedCodecCombination(
+      key_system, EmeMediaType::AUDIO, mime_type, codecs);
+}
+
 // Adds test container and codec masks.
 // This function must be called after SetMediaClient() if a MediaClient will be
 // provided.
@@ -82,8 +107,8 @@
 
   AddContainerMask("audio/foo", TEST_CODEC_FOO_AUDIO_ALL);
   AddContainerMask("video/foo", TEST_CODEC_FOO_ALL);
-  AddCodecMask("fooaudio", TEST_CODEC_FOO_AUDIO);
-  AddCodecMask("foovideo", TEST_CODEC_FOO_VIDEO);
+  AddCodecMask(EmeMediaType::AUDIO, "fooaudio", TEST_CODEC_FOO_AUDIO);
+  AddCodecMask(EmeMediaType::VIDEO, "foovideo", TEST_CODEC_FOO_VIDEO);
 
   is_test_masks_added = true;
 }
@@ -165,7 +190,9 @@
   system.key_system = name;
   system.supported_codecs = EME_CODEC_WEBM_ALL;
   system.supported_codecs |= TEST_CODEC_FOO_ALL;
-  system.supported_init_data_types = EME_INIT_DATA_TYPE_WEBM;
+  system.supported_init_data_types = kInitDataTypeMaskWebM;
+  system.max_audio_robustness = EmeRobustness::EMPTY;
+  system.max_video_robustness = EmeRobustness::EMPTY;
   system.persistent_license_support = EME_SESSION_TYPE_NOT_SUPPORTED;
   system.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED;
   system.persistent_state_support = EME_FEATURE_NOT_SUPPORTED;
@@ -180,7 +207,9 @@
   ext.key_system = kExternal;
   ext.supported_codecs = EME_CODEC_WEBM_ALL;
   ext.supported_codecs |= TEST_CODEC_FOO_ALL;
-  ext.supported_init_data_types = EME_INIT_DATA_TYPE_WEBM;
+  ext.supported_init_data_types = kInitDataTypeMaskWebM;
+  ext.max_audio_robustness = EmeRobustness::EMPTY;
+  ext.max_video_robustness = EmeRobustness::EMPTY;
   ext.persistent_license_support = EME_SESSION_TYPE_SUPPORTED;
   ext.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED;
   ext.persistent_state_support = EME_FEATURE_ALWAYS_ENABLED;
@@ -222,7 +251,6 @@
   PotentiallySupportedNamesTestMediaClient test_media_client_;
 };
 
-// TODO(sandersd): Refactor. http://crbug.com/417444
 class KeySystemsTest : public testing::Test {
  protected:
   KeySystemsTest() {
@@ -334,8 +362,6 @@
 
 TEST_F(KeySystemsTest, EmptyKeySystem) {
   EXPECT_FALSE(IsSupportedKeySystem(std::string()));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), std::string()));
   EXPECT_EQ("Unknown", GetKeySystemNameForUMA(std::string()));
 }
 
@@ -349,8 +375,6 @@
 
   // Prefixed Clear Key is not supported internally.
   EXPECT_FALSE(IsSupportedKeySystem(kPrefixedClearKey));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), kPrefixedClearKey));
   EXPECT_EQ("Unknown", GetKeySystemNameForUMA(kPrefixedClearKey));
 }
 
@@ -368,8 +392,6 @@
   static const char* const kUnrecognized = "x-org.example.unrecognized";
 
   EXPECT_FALSE(IsSupportedKeySystem(kUnrecognized));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), kUnrecognized));
 
   EXPECT_EQ("Unknown", GetKeySystemNameForUMA(kUnrecognized));
 
@@ -413,14 +435,16 @@
   EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, vp80_codec(), kUsesAes));
   EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, vp8_and_vorbis_codecs(), kUsesAes));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, vp9_codec(), kUsesAes));
   EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, vp90_codec(), kUsesAes));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+
+  // Audio in a video container.
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+      kVideoWebM, vp8_and_vorbis_codecs(), kUsesAes));
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, vp9_and_vorbis_codecs(), kUsesAes));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, vorbis_codec(), kUsesAes));
 
   // Non-Webm codecs.
@@ -432,9 +456,9 @@
       kVideoWebM, mixed_codecs(), kUsesAes));
 
   // Valid audio types.
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_TRUE(IsSupportedKeySystemWithAudioMimeType(
       kAudioWebM, no_codecs(), kUsesAes));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_TRUE(IsSupportedKeySystemWithAudioMimeType(
       kAudioWebM, vorbis_codec(), kUsesAes));
 
   // Non-audio codecs.
@@ -448,15 +472,13 @@
       kAudioWebM, vp9_and_vorbis_codecs(), kUsesAes));
 
   // Non-Webm codec.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioWebM, fooaudio_codec(), kUsesAes));
 }
 
 // No parent is registered for UsesAes.
 TEST_F(KeySystemsTest, Parent_NoParentRegistered) {
   EXPECT_FALSE(IsSupportedKeySystem(kUsesAesParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), kUsesAesParent));
 
   // The parent is not supported for most things.
   EXPECT_EQ("Unknown", GetKeySystemNameForUMA(kUsesAesParent));
@@ -475,43 +497,27 @@
 TEST_F(KeySystemsTest, IsSupportedKeySystem_InvalidVariants) {
   // Case sensitive.
   EXPECT_FALSE(IsSupportedKeySystem("x-org.example.ClEaR"));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(kVideoWebM, no_codecs(),
-                                                     "x-org.example.ClEaR"));
 
   // TLDs are not allowed.
   EXPECT_FALSE(IsSupportedKeySystem("org."));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), "org."));
   EXPECT_FALSE(IsSupportedKeySystem("com"));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), "com"));
 
   // Extra period.
   EXPECT_FALSE(IsSupportedKeySystem("x-org.example.clear."));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(kVideoWebM, no_codecs(),
-                                                     "x-org.example.clear."));
   EXPECT_FALSE(IsSupportedKeySystem("x-org.example."));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(kVideoWebM, no_codecs(),
-                                                     "x-org.example."));
 
   // Incomplete.
   EXPECT_FALSE(IsSupportedKeySystem("x-org.example.clea"));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(kVideoWebM, no_codecs(),
-                                                     "x-org.example.clea"));
 
   // Extra character.
   EXPECT_FALSE(IsSupportedKeySystem("x-org.example.clearz"));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(kVideoWebM, no_codecs(),
-                                                     "x-org.example.clearz"));
 
   // There are no child key systems for UsesAes.
   EXPECT_FALSE(IsSupportedKeySystem("x-org.example.clear.foo"));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), "x-org.example.clear.foo"));
 }
 
 TEST_F(KeySystemsTest, IsSupportedKeySystemWithMediaMimeType_NoType) {
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       std::string(), no_codecs(), kUsesAes));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       std::string(), no_codecs(), kUsesAesParent));
@@ -529,14 +535,13 @@
   // Valid video types.
   EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
       kVideoFoo, no_codecs(), kUsesAes));
-  // The parent should be supported but is not. See http://crbug.com/164303.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoFoo, no_codecs(), kUsesAesParent));
   EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
       kVideoFoo, foovideo_codec(), kUsesAes));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+
+  // Audio in a video container.
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       kVideoFoo, foovideo_and_fooaudio_codecs(), kUsesAes));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       kVideoFoo, fooaudio_codec(), kUsesAes));
 
   // Extended codecs fail because this is handled by SimpleWebMimeRegistryImpl.
@@ -557,19 +562,19 @@
       kVideoFoo, mixed_codecs(), kUsesAes));
 
   // Valid audio types.
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_TRUE(IsSupportedKeySystemWithAudioMimeType(
       kAudioFoo, no_codecs(), kUsesAes));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_TRUE(IsSupportedKeySystemWithAudioMimeType(
       kAudioFoo, fooaudio_codec(), kUsesAes));
 
   // Non-audio codecs.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioFoo, foovideo_codec(), kUsesAes));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioFoo, foovideo_and_fooaudio_codecs(), kUsesAes));
 
   // Non-container2 codec.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioFoo, vorbis_codec(), kUsesAes));
 }
 
@@ -589,10 +594,8 @@
 }
 
 TEST_F(KeySystemsTest, Parent_ParentRegistered) {
-  // The parent system is not a concrete system but is supported.
+  // Unprefixed has no parent key system support.
   EXPECT_FALSE(IsSupportedKeySystem(kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), kExternalParent));
   EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, no_codecs(), kExternalParent));
 
@@ -621,34 +624,19 @@
   EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, vp80_codec(), kExternal));
   EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, vp8_and_vorbis_codecs(), kExternal));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, vp9_codec(), kExternal));
   EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, vp90_codec(), kExternal));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+
+  // Audio in a video container.
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+      kVideoWebM, vp8_and_vorbis_codecs(), kExternal));
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, vp9_and_vorbis_codecs(), kExternal));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, vorbis_codec(), kExternal));
 
   // Valid video types - parent key system.
-  // Unprefixed has no parent key system support.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, vp8_codec(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, vp80_codec(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, vp8_and_vorbis_codecs(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, vp9_codec(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, vp90_codec(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, vp9_and_vorbis_codecs(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, vorbis_codec(), kExternalParent));
   // Prefixed has parent key system support.
   EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, no_codecs(), kExternalParent));
@@ -676,17 +664,12 @@
       kVideoWebM, mixed_codecs(), kExternal));
 
   // Valid audio types.
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_TRUE(IsSupportedKeySystemWithAudioMimeType(
       kAudioWebM, no_codecs(), kExternal));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_TRUE(IsSupportedKeySystemWithAudioMimeType(
       kAudioWebM, vorbis_codec(), kExternal));
 
   // Valid audio types - parent key system.
-  // Unprefixed has no parent key system support.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kAudioWebM, no_codecs(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kAudioWebM, vorbis_codec(), kExternalParent));
   // Prefixed has parent key system support.
   EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
       kAudioWebM, no_codecs(), kExternalParent));
@@ -694,17 +677,17 @@
       kAudioWebM, vorbis_codec(), kExternalParent));
 
   // Non-audio codecs.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioWebM, vp8_codec(), kExternal));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioWebM, vp8_and_vorbis_codecs(), kExternal));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioWebM, vp9_codec(), kExternal));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioWebM, vp9_and_vorbis_codecs(), kExternal));
 
   // Non-Webm codec.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioWebM, fooaudio_codec(), kExternal));
 }
 
@@ -716,21 +699,14 @@
       kVideoFoo, no_codecs(), kExternal));
   EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
       kVideoFoo, foovideo_codec(), kExternal));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+
+  // Audio in a video container.
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       kVideoFoo, foovideo_and_fooaudio_codecs(), kExternal));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       kVideoFoo, fooaudio_codec(), kExternal));
 
   // Valid video types - parent key system.
-  // Unprefixed has no parent key system support.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoFoo, no_codecs(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoFoo, foovideo_codec(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoFoo, foovideo_and_fooaudio_codecs(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoFoo, fooaudio_codec(), kExternalParent));
   // Prefixed has parent key system support.
   EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
       kVideoFoo, no_codecs(), kExternalParent));
@@ -759,17 +735,12 @@
       kVideoFoo, mixed_codecs(), kExternal));
 
   // Valid audio types.
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_TRUE(IsSupportedKeySystemWithAudioMimeType(
       kAudioFoo, no_codecs(), kExternal));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_TRUE(IsSupportedKeySystemWithAudioMimeType(
       kAudioFoo, fooaudio_codec(), kExternal));
 
   // Valid audio types - parent key system.
-  // Unprefixed has no parent key system support.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kAudioFoo, no_codecs(), kExternalParent));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kAudioFoo, fooaudio_codec(), kExternalParent));
   // Prefixed has parent key system support.
   EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
       kAudioFoo, no_codecs(), kExternalParent));
@@ -777,13 +748,13 @@
       kAudioFoo, fooaudio_codec(), kExternalParent));
 
   // Non-audio codecs.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioFoo, foovideo_codec(), kExternal));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioFoo, foovideo_and_fooaudio_codecs(), kExternal));
 
   // Non-container2 codec.
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
       kAudioFoo, vorbis_codec(), kExternal));
 }
 
@@ -810,8 +781,6 @@
   EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
       kVideoWebM, no_codecs(), kUsesAes));
   EXPECT_FALSE(IsSupportedKeySystem(kExternal));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), kExternal));
 }
 
 TEST_F(KeySystemsTest, PrefixedKeySystemsUpdate) {
diff --git a/media/base/media_log.cc b/media/base/media_log.cc
index a5188f4..5cf2e83 100644
--- a/media/base/media_log.cc
+++ b/media/base/media_log.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/atomic_sequence_num.h"
+#include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/values.h"
 
@@ -98,6 +99,22 @@
   return NULL;
 }
 
+std::string MediaLog::MediaEventToLogString(const MediaLogEvent& event) {
+  // Special case for PIPELINE_ERROR, since that's by far the most useful
+  // event for figuring out media pipeline failures, and just reporting
+  // pipeline status as numeric code is not very helpful/user-friendly.
+  int error_code = 0;
+  if (event.type == MediaLogEvent::PIPELINE_ERROR &&
+      event.params.GetInteger("pipeline_error", &error_code)) {
+    PipelineStatus status = static_cast<PipelineStatus>(error_code);
+    return EventTypeToString(event.type) + " " +
+        media::MediaLog::PipelineStatusToString(status);
+  }
+  std::string params_json;
+  base::JSONWriter::Write(&event.params, &params_json);
+  return EventTypeToString(event.type) + " " + params_json;
+}
+
 LogHelper::LogHelper(const LogCB& log_cb) : log_cb_(log_cb) {}
 
 LogHelper::~LogHelper() {
diff --git a/media/base/media_log.h b/media/base/media_log.h
index abeb4205..e388c9b0 100644
--- a/media/base/media_log.h
+++ b/media/base/media_log.h
@@ -48,6 +48,8 @@
   static std::string EventTypeToString(MediaLogEvent::Type type);
   static std::string PipelineStatusToString(PipelineStatus status);
 
+  static std::string MediaEventToLogString(const MediaLogEvent& event);
+
   MediaLog();
 
   // Add an event to this log. Overriden by inheritors to actually do something
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 9731fd0f..c4249c5 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -130,7 +130,7 @@
                      const PaintCB& paint_cb,
                      const base::Closure& ended_cb,
                      const PipelineStatusCB& error_cb,
-                     const TimeDeltaCB& get_time_cb,
+                     const WallClockTimeCB& wall_clock_time_cb,
                      const base::Closure& waiting_for_decryption_key_cb));
   MOCK_METHOD1(Flush, void(const base::Closure& callback));
   MOCK_METHOD1(StartPlayingFrom, void(base::TimeDelta));
@@ -204,7 +204,7 @@
   MOCK_METHOD1(SetPlaybackRate, void(float));
   MOCK_METHOD1(SetMediaTime, void(base::TimeDelta));
   MOCK_METHOD0(CurrentMediaTime, base::TimeDelta());
-  MOCK_METHOD0(CurrentMediaTimeForSyncingVideo, base::TimeDelta());
+  MOCK_METHOD1(GetWallClockTime, base::TimeTicks(base::TimeDelta));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockTimeSource);
diff --git a/media/base/time_source.h b/media/base/time_source.h
index 8ad364b..71fe858 100644
--- a/media/base/time_source.h
+++ b/media/base/time_source.h
@@ -33,24 +33,24 @@
   // time source is not ticking.
   virtual void SetMediaTime(base::TimeDelta time) = 0;
 
-  // Returns the current media time.
+  // Returns the current media timestamp relative to the timestamp set by
+  // SetMediaTime().
   //
   // Values returned are intended for informational purposes, such as displaying
   // UI with the current minute and second count. While it is guaranteed values
   // will never go backwards, the frequency at which they update may be low.
   virtual base::TimeDelta CurrentMediaTime() = 0;
 
-  // Returns the current media time for use with synchronizing video.
+  // Converts a media timestamp into a wall clock time. If the media time is
+  // stopped, returns a null TimeTicks.
   //
-  // Differences from CurrentMediaTime():
-  //   - Values returned update at a much higher frequency (e.g., suitable for
-  //     playback of 60 FPS content).
-  //   - As a result, values may go slightly backwards depending on the
-  //     implementation (e.g., uses interpolation).
+  // |media_time| values too far ahead of the current media time will return an
+  // estimated value; as such, these values may go backwards in time slightly.
   //
-  // TODO(scherkus): Replace with a method that returns wall clock time for a
-  // given media time for use with VideoFrameScheduler http://crbug.com/110814
-  virtual base::TimeDelta CurrentMediaTimeForSyncingVideo() = 0;
+  // |media_time| values behind the current media time may be significantly
+  // incorrect if the playback rate has changed recently. The only guarantee is
+  // that the returned time will be less than the current wall clock time.
+  virtual base::TimeTicks GetWallClockTime(base::TimeDelta media_time) = 0;
 };
 
 }  // namespace media
diff --git a/media/base/video_renderer.h b/media/base/video_renderer.h
index 7a764b3..360e632 100644
--- a/media/base/video_renderer.h
+++ b/media/base/video_renderer.h
@@ -24,8 +24,8 @@
   // Used to paint VideoFrame.
   typedef base::Callback<void(const scoped_refptr<VideoFrame>&)> PaintCB;
 
-  // Used to query the current time or duration of the media.
-  typedef base::Callback<base::TimeDelta()> TimeDeltaCB;
+  // Used to convert a media timestamp into a wall clock timestamp.
+  typedef base::Callback<base::TimeTicks(base::TimeDelta)> WallClockTimeCB;
 
   VideoRenderer();
 
@@ -52,7 +52,8 @@
   //
   // |error_cb| is executed if an error was encountered after initialization.
   //
-  // |get_time_cb| is used to query the current media playback time.
+  // |wall_clock_time_cb| is used to convert media timestamps into wallclock
+  // timestamps.
   //
   // |waiting_for_decryption_key_cb| is executed whenever the key needed to
   // decrypt the stream is not available.
@@ -65,7 +66,7 @@
       const PaintCB& paint_cb,
       const base::Closure& ended_cb,
       const PipelineStatusCB& error_cb,
-      const TimeDeltaCB& get_time_cb,
+      const WallClockTimeCB& wall_clock_time_cb,
       const base::Closure& waiting_for_decryption_key_cb) = 0;
 
   // Discards any video data and stops reading from |stream|, executing
diff --git a/media/base/wall_clock_time_source.cc b/media/base/wall_clock_time_source.cc
index 408de93e..d8325464 100644
--- a/media/base/wall_clock_time_source.cc
+++ b/media/base/wall_clock_time_source.cc
@@ -60,8 +60,15 @@
   return CurrentMediaTime_Locked();
 }
 
-base::TimeDelta WallClockTimeSource::CurrentMediaTimeForSyncingVideo() {
-  return CurrentMediaTime();
+base::TimeTicks WallClockTimeSource::GetWallClockTime(base::TimeDelta time) {
+  base::AutoLock auto_lock(lock_);
+  if (!ticking_ || playback_rate_ == 0.0)
+    return base::TimeTicks();
+
+  // See notes about |time| values less than |base_time_| in TimeSource header.
+  return reference_wall_ticks_ +
+         base::TimeDelta::FromMicroseconds(
+             (time - base_time_).InMicroseconds() * playback_rate_);
 }
 
 void WallClockTimeSource::SetTickClockForTesting(
@@ -71,7 +78,7 @@
 
 base::TimeDelta WallClockTimeSource::CurrentMediaTime_Locked() {
   lock_.AssertAcquired();
-  if (!ticking_)
+  if (!ticking_ || playback_rate_ == 0.0)
     return base_time_;
 
   base::TimeTicks now = tick_clock_->NowTicks();
diff --git a/media/base/wall_clock_time_source.h b/media/base/wall_clock_time_source.h
index c1aca3f..f2cf716 100644
--- a/media/base/wall_clock_time_source.h
+++ b/media/base/wall_clock_time_source.h
@@ -28,7 +28,7 @@
   void SetPlaybackRate(float playback_rate) override;
   void SetMediaTime(base::TimeDelta time) override;
   base::TimeDelta CurrentMediaTime() override;
-  base::TimeDelta CurrentMediaTimeForSyncingVideo() override;
+  base::TimeTicks GetWallClockTime(base::TimeDelta time) override;
 
   void SetTickClockForTesting(scoped_ptr<base::TickClock> tick_clock);
 
diff --git a/media/blink/webencryptedmediaclient_impl.cc b/media/blink/webencryptedmediaclient_impl.cc
index e770c4ec..7b337497 100644
--- a/media/blink/webencryptedmediaclient_impl.cc
+++ b/media/blink/webencryptedmediaclient_impl.cc
@@ -31,86 +31,210 @@
   CONFIGURATION_SUPPORTED,
 };
 
-static bool IsSupportedContentType(const std::string& key_system,
-                                   const std::string& mime_type,
-                                   const std::string& codecs) {
+// Accumulates configuration rules to determine if a feature (additional
+// configuration rule) can be added to an accumulated configuration.
+class ConfigState {
+ public:
+  ConfigState(bool was_permission_requested, bool is_permission_granted)
+      : was_permission_requested_(was_permission_requested),
+        is_permission_granted_(is_permission_granted),
+        is_identifier_required_(false),
+        is_identifier_recommended_(false){
+  }
+
+  bool IsPermissionGranted() const {
+    return is_permission_granted_;
+  }
+
+  // Permission is possible if it has not been denied.
+  bool IsPermissionPossible() const {
+    return is_permission_granted_ || !was_permission_requested_;
+  }
+
+  bool IsIdentifierRequired() const {
+    return is_identifier_required_;
+  }
+
+  bool IsIdentifierRecommended() const {
+    return is_identifier_recommended_;
+  }
+
+  // Checks whether a rule is compatible with all previously added rules.
+  bool IsRuleSupported(EmeConfigRule rule) const {
+    switch (rule) {
+      case EmeConfigRule::NOT_SUPPORTED:
+        return false;
+      case EmeConfigRule::IDENTIFIER_REQUIRED:
+        return IsPermissionPossible();
+      case EmeConfigRule::IDENTIFIER_RECOMMENDED:
+        return true;
+      case EmeConfigRule::SUPPORTED:
+        return true;
+    }
+    NOTREACHED();
+    return false;
+  }
+
+  // Checks whether a rule is compatible with all previously added rules, and
+  // can be accepted without needing to add it to the configuration state. This
+  // allows considering more rules after the configuration state is final (that
+  // is, after distinctiveIdentifier has been resolved).
+  bool IsRuleSupportedWithCurrentState(EmeConfigRule rule) const {
+    switch (rule) {
+      case EmeConfigRule::NOT_SUPPORTED:
+        return false;
+      case EmeConfigRule::IDENTIFIER_REQUIRED:
+        return is_permission_granted_;
+      case EmeConfigRule::IDENTIFIER_RECOMMENDED:
+        return true;
+      case EmeConfigRule::SUPPORTED:
+        return true;
+    }
+    NOTREACHED();
+    return false;
+  }
+
+  // Add a rule to the accumulated configuration state.
+  void AddRule(EmeConfigRule rule) {
+    switch (rule) {
+      case EmeConfigRule::NOT_SUPPORTED:
+        return;
+      case EmeConfigRule::IDENTIFIER_REQUIRED:
+        is_identifier_required_ = true;
+        return;
+      case EmeConfigRule::IDENTIFIER_RECOMMENDED:
+        is_identifier_recommended_ = true;
+        return;
+      case EmeConfigRule::SUPPORTED:
+        return;
+    }
+    NOTREACHED();
+  }
+
+ private:
+  // Whether permission to use a distinctive identifier was requested. If set,
+  // |is_permission_granted_| represents the final decision.
+  const bool was_permission_requested_;
+
+  // Whether permission to use a distinctive identifier has been granted.
+  const bool is_permission_granted_;
+
+  // Whether a rule has been added that requires a distinctive identifier.
+  bool is_identifier_required_;
+
+  // Whether a rule has been added that recommends a distinctive identifier.
+  bool is_identifier_recommended_;
+};
+
+static bool IsSupportedContentType(
+    const KeySystems& key_systems,
+    const std::string& key_system,
+    EmeMediaType media_type,
+    const std::string& container_mime_type,
+    const std::string& codecs) {
   // TODO(sandersd): Move contentType parsing from Blink to here so that invalid
   // parameters can be rejected. http://crbug.com/417561
-  // TODO(sandersd): Pass in the media type (audio or video) and check that the
-  // container type matches. http://crbug.com/457384
-  std::string container = base::StringToLowerASCII(mime_type);
+  std::string container_lower = base::StringToLowerASCII(container_mime_type);
 
-  // Check that |codecs| are supported by the CDM. This check does not handle
-  // extended codecs, so extended codec information is stripped.
-  // TODO(sandersd): Reject codecs that do not match the media type.
-  // http://crbug.com/457386
+  // Check that |container_mime_type| and |codecs| are supported by the CDM.
+  // This check does not handle extended codecs, so extended codec information
+  // is stripped.
   std::vector<std::string> codec_vector;
   net::ParseCodecString(codecs, &codec_vector, true);
-  if (!IsSupportedKeySystemWithMediaMimeType(container, codec_vector,
-                                             key_system)) {
+  if (!key_systems.IsSupportedCodecCombination(
+           key_system, media_type, container_lower, codec_vector)) {
+    return false;
+  }
+
+  // Check that |container_mime_type| is supported by Chrome. This can only
+  // happen if the CDM declares support for a container that Chrome does not.
+  if (!net::IsSupportedMediaMimeType(container_lower)) {
+    NOTREACHED();
     return false;
   }
 
   // Check that |codecs| are supported by Chrome. This is done primarily to
   // validate extended codecs, but it also ensures that the CDM cannot support
-  // codecs that Chrome does not (which would be bad because it would require
-  // considering the accumulated configuration, and could affect whether secure
-  // decode is required).
-  // TODO(sandersd): Reject ambiguous codecs. http://crbug.com/374751
+  // codecs that Chrome does not (which could complicate the robustness
+  // algorithm).
+  if (codec_vector.empty())
+    return true;
   codec_vector.clear();
   net::ParseCodecString(codecs, &codec_vector, false);
-  return net::AreSupportedMediaCodecs(codec_vector);
+  return (net::IsSupportedStrictMediaMimeType(container_lower, codec_vector) ==
+          net::IsSupported);
 }
 
 static bool GetSupportedCapabilities(
+    const KeySystems& key_systems,
     const std::string& key_system,
+    EmeMediaType media_type,
     const blink::WebVector<blink::WebMediaKeySystemMediaCapability>&
-        capabilities,
+        requested_media_capabilities,
+    ConfigState* config_state,
     std::vector<blink::WebMediaKeySystemMediaCapability>*
-        media_type_capabilities) {
+        supported_media_capabilities) {
   // From
   // https://w3c.github.io/encrypted-media/#get-supported-capabilities-for-media-type
-  // 1. Let accumulated capabilities be partial configuration.
-  //    (Skipped as there are no configuration-based codec restrictions.)
-  // 2. Let media type capabilities be empty.
-  DCHECK_EQ(media_type_capabilities->size(), 0ul);
-  // 3. For each value in capabilities:
-  for (size_t i = 0; i < capabilities.size(); i++) {
+  // 1. Let local accumulated capabilities be a local copy of partial
+  //    configuration.
+  //    (Skipped as we directly update |config_state|. This is safe because we
+  //    only do so when at least one requested media capability is supported.)
+  // 2. Let supported media capabilities be empty.
+  DCHECK_EQ(supported_media_capabilities->size(), 0ul);
+  // 3. For each value in requested media capabilities:
+  for (size_t i = 0; i < requested_media_capabilities.size(); i++) {
     // 3.1. Let contentType be the value's contentType member.
     // 3.2. Let robustness be the value's robustness member.
-    const blink::WebMediaKeySystemMediaCapability& capability = capabilities[i];
+    const blink::WebMediaKeySystemMediaCapability& capability =
+        requested_media_capabilities[i];
     // 3.3. If contentType is the empty string, return null.
-    if (capability.mimeType.isEmpty())
+    if (capability.mimeType.isEmpty()) {
+      DVLOG(2) << "Rejecting requested configuration because "
+               << "a capability contentType was empty.";
       return false;
+    }
     // 3.4-3.11. (Implemented by IsSupportedContentType().)
     if (!base::IsStringASCII(capability.mimeType) ||
         !base::IsStringASCII(capability.codecs) ||
-        !IsSupportedContentType(key_system,
+        !IsSupportedContentType(key_systems, key_system, media_type,
                                 base::UTF16ToASCII(capability.mimeType),
                                 base::UTF16ToASCII(capability.codecs))) {
       continue;
     }
     // 3.12. If robustness is not the empty string, run the following steps:
-    //       (Robustness is not supported.)
-    // TODO(sandersd): Implement robustness. http://crbug.com/442586
     if (!capability.robustness.isEmpty()) {
-      LOG(WARNING) << "Configuration rejected because rubustness strings are "
-                   << "not yet supported.";
-      continue;
+      // 3.12.1. If robustness is an unrecognized value or not supported by
+      //         implementation, continue to the next iteration. String
+      //         comparison is case-sensitive.
+      if (!base::IsStringASCII(capability.robustness))
+        continue;
+      EmeConfigRule robustness_rule = key_systems.GetRobustnessConfigRule(
+          key_system, media_type, base::UTF16ToASCII(capability.robustness));
+      if (!config_state->IsRuleSupported(robustness_rule))
+        continue;
+      config_state->AddRule(robustness_rule);
+      // 3.12.2. Add robustness to configuration.
+      //         (It's already added, we use capability as configuration.)
     }
     // 3.13. If the user agent and implementation do not support playback of
     //       encrypted media data as specified by configuration, including all
-    //       media types, in combination with accumulated capabilities,
+    //       media types, in combination with local accumulated capabilities,
     //       continue to the next iteration.
-    //       (Skipped as there are no configuration-based codec restrictions.)
-    // 3.14. Add configuration to media type capabilities.
-    media_type_capabilities->push_back(capability);
-    // 3.15. Add configuration to accumulated capabilities.
-    //       (Skipped as there are no configuration-based codec restrictions.)
+    //       (This is handled when adding rules to |config_state|.)
+    // 3.14. Add configuration to supported media capabilities.
+    supported_media_capabilities->push_back(capability);
+    // 3.15. Add configuration to local accumulated capabilities.
+    //       (Skipped as we directly update |config_state|.)
   }
-  // 4. If media type capabilities is empty, return null.
+  // 4. If supported media capabilities is empty, return null.
+  if (supported_media_capabilities->empty()) {
+    DVLOG(2) << "Rejecting requested configuration because "
+             << "no capabilities were supported.";
+    return false;
+  }
   // 5. Return media type capabilities.
-  return !media_type_capabilities->empty();
+  return true;
 }
 
 static EmeFeatureRequirement ConvertRequirement(
@@ -129,17 +253,13 @@
 }
 
 static ConfigurationSupport GetSupportedConfiguration(
+    const KeySystems& key_systems,
     const std::string& key_system,
     const blink::WebMediaKeySystemConfiguration& candidate,
-    blink::WebMediaKeySystemConfiguration* accumulated_configuration,
     bool was_permission_requested,
-    bool is_permission_granted) {
-  DCHECK(was_permission_requested || !is_permission_granted);
-
-  // It is possible to obtain user permission unless permission was already
-  // requested and denied.
-  bool is_permission_possible =
-      !was_permission_requested || is_permission_granted;
+    bool is_permission_granted,
+    blink::WebMediaKeySystemConfiguration* accumulated_configuration) {
+  ConfigState config_state(was_permission_requested, is_permission_granted);
 
   // From https://w3c.github.io/encrypted-media/#get-supported-configuration
   // 1. Let accumulated configuration be empty. (Done by caller.)
@@ -184,8 +304,11 @@
     }
 
     // 2.3. If supported types is empty, return null.
-    if (supported_types.empty())
+    if (supported_types.empty()) {
+      DVLOG(2) << "Rejecting requested configuration because "
+               << "no initDataType values were supported.";
       return CONFIGURATION_NOT_SUPPORTED;
+    }
 
     // 2.4. Add supported types to accumulated configuration.
     accumulated_configuration->initDataTypes = supported_types;
@@ -200,12 +323,16 @@
   //     - "not-allowed": If the implementation requires a Distinctive
   //       Identifier in combination with accumulated configuration, return
   //       null.
-  EmeFeatureRequirement di_requirement =
-      ConvertRequirement(candidate.distinctiveIdentifier);
-  if (!IsDistinctiveIdentifierRequirementSupported(key_system, di_requirement,
-                                                   is_permission_possible)) {
+  // We also reject OPTIONAL when distinctive identifiers are ALWAYS_ENABLED and
+  // permission has already been denied. This would happen anyway at step 11.
+  EmeConfigRule di_rule = key_systems.GetDistinctiveIdentifierConfigRule(
+      key_system, ConvertRequirement(candidate.distinctiveIdentifier));
+  if (!config_state.IsRuleSupported(di_rule)) {
+    DVLOG(2) << "Rejecting requested configuration because "
+             << "the distinctiveIdentifier requirement was not supported.";
     return CONFIGURATION_NOT_SUPPORTED;
   }
+  config_state.AddRule(di_rule);
 
   // 4. Add the value of the candidate configuration's distinctiveIdentifier
   //    attribute to accumulated configuration.
@@ -219,12 +346,14 @@
   //     - "optional": Continue.
   //     - "not-allowed": If the implementation requires persisting state in
   //       combination with accumulated configuration, return null.
-  EmeFeatureRequirement ps_requirement =
-      ConvertRequirement(candidate.persistentState);
-  if (!IsPersistentStateRequirementSupported(key_system, ps_requirement,
-                                             is_permission_possible)) {
+  EmeConfigRule ps_rule = key_systems.GetPersistentStateConfigRule(
+      key_system, ConvertRequirement(candidate.persistentState));
+  if (!config_state.IsRuleSupported(ps_rule)) {
+    DVLOG(2) << "Rejecting requested configuration because "
+             << "the persistentState requirement was not supported.";
     return CONFIGURATION_NOT_SUPPORTED;
   }
+  config_state.AddRule(ps_rule);
 
   // 6. Add the value of the candidate configuration's persistentState
   //    attribute to accumulated configuration.
@@ -239,8 +368,9 @@
     //      configuration.
     // 7.2. If video capabilities is null, return null.
     std::vector<blink::WebMediaKeySystemMediaCapability> video_capabilities;
-    if (!GetSupportedCapabilities(key_system, candidate.videoCapabilities,
-                                  &video_capabilities)) {
+    if (!GetSupportedCapabilities(key_systems, key_system, EmeMediaType::VIDEO,
+                                  candidate.videoCapabilities,
+                                  &config_state, &video_capabilities)) {
       return CONFIGURATION_NOT_SUPPORTED;
     }
 
@@ -257,8 +387,9 @@
     //      configuration.
     // 8.2. If audio capabilities is null, return null.
     std::vector<blink::WebMediaKeySystemMediaCapability> audio_capabilities;
-    if (!GetSupportedCapabilities(key_system, candidate.audioCapabilities,
-                                  &audio_capabilities)) {
+    if (!GetSupportedCapabilities(key_systems, key_system, EmeMediaType::AUDIO,
+                                  candidate.audioCapabilities,
+                                  &config_state, &audio_capabilities)) {
       return CONFIGURATION_NOT_SUPPORTED;
     }
 
@@ -274,20 +405,52 @@
   //        configuration's distinctiveIdentifier value to "required".
   //      - Otherwise, change accumulated configuration's distinctiveIdentifier
   //        value to "not-allowed".
-  //    (Without robustness support, capabilities do not affect this.)
-  // TODO(sandersd): Implement robustness. http://crbug.com/442586
   if (accumulated_configuration->distinctiveIdentifier ==
       blink::WebMediaKeySystemConfiguration::Requirement::Optional) {
-    if (IsDistinctiveIdentifierRequirementSupported(
-            key_system, EME_FEATURE_NOT_ALLOWED, is_permission_possible)) {
-      accumulated_configuration->distinctiveIdentifier =
-          blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed;
-    } else {
+    EmeConfigRule not_allowed_rule =
+        key_systems.GetDistinctiveIdentifierConfigRule(
+            key_system, EME_FEATURE_NOT_ALLOWED);
+    EmeConfigRule required_rule =
+        key_systems.GetDistinctiveIdentifierConfigRule(
+            key_system, EME_FEATURE_REQUIRED);
+    bool not_allowed_supported = config_state.IsRuleSupported(not_allowed_rule);
+    bool required_supported = config_state.IsRuleSupported(required_rule);
+    if (not_allowed_supported) {
+      bool prefer_required = config_state.IsIdentifierRequired() ||
+                             (config_state.IsIdentifierRecommended() &&
+                              config_state.IsPermissionPossible());
+      if (required_supported && prefer_required) {
+        accumulated_configuration->distinctiveIdentifier =
+            blink::WebMediaKeySystemConfiguration::Requirement::Required;
+        config_state.AddRule(required_rule);
+        DCHECK(config_state.IsIdentifierRequired());
+      } else {
+        accumulated_configuration->distinctiveIdentifier =
+            blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed;
+        config_state.AddRule(not_allowed_rule);
+      }
+    } else if (required_supported) {
       accumulated_configuration->distinctiveIdentifier =
           blink::WebMediaKeySystemConfiguration::Requirement::Required;
+      config_state.AddRule(required_rule);
+    } else {
+      // We should not have passed step 3.
+      NOTREACHED();
+      return CONFIGURATION_NOT_SUPPORTED;
     }
   }
 
+  // If permission is required but we couldn't enable it, reject the
+  // configuration.
+  if (config_state.IsIdentifierRequired() &&
+      accumulated_configuration->distinctiveIdentifier !=
+      blink::WebMediaKeySystemConfiguration::Requirement::Required) {
+    DVLOG(2) << "Rejecting requested configuration because "
+             << "distinctiveIdentifier was implicitly required but "
+             << "not allowed.";
+    return CONFIGURATION_NOT_SUPPORTED;
+  }
+
   // 10. If accumulated configuration's persistentState value is "optional",
   //     follow the steps for the first matching condition from the following
   //     list:
@@ -298,59 +461,60 @@
   //         to "not-allowed".
   if (accumulated_configuration->persistentState ==
       blink::WebMediaKeySystemConfiguration::Requirement::Optional) {
-    if (IsPersistentStateRequirementSupported(
-            key_system, EME_FEATURE_NOT_ALLOWED, is_permission_possible)) {
+    EmeConfigRule not_allowed_rule =
+        key_systems.GetPersistentStateConfigRule(
+            key_system, EME_FEATURE_NOT_ALLOWED);
+    EmeConfigRule required_rule =
+        key_systems.GetPersistentStateConfigRule(
+            key_system, EME_FEATURE_REQUIRED);
+    // Now that distinctiveIdentifier has been resolved, it is too late to allow
+    // persistentState to affect the configuration.
+    bool not_allowed_supported =
+        config_state.IsRuleSupportedWithCurrentState(not_allowed_rule);
+    bool required_supported =
+        config_state.IsRuleSupportedWithCurrentState(required_rule);
+    if (not_allowed_supported) {
       accumulated_configuration->persistentState =
           blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed;
-    } else {
+    } else if (required_supported) {
       accumulated_configuration->persistentState =
           blink::WebMediaKeySystemConfiguration::Requirement::Required;
+    } else {
+      // We should not have passed step 5.
+      NOTREACHED();
+      return CONFIGURATION_NOT_SUPPORTED;
     }
   }
 
   // 11. If implementation in the configuration specified by the combination of
   //     the values in accumulated configuration is not supported or not allowed
   //     in the origin, return null.
-  di_requirement =
-      ConvertRequirement(accumulated_configuration->distinctiveIdentifier);
-  if (!IsDistinctiveIdentifierRequirementSupported(key_system, di_requirement,
-                                                   is_permission_granted)) {
-    if (was_permission_requested) {
-      // The optional permission was requested and denied.
-      // TODO(sandersd): Avoid the need for this logic - crbug.com/460616.
-      DCHECK(candidate.distinctiveIdentifier ==
-             blink::WebMediaKeySystemConfiguration::Requirement::Optional);
-      DCHECK(di_requirement == EME_FEATURE_REQUIRED);
-      DCHECK(!is_permission_granted);
-      accumulated_configuration->distinctiveIdentifier =
-          blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed;
-    } else {
+  // 12. If accumulated configuration's distinctiveIdentifier value is
+  //     "required", [prompt the user for consent].
+  if (accumulated_configuration->distinctiveIdentifier ==
+      blink::WebMediaKeySystemConfiguration::Requirement::Required) {
+    // The caller is responsible for resolving what to do if permission is
+    // required but has been denied (it should treat it as NOT_SUPPORTED).
+    if (!config_state.IsPermissionGranted())
       return CONFIGURATION_REQUIRES_PERMISSION;
-    }
   }
 
-  ps_requirement =
-      ConvertRequirement(accumulated_configuration->persistentState);
-  if (!IsPersistentStateRequirementSupported(key_system, ps_requirement,
-                                             is_permission_granted)) {
-    DCHECK(!was_permission_requested);  // Should have failed at step 5.
-    return CONFIGURATION_REQUIRES_PERMISSION;
-  }
-
-  // 12. Return accumulated configuration.
-  //     (As an extra step, we record the available session types so that
-  //     createSession() can be synchronous.)
+  // 13. Return accumulated configuration.
+  //
+  // We also record the available session types so that createSession() can be
+  // synchronous.
   std::vector<blink::WebEncryptedMediaSessionType> session_types;
   session_types.push_back(blink::WebEncryptedMediaSessionType::Temporary);
   if (accumulated_configuration->persistentState ==
       blink::WebMediaKeySystemConfiguration::Requirement::Required) {
-    if (IsPersistentLicenseSessionSupported(key_system,
-                                            is_permission_granted)) {
+    if (config_state.IsRuleSupportedWithCurrentState(
+            key_systems.GetPersistentLicenseSessionConfigRule(key_system))) {
       session_types.push_back(
           blink::WebEncryptedMediaSessionType::PersistentLicense);
     }
-    if (IsPersistentReleaseMessageSessionSupported(key_system,
-                                                   is_permission_granted)) {
+    if (config_state.IsRuleSupportedWithCurrentState(
+            key_systems.GetPersistentReleaseMessageSessionConfigRule(
+                key_system))) {
       session_types.push_back(
           blink::WebEncryptedMediaSessionType::PersistentReleaseMessage);
     }
@@ -413,7 +577,9 @@
 WebEncryptedMediaClientImpl::WebEncryptedMediaClientImpl(
     scoped_ptr<CdmFactory> cdm_factory,
     MediaPermission* media_permission)
-    : cdm_factory_(cdm_factory.Pass()), media_permission_(media_permission),
+    : key_systems_(KeySystems::GetInstance()),
+      cdm_factory_(cdm_factory.Pass()),
+      media_permission_(media_permission),
       weak_factory_(this) {
   DCHECK(media_permission);
 }
@@ -441,7 +607,7 @@
   std::string key_system = base::UTF16ToASCII(request.keySystem());
   GetReporter(key_system)->ReportRequested();
 
-  if (!IsSupportedKeySystem(key_system)) {
+  if (!key_systems_.IsSupportedKeySystem(key_system)) {
     request.requestNotSupported("Unsupported keySystem");
     return;
   }
@@ -474,13 +640,18 @@
     //        new MediaKeySystemAccess object.]
     blink::WebMediaKeySystemConfiguration accumulated_configuration;
     ConfigurationSupport supported = GetSupportedConfiguration(
-        key_system, candidate_configuration, &accumulated_configuration,
-        was_permission_requested, is_permission_granted);
+        key_systems_, key_system, candidate_configuration,
+        was_permission_requested, is_permission_granted,
+        &accumulated_configuration);
     switch (supported) {
       case CONFIGURATION_NOT_SUPPORTED:
         continue;
       case CONFIGURATION_REQUIRES_PERMISSION:
-        DCHECK(!was_permission_requested);
+        if (was_permission_requested) {
+          DVLOG(2) << "Rejecting requested configuration because "
+                   << "permission was denied.";
+          continue;
+        }
         media_permission_->RequestPermission(
             MediaPermission::PROTECTED_MEDIA_IDENTIFIER,
             GURL(request.securityOrigin().toString()),
diff --git a/media/blink/webencryptedmediaclient_impl.h b/media/blink/webencryptedmediaclient_impl.h
index 36d104b..e18947ee 100644
--- a/media/blink/webencryptedmediaclient_impl.h
+++ b/media/blink/webencryptedmediaclient_impl.h
@@ -18,6 +18,7 @@
 
 namespace media {
 
+class KeySystems;
 class MediaPermission;
 
 class MEDIA_EXPORT WebEncryptedMediaClientImpl
@@ -60,6 +61,7 @@
   typedef base::ScopedPtrHashMap<std::string, Reporter> Reporters;
   Reporters reporters_;
 
+  const KeySystems& key_systems_;
   scoped_ptr<CdmFactory> cdm_factory_;
   MediaPermission* media_permission_;
 
diff --git a/media/filters/audio_clock.cc b/media/filters/audio_clock.cc
index 117d603..7a4981a 100644
--- a/media/filters/audio_clock.cc
+++ b/media/filters/audio_clock.cc
@@ -80,15 +80,6 @@
                                         microseconds_per_frame_);
 }
 
-base::TimeDelta AudioClock::TimestampSinceWriting(
-    base::TimeDelta time_since_writing) const {
-  int64_t frames_played_since_writing = std::min(
-      total_buffered_frames_,
-      static_cast<int64_t>(time_since_writing.InSecondsF() * sample_rate_));
-  return front_timestamp_ +
-         ComputeBufferedMediaTime(frames_played_since_writing);
-}
-
 base::TimeDelta AudioClock::TimeUntilPlayback(base::TimeDelta timestamp) const {
   DCHECK(timestamp >= front_timestamp_);
   DCHECK(timestamp <= back_timestamp_);
diff --git a/media/filters/audio_clock.h b/media/filters/audio_clock.h
index 6472f11..3010fbfc 100644
--- a/media/filters/audio_clock.h
+++ b/media/filters/audio_clock.h
@@ -77,13 +77,6 @@
   base::TimeDelta front_timestamp() const { return front_timestamp_; }
   base::TimeDelta back_timestamp() const { return back_timestamp_; }
 
-  // Clients can provide |time_since_writing| to simulate the passage of time
-  // since last writing audio to get a more accurate current media timestamp.
-  //
-  // The value will be bounded between front_timestamp() and back_timestamp().
-  base::TimeDelta TimestampSinceWriting(
-      base::TimeDelta time_since_writing) const;
-
   // Returns the amount of wall time until |timestamp| will be played by the
   // audio hardware.
   //
diff --git a/media/filters/audio_clock_unittest.cc b/media/filters/audio_clock_unittest.cc
index 557fa7c..7b4d85fd 100644
--- a/media/filters/audio_clock_unittest.cc
+++ b/media/filters/audio_clock_unittest.cc
@@ -34,11 +34,6 @@
     return clock_.back_timestamp().InMilliseconds();
   }
 
-  int TimestampSinceLastWritingInMilliseconds(int milliseconds) {
-    return clock_.TimestampSinceWriting(base::TimeDelta::FromMilliseconds(
-                                            milliseconds)).InMilliseconds();
-  }
-
   int TimeUntilPlaybackInMilliseconds(int timestamp_ms) {
     return clock_.TimeUntilPlayback(base::TimeDelta::FromMilliseconds(
                                         timestamp_ms)).InMilliseconds();
@@ -78,14 +73,6 @@
   EXPECT_EQ(expected, clock.back_timestamp());
 }
 
-TEST_F(AudioClockTest, TimestampSinceWritingStartsAtStartTimestamp) {
-  base::TimeDelta expected = base::TimeDelta::FromSeconds(123);
-  AudioClock clock(expected, sample_rate_);
-
-  base::TimeDelta time_since_writing = base::TimeDelta::FromSeconds(456);
-  EXPECT_EQ(expected, clock.TimestampSinceWriting(time_since_writing));
-}
-
 TEST_F(AudioClockTest, ContiguousAudioDataBufferedStartsAtZero) {
   EXPECT_EQ(base::TimeDelta(), clock_.contiguous_audio_data_buffered());
 }
@@ -280,47 +267,6 @@
   EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
 }
 
-TEST_F(AudioClockTest, TimestampSinceLastWriting) {
-  // Construct an audio clock with the following representation:
-  //
-  // |- existing  delay -|------------ calls to WroteAudio() -----------------|
-  // +-------------------+----------------+------------------+----------------+
-  // | 20 frames silence | 10 frames @ 1x | 10 frames @ 0.5x | 10 frames @ 2x |
-  // +-------------------+----------------+------------------+----------------+
-  // Media timestamp:    0              1000               1500             3500
-  // Wall clock time:  2000             3000               4000             5000
-  WroteAudio(10, 10, 40, 1.0);
-  WroteAudio(10, 10, 40, 0.5);
-  WroteAudio(10, 10, 40, 2.0);
-  EXPECT_EQ(0, FrontTimestampInMilliseconds());
-  EXPECT_EQ(3500, BackTimestampInMilliseconds());
-  EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
-
-  // Simulate passing 2000ms of initial delay in the audio hardware.
-  EXPECT_EQ(0, TimestampSinceLastWritingInMilliseconds(0));
-  EXPECT_EQ(0, TimestampSinceLastWritingInMilliseconds(500));
-  EXPECT_EQ(0, TimestampSinceLastWritingInMilliseconds(1000));
-  EXPECT_EQ(0, TimestampSinceLastWritingInMilliseconds(1500));
-  EXPECT_EQ(0, TimestampSinceLastWritingInMilliseconds(2000));
-
-  // Now we should see the 1.0x buffer.
-  EXPECT_EQ(500, TimestampSinceLastWritingInMilliseconds(2500));
-  EXPECT_EQ(1000, TimestampSinceLastWritingInMilliseconds(3000));
-
-  // Now we should see the 0.5x buffer.
-  EXPECT_EQ(1250, TimestampSinceLastWritingInMilliseconds(3500));
-  EXPECT_EQ(1500, TimestampSinceLastWritingInMilliseconds(4000));
-
-  // Now we should see the 2.0x buffer.
-  EXPECT_EQ(2500, TimestampSinceLastWritingInMilliseconds(4500));
-  EXPECT_EQ(3500, TimestampSinceLastWritingInMilliseconds(5000));
-
-  // Times beyond the known length of the audio clock should return the last
-  // media timestamp we know of.
-  EXPECT_EQ(3500, TimestampSinceLastWritingInMilliseconds(5001));
-  EXPECT_EQ(3500, TimestampSinceLastWritingInMilliseconds(6000));
-}
-
 TEST_F(AudioClockTest, TimeUntilPlayback) {
   // Construct an audio clock with the following representation:
   //
diff --git a/media/filters/decrypting_demuxer_stream_unittest.cc b/media/filters/decrypting_demuxer_stream_unittest.cc
index 3cba1f7..e5d2567f 100644
--- a/media/filters/decrypting_demuxer_stream_unittest.cc
+++ b/media/filters/decrypting_demuxer_stream_unittest.cc
@@ -29,7 +29,7 @@
 static const uint8 kFakeIv[DecryptConfig::kDecryptionKeySize] = { 0 };
 
 // Create a fake non-empty buffer in an encrypted stream. When |is_clear| is
-// ture, the buffer is not encrypted (signaled by an empty IV).
+// true, the buffer is not encrypted (signaled by an empty IV).
 static scoped_refptr<DecoderBuffer> CreateFakeEncryptedStreamBuffer(
     bool is_clear) {
   scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(kFakeBufferSize));
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
index 703cb20..d059c0b 100644
--- a/media/filters/gpu_video_decoder.cc
+++ b/media/filters/gpu_video_decoder.cc
@@ -97,7 +97,8 @@
   vda_->Reset();
 }
 
-static bool IsCodedSizeSupported(const gfx::Size& coded_size) {
+static bool IsCodedSizeSupported(const gfx::Size& coded_size,
+                                 VideoCodecProfile profile) {
 #if defined(OS_WIN)
   // Windows Media Foundation H.264 decoding does not support decoding videos
   // with any dimension smaller than 48 pixels:
@@ -118,7 +119,9 @@
   bool hw_large_video_support =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kIgnoreResolutionLimitsForAcceleratedVideoDecode) ||
-      ((cpu.vendor_name() == "GenuineIntel") && cpu.model() >= 55);
+      ((cpu.vendor_name() == "GenuineIntel") && cpu.model() >= 55 &&
+       // TODO(posciak, henryhsu): Remove this once we can query in runtime.
+       profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX);
   bool os_large_video_support = true;
 #if defined(OS_WIN)
   os_large_video_support = false;
@@ -166,7 +169,7 @@
     return;
   }
 
-  if (!IsCodedSizeSupported(config.coded_size())) {
+  if (!IsCodedSizeSupported(config.coded_size(), config.profile())) {
     status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
     return;
   }
diff --git a/media/media.gyp b/media/media.gyp
index 20d84cc..dbe8562 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -1822,6 +1822,7 @@
           'type': 'none',
           'dependencies': [
             '../base/base.gyp:base',
+            'media_android_captureapitype',
             'media_android_imageformat',
           ],
           'export_dependent_settings': [
@@ -1833,6 +1834,15 @@
           'includes': ['../build/java.gypi'],
         },
         {
+          # GN: //media/base/android:media_android_captureapitype
+          'target_name': 'media_android_captureapitype',
+          'type': 'none',
+          'variables': {
+            'source_file': 'video/capture/video_capture_device.h',
+          },
+          'includes': [ '../build/android/java_cpp_enum.gypi' ],
+        },
+        {
           # GN: //media/base/android:media_android_imageformat
           'target_name': 'media_android_imageformat',
           'type': 'none',
diff --git a/media/midi/midi_manager_alsa.cc b/media/midi/midi_manager_alsa.cc
index d80aa2b3..89b0da8c 100644
--- a/media/midi/midi_manager_alsa.cc
+++ b/media/midi/midi_manager_alsa.cc
@@ -243,14 +243,16 @@
                             base::Unretained(client), data.size()));
 }
 
-MidiManagerAlsa::AlsaRawmidi::AlsaRawmidi(const MidiManagerAlsa* outer,
-                                          const std::string& alsa_name,
-                                          const std::string& alsa_longname,
-                                          const std::string& alsa_driver,
-                                          int card_index)
+MidiManagerAlsa::AlsaCard::AlsaCard(const MidiManagerAlsa* outer,
+                                    const std::string& alsa_name,
+                                    const std::string& alsa_longname,
+                                    const std::string& alsa_driver,
+                                    int card_index,
+                                    int midi_count)
     : alsa_name_(alsa_name),
       alsa_longname_(alsa_longname),
-      alsa_driver_(alsa_driver) {
+      alsa_driver_(alsa_driver),
+      midi_count_(midi_count) {
   // Get udev properties if available.
   std::string vendor;
   std::string vendor_from_database;
@@ -297,38 +299,38 @@
       vendor, vendor_id_, vendor_from_database, alsa_name, alsa_longname);
 }
 
-MidiManagerAlsa::AlsaRawmidi::~AlsaRawmidi() {
+MidiManagerAlsa::AlsaCard::~AlsaCard() {
 }
 
-const std::string MidiManagerAlsa::AlsaRawmidi::alsa_name() const {
+const std::string MidiManagerAlsa::AlsaCard::alsa_name() const {
   return alsa_name_;
 }
 
-const std::string MidiManagerAlsa::AlsaRawmidi::alsa_longname() const {
+const std::string MidiManagerAlsa::AlsaCard::alsa_longname() const {
   return alsa_longname_;
 }
 
-const std::string MidiManagerAlsa::AlsaRawmidi::manufacturer() const {
+const std::string MidiManagerAlsa::AlsaCard::manufacturer() const {
   return manufacturer_;
 }
 
-const std::string MidiManagerAlsa::AlsaRawmidi::alsa_driver() const {
+const std::string MidiManagerAlsa::AlsaCard::alsa_driver() const {
   return alsa_driver_;
 }
 
-const std::string MidiManagerAlsa::AlsaRawmidi::path() const {
+const std::string MidiManagerAlsa::AlsaCard::path() const {
   return path_;
 }
 
-const std::string MidiManagerAlsa::AlsaRawmidi::bus() const {
+const std::string MidiManagerAlsa::AlsaCard::bus() const {
   return bus_;
 }
 
-const std::string MidiManagerAlsa::AlsaRawmidi::vendor_id() const {
+const std::string MidiManagerAlsa::AlsaCard::vendor_id() const {
   return vendor_id_;
 }
 
-const std::string MidiManagerAlsa::AlsaRawmidi::id() const {
+const std::string MidiManagerAlsa::AlsaCard::id() const {
   std::string id = vendor_id_;
   if (!model_id_.empty())
     id += ":" + model_id_;
@@ -337,8 +339,12 @@
   return id;
 }
 
+const int MidiManagerAlsa::AlsaCard::midi_count() const {
+  return midi_count_;
+}
+
 // static
-std::string MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+std::string MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
     const std::string& udev_id_vendor,
     const std::string& udev_id_vendor_id,
     const std::string& udev_id_vendor_from_database,
@@ -445,13 +451,14 @@
 
 // TODO(agoode): Add a client->card/rawmidi mapping to the kernel to avoid
 //               needing to probe in this way.
-ScopedVector<MidiManagerAlsa::AlsaRawmidi> MidiManagerAlsa::AllAlsaRawmidis() {
-  ScopedVector<AlsaRawmidi> devices;
+ScopedVector<MidiManagerAlsa::AlsaCard> MidiManagerAlsa::AllMidiCards() {
+  ScopedVector<AlsaCard> devices;
   snd_ctl_card_info_t* card;
   snd_hwdep_info_t* hwdep;
   snd_ctl_card_info_alloca(&card);
   snd_hwdep_info_alloca(&hwdep);
   for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0;) {
+    int midi_count = 0;
     const std::string id = base::StringPrintf("hw:CARD=%i", card_index);
     snd_ctl_t* handle;
     int err = snd_ctl_open(&handle, id.c_str(), 0);
@@ -471,12 +478,10 @@
 
     // Count rawmidi devices (not subdevices).
     for (int device = -1;
-         !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;) {
-      devices.push_back(
-          new AlsaRawmidi(this, name, longname, driver, card_index));
-    }
+         !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;)
+      ++midi_count;
 
-    // Count any hwdep synths that become MIDI devices.
+    // Count any hwdep synths that become MIDI devices outside of rawmidi.
     //
     // Explanation:
     // Any kernel driver can create an ALSA client (visible to us).
@@ -503,9 +508,12 @@
       snd_hwdep_iface_t iface = snd_hwdep_info_get_iface(hwdep);
       if (iface == SND_HWDEP_IFACE_OPL2 || iface == SND_HWDEP_IFACE_OPL3 ||
           iface == SND_HWDEP_IFACE_OPL4)
-        devices.push_back(
-            new AlsaRawmidi(this, name, longname, driver, card_index));
+        ++midi_count;
     }
+
+    if (midi_count > 0)
+      devices.push_back(
+          new AlsaCard(this, name, longname, driver, card_index, midi_count));
     snd_ctl_close(handle);
   }
 
@@ -513,7 +521,13 @@
 }
 
 void MidiManagerAlsa::EnumeratePorts() {
-  ScopedVector<AlsaRawmidi> devices = AllAlsaRawmidis();
+  ScopedVector<AlsaCard> cards = AllMidiCards();
+  std::vector<const AlsaCard*> devices;
+  for (const auto* card : cards) {
+    // Insert 1 AlsaCard per number of MIDI devices.
+    for (int n = 0; n < card->midi_count(); ++n)
+      devices.push_back(card);
+  }
 
   snd_seq_port_subscribe_t* subs;
   snd_seq_port_subscribe_alloca(&subs);
@@ -551,14 +565,14 @@
     std::string card_name;
     std::string card_longname;
 
-    // Join kernel clients against the list of AlsaRawmidis.
+    // Join kernel clients against the list of AlsaCards.
     // In the current ALSA kernel implementation, kernel clients match the
-    // kernel devices in the same order, for devices with client_id over
+    // kernel devices in the same order, for clients with client_id over
     // kMinimumClientIdForCards.
     if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) &&
         (current_device < devices.size()) &&
         (client_id >= kMinimumClientIdForCards)) {
-      const AlsaRawmidi* device = devices[current_device];
+      const AlsaCard* device = devices[current_device];
       manufacturer = device->manufacturer();
       driver = device->alsa_driver();
       path = device->path();
@@ -700,6 +714,10 @@
         // TODO(agoode): rescan hardware devices.
         break;
 
+      case SND_SEQ_EVENT_PORT_START:
+        // TODO(agoode): add port.
+        break;
+
       case SND_SEQ_EVENT_CLIENT_EXIT:
         // Check for disconnection of our "out" client. This means "shut down".
         if (event->data.addr.client == out_client_id_)
@@ -708,26 +726,21 @@
         // TODO(agoode): remove all ports for a client.
         break;
 
-      case SND_SEQ_EVENT_PORT_START:
-        // TODO(agoode): add port.
-        break;
-
       case SND_SEQ_EVENT_PORT_EXIT:
         // TODO(agoode): remove port.
         break;
     }
+  } else {
+    ProcessSingleEvent(event, timestamp);
   }
 
-  ProcessSingleEvent(event, timestamp);
-
   // Do again.
   ScheduleEventLoop();
 }
 
 void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event,
                                          double timestamp) {
-  std::map<int, uint32>::iterator source_it =
-      source_map_.find(AddrToInt(&event->source));
+  auto source_it = source_map_.find(AddrToInt(&event->source));
   if (source_it != source_map_.end()) {
     uint32 source = source_it->second;
     if (event->type == SND_SEQ_EVENT_SYSEX) {
diff --git a/media/midi/midi_manager_alsa.h b/media/midi/midi_manager_alsa.h
index 4ee47bf9..11f76c07 100644
--- a/media/midi/midi_manager_alsa.h
+++ b/media/midi/midi_manager_alsa.h
@@ -39,14 +39,15 @@
   FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest, ExtractManufacturer);
   FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest, JSONPortMetadata);
 
-  class AlsaRawmidi {
+  class AlsaCard {
    public:
-    AlsaRawmidi(const MidiManagerAlsa* outer,
-                const std::string& alsa_name,
-                const std::string& alsa_longname,
-                const std::string& alsa_driver,
-                int card_index);
-    ~AlsaRawmidi();
+    AlsaCard(const MidiManagerAlsa* outer,
+             const std::string& alsa_name,
+             const std::string& alsa_longname,
+             const std::string& alsa_driver,
+             int card_index,
+             int midi_count);
+    ~AlsaCard();
 
     const std::string alsa_name() const;
     const std::string alsa_longname() const;
@@ -56,6 +57,7 @@
     const std::string bus() const;
     const std::string vendor_id() const;
     const std::string id() const;
+    const int midi_count() const;
 
    private:
     FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest, ExtractManufacturer);
@@ -77,8 +79,9 @@
     std::string vendor_id_;
     std::string model_id_;
     std::string usb_interface_num_;
+    int midi_count_;
 
-    DISALLOW_COPY_AND_ASSIGN(AlsaRawmidi);
+    DISALLOW_COPY_AND_ASSIGN(AlsaCard);
   };
 
   class AlsaPortMetadata {
@@ -124,8 +127,8 @@
     DISALLOW_COPY_AND_ASSIGN(AlsaPortMetadata);
   };
 
-  // Returns an ordered vector of all the rawmidi devices on the system.
-  ScopedVector<AlsaRawmidi> AllAlsaRawmidis();
+  // Returns an ordered vector of all the cards with MIDI capabilities.
+  ScopedVector<AlsaCard> AllMidiCards();
 
   // Enumerate all the ports for initial setup.
   void EnumeratePorts();
diff --git a/media/midi/midi_manager_alsa_unittest.cc b/media/midi/midi_manager_alsa_unittest.cc
index 811b635..9aaf400 100644
--- a/media/midi/midi_manager_alsa_unittest.cc
+++ b/media/midi/midi_manager_alsa_unittest.cc
@@ -10,53 +10,52 @@
 
 TEST(MidiManagerAlsaTest, ExtractManufacturer) {
   ASSERT_EQ("My\\x20Vendor",
-            MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+            MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                 "My\\x20Vendor", "1234", "My Vendor, Inc.", "Card",
                 "My Vendor Inc Card at bus"));
-  ASSERT_EQ("My Vendor",
-            MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
-                "My Vendor", "1234", "My Vendor, Inc.", "Card",
-                "My Vendor Inc Card at bus"));
+  ASSERT_EQ("My Vendor", MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
+                             "My Vendor", "1234", "My Vendor, Inc.", "Card",
+                             "My Vendor Inc Card at bus"));
   ASSERT_EQ("My Vendor, Inc.",
-            MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+            MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                 "1234", "1234", "My Vendor, Inc.", "Card",
                 "My Vendor Inc Card at bus"));
   ASSERT_EQ("My Vendor Inc",
-            MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+            MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                 "1234", "1234", "", "Card", "My Vendor Inc Card at bus"));
   ASSERT_EQ("My Vendor Inc",
-            MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+            MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                 "", "", "", "Card", "My Vendor Inc Card at bus"));
-  ASSERT_EQ("", MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+  ASSERT_EQ("", MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                     "1234", "1234", "", "Card", "Longname"));
   ASSERT_EQ("Keystation\\x20Mini\\x2032",
-            MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+            MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                 "Keystation\\x20Mini\\x2032", "129d",
                 "Evolution Electronics, Ltd", "Keystation Mini 32",
                 "Keystation Mini 32 Keystation Mini 32 at"
                 " usb-0000:00:14.0-2.4.4, full speed"));
   ASSERT_EQ("Keystation Mini 32",
-            MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+            MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                 "Keystation Mini 32", "129d", "Evolution Electronics, Ltd",
                 "Keystation Mini 32",
                 "Keystation Mini 32 Keystation Mini 32 at"
                 " usb-0000:00:14.0-2.4.4, full speed"));
   ASSERT_EQ("Keystation Mini 32",
-            MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+            MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                 "", "", "", "Keystation Mini 32",
                 "Keystation Mini 32 Keystation Mini 32 at"
                 " usb-0000:00:14.0-2.4.4, full speed"));
-  ASSERT_EQ("", MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+  ASSERT_EQ("", MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                     "", "", "", "Serial MIDI (UART16550A)",
                     "Serial MIDI (UART16550A) [Soundcanvas] at 0x3f8, irq 4"));
-  ASSERT_EQ("", MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+  ASSERT_EQ("", MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                     "", "", "", "VirMIDI", "Virtual MIDI Card 1"));
   ASSERT_EQ("C-Media Electronics Inc",
-            MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+            MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                 "", "0x13f6", "C-Media Electronics Inc", "C-Media CMI8738 MIDI",
                 "C-Media CMI8738 (model 55) at 0xd000, irq 19"));
   ASSERT_EQ("C-Media Electronics Inc",
-            MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString(
+            MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
                 "", "0x13f6", "C-Media Electronics Inc", "C-Media CMI8738 FM",
                 "C-Media CMI8738 (model 55) at 0xd000, irq 19"));
 }
diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc
index bb6dffa..6f839db 100644
--- a/media/renderers/audio_renderer_impl.cc
+++ b/media/renderers/audio_renderer_impl.cc
@@ -166,15 +166,27 @@
   return current_media_time;
 }
 
-base::TimeDelta AudioRendererImpl::CurrentMediaTimeForSyncingVideo() {
-  DVLOG(3) << __FUNCTION__;
-
+base::TimeTicks AudioRendererImpl::GetWallClockTime(base::TimeDelta time) {
   base::AutoLock auto_lock(lock_);
-  if (last_render_ticks_.is_null())
-    return audio_clock_->front_timestamp();
+  if (last_render_ticks_.is_null() || playback_rate_ == 0.0)
+    return base::TimeTicks();
 
-  return audio_clock_->TimestampSinceWriting(base::TimeTicks::Now() -
-                                             last_render_ticks_);
+  base::TimeDelta base_time;
+  if (time < audio_clock_->front_timestamp()) {
+    // See notes about |time| values less than |base_time| in TimeSource header.
+    base_time = audio_clock_->front_timestamp();
+  } else if (time > audio_clock_->back_timestamp()) {
+    base_time = audio_clock_->back_timestamp();
+  } else {
+    // No need to estimate time, so return the actual wallclock time.
+    return last_render_ticks_ + audio_clock_->TimeUntilPlayback(time);
+  }
+
+  // In practice, most calls will be estimates given the relatively small window
+  // in which clients can get the actual time.
+  return last_render_ticks_ + audio_clock_->TimeUntilPlayback(base_time) +
+         base::TimeDelta::FromMicroseconds((time - base_time).InMicroseconds() /
+                                           playback_rate_);
 }
 
 TimeSource* AudioRendererImpl::GetTimeSource() {
diff --git a/media/renderers/audio_renderer_impl.h b/media/renderers/audio_renderer_impl.h
index 1972c872..230a8d3 100644
--- a/media/renderers/audio_renderer_impl.h
+++ b/media/renderers/audio_renderer_impl.h
@@ -71,7 +71,7 @@
   void SetPlaybackRate(float rate) override;
   void SetMediaTime(base::TimeDelta time) override;
   base::TimeDelta CurrentMediaTime() override;
-  base::TimeDelta CurrentMediaTimeForSyncingVideo() override;
+  base::TimeTicks GetWallClockTime(base::TimeDelta time) override;
 
   // AudioRenderer implementation.
   void Initialize(DemuxerStream* stream,
diff --git a/media/renderers/renderer_impl.cc b/media/renderers/renderer_impl.cc
index 93671851..e81c97ae 100644
--- a/media/renderers/renderer_impl.cc
+++ b/media/renderers/renderer_impl.cc
@@ -202,16 +202,16 @@
   clockless_video_playback_enabled_for_testing_ = true;
 }
 
-base::TimeDelta RendererImpl::GetMediaTimeForSyncingVideo() {
+base::TimeTicks RendererImpl::GetWallClockTime(base::TimeDelta time) {
   // No BelongsToCurrentThread() checking because this can be called from other
   // threads.
   //
   // TODO(scherkus): Currently called from VideoRendererImpl's internal thread,
   // which should go away at some point http://crbug.com/110814
   if (clockless_video_playback_enabled_for_testing_)
-    return base::TimeDelta::Max();
+    return base::TimeTicks::Now();
 
-  return time_source_->CurrentMediaTimeForSyncingVideo();
+  return time_source_->GetWallClockTime(time);
 }
 
 void RendererImpl::SetDecryptorReadyCallback(
@@ -310,8 +310,7 @@
       base::ResetAndReturn(&paint_cb_),
       base::Bind(&RendererImpl::OnVideoRendererEnded, weak_this_),
       base::Bind(&RendererImpl::OnError, weak_this_),
-      base::Bind(&RendererImpl::GetMediaTimeForSyncingVideo,
-                 base::Unretained(this)),
+      base::Bind(&RendererImpl::GetWallClockTime, base::Unretained(this)),
       waiting_for_decryption_key_cb_);
 }
 
diff --git a/media/renderers/renderer_impl.h b/media/renderers/renderer_impl.h
index befa95c1..03942d52 100644
--- a/media/renderers/renderer_impl.h
+++ b/media/renderers/renderer_impl.h
@@ -74,7 +74,7 @@
     STATE_ERROR
   };
 
-  base::TimeDelta GetMediaTimeForSyncingVideo();
+  base::TimeTicks GetWallClockTime(base::TimeDelta time);
 
   // Requests that this object notifies when a decryptor is ready through the
   // |decryptor_ready_cb| provided.
diff --git a/media/renderers/video_renderer_impl.cc b/media/renderers/video_renderer_impl.cc
index 0ad19e4..a43ddab64 100644
--- a/media/renderers/video_renderer_impl.cc
+++ b/media/renderers/video_renderer_impl.cc
@@ -10,6 +10,7 @@
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/platform_thread.h"
+#include "base/time/default_tick_clock.h"
 #include "base/trace_event/trace_event.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/buffers.h"
@@ -36,11 +37,10 @@
       pending_read_(false),
       drop_frames_(drop_frames),
       buffering_state_(BUFFERING_HAVE_NOTHING),
-      last_timestamp_(kNoTimestamp()),
-      last_painted_timestamp_(kNoTimestamp()),
       frames_decoded_(0),
       frames_dropped_(0),
       is_shutting_down_(false),
+      tick_clock_(new base::DefaultTickClock()),
       weak_factory_(this) {
 }
 
@@ -109,7 +109,7 @@
     const PaintCB& paint_cb,
     const base::Closure& ended_cb,
     const PipelineStatusCB& error_cb,
-    const TimeDeltaCB& get_time_cb,
+    const WallClockTimeCB& wall_clock_time_cb,
     const base::Closure& waiting_for_decryption_key_cb) {
   DCHECK(task_runner_->BelongsToCurrentThread());
   base::AutoLock auto_lock(lock_);
@@ -120,7 +120,7 @@
   DCHECK(!buffering_state_cb.is_null());
   DCHECK(!paint_cb.is_null());
   DCHECK(!ended_cb.is_null());
-  DCHECK(!get_time_cb.is_null());
+  DCHECK(!wall_clock_time_cb.is_null());
   DCHECK_EQ(kUninitialized, state_);
 
   low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE);
@@ -134,7 +134,7 @@
   paint_cb_ = paint_cb,
   ended_cb_ = ended_cb;
   error_cb_ = error_cb;
-  get_time_cb_ = get_time_cb;
+  wall_clock_time_cb_ = wall_clock_time_cb;
   state_ = kInitializing;
 
   video_frame_stream_->Initialize(
@@ -208,7 +208,7 @@
       continue;
     }
 
-    base::TimeDelta now = get_time_cb_.Run();
+    base::TimeTicks now = tick_clock_->NowTicks();
 
     // Remain idle until we have the next frame ready for rendering.
     if (ready_frames_.empty()) {
@@ -217,8 +217,8 @@
           rendered_end_of_stream_ = true;
           task_runner_->PostTask(FROM_HERE, ended_cb_);
         }
-      } else if (last_painted_timestamp_ != kNoTimestamp() &&
-                 now - last_painted_timestamp_ >= kTimeToDeclareHaveNothing) {
+      } else if (!last_painted_time_.is_null() &&
+                 now - last_painted_time_ >= kTimeToDeclareHaveNothing) {
         buffering_state_ = BUFFERING_HAVE_NOTHING;
         task_runner_->PostTask(
             FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING));
@@ -228,8 +228,16 @@
       continue;
     }
 
-    base::TimeDelta target_paint_timestamp = ready_frames_.front()->timestamp();
-    base::TimeDelta latest_paint_timestamp;
+    base::TimeTicks target_paint_time =
+        wall_clock_time_cb_.Run(ready_frames_.front()->timestamp());
+
+    // If media time has stopped, don't attempt to paint any more frames.
+    if (target_paint_time.is_null()) {
+      UpdateStatsAndWait_Locked(kIdleTimeDelta);
+      continue;
+    }
+
+    base::TimeTicks latest_possible_paint_time;
 
     // Deadline is defined as the duration between this frame and the next
     // frame, using the delta between this frame and the previous frame as the
@@ -237,20 +245,22 @@
     //
     // TODO(scherkus): This can be vastly improved. Use a histogram to measure
     // the accuracy of our frame timing code. http://crbug.com/149829
-    if (last_timestamp_ == kNoTimestamp()) {
-      latest_paint_timestamp = base::TimeDelta::Max();
+    if (last_media_time_.is_null()) {
+      latest_possible_paint_time = now;
     } else {
-      base::TimeDelta duration = target_paint_timestamp - last_timestamp_;
-      latest_paint_timestamp = target_paint_timestamp + duration;
+      base::TimeDelta duration = target_paint_time - last_media_time_;
+      latest_possible_paint_time = target_paint_time + duration;
     }
 
     // Remain idle until we've reached our target paint window.
-    if (now < target_paint_timestamp) {
-      UpdateStatsAndWait_Locked(kIdleTimeDelta);
+    if (now < target_paint_time) {
+      UpdateStatsAndWait_Locked(
+          std::min(target_paint_time - now, kIdleTimeDelta));
       continue;
     }
 
-    if (now > latest_paint_timestamp && drop_frames_) {
+    if (ready_frames_.size() > 1 && now > latest_possible_paint_time &&
+        drop_frames_) {
       DropNextReadyFrame_Locked();
       continue;
     }
@@ -263,6 +273,11 @@
   }
 }
 
+void VideoRendererImpl::SetTickClockForTesting(
+    scoped_ptr<base::TickClock> tick_clock) {
+  tick_clock_.swap(tick_clock);
+}
+
 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
   lock_.AssertAcquired();
 
@@ -270,8 +285,8 @@
   ready_frames_.pop_front();
   frames_decoded_++;
 
-  last_timestamp_ = next_frame->timestamp();
-  last_painted_timestamp_ = next_frame->timestamp();
+  last_media_time_ = last_painted_time_ =
+      wall_clock_time_cb_.Run(next_frame->timestamp());
 
   paint_cb_.Run(next_frame);
 
@@ -285,7 +300,9 @@
 
   lock_.AssertAcquired();
 
-  last_timestamp_ = ready_frames_.front()->timestamp();
+  last_media_time_ =
+      wall_clock_time_cb_.Run(ready_frames_.front()->timestamp());
+
   ready_frames_.pop_front();
   frames_decoded_++;
   frames_dropped_++;
@@ -426,8 +443,7 @@
   DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
 
   state_ = kFlushed;
-  last_timestamp_ = kNoTimestamp();
-  last_painted_timestamp_ = kNoTimestamp();
+  last_media_time_ = last_painted_time_ = base::TimeTicks();
   base::ResetAndReturn(&flush_cb_).Run();
 }
 
diff --git a/media/renderers/video_renderer_impl.h b/media/renderers/video_renderer_impl.h
index eec8b15..6f6c027 100644
--- a/media/renderers/video_renderer_impl.h
+++ b/media/renderers/video_renderer_impl.h
@@ -25,6 +25,7 @@
 
 namespace base {
 class SingleThreadTaskRunner;
+class TickClock;
 }
 
 namespace media {
@@ -60,7 +61,7 @@
                   const PaintCB& paint_cb,
                   const base::Closure& ended_cb,
                   const PipelineStatusCB& error_cb,
-                  const TimeDeltaCB& get_time_cb,
+                  const WallClockTimeCB& wall_clock_time_cb,
                   const base::Closure& waiting_for_decryption_key_cb) override;
   void Flush(const base::Closure& callback) override;
   void StartPlayingFrom(base::TimeDelta timestamp) override;
@@ -68,6 +69,8 @@
   // PlatformThread::Delegate implementation.
   void ThreadMain() override;
 
+  void SetTickClockForTesting(scoped_ptr<base::TickClock> tick_clock);
+
  private:
   // Creates a dedicated |thread_| for video rendering.
   void CreateVideoThread();
@@ -182,21 +185,21 @@
   BufferingStateCB buffering_state_cb_;
   base::Closure ended_cb_;
   PipelineStatusCB error_cb_;
-  TimeDeltaCB get_time_cb_;
+  WallClockTimeCB wall_clock_time_cb_;
 
   base::TimeDelta start_timestamp_;
 
   // Embedder callback for notifying a new frame is available for painting.
   PaintCB paint_cb_;
 
-  // The timestamp of the last frame removed from the |ready_frames_| queue,
-  // either for calling |paint_cb_| or for dropping. Set to kNoTimestamp()
-  // during flushing.
-  base::TimeDelta last_timestamp_;
+  // The wallclock times of the last frame removed from the |ready_frames_|
+  // queue, either for calling |paint_cb_| or for dropping. Set to null during
+  // flushing.
+  base::TimeTicks last_media_time_;
 
-  // The timestamp of the last successfully painted frame. Set to kNoTimestamp()
+  // The wallclock time of the last successfully painted frame. Set to null
   // during flushing.
-  base::TimeDelta last_painted_timestamp_;
+  base::TimeTicks last_painted_time_;
 
   // Keeps track of the number of frames decoded and dropped since the
   // last call to |statistics_cb_|. These must be accessed under lock.
@@ -205,6 +208,8 @@
 
   bool is_shutting_down_;
 
+  scoped_ptr<base::TickClock> tick_clock_;
+
   // NOTE: Weak pointers must be invalidated before all other member variables.
   base::WeakPtrFactory<VideoRendererImpl> weak_factory_;
 
diff --git a/media/renderers/video_renderer_impl_unittest.cc b/media/renderers/video_renderer_impl_unittest.cc
index d9cad51e..7ef623399 100644
--- a/media/renderers/video_renderer_impl_unittest.cc
+++ b/media/renderers/video_renderer_impl_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/lock.h"
+#include "base/test/simple_test_tick_clock.h"
 #include "media/base/data_buffer.h"
 #include "media/base/gmock_callback_support.h"
 #include "media/base/limits.h"
@@ -45,7 +46,8 @@
 class VideoRendererImplTest : public ::testing::Test {
  public:
   VideoRendererImplTest()
-      : decoder_(new MockVideoDecoder()),
+      : tick_clock_(new base::SimpleTestTickClock()),
+        decoder_(new MockVideoDecoder()),
         demuxer_stream_(DemuxerStream::VIDEO) {
     ScopedVector<VideoDecoder> decoders;
     decoders.push_back(decoder_);
@@ -53,6 +55,10 @@
     renderer_.reset(new VideoRendererImpl(message_loop_.message_loop_proxy(),
                                           decoders.Pass(), true,
                                           new MediaLog()));
+    renderer_->SetTickClockForTesting(scoped_ptr<base::TickClock>(tick_clock_));
+
+    // Start wallclock time at a non-zero value.
+    AdvanceWallclockTimeInMs(12345);
 
     demuxer_stream_.set_video_decoder_config(TestVideoConfig::Normal());
 
@@ -103,7 +109,8 @@
                    base::Unretained(&mock_cb_)),
         base::Bind(&StrictMock<MockCB>::Display, base::Unretained(&mock_cb_)),
         ended_event_.GetClosure(), error_event_.GetPipelineStatusCB(),
-        base::Bind(&VideoRendererImplTest::GetTime, base::Unretained(this)),
+        base::Bind(&VideoRendererImplTest::GetWallClockTime,
+                   base::Unretained(this)),
         base::Bind(&VideoRendererImplTest::OnWaitingForDecryptionKey,
                    base::Unretained(this)));
   }
@@ -237,6 +244,12 @@
         base::Bind(base::ResetAndReturn(&decode_cb_), VideoDecoder::kOk));
   }
 
+  void AdvanceWallclockTimeInMs(int time_ms) {
+    DCHECK_EQ(&message_loop_, base::MessageLoop::current());
+    base::AutoLock l(lock_);
+    tick_clock_->Advance(base::TimeDelta::FromMilliseconds(time_ms));
+  }
+
   void AdvanceTimeInMs(int time_ms) {
     DCHECK_EQ(&message_loop_, base::MessageLoop::current());
     base::AutoLock l(lock_);
@@ -246,6 +259,7 @@
  protected:
   // Fixture members.
   scoped_ptr<VideoRendererImpl> renderer_;
+  base::SimpleTestTickClock* tick_clock_;  // Owned by |renderer_|.
   MockVideoDecoder* decoder_;  // Owned by |renderer_|.
   NiceMock<MockDemuxerStream> demuxer_stream_;
 
@@ -258,9 +272,9 @@
   StrictMock<MockCB> mock_cb_;
 
  private:
-  base::TimeDelta GetTime() {
+  base::TimeTicks GetWallClockTime(base::TimeDelta time) {
     base::AutoLock l(lock_);
-    return time_;
+    return tick_clock_->NowTicks() + (time - time_);
   }
 
   void DecodeRequested(const scoped_refptr<DecoderBuffer>& buffer,
@@ -472,20 +486,31 @@
 TEST_F(VideoRendererImplTest, Underflow) {
   Initialize();
   QueueFrames("0 10 20 30");
-  EXPECT_CALL(mock_cb_, Display(HasTimestamp(0)));
-  EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
-  StartPlayingFrom(0);
+
+  {
+    WaitableMessageLoopEvent event;
+    EXPECT_CALL(mock_cb_, Display(HasTimestamp(0)))
+        .Times(1)
+        .WillOnce(RunClosure(event.GetClosure()));
+    EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+    StartPlayingFrom(0);
+    event.RunAndWait();
+  }
 
   // Advance time slightly. Frames should be dropped and we should NOT signal
   // having nothing.
   AdvanceTimeInMs(100);
 
-  // Advance time more. Now we should signal having nothing.
+  // Advance time more. Now we should signal having nothing. And put
+  // the last frame up for display.
   {
     SCOPED_TRACE("Waiting for BUFFERING_HAVE_NOTHING");
     WaitableMessageLoopEvent event;
     EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_NOTHING))
         .WillOnce(RunClosure(event.GetClosure()));
+    EXPECT_CALL(mock_cb_, Display(HasTimestamp(10))).Times(0);
+    EXPECT_CALL(mock_cb_, Display(HasTimestamp(20))).Times(0);
+    EXPECT_CALL(mock_cb_, Display(HasTimestamp(30))).Times(1);
     AdvanceTimeInMs(3000);  // Must match kTimeToDeclareHaveNothing.
     event.RunAndWait();
   }
diff --git a/media/test/data/eme_player_js/player_utils.js b/media/test/data/eme_player_js/player_utils.js
index d72bef7a..86a3e0e 100644
--- a/media/test/data/eme_player_js/player_utils.js
+++ b/media/test/data/eme_player_js/player_utils.js
@@ -85,8 +85,10 @@
 
   this.registerDefaultEventListeners(player);
   Utils.timeLog('Setting video media keys: ' + player.testConfig.keySystem);
+  var persistentState = player.testConfig.sessionToLoad ? "required"
+                                                        : "optional";
   return navigator.requestMediaKeySystemAccess(
-      player.testConfig.keySystem, [{}])
+      player.testConfig.keySystem, [{persistentState: persistentState}])
       .then(function(access) { return access.createMediaKeys(); })
       .then(function(mediaKeys) {
         return player.video.setMediaKeys(mediaKeys);
diff --git a/media/video/capture/android/imageformat_list.h b/media/video/capture/android/imageformat_list.h
deleted file mode 100644
index fe8cfb2..0000000
--- a/media/video/capture/android/imageformat_list.h
+++ /dev/null
@@ -1,18 +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.
-
-// This file intentionally does not have header guards, it's included
-// inside a macro to generate enum and a java class for the values.
-
-#ifndef DEFINE_ANDROID_IMAGEFORMAT
-#error "DEFINE_ANDROID_IMAGEFORMAT should be defined."
-#endif
-
-// Android graphics ImageFormat mapping, see reference in:
-// http://developer.android.com/reference/android/graphics/ImageFormat.html
-
-DEFINE_ANDROID_IMAGEFORMAT(ANDROID_IMAGEFORMAT_NV21, 17)
-DEFINE_ANDROID_IMAGEFORMAT(ANDROID_IMAGEFORMAT_YV12, 842094169)
-
-DEFINE_ANDROID_IMAGEFORMAT(ANDROID_IMAGEFORMAT_UNKNOWN, 0)
diff --git a/media/video/capture/android/video_capture_device_factory_android.cc b/media/video/capture/android/video_capture_device_factory_android.cc
index 828c376..9516865 100644
--- a/media/video/capture/android/video_capture_device_factory_android.cc
+++ b/media/video/capture/android/video_capture_device_factory_android.cc
@@ -71,9 +71,14 @@
     if (device_name.obj() == NULL)
       continue;
 
+    const int capture_api_type =
+        Java_VideoCaptureFactory_getCaptureApiType(env, camera_id, context);
+
     VideoCaptureDevice::Name name(
         base::android::ConvertJavaStringToUTF8(device_name),
-        base::IntToString(camera_id));
+        base::IntToString(camera_id),
+        static_cast<VideoCaptureDevice::Name::CaptureApiType>(
+            capture_api_type));
     device_names->push_back(name);
 
     DVLOG(1) << "VideoCaptureDeviceFactoryAndroid::GetDeviceNames: camera "
diff --git a/media/video/capture/fake_video_capture_device_factory.cc b/media/video/capture/fake_video_capture_device_factory.cc
index bae40ba..da4029a 100644
--- a/media/video/capture/fake_video_capture_device_factory.cc
+++ b/media/video/capture/fake_video_capture_device_factory.cc
@@ -37,6 +37,8 @@
                                   , VideoCaptureDevice::Name::AVFOUNDATION
 #elif defined(OS_WIN)
                                   , VideoCaptureDevice::Name::DIRECT_SHOW
+#elif defined(OS_ANDROID)
+                                  , VideoCaptureDevice::Name::API2_LEGACY
 #endif
     );
     device_names->push_back(name);
diff --git a/media/video/capture/video_capture_device.cc b/media/video/capture/video_capture_device.cc
index 70c24df0..dabeed3 100644
--- a/media/video/capture/video_capture_device.cc
+++ b/media/video/capture/video_capture_device.cc
@@ -58,6 +58,13 @@
       capture_api_class_(api_type),
       transport_type_(transport_type),
       is_blacklisted_(false) {}
+#elif defined(ANDROID)
+VideoCaptureDevice::Name::Name(const std::string& name,
+                               const std::string& id,
+                               const CaptureApiType api_type)
+    : device_name_(name),
+      unique_id_(id),
+      capture_api_class_(api_type) {}
 #endif
 
 VideoCaptureDevice::Name::~Name() {}
@@ -102,6 +109,25 @@
       return "Unknown API";
   }
 }
+#elif defined(OS_ANDROID)
+const char* VideoCaptureDevice::Name::GetCaptureApiTypeString() const {
+  switch(capture_api_type()) {
+    case API1:
+      return "Camera API1";
+    case API2_LEGACY:
+      return "Camera API2 Legacy";
+    case API2_FULL:
+      return "Camera API2 Full";
+    case API2_LIMITED:
+      return "Camera API2 Limited";
+    case TANGO:
+      return "Tango API";
+    case API_TYPE_UNKNOWN:
+    default:
+      NOTREACHED() << "Unknown Video Capture API type!";
+      return "Unknown API";
+  }
+}
 #endif
 
 VideoCaptureDevice::~VideoCaptureDevice() {}
diff --git a/media/video/capture/video_capture_device.h b/media/video/capture/video_capture_device.h
index 874b926..fe7756f 100644
--- a/media/video/capture/video_capture_device.h
+++ b/media/video/capture/video_capture_device.h
@@ -69,10 +69,25 @@
       USB_OR_BUILT_IN,
       OTHER_TRANSPORT
     };
+#elif defined (OS_ANDROID)
+    // Android targets Capture Api type: it can only be set on construction.
+    // Automatically generated enum to interface with Java world.
+    //
+    // A Java counterpart will be generated for this enum.
+    // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.media
+    enum CaptureApiType {
+      API1,
+      API2_LEGACY,
+      API2_FULL,
+      API2_LIMITED,
+      TANGO,
+      API_TYPE_UNKNOWN
+    };
 #endif
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
-    Name(const std::string& name,
-         const std::string& id,
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
+    defined(OS_ANDROID)
+    Name(const std::string& name, const std::string& id,
          const CaptureApiType api_type);
 #endif
 #if defined(OS_MACOSX)
@@ -108,7 +123,8 @@
       return unique_id_ < other.id();
     }
 
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
+    defined(OS_ANDROID)
     CaptureApiType capture_api_type() const {
       return capture_api_class_.capture_api_type();
     }
@@ -123,7 +139,7 @@
     void set_capabilities_id(const std::string& id) {
       capabilities_id_ = id;
     }
-#endif
+#endif  // if defined(OS_WIN)
 #if defined(OS_MACOSX)
     TransportType transport_type() const {
       return transport_type_;
@@ -134,12 +150,13 @@
     void set_is_blacklisted(bool is_blacklisted) {
       is_blacklisted_ = is_blacklisted;
     }
-#endif  // if defined(OS_WIN)
+#endif  // if defined(OS_MACOSX)
 
    private:
     std::string device_name_;
     std::string unique_id_;
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
+    defined(OS_ANDROID)
     // This class wraps the CaptureApiType to give it a by default value if not
     // initialized.
     class CaptureApiClass {
diff --git a/mojo/gpu/mojo_gles2_impl_autogen.cc b/mojo/gpu/mojo_gles2_impl_autogen.cc
index efc1f01..513c00f 100644
--- a/mojo/gpu/mojo_gles2_impl_autogen.cc
+++ b/mojo/gpu/mojo_gles2_impl_autogen.cc
@@ -11,9 +11,12 @@
 #include "mojo/gpu/mojo_gles2_impl_autogen.h"
 
 #include "base/logging.h"
+#include "third_party/mojo/src/mojo/public/c/gles2/chromium_miscellaneous.h"
+#include "third_party/mojo/src/mojo/public/c/gles2/chromium_sub_image.h"
 #include "third_party/mojo/src/mojo/public/c/gles2/chromium_sync_point.h"
 #include "third_party/mojo/src/mojo/public/c/gles2/chromium_texture_mailbox.h"
 #include "third_party/mojo/src/mojo/public/c/gles2/gles2.h"
+#include "third_party/mojo/src/mojo/public/c/gles2/occlusion_query_ext.h"
 
 namespace mojo {
 
@@ -762,8 +765,8 @@
   NOTREACHED() << "Unimplemented ShallowFinishCHROMIUM.";
 }
 void MojoGLES2Impl::ShallowFlushCHROMIUM() {
-  return static_cast<gpu::gles2::GLES2Interface*>(
-             MojoGLES2GetGLES2Interface(context_))->ShallowFlushCHROMIUM();
+  MojoGLES2MakeCurrent(context_);
+  glShallowFlushCHROMIUM();
 }
 void MojoGLES2Impl::OrderingBarrierCHROMIUM() {
   NOTREACHED() << "Unimplemented OrderingBarrierCHROMIUM.";
@@ -1194,41 +1197,40 @@
   NOTREACHED() << "Unimplemented TexStorage2DEXT.";
 }
 void MojoGLES2Impl::GenQueriesEXT(GLsizei n, GLuint* queries) {
-  return static_cast<gpu::gles2::GLES2Interface*>(
-             MojoGLES2GetGLES2Interface(context_))->GenQueriesEXT(n, queries);
+  MojoGLES2MakeCurrent(context_);
+  glGenQueriesEXT(n, queries);
 }
 void MojoGLES2Impl::DeleteQueriesEXT(GLsizei n, const GLuint* queries) {
-  return static_cast<gpu::gles2::GLES2Interface*>(
-             MojoGLES2GetGLES2Interface(context_))
-      ->DeleteQueriesEXT(n, queries);
+  MojoGLES2MakeCurrent(context_);
+  glDeleteQueriesEXT(n, queries);
 }
 GLboolean MojoGLES2Impl::IsQueryEXT(GLuint id) {
-  NOTREACHED() << "Unimplemented IsQueryEXT.";
-  return 0;
+  MojoGLES2MakeCurrent(context_);
+  return glIsQueryEXT(id);
 }
 void MojoGLES2Impl::BeginQueryEXT(GLenum target, GLuint id) {
-  return static_cast<gpu::gles2::GLES2Interface*>(
-             MojoGLES2GetGLES2Interface(context_))->BeginQueryEXT(target, id);
+  MojoGLES2MakeCurrent(context_);
+  glBeginQueryEXT(target, id);
 }
 void MojoGLES2Impl::BeginTransformFeedback(GLenum primitivemode) {
   NOTREACHED() << "Unimplemented BeginTransformFeedback.";
 }
 void MojoGLES2Impl::EndQueryEXT(GLenum target) {
-  return static_cast<gpu::gles2::GLES2Interface*>(
-             MojoGLES2GetGLES2Interface(context_))->EndQueryEXT(target);
+  MojoGLES2MakeCurrent(context_);
+  glEndQueryEXT(target);
 }
 void MojoGLES2Impl::EndTransformFeedback() {
   NOTREACHED() << "Unimplemented EndTransformFeedback.";
 }
 void MojoGLES2Impl::GetQueryivEXT(GLenum target, GLenum pname, GLint* params) {
-  NOTREACHED() << "Unimplemented GetQueryivEXT.";
+  MojoGLES2MakeCurrent(context_);
+  glGetQueryivEXT(target, pname, params);
 }
 void MojoGLES2Impl::GetQueryObjectuivEXT(GLuint id,
                                          GLenum pname,
                                          GLuint* params) {
-  return static_cast<gpu::gles2::GLES2Interface*>(
-             MojoGLES2GetGLES2Interface(context_))
-      ->GetQueryObjectuivEXT(id, pname, params);
+  MojoGLES2MakeCurrent(context_);
+  glGetQueryObjectuivEXT(id, pname, params);
 }
 void MojoGLES2Impl::InsertEventMarkerEXT(GLsizei length, const GLchar* marker) {
   NOTREACHED() << "Unimplemented InsertEventMarkerEXT.";
@@ -1304,15 +1306,13 @@
                                               GLenum format,
                                               GLenum type,
                                               GLenum access) {
-  return static_cast<gpu::gles2::GLES2Interface*>(
-             MojoGLES2GetGLES2Interface(context_))
-      ->MapTexSubImage2DCHROMIUM(target, level, xoffset, yoffset, width, height,
-                                 format, type, access);
+  MojoGLES2MakeCurrent(context_);
+  return glMapTexSubImage2DCHROMIUM(target, level, xoffset, yoffset, width,
+                                    height, format, type, access);
 }
 void MojoGLES2Impl::UnmapTexSubImage2DCHROMIUM(const void* mem) {
-  return static_cast<gpu::gles2::GLES2Interface*>(
-             MojoGLES2GetGLES2Interface(context_))
-      ->UnmapTexSubImage2DCHROMIUM(mem);
+  MojoGLES2MakeCurrent(context_);
+  glUnmapTexSubImage2DCHROMIUM(mem);
 }
 void MojoGLES2Impl::ResizeCHROMIUM(GLuint width,
                                    GLuint height,
diff --git a/mojo/services/html_viewer/BUILD.gn b/mojo/services/html_viewer/BUILD.gn
index c609bb5..e470e513 100644
--- a/mojo/services/html_viewer/BUILD.gn
+++ b/mojo/services/html_viewer/BUILD.gn
@@ -10,7 +10,8 @@
   script = "//mojo/services/html_viewer/generate_blink_resource_map.py"
   args = [
     "--pak-file",
-    rebase_path("$root_out_dir/gen/blink/public/resources/blink_resources.pak"),
+    rebase_path(
+        "$root_out_dir/gen/blink/public/resources/blink_resources_100_percent.pak"),
     "--header",
     rebase_path("$target_gen_dir/blink_resource_map.h"),
     "--cpp",
@@ -102,6 +103,7 @@
     "//third_party/mojo_services/src/surfaces/public/interfaces",
     "//ui/gfx/geometry",
     "//ui/native_theme",
+    "//ui/resources:ui_test_pak",
     "//url",
   ]
 
diff --git a/mojo/services/html_viewer/blink_input_events_type_converters.cc b/mojo/services/html_viewer/blink_input_events_type_converters.cc
index 11434b5..3599df6 100644
--- a/mojo/services/html_viewer/blink_input_events_type_converters.cc
+++ b/mojo/services/html_viewer/blink_input_events_type_converters.cc
@@ -15,6 +15,10 @@
 // Used for scrolling. This matches Firefox behavior.
 const int kPixelsPerTick = 53;
 
+double EventTimeToWebEventTime(const EventPtr& event) {
+  return base::TimeDelta::FromInternalValue(event->time_stamp).InSecondsF();
+}
+
 int EventFlagsToWebEventModifiers(int flags) {
   int modifiers = 0;
 
@@ -57,24 +61,21 @@
   return 1;
 }
 
+void SetWebMouseEventLocation(const mojo::PointerData& pointer_data,
+                              blink::WebMouseEvent* web_event) {
+  web_event->x = static_cast<int>(pointer_data.x);
+  web_event->y = static_cast<int>(pointer_data.y);
+  web_event->globalX = static_cast<int>(pointer_data.screen_x);
+  web_event->globalY = static_cast<int>(pointer_data.screen_y);
+}
+
 scoped_ptr<blink::WebInputEvent> BuildWebMouseEventFrom(const EventPtr& event) {
   scoped_ptr<blink::WebMouseEvent> web_event(new blink::WebMouseEvent);
-  web_event->x = event->location_data->in_view_location->x;
-  web_event->y = event->location_data->in_view_location->y;
 
-  // TODO(erg): Remove this if check once we can rely on screen_location
-  // actually being passed to us. As written today, getting the screen
-  // location from ui::Event objects can only be done by querying the
-  // underlying native events, so all synthesized events don't have screen
-  // locations.
-  if (!event->location_data->screen_location.is_null()) {
-    web_event->globalX = event->location_data->screen_location->x;
-    web_event->globalY = event->location_data->screen_location->y;
-  }
+  SetWebMouseEventLocation(*(event->pointer_data), web_event.get());
 
   web_event->modifiers = EventFlagsToWebEventModifiers(event->flags);
-  web_event->timeStampSeconds =
-      base::TimeDelta::FromInternalValue(event->time_stamp).InSecondsF();
+  web_event->timeStampSeconds = EventTimeToWebEventTime(event);
 
   web_event->button = blink::WebMouseEvent::ButtonNone;
   if (event->flags & mojo::EVENT_FLAGS_LEFT_MOUSE_BUTTON)
@@ -85,19 +86,13 @@
     web_event->button = blink::WebMouseEvent::ButtonRight;
 
   switch (event->action) {
-    case EVENT_TYPE_MOUSE_PRESSED:
+    case mojo::EVENT_TYPE_POINTER_DOWN:
       web_event->type = blink::WebInputEvent::MouseDown;
       break;
-    case EVENT_TYPE_MOUSE_RELEASED:
+    case mojo::EVENT_TYPE_POINTER_UP:
       web_event->type = blink::WebInputEvent::MouseUp;
       break;
-    case EVENT_TYPE_MOUSE_ENTERED:
-      web_event->type = blink::WebInputEvent::MouseLeave;
-      web_event->button = blink::WebMouseEvent::ButtonNone;
-      break;
-    case EVENT_TYPE_MOUSE_EXITED:
-    case EVENT_TYPE_MOUSE_MOVED:
-    case EVENT_TYPE_MOUSE_DRAGGED:
+    case mojo::EVENT_TYPE_POINTER_MOVE:
       web_event->type = blink::WebInputEvent::MouseMove;
       break;
     default:
@@ -115,8 +110,7 @@
   scoped_ptr<blink::WebKeyboardEvent> web_event(new blink::WebKeyboardEvent);
 
   web_event->modifiers = EventFlagsToWebInputEventModifiers(event->flags);
-  web_event->timeStampSeconds =
-      base::TimeDelta::FromInternalValue(event->time_stamp).InSecondsF();
+  web_event->timeStampSeconds = EventTimeToWebEventTime(event);
 
   switch (event->action) {
     case EVENT_TYPE_KEY_PRESSED:
@@ -149,27 +143,20 @@
   web_event->type = blink::WebInputEvent::MouseWheel;
   web_event->button = blink::WebMouseEvent::ButtonNone;
   web_event->modifiers = EventFlagsToWebEventModifiers(event->flags);
-  web_event->timeStampSeconds =
-      base::TimeDelta::FromInternalValue(event->time_stamp).InSecondsF();
+  web_event->timeStampSeconds = EventTimeToWebEventTime(event);
 
-  web_event->x = event->location_data->in_view_location->x;
-  web_event->y = event->location_data->in_view_location->y;
-
-  // TODO(erg): Remove this null check as parallel to above.
-  if (!event->location_data->screen_location.is_null()) {
-    web_event->globalX = event->location_data->screen_location->x;
-    web_event->globalY = event->location_data->screen_location->y;
-  }
+  SetWebMouseEventLocation(*(event->pointer_data), web_event.get());
 
   if ((event->flags & mojo::EVENT_FLAGS_SHIFT_DOWN) != 0 &&
-      event->wheel_data->x_offset == 0) {
-    web_event->deltaX = event->wheel_data->y_offset;
+      event->pointer_data->horizontal_wheel == 0) {
+    web_event->deltaX = event->pointer_data->horizontal_wheel;
     web_event->deltaY = 0;
   } else {
-    web_event->deltaX = event->wheel_data->x_offset;
-    web_event->deltaY = event->wheel_data->y_offset;
+    web_event->deltaX = event->pointer_data->horizontal_wheel;
+    web_event->deltaY = event->pointer_data->vertical_wheel;
   }
 
+  // TODO(sky): resole this, doesn't work for desktop.
   web_event->wheelTicksX = web_event->deltaX / kPixelsPerTick;
   web_event->wheelTicksY = web_event->deltaY / kPixelsPerTick;
 
@@ -182,22 +169,22 @@
 scoped_ptr<blink::WebInputEvent>
 TypeConverter<scoped_ptr<blink::WebInputEvent>, EventPtr>::Convert(
     const EventPtr& event) {
-  if (event->action == EVENT_TYPE_MOUSE_PRESSED ||
-      event->action == EVENT_TYPE_MOUSE_RELEASED ||
-      event->action == EVENT_TYPE_MOUSE_ENTERED ||
-      event->action == EVENT_TYPE_MOUSE_EXITED ||
-      event->action == EVENT_TYPE_MOUSE_MOVED ||
-      event->action == EVENT_TYPE_MOUSE_DRAGGED) {
-    return BuildWebMouseEventFrom(event);
-  } else if ((event->action == EVENT_TYPE_KEY_PRESSED ||
-              event->action == EVENT_TYPE_KEY_RELEASED) &&
+  if (event->action == mojo::EVENT_TYPE_POINTER_DOWN ||
+      event->action == mojo::EVENT_TYPE_POINTER_UP ||
+      event->action == mojo::EVENT_TYPE_POINTER_CANCEL ||
+      event->action == mojo::EVENT_TYPE_POINTER_MOVE) {
+    if (event->pointer_data->horizontal_wheel != 0 ||
+        event->pointer_data->vertical_wheel != 0) {
+      return BuildWebMouseWheelEventFrom(event);
+    }
+    if (event->pointer_data->kind == mojo::POINTER_KIND_MOUSE)
+      return BuildWebMouseEventFrom(event);
+  } else if ((event->action == mojo::EVENT_TYPE_KEY_PRESSED ||
+              event->action == mojo::EVENT_TYPE_KEY_RELEASED) &&
              event->key_data) {
     return BuildWebKeyboardEvent(event);
-  } else if (event->action == EVENT_TYPE_MOUSEWHEEL) {
-    return BuildWebMouseWheelEventFrom(event);
   }
-
-  return scoped_ptr<blink::WebInputEvent>();
+  return nullptr;
 }
 
 }  // namespace mojo
diff --git a/mojo/services/html_viewer/blink_platform_impl.cc b/mojo/services/html_viewer/blink_platform_impl.cc
index bf920d79..048fdfeb 100644
--- a/mojo/services/html_viewer/blink_platform_impl.cc
+++ b/mojo/services/html_viewer/blink_platform_impl.cc
@@ -157,10 +157,10 @@
   for (size_t i = 0; i < arraysize(kDataResources); ++i) {
     if (!strcmp(resource, kDataResources[i].name)) {
       int length;
-      const char* data =
+      const unsigned char* data =
           blink_resource_map_.GetResource(kDataResources[i].id, &length);
       CHECK(data != nullptr && length > 0);
-      return blink::WebData(data, length);
+      return blink::WebData(reinterpret_cast<const char*>(data), length);
     }
   }
   NOTREACHED() << "Requested resource is unavailable: " << resource;
diff --git a/mojo/services/html_viewer/blink_resource_constants.h b/mojo/services/html_viewer/blink_resource_constants.h
index c51afdfd..0091b03 100644
--- a/mojo/services/html_viewer/blink_resource_constants.h
+++ b/mojo/services/html_viewer/blink_resource_constants.h
@@ -15,6 +15,62 @@
 };
 
 const DataResource kDataResources[] = {
+    {"missingImage", IDR_BROKENIMAGE},
+    // Skipping missingImage@2x
+    {"mediaplayerPause", IDR_MEDIAPLAYER_PAUSE_BUTTON},
+    {"mediaplayerPauseHover", IDR_MEDIAPLAYER_PAUSE_BUTTON_HOVER},
+    {"mediaplayerPauseDown", IDR_MEDIAPLAYER_PAUSE_BUTTON_DOWN},
+    {"mediaplayerPlay", IDR_MEDIAPLAYER_PLAY_BUTTON},
+    {"mediaplayerPlayHover", IDR_MEDIAPLAYER_PLAY_BUTTON_HOVER},
+    {"mediaplayerPlayDown", IDR_MEDIAPLAYER_PLAY_BUTTON_DOWN},
+    {"mediaplayerPlayDisabled", IDR_MEDIAPLAYER_PLAY_BUTTON_DISABLED},
+    {"mediaplayerSoundLevel3", IDR_MEDIAPLAYER_SOUND_LEVEL3_BUTTON},
+    {"mediaplayerSoundLevel3Hover", IDR_MEDIAPLAYER_SOUND_LEVEL3_BUTTON_HOVER},
+    {"mediaplayerSoundLevel3Down", IDR_MEDIAPLAYER_SOUND_LEVEL3_BUTTON_DOWN},
+    {"mediaplayerSoundLevel2", IDR_MEDIAPLAYER_SOUND_LEVEL2_BUTTON},
+    {"mediaplayerSoundLevel2Hover", IDR_MEDIAPLAYER_SOUND_LEVEL2_BUTTON_HOVER},
+    {"mediaplayerSoundLevel2Down", IDR_MEDIAPLAYER_SOUND_LEVEL2_BUTTON_DOWN},
+    {"mediaplayerSoundLevel1", IDR_MEDIAPLAYER_SOUND_LEVEL1_BUTTON},
+    {"mediaplayerSoundLevel1Hover", IDR_MEDIAPLAYER_SOUND_LEVEL1_BUTTON_HOVER},
+    {"mediaplayerSoundLevel1Down", IDR_MEDIAPLAYER_SOUND_LEVEL1_BUTTON_DOWN},
+    {"mediaplayerSoundLevel0", IDR_MEDIAPLAYER_SOUND_LEVEL0_BUTTON},
+    {"mediaplayerSoundLevel0Hover", IDR_MEDIAPLAYER_SOUND_LEVEL0_BUTTON_HOVER},
+    {"mediaplayerSoundLevel0Down", IDR_MEDIAPLAYER_SOUND_LEVEL0_BUTTON_DOWN},
+    {"mediaplayerSoundDisabled", IDR_MEDIAPLAYER_SOUND_DISABLED},
+    {"mediaplayerSliderThumb", IDR_MEDIAPLAYER_SLIDER_THUMB},
+    {"mediaplayerSliderThumbHover", IDR_MEDIAPLAYER_SLIDER_THUMB_HOVER},
+    {"mediaplayerSliderThumbDown", IDR_MEDIAPLAYER_SLIDER_THUMB_DOWN},
+    {"mediaplayerVolumeSliderThumb", IDR_MEDIAPLAYER_VOLUME_SLIDER_THUMB},
+    {"mediaplayerVolumeSliderThumbHover",
+     IDR_MEDIAPLAYER_VOLUME_SLIDER_THUMB_HOVER},
+    {"mediaplayerVolumeSliderThumbDown",
+     IDR_MEDIAPLAYER_VOLUME_SLIDER_THUMB_DOWN},
+    {"mediaplayerVolumeSliderThumbDisabled",
+     IDR_MEDIAPLAYER_VOLUME_SLIDER_THUMB_DISABLED},
+    {"mediaplayerClosedCaption", IDR_MEDIAPLAYER_CLOSEDCAPTION_BUTTON},
+    {"mediaplayerClosedCaptionHover",
+     IDR_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_HOVER},
+    {"mediaplayerClosedCaptionDown", IDR_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_DOWN},
+    {"mediaplayerClosedCaptionDisabled",
+     IDR_MEDIAPLAYER_CLOSEDCAPTION_BUTTON_DISABLED},
+    {"mediaplayerFullscreen", IDR_MEDIAPLAYER_FULLSCREEN_BUTTON},
+    {"mediaplayerFullscreenHover", IDR_MEDIAPLAYER_FULLSCREEN_BUTTON_HOVER},
+    {"mediaplayerFullscreenDown", IDR_MEDIAPLAYER_FULLSCREEN_BUTTON_DOWN},
+    {"mediaplayerCastOff", IDR_MEDIAPLAYER_CAST_BUTTON_OFF},
+    {"mediaplayerCastOn", IDR_MEDIAPLAYER_CAST_BUTTON_ON},
+    {"mediaplayerFullscreenDisabled",
+     IDR_MEDIAPLAYER_FULLSCREEN_BUTTON_DISABLED},
+    {"mediaplayerOverlayCastOff", IDR_MEDIAPLAYER_OVERLAY_CAST_BUTTON_OFF},
+    {"mediaplayerOverlayPlay", IDR_MEDIAPLAYER_OVERLAY_PLAY_BUTTON},
+    {"panIcon", IDR_PAN_SCROLL_ICON},
+    {"searchCancel", IDR_SEARCH_CANCEL},
+    {"searchCancelPressed", IDR_SEARCH_CANCEL_PRESSED},
+    {"searchMagnifier", IDR_SEARCH_MAGNIFIER},
+    {"searchMagnifierResults", IDR_SEARCH_MAGNIFIER_RESULTS},
+    {"textAreaResizeCorner", IDR_TEXTAREA_RESIZER},
+    // Skipping "textAreaResizeCorner@2x"
+    {"generatePassword", IDR_PASSWORD_GENERATION_ICON},
+    {"generatePasswordHover", IDR_PASSWORD_GENERATION_ICON_HOVER},
     {"html.css", IDR_UASTYLE_HTML_CSS},
     {"quirks.css", IDR_UASTYLE_QUIRKS_CSS},
     {"view-source.css", IDR_UASTYLE_VIEW_SOURCE_CSS},
diff --git a/mojo/services/html_viewer/generate_blink_resource_map.py b/mojo/services/html_viewer/generate_blink_resource_map.py
index 4044154..0fdee71 100644
--- a/mojo/services/html_viewer/generate_blink_resource_map.py
+++ b/mojo/services/html_viewer/generate_blink_resource_map.py
@@ -17,9 +17,6 @@
   print 'ImportError: ', e
   sys.exit(-1)
 
-def is_ascii(s):
-  return all(ord(c) < 128 for c in s)
-
 header_template = \
 """// Copyright 2015 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
@@ -35,11 +32,11 @@
 class BlinkResourceMap {
  public:
   BlinkResourceMap();
-  const char* GetResource(int id, int* length);
+  const unsigned char* GetResource(int id, int* length);
 
  private:
   struct ResourceEntry {
-    const char* data;
+    const unsigned char* data;
     int length;
 
     ResourceEntry()
@@ -47,7 +44,7 @@
       , length(0) {
     }
 
-    ResourceEntry(const char* data, int length)
+    ResourceEntry(const unsigned char* data, int length)
       : data(data)
       , length(length) {
     }
@@ -77,7 +74,7 @@
   $map_initializer
 }
 
-const char* BlinkResourceMap::GetResource(int id, int* length)
+const unsigned char* BlinkResourceMap::GetResource(int id, int* length)
 {
   ResourceMap::iterator it = resources_.find(id);
   if (it == resources_.end()) {
@@ -117,14 +114,12 @@
   definitions = []
 
   for (resId, data) in pak_contents.resources.iteritems():
-    if not is_ascii(data):
-      continue
     resourceIds.append(resId)
     hex_values = ['0x{0:02x}'.format(ord(char)) for char in data]
     f = lambda A, n=12: [A[i:i+n] for i in range(0, len(A), n)]
     hex_values_string = ',\n    '.join(', '.join(x) for x in f(hex_values))
     cpp_definition = \
-        'const char kResource%s[%d] = {\n    %s \n};' % \
+        'const unsigned char kResource%s[%d] = {\n    %s \n};' % \
         (str(resId), len(hex_values), hex_values_string)
     definitions.append(cpp_definition)
 
diff --git a/mojo/services/html_viewer/webscheduler_impl.cc b/mojo/services/html_viewer/webscheduler_impl.cc
index 14b20aa..f414cb6 100644
--- a/mojo/services/html_viewer/webscheduler_impl.cc
+++ b/mojo/services/html_viewer/webscheduler_impl.cc
@@ -21,8 +21,8 @@
 }
 
 void WebSchedulerImpl::postIdleTask(const blink::WebTraceLocation& web_location,
-                                    blink::WebScheduler::IdleTask* task) {
-  scoped_ptr<blink::WebScheduler::IdleTask> scoped_task(task);
+                                    blink::WebThread::IdleTask* task) {
+  scoped_ptr<blink::WebThread::IdleTask> scoped_task(task);
   tracked_objects::Location location(web_location.functionName(),
                                      web_location.fileName(), -1, nullptr);
   task_runner_->PostTask(location, base::Bind(&WebSchedulerImpl::RunIdleTask,
@@ -54,7 +54,7 @@
 
 // static
 void WebSchedulerImpl::RunIdleTask(
-    scoped_ptr<blink::WebScheduler::IdleTask> task) {
+    scoped_ptr<blink::WebThread::IdleTask> task) {
   // TODO(davemoore) Implement idle scheduling.
   task->run(0);
 }
diff --git a/mojo/services/html_viewer/webscheduler_impl.h b/mojo/services/html_viewer/webscheduler_impl.h
index af647f9..bd2af6f 100644
--- a/mojo/services/html_viewer/webscheduler_impl.h
+++ b/mojo/services/html_viewer/webscheduler_impl.h
@@ -20,14 +20,14 @@
  private:
   // blink::WebScheduler overrides.
   virtual void postIdleTask(const blink::WebTraceLocation& location,
-                    blink::WebScheduler::IdleTask* task);
+                            blink::WebThread::IdleTask* task);
   virtual void postLoadingTask(const blink::WebTraceLocation& location,
                        blink::WebThread::Task* task);
   virtual void postTimerTask(const blink::WebTraceLocation& location,
                              blink::WebThread::Task* task,
                              long long delayMs);
 
-  static void RunIdleTask(scoped_ptr<blink::WebScheduler::IdleTask> task);
+  static void RunIdleTask(scoped_ptr<blink::WebThread::IdleTask> task);
   static void RunTask(scoped_ptr<blink::WebThread::Task> task);
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/native_client_sdk/doc_generated/sdk/release-notes.html b/native_client_sdk/doc_generated/sdk/release-notes.html
index 916c7f2..7ef7f66 100644
--- a/native_client_sdk/doc_generated/sdk/release-notes.html
+++ b/native_client_sdk/doc_generated/sdk/release-notes.html
@@ -10,6 +10,8 @@
 <li>The C11/C++11 <code>acquire</code>, <code>release</code>, and <code>acq_rel</code> memory orders are now
 generated by default. The in-browser Chrome 42 translator supports them, the
 SDK can therefore generate them.</li>
+<li>Fix a <a class="reference external" href="https://code.google.com/p/chromium/issues/detail?id=460432">code generation bug on ARM</a> when dealing with 16-bit load/store and
+<code>bswap</code> which led to a NaCl validation failure.</li>
 </ul>
 <h2 id="chrome-pepper-42-20-february-2015">Chrome/Pepper 42 (20 February 2015)</h2>
 <h3 id="sdk">SDK</h3>
diff --git a/native_client_sdk/src/doc/sdk/release-notes.rst b/native_client_sdk/src/doc/sdk/release-notes.rst
index 702d0d44..8fbf326 100644
--- a/native_client_sdk/src/doc/sdk/release-notes.rst
+++ b/native_client_sdk/src/doc/sdk/release-notes.rst
@@ -17,6 +17,10 @@
 * The C11/C++11 ``acquire``, ``release``, and ``acq_rel`` memory orders are now
   generated by default. The in-browser Chrome 42 translator supports them, the
   SDK can therefore generate them.
+* Fix a `code generation bug on ARM`_ when dealing with 16-bit load/store and
+  ``bswap`` which led to a NaCl validation failure.
+
+.. _`code generation bug on ARM`: https://code.google.com/p/chromium/issues/detail?id=460432
 
 Chrome/Pepper 42 (20 February 2015)
 ===================================
diff --git a/native_client_sdk/src/libraries/nacl_io/path.cc b/native_client_sdk/src/libraries/nacl_io/path.cc
index 21965607..e75f068 100644
--- a/native_client_sdk/src/libraries/nacl_io/path.cc
+++ b/native_client_sdk/src/libraries/nacl_io/path.cc
@@ -170,7 +170,7 @@
     }
   }
 
-  if (pstart > pend)
+  if (slashes < start || pstart > pend)
     return std::string();
 
   return std::string(pstart, pend - pstart);
diff --git a/native_client_sdk/src/libraries/ppapi/library.dsc b/native_client_sdk/src/libraries/ppapi/library.dsc
index a982410e..f52d762 100644
--- a/native_client_sdk/src/libraries/ppapi/library.dsc
+++ b/native_client_sdk/src/libraries/ppapi/library.dsc
@@ -100,7 +100,6 @@
         'ppb_file_chooser_dev.h',
         'ppb_font_dev.h',
         'ppb_memory_dev.h',
-        'ppb_messaging_deprecated.h',
         'ppb_opengles2ext_dev.h',
         'ppb_printing_dev.h',
         'ppb_trace_event_dev.h',
diff --git a/native_client_sdk/src/tests/nacl_io_test/path_test.cc b/native_client_sdk/src/tests/nacl_io_test/path_test.cc
index b7b82cc..630e0e8 100644
--- a/native_client_sdk/src/tests/nacl_io_test/path_test.cc
+++ b/native_client_sdk/src/tests/nacl_io_test/path_test.cc
@@ -70,6 +70,7 @@
   EXPECT_EQ("/", p.Range(0, 1));
   EXPECT_EQ("foo", p.Range(1, 2));
   EXPECT_EQ("/foo", p.Range(0, 2));
+  EXPECT_EQ("", p.Range(2, 2));
   EXPECT_EQ("/", p.Parent().Join());
 }
 
@@ -85,6 +86,7 @@
   EXPECT_EQ("foo", p.Range(0, 1));
   EXPECT_EQ("bar", p.Range(1, 2));
   EXPECT_EQ("foo/bar", p.Range(0, 2));
+  EXPECT_EQ("", p.Range(2, 2));
   EXPECT_EQ("foo", p.Parent().Join());
 }
 
@@ -185,6 +187,9 @@
   EXPECT_EQ("relative/path", p.Range(1, 3));
 
   EXPECT_EQ("path", p.Range(2, 3));
+
+  EXPECT_EQ("path", p.Range(2, 100));
+  EXPECT_EQ("", p.Range(42, 67));
 }
 
 TEST(PathTest, Range_Absolute) {
@@ -203,6 +208,9 @@
   EXPECT_EQ("absolute/path", p.Range(2, 4));
 
   EXPECT_EQ("path", p.Range(3, 4));
+
+  EXPECT_EQ("absolute/path", p.Range(2, 100));
+  EXPECT_EQ("", p.Range(42, 67));
 }
 
 TEST(PathTest, Assign) {
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 49373d8..0836eb2 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1139,40 +1139,24 @@
     ]
   }
 
-  source_set("quic_base") {
+  source_set("epoll_quic_tools") {
     sources = [
       "tools/quic/quic_client.cc",
       "tools/quic/quic_client.h",
-      "tools/quic/quic_client_session.cc",
-      "tools/quic/quic_client_session.h",
       "tools/quic/quic_default_packet_writer.cc",
       "tools/quic/quic_default_packet_writer.h",
-      "tools/quic/quic_dispatcher.cc",
-      "tools/quic/quic_dispatcher.h",
       "tools/quic/quic_epoll_clock.cc",
       "tools/quic/quic_epoll_clock.h",
       "tools/quic/quic_epoll_connection_helper.cc",
       "tools/quic/quic_epoll_connection_helper.h",
-      "tools/quic/quic_in_memory_cache.cc",
-      "tools/quic/quic_in_memory_cache.h",
       "tools/quic/quic_packet_reader.cc",
       "tools/quic/quic_packet_reader.h",
       "tools/quic/quic_packet_writer_wrapper.cc",
       "tools/quic/quic_packet_writer_wrapper.h",
-      "tools/quic/quic_per_connection_packet_writer.cc",
-      "tools/quic/quic_per_connection_packet_writer.h",
       "tools/quic/quic_server.cc",
       "tools/quic/quic_server.h",
-      "tools/quic/quic_server_session.cc",
-      "tools/quic/quic_server_session.h",
       "tools/quic/quic_socket_utils.cc",
       "tools/quic/quic_socket_utils.h",
-      "tools/quic/quic_spdy_client_stream.cc",
-      "tools/quic/quic_spdy_client_stream.h",
-      "tools/quic/quic_spdy_server_stream.cc",
-      "tools/quic/quic_spdy_server_stream.h",
-      "tools/quic/quic_time_wait_list_manager.cc",
-      "tools/quic/quic_time_wait_list_manager.h",
     ]
     deps = [
       ":balsa",
@@ -1186,14 +1170,29 @@
     ]
   }
 
-  executable("quic_client") {
+  executable("epoll_quic_client") {
     sources = [
       "tools/quic/quic_client_bin.cc",
     ]
     deps = [
       ":balsa",
       ":epoll_server",
-      ":quic_base",
+      ":epoll_quic_tools",
+      ":net",
+      ":simple_quic_tools",
+      "//base",
+      "//third_party/boringssl",
+    ]
+  }
+
+  executable("epoll_quic_server") {
+    sources = [
+      "tools/quic/quic_server_bin.cc",
+    ]
+    deps = [
+      ":balsa",
+      ":epoll_server",
+      ":epoll_quic_tools",
       ":net",
       ":simple_quic_tools",
       "//base",
@@ -1237,42 +1236,32 @@
   }
 }
 
-# This source_set is defined even if no build targets need it,
-# such as when all dependees are not buildable for the chosen OS.
-source_set("quic_tools") {
-  sources = [
-    "quic/quic_dispatcher.cc",
-    "quic/quic_dispatcher.h",
-    "quic/quic_in_memory_cache.cc",
-    "quic/quic_in_memory_cache.h",
-    "quic/quic_per_connection_packet_writer.cc",
-    "quic/quic_per_connection_packet_writer.h",
-    "quic/quic_server.cc",
-    "quic/quic_server.h",
-    "quic/quic_server_packet_writer.cc",
-    "quic/quic_server_packet_writer.h",
-    "quic/quic_server_session.cc",
-    "quic/quic_server_session.h",
-    "quic/quic_spdy_server_stream.cc",
-    "quic/quic_spdy_server_stream.h",
-    "quic/quic_time_wait_list_manager.cc",
-    "quic/quic_time_wait_list_manager.h",
-  ]
-  deps = [
-    ":net",
-    "//base",
-    "//base/third_party/dynamic_annotations",
-    "//url",
-  ]
-}
 source_set("simple_quic_tools") {
   sources = [
+    "tools/quic/quic_client_session.cc",
+    "tools/quic/quic_client_session.h",
+    "tools/quic/quic_dispatcher.cc",
+    "tools/quic/quic_dispatcher.h",
+    "tools/quic/quic_in_memory_cache.cc",
+    "tools/quic/quic_in_memory_cache.h",
+    "tools/quic/quic_per_connection_packet_writer.cc",
+    "tools/quic/quic_per_connection_packet_writer.h",
+    "tools/quic/quic_server_session.cc",
+    "tools/quic/quic_server_session.h",
     "tools/quic/quic_simple_client.cc",
     "tools/quic/quic_simple_client.h",
-    "tools/quic/quic_simple_client_session.cc",
-    "tools/quic/quic_simple_client_session.h",
-    "tools/quic/quic_simple_client_stream.cc",
-    "tools/quic/quic_simple_client_stream.h",
+    "tools/quic/quic_simple_per_connection_packet_writer.cc",
+    "tools/quic/quic_simple_per_connection_packet_writer.h",
+    "tools/quic/quic_simple_server.cc",
+    "tools/quic/quic_simple_server.h",
+    "tools/quic/quic_simple_server_packet_writer.cc",
+    "tools/quic/quic_simple_server_packet_writer.h",
+    "tools/quic/quic_spdy_client_stream.cc",
+    "tools/quic/quic_spdy_client_stream.h",
+    "tools/quic/quic_spdy_server_stream.cc",
+    "tools/quic/quic_spdy_server_stream.h",
+    "tools/quic/quic_time_wait_list_manager.cc",
+    "tools/quic/quic_time_wait_list_manager.h",
     "tools/quic/synchronous_host_resolver.cc",
     "tools/quic/synchronous_host_resolver.h",
   ]
@@ -1284,8 +1273,7 @@
   ]
 }
 
-executable("simple_quic_client") {
-  testonly = true
+executable("quic_client") {
   sources = [
     "tools/quic/quic_simple_client_bin.cc",
   ]
@@ -1297,6 +1285,18 @@
   ]
 }
 
+executable("quic_server") {
+  sources = [
+    "tools/quic/quic_simple_server_bin.cc",
+  ]
+  deps = [
+    ":net",
+    ":simple_quic_tools",
+    "//base",
+    "//third_party/boringssl",
+  ]
+}
+
 # TODO(GYP) make this compile on Android, we need some native test deps done.
 # TODO(GYP) Also doesn't work on Windows; dependency on boringssl is wrong.
 # TODO(GYP) Also doesn't work on Mac, need to figure out why not.
@@ -1312,7 +1312,7 @@
       ":extras",
       ":http_server",
       ":net",
-      ":quic_tools",
+      ":epoll_quic_tools",
       ":simple_quic_tools",
       ":test_support",
       "//base",
@@ -1338,7 +1338,7 @@
         ":balsa",
         ":epoll_server",
         ":flip_in_mem_edsm_server_base",
-        ":quic_base",
+        ":epoll_quic_tools",
       ]
     }
 
@@ -1598,20 +1598,6 @@
   }
 }  # !is_android && !is_win && !is_mac
 
-if (!is_android && !is_win) {
-  executable("quic_server") {
-    sources = [
-      "quic/quic_server_bin.cc",
-    ]
-    deps = [
-      ":quic_tools",
-      ":net",
-      "//base",
-      "//third_party/boringssl",
-    ]
-  }
-}
-
 executable("net_perftests") {
   testonly = true
   sources = [
diff --git a/net/base/filename_util.cc b/net/base/filename_util.cc
index 6573c22..c9d5fe4 100644
--- a/net/base/filename_util.cc
+++ b/net/base/filename_util.cc
@@ -13,7 +13,6 @@
 #include "base/threading/thread_restrictions.h"
 #include "net/base/escape.h"
 #include "net/base/filename_util_internal.h"
-#include "net/base/mime_util.h"
 #include "net/base/net_string_util.h"
 #include "net/http/http_content_disposition.h"
 #include "url/gurl.h"
diff --git a/net/base/layered_network_delegate.cc b/net/base/layered_network_delegate.cc
index 9fd4973..95e4236 100644
--- a/net/base/layered_network_delegate.cc
+++ b/net/base/layered_network_delegate.cc
@@ -252,15 +252,6 @@
     const GURL& first_party_for_cookies) const {
 }
 
-bool LayeredNetworkDelegate::OnFirstPartyOnlyCookieExperimentEnabled() const {
-  OnFirstPartyOnlyCookieExperimentEnabledInternal();
-  return nested_network_delegate_->FirstPartyOnlyCookieExperimentEnabled();
-}
-
-void LayeredNetworkDelegate::OnFirstPartyOnlyCookieExperimentEnabledInternal()
-    const {
-}
-
 bool LayeredNetworkDelegate::
     OnCancelURLRequestWithPolicyViolatingReferrerHeader(
         const URLRequest& request,
diff --git a/net/base/layered_network_delegate.h b/net/base/layered_network_delegate.h
index 87c1d86..e4b6411 100644
--- a/net/base/layered_network_delegate.h
+++ b/net/base/layered_network_delegate.h
@@ -80,7 +80,6 @@
   bool OnCanThrottleRequest(const URLRequest& request) const final;
   bool OnCanEnablePrivacyMode(const GURL& url,
                               const GURL& first_party_for_cookies) const final;
-  bool OnFirstPartyOnlyCookieExperimentEnabled() const final;
   bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(
       const URLRequest& request,
       const GURL& target_url,
@@ -153,8 +152,6 @@
       const GURL& url,
       const GURL& first_party_for_cookies) const;
 
-  virtual void OnFirstPartyOnlyCookieExperimentEnabledInternal() const;
-
   virtual void OnCancelURLRequestWithPolicyViolatingReferrerHeaderInternal(
       const URLRequest& request,
       const GURL& target_url,
diff --git a/net/base/mime_sniffer.cc b/net/base/mime_sniffer.cc
index 0d1f1169..45e810f 100644
--- a/net/base/mime_sniffer.cc
+++ b/net/base/mime_sniffer.cc
@@ -100,7 +100,6 @@
 #include "base/logging.h"
 #include "base/metrics/histogram.h"
 #include "base/strings/string_util.h"
-#include "net/base/mime_util.h"
 #include "url/gurl.h"
 
 namespace net {
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index 8460f6df..6f152c9ce 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -340,6 +340,12 @@
 // minimum fallback version, and thus fallback failed.
 NET_ERROR(SSL_FALLBACK_BEYOND_MINIMUM_VERSION, -165)
 
+// Resolving a hostname to an IP address list included the IPv4 address
+// "127.0.53.53". This is a special IP address which ICANN has recommended to
+// indicate there was a name collision, and alert admins to a potential
+// problem.
+NET_ERROR(ICANN_NAME_COLLISION, -166)
+
 // Certificate error codes
 //
 // The values of certificate error codes must be consecutive.
diff --git a/net/base/net_util.cc b/net/base/net_util.cc
index 3b49dff..c893e616 100644
--- a/net/base/net_util.cc
+++ b/net/base/net_util.cc
@@ -981,10 +981,9 @@
 }
 
 bool IsLocalhost(const std::string& host) {
-  if (host == "localhost" ||
-      host == "localhost.localdomain" ||
-      host == "localhost6" ||
-      host == "localhost6.localdomain6")
+  if (host == "localhost" || host == "localhost.localdomain" ||
+      host == "localhost6" || host == "localhost6.localdomain6" ||
+      IsLocalhostTLD(host))
     return true;
 
   IPAddressNumber ip_number;
@@ -1014,6 +1013,24 @@
   return false;
 }
 
+bool IsLocalhostTLD(const std::string& host) {
+  const char kLocalhostTLD[] = ".localhost";
+  const size_t kLocalhostTLDLength = arraysize(kLocalhostTLD) - 1;
+
+  if (host.empty())
+    return false;
+
+  size_t host_len = host.size();
+  if (*host.rbegin() == '.')
+    --host_len;
+  if (host_len < kLocalhostTLDLength)
+    return false;
+
+  const char* host_suffix = host.data() + host_len - kLocalhostTLDLength;
+  return base::strncasecmp(host_suffix, kLocalhostTLD, kLocalhostTLDLength) ==
+         0;
+}
+
 NetworkInterface::NetworkInterface()
     : type(NetworkChangeNotifier::CONNECTION_UNKNOWN), prefix_length(0) {
 }
diff --git a/net/base/net_util.h b/net/base/net_util.h
index c7d1715..e40ec0c 100644
--- a/net/base/net_util.h
+++ b/net/base/net_util.h
@@ -441,6 +441,8 @@
 // machine.
 NET_EXPORT_PRIVATE bool IsLocalhost(const std::string& host);
 
+NET_EXPORT_PRIVATE bool IsLocalhostTLD(const std::string& host);
+
 // A subset of IP address attributes which are actionable by the
 // application layer. Currently unimplemented for all hosts;
 // IP_ADDRESS_ATTRIBUTE_NONE is always returned.
diff --git a/net/base/net_util_unittest.cc b/net/base/net_util_unittest.cc
index fa2a60b3..cada1a9a 100644
--- a/net/base/net_util_unittest.cc
+++ b/net/base/net_util_unittest.cc
@@ -803,6 +803,7 @@
   EXPECT_TRUE(net::IsLocalhost("127.255.0.0"));
   EXPECT_TRUE(net::IsLocalhost("::1"));
   EXPECT_TRUE(net::IsLocalhost("0:0:0:0:0:0:0:1"));
+  EXPECT_TRUE(net::IsLocalhost("foo.localhost"));
 
   EXPECT_FALSE(net::IsLocalhost("localhostx"));
   EXPECT_FALSE(net::IsLocalhost("foo.localdomain"));
@@ -816,6 +817,16 @@
   EXPECT_FALSE(net::IsLocalhost("0:0:0:0:1:0:0:1"));
   EXPECT_FALSE(net::IsLocalhost("::1:1"));
   EXPECT_FALSE(net::IsLocalhost("0:0:0:0:0:0:0:0:1"));
+  EXPECT_FALSE(net::IsLocalhost("foo.localhost.com"));
+  EXPECT_FALSE(net::IsLocalhost("foo.localhoste"));
+}
+
+TEST(NetUtilTest, IsLocalhostTLD) {
+  EXPECT_TRUE(net::IsLocalhostTLD("foo.localhost"));
+  EXPECT_TRUE(net::IsLocalhostTLD("foo.localhost."));
+  EXPECT_FALSE(net::IsLocalhostTLD("foo.localhos"));
+  EXPECT_FALSE(net::IsLocalhostTLD("foo.localhost.com"));
+  EXPECT_FALSE(net::IsLocalhost("foo.localhoste"));
 }
 
 // Verify GetNetworkList().
diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc
index 1f59d03..b6858da 100644
--- a/net/base/network_delegate.cc
+++ b/net/base/network_delegate.cc
@@ -229,10 +229,6 @@
   return OnCanEnablePrivacyMode(url, first_party_for_cookies);
 }
 
-bool NetworkDelegate::FirstPartyOnlyCookieExperimentEnabled() const {
-  return OnFirstPartyOnlyCookieExperimentEnabled();
-}
-
 bool NetworkDelegate::CancelURLRequestWithPolicyViolatingReferrerHeader(
     const URLRequest& request,
     const GURL& target_url,
diff --git a/net/base/network_delegate.h b/net/base/network_delegate.h
index 9ea8202..5af2ac14 100644
--- a/net/base/network_delegate.h
+++ b/net/base/network_delegate.h
@@ -104,10 +104,6 @@
   bool CanEnablePrivacyMode(const GURL& url,
                             const GURL& first_party_for_cookies) const;
 
-  // TODO(mkwst): Remove this once we decide whether or not we wish to ship
-  // first-party cookies. https://crbug.com/459154
-  bool FirstPartyOnlyCookieExperimentEnabled() const;
-
   bool CancelURLRequestWithPolicyViolatingReferrerHeader(
       const URLRequest& request,
       const GURL& target_url,
@@ -270,13 +266,6 @@
       const GURL& url,
       const GURL& first_party_for_cookies) const = 0;
 
-  // Returns true if the embedder has enabled the "first-party" cookie
-  // experiment, and false otherwise.
-  //
-  // TODO(mkwst): Remove this once we decide whether or not we wish to ship
-  // first-party cookies. https://crbug.com/459154
-  virtual bool OnFirstPartyOnlyCookieExperimentEnabled() const = 0;
-
   // Called when the |referrer_url| for requesting |target_url| during handling
   // of the |request| is does not comply with the referrer policy (e.g. a
   // secure referrer for an insecure initial target).
diff --git a/net/base/network_delegate_impl.cc b/net/base/network_delegate_impl.cc
index c4bdd04..f10bee3 100644
--- a/net/base/network_delegate_impl.cc
+++ b/net/base/network_delegate_impl.cc
@@ -105,10 +105,6 @@
   return false;
 }
 
-bool NetworkDelegateImpl::OnFirstPartyOnlyCookieExperimentEnabled() const {
-  return false;
-}
-
 bool NetworkDelegateImpl::OnCancelURLRequestWithPolicyViolatingReferrerHeader(
     const URLRequest& request,
     const GURL& target_url,
diff --git a/net/base/network_delegate_impl.h b/net/base/network_delegate_impl.h
index 8ad483ec..aa87bd4 100644
--- a/net/base/network_delegate_impl.h
+++ b/net/base/network_delegate_impl.h
@@ -184,13 +184,6 @@
       const GURL& url,
       const GURL& first_party_for_cookies) const override;
 
-  // Returns true if the embedder has enabled the "first-party" cookie
-  // experiment, and false otherwise.
-  //
-  // TODO(mkwst): Remove this once we decide whether or not we wish to ship
-  // first-party cookies. https://crbug.com/459154
-  bool OnFirstPartyOnlyCookieExperimentEnabled() const override;
-
   // Called when the |referrer_url| for requesting |target_url| during handling
   // of the |request| is does not comply with the referrer policy (e.g. a
   // secure referrer for an insecure initial target).
diff --git a/net/cert/cert_policy_enforcer.cc b/net/cert/cert_policy_enforcer.cc
index 680c2c6d..652f7b9c 100644
--- a/net/cert/cert_policy_enforcer.cc
+++ b/net/cert/cert_policy_enforcer.cc
@@ -43,20 +43,34 @@
 #endif
 }
 
-uint32_t ApproximateMonthDifference(const base::Time& start,
-                                    const base::Time& end) {
+// Returns a rounded-down months difference of |start| and |end|,
+// together with an indication of whether the last month was
+// a full month, because the range starts specified in the policy
+// are not consistent in terms of including the range start value.
+void RoundedDownMonthDifference(const base::Time& start,
+                                const base::Time& end,
+                                size_t* rounded_months_difference,
+                                bool* has_partial_month) {
+  DCHECK(rounded_months_difference);
+  DCHECK(has_partial_month);
   base::Time::Exploded exploded_start;
   base::Time::Exploded exploded_expiry;
   start.UTCExplode(&exploded_start);
   end.UTCExplode(&exploded_expiry);
+  if (end < start) {
+    *rounded_months_difference = 0;
+    *has_partial_month = false;
+  }
+
+  *has_partial_month = true;
   uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 +
                         (exploded_expiry.month - exploded_start.month);
+  if (exploded_expiry.day_of_month < exploded_start.day_of_month)
+    --month_diff;
+  else if (exploded_expiry.day_of_month == exploded_start.day_of_month)
+    *has_partial_month = false;
 
-  // Add any remainder as a full month.
-  if (exploded_expiry.day_of_month > exploded_start.day_of_month)
-    ++month_diff;
-
-  return month_diff;
+  *rounded_months_difference = month_diff;
 }
 
 bool HasRequiredNumberOfSCTs(const X509Certificate& cert,
@@ -81,18 +95,20 @@
     return false;
   }
 
-  uint32_t expiry_in_months_approx =
-      ApproximateMonthDifference(cert.valid_start(), cert.valid_expiry());
+  size_t lifetime;
+  bool has_partial_month;
+  RoundedDownMonthDifference(cert.valid_start(), cert.valid_expiry(), &lifetime,
+                             &has_partial_month);
 
   // For embedded SCTs, if the certificate has the number of SCTs specified in
   // table 1 of the "Qualifying Certificate" section of the CT/EV policy, then
   // it qualifies.
   size_t num_required_embedded_scts;
-  if (expiry_in_months_approx > 39) {
+  if (lifetime > 39 || (lifetime == 39 && has_partial_month)) {
     num_required_embedded_scts = 5;
-  } else if (expiry_in_months_approx > 27) {
+  } else if (lifetime > 27 || (lifetime == 27 && has_partial_month)) {
     num_required_embedded_scts = 4;
-  } else if (expiry_in_months_approx >= 15) {
+  } else if (lifetime >= 15) {
     num_required_embedded_scts = 3;
   } else {
     num_required_embedded_scts = 2;
diff --git a/net/cert/cert_policy_enforcer_unittest.cc b/net/cert/cert_policy_enforcer_unittest.cc
index f920963f..bc4881c 100644
--- a/net/cert/cert_policy_enforcer_unittest.cc
+++ b/net/cert/cert_policy_enforcer_unittest.cc
@@ -67,6 +67,29 @@
     }
   }
 
+  void CheckCertificateCompliesWithExactNumberOfEmbeddedSCTs(
+      const base::Time& start,
+      const base::Time& end,
+      size_t required_scts) {
+    scoped_refptr<X509Certificate> cert(
+        new X509Certificate("subject", "issuer", start, end));
+    ct::CTVerifyResult result;
+    for (size_t i = 0; i < required_scts - 1; ++i) {
+      FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED,
+                                 1, &result);
+      EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(
+          cert.get(), nullptr, result, BoundNetLog()))
+          << " for: " << (end - start).InDays() << " and " << required_scts
+          << " scts=" << result.verified_scts.size() << " i=" << i;
+    }
+    FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+                               &result);
+    EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(
+        cert.get(), nullptr, result, BoundNetLog()))
+        << " for: " << (end - start).InDays() << " and " << required_scts
+        << " scts=" << result.verified_scts.size();
+  }
+
  protected:
   scoped_ptr<CertPolicyEnforcer> policy_enforcer_;
   scoped_refptr<X509Certificate> chain_;
@@ -140,31 +163,45 @@
 
 TEST_F(CertPolicyEnforcerTest,
        ConformsToPolicyExactNumberOfSCTsForValidityPeriod) {
-  // Test multiple validity periods: Over 27 months, Over 15 months (but less
-  // than 27 months),
-  // Less than 15 months.
-  const size_t validity_period[] = {12, 19, 30, 50};
-  const size_t needed_scts[] = {2, 3, 4, 5};
+  // Test multiple validity periods
+  const struct TestData {
+    base::Time validity_start;
+    base::Time validity_end;
+    size_t scts_required;
+  } kTestData[] = {{// Cert valid for 14 months, needs 2 SCTs.
+                    base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
+                    base::Time::FromUTCExploded({2016, 6, 0, 6, 11, 25, 0, 0}),
+                    2},
+                   {// Cert valid for exactly 15 months, needs 3 SCTs.
+                    base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
+                    base::Time::FromUTCExploded({2016, 6, 0, 25, 11, 25, 0, 0}),
+                    3},
+                   {// Cert valid for over 15 months, needs 3 SCTs.
+                    base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
+                    base::Time::FromUTCExploded({2016, 6, 0, 27, 11, 25, 0, 0}),
+                    3},
+                   {// Cert valid for exactly 27 months, needs 3 SCTs.
+                    base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
+                    base::Time::FromUTCExploded({2017, 6, 0, 25, 11, 25, 0, 0}),
+                    3},
+                   {// Cert valid for over 27 months, needs 4 SCTs.
+                    base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
+                    base::Time::FromUTCExploded({2017, 6, 0, 28, 11, 25, 0, 0}),
+                    4},
+                   {// Cert valid for exactly 39 months, needs 4 SCTs.
+                    base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
+                    base::Time::FromUTCExploded({2018, 6, 0, 25, 11, 25, 0, 0}),
+                    4},
+                   {// Cert valid for over 39 months, needs 5 SCTs.
+                    base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
+                    base::Time::FromUTCExploded({2018, 6, 0, 27, 11, 25, 0, 0}),
+                    5}};
 
-  for (int i = 0; i < 3; ++i) {
-    size_t curr_validity = validity_period[i];
-    scoped_refptr<X509Certificate> cert(new X509Certificate(
-        "subject", "issuer", base::Time::Now(),
-        base::Time::Now() + base::TimeDelta::FromDays(31 * curr_validity)));
-    size_t curr_required_scts = needed_scts[i];
-    ct::CTVerifyResult result;
-    for (size_t j = 0; j < curr_required_scts - 1; ++j) {
-      FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED,
-                                 1, &result);
-      EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(
-          cert.get(), nullptr, result, BoundNetLog()))
-          << " for: " << curr_validity << " and " << curr_required_scts
-          << " scts=" << result.verified_scts.size() << " j=" << j;
-    }
-    FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
-                               &result);
-    EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(
-        cert.get(), nullptr, result, BoundNetLog()));
+  for (size_t i = 0; i < arraysize(kTestData); ++i) {
+    SCOPED_TRACE(i);
+    CheckCertificateCompliesWithExactNumberOfEmbeddedSCTs(
+        kTestData[i].validity_start, kTestData[i].validity_end,
+        kTestData[i].scts_required);
   }
 }
 
diff --git a/net/data/quic_in_memory_cache_data/quic-datatesturl.com/index.html b/net/data/quic_in_memory_cache_data/quic-datatesturl.com/index.html
index 041f56d..71aaeb0 100644
--- a/net/data/quic_in_memory_cache_data/quic-datatesturl.com/index.html
+++ b/net/data/quic_in_memory_cache_data/quic-datatesturl.com/index.html
@@ -7,7 +7,7 @@
 Vary: Accept-Encoding
 Content-Encoding: None 
 Keep-Alive: timeout=5, max=100
-Connection: Keep-Alive
+Connection: close
 Content-Type: text/html
 X-Original-Url: http://quic.test.url/index.html
 
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 6c0a1549..9aa5016 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -72,6 +72,8 @@
 // Minimum TTL for successful resolutions with DnsTask.
 const unsigned kMinimumTTLSeconds = kCacheEntryTTLSeconds;
 
+const char kLocalhost[] = "localhost.";
+
 // We use a separate histogram name for each platform to facilitate the
 // display of error codes by their symbolic name (since each platform has
 // different mappings).
@@ -144,6 +146,17 @@
   RESOLVE_STATUS_MAX
 };
 
+// ICANN uses this localhost address to indicate a name collision.
+//
+// The policy in Chromium is to fail host resolving if it resolves to
+// this special address.
+//
+// Not however that IP literals are exempt from this policy, so it is still
+// possible to navigate to http://127.0.53.53/ directly.
+//
+// For more details: https://www.icann.org/news/announcement-2-2014-08-01-en
+const unsigned char kIcanNameCollisionIp[] = {127, 0, 53, 53};
+
 void UmaAsyncDnsResolveStatus(DnsResolveStatus result) {
   UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ResolveStatus",
                             result,
@@ -663,6 +676,17 @@
                                                &results,
                                                &os_error);
 
+    // Fail the resolution if the result contains 127.0.53.53. See the comment
+    // block of kIcanNameCollisionIp for details on why.
+    for (const auto& it : results) {
+      const IPAddressNumber& cur = it.address();
+      if (cur.size() == arraysize(kIcanNameCollisionIp) &&
+          0 == memcmp(&cur.front(), kIcanNameCollisionIp, cur.size())) {
+        error = ERR_ICANN_NAME_COLLISION;
+        break;
+      }
+    }
+
     origin_loop_->PostTask(
         FROM_HERE,
         base::Bind(&ProcTask::OnLookupComplete, this, results, start_time,
@@ -1269,7 +1293,13 @@
   }
 
   void AddRequest(scoped_ptr<Request> req) {
-    DCHECK_EQ(key_.hostname, req->info().hostname());
+    // .localhost queries are redirected to "localhost." to make sure
+    // that they are never sent out on the network, per RFC 6761.
+    if (IsLocalhostTLD(req->info().hostname())) {
+      DCHECK_EQ(key_.hostname, kLocalhost);
+    } else {
+      DCHECK_EQ(key_.hostname, req->info().hostname());
+    }
 
     req->set_job(this);
     priority_tracker_.Add(req->priority());
@@ -2192,7 +2222,13 @@
     }
   }
 
-  return Key(info.hostname(), effective_address_family, effective_flags);
+  std::string hostname = info.hostname();
+  // Redirect .localhost queries to "localhost." to make sure that they
+  // are never sent out on the network, per RFC 6761.
+  if (IsLocalhostTLD(info.hostname()))
+    hostname = kLocalhost;
+
+  return Key(hostname, effective_address_family, effective_flags);
 }
 
 void HostResolverImpl::AbortAllInProgressJobs() {
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc
index 7a46a1c..d71db2f 100644
--- a/net/dns/host_resolver_impl_unittest.cc
+++ b/net/dns/host_resolver_impl_unittest.cc
@@ -550,6 +550,17 @@
   EXPECT_EQ("just.testing", proc_->GetCaptureList()[0].hostname);
 }
 
+TEST_F(HostResolverImplTest, LocalhostLookup) {
+  proc_->SignalMultiple(1u);
+  Request* req = CreateRequest("foo.localhost", 80);
+  EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
+  EXPECT_EQ(OK, req->WaitForResult());
+
+  EXPECT_TRUE(req->HasOneAddress("127.0.0.1", 80));
+
+  EXPECT_EQ("localhost.", proc_->GetCaptureList()[0].hostname);
+}
+
 TEST_F(HostResolverImplTest, EmptyListMeansNameNotResolved) {
   proc_->AddRuleForAllFamilies("just.testing", "");
   proc_->SignalMultiple(1u);
@@ -1319,6 +1330,53 @@
   EXPECT_EQ(resolver_proc->resolved_attempt_number(), kAttemptNumberToResolve);
 }
 
+// If a host resolves to a list that includes 127.0.53.53, this is treated as
+// an error. 127.0.53.53 is a localhost address, however it has been given a
+// special significance by ICANN to help surfance name collision resulting from
+// the new gTLDs.
+TEST_F(HostResolverImplTest, NameCollision127_0_53_53) {
+  proc_->AddRuleForAllFamilies("single", "127.0.53.53");
+  proc_->AddRuleForAllFamilies("multiple", "127.0.0.1,127.0.53.53");
+  proc_->AddRuleForAllFamilies("ipv6", "::127.0.53.53");
+  proc_->AddRuleForAllFamilies("not_reserved1", "53.53.0.127");
+  proc_->AddRuleForAllFamilies("not_reserved2", "127.0.53.54");
+  proc_->AddRuleForAllFamilies("not_reserved3", "10.0.53.53");
+  proc_->SignalMultiple(6u);
+
+  Request* request;
+
+  request = CreateRequest("single");
+  EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+  EXPECT_EQ(ERR_ICANN_NAME_COLLISION, request->WaitForResult());
+
+  request = CreateRequest("multiple");
+  EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+  EXPECT_EQ(ERR_ICANN_NAME_COLLISION, request->WaitForResult());
+
+  // Resolving an IP literal of 127.0.53.53 however is allowed.
+  EXPECT_EQ(OK, CreateRequest("127.0.53.53")->Resolve());
+
+  // Moreover the address should not be recognized when embedded in an IPv6
+  // address.
+  request = CreateRequest("ipv6");
+  EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+  EXPECT_EQ(OK, request->WaitForResult());
+
+  // Try some other IPs which are similar, but NOT an exact match on
+  // 127.0.53.53.
+  request = CreateRequest("not_reserved1");
+  EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+  EXPECT_EQ(OK, request->WaitForResult());
+
+  request = CreateRequest("not_reserved2");
+  EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+  EXPECT_EQ(OK, request->WaitForResult());
+
+  request = CreateRequest("not_reserved3");
+  EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+  EXPECT_EQ(OK, request->WaitForResult());
+}
+
 DnsConfig CreateValidDnsConfig() {
   IPAddressNumber dns_ip;
   bool rv = ParseIPLiteralToNumber("192.168.1.0", &dns_ip);
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 8085641..04eb2e5 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -28,7 +28,6 @@
 #include "net/socket/client_socket_pool_manager_impl.h"
 #include "net/socket/next_proto.h"
 #include "net/socket/ssl_client_socket.h"
-#include "net/spdy/hpack_huffman_aggregator.h"
 #include "net/spdy/spdy_session_pool.h"
 
 namespace {
@@ -191,10 +190,6 @@
     }
   }
 
-  if (HpackHuffmanAggregator::UseAggregator()) {
-    huffman_aggregator_.reset(new HpackHuffmanAggregator());
-  }
-
   http_server_properties_->SetAlternateProtocolProbabilityThreshold(
       params.alternate_protocol_probability_threshold);
 }
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index e46ffc9..982d41e 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -36,7 +36,6 @@
 class ClientSocketPoolManager;
 class CTVerifier;
 class HostResolver;
-class HpackHuffmanAggregator;
 class HttpAuthHandlerFactory;
 class HttpNetworkSessionPeer;
 class HttpProxyClientSocketPool;
@@ -185,9 +184,6 @@
   NetLog* net_log() {
     return net_log_;
   }
-  HpackHuffmanAggregator* huffman_aggregator() {
-    return huffman_aggregator_.get();
-  }
 
   // Creates a Value summary of the state of the socket pools. The caller is
   // responsible for deleting the returned value.
@@ -244,9 +240,6 @@
   scoped_ptr<HttpStreamFactory> http_stream_factory_for_websocket_;
   std::set<HttpResponseBodyDrainer*> response_drainers_;
 
-  // TODO(jgraettinger): Remove when Huffman collection is complete.
-  scoped_ptr<HpackHuffmanAggregator> huffman_aggregator_;
-
   NextProtoVector next_protos_;
   bool enabled_protocols_[NUM_VALID_ALTERNATE_PROTOCOLS];
 
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 199a5ff4..15177683 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -54,7 +54,6 @@
 #include "net/socket/ssl_client_socket.h"
 #include "net/socket/ssl_client_socket_pool.h"
 #include "net/socket/transport_client_socket_pool.h"
-#include "net/spdy/hpack_huffman_aggregator.h"
 #include "net/spdy/spdy_http_stream.h"
 #include "net/spdy/spdy_session.h"
 #include "net/spdy/spdy_session_pool.h"
@@ -1058,14 +1057,6 @@
     stream_->GetSSLInfo(&response_.ssl_info);
 
   headers_valid_ = true;
-
-  if (session_->huffman_aggregator()) {
-    session_->huffman_aggregator()->AggregateTransactionCharacterCounts(
-        *request_,
-        request_headers_,
-        proxy_info_.proxy_server(),
-        *response_.headers.get());
-  }
   return OK;
 }
 
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index a3a96c4..edb03d2 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -570,17 +570,18 @@
 typedef CaptureGroupNameSocketPool<SSLClientSocketPool>
 CaptureGroupNameSSLSocketPool;
 
-template<typename ParentPool>
+template <typename ParentPool>
 CaptureGroupNameSocketPool<ParentPool>::CaptureGroupNameSocketPool(
     HostResolver* host_resolver,
     CertVerifier* /* cert_verifier */)
-    : ParentPool(0, 0, NULL, host_resolver, NULL, NULL) {}
+    : ParentPool(0, 0, host_resolver, NULL, NULL) {
+}
 
 template <>
 CaptureGroupNameHttpProxySocketPool::CaptureGroupNameSocketPool(
     HostResolver* /* host_resolver */,
     CertVerifier* /* cert_verifier */)
-    : HttpProxyClientSocketPool(0, 0, NULL, NULL, NULL, NULL) {
+    : HttpProxyClientSocketPool(0, 0, NULL, NULL, NULL) {
 }
 
 template <>
@@ -589,7 +590,6 @@
     CertVerifier* cert_verifier)
     : SSLClientSocketPool(0,
                           0,
-                          NULL,
                           cert_verifier,
                           NULL,
                           NULL,
@@ -10165,11 +10165,9 @@
   // to validate that the TCP socket is not released to the pool between
   // each round of multi-round authentication.
   HttpNetworkSessionPeer session_peer(session);
-  ClientSocketPoolHistograms transport_pool_histograms("SmallTCP");
   TransportClientSocketPool* transport_pool = new TransportClientSocketPool(
       50,  // Max sockets for pool
       1,   // Max sockets per group
-      &transport_pool_histograms,
       session_deps_.host_resolver.get(),
       session_deps_.socket_factory.get(),
       session_deps_.net_log);
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
index 9c8a5317..a861ca47 100644
--- a/net/http/http_proxy_client_socket_pool.cc
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -440,18 +440,17 @@
 HttpProxyClientSocketPool::HttpProxyClientSocketPool(
     int max_sockets,
     int max_sockets_per_group,
-    ClientSocketPoolHistograms* histograms,
     TransportClientSocketPool* transport_pool,
     SSLClientSocketPool* ssl_pool,
     NetLog* net_log)
     : transport_pool_(transport_pool),
       ssl_pool_(ssl_pool),
-      base_(this, max_sockets, max_sockets_per_group, histograms,
+      base_(this,
+            max_sockets,
+            max_sockets_per_group,
             ClientSocketPool::unused_idle_socket_timeout(),
             ClientSocketPool::used_idle_socket_timeout(),
-            new HttpProxyConnectJobFactory(transport_pool,
-                                           ssl_pool,
-                                           net_log)) {
+            new HttpProxyConnectJobFactory(transport_pool, ssl_pool, net_log)) {
   // We should always have a |transport_pool_| except in unit tests.
   if (transport_pool_)
     base_.AddLowerLayeredPool(transport_pool_);
@@ -544,10 +543,6 @@
   return base_.ConnectionTimeout();
 }
 
-ClientSocketPoolHistograms* HttpProxyClientSocketPool::histograms() const {
-  return base_.histograms();
-}
-
 bool HttpProxyClientSocketPool::IsStalled() const {
   return base_.IsStalled();
 }
diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h
index f9cee7e..824b15f 100644
--- a/net/http/http_proxy_client_socket_pool.h
+++ b/net/http/http_proxy_client_socket_pool.h
@@ -19,7 +19,6 @@
 #include "net/http/proxy_client_socket.h"
 #include "net/socket/client_socket_pool.h"
 #include "net/socket/client_socket_pool_base.h"
-#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/spdy/spdy_session.h"
 
@@ -188,7 +187,6 @@
 
   HttpProxyClientSocketPool(int max_sockets,
                             int max_sockets_per_group,
-                            ClientSocketPoolHistograms* histograms,
                             TransportClientSocketPool* transport_pool,
                             SSLClientSocketPool* ssl_pool,
                             NetLog* net_log);
@@ -233,8 +231,6 @@
 
   base::TimeDelta ConnectionTimeout() const override;
 
-  ClientSocketPoolHistograms* histograms() const override;
-
   // LowerLayeredPool implementation.
   bool IsStalled() const override;
 
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index acff27b..7d1e432 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -15,7 +15,6 @@
 #include "net/http/http_proxy_client_socket.h"
 #include "net/http/http_response_headers.h"
 #include "net/socket/client_socket_handle.h"
-#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/next_proto.h"
 #include "net/socket/socket_test_util.h"
 #include "net/spdy/spdy_protocol.h"
@@ -156,16 +155,12 @@
  protected:
   HttpProxyClientSocketPoolTest()
       : session_deps_(GetParam().protocol),
-        tcp_histograms_("MockTCP"),
         transport_socket_pool_(
             kMaxSockets,
             kMaxSocketsPerGroup,
-            &tcp_histograms_,
             session_deps_.deterministic_socket_factory.get()),
-        ssl_histograms_("MockSSL"),
         ssl_socket_pool_(kMaxSockets,
                          kMaxSocketsPerGroup,
-                         &ssl_histograms_,
                          session_deps_.cert_verifier.get(),
                          NULL /* channel_id_store */,
                          NULL /* transport_security_state */,
@@ -179,11 +174,9 @@
                          session_deps_.ssl_config_service.get(),
                          BoundNetLog().net_log()),
         session_(CreateNetworkSession()),
-        http_proxy_histograms_("HttpProxyUnitTest"),
         spdy_util_(GetParam().protocol),
         pool_(kMaxSockets,
               kMaxSocketsPerGroup,
-              &http_proxy_histograms_,
               &transport_socket_pool_,
               &ssl_socket_pool_,
               NULL) {}
@@ -310,15 +303,12 @@
  private:
   SpdySessionDependencies session_deps_;
 
-  ClientSocketPoolHistograms tcp_histograms_;
   MockTransportClientSocketPool transport_socket_pool_;
-  ClientSocketPoolHistograms ssl_histograms_;
   MockHostResolver host_resolver_;
   scoped_ptr<CertVerifier> cert_verifier_;
   SSLClientSocketPool ssl_socket_pool_;
 
   const scoped_refptr<HttpNetworkSession> session_;
-  ClientSocketPoolHistograms http_proxy_histograms_;
 
  protected:
   SpdyTestUtil spdy_util_;
diff --git a/net/http/http_server_properties_impl_unittest.cc b/net/http/http_server_properties_impl_unittest.cc
index a9463b81..096be3b8 100644
--- a/net/http/http_server_properties_impl_unittest.cc
+++ b/net/http/http_server_properties_impl_unittest.cc
@@ -506,6 +506,22 @@
   EXPECT_FALSE(HasAlternativeService(test_host_port_pair));
 }
 
+// Adding an alternative service for a new host overrides canonical host.
+TEST_F(AlternateProtocolServerPropertiesTest, CanonicalOverride) {
+  HostPortPair test_host_port_pair("foo.c.youtube.com", 80);
+  HostPortPair bar_host_port_pair("bar.c.youtube.com", 80);
+  AlternativeService bar_alternative_service(QUIC, "bar.c.youtube.com", 1234);
+  impl_.SetAlternativeService(bar_host_port_pair, bar_alternative_service, 1.0);
+  AlternativeService altsvc = impl_.GetAlternativeService(test_host_port_pair);
+  EXPECT_EQ(1234, altsvc.port);
+
+  HostPortPair qux_host_port_pair("qux.c.youtube.com", 80);
+  AlternativeService qux_alternative_service(QUIC, "qux.c.youtube.com", 443);
+  impl_.SetAlternativeService(qux_host_port_pair, qux_alternative_service, 1.0);
+  altsvc = impl_.GetAlternativeService(test_host_port_pair);
+  EXPECT_EQ(443, altsvc.port);
+}
+
 TEST_F(AlternateProtocolServerPropertiesTest, ClearWithCanonical) {
   HostPortPair test_host_port_pair("foo.c.youtube.com", 80);
   HostPortPair canonical_port_pair("bar.c.youtube.com", 80);
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index a6ae28f..e1a547e 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -1013,8 +1013,6 @@
   establishing_tunnel_ = false;
 
   if (connection_->socket()) {
-    LogHttpConnectedMetrics(*connection_);
-
     // We officially have a new connection.  Record the type.
     if (!connection_->is_reused()) {
       ConnectionType type = using_spdy_ ? CONNECTION_SPDY : CONNECTION_HTTP;
@@ -1420,40 +1418,6 @@
     using_spdy_ = true;
 }
 
-// static
-void HttpStreamFactoryImpl::Job::LogHttpConnectedMetrics(
-    const ClientSocketHandle& handle) {
-  UMA_HISTOGRAM_ENUMERATION("Net.HttpSocketType", handle.reuse_type(),
-                            ClientSocketHandle::NUM_TYPES);
-
-  switch (handle.reuse_type()) {
-    case ClientSocketHandle::UNUSED:
-      UMA_HISTOGRAM_CUSTOM_TIMES("Net.HttpConnectionLatency",
-                                 handle.setup_time(),
-                                 base::TimeDelta::FromMilliseconds(1),
-                                 base::TimeDelta::FromMinutes(10),
-                                 100);
-      break;
-    case ClientSocketHandle::UNUSED_IDLE:
-      UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_UnusedSocket",
-                                 handle.idle_time(),
-                                 base::TimeDelta::FromMilliseconds(1),
-                                 base::TimeDelta::FromMinutes(6),
-                                 100);
-      break;
-    case ClientSocketHandle::REUSED_IDLE:
-      UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_ReusedSocket",
-                                 handle.idle_time(),
-                                 base::TimeDelta::FromMilliseconds(1),
-                                 base::TimeDelta::FromMinutes(6),
-                                 100);
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-}
-
 bool HttpStreamFactoryImpl::Job::IsPreconnecting() const {
   DCHECK_GE(num_streams_, 0);
   return num_streams_ > 0;
diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc
index c8c42c6..2b9ba959 100644
--- a/net/http/http_stream_factory_impl_unittest.cc
+++ b/net/http/http_stream_factory_impl_unittest.cc
@@ -381,17 +381,18 @@
 typedef CapturePreconnectsSocketPool<SSLClientSocketPool>
 CapturePreconnectsSSLSocketPool;
 
-template<typename ParentPool>
+template <typename ParentPool>
 CapturePreconnectsSocketPool<ParentPool>::CapturePreconnectsSocketPool(
-    HostResolver* host_resolver, CertVerifier* /* cert_verifier */)
-    : ParentPool(0, 0, nullptr, host_resolver, nullptr, nullptr),
-      last_num_streams_(-1) {}
+    HostResolver* host_resolver,
+    CertVerifier* /* cert_verifier */)
+    : ParentPool(0, 0, host_resolver, nullptr, nullptr), last_num_streams_(-1) {
+}
 
 template <>
 CapturePreconnectsHttpProxySocketPool::CapturePreconnectsSocketPool(
     HostResolver* /* host_resolver */,
     CertVerifier* /* cert_verifier */)
-    : HttpProxyClientSocketPool(0, 0, nullptr, nullptr, nullptr, nullptr),
+    : HttpProxyClientSocketPool(0, 0, nullptr, nullptr, nullptr),
       last_num_streams_(-1) {
 }
 
@@ -401,7 +402,6 @@
     CertVerifier* cert_verifier)
     : SSLClientSocketPool(0,
                           0,
-                          nullptr,  // ssl_histograms
                           cert_verifier,
                           nullptr,        // channel_id_store
                           nullptr,        // transport_security_state
diff --git a/net/http/transport_security_state_static.h b/net/http/transport_security_state_static.h
index 1943305..04e64a0 100644
--- a/net/http/transport_security_state_static.h
+++ b/net/http/transport_security_state_static.h
@@ -682,1595 +682,1597 @@
     0xe4, 0xae, 0x00, 0xf2, 0xf9, 0xe6, 0x02, 0xed, 0xe9, 0x03, 0x01, 0x04,
     0xb3, 0xb2, 0xb9, 0xb8, 0x07, 0xb5, 0xf1, 0x08, 0x06, 0x09, 0x0a, 0xfa,
     0x0b, 0xf6, 0xeb, 0x0c, 0xe3, 0x0d, 0xe1, 0x0e, 0x80, 0x0f, 0x05, 0x10,
-    0xe2, 0xf7, 0x12, 0xe7, 0xef, 0x13, 0xff, 0x14, 0xb7, 0xb6, 0x16, 0xb1,
-    0xb0, 0x17, 0xb4, 0x18, 0x19, 0xea, 0xf8, 0x1a, 0xad, 0x1b, 0xe8, 0x1c,
-    0xf3, 0x1d, 0xe5, 0x1e, 0xee, 0xec, 0xf5, 0xf0, 0xf4, 0x21, 0x20, 0x22,
-    0x1f, 0x23, 0x15, 0x24, 0x11, 0x25,
+    0xe2, 0xf7, 0x12, 0xe7, 0xef, 0x13, 0xff, 0x14, 0xf3, 0xee, 0xe5, 0x16,
+    0xb7, 0xb6, 0x18, 0xb1, 0xb0, 0x19, 0xb4, 0x1a, 0x1b, 0xea, 0xf8, 0x1c,
+    0xad, 0x1d, 0xe8, 0x1e, 0x1f, 0xec, 0xf5, 0xf0, 0xf4, 0x21, 0x20, 0x22,
+    0x17, 0x23, 0x15, 0x24, 0x11, 0x25,
 };
 
 static const uint8 kPreloadedHSTSData[] = {
-    0xfe, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x44, 0xb3, 0x9c, 0x3b, 0xf5, 0xa7,
-    0xff, 0x9b, 0x8c, 0xe1, 0xa3, 0x76, 0x73, 0x67, 0x96, 0x9a, 0xb4, 0xb4,
+    0xfe, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x44, 0xb3, 0x9c, 0x3c, 0xf5, 0xa7,
+    0xff, 0x9b, 0x6c, 0xe1, 0xa3, 0x78, 0x73, 0x67, 0x96, 0x9a, 0xb4, 0xb4,
     0x00, 0xf8, 0xc9, 0x3a, 0x7d, 0xdb, 0xd5, 0x5d, 0x51, 0x16, 0xcf, 0xf8,
-    0xad, 0x12, 0xd1, 0x0e, 0xce, 0x5a, 0x7f, 0xf1, 0x08, 0xb6, 0xc6, 0x67,
-    0x75, 0x96, 0xad, 0x36, 0x6c, 0x94, 0x99, 0x29, 0x7f, 0xd3, 0x4b, 0xf8,
-    0xb4, 0xfe, 0xf6, 0x77, 0xf3, 0x96, 0x92, 0x09, 0xa9, 0x9f, 0x9d, 0xc2,
-    0x7f, 0x8f, 0x2d, 0x0c, 0x7e, 0x54, 0x87, 0x3f, 0xc2, 0xdc, 0x2b, 0xb1,
-    0xc9, 0x69, 0x05, 0xf3, 0x15, 0x38, 0xb4, 0x20, 0xb0, 0x87, 0x86, 0x7a,
-    0x3d, 0xde, 0x8c, 0x24, 0xc2, 0x09, 0xfc, 0x1c, 0xed, 0xea, 0xae, 0xa8,
-    0xa8, 0xa7, 0xfc, 0x0c, 0x38, 0x77, 0xb0, 0x4b, 0xcb, 0x4f, 0xff, 0x70,
-    0xb8, 0x13, 0x73, 0x2c, 0x03, 0x60, 0x16, 0x9f, 0x76, 0xf5, 0x57, 0x54,
+    0xad, 0x12, 0xd1, 0x0f, 0x0e, 0x5a, 0x7f, 0xf1, 0x08, 0xb7, 0x06, 0x67,
+    0x75, 0x96, 0xad, 0x36, 0x70, 0x94, 0x99, 0x29, 0x7f, 0xd3, 0x4b, 0xf8,
+    0xb4, 0xfe, 0xf6, 0x77, 0xf3, 0x96, 0x92, 0x09, 0xa9, 0x9f, 0x9d, 0xb2,
+    0x7f, 0x8f, 0x2d, 0x0c, 0x7e, 0x54, 0x87, 0x3f, 0xc2, 0xdb, 0x2b, 0xb1,
+    0xc9, 0x69, 0x05, 0xf3, 0x15, 0x38, 0xb4, 0x20, 0xb0, 0x87, 0x66, 0x7a,
+    0x3d, 0xe6, 0x8c, 0x24, 0xc2, 0x09, 0xfc, 0x1c, 0xed, 0xea, 0xae, 0xa8,
+    0xa8, 0xa7, 0xfc, 0x0c, 0x38, 0x79, 0xb0, 0x4b, 0xcb, 0x4f, 0xff, 0x6c,
+    0xb6, 0x13, 0x73, 0x2c, 0x03, 0x60, 0x16, 0x9f, 0x76, 0xf5, 0x57, 0x54,
     0x57, 0xb3, 0xe2, 0x7d, 0xbf, 0xd6, 0x2d, 0x20, 0xe1, 0xef, 0xf4, 0xce,
     0x7f, 0xe0, 0xd3, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x12, 0x34, 0xff, 0xe0,
     0x9e, 0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0x9e, 0x27, 0xff, 0x04, 0xf4,
     0xe0, 0xe7, 0x6f, 0x55, 0x75, 0x44, 0xff, 0x3f, 0xf8, 0x27, 0xa7, 0x07,
     0x3b, 0x7a, 0xab, 0xaa, 0x28, 0x69, 0xff, 0x8f, 0x4e, 0x0e, 0x76, 0xf5,
-    0x57, 0x54, 0x51, 0x13, 0xfe, 0x70, 0x7e, 0xd6, 0xe6, 0xd3, 0x39, 0x68,
+    0x57, 0x54, 0x51, 0x13, 0xfe, 0x70, 0x7e, 0xd7, 0x26, 0xd3, 0x39, 0x68,
     0x0a, 0x24, 0x19, 0x3e, 0x7f, 0xd4, 0xe0, 0xe7, 0x6f, 0x55, 0x75, 0x45,
-    0x23, 0x3f, 0xff, 0x60, 0x38, 0xc6, 0x07, 0xc3, 0x5f, 0xeb, 0x4d, 0xfa,
-    0x52, 0x09, 0xd1, 0x4b, 0xbc, 0x93, 0x3f, 0xf0, 0x69, 0xc1, 0xce, 0xde,
+    0x23, 0x3f, 0xff, 0x60, 0x36, 0xc6, 0x07, 0xc3, 0x5f, 0xeb, 0x4d, 0xfa,
+    0x52, 0x09, 0xd1, 0x4b, 0xcc, 0x93, 0x3f, 0xf0, 0x69, 0xc1, 0xce, 0xde,
     0xaa, 0xea, 0x89, 0x42, 0x2c, 0x5f, 0xa1, 0x74, 0xaa, 0xfb, 0xc2, 0x00,
-    0x8f, 0xb7, 0xe7, 0xfd, 0x85, 0x71, 0xc9, 0xf8, 0x9f, 0xfa, 0xab, 0xe4,
+    0x8f, 0xb9, 0xe7, 0xfd, 0x85, 0x71, 0xc9, 0xf6, 0x9f, 0xfa, 0xab, 0xe4,
     0xab, 0xa8, 0x72, 0xfd, 0x0c, 0x77, 0xd2, 0xc9, 0xff, 0xc1, 0x3d, 0x38,
     0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x39, 0xcf, 0xfe, 0x09, 0xe9, 0xc1, 0xce,
-    0xde, 0xaa, 0xea, 0x8a, 0x26, 0x7f, 0xee, 0xb8, 0x3c, 0x2d, 0x3c, 0xe2,
+    0xde, 0xaa, 0xea, 0x8a, 0x26, 0x7f, 0xee, 0xb8, 0x3b, 0x2d, 0x3c, 0xe2,
     0xfd, 0x68, 0xea, 0x3c, 0x34, 0xab, 0xf2, 0xac, 0xfe, 0x0e, 0x76, 0xf5,
-    0x57, 0x54, 0x43, 0x93, 0xfc, 0xdd, 0xcb, 0xf1, 0xb6, 0x72, 0xd3, 0xd7,
+    0x57, 0x54, 0x43, 0x93, 0xfc, 0xdd, 0xcb, 0xed, 0xb8, 0x72, 0xd3, 0xd7,
     0xaa, 0xba, 0xa2, 0x2f, 0x9f, 0x63, 0xce, 0xad, 0x2d, 0x1d, 0x3d, 0x43,
-    0x0b, 0x67, 0xf6, 0xb0, 0x1c, 0xcf, 0xe9, 0x69, 0x86, 0xc5, 0xa7, 0xfb,
-    0xc2, 0xc7, 0x30, 0x1c, 0x65, 0xa1, 0x8f, 0x38, 0x85, 0xa6, 0xd6, 0xcb,
+    0x0b, 0x67, 0xf6, 0xb0, 0x1b, 0xcf, 0xe9, 0x69, 0x86, 0xc5, 0xa7, 0xfb,
+    0xc2, 0xc7, 0x30, 0x1b, 0x65, 0xa1, 0x8f, 0x38, 0x85, 0xa6, 0xd7, 0x0b,
     0x4f, 0x79, 0xcc, 0x6a, 0x50, 0x73, 0x73, 0x42, 0xf3, 0xea, 0xb3, 0xd9,
-    0xc5, 0xa4, 0x1b, 0x13, 0xcf, 0xc8, 0x46, 0x3c, 0x47, 0xc8, 0x40, 0x0a,
-    0xce, 0xf1, 0x04, 0xff, 0xe0, 0x9e, 0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8,
+    0xb5, 0xa4, 0x1b, 0x13, 0xcf, 0xc8, 0x46, 0x3c, 0x47, 0xb8, 0x40, 0x0a,
+    0xcf, 0x31, 0x04, 0xff, 0xe0, 0x9e, 0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8,
     0x99, 0x27, 0xf0, 0x73, 0xb7, 0xaa, 0xba, 0xa2, 0xde, 0x9f, 0xfc, 0x13,
-    0xd3, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x14, 0x9c, 0xdb, 0xe9, 0xf0, 0x5a,
-    0x7e, 0xdb, 0xf3, 0x3b, 0x5f, 0xad, 0x37, 0x02, 0xf8, 0x9e, 0x95, 0x10,
+    0xd3, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x14, 0x9c, 0xdc, 0xe9, 0xf0, 0x5a,
+    0x7e, 0xe3, 0xf3, 0x3b, 0x5f, 0xad, 0x36, 0xc2, 0xf8, 0x9e, 0x95, 0x10,
     0xc3, 0x2b, 0xe4, 0x74, 0xa0, 0xdc, 0x6b, 0xd2, 0xab, 0xf3, 0xe3, 0x21,
     0x4f, 0x3f, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x10, 0xec, 0xff, 0xe0, 0x9e,
     0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0x96, 0x27, 0xf0, 0x73, 0xb7, 0xaa,
-    0xba, 0xa2, 0x30, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x47, 0xb3, 0xda, 0xad,
-    0x9f, 0xad, 0x3c, 0x1a, 0x70, 0x70, 0xf5, 0xfc, 0x67, 0x3f, 0x83, 0x9d,
+    0xba, 0xa2, 0x30, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x47, 0xb3, 0xda, 0xae,
+    0x1f, 0xad, 0x3c, 0x1a, 0x70, 0x70, 0xf5, 0xfc, 0x67, 0x3f, 0x83, 0x9d,
     0xbd, 0x55, 0xd5, 0x16, 0x1c, 0xfe, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x5c,
     0xf0, 0xc9, 0xee, 0x58, 0x7c, 0xe5, 0x52, 0x84, 0xa9, 0xce, 0x3c, 0x7d,
     0x3f, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x10, 0xf4, 0xfb, 0xb7, 0xaa, 0xba,
-    0xa2, 0x26, 0x9f, 0xfb, 0xd5, 0xce, 0x33, 0xf7, 0x67, 0x59, 0x69, 0xfe,
+    0xa2, 0x26, 0x9f, 0xfb, 0xd5, 0xbd, 0xb3, 0xf7, 0x67, 0x59, 0x69, 0xfe,
     0x2f, 0x17, 0xb3, 0xf6, 0xea, 0xd1, 0x87, 0xf7, 0x48, 0x93, 0x78, 0xeb,
-    0x4f, 0xfe, 0xce, 0x19, 0x66, 0x98, 0xb6, 0xf3, 0x1d, 0x68, 0xd8, 0xf8,
-    0x78, 0x2d, 0x3d, 0xef, 0xa8, 0xc5, 0x44, 0x2f, 0x3f, 0x03, 0x99, 0x67,
-    0xc2, 0xb4, 0x83, 0x89, 0xd8, 0x72, 0x15, 0xda, 0x84, 0x0f, 0xc4, 0x6f,
-    0xcc, 0x26, 0xfb, 0x8b, 0x4f, 0x7b, 0x85, 0xe5, 0xa7, 0xff, 0xfe, 0xe7,
-    0xc0, 0x2c, 0xeb, 0xc3, 0x8f, 0xf9, 0x80, 0x6d, 0x69, 0x8d, 0x5a, 0x7f,
-    0xf1, 0x3c, 0xec, 0xd9, 0xdb, 0x8f, 0x54, 0x2b, 0x4d, 0x4f, 0xab, 0x49,
-    0xf1, 0xc4, 0x7c, 0x5c, 0x87, 0x8e, 0x94, 0x97, 0x36, 0xf1, 0xab, 0x4f,
-    0xb9, 0x5e, 0xa7, 0x2d, 0x3f, 0x9d, 0xa6, 0xd6, 0xfb, 0x37, 0xd6, 0xfa,
-    0x5a, 0x78, 0xb4, 0x5f, 0xad, 0x3a, 0xdb, 0x6d, 0x4a, 0x7d, 0x43, 0xee,
-    0x62, 0x41, 0x2f, 0xe7, 0xd5, 0xe7, 0xb3, 0xcb, 0x40, 0x11, 0x37, 0xfa,
-    0x00, 0x9a, 0x4f, 0x72, 0xaa, 0x96, 0x9f, 0xfb, 0xf2, 0x7b, 0x77, 0x18,
-    0xbb, 0xf6, 0xfd, 0x69, 0xff, 0x11, 0xac, 0x2d, 0xa6, 0x23, 0xad, 0x3f,
+    0x4f, 0xfe, 0xcd, 0x99, 0x66, 0x98, 0xb8, 0xf3, 0x1d, 0x68, 0xe0, 0xf8,
+    0x76, 0x2d, 0x3d, 0xef, 0xa8, 0xc5, 0x44, 0x2f, 0x3f, 0x03, 0x79, 0x67,
+    0xc2, 0xb4, 0x83, 0x89, 0xd8, 0x6e, 0x15, 0xda, 0x84, 0x0f, 0xc4, 0x6f,
+    0xcc, 0x26, 0xfb, 0x6b, 0x4f, 0x7b, 0x65, 0xe5, 0xa7, 0xff, 0xfe, 0xdf,
+    0xc0, 0x2c, 0xeb, 0xc3, 0x8f, 0xf7, 0x80, 0x6d, 0x69, 0x8d, 0x5a, 0x7f,
+    0xf1, 0x3c, 0xec, 0xe1, 0xdc, 0x8f, 0x54, 0x2b, 0x4d, 0x4f, 0xab, 0x49,
+    0xf1, 0xc4, 0x7c, 0x5c, 0x87, 0x6e, 0x94, 0x97, 0x37, 0x31, 0xab, 0x4f,
+    0xb7, 0x5e, 0xa7, 0x2d, 0x3f, 0x9d, 0xa6, 0xd7, 0x3b, 0x39, 0xd7, 0x3a,
+    0x5a, 0x78, 0xb4, 0x5f, 0xad, 0x3a, 0xdb, 0x6d, 0x4a, 0x7d, 0x43, 0xed,
+    0xe2, 0x41, 0x2f, 0xe7, 0xd5, 0xe7, 0xb3, 0xcb, 0x40, 0x11, 0x37, 0xfa,
+    0x00, 0x9a, 0x4f, 0x6e, 0xaa, 0x96, 0x9f, 0xfb, 0xf2, 0x7b, 0x96, 0xd8,
+    0xbb, 0xf7, 0x3d, 0x69, 0xff, 0x11, 0xac, 0x2d, 0xa6, 0x23, 0xad, 0x3f,
     0x63, 0xff, 0x9d, 0x9a, 0x5a, 0x7b, 0x01, 0x96, 0x2d, 0x18, 0xa8, 0xbb,
-    0x62, 0x62, 0x87, 0x16, 0xfc, 0xc1, 0xe1, 0xfe, 0x26, 0x09, 0xdf, 0x8b,
+    0x82, 0x62, 0x87, 0x17, 0x3c, 0xc1, 0xe1, 0xfd, 0xa6, 0x09, 0xdf, 0x8b,
     0xe7, 0xec, 0xb5, 0xb0, 0x04, 0xb4, 0xe6, 0xfb, 0xf5, 0xa7, 0xff, 0xff,
-    0x08, 0xec, 0xc6, 0x8e, 0x03, 0x84, 0xf6, 0xed, 0x66, 0xce, 0xee, 0x5a,
-    0xb4, 0xf8, 0xbd, 0xf3, 0x58, 0xb4, 0xff, 0xfe, 0xcd, 0x9d, 0xa3, 0xe0,
-    0xee, 0x7d, 0x2e, 0x58, 0x59, 0xd5, 0xa7, 0xff, 0xfd, 0x6e, 0xe1, 0xcb,
-    0xf0, 0x87, 0x3e, 0xdb, 0xdb, 0xaa, 0xdc, 0x35, 0x69, 0xfd, 0xb3, 0xb4,
-    0x6f, 0xcd, 0xb2, 0xd3, 0xfa, 0xad, 0xf9, 0xe6, 0xf5, 0x2d, 0x0c, 0x8d,
-    0xd2, 0x72, 0x13, 0x79, 0xef, 0xfc, 0x56, 0x2d, 0x3f, 0xbd, 0xc2, 0x1f,
-    0xcc, 0x3a, 0xd3, 0xfa, 0xc6, 0x38, 0xb6, 0xdf, 0xad, 0x26, 0x71, 0xf4,
+    0x08, 0xf0, 0xc6, 0x8e, 0x03, 0x64, 0xf7, 0x2d, 0x67, 0x0e, 0xee, 0x5a,
+    0xb4, 0xf8, 0xbd, 0xf3, 0x58, 0xb4, 0xff, 0xfe, 0xce, 0x1d, 0xa3, 0xe0,
+    0xf2, 0x7d, 0x2d, 0xd8, 0x59, 0xd5, 0xa7, 0xff, 0xfd, 0x6f, 0x21, 0xcb,
+    0xec, 0x87, 0x3e, 0xe3, 0xdc, 0xaa, 0xdc, 0x35, 0x69, 0xfd, 0xc3, 0xb4,
+    0x6f, 0xcd, 0xc2, 0xd3, 0xfa, 0xad, 0xf9, 0xe6, 0xf5, 0x2d, 0x0c, 0x8d,
+    0xd2, 0x72, 0x13, 0x79, 0xef, 0xfc, 0x56, 0x2d, 0x3f, 0xbd, 0xb2, 0x1f,
+    0xcc, 0x3a, 0xd3, 0xfa, 0xc6, 0x38, 0xb7, 0x1f, 0xad, 0x26, 0x71, 0xf4,
     0x51, 0xa4, 0x32, 0xac, 0x3c, 0x7b, 0x22, 0x8e, 0xc6, 0x85, 0x45, 0xa3,
     0x08, 0xb9, 0xbe, 0x72, 0xd3, 0xe6, 0xff, 0x56, 0x79, 0x69, 0xd5, 0xed,
     0x2d, 0x37, 0xd6, 0x2d, 0x38, 0x7c, 0xf3, 0x8d, 0x9f, 0x46, 0xe7, 0xef,
-    0xaf, 0xb8, 0x1b, 0x5a, 0xb4, 0x31, 0xf3, 0x11, 0x9c, 0xff, 0xfb, 0xe6,
-    0xf6, 0xe1, 0xf9, 0xf7, 0x76, 0x7b, 0x7b, 0x3c, 0xb4, 0xff, 0xdc, 0x63,
-    0x77, 0x78, 0x6b, 0xcd, 0xe5, 0xa7, 0xf6, 0xed, 0x9c, 0x5c, 0xf8, 0xeb,
-    0x47, 0xe7, 0xf8, 0x28, 0xb3, 0xfe, 0xeb, 0xdb, 0xb4, 0xc5, 0x65, 0x79,
+    0xaf, 0xc8, 0x1c, 0x5a, 0xb4, 0x31, 0xf3, 0x11, 0x9c, 0xff, 0xfb, 0xe6,
+    0xf7, 0x21, 0xf9, 0xf7, 0x96, 0x7b, 0x9b, 0x3c, 0xb4, 0xff, 0xdb, 0x63,
+    0x79, 0x78, 0x6b, 0xcd, 0xe5, 0xa7, 0xf7, 0x2e, 0x1c, 0x5b, 0xf8, 0xeb,
+    0x47, 0xe7, 0xf8, 0x28, 0xb3, 0xfe, 0xeb, 0xdc, 0xb4, 0xc5, 0x65, 0x79,
     0x68, 0x63, 0xe3, 0xf8, 0x8a, 0x7f, 0xff, 0xfb, 0xe1, 0xd7, 0x45, 0x9e,
-    0xdc, 0x67, 0x89, 0xd9, 0xb7, 0xb1, 0xf8, 0xe6, 0x96, 0x93, 0x2d, 0x3f,
-    0x57, 0x4b, 0x4d, 0xb2, 0xd3, 0xfe, 0xab, 0x7a, 0xc3, 0xee, 0x63, 0xea,
-    0xd0, 0xe3, 0xec, 0xe9, 0x64, 0xff, 0x70, 0xba, 0xf7, 0x2b, 0x3a, 0xb4,
-    0xff, 0xff, 0xfd, 0xfe, 0x87, 0x2b, 0x6d, 0xc0, 0xe7, 0xdb, 0x87, 0x36,
-    0x76, 0x7d, 0x60, 0xe5, 0x6c, 0xb4, 0x5d, 0x19, 0x04, 0x71, 0x3a, 0xaa,
-    0xea, 0x8a, 0x62, 0x30, 0xf2, 0x6e, 0x45, 0x3f, 0xec, 0x1d, 0x9d, 0xaf,
-    0x37, 0x5e, 0x5a, 0x76, 0x17, 0xeb, 0x4b, 0x16, 0x9d, 0xb0, 0xe7, 0xe6,
-    0xa9, 0xf8, 0xdc, 0x12, 0x27, 0xa9, 0xa6, 0x73, 0x9b, 0x65, 0xa7, 0xf1,
-    0xc1, 0xc6, 0xb3, 0x2d, 0x5a, 0x36, 0x3d, 0x0a, 0x1c, 0x9f, 0xff, 0xc3,
-    0x97, 0xf7, 0x9b, 0xda, 0x78, 0x72, 0xdd, 0xcd, 0xbe, 0xeb, 0x4f, 0xfa,
-    0xec, 0x7e, 0x66, 0xb0, 0x85, 0x68, 0x64, 0x52, 0x71, 0xa6, 0x7d, 0xd6,
-    0x7e, 0xdd, 0x5a, 0x7e, 0xa1, 0xdc, 0x06, 0x15, 0xa3, 0x0f, 0xd8, 0x88,
+    0xe4, 0x67, 0x89, 0xd9, 0xc7, 0xb1, 0xf8, 0xe6, 0x96, 0x93, 0x2d, 0x3f,
+    0x57, 0x4b, 0x4d, 0xc2, 0xd3, 0xfe, 0xab, 0x7a, 0xc3, 0xed, 0xe3, 0xea,
+    0xd0, 0xe3, 0xec, 0xe9, 0x64, 0xff, 0x6c, 0xba, 0xf6, 0xeb, 0x3a, 0xb4,
+    0xff, 0xff, 0xfd, 0xfe, 0x87, 0x2b, 0x8e, 0x40, 0xdf, 0xdc, 0x87, 0x38,
+    0x76, 0x7d, 0x60, 0xe5, 0x70, 0xb4, 0x5d, 0x19, 0x04, 0x71, 0x3a, 0xaa,
+    0xea, 0x8a, 0x62, 0x30, 0xf2, 0x6e, 0x45, 0x3f, 0xec, 0x1e, 0x1d, 0xaf,
+    0x37, 0x5e, 0x5a, 0x76, 0x17, 0xeb, 0x4b, 0x16, 0x9d, 0xc0, 0xe7, 0xe6,
+    0xa9, 0xf8, 0xdc, 0x12, 0x27, 0xa9, 0xa6, 0x73, 0x9b, 0x85, 0xa7, 0xf1,
+    0xc1, 0xb6, 0xb3, 0x2d, 0x5a, 0x38, 0x3d, 0x0a, 0x1c, 0x9f, 0xff, 0xc3,
+    0x97, 0xf7, 0x9b, 0xda, 0x78, 0x72, 0xde, 0x4d, 0xce, 0xeb, 0x4f, 0xfa,
+    0xec, 0x7d, 0xe6, 0xb0, 0x85, 0x68, 0x64, 0x52, 0x6d, 0xa6, 0x7d, 0xd6,
+    0x7e, 0xdd, 0x5a, 0x7e, 0xa1, 0xe4, 0x06, 0x15, 0xa3, 0x0f, 0xd8, 0x88,
     0xbe, 0x28, 0x8b, 0x17, 0xc9, 0x1c, 0x2e, 0x08, 0x61, 0x1a, 0x41, 0x78,
-    0xcf, 0xf6, 0x22, 0x28, 0x41, 0x3c, 0xf7, 0xd8, 0xcc, 0xcf, 0x0b, 0xf1,
-    0x71, 0xf4, 0x67, 0x93, 0xfd, 0xda, 0xb7, 0x75, 0x02, 0xac, 0x5a, 0x7f,
-    0x99, 0xee, 0x13, 0x1e, 0x80, 0xb4, 0x1a, 0x7e, 0x5f, 0x1d, 0xcf, 0x60,
-    0x9b, 0xbf, 0x5a, 0x7f, 0xff, 0xfd, 0xc2, 0xf7, 0x33, 0xac, 0xff, 0x71,
-    0xfe, 0x7d, 0xdd, 0x9b, 0x3b, 0x3e, 0x1f, 0xf4, 0xb4, 0xbc, 0x48, 0xb4,
-    0xd1, 0x2c, 0xff, 0xfb, 0x33, 0x85, 0x7e, 0x66, 0xed, 0x7d, 0xc6, 0xdf,
-    0xad, 0x3f, 0xfd, 0xee, 0x67, 0xfb, 0x8e, 0xd9, 0xe0, 0x60, 0xad, 0x3f,
-    0xdb, 0x6e, 0x3b, 0x60, 0xfb, 0x79, 0x69, 0x6b, 0x11, 0x1b, 0x4a, 0x12,
-    0x21, 0x4c, 0x3b, 0xd0, 0xe7, 0x9f, 0x39, 0xb6, 0x76, 0x96, 0x9f, 0xff,
-    0xff, 0xf6, 0x09, 0xbb, 0x8b, 0x9f, 0x05, 0xe2, 0xf3, 0x1c, 0x68, 0xde,
-    0x10, 0x73, 0x9b, 0x7e, 0xc0, 0x4a, 0x7f, 0xff, 0xeb, 0x95, 0xa0, 0xfb,
-    0xbb, 0xbe, 0x6b, 0x35, 0x7e, 0x7d, 0xcd, 0xaa, 0xd5, 0xa6, 0xfa, 0xcd,
-    0x93, 0x42, 0x22, 0x8d, 0x42, 0x7a, 0x19, 0x57, 0x4f, 0x23, 0x36, 0x18,
-    0xdb, 0xe7, 0xff, 0x1e, 0xfb, 0xb9, 0x4e, 0xc3, 0xd0, 0xfe, 0xb4, 0xff,
-    0xf8, 0x87, 0xdc, 0xc7, 0xdf, 0x3d, 0x9b, 0xfa, 0x1f, 0xd6, 0x9f, 0xf9,
-    0x86, 0xc1, 0xc7, 0xfa, 0xf9, 0xb6, 0x5a, 0x34, 0x8a, 0x1f, 0x96, 0xe7,
-    0xb3, 0xd8, 0xf9, 0x2d, 0x3b, 0x7b, 0x38, 0xb4, 0xd8, 0x05, 0xa2, 0xc4,
+    0xcf, 0xf8, 0x22, 0x28, 0x41, 0x3c, 0xf7, 0xd8, 0xcc, 0xcf, 0x0b, 0xf1,
+    0x71, 0xf4, 0x67, 0x93, 0xfd, 0xda, 0xb7, 0x95, 0x02, 0xac, 0x5a, 0x7f,
+    0x99, 0xed, 0x93, 0x1e, 0x80, 0xb4, 0x1a, 0x7e, 0x5f, 0x1d, 0xcf, 0x60,
+    0x9b, 0xcf, 0x5a, 0x7f, 0xff, 0xfd, 0xb2, 0xf6, 0xf3, 0xac, 0xff, 0x91,
+    0xfe, 0x7d, 0xe5, 0x9c, 0x3b, 0x3e, 0x1f, 0xf4, 0xb4, 0xbc, 0x48, 0xb4,
+    0xd1, 0x2c, 0xff, 0xfb, 0x33, 0x65, 0x7d, 0xe7, 0x2d, 0x7d, 0xb6, 0xe7,
+    0xad, 0x3f, 0xfd, 0xed, 0xe7, 0xfc, 0x8e, 0xd9, 0xe0, 0x60, 0xad, 0x3f,
+    0xdc, 0x72, 0x3b, 0x60, 0xfb, 0x99, 0x69, 0x6b, 0x11, 0x1b, 0x4a, 0x12,
+    0x21, 0x4c, 0x3b, 0xd0, 0xe7, 0x9f, 0x39, 0xb8, 0x76, 0x96, 0x9f, 0xff,
+    0xff, 0xf6, 0x09, 0xbc, 0x8b, 0x7f, 0x05, 0xe2, 0xf3, 0x1c, 0x68, 0xdd,
+    0x90, 0x73, 0x7c, 0x7e, 0xc0, 0x4a, 0x7f, 0xff, 0xeb, 0x95, 0xa0, 0xfb,
+    0xbc, 0xbe, 0x6b, 0x35, 0x7d, 0xfd, 0xbe, 0x2a, 0xd5, 0xa6, 0xfa, 0xce,
+    0x13, 0x42, 0x22, 0x8d, 0x42, 0x7a, 0x19, 0x57, 0x4e, 0xe3, 0x36, 0x18,
+    0xdb, 0xe7, 0xff, 0x1e, 0xfc, 0xb7, 0x4e, 0xc3, 0xd0, 0xfe, 0xb4, 0xff,
+    0xf8, 0x87, 0xdb, 0xc7, 0xdf, 0x3d, 0x9c, 0xfa, 0x1f, 0xd6, 0x9f, 0xf9,
+    0x86, 0xc1, 0xc7, 0xfa, 0xf9, 0xb8, 0x5a, 0x34, 0x8a, 0x1f, 0x96, 0xe7,
+    0xb3, 0xd8, 0xf9, 0x2d, 0x3b, 0x9b, 0x36, 0xb4, 0xd8, 0x05, 0xa2, 0xc4,
     0xda, 0x1d, 0x0e, 0xcd, 0x12, 0x78, 0x95, 0xf8, 0xf4, 0xf8, 0xbd, 0xac,
-    0xfd, 0x69, 0xf8, 0x0c, 0x3c, 0xa7, 0x2d, 0x39, 0x80, 0xcb, 0x4f, 0xce,
-    0xcf, 0x6a, 0x9e, 0xdc, 0x78, 0xa7, 0x2b, 0x87, 0x22, 0xcd, 0xe6, 0xc8,
+    0xfd, 0x69, 0xf8, 0x0c, 0x3b, 0xa7, 0x2d, 0x39, 0x80, 0xcb, 0x4f, 0xce,
+    0xcf, 0x6a, 0x9e, 0xe4, 0x78, 0xa7, 0x2b, 0x87, 0x22, 0xcd, 0xe6, 0xc8,
     0x5a, 0x7e, 0xd5, 0xef, 0x42, 0x6a, 0xd0, 0xf9, 0xa6, 0xdf, 0xc1, 0x53,
-    0xf3, 0xef, 0x2b, 0x7e, 0xdf, 0xad, 0x3f, 0xba, 0xf0, 0xe7, 0x2b, 0xcb,
+    0xf3, 0xee, 0xeb, 0x9e, 0xdf, 0xad, 0x3f, 0xba, 0xf0, 0xe6, 0xeb, 0xcb,
     0x43, 0x22, 0x3b, 0x0a, 0x08, 0xd2, 0x7e, 0x6f, 0xf3, 0xc6, 0x58, 0xb4,
-    0x38, 0xf7, 0x04, 0xb6, 0x7f, 0xbd, 0xcc, 0x75, 0x0e, 0x6c, 0xb4, 0xff,
-    0xff, 0x0d, 0x7f, 0xab, 0x30, 0x87, 0xc3, 0x8f, 0xf7, 0x6a, 0xd1, 0x5a,
-    0x05, 0x14, 0x1e, 0x37, 0x9f, 0xf1, 0x63, 0xdb, 0xb4, 0xdc, 0xad, 0xfa,
-    0xd3, 0xfd, 0xb6, 0x36, 0xcf, 0x0b, 0x1d, 0x69, 0xfd, 0x5b, 0x6b, 0x99,
-    0x5d, 0x5a, 0x00, 0x8a, 0xee, 0x21, 0xf8, 0xe6, 0x7f, 0xb3, 0xb6, 0x31,
-    0x77, 0x05, 0x69, 0x1d, 0x6d, 0xc6, 0xda, 0x70, 0xe0, 0x16, 0x86, 0x37,
-    0x9b, 0x11, 0x4f, 0xdb, 0x6b, 0x30, 0x4d, 0x5a, 0x78, 0xe6, 0x37, 0xeb,
-    0x46, 0x1e, 0x81, 0x16, 0xce, 0xfb, 0x6f, 0xd6, 0x86, 0x54, 0x45, 0x90,
-    0xd0, 0x28, 0x51, 0x75, 0xc4, 0x48, 0x27, 0xf7, 0x8a, 0xcc, 0xe7, 0xde,
-    0x5a, 0x4f, 0x2d, 0x3f, 0x67, 0xd9, 0xcb, 0xd8, 0xb4, 0xd9, 0xb3, 0x1b,
-    0xfd, 0x08, 0xcf, 0xdc, 0xa7, 0x1f, 0xa4, 0xb4, 0xff, 0xe6, 0x34, 0xda,
-    0xf6, 0xbc, 0x2d, 0xb3, 0x96, 0x9e, 0xff, 0xb9, 0xb2, 0xd3, 0xe2, 0xd6,
+    0x38, 0xf7, 0x04, 0xb6, 0x7f, 0xbd, 0xbc, 0x75, 0x0e, 0x70, 0xb4, 0xff,
+    0xff, 0x0d, 0x7f, 0xab, 0x30, 0x87, 0xc3, 0x8f, 0xf9, 0x6a, 0xd1, 0x5a,
+    0x05, 0x14, 0x1e, 0x37, 0x9f, 0xf1, 0x63, 0xdc, 0xb4, 0xdb, 0xae, 0x7a,
+    0xd3, 0xfd, 0xc6, 0x37, 0x0f, 0x0b, 0x1d, 0x69, 0xfd, 0x5c, 0x6b, 0x79,
+    0x5d, 0x5a, 0x00, 0x8a, 0xed, 0xa1, 0xf8, 0xe6, 0x7f, 0xb3, 0xb6, 0x31,
+    0x77, 0x05, 0x69, 0x1d, 0x6e, 0x46, 0xda, 0x70, 0xe0, 0x16, 0x86, 0x37,
+    0x9c, 0x11, 0x4f, 0xdc, 0x6b, 0x30, 0x4d, 0x5a, 0x78, 0xe6, 0x37, 0xeb,
+    0x46, 0x1e, 0x81, 0x16, 0xce, 0xfb, 0x8f, 0xd6, 0x86, 0x54, 0x45, 0x90,
+    0xd0, 0x28, 0x51, 0x75, 0xc4, 0x48, 0x27, 0xf7, 0x8a, 0xcc, 0xdf, 0xde,
+    0x5a, 0x4f, 0x2d, 0x3f, 0x67, 0xd9, 0xbb, 0xd8, 0xb4, 0xd9, 0xc3, 0x1b,
+    0xfd, 0x08, 0xcf, 0xdb, 0xa7, 0x1f, 0xa4, 0xb4, 0xff, 0xe6, 0x34, 0xda,
+    0xf6, 0xbc, 0x2d, 0xc3, 0x96, 0x9e, 0xff, 0xb9, 0xc2, 0xd3, 0xe2, 0xd6,
     0x98, 0xeb, 0x41, 0x1e, 0x4b, 0xf2, 0x38, 0x35, 0x38, 0x22, 0x73, 0xfc,
-    0xb0, 0x4b, 0x3e, 0x84, 0xa4, 0xff, 0xfd, 0xd7, 0x73, 0x1e, 0xe3, 0x09,
-    0xa3, 0x9e, 0xe7, 0xcb, 0x4f, 0xff, 0xe2, 0xcd, 0x9d, 0xaf, 0x71, 0x8e,
-    0xc4, 0x0d, 0xcd, 0xbe, 0xeb, 0x4f, 0xfb, 0xed, 0x6e, 0x39, 0x9c, 0xae,
-    0x32, 0xd3, 0xfc, 0xcf, 0x6e, 0xcf, 0x7d, 0x9b, 0x2d, 0x02, 0x7f, 0xdf,
-    0x21, 0x4f, 0xff, 0xff, 0xbe, 0x1d, 0xd6, 0xd6, 0xce, 0xed, 0x7f, 0xbb,
-    0x5f, 0x36, 0xdc, 0xc2, 0x1c, 0x35, 0x69, 0xff, 0xf7, 0xdd, 0xee, 0x7b,
-    0x5c, 0x2c, 0xdd, 0xcf, 0xd9, 0x68, 0xc4, 0x71, 0xf2, 0x11, 0x73, 0xff,
-    0xce, 0x7d, 0xe6, 0x30, 0xbc, 0x39, 0xed, 0x52, 0xd3, 0xff, 0xe0, 0x61,
-    0xf7, 0x3e, 0xf9, 0xb4, 0xf3, 0xab, 0xb6, 0x2d, 0x3f, 0xff, 0x78, 0xac,
-    0xc0, 0x6e, 0x3d, 0x3b, 0xa5, 0x65, 0x1d, 0x69, 0xfd, 0x65, 0x1c, 0x5b,
-    0xaf, 0x2d, 0x3f, 0xdb, 0x61, 0xbf, 0x9f, 0x37, 0x77, 0x11, 0x20, 0x4b,
-    0xb3, 0xfd, 0x83, 0x66, 0xef, 0xea, 0x9e, 0x5a, 0x7f, 0xbb, 0x63, 0x59,
-    0x67, 0xdd, 0xb1, 0x69, 0xfe, 0xf8, 0x77, 0x6b, 0xed, 0xef, 0xba, 0xb4,
-    0x31, 0xff, 0xe8, 0xfa, 0x7f, 0xf5, 0x9b, 0xb5, 0xcf, 0xb7, 0x7b, 0x5a,
-    0x6e, 0xad, 0x3f, 0xff, 0xec, 0xdb, 0x45, 0x96, 0xee, 0xe6, 0x7b, 0x59,
-    0x66, 0xec, 0x20, 0x2d, 0x18, 0x8c, 0x12, 0x51, 0x8b, 0x17, 0x57, 0xcd,
-    0x87, 0x35, 0xe3, 0x05, 0xd8, 0x9c, 0x94, 0x3b, 0x0e, 0xc3, 0xaa, 0x6a,
-    0x17, 0x5e, 0x87, 0x14, 0xfe, 0xf0, 0xe7, 0xb9, 0x96, 0x2d, 0x3f, 0xea,
-    0xef, 0x33, 0xcc, 0x25, 0xfa, 0xd3, 0xfd, 0x96, 0xec, 0xed, 0x6e, 0xf1,
-    0x2d, 0x3f, 0xff, 0xe6, 0xbb, 0x1f, 0x73, 0xdc, 0x21, 0x06, 0xe3, 0x2e,
-    0x39, 0xcc, 0x5a, 0x19, 0x31, 0x9b, 0x99, 0x09, 0xe3, 0xe9, 0xe4, 0xf1,
-    0x78, 0x99, 0x69, 0xff, 0xff, 0xc2, 0x46, 0xf2, 0x9e, 0xdc, 0x3f, 0x3e,
-    0xee, 0xcd, 0x9d, 0x9f, 0x0f, 0xfa, 0x5a, 0x7d, 0xc2, 0x61, 0xb1, 0x69,
+    0xb0, 0x4b, 0x3e, 0x84, 0xa4, 0xff, 0xfd, 0xd7, 0x6f, 0x1e, 0xdb, 0x09,
+    0xa3, 0x9e, 0xdf, 0xcb, 0x4f, 0xff, 0xe2, 0xce, 0x1d, 0xaf, 0x6d, 0x8e,
+    0xc4, 0x0e, 0x4d, 0xce, 0xeb, 0x4f, 0xfb, 0xed, 0x72, 0x39, 0x9b, 0xad,
+    0xb2, 0xd3, 0xfc, 0xcf, 0x72, 0xcf, 0x7d, 0x9c, 0x2d, 0x02, 0x7f, 0xdf,
+    0x21, 0x4f, 0xff, 0xff, 0xbe, 0x1e, 0x56, 0xd7, 0x0e, 0xed, 0x7f, 0xcb,
+    0x5f, 0x37, 0x1b, 0xc2, 0x1c, 0x35, 0x69, 0xff, 0xf7, 0xdd, 0xee, 0x7b,
+    0x5b, 0x2c, 0xe5, 0xbf, 0xd9, 0x68, 0xc4, 0x71, 0xee, 0x11, 0x73, 0xff,
+    0xce, 0x7d, 0xde, 0x30, 0xbc, 0x39, 0xed, 0x52, 0xd3, 0xff, 0xe0, 0x61,
+    0xf9, 0x3e, 0xf9, 0xb4, 0xf3, 0xab, 0xb6, 0x2d, 0x3f, 0xff, 0x78, 0xac,
+    0xc0, 0x72, 0x3d, 0x3b, 0xa5, 0x65, 0x1d, 0x69, 0xfd, 0x65, 0x1c, 0x5b,
+    0xaf, 0x2d, 0x3f, 0xdc, 0x61, 0xbf, 0x9f, 0x39, 0x77, 0x11, 0x20, 0x4b,
+    0xb3, 0xfd, 0x83, 0x67, 0x2f, 0xea, 0x9e, 0x5a, 0x7f, 0xbb, 0x63, 0x59,
+    0x67, 0xdd, 0xb1, 0x69, 0xfe, 0xf8, 0x79, 0x6b, 0xee, 0x6f, 0xba, 0xb4,
+    0x31, 0xff, 0xe8, 0xfa, 0x7f, 0xf5, 0x9c, 0xb5, 0xbf, 0xb9, 0x7b, 0x5a,
+    0x6e, 0xad, 0x3f, 0xff, 0xec, 0xe3, 0x45, 0x96, 0xf2, 0xde, 0x7b, 0x59,
+    0x67, 0x2c, 0x20, 0x2d, 0x18, 0x8c, 0x12, 0x51, 0x8b, 0x17, 0x57, 0xcd,
+    0x87, 0x35, 0xe3, 0x05, 0xe0, 0x9c, 0x94, 0x3b, 0x0e, 0xc3, 0xaa, 0x6a,
+    0x17, 0x5e, 0x87, 0x14, 0xfe, 0xf0, 0xe7, 0xb7, 0x96, 0x2d, 0x3f, 0xea,
+    0xee, 0xf3, 0xcc, 0x25, 0xfa, 0xd3, 0xfd, 0x96, 0xf0, 0xed, 0x72, 0xf1,
+    0x2d, 0x3f, 0xff, 0xe6, 0xbb, 0x1f, 0x93, 0xdb, 0x21, 0x07, 0x23, 0x2e,
+    0x39, 0xbc, 0x5a, 0x19, 0x31, 0x9b, 0x99, 0x09, 0xe3, 0xe9, 0xe4, 0xf1,
+    0x78, 0x99, 0x69, 0xff, 0xff, 0xc2, 0x46, 0xee, 0x9e, 0xe4, 0x3f, 0x3e,
+    0xf2, 0xce, 0x1d, 0x9f, 0x0f, 0xfa, 0x5a, 0x7d, 0xb2, 0x61, 0xb1, 0x69,
     0xbc, 0xc7, 0x45, 0x43, 0xf8, 0x41, 0x43, 0x91, 0xf8, 0x50, 0xc7, 0x86,
     0x64, 0x69, 0xe4, 0xed, 0xd9, 0x47, 0x6d, 0x51, 0x9d, 0xcf, 0x98, 0x7c,
-    0x4f, 0xd6, 0x9e, 0xf6, 0xab, 0xf5, 0xa7, 0xfe, 0xe1, 0x6e, 0x2e, 0xfd,
-    0x65, 0x76, 0xeb, 0x4f, 0x17, 0x2a, 0xc5, 0xa7, 0x9f, 0xb7, 0x99, 0x69,
-    0xf7, 0xcf, 0x90, 0xd7, 0x96, 0x9f, 0xb9, 0x56, 0x6e, 0x77, 0x96, 0x9f,
-    0x18, 0x2e, 0x71, 0xd6, 0x9f, 0x57, 0x47, 0x62, 0x5a, 0x19, 0x15, 0x44,
+    0x4f, 0xd6, 0x9e, 0xf6, 0xab, 0xf5, 0xa7, 0xfe, 0xd9, 0x72, 0x2e, 0xfd,
+    0x65, 0x76, 0xeb, 0x4f, 0x16, 0xea, 0xc5, 0xa7, 0x9f, 0xb7, 0x99, 0x69,
+    0xf7, 0xcf, 0x90, 0xd7, 0x96, 0x9f, 0xb7, 0x56, 0x72, 0x77, 0x96, 0x9f,
+    0x18, 0x2e, 0x71, 0xd6, 0x9f, 0x57, 0x47, 0x82, 0x5a, 0x19, 0x15, 0x44,
     0x59, 0xf9, 0x87, 0x8a, 0x20, 0x09, 0xdc, 0xe1, 0x39, 0xa4, 0x57, 0x47,
-    0x22, 0x1f, 0x43, 0x32, 0x7f, 0x71, 0x88, 0x6b, 0xa6, 0xad, 0x3f, 0x31,
-    0x0d, 0x74, 0xd5, 0xa7, 0xf7, 0xf5, 0xff, 0x7a, 0x5d, 0xdc, 0x7b, 0x9c,
-    0x31, 0x9e, 0x33, 0xea, 0xd2, 0xd3, 0xff, 0xf1, 0x77, 0x76, 0x16, 0x09,
-    0xbc, 0x2c, 0x3e, 0x5d, 0x69, 0xff, 0xfe, 0x2b, 0x3a, 0xc7, 0xd7, 0xcd,
-    0xb9, 0xba, 0xc0, 0xe3, 0x1d, 0x68, 0xc4, 0x60, 0xf5, 0x66, 0x7f, 0xdd,
-    0xcd, 0xb9, 0xb3, 0x1e, 0xc6, 0x5a, 0x7f, 0xff, 0xf7, 0x7a, 0x56, 0x6e,
-    0xe6, 0x78, 0x59, 0xee, 0x65, 0x8d, 0xb7, 0xbe, 0xba, 0xd3, 0xff, 0xff,
-    0x31, 0xbc, 0x6d, 0xc3, 0x9f, 0x1b, 0xbb, 0x67, 0x68, 0x84, 0xc6, 0xb1,
-    0x69, 0xf6, 0xda, 0x2c, 0x15, 0xa3, 0x11, 0x3f, 0xf3, 0xcc, 0x12, 0x67,
-    0xfc, 0x8c, 0x72, 0x7f, 0xe7, 0x68, 0x8d, 0xe3, 0x15, 0x8c, 0x6a, 0xd3,
-    0xfc, 0x3f, 0x3c, 0x39, 0xca, 0xf2, 0xd3, 0xfc, 0x0e, 0x35, 0xa0, 0xe5,
-    0x1d, 0x69, 0xff, 0xfe, 0xae, 0xf3, 0x3c, 0x58, 0x36, 0x69, 0xb0, 0x41,
-    0xc6, 0x5a, 0x7b, 0xdb, 0xac, 0xea, 0xd1, 0xb2, 0x21, 0xe9, 0x8a, 0x7f,
-    0xf5, 0xb4, 0x36, 0x1f, 0xe2, 0x01, 0x09, 0x2d, 0x3f, 0x3d, 0xb3, 0xbe,
-    0x6f, 0x2d, 0x3f, 0xdc, 0xa3, 0x8e, 0x72, 0xbc, 0xb4, 0xf6, 0x77, 0xfb,
+    0x22, 0x1f, 0x43, 0x32, 0x7f, 0x6d, 0x88, 0x6b, 0xa6, 0xad, 0x3f, 0x31,
+    0x0d, 0x74, 0xd5, 0xa7, 0xf7, 0xf5, 0xff, 0x7a, 0x5d, 0xe4, 0x7b, 0x9b,
+    0x31, 0x9e, 0x33, 0xea, 0xd2, 0xd3, 0xff, 0xf1, 0x77, 0x96, 0x16, 0x09,
+    0xbb, 0x2c, 0x3e, 0x5d, 0x69, 0xff, 0xfe, 0x2b, 0x3a, 0xc7, 0xd7, 0xcd,
+    0xc9, 0xba, 0xc0, 0xdb, 0x1d, 0x68, 0xc4, 0x60, 0xf5, 0x66, 0x7f, 0xdd,
+    0xce, 0x37, 0xc3, 0x1e, 0xc6, 0x5a, 0x7f, 0xff, 0xf7, 0x7a, 0x56, 0x72,
+    0xde, 0x78, 0x59, 0xed, 0xe5, 0x8d, 0xc7, 0xbe, 0xba, 0xd3, 0xff, 0xff,
+    0x31, 0xbb, 0x6e, 0x43, 0x9f, 0x1b, 0xcb, 0x87, 0x68, 0x84, 0xc6, 0xb1,
+    0x69, 0xf7, 0x1a, 0x2c, 0x15, 0xa3, 0x11, 0x3f, 0xf3, 0xcc, 0x12, 0x67,
+    0xfb, 0x8c, 0x72, 0x7f, 0xe7, 0x68, 0x8d, 0xdb, 0x15, 0x8c, 0x6a, 0xd3,
+    0xfc, 0x3f, 0x3c, 0x39, 0xba, 0xf2, 0xd3, 0xfc, 0x0d, 0xb5, 0xa0, 0xdd,
+    0x1d, 0x69, 0xff, 0xfe, 0xae, 0xef, 0x3c, 0x58, 0x36, 0x69, 0xb0, 0x41,
+    0xb6, 0x5a, 0x7b, 0xdc, 0xac, 0xea, 0xd1, 0xc2, 0x21, 0xe9, 0x8a, 0x7f,
+    0xf5, 0xb4, 0x36, 0x1f, 0xe2, 0x01, 0x09, 0x2d, 0x3f, 0x3d, 0xc3, 0xbe,
+    0x6f, 0x2d, 0x3f, 0xdb, 0xa3, 0x8e, 0x6e, 0xbc, 0xb4, 0xf6, 0x77, 0xfb,
     0x56, 0x86, 0x44, 0x4e, 0x8c, 0x3c, 0x6d, 0x3f, 0x67, 0xbb, 0xdc, 0x72,
-    0xd3, 0xf0, 0xe7, 0x07, 0x2e, 0xb4, 0xf0, 0xe6, 0xb7, 0x3c, 0x7a, 0xde,
-    0x2c, 0x80, 0x2e, 0x5e, 0x1a, 0x45, 0x78, 0xdf, 0xf6, 0x29, 0x24, 0x5e,
-    0x9c, 0x72, 0x18, 0x1a, 0x23, 0xf4, 0x34, 0x9f, 0xc2, 0x16, 0x7f, 0xe1,
-    0x1c, 0xdd, 0xdc, 0xe0, 0xe3, 0xcb, 0x43, 0x2f, 0x44, 0xe4, 0xed, 0x05,
-    0x42, 0x9a, 0x7e, 0xe6, 0x0f, 0xbf, 0xf9, 0x69, 0xf0, 0xb1, 0xe8, 0x0b,
-    0x4f, 0xec, 0x7b, 0x99, 0xe6, 0x02, 0xd3, 0xff, 0xff, 0xdc, 0xcf, 0x6a,
-    0x9e, 0xdc, 0x39, 0xb3, 0xb3, 0xeb, 0x07, 0x01, 0xc2, 0x79, 0x69, 0xbe,
-    0x35, 0x68, 0xea, 0x26, 0x7d, 0x08, 0x19, 0xfb, 0x2c, 0x1c, 0xdb, 0x4b,
-    0x4f, 0xfe, 0xee, 0xce, 0xd6, 0x1f, 0x66, 0xe7, 0x19, 0x69, 0x69, 0x68,
-    0xf8, 0xf6, 0xcc, 0x4b, 0x9f, 0xcc, 0x71, 0xce, 0x57, 0x96, 0x9e, 0xdd,
-    0x7b, 0x2d, 0x5a, 0x7f, 0xff, 0xde, 0x16, 0xff, 0x67, 0x6b, 0xe6, 0xf0,
+    0xd3, 0xf0, 0xe6, 0xc7, 0x2e, 0xb4, 0xf0, 0xe6, 0xb9, 0x3c, 0x7a, 0xde,
+    0x2c, 0x80, 0x2e, 0x5e, 0x1a, 0x45, 0x78, 0xdf, 0xf8, 0x29, 0x24, 0x5e,
+    0x9c, 0x6e, 0x18, 0x1a, 0x23, 0xf4, 0x34, 0x9f, 0xc2, 0x16, 0x7f, 0xe1,
+    0x1c, 0xe5, 0xdc, 0xd8, 0xe3, 0xcb, 0x43, 0x2f, 0x44, 0xe4, 0xed, 0x05,
+    0x42, 0x9a, 0x7e, 0xde, 0x0f, 0xbf, 0xf9, 0x69, 0xf0, 0xb1, 0xe8, 0x0b,
+    0x4f, 0xec, 0x7b, 0x79, 0xe6, 0x02, 0xd3, 0xff, 0xff, 0xdb, 0xcf, 0x6a,
+    0x9e, 0xe4, 0x39, 0xc3, 0xb3, 0xeb, 0x07, 0x01, 0xb2, 0x79, 0x69, 0xbe,
+    0x35, 0x68, 0xea, 0x26, 0x7d, 0x08, 0x19, 0xfb, 0x2c, 0x1c, 0xe3, 0x4b,
+    0x4f, 0xfe, 0xef, 0x0e, 0xd6, 0x1f, 0x86, 0xde, 0xd9, 0x69, 0x69, 0x68,
+    0xf8, 0xf6, 0xcc, 0x4b, 0x9f, 0xcc, 0x71, 0xcd, 0xd7, 0x96, 0x9e, 0xe5,
+    0x7b, 0x2d, 0x5a, 0x7f, 0xff, 0xde, 0x16, 0xff, 0x87, 0x6b, 0xe6, 0xf0,
     0xe0, 0x0b, 0x3a, 0xf2, 0xd0, 0x05, 0x56, 0xa4, 0x5c, 0xf1, 0x2d, 0x43,
     0x2c, 0x4a, 0x35, 0x08, 0x8f, 0x89, 0x1f, 0x98, 0x18, 0x4d, 0x3e, 0x71,
-    0x77, 0x36, 0x5a, 0x7d, 0x9a, 0xb3, 0x2d, 0x5a, 0x36, 0x3c, 0xfe, 0x13,
-    0xcf, 0xff, 0x8a, 0xcc, 0xb8, 0x38, 0x58, 0x6e, 0xe3, 0x7e, 0x65, 0xa7,
-    0x9f, 0x7d, 0x9c, 0x5a, 0x62, 0x3a, 0xd3, 0xff, 0xfe, 0xd9, 0xd5, 0xb6,
-    0xe1, 0xf9, 0xf7, 0x76, 0x6c, 0xec, 0xf8, 0x7f, 0xd2, 0xd0, 0x74, 0x46,
-    0xbe, 0x8a, 0xc5, 0x88, 0xd7, 0xc8, 0x58, 0xcf, 0xff, 0xff, 0x9b, 0x6d,
-    0x7c, 0xdb, 0x6e, 0xe6, 0x03, 0x70, 0xe7, 0xc6, 0xee, 0xd7, 0xff, 0x97,
-    0x96, 0x9f, 0x30, 0xed, 0x56, 0x2d, 0x3f, 0xff, 0xff, 0xf1, 0xf2, 0xed,
-    0xc6, 0x3f, 0xb3, 0x5a, 0x6e, 0xf5, 0x87, 0x2c, 0xcf, 0xb5, 0xf3, 0xc2,
+    0x77, 0x38, 0x5a, 0x7d, 0x9a, 0xb3, 0x2d, 0x5a, 0x38, 0x3c, 0xfd, 0x93,
+    0xcf, 0xff, 0x8a, 0xcc, 0xb8, 0x36, 0x58, 0x6f, 0x23, 0x7e, 0x65, 0xa7,
+    0x9f, 0x7d, 0x9b, 0x5a, 0x62, 0x3a, 0xd3, 0xff, 0xfe, 0xe1, 0xd5, 0xc7,
+    0x21, 0xf9, 0xf7, 0x96, 0x70, 0xec, 0xf8, 0x7f, 0xd2, 0xd0, 0x74, 0x46,
+    0xbe, 0x8a, 0xc5, 0x88, 0xd7, 0xc8, 0x58, 0xcf, 0xff, 0xff, 0x9b, 0x8d,
+    0x7c, 0xdc, 0x72, 0xde, 0x03, 0x90, 0xe7, 0xc6, 0xf2, 0xd7, 0xff, 0x97,
+    0x96, 0x9f, 0x30, 0xf1, 0x56, 0x2d, 0x3f, 0xff, 0xff, 0xf1, 0xf2, 0xed,
+    0xb6, 0x3f, 0xb3, 0x5a, 0x6e, 0xf5, 0x87, 0x2c, 0xcf, 0xb5, 0xf3, 0xc2,
     0xc7, 0x5a, 0x7d, 0x5f, 0x99, 0x56, 0x2d, 0x18, 0x8c, 0x82, 0x84, 0xfc,
-    0xfc, 0x01, 0xce, 0x57, 0x96, 0x9f, 0x7b, 0x67, 0x17, 0x56, 0x97, 0xf8,
+    0xfc, 0x01, 0xcd, 0xd7, 0x96, 0x9f, 0x7b, 0x87, 0x17, 0x56, 0x97, 0xf8,
     0x7a, 0x7a, 0x2b, 0x9f, 0xd6, 0x7c, 0xde, 0x39, 0x79, 0x69, 0xff, 0x31,
-    0xaf, 0xbf, 0x60, 0xbc, 0x61, 0xd6, 0x8c, 0x3f, 0x8d, 0x8d, 0x27, 0xfd,
-    0x99, 0xfe, 0xb7, 0x77, 0x40, 0xd2, 0xd3, 0xfb, 0xb9, 0x7e, 0x17, 0x5e,
+    0xaf, 0xbf, 0x60, 0xbc, 0x61, 0xd6, 0x8c, 0x3f, 0x8e, 0x0d, 0x27, 0xfd,
+    0x99, 0xfe, 0xb9, 0x77, 0x40, 0xd2, 0xd3, 0xfb, 0xb9, 0x7d, 0x97, 0x5e,
     0x5a, 0x6c, 0xf0, 0x0f, 0xd3, 0x10, 0x67, 0xac, 0x21, 0xf2, 0xd0, 0xcb,
-    0x9e, 0xb8, 0x46, 0x51, 0x98, 0x74, 0xa0, 0xf0, 0x8f, 0xe4, 0x3b, 0xe9,
-    0xe4, 0x61, 0x41, 0xa8, 0x4b, 0x7c, 0x59, 0x3f, 0xf3, 0x3d, 0xbb, 0xc3,
-    0x96, 0xe1, 0x69, 0x69, 0xc0, 0xdc, 0xf2, 0xd3, 0xff, 0xdb, 0x38, 0x5b,
-    0x71, 0xc5, 0x8d, 0xf1, 0x58, 0xb4, 0x61, 0xf8, 0x90, 0xfc, 0xfc, 0xdd,
-    0xdc, 0x2f, 0xed, 0x5a, 0x7e, 0xb1, 0xe7, 0x6f, 0x9d, 0xb1, 0x69, 0xf1,
+    0x9e, 0xb8, 0x46, 0x51, 0x98, 0x74, 0xa0, 0xf0, 0x8f, 0xdc, 0x3b, 0xe9,
+    0xe4, 0x61, 0x41, 0xa8, 0x4b, 0x7c, 0x59, 0x3f, 0xf3, 0x3d, 0xcb, 0xc3,
+    0x96, 0xe1, 0x69, 0x69, 0xc0, 0xe4, 0xf2, 0xd3, 0xff, 0xdc, 0x38, 0x5b,
+    0x91, 0xc5, 0x8d, 0xf1, 0x58, 0xb4, 0x61, 0xf8, 0x90, 0xfc, 0xfc, 0xdd,
+    0xe4, 0x2f, 0xed, 0x5a, 0x7e, 0xb1, 0xe7, 0x73, 0x9d, 0xb1, 0x69, 0xf1,
     0xc8, 0x18, 0x05, 0xa4, 0xf2, 0xd3, 0x31, 0xd6, 0x97, 0x96, 0x8e, 0x9a,
-    0x5a, 0x15, 0x80, 0x1e, 0xb7, 0x0d, 0xa7, 0xbd, 0xaa, 0xd9, 0x69, 0x9e,
-    0xa5, 0xa7, 0xf7, 0x0b, 0xbb, 0x38, 0x59, 0x68, 0xdf, 0x49, 0x9f, 0x31,
-    0xb6, 0x3e, 0x11, 0x11, 0xc8, 0xbe, 0x16, 0x9f, 0xda, 0x2d, 0xad, 0xa1,
-    0xfd, 0x69, 0xff, 0xf9, 0x8e, 0x39, 0x7d, 0xbc, 0x35, 0xc3, 0xb6, 0x79,
+    0x5a, 0x15, 0x80, 0x1e, 0xb6, 0xcd, 0xa7, 0xbd, 0xaa, 0xe1, 0x69, 0x9e,
+    0xa5, 0xa7, 0xf6, 0xcb, 0xbc, 0x38, 0x59, 0x68, 0xe7, 0x49, 0x9f, 0x31,
+    0xb6, 0x3e, 0x11, 0x11, 0xc8, 0xbe, 0x16, 0x9f, 0xda, 0x2e, 0x2d, 0xa1,
+    0xfd, 0x69, 0xff, 0xf9, 0x8e, 0x39, 0x7e, 0x3c, 0x35, 0xb3, 0xb6, 0x79,
     0x69, 0xff, 0xf3, 0x75, 0xaf, 0x87, 0x6b, 0xb8, 0xb5, 0xf1, 0xd6, 0x87,
-    0x91, 0xc1, 0xc3, 0x4d, 0xe5, 0x89, 0xfb, 0xb8, 0xc6, 0xb0, 0xad, 0x38,
+    0x91, 0xc1, 0xb3, 0x4e, 0x65, 0x89, 0xfb, 0xb8, 0xc6, 0xb0, 0xad, 0x38,
     0x68, 0xeb, 0x4d, 0xfb, 0x2d, 0x0e, 0x3d, 0xb2, 0x2b, 0x11, 0xa8, 0x7c,
-    0x1d, 0xe5, 0x56, 0xf9, 0x18, 0x7b, 0xe7, 0x46, 0x69, 0x49, 0x16, 0x3d,
+    0x1d, 0xe5, 0x57, 0x39, 0x18, 0x7b, 0xe7, 0x46, 0x69, 0x49, 0x16, 0x3d,
     0xb8, 0xac, 0x12, 0xb8, 0x32, 0x7e, 0x0c, 0xd8, 0xcf, 0x6f, 0x2f, 0x07,
-    0x68, 0xe9, 0x0a, 0x30, 0x67, 0xa3, 0x45, 0xec, 0xae, 0x33, 0xc7, 0xa9,
-    0xca, 0x49, 0x25, 0x4a, 0x0a, 0xfe, 0x17, 0x03, 0x3d, 0xaf, 0xa9, 0x5e,
-    0x5e, 0x9d, 0xd0, 0xfa, 0x14, 0x8f, 0xe1, 0x6d, 0x69, 0x06, 0xf4, 0x6d,
-    0xe6, 0x46, 0x78, 0xfb, 0x08, 0xf9, 0xff, 0xfb, 0xeb, 0x87, 0x6f, 0x75,
-    0xb3, 0x6d, 0xf0, 0x15, 0x46, 0xad, 0x01, 0x54, 0xf6, 0x52, 0x86, 0x27,
+    0x88, 0xe9, 0x0a, 0x30, 0x67, 0xa3, 0x45, 0xec, 0xae, 0x33, 0xc7, 0xa9,
+    0xba, 0x49, 0x25, 0x4a, 0x0a, 0xfe, 0x17, 0x03, 0x3d, 0xaf, 0xa9, 0x5e,
+    0x5e, 0x9d, 0xd0, 0xfa, 0x14, 0x8f, 0xe1, 0x6d, 0x69, 0x07, 0x34, 0x6d,
+    0xe6, 0x46, 0x78, 0xfb, 0x08, 0xf9, 0xff, 0xfb, 0xeb, 0x87, 0x8f, 0x75,
+    0xb3, 0x8e, 0x70, 0x15, 0x46, 0xad, 0x01, 0x54, 0xf6, 0x52, 0x86, 0x27,
     0xf0, 0x73, 0xb7, 0xaa, 0xba, 0xa2, 0x9d, 0x9f, 0x76, 0xf5, 0x57, 0x54,
     0x54, 0x73, 0xfe, 0xa7, 0x07, 0x3b, 0x7a, 0xab, 0xaa, 0x26, 0x88, 0xc3,
     0xfc, 0x39, 0x9c, 0xfe, 0x32, 0x9f, 0x87, 0xc5, 0x62, 0xd0, 0x13, 0xd7,
     0x62, 0x19, 0xfc, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0xab, 0xe7, 0xdd, 0xbd,
     0x55, 0xd5, 0x15, 0xb4, 0xff, 0x38, 0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x20,
     0xc8, 0x38, 0x7e, 0xf4, 0x67, 0x3f, 0xf0, 0x69, 0xc1, 0xce, 0xde, 0xaa,
-    0xea, 0x89, 0x0e, 0x7f, 0xce, 0xe3, 0x7b, 0x5b, 0xef, 0xa7, 0xc0, 0x56,
-    0x96, 0xcb, 0x4c, 0x42, 0xb4, 0x00, 0xd2, 0xbc, 0x23, 0x3e, 0xcc, 0x1f,
+    0xea, 0x89, 0x0e, 0x7f, 0xce, 0xdb, 0x7b, 0x5c, 0xef, 0xa7, 0xc0, 0x56,
+    0x97, 0x0b, 0x4c, 0x42, 0xb4, 0x00, 0xd2, 0xbc, 0x23, 0x3e, 0xcc, 0x1f,
     0xf4, 0xb4, 0xfb, 0xb7, 0xaa, 0xba, 0xa2, 0xc4, 0x9f, 0x67, 0x58, 0x4d,
-    0x5a, 0x7c, 0x66, 0xf6, 0x75, 0x96, 0x9d, 0xa6, 0x15, 0xa5, 0xc6, 0x3c,
+    0x5a, 0x7c, 0x67, 0x36, 0x75, 0x96, 0x9d, 0xa6, 0x15, 0xa5, 0xb6, 0x3c,
     0x42, 0x2a, 0x9f, 0xf9, 0xb3, 0x58, 0x20, 0x2f, 0xd8, 0x56, 0x9c, 0x2d,
-    0x62, 0xd2, 0x70, 0x9e, 0xdf, 0xc8, 0x13, 0xb7, 0x8b, 0xf5, 0xa7, 0xc4,
-    0xff, 0xea, 0xd2, 0xd3, 0xfc, 0x0e, 0x61, 0xf2, 0xbb, 0x62, 0xd3, 0xff,
-    0x3f, 0xfb, 0x36, 0x03, 0xfe, 0x65, 0x3c, 0xb4, 0xcc, 0xe5, 0xa1, 0x91,
+    0x62, 0xd2, 0x70, 0x9e, 0xdf, 0xc8, 0x13, 0xb9, 0x8b, 0xf5, 0xa7, 0xc4,
+    0xff, 0xea, 0xd2, 0xd3, 0xfc, 0x0d, 0xe1, 0xf2, 0xbb, 0x62, 0xd3, 0xff,
+    0x3f, 0xfb, 0x38, 0x03, 0xfd, 0xe5, 0x3c, 0xb4, 0xcc, 0xe5, 0xa1, 0x91,
     0x2f, 0x87, 0x1d, 0x4a, 0x9f, 0xe6, 0xd1, 0x99, 0xdc, 0xc7, 0x96, 0x9f,
-    0xd9, 0xd7, 0x7f, 0x98, 0xf2, 0xd3, 0xea, 0x71, 0x9c, 0x65, 0xa7, 0xed,
-    0x6b, 0xa5, 0x6e, 0xfd, 0x68, 0x64, 0x46, 0x11, 0x9d, 0x13, 0xcf, 0xff,
-    0x39, 0x8e, 0xce, 0x00, 0xb7, 0x2b, 0x3a, 0xb4, 0xfe, 0xb8, 0xe3, 0xf1,
-    0xcd, 0x2d, 0x1b, 0x1f, 0xf7, 0x13, 0x27, 0xf7, 0xed, 0x9e, 0x21, 0xb5,
-    0x29, 0xff, 0xb9, 0x45, 0xe7, 0x61, 0xa3, 0x9f, 0xad, 0x3e, 0xce, 0x6d,
-    0x5b, 0xf5, 0xa7, 0xbf, 0xd3, 0x0a, 0xd3, 0xfb, 0xbc, 0xa2, 0xd1, 0x3f,
+    0xd9, 0xd7, 0x7f, 0x98, 0xf2, 0xd3, 0xea, 0x71, 0x9b, 0x65, 0xa7, 0xed,
+    0x6b, 0xa5, 0x6f, 0x3d, 0x68, 0x64, 0x46, 0x11, 0x9d, 0x13, 0xcf, 0xff,
+    0x39, 0x8e, 0xce, 0x00, 0xb6, 0xeb, 0x3a, 0xb4, 0xfe, 0xb8, 0xe3, 0xf1,
+    0xcd, 0x2d, 0x1c, 0x1f, 0xf6, 0xd3, 0x27, 0xf7, 0xed, 0x9e, 0x21, 0xb5,
+    0x29, 0xff, 0xb7, 0x45, 0xe7, 0x61, 0xa3, 0x9f, 0xad, 0x3e, 0xcd, 0xf1,
+    0x5c, 0xf5, 0xa7, 0xbf, 0xd3, 0x0a, 0xd3, 0xfb, 0xbb, 0xa2, 0xd1, 0x3f,
     0x5a, 0x18, 0xf5, 0x30, 0x86, 0x09, 0x1c, 0x42, 0x87, 0xa7, 0xf9, 0xfa,
-    0xd6, 0xef, 0x69, 0xe5, 0xa7, 0xd6, 0x79, 0xb6, 0x72, 0xd0, 0xc7, 0xaf,
-    0x45, 0xd3, 0xfe, 0x16, 0x20, 0x78, 0x6b, 0x6d, 0x2d, 0x0f, 0x82, 0xf4,
-    0x8b, 0x37, 0x80, 0x8b, 0x09, 0x8d, 0x33, 0xbb, 0x56, 0xd0, 0x81, 0x22,
-    0x87, 0x87, 0xfb, 0x0c, 0x03, 0x97, 0x72, 0x18, 0x95, 0x0a, 0xbf, 0xc8,
-    0xf5, 0x18, 0xa7, 0xa1, 0x0a, 0x61, 0x04, 0xff, 0xdc, 0x26, 0xf7, 0x28,
-    0xdc, 0x1f, 0xd6, 0x9f, 0xfe, 0xcd, 0x9d, 0xef, 0xac, 0x0f, 0x28, 0xe5,
-    0xa5, 0xa7, 0x78, 0x59, 0x69, 0xb8, 0x1c, 0x45, 0x78, 0xa1, 0x98, 0xa5,
+    0xd6, 0xef, 0x69, 0xe5, 0xa7, 0xd6, 0x79, 0xb8, 0x72, 0xd0, 0xc7, 0xaf,
+    0x45, 0xd3, 0xfe, 0x16, 0x20, 0x78, 0x6b, 0x8d, 0x2d, 0x0f, 0x82, 0xf4,
+    0x8b, 0x37, 0x80, 0x8b, 0x09, 0x8d, 0x33, 0xbb, 0x57, 0x10, 0x81, 0x22,
+    0x87, 0x87, 0xfb, 0x0c, 0x03, 0x97, 0x6e, 0x18, 0x95, 0x0a, 0xbf, 0xc8,
+    0xf5, 0x18, 0xa7, 0xa1, 0x0a, 0x61, 0x04, 0xff, 0xdb, 0x26, 0xf6, 0xe8,
+    0xdc, 0x1f, 0xd6, 0x9f, 0xfe, 0xce, 0x1d, 0xef, 0xac, 0x0e, 0xe8, 0xe5,
+    0xa5, 0xa7, 0x78, 0x59, 0x69, 0xb6, 0x1c, 0x45, 0x78, 0xa1, 0x98, 0xa5,
     0x01, 0x4f, 0xe1, 0xa3, 0xbd, 0x9f, 0xb3, 0xb7, 0xaa, 0xba, 0xa2, 0xcd,
-    0x9f, 0x3e, 0x87, 0x8d, 0xc5, 0xa0, 0x27, 0xc5, 0x47, 0x13, 0x60, 0x16,
+    0x9f, 0x3e, 0x87, 0x6d, 0xb5, 0xa0, 0x27, 0xc5, 0x47, 0x13, 0x60, 0x16,
     0x9f, 0xf5, 0x38, 0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x40, 0x4f, 0xe2, 0x3b,
-    0x6f, 0xf9, 0x96, 0x2d, 0x20, 0xe2, 0x25, 0x0e, 0x2b, 0xc4, 0x99, 0xfc,
-    0x1a, 0xf6, 0x9c, 0x5f, 0xad, 0x37, 0xdc, 0x5a, 0x7e, 0xd3, 0x1c, 0x59,
-    0xe5, 0xa5, 0xc5, 0xa7, 0x18, 0x2f, 0x2d, 0x36, 0x7b, 0x0d, 0x6f, 0xe2,
-    0x11, 0xfa, 0x21, 0x45, 0x66, 0x6d, 0xda, 0x5a, 0x7f, 0xb9, 0x9d, 0xf0,
+    0x73, 0xf7, 0x96, 0x2d, 0x20, 0xe2, 0x25, 0x0e, 0x2b, 0xb4, 0x99, 0xfc,
+    0x1a, 0xf6, 0x9c, 0x5f, 0xad, 0x37, 0xdb, 0x5a, 0x7e, 0xd3, 0x1c, 0x59,
+    0xe5, 0xa5, 0xb5, 0xa7, 0x18, 0x2f, 0x2d, 0x36, 0x7b, 0x0d, 0x6f, 0xe2,
+    0x11, 0xfa, 0x21, 0x45, 0x66, 0x6e, 0x5a, 0x5a, 0x7f, 0xb7, 0x9d, 0xf0,
     0xb1, 0xf4, 0xb4, 0xf5, 0xea, 0xae, 0xa8, 0xb7, 0xe7, 0xe3, 0x3d, 0x57,
     0xae, 0xad, 0x1d, 0x3d, 0xaf, 0xcb, 0x67, 0xb0, 0x49, 0xe5, 0xa7, 0xde,
-    0x76, 0x8c, 0xd2, 0xd3, 0xb5, 0xc0, 0x2d, 0x3e, 0x33, 0xc2, 0xdf, 0xad,
-    0x07, 0x3c, 0x47, 0xe3, 0x93, 0xff, 0x8f, 0xed, 0x16, 0x98, 0x73, 0xdc,
-    0x25, 0xa7, 0xc0, 0x6d, 0xff, 0xc7, 0x5a, 0x19, 0x12, 0xf8, 0x48, 0x48,
-    0xf3, 0xd4, 0x43, 0xe5, 0xa7, 0xe7, 0xa8, 0x1c, 0x23, 0xad, 0x3f, 0x78,
-    0xb0, 0x0c, 0x75, 0xa3, 0xc7, 0xb1, 0x69, 0x6c, 0xda, 0x15, 0xa7, 0xed,
-    0xb4, 0xd7, 0xc3, 0xad, 0x14, 0x7c, 0xba, 0x23, 0xf8, 0x56, 0x7b, 0x86,
+    0x76, 0x8c, 0xd2, 0xd3, 0xb5, 0xb0, 0x2d, 0x3e, 0x33, 0xc2, 0xdf, 0xad,
+    0x07, 0x3c, 0x47, 0xe3, 0x93, 0xff, 0x8f, 0xed, 0x16, 0x98, 0x73, 0xdb,
+    0x25, 0xa7, 0xc0, 0x6e, 0x7f, 0xc7, 0x5a, 0x19, 0x12, 0xf8, 0x48, 0x48,
+    0xf3, 0xd4, 0x43, 0xe5, 0xa7, 0xe7, 0xa8, 0x1b, 0x23, 0xad, 0x3f, 0x78,
+    0xb0, 0x0c, 0x75, 0xa3, 0xc7, 0xb1, 0x69, 0x6c, 0xda, 0x15, 0xa7, 0xee,
+    0x34, 0xd7, 0xc3, 0xad, 0x14, 0x7c, 0xba, 0x23, 0xf8, 0x56, 0x7b, 0x66,
     0x80, 0xeb, 0x4d, 0x8e, 0x5a, 0x30, 0xdc, 0x09, 0x1c, 0x82, 0xf8, 0x2e,
     0x1f, 0x34, 0x27, 0x1c, 0x46, 0x01, 0x8c, 0x84, 0x89, 0x11, 0xf4, 0x83,
-    0x91, 0x82, 0x7e, 0x5a, 0x30, 0xdc, 0xf3, 0x3c, 0xff, 0x8b, 0x56, 0x65,
-    0xa1, 0xdf, 0x7d, 0xf4, 0xf8, 0x2d, 0x3f, 0xea, 0x70, 0x73, 0xb7, 0xaa,
+    0x71, 0x82, 0x7e, 0x5a, 0x30, 0xdc, 0xf3, 0x3c, 0xff, 0x8b, 0x56, 0x65,
+    0xa1, 0xe7, 0x7e, 0x74, 0xf8, 0x2d, 0x3f, 0xea, 0x70, 0x73, 0xb7, 0xaa,
     0xba, 0xa2, 0x94, 0x9f, 0xe7, 0x07, 0x3b, 0x7a, 0xab, 0xaa, 0x24, 0xe9,
     0x04, 0xe8, 0x8d, 0xa4, 0x98, 0x67, 0xd4, 0x25, 0xb2, 0x3e, 0xe0, 0x5b,
-    0xf8, 0x2c, 0x2b, 0xbc, 0x62, 0x24, 0x43, 0xbf, 0x85, 0xab, 0xc4, 0xc7,
-    0xa4, 0x6c, 0x72, 0x11, 0x7f, 0xc2, 0xd4, 0x4d, 0x75, 0x38, 0xe3, 0xe8,
+    0xf8, 0x2c, 0x2b, 0xbc, 0x62, 0x24, 0x43, 0xcf, 0x85, 0xab, 0xc4, 0xc7,
+    0xa4, 0x6c, 0x6e, 0x11, 0x7f, 0xc2, 0xd4, 0x4d, 0x75, 0x38, 0xe3, 0xe8,
     0x5c, 0x3f, 0x86, 0xfc, 0xfb, 0xb7, 0xaa, 0xba, 0xa2, 0x1f, 0x9f, 0xf5,
     0x38, 0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x29, 0xc8, 0x38, 0x7f, 0x87, 0x33,
     0x9f, 0xc1, 0xce, 0xde, 0xaa, 0xea, 0x88, 0x9e, 0x7e, 0x78, 0xbd, 0xaf,
     0x8e, 0xb4, 0xfb, 0xb7, 0xaa, 0xba, 0xa2, 0x31, 0x90, 0x58, 0xf8, 0xb0,
     0xbe, 0x7f, 0xf0, 0x4f, 0x4e, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x4f, 0x30,
-    0xc9, 0xa1, 0x58, 0x4e, 0xe8, 0x50, 0x70, 0x9a, 0x7d, 0xdb, 0xd5, 0x5d,
+    0xc9, 0xa1, 0x58, 0x4e, 0xe8, 0x50, 0x6c, 0x9a, 0x7d, 0xdb, 0xd5, 0x5d,
     0x51, 0x10, 0x4f, 0xfa, 0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0x95, 0x24,
     0x1c, 0x3f, 0xc3, 0x99, 0xcf, 0xe0, 0xe7, 0x6f, 0x55, 0x75, 0x44, 0x51,
     0x3f, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x11, 0x94, 0xff, 0xe0, 0x9e, 0x9c,
     0x1c, 0xed, 0xea, 0xae, 0xa8, 0x99, 0x67, 0xf0, 0x73, 0xb7, 0xaa, 0xba,
     0xa2, 0xa4, 0x9f, 0xc1, 0xce, 0xde, 0xaa, 0xea, 0x8a, 0xea, 0x7f, 0x07,
     0x3b, 0x7a, 0xab, 0xaa, 0x2c, 0x59, 0xff, 0x8f, 0x4e, 0x0e, 0x76, 0xf5,
-    0x57, 0x54, 0x4f, 0x53, 0xfd, 0x68, 0x69, 0x8b, 0x78, 0x9f, 0x56, 0x9f,
-    0xfd, 0xed, 0x56, 0xc1, 0x1c, 0xf6, 0x88, 0xeb, 0x40, 0x51, 0xaa, 0xc9,
-    0xe4, 0x7b, 0x3e, 0xcf, 0x17, 0xda, 0x5a, 0x6d, 0xf7, 0xdf, 0x62, 0xd1,
-    0xbe, 0xc3, 0xcc, 0x7c, 0xd2, 0x79, 0xfe, 0xdf, 0x0c, 0x1c, 0xef, 0x5a,
-    0xeb, 0x4f, 0x74, 0xfe, 0xd9, 0x69, 0xd7, 0xce, 0x25, 0x38, 0xed, 0xa5,
-    0xa7, 0xff, 0xee, 0x0d, 0x1a, 0x5d, 0x67, 0x3b, 0x35, 0xff, 0xec, 0xb4,
+    0x57, 0x54, 0x4f, 0x53, 0xfd, 0x68, 0x69, 0x8b, 0x98, 0x9f, 0x56, 0x9f,
+    0xfd, 0xed, 0x57, 0x01, 0x1c, 0xf6, 0x88, 0xeb, 0x40, 0x51, 0xaa, 0xc9,
+    0xe4, 0x7b, 0x3e, 0xcf, 0x17, 0xda, 0x5a, 0x6e, 0x77, 0xe7, 0x62, 0xd1,
+    0xce, 0xc3, 0xcc, 0x7c, 0xd2, 0x79, 0xfe, 0xe7, 0x0c, 0x1c, 0xef, 0x5a,
+    0xeb, 0x4f, 0x74, 0xfe, 0xe1, 0x69, 0xd7, 0xcd, 0xa5, 0x38, 0xed, 0xa5,
+    0xa7, 0xff, 0xed, 0x8d, 0x1a, 0x5d, 0x67, 0x3b, 0x35, 0xff, 0xec, 0xb4,
     0xff, 0xfd, 0xd2, 0x7d, 0xa3, 0x85, 0xbb, 0xd2, 0x7d, 0xbf, 0xd6, 0x2d,
-    0x3c, 0x5c, 0x30, 0xeb, 0x4f, 0xfe, 0x23, 0x77, 0x60, 0xd6, 0xda, 0x34,
-    0xda, 0x5a, 0x2e, 0x7d, 0xe2, 0x45, 0x3f, 0xfa, 0xb4, 0x39, 0xf8, 0x78,
-    0x57, 0xae, 0xad, 0x3f, 0x03, 0x0d, 0xf6, 0x5d, 0x69, 0xee, 0x31, 0xee,
-    0xb4, 0x51, 0xe7, 0x78, 0xb6, 0x7f, 0xf0, 0xee, 0x66, 0x16, 0x3e, 0xed,
-    0xdc, 0xde, 0x5a, 0x6a, 0xd9, 0x69, 0xfd, 0x5a, 0xae, 0x7c, 0x36, 0x2d,
+    0x3c, 0x5b, 0x30, 0xeb, 0x4f, 0xfe, 0x23, 0x79, 0x60, 0xd7, 0x1a, 0x34,
+    0xda, 0x5a, 0x2e, 0x7d, 0xe2, 0x45, 0x3f, 0xfa, 0xb4, 0x39, 0xf8, 0x76,
+    0x57, 0xae, 0xad, 0x3f, 0x03, 0x0d, 0xf6, 0x5d, 0x69, 0xed, 0xb1, 0xee,
+    0xb4, 0x51, 0xe7, 0x78, 0xb6, 0x7f, 0xf0, 0xf2, 0x66, 0x16, 0x3f, 0x2e,
+    0x5b, 0xe6, 0x5a, 0x6a, 0xe1, 0x69, 0xfd, 0x5a, 0xad, 0xfc, 0x36, 0x2d,
     0x02, 0x79, 0x3f, 0x0b, 0x45, 0x8a, 0xb6, 0x88, 0x6f, 0xab, 0x67, 0x86,
-    0x8f, 0x08, 0x46, 0x12, 0xde, 0x21, 0x7d, 0x84, 0xcc, 0xe1, 0xaf, 0x2d,
+    0x8e, 0xc8, 0x46, 0x12, 0xde, 0x21, 0x7d, 0x84, 0xcc, 0xe1, 0xaf, 0x2d,
     0x38, 0x4b, 0x4b, 0x4a, 0xd7, 0xc4, 0xda, 0x30, 0xd4, 0xf7, 0x5f, 0xd1,
-    0xd6, 0x8c, 0x3c, 0xf2, 0x2c, 0x9f, 0xee, 0x57, 0x99, 0xed, 0x11, 0xd6,
-    0x9d, 0xcf, 0x8e, 0xb4, 0x11, 0xea, 0xfc, 0x71, 0x3e, 0x13, 0x4b, 0x34,
-    0xb4, 0xfa, 0xda, 0xf5, 0x5d, 0x69, 0xff, 0xb2, 0xda, 0xff, 0x2f, 0xca,
-    0xff, 0x4b, 0x4f, 0xfb, 0x55, 0xb3, 0x0d, 0x03, 0x0d, 0x5a, 0x09, 0x19,
-    0xfc, 0x26, 0x12, 0x7f, 0x22, 0xce, 0x3b, 0x71, 0x69, 0xe1, 0x06, 0xe3,
+    0xd6, 0x8c, 0x3c, 0xf2, 0x2c, 0x9f, 0xed, 0xd7, 0x99, 0xed, 0x11, 0xd6,
+    0x9d, 0xbf, 0x8e, 0xb4, 0x11, 0xea, 0xfc, 0x71, 0x3e, 0x13, 0x4b, 0x34,
+    0xb4, 0xfa, 0xda, 0xf5, 0x5d, 0x69, 0xff, 0xb2, 0xda, 0xff, 0x2f, 0xba,
+    0xff, 0x4b, 0x4f, 0xfb, 0x55, 0xc3, 0x0d, 0x03, 0x0d, 0x5a, 0x09, 0x19,
+    0xfb, 0x26, 0x12, 0x7f, 0x22, 0xce, 0x3b, 0x6d, 0x69, 0xe1, 0x07, 0x23,
     0x56, 0x9f, 0xb3, 0x56, 0xd0, 0xd8, 0xb4, 0x61, 0xf6, 0x50, 0xd8, 0x92,
-    0x4f, 0xbe, 0x37, 0x40, 0x65, 0xa3, 0x11, 0xf8, 0x50, 0xa7, 0xe1, 0x64,
-    0xff, 0xff, 0xee, 0x31, 0x77, 0xac, 0x67, 0x88, 0x7e, 0x76, 0x68, 0x1f,
-    0x57, 0x5c, 0xb4, 0xfa, 0xc0, 0x74, 0xad, 0x5a, 0x7e, 0x05, 0x77, 0xfd,
-    0xec, 0x5a, 0x79, 0xdc, 0x31, 0xfa, 0xd3, 0xfe, 0x12, 0x77, 0xb9, 0x9d,
+    0x4f, 0xbe, 0x37, 0x40, 0x65, 0xa3, 0x11, 0xf8, 0x50, 0xa7, 0xd9, 0x64,
+    0xff, 0xff, 0xed, 0xb1, 0x77, 0xac, 0x67, 0x88, 0x7e, 0x76, 0x68, 0x1f,
+    0x57, 0x5c, 0xb4, 0xfa, 0xc0, 0x74, 0xad, 0x5a, 0x7e, 0x05, 0x77, 0xfe,
+    0x6c, 0x5a, 0x79, 0xdb, 0x31, 0xfa, 0xd3, 0xfe, 0x12, 0x77, 0xb7, 0x9d,
     0x2d, 0x2d, 0x3f, 0xea, 0xcf, 0xd8, 0x68, 0x18, 0x6a, 0xd0, 0xe3, 0xf8,
-    0xfc, 0xf2, 0x7f, 0x56, 0xda, 0xf7, 0xb3, 0x7e, 0xb4, 0xff, 0xe6, 0xcd,
-    0xbd, 0xc6, 0xf0, 0xb7, 0xfa, 0x5a, 0x76, 0xb8, 0x75, 0xa3, 0xc7, 0xcb,
+    0xfc, 0xf2, 0x7f, 0x57, 0x1a, 0xf7, 0xb3, 0x9e, 0xb4, 0xff, 0xe6, 0xce,
+    0x3d, 0xb6, 0xf0, 0xb7, 0xfa, 0x5a, 0x76, 0xb6, 0x75, 0xa3, 0xc7, 0xcb,
     0xf2, 0x4c, 0xfd, 0xf6, 0xbd, 0xac, 0xf2, 0xd3, 0xad, 0xb6, 0xd4, 0xa7,
-    0xff, 0x78, 0xbd, 0xae, 0x16, 0xf1, 0x9d, 0x67, 0x24, 0x12, 0xfe, 0x09,
+    0xff, 0x78, 0xbd, 0xad, 0x97, 0x31, 0x9d, 0x67, 0x24, 0x12, 0xfe, 0x09,
     0x15, 0x3f, 0x26, 0xc3, 0x95, 0x8e, 0x63, 0xc1, 0x14, 0xbc, 0x61, 0xd8,
-    0x4e, 0x9c, 0x8c, 0x61, 0x39, 0xe8, 0x66, 0x4f, 0xf6, 0xce, 0xd6, 0xb0,
-    0x15, 0xe5, 0xa5, 0x8b, 0x43, 0x1e, 0x41, 0xa7, 0x33, 0x6d, 0x4b, 0x4f,
-    0xff, 0xd5, 0xe2, 0xe5, 0x1f, 0x76, 0x69, 0xb3, 0xb8, 0x2b, 0x48, 0xba,
-    0x7d, 0xc2, 0x2d, 0x3d, 0xe6, 0xda, 0xd5, 0xa3, 0x87, 0x93, 0x69, 0x34,
-    0xd6, 0xf1, 0x69, 0xff, 0xcc, 0x37, 0x33, 0xb5, 0xf9, 0x7b, 0x8c, 0xb4,
+    0x4e, 0x9c, 0x8c, 0x61, 0x39, 0xe8, 0x66, 0x4f, 0xf7, 0x0e, 0xd6, 0xb0,
+    0x15, 0xe5, 0xa5, 0x8b, 0x43, 0x1e, 0x41, 0xa7, 0x33, 0x71, 0x4b, 0x4f,
+    0xff, 0xd5, 0xe2, 0xdd, 0x1f, 0x96, 0x69, 0xb3, 0xb8, 0x2b, 0x48, 0xba,
+    0x7d, 0xc2, 0x2d, 0x3d, 0xe6, 0xe2, 0xd5, 0xa3, 0x67, 0x93, 0x69, 0x34,
+    0xd6, 0xed, 0x69, 0xff, 0xcc, 0x37, 0x33, 0xb5, 0xf9, 0x7b, 0x6c, 0xb4,
     0xfe, 0x2b, 0x75, 0x98, 0x36, 0x2d, 0x3f, 0xfb, 0xe6, 0x1a, 0xf3, 0x98,
     0xda, 0xb7, 0xcb, 0x45, 0xd1, 0xb9, 0xf8, 0xbe, 0x92, 0x7c, 0x65, 0x3f,
-    0x5a, 0x6d, 0x50, 0xfe, 0xb4, 0xdf, 0x1d, 0x68, 0x23, 0xc5, 0xe1, 0x74,
-    0xfe, 0x6a, 0xef, 0x2d, 0xa0, 0x25, 0x35, 0xb6, 0xa5, 0x00, 0x3c, 0xab,
-    0x4c, 0xa7, 0x78, 0xb7, 0xe9, 0x04, 0xd1, 0xcf, 0xdb, 0x5c, 0xb5, 0xc2,
+    0x5a, 0x6d, 0x50, 0xfe, 0xb4, 0xdf, 0x1d, 0x68, 0x23, 0xc5, 0xd9, 0x74,
+    0xfe, 0x6a, 0xee, 0xed, 0xa0, 0x25, 0x35, 0xb6, 0xa5, 0x00, 0x3c, 0xab,
+    0x4c, 0xa7, 0x78, 0xb9, 0xe9, 0x04, 0xd1, 0xcf, 0xdc, 0x5c, 0xb5, 0xb2,
     0x5a, 0x7b, 0xe7, 0x7d, 0xe5, 0xa1, 0xe4, 0xc3, 0xb5, 0x08, 0xef, 0x17,
-    0xef, 0x16, 0xcd, 0x6e, 0x96, 0x96, 0x2d, 0x2d, 0xb0, 0xd3, 0x78, 0x5e,
-    0x7f, 0xed, 0x68, 0x02, 0xc0, 0xe3, 0x7b, 0x4b, 0x43, 0x1f, 0x57, 0xc4,
-    0xf3, 0xfe, 0xf1, 0x76, 0xbf, 0x7d, 0xf6, 0x71, 0x69, 0xfe, 0x2b, 0x47,
+    0xf3, 0x16, 0xcd, 0x6e, 0x96, 0x96, 0x2d, 0x2e, 0x30, 0xd3, 0x78, 0x5e,
+    0x7f, 0xed, 0x68, 0x02, 0xc0, 0xdb, 0x7b, 0x4b, 0x43, 0x1f, 0x57, 0xc4,
+    0xf3, 0xfe, 0xf1, 0x76, 0xbf, 0x7d, 0xf6, 0x6d, 0x69, 0xfe, 0x2b, 0x47,
     0x3c, 0x5f, 0x7e, 0xb4, 0xfe, 0xae, 0xfb, 0x42, 0x46, 0xad, 0x0e, 0x46,
-    0x71, 0x11, 0x6f, 0xd0, 0x68, 0xe6, 0x76, 0x17, 0x56, 0x9f, 0xce, 0x23,
-    0xf3, 0x6a, 0xdf, 0xad, 0x3d, 0x5d, 0x67, 0x2d, 0x16, 0x1f, 0x8e, 0xc3,
-    0x47, 0x37, 0x9f, 0xf3, 0x6d, 0xc6, 0x3d, 0xf8, 0x4f, 0x2d, 0x18, 0x7e,
-    0x1d, 0x31, 0x9f, 0xbc, 0xdd, 0x06, 0x0a, 0xd3, 0xf8, 0x1c, 0x61, 0xb6,
-    0xb6, 0x5a, 0x7f, 0xe2, 0x07, 0x1a, 0xed, 0xfd, 0x0f, 0xeb, 0x4f, 0xff,
-    0xf1, 0xe8, 0x76, 0x70, 0x6d, 0xc2, 0x7f, 0x83, 0x80, 0xa7, 0x2e, 0x2f,
+    0x71, 0x11, 0x73, 0xd0, 0x68, 0xe6, 0x76, 0x17, 0x56, 0x9f, 0xce, 0x23,
+    0xef, 0x8a, 0xe7, 0xad, 0x3d, 0x5d, 0x67, 0x2d, 0x16, 0x1f, 0x8f, 0x03,
+    0x47, 0x37, 0x9f, 0xf3, 0x71, 0xb6, 0x3d, 0xf6, 0x4f, 0x2d, 0x18, 0x7e,
+    0x1d, 0x31, 0x9f, 0xbc, 0xdd, 0x06, 0x0a, 0xd3, 0xf8, 0x1b, 0x61, 0xb6,
+    0xb8, 0x5a, 0x7f, 0xe2, 0x06, 0xda, 0xed, 0xfd, 0x0f, 0xeb, 0x4f, 0xff,
+    0xf1, 0xe8, 0x78, 0x70, 0x6d, 0xc2, 0x7f, 0x83, 0x80, 0xa7, 0x2e, 0x2f,
     0x59, 0xef, 0x0f, 0xd6, 0x2d, 0x04, 0x89, 0x61, 0x73, 0x86, 0x4d, 0x62,
-    0x8a, 0xf4, 0x67, 0xf4, 0x36, 0x67, 0xff, 0xb3, 0xda, 0xc7, 0xb8, 0xcf,
-    0xf3, 0x9c, 0x65, 0xa7, 0xff, 0xff, 0xc5, 0x9b, 0x68, 0xb2, 0xdd, 0xd9,
-    0xb3, 0xb4, 0x5e, 0xd1, 0x6c, 0x00, 0x7d, 0x62, 0xd3, 0xff, 0xe2, 0x10,
-    0x6e, 0x33, 0xc5, 0xaa, 0x21, 0xf9, 0xcb, 0x40, 0xa6, 0x5f, 0xe5, 0x07,
-    0xf0, 0x89, 0x9f, 0xd6, 0x57, 0xbf, 0x30, 0x5c, 0xb4, 0xfd, 0xca, 0xed,
-    0xdb, 0xcb, 0x4d, 0x40, 0x5a, 0x7e, 0xed, 0x6b, 0x94, 0xe5, 0xa5, 0xb2,
+    0x8a, 0xf4, 0x67, 0xf4, 0x36, 0x67, 0xff, 0xb3, 0xda, 0xc7, 0xb6, 0xcf,
+    0xf3, 0x7b, 0x65, 0xa7, 0xff, 0xff, 0xc5, 0x9c, 0x68, 0xb2, 0xde, 0x59,
+    0xc3, 0xb4, 0x5e, 0xd1, 0x70, 0x00, 0x7d, 0x62, 0xd3, 0xff, 0xe2, 0x10,
+    0x72, 0x33, 0xc5, 0xaa, 0x21, 0xf9, 0xcb, 0x40, 0xa6, 0x5f, 0xe5, 0x07,
+    0xf0, 0x89, 0x9f, 0xd6, 0x57, 0xbf, 0x30, 0x5c, 0xb4, 0xfd, 0xba, 0xed,
+    0xdb, 0xcb, 0x4d, 0x40, 0x5a, 0x7e, 0xed, 0x6b, 0x74, 0xe5, 0xa5, 0xc2,
     0xd3, 0x15, 0xab, 0x4c, 0x67, 0xeb, 0x43, 0xc6, 0xbb, 0xf1, 0x69, 0xce,
     0xa0, 0x2d, 0x35, 0xb6, 0xad, 0x0e, 0x46, 0xb6, 0x0a, 0xdc, 0xb0, 0x8f,
     0xf4, 0x47, 0x68, 0xdc, 0xef, 0x19, 0xfa, 0x41, 0x3d, 0x69, 0xff, 0xff,
-    0xde, 0x60, 0x73, 0xea, 0x34, 0xf4, 0x0c, 0xc1, 0x37, 0x5a, 0xc1, 0xfd,
-    0x68, 0x02, 0x7f, 0xfb, 0x46, 0xb0, 0x45, 0xf3, 0xfd, 0x9e, 0xe3, 0x77,
+    0xde, 0x60, 0x6f, 0xea, 0x34, 0xf4, 0x0c, 0xc1, 0x37, 0x5a, 0xc1, 0xfd,
+    0x68, 0x02, 0x7f, 0xfc, 0x46, 0xb0, 0x45, 0xf3, 0xfd, 0x9e, 0xdb, 0x77,
     0xd9, 0x75, 0xa7, 0x37, 0x5e, 0x5a, 0x31, 0x57, 0xa9, 0x1b, 0xd4, 0xa3,
-    0x41, 0x37, 0xde, 0x37, 0x9d, 0xaa, 0x79, 0x69, 0xec, 0xf5, 0x3e, 0xad,
-    0x1b, 0x1b, 0xf1, 0x1c, 0x9f, 0xf5, 0x08, 0x30, 0x7e, 0x76, 0x69, 0x69,
-    0xe6, 0xc3, 0x00, 0xb4, 0xff, 0x77, 0x1e, 0xe6, 0x6a, 0x85, 0x69, 0x65,
+    0x41, 0x37, 0xe6, 0x37, 0x9d, 0xaa, 0x79, 0x69, 0xec, 0xf5, 0x3e, 0xad,
+    0x1c, 0x1b, 0xf1, 0x1c, 0x9f, 0xf5, 0x08, 0x30, 0x7e, 0x76, 0x69, 0x69,
+    0xe6, 0xc3, 0x00, 0xb4, 0xff, 0x77, 0x1e, 0xde, 0x6a, 0x85, 0x69, 0x65,
     0xcf, 0x63, 0xa4, 0x33, 0x98, 0x7c, 0xb4, 0xf5, 0x75, 0x9c, 0xb4, 0xff,
-    0xfc, 0x43, 0x44, 0x3f, 0xf5, 0xb9, 0x45, 0xe6, 0xe2, 0xd0, 0x27, 0xed,
-    0xe2, 0x09, 0xf1, 0x7b, 0x5f, 0x58, 0xb4, 0xff, 0xf3, 0x09, 0x7f, 0xed,
-    0xae, 0x5d, 0x31, 0x80, 0xb4, 0x12, 0x26, 0x8e, 0x43, 0xe2, 0x99, 0xfc,
-    0x2d, 0xdf, 0xaf, 0xc6, 0x5a, 0x7c, 0x46, 0xf2, 0x9c, 0xb4, 0xfc, 0x4c,
+    0xfc, 0x43, 0x44, 0x3f, 0xf5, 0xb7, 0x45, 0xe6, 0xda, 0xd0, 0x27, 0xed,
+    0xe2, 0x09, 0xf1, 0x7b, 0x5f, 0x58, 0xb4, 0xff, 0xf3, 0x09, 0x7f, 0xee,
+    0x2e, 0x5d, 0x31, 0x80, 0xb4, 0x12, 0x26, 0x8e, 0x43, 0xe2, 0x99, 0xfc,
+    0x2d, 0xdf, 0xaf, 0xb6, 0x5a, 0x7c, 0x46, 0xee, 0x9c, 0xb4, 0xfc, 0x4c,
     0x7b, 0x32, 0xd5, 0xa6, 0xb6, 0xd5, 0xa2, 0xe7, 0xd9, 0xd2, 0x7b, 0x4b,
-    0x67, 0xfb, 0x1e, 0xa7, 0xab, 0x67, 0x69, 0x20, 0x9b, 0x09, 0xfd, 0x96,
-    0x60, 0xd9, 0x96, 0xad, 0x3b, 0x7e, 0x4f, 0xd6, 0x9c, 0x06, 0x3a, 0xd3,
-    0xfb, 0x3f, 0xc7, 0x9b, 0x6d, 0x2d, 0x0c, 0x79, 0xf8, 0x37, 0x3f, 0xfb,
-    0x07, 0xfc, 0xe1, 0x6d, 0xae, 0x7d, 0xa5, 0xa7, 0xe1, 0xaf, 0xc1, 0x82,
-    0xb4, 0xfc, 0x72, 0xde, 0xce, 0x9a, 0xb4, 0xeb, 0x6d, 0xb5, 0x29, 0xff,
+    0x67, 0xfb, 0x1e, 0xa7, 0xab, 0x87, 0x69, 0x20, 0x9b, 0x09, 0xfd, 0x96,
+    0x60, 0xd9, 0x96, 0xad, 0x3b, 0x9e, 0x4f, 0xd6, 0x9c, 0x06, 0x3a, 0xd3,
+    0xfb, 0x3f, 0xc7, 0x9b, 0x8d, 0x2d, 0x0c, 0x79, 0xf8, 0x37, 0x3f, 0xfb,
+    0x07, 0xfc, 0xd9, 0x71, 0xad, 0xfd, 0xa5, 0xa7, 0xe1, 0xaf, 0xc1, 0x82,
+    0xb4, 0xfc, 0x72, 0xe6, 0xce, 0x9a, 0xb4, 0xeb, 0x6d, 0xb5, 0x29, 0xff,
     0x9b, 0xa3, 0xf5, 0x19, 0x8f, 0x31, 0xd6, 0x09, 0x7f, 0x0c, 0xb8, 0x99,
-    0x91, 0x95, 0x6f, 0xcb, 0xde, 0x86, 0x77, 0x52, 0xa8, 0xcc, 0x5c, 0x34,
+    0x91, 0x95, 0x73, 0xcb, 0xde, 0x86, 0x77, 0x52, 0xa8, 0xcc, 0x5c, 0x34,
     0x41, 0xe4, 0xbf, 0x8a, 0xed, 0x4f, 0x93, 0xe4, 0xb4, 0x89, 0x69, 0xfe,
-    0xf3, 0x77, 0x5c, 0x61, 0xf2, 0xd2, 0x7c, 0x96, 0x9f, 0xef, 0x37, 0x75,
-    0xc6, 0x1f, 0x2d, 0x36, 0x99, 0x69, 0xc7, 0x03, 0x2d, 0x3f, 0x87, 0xd4,
+    0xf3, 0x77, 0x5b, 0x61, 0xf2, 0xd2, 0x7c, 0x96, 0x9f, 0xef, 0x37, 0x75,
+    0xb6, 0x1f, 0x2d, 0x36, 0x99, 0x69, 0xc7, 0x03, 0x2d, 0x3f, 0x87, 0xd4,
     0xf6, 0x9b, 0xcb, 0x41, 0x1e, 0x6d, 0x0d, 0xcf, 0x15, 0x65, 0xd6, 0x9f,
     0xf6, 0x3f, 0x1c, 0xd3, 0xc2, 0xc0, 0x5a, 0x7a, 0xe0, 0x6b, 0x16, 0x9b,
     0xf6, 0x5a, 0x76, 0x8b, 0xab, 0x46, 0x1e, 0x9f, 0xe4, 0x5e, 0x16, 0x9f,
     0xce, 0xc1, 0x20, 0x60, 0xad, 0x3b, 0xda, 0x0b, 0x27, 0xb9, 0x82, 0xaf,
-    0x1b, 0xf1, 0x8b, 0xf2, 0x01, 0x1f, 0xd4, 0x24, 0x7c, 0x5f, 0x36, 0x99,
+    0x1b, 0xed, 0x8b, 0xf2, 0x01, 0x1f, 0xd4, 0x24, 0x7c, 0x5f, 0x36, 0x99,
     0x69, 0xc7, 0x03, 0x2d, 0x3f, 0x87, 0xd4, 0xf6, 0x9b, 0xcb, 0x41, 0x1e,
     0x6d, 0x0d, 0xcf, 0x15, 0x65, 0xd6, 0x9f, 0xf6, 0x3f, 0x1c, 0xd3, 0xc2,
-    0xc0, 0x5a, 0x7a, 0xe0, 0x6b, 0x16, 0x9f, 0x9e, 0xfc, 0xb7, 0xf4, 0xf2,
+    0xc0, 0x5a, 0x7a, 0xe0, 0x6b, 0x16, 0x9f, 0x9e, 0xfc, 0xb9, 0xf4, 0xf2,
     0xd3, 0x7e, 0xcb, 0x4e, 0xd1, 0x75, 0x68, 0xc4, 0x43, 0x5c, 0x8b, 0xf3,
     0x3f, 0x0b, 0x4f, 0xe7, 0x60, 0x90, 0x30, 0x56, 0x9f, 0x86, 0x81, 0x86,
-    0x85, 0x97, 0x24, 0x00, 0x2b, 0x81, 0xf7, 0x94, 0x26, 0xf4, 0x22, 0x38,
+    0x85, 0x97, 0x24, 0x00, 0x2b, 0x81, 0xf7, 0x94, 0x26, 0xf4, 0x22, 0x36,
     0xc5, 0xf9, 0x00, 0x8f, 0xea, 0x18, 0x9e, 0x3d, 0x9f, 0xff, 0x50, 0x33,
     0x04, 0xd3, 0x05, 0x86, 0xc2, 0xea, 0xd3, 0xab, 0xb6, 0x2e, 0x41, 0x29,
-    0xe7, 0x66, 0xce, 0x5c, 0x82, 0x53, 0xb4, 0x40, 0x5c, 0x82, 0x53, 0x5b,
+    0xe7, 0x67, 0x0e, 0x5c, 0x82, 0x53, 0xb4, 0x40, 0x5c, 0x82, 0x53, 0x5b,
     0x6a, 0xe4, 0x12, 0x8b, 0xa2, 0x95, 0xe2, 0x9f, 0x17, 0xda, 0x51, 0x35,
-    0x79, 0x32, 0x09, 0x02, 0x6f, 0xe7, 0xec, 0xe6, 0x60, 0x9a, 0xb4, 0xea,
-    0x1f, 0xd9, 0x52, 0xa9, 0xe1, 0x80, 0x31, 0x9b, 0xf8, 0xce, 0x7b, 0x7b,
-    0x2b, 0x8b, 0x4b, 0x37, 0xe7, 0xf9, 0xfa, 0xcc, 0xfe, 0x79, 0xdf, 0x59,
-    0xe2, 0xb1, 0x29, 0xfc, 0x4e, 0xfa, 0xb5, 0xc2, 0x5a, 0x7e, 0xc7, 0xf4,
-    0x7c, 0xe2, 0xd2, 0x72, 0x53, 0xab, 0xb6, 0x25, 0x09, 0x43, 0x1b, 0x6b,
+    0x79, 0x32, 0x09, 0x02, 0x6f, 0xe7, 0xec, 0xde, 0x60, 0x9a, 0xb4, 0xea,
+    0x1f, 0xd9, 0x52, 0xa9, 0xe1, 0x80, 0x31, 0x9b, 0xf8, 0xce, 0x7b, 0x9b,
+    0x2b, 0x6b, 0x4b, 0x39, 0xe7, 0xf9, 0xfa, 0xcc, 0xfe, 0x79, 0xdf, 0x59,
+    0xe2, 0xb1, 0x29, 0xfc, 0x4e, 0xfa, 0xb5, 0xb2, 0x5a, 0x7e, 0xc7, 0xf4,
+    0x7c, 0xda, 0xd2, 0x72, 0x53, 0xab, 0xb6, 0x25, 0x09, 0x43, 0x1b, 0x6b,
     0x88, 0x1c, 0x72, 0x73, 0x7f, 0x74, 0x82, 0x6b, 0x60, 0xe8, 0xbc, 0x18,
     0x48, 0x43, 0x26, 0x2b, 0x90, 0xee, 0x9e, 0xfa, 0xba, 0xe5, 0xa4, 0x75,
     0xa6, 0xc7, 0x00, 0xd9, 0x3c, 0x45, 0x3e, 0xb7, 0x1e, 0x23, 0xad, 0x3f,
-    0x5d, 0x9e, 0x73, 0x7e, 0x94, 0x89, 0x69, 0xfe, 0xb3, 0xe7, 0x03, 0x9f,
+    0x5d, 0x9e, 0x73, 0x7e, 0x94, 0x89, 0x69, 0xfe, 0xb3, 0xe7, 0x03, 0x7f,
     0x50, 0xad, 0x3f, 0x13, 0x1e, 0xcc, 0xb5, 0x69, 0xf6, 0x58, 0x7c, 0x3a,
     0xd3, 0xb4, 0x5d, 0x5a, 0x04, 0xf0, 0xbc, 0x4f, 0x3f, 0x0d, 0x7f, 0xff,
     0xda, 0x5a, 0x7e, 0x23, 0x4c, 0x63, 0x84, 0x09, 0x84, 0xe0, 0x7f, 0x4e,
     0xf4, 0xdf, 0xe2, 0x18, 0xc4, 0xf3, 0x48, 0xa0, 0x63, 0x37, 0x9e, 0x1c,
-    0xb3, 0xe5, 0xa1, 0xcb, 0x84, 0x38, 0x59, 0xc8, 0xd3, 0x05, 0x67, 0x51,
-    0xfd, 0x78, 0xd2, 0x7f, 0xfd, 0x67, 0x7b, 0x80, 0xa3, 0xf2, 0xbc, 0x2c,
+    0xb3, 0xe5, 0xa1, 0xcb, 0x84, 0x38, 0x59, 0xb8, 0xd3, 0x05, 0x67, 0x51,
+    0xfd, 0x78, 0xd2, 0x7f, 0xfd, 0x67, 0x7b, 0x80, 0xa3, 0xee, 0xbc, 0x2c,
     0xf2, 0xd3, 0xff, 0xff, 0xbe, 0xef, 0xcf, 0x0d, 0x7f, 0xad, 0x61, 0xb9,
-    0xf7, 0x68, 0x0d, 0xc6, 0x5a, 0x75, 0xb6, 0xda, 0x94, 0xce, 0x64, 0x82,
-    0x5f, 0xc3, 0x26, 0x27, 0x62, 0xaf, 0x61, 0x35, 0x3f, 0x58, 0xf3, 0xb7,
-    0xce, 0xd8, 0xb4, 0xf7, 0x2b, 0x6b, 0x56, 0x9f, 0x59, 0xd2, 0x63, 0xad,
-    0x3f, 0xfc, 0xc6, 0x78, 0x5b, 0xfa, 0xf7, 0xe6, 0x0b, 0x96, 0x8d, 0xf4,
+    0xf7, 0x68, 0x0d, 0xb6, 0x5a, 0x75, 0xb6, 0xda, 0x94, 0xce, 0x64, 0x82,
+    0x5f, 0xc3, 0x26, 0x27, 0x62, 0xaf, 0x61, 0x35, 0x3f, 0x58, 0xf3, 0xb9,
+    0xce, 0xd8, 0xb4, 0xf6, 0xeb, 0x8b, 0x56, 0x9f, 0x59, 0xd2, 0x63, 0xad,
+    0x3f, 0xfc, 0xc6, 0x78, 0x5b, 0xfa, 0xf7, 0xe6, 0x0b, 0x96, 0x8e, 0x74,
     0x8d, 0x12, 0x36, 0xa2, 0x3f, 0x13, 0xce, 0x30, 0x0f, 0x2d, 0x39, 0xc4,
     0x2b, 0x4f, 0x58, 0x63, 0xed, 0xab, 0x4f, 0xde, 0x67, 0x87, 0xed, 0x2d,
     0x16, 0x22, 0x39, 0xc3, 0xd4, 0x35, 0xe2, 0x89, 0xab, 0x4b, 0x4d, 0xf3,
-    0xe4, 0xb4, 0x09, 0xb1, 0xf8, 0x56, 0x6a, 0x0b, 0xe0, 0xeb, 0x34, 0xf7,
+    0xe4, 0xb4, 0x09, 0xb1, 0xf8, 0x56, 0x6a, 0x0b, 0xe0, 0xeb, 0x34, 0xf9,
     0xc3, 0x17, 0xce, 0x7a, 0xf9, 0x84, 0x6d, 0x2b, 0xd6, 0xc8, 0xc1, 0xdc,
-    0xea, 0x08, 0xfb, 0x72, 0x5a, 0x79, 0xb0, 0xc8, 0xbc, 0x36, 0x76, 0x87,
-    0xf9, 0x47, 0x22, 0xf4, 0x74, 0xbd, 0x8c, 0x80, 0xf1, 0xbc, 0xf2, 0x71,
+    0xea, 0x08, 0xfb, 0x72, 0x5a, 0x79, 0xb0, 0xc8, 0xbc, 0x36, 0x78, 0x87,
+    0xf9, 0x47, 0x22, 0xf4, 0x74, 0xbd, 0x8c, 0x80, 0xf1, 0xbc, 0xee, 0x71,
     0x9a, 0xa1, 0x23, 0xf9, 0x08, 0xc2, 0x53, 0x53, 0x8e, 0x7e, 0xa6, 0x15,
-    0xfd, 0x1b, 0xbe, 0xf4, 0x60, 0xe6, 0x43, 0x3d, 0xf5, 0xba, 0x7f, 0xf0,
+    0xfd, 0x1b, 0xbf, 0x34, 0x60, 0xe6, 0x43, 0x3d, 0xf5, 0xba, 0x7f, 0xf0,
     0x4f, 0x4e, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x51, 0x53, 0xf8, 0x39, 0xdb,
     0xd5, 0x5d, 0x51, 0x74, 0x4f, 0xfc, 0x1a, 0x70, 0x73, 0xb7, 0xaa, 0xba,
     0xa2, 0x4b, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x5e, 0x53, 0x53, 0xf5, 0xa4,
-    0x1c, 0x3c, 0xad, 0xe3, 0x38, 0x67, 0x66, 0x55, 0x61, 0x3b, 0x8f, 0xb0,
-    0xfa, 0xea, 0xaf, 0x1f, 0x1c, 0xfb, 0x91, 0x89, 0x8d, 0xad, 0x4d, 0xd4,
+    0x1c, 0x3c, 0xae, 0x63, 0x38, 0x67, 0x66, 0x55, 0x61, 0x3b, 0x8f, 0xb0,
+    0xfa, 0xea, 0xaf, 0x1f, 0x1c, 0xfb, 0x71, 0x89, 0x8d, 0xad, 0x4d, 0xd4,
     0xa0, 0x0f, 0x2a, 0xfc, 0x7c, 0xfe, 0x13, 0x33, 0xff, 0x82, 0x7a, 0x70,
-    0x73, 0xb7, 0xaa, 0xba, 0xa2, 0x59, 0x9b, 0xee, 0x2d, 0x3f, 0x03, 0xdf,
-    0x53, 0xb4, 0xb4, 0xfe, 0x6e, 0x9b, 0x6d, 0x73, 0x4b, 0x4f, 0x5e, 0xaa,
+    0x73, 0xb7, 0xaa, 0xba, 0xa2, 0x59, 0x9b, 0xed, 0xad, 0x3f, 0x03, 0xdf,
+    0x53, 0xb4, 0xb4, 0xfe, 0x6e, 0x9b, 0x6d, 0x6f, 0x4b, 0x4f, 0x5e, 0xaa,
     0xea, 0x88, 0xce, 0x7e, 0x26, 0x3d, 0x99, 0x6a, 0xd3, 0x0b, 0x96, 0x91,
-    0xd6, 0x9f, 0x17, 0x79, 0x41, 0xe9, 0xe9, 0x1c, 0xb4, 0xc1, 0x59, 0xfa,
+    0xd6, 0x9f, 0x17, 0x77, 0x41, 0xe9, 0xe9, 0x1c, 0xb4, 0xc1, 0x59, 0xfa,
     0x9c, 0x5d, 0xae, 0xad, 0x3f, 0xf6, 0x8b, 0xed, 0x60, 0xfc, 0xec, 0xd2,
-    0xd3, 0xb4, 0xdb, 0x2d, 0x0f, 0x27, 0x15, 0xd3, 0x43, 0xbf, 0x8a, 0x66,
-    0x8a, 0xfc, 0x89, 0x3f, 0xce, 0xd6, 0x36, 0xe6, 0xd1, 0xd6, 0x9f, 0xfb,
-    0xc5, 0xda, 0xff, 0x76, 0xce, 0x63, 0xad, 0x3e, 0x16, 0x76, 0xa9, 0x68,
+    0xd3, 0xb4, 0xdc, 0x2d, 0x0f, 0x27, 0x15, 0xd3, 0x43, 0xbf, 0x8a, 0x66,
+    0x8a, 0xfc, 0x89, 0x3f, 0xce, 0xd6, 0x37, 0x26, 0xd1, 0xd6, 0x9f, 0xfb,
+    0xc5, 0xda, 0xff, 0x97, 0x0e, 0x63, 0xad, 0x3e, 0x16, 0x76, 0xa9, 0x68,
     0x23, 0xec, 0x7e, 0x8f, 0x3e, 0x37, 0xde, 0x63, 0xad, 0x31, 0x8e, 0x5a,
-    0x08, 0xdf, 0xd1, 0x44, 0xff, 0xf7, 0xde, 0xd6, 0xce, 0x61, 0xff, 0x5b,
-    0xbe, 0x15, 0xa7, 0x37, 0x99, 0x69, 0x10, 0x0f, 0xbd, 0xfa, 0xac, 0xdc,
-    0x79, 0x69, 0xfd, 0xb6, 0x87, 0x3a, 0x06, 0x5a, 0x08, 0xf2, 0x68, 0x5e,
-    0x7b, 0xdc, 0xfb, 0x79, 0x69, 0xfe, 0xf1, 0x5b, 0xa6, 0xe5, 0x6f, 0xd6,
-    0x96, 0x11, 0xf1, 0x1c, 0x9a, 0x7f, 0xdc, 0x62, 0xe1, 0x30, 0xbd, 0xf2,
-    0xd3, 0xb4, 0x56, 0xad, 0x3f, 0xb3, 0x4d, 0xed, 0x70, 0x96, 0x90, 0x5f,
-    0x05, 0xd6, 0x0b, 0x05, 0xf2, 0x3b, 0x8d, 0x96, 0x1e, 0x85, 0x2f, 0x18,
-    0xaa, 0x11, 0xdf, 0xb9, 0x0c, 0x20, 0xb4, 0x4c, 0xfc, 0xf7, 0x78, 0x72,
+    0x08, 0xdf, 0xd1, 0x44, 0xff, 0xf7, 0xde, 0xd7, 0x0e, 0x61, 0xff, 0x5c,
+    0xbe, 0x15, 0xa7, 0x37, 0x99, 0x69, 0x10, 0x0f, 0xbd, 0xfa, 0xac, 0xdb,
+    0x79, 0x69, 0xfd, 0xc6, 0x87, 0x3a, 0x06, 0x5a, 0x08, 0xf2, 0x68, 0x5e,
+    0x7b, 0xdb, 0xfb, 0x99, 0x69, 0xfe, 0xf1, 0x5b, 0xa6, 0xdd, 0x73, 0xd6,
+    0x96, 0x11, 0xf1, 0x1c, 0x9a, 0x7f, 0xdb, 0x62, 0xd9, 0x30, 0xbd, 0xf2,
+    0xd3, 0xb4, 0x56, 0xad, 0x3f, 0xb3, 0x4d, 0xed, 0x6c, 0x96, 0x90, 0x5f,
+    0x05, 0xd6, 0x0b, 0x05, 0xf2, 0x3b, 0x8e, 0x16, 0x1e, 0x85, 0x2e, 0xd8,
+    0xaa, 0x11, 0xdf, 0xb9, 0x0c, 0x20, 0xb4, 0x4c, 0xfc, 0xf7, 0x98, 0x72,
     0x7f, 0xf0, 0x4f, 0x4e, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x4d, 0x33, 0xff,
     0x82, 0x7a, 0x70, 0x73, 0xb7, 0xaa, 0xba, 0xa2, 0x71, 0x9f, 0xfc, 0x13,
     0xd3, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x14, 0x0c, 0xfb, 0xb7, 0xaa, 0xba,
     0xa2, 0xe0, 0x9f, 0x87, 0x3d, 0x72, 0xd2, 0xd3, 0x31, 0x2d, 0x3f, 0x85,
-    0xcc, 0x40, 0xc3, 0xad, 0x20, 0xe2, 0x28, 0x8e, 0x67, 0xc2, 0xbf, 0x85,
+    0xcc, 0x40, 0xc3, 0xad, 0x20, 0xe2, 0x28, 0x8e, 0x67, 0xb2, 0xbf, 0x85,
     0x27, 0xf1, 0x04, 0xc6, 0xff, 0x2d, 0x5a, 0x7f, 0x9c, 0x1c, 0xed, 0xea,
-    0xae, 0xa8, 0x92, 0x64, 0x1e, 0x1f, 0xc5, 0x1a, 0x4f, 0xa8, 0x20, 0xb7,
+    0xae, 0xa8, 0x92, 0x64, 0x1d, 0x9f, 0xc5, 0x1a, 0x4f, 0xa8, 0x20, 0xb7,
     0xf5, 0xa2, 0xc6, 0x49, 0x7b, 0xa7, 0x6d, 0x6f, 0x0c, 0xe7, 0x95, 0x7f,
     0x55, 0xd4, 0x3d, 0xfd, 0x0b, 0xb7, 0xe4, 0xf3, 0xfe, 0x36, 0x83, 0x9d,
     0xbd, 0x55, 0xd5, 0x16, 0xa4, 0xff, 0xa9, 0xc1, 0xce, 0xde, 0xaa, 0xea,
     0x89, 0x56, 0x16, 0x90, 0x4d, 0x44, 0xe9, 0xd2, 0x9f, 0x52, 0x67, 0xf0,
     0x73, 0xb7, 0xaa, 0xba, 0xa2, 0x29, 0x9f, 0xc1, 0xce, 0xde, 0xaa, 0xea,
-    0x88, 0xd2, 0x6f, 0xb8, 0xb4, 0xff, 0xff, 0xb7, 0xf5, 0xf6, 0xb9, 0x80,
+    0x88, 0xd2, 0x6f, 0xb6, 0xb4, 0xff, 0xff, 0xb9, 0xf5, 0xf6, 0xb7, 0x80,
     0x30, 0xcd, 0x11, 0x9e, 0x2d, 0x3c, 0x62, 0xd3, 0xd7, 0xaa, 0xba, 0xa2,
     0x9f, 0x9b, 0xcc, 0xb4, 0x74, 0xf0, 0xbc, 0x5b, 0x3f, 0xef, 0x3c, 0xde,
-    0xe5, 0x3d, 0xbb, 0x16, 0x9f, 0xfc, 0x47, 0x1f, 0xb8, 0x5d, 0xcd, 0x3c,
+    0xdd, 0x3d, 0xcb, 0x16, 0x9f, 0xfc, 0x47, 0x1f, 0xb6, 0x5d, 0xcd, 0x3c,
     0xcb, 0x4f, 0xfd, 0xf7, 0xbb, 0xf3, 0xda, 0xbd, 0x3d, 0xa5, 0xa7, 0xfe,
     0xc7, 0xbe, 0x1b, 0x35, 0x7a, 0x7b, 0x4b, 0x48, 0x2f, 0x82, 0xa0, 0xb6,
     0x17, 0xc8, 0x5c, 0x11, 0x15, 0x20, 0x8a, 0x4f, 0x92, 0x67, 0xff, 0x04,
     0xf4, 0xe0, 0xe7, 0x6f, 0x55, 0x75, 0x44, 0xd5, 0x3f, 0x83, 0x9d, 0xbd,
     0x55, 0xd5, 0x15, 0xdc, 0xf5, 0xea, 0xae, 0xa8, 0xaf, 0xa7, 0x5b, 0x6d,
-    0xa9, 0x48, 0x52, 0x09, 0x7f, 0x1d, 0x3e, 0xa3, 0xa6, 0x4e, 0x73, 0x6c,
-    0xb4, 0xe7, 0xb3, 0x8b, 0x4f, 0xfb, 0x39, 0x6d, 0x7b, 0x4d, 0x96, 0x2d,
+    0xa9, 0x48, 0x52, 0x09, 0x7f, 0x1d, 0x3e, 0xa3, 0xa6, 0x4e, 0x73, 0x70,
+    0xb4, 0xe7, 0xb3, 0x6b, 0x4f, 0xfb, 0x37, 0x6d, 0x7b, 0x4d, 0x96, 0x2d,
     0x2f, 0x2d, 0x3f, 0xc5, 0xdc, 0xb0, 0x9d, 0x80, 0x5a, 0x3a, 0x78, 0xf4,
-    0x21, 0x2e, 0xad, 0x0b, 0x4b, 0x84, 0x5d, 0x74, 0x42, 0x41, 0xc4, 0xcf,
-    0xde, 0x22, 0xa1, 0xc1, 0x1c, 0xd4, 0x21, 0x77, 0x8e, 0xe7, 0xff, 0x04,
+    0x21, 0x2e, 0xad, 0x0b, 0x4b, 0x64, 0x5d, 0x74, 0x42, 0x41, 0xc4, 0xcf,
+    0xde, 0x22, 0xa1, 0xc1, 0x1c, 0xd4, 0x21, 0x79, 0x8e, 0xe7, 0xff, 0x04,
     0xf4, 0xe0, 0xe7, 0x6f, 0x55, 0x75, 0x44, 0xf7, 0x3f, 0x83, 0x9d, 0xbd,
     0x55, 0xd5, 0x17, 0x0c, 0xff, 0xc1, 0xa7, 0x07, 0x3b, 0x7a, 0xab, 0xaa,
     0x24, 0xf8, 0x65, 0xeb, 0x4b, 0x0e, 0x9c, 0x7c, 0x6c, 0xa4, 0x9b, 0xb8,
-    0xbc, 0xab, 0xd8, 0xf9, 0xf8, 0xc9, 0xa5, 0x57, 0xe7, 0xd3, 0xf8, 0x39,
+    0xbc, 0xab, 0xd8, 0xf9, 0xf6, 0xc9, 0xa5, 0x57, 0xe7, 0xd3, 0xf8, 0x39,
     0xdb, 0xd5, 0x5d, 0x51, 0x15, 0x4f, 0xbb, 0x7a, 0xab, 0xaa, 0x29, 0x99,
-    0xf8, 0xb9, 0x95, 0xb7, 0xeb, 0x48, 0x38, 0x7c, 0x1b, 0xc6, 0x73, 0xff,
+    0xf8, 0xb7, 0x95, 0xc7, 0xeb, 0x48, 0x38, 0x7c, 0x1c, 0xc6, 0x73, 0xff,
     0x82, 0x7a, 0x70, 0x73, 0xb7, 0xaa, 0xba, 0xa2, 0x67, 0x9f, 0xfc, 0x13,
     0xd3, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x14, 0x6c, 0x58, 0x9b, 0x90, 0x21,
     0x3c, 0x69, 0x57, 0x95, 0x67, 0xfe, 0x0d, 0x38, 0x39, 0xdb, 0xd5, 0x5d,
-    0x51, 0x1d, 0x4d, 0xf7, 0x16, 0x9e, 0xbd, 0xfe, 0xba, 0xd3, 0xf1, 0x60,
-    0x83, 0x2c, 0x5a, 0x7f, 0xc3, 0x86, 0xd3, 0xda, 0xe3, 0x01, 0x69, 0xeb,
-    0xd5, 0x5d, 0x51, 0x4d, 0x4f, 0xb3, 0x6b, 0x6b, 0x8b, 0x41, 0x22, 0x4f,
-    0xa7, 0xc2, 0x5b, 0x3f, 0xfb, 0x85, 0xed, 0x56, 0xd8, 0xf3, 0x73, 0x4b,
-    0x4f, 0xfc, 0xee, 0x10, 0xd9, 0xaf, 0x9b, 0xaf, 0x2d, 0x3f, 0xda, 0xd5,
+    0x51, 0x1d, 0x4d, 0xf6, 0xd6, 0x9e, 0xbd, 0xfe, 0xba, 0xd3, 0xf1, 0x60,
+    0x83, 0x2c, 0x5a, 0x7f, 0xc3, 0x86, 0xd3, 0xda, 0xdb, 0x01, 0x69, 0xeb,
+    0xd5, 0x5d, 0x51, 0x4d, 0x4f, 0xb3, 0x8b, 0x6b, 0x6b, 0x41, 0x22, 0x4f,
+    0xa7, 0xc2, 0x5b, 0x3f, 0xfb, 0x65, 0xed, 0x57, 0x18, 0xf3, 0x6f, 0x4b,
+    0x4f, 0xfc, 0xed, 0x90, 0xd9, 0xaf, 0x9b, 0xaf, 0x2d, 0x3f, 0xda, 0xd5,
     0xcb, 0xbd, 0x2b, 0x16, 0x99, 0x8d, 0x5a, 0x7e, 0x26, 0x3d, 0x99, 0x6a,
-    0xd3, 0xff, 0xd9, 0xee, 0x63, 0xbb, 0x43, 0xee, 0x53, 0x96, 0x99, 0xbf,
-    0x5a, 0x7f, 0x56, 0xcd, 0x72, 0xb8, 0x7a, 0x89, 0x01, 0x2d, 0x31, 0x36,
-    0x38, 0x8f, 0xed, 0x42, 0x9a, 0x41, 0x7c, 0x15, 0x8a, 0xb0, 0xb8, 0x08,
-    0xf2, 0x17, 0xd7, 0x2e, 0xd9, 0x2b, 0xa8, 0xde, 0x8c, 0xde, 0x7d, 0xdb,
+    0xd3, 0xff, 0xd9, 0xed, 0xe3, 0xbb, 0x43, 0xed, 0xd3, 0x96, 0x99, 0xbf,
+    0x5a, 0x7f, 0x57, 0x0d, 0x72, 0xb8, 0x7a, 0x89, 0x01, 0x2d, 0x31, 0x36,
+    0x36, 0x8f, 0xed, 0x42, 0x9a, 0x41, 0x7c, 0x15, 0x8a, 0xb0, 0xb8, 0x08,
+    0xf2, 0x17, 0xd7, 0x2e, 0xe1, 0x2b, 0xa8, 0xde, 0x8c, 0xde, 0x7d, 0xdb,
     0xd5, 0x5d, 0x51, 0x55, 0x4f, 0xfa, 0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8,
     0x9b, 0x64, 0x1c, 0x3f, 0xc3, 0x99, 0xcf, 0xe0, 0xe7, 0x6f, 0x55, 0x75,
     0x45, 0x7f, 0x3f, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x16, 0x34, 0xff, 0x67,
     0xbd, 0x9d, 0x06, 0x0a, 0xd3, 0x8d, 0x76, 0x2d, 0x38, 0xba, 0x11, 0x3d,
     0x2d, 0x1b, 0x4f, 0xbb, 0x7a, 0xab, 0xaa, 0x2d, 0x59, 0xff, 0x53, 0x83,
     0x9d, 0xbd, 0x55, 0xd5, 0x14, 0x14, 0x83, 0x87, 0xf8, 0x73, 0x39, 0xeb,
-    0xd5, 0x5d, 0x51, 0x71, 0x4f, 0xdf, 0x56, 0xc0, 0xce, 0xad, 0x1d, 0x3d,
+    0xd5, 0x5d, 0x51, 0x71, 0x4f, 0xdf, 0x57, 0x00, 0xce, 0xad, 0x1d, 0x3d,
     0xbd, 0x16, 0xce, 0xeb, 0x1d, 0x69, 0xea, 0x7d, 0x2b, 0x16, 0x9e, 0x7d,
     0x7d, 0x2b, 0xad, 0x3f, 0x98, 0xf4, 0xfd, 0xba, 0xfa, 0xb4, 0xfd, 0x9d,
-    0xaf, 0x7c, 0xcb, 0x4f, 0xff, 0x50, 0xb1, 0x3e, 0xe8, 0xb5, 0x9c, 0xcd,
-    0xe5, 0xa4, 0xf0, 0x0f, 0xf5, 0xf9, 0x64, 0x83, 0x89, 0xb3, 0x74, 0x8b,
-    0x83, 0x74, 0x49, 0xe2, 0x73, 0x21, 0x67, 0x3e, 0xed, 0xea, 0xae, 0xa8,
+    0xaf, 0x7c, 0xcb, 0x4f, 0xff, 0x50, 0xb1, 0x3e, 0xe8, 0xb5, 0x9b, 0xce,
+    0x65, 0xa4, 0xf0, 0x0f, 0xf5, 0xf9, 0x64, 0x83, 0x89, 0xb3, 0x74, 0x8b,
+    0x63, 0x74, 0x49, 0xe2, 0x73, 0x21, 0x67, 0x3e, 0xed, 0xea, 0xae, 0xa8,
     0xba, 0x67, 0xcc, 0x34, 0x06, 0x5a, 0x41, 0xc3, 0xd7, 0x23, 0x39, 0xff,
-    0xec, 0x1f, 0x11, 0xf8, 0xc4, 0x35, 0xd3, 0x56, 0x9f, 0xff, 0x8e, 0x1c,
-    0x73, 0x77, 0xfd, 0xda, 0xe3, 0x1e, 0x85, 0x69, 0xec, 0xb7, 0x7d, 0x1d,
-    0x69, 0xfe, 0x2e, 0x8b, 0x6c, 0x7a, 0xf2, 0xd3, 0xff, 0xf7, 0x07, 0xea,
-    0xdf, 0xe3, 0xb9, 0x86, 0xe0, 0x1e, 0xf9, 0x69, 0xaf, 0x8b, 0x43, 0x1f,
+    0xec, 0x1f, 0x11, 0xf6, 0xc4, 0x35, 0xd3, 0x56, 0x9f, 0xff, 0x8e, 0x1c,
+    0x73, 0x77, 0xfe, 0x5a, 0xdb, 0x1e, 0x85, 0x69, 0xec, 0xb7, 0x9d, 0x1d,
+    0x69, 0xfe, 0x2e, 0x8b, 0x70, 0x7a, 0xf2, 0xd3, 0xff, 0xf6, 0xc7, 0xea,
+    0xe7, 0xe3, 0xb7, 0x86, 0xe0, 0x1e, 0xf9, 0x69, 0xaf, 0x8b, 0x43, 0x1f,
     0xb1, 0xac, 0x33, 0xfe, 0x76, 0x77, 0xb4, 0xeb, 0x32, 0xd5, 0xa7, 0xfe,
     0xc0, 0x33, 0x9b, 0xac, 0x65, 0x0a, 0xd3, 0xfe, 0x7f, 0x8e, 0x38, 0x09,
     0xfb, 0x01, 0x68, 0x3a, 0x20, 0xa9, 0x06, 0x6a, 0x0f, 0x53, 0x05, 0xf1,
-    0x11, 0x90, 0xc1, 0x8e, 0x27, 0xe8, 0x31, 0xd9, 0x4f, 0xfb, 0xad, 0xb7,
-    0x2b, 0x5e, 0x6b, 0x56, 0x9b, 0xee, 0x2d, 0x3e, 0x1c, 0xe5, 0x79, 0x69,
-    0xf9, 0xd5, 0x8f, 0xd8, 0x0b, 0x43, 0xe2, 0x7a, 0x5b, 0xed, 0x25, 0x9f,
-    0xbf, 0xa7, 0x9c, 0x5e, 0x5a, 0x7f, 0xff, 0x98, 0x1c, 0xcd, 0xbc, 0x35,
-    0xcf, 0xeb, 0x5a, 0xae, 0xdd, 0x69, 0xfd, 0x99, 0x86, 0x9b, 0x4e, 0x5a,
+    0x11, 0x90, 0xc1, 0x8d, 0xa7, 0xe8, 0x31, 0xd9, 0x4f, 0xfb, 0xad, 0xc6,
+    0xeb, 0x5e, 0x6b, 0x56, 0x9b, 0xed, 0xad, 0x3e, 0x1c, 0xdd, 0x79, 0x69,
+    0xf9, 0xd5, 0x8f, 0xd8, 0x0b, 0x43, 0xe2, 0x7a, 0x5c, 0xed, 0x25, 0x9f,
+    0xbf, 0xa7, 0x9c, 0x5e, 0x5a, 0x7f, 0xff, 0x98, 0x1b, 0xce, 0x3c, 0x35,
+    0xbf, 0xeb, 0x5a, 0xae, 0xdd, 0x69, 0xfd, 0x99, 0x86, 0x9b, 0x4e, 0x5a,
     0x0d, 0x44, 0xc7, 0x99, 0xa7, 0xcf, 0xcb, 0xb8, 0x05, 0xa7, 0xff, 0xb5,
-    0xc2, 0xd7, 0xb9, 0x80, 0x2d, 0x60, 0xad, 0x3b, 0x8c, 0x38, 0x7e, 0xbf,
-    0x14, 0x4f, 0x5e, 0xaa, 0xea, 0x89, 0x32, 0x7d, 0x77, 0xcd, 0xdf, 0x37,
-    0xd3, 0xe2, 0xb4, 0xfe, 0xcd, 0xbd, 0xea, 0x76, 0x96, 0x86, 0x3f, 0x2e,
+    0xb2, 0xd7, 0xb7, 0x80, 0x2d, 0x60, 0xad, 0x3b, 0x6c, 0x38, 0x7e, 0xbf,
+    0x14, 0x4f, 0x5e, 0xaa, 0xea, 0x89, 0x32, 0x7d, 0x77, 0xcd, 0xe7, 0x39,
+    0xd3, 0xe2, 0xb4, 0xfe, 0xce, 0x3d, 0xea, 0x76, 0x96, 0x86, 0x3f, 0x2e,
     0x9f, 0xc3, 0x91, 0xe5, 0xd3, 0x23, 0xc2, 0x7e, 0x7e, 0x07, 0xef, 0x8e,
-    0xbf, 0xde, 0x5a, 0x76, 0x88, 0x0b, 0x4f, 0xc2, 0xdb, 0x1e, 0xbc, 0xb4,
-    0xff, 0x6d, 0x72, 0xee, 0x9e, 0x6a, 0x5a, 0x6e, 0x13, 0xc8, 0x80, 0xe8,
-    0xdf, 0x8b, 0x27, 0xbc, 0x47, 0xa5, 0xa7, 0xf1, 0x73, 0x4d, 0xe6, 0x02,
-    0xd1, 0x87, 0xa3, 0xe2, 0x09, 0xfe, 0x6e, 0x78, 0xac, 0x3b, 0x5d, 0x69,
-    0xff, 0xf5, 0x5f, 0xad, 0xc1, 0xcf, 0x60, 0x6d, 0xb6, 0xd4, 0xa1, 0xc8,
+    0xbf, 0xe6, 0x5a, 0x76, 0x88, 0x0b, 0x4f, 0xc2, 0xdc, 0x1e, 0xbc, 0xb4,
+    0xff, 0x71, 0x72, 0xee, 0x9e, 0x6a, 0x5a, 0x6d, 0x93, 0xc8, 0x80, 0xe8,
+    0xdf, 0x8b, 0x27, 0xbc, 0x47, 0xa5, 0xa7, 0xf1, 0x6f, 0x4d, 0xe6, 0x02,
+    0xd1, 0x87, 0xa3, 0xe2, 0x09, 0xfe, 0x6d, 0xf8, 0xac, 0x3b, 0x5d, 0x69,
+    0xff, 0xf5, 0x5f, 0xad, 0xb1, 0xcf, 0x60, 0x6d, 0xb6, 0xd4, 0xa1, 0xc8,
     0xaf, 0xd1, 0x0f, 0x8e, 0x27, 0xeb, 0xdf, 0xeb, 0x8b, 0x2d, 0x3f, 0xfb,
-    0xe1, 0xaf, 0x37, 0x75, 0xc6, 0x1f, 0x2d, 0x3a, 0xdb, 0x6d, 0x4a, 0x7a,
+    0xe1, 0xaf, 0x37, 0x75, 0xb6, 0x1f, 0x2d, 0x3a, 0xdb, 0x6d, 0x4a, 0x7a,
     0xc3, 0x96, 0x92, 0x09, 0x7f, 0x33, 0x01, 0x69, 0x76, 0x8f, 0x18, 0x4c,
-    0x67, 0xed, 0x68, 0xb0, 0x7f, 0x5a, 0x77, 0x73, 0x7e, 0xb4, 0xb3, 0xa7,
-    0x96, 0x72, 0xc9, 0xf1, 0x7a, 0xcc, 0xb5, 0x69, 0xfe, 0x6b, 0xf0, 0x7e,
-    0xf3, 0x69, 0x69, 0xff, 0xf7, 0xb9, 0x8e, 0xdd, 0x7e, 0x17, 0xb6, 0xb9,
-    0x75, 0x68, 0xea, 0x2e, 0x0e, 0x50, 0x27, 0x13, 0xfd, 0x95, 0xfe, 0xec,
-    0x05, 0x39, 0x69, 0xfe, 0xdc, 0x3b, 0xb5, 0x65, 0x57, 0x16, 0x9f, 0xfd,
-    0x56, 0x70, 0x98, 0x71, 0xe2, 0xe9, 0x2d, 0x18, 0x8e, 0xbf, 0x17, 0xfc,
-    0x73, 0x69, 0xe4, 0xf8, 0x73, 0x95, 0xe5, 0xa7, 0xfc, 0xe6, 0x1b, 0x0c,
-    0xe8, 0xfc, 0xe5, 0xa3, 0x7e, 0x7c, 0xaf, 0x12, 0xcf, 0x7a, 0xcc, 0x02,
-    0xd3, 0xfc, 0x46, 0x99, 0xdf, 0xce, 0x5a, 0x5a, 0x1e, 0x3d, 0xdd, 0xe2,
+    0x67, 0xed, 0x68, 0xb0, 0x7f, 0x5a, 0x77, 0x73, 0x9e, 0xb4, 0xb3, 0xa7,
+    0x96, 0x72, 0xc9, 0xf1, 0x7a, 0xcc, 0xb5, 0x69, 0xfe, 0x6b, 0xec, 0x7e,
+    0xf3, 0x69, 0x69, 0xff, 0xf7, 0xb7, 0x8e, 0xe5, 0x7d, 0x97, 0xb8, 0xb9,
+    0x75, 0x68, 0xea, 0x2e, 0x0e, 0x50, 0x27, 0x13, 0xfd, 0x95, 0xff, 0x2c,
+    0x05, 0x39, 0x69, 0xfe, 0xe4, 0x3c, 0xb5, 0x65, 0x56, 0xd6, 0x9f, 0xfd,
+    0x56, 0x6c, 0x98, 0x71, 0xe2, 0xe9, 0x2d, 0x18, 0x8e, 0xbf, 0x17, 0xfc,
+    0x73, 0x69, 0xe4, 0xf8, 0x73, 0x75, 0xe5, 0xa7, 0xfc, 0xe6, 0x1b, 0x0c,
+    0xe8, 0xfc, 0xe5, 0xa3, 0x9e, 0x7c, 0xaf, 0x12, 0xcf, 0x7a, 0xcc, 0x02,
+    0xd3, 0xfc, 0x46, 0x99, 0xdf, 0xce, 0x5a, 0x5a, 0x1e, 0x3d, 0xde, 0x62,
     0x29, 0xd6, 0xdb, 0x6a, 0xd3, 0xff, 0xec, 0x3f, 0x85, 0xbf, 0xc0, 0x6b,
-    0x0e, 0xd7, 0x48, 0x25, 0xfc, 0x62, 0x65, 0x65, 0x08, 0x4e, 0x22, 0xce,
-    0xf3, 0xb8, 0xb4, 0x89, 0x69, 0xfb, 0x8d, 0xba, 0xe5, 0x62, 0xd3, 0xf7,
+    0x0e, 0xd7, 0x48, 0x25, 0xfc, 0x62, 0x65, 0x65, 0x08, 0x4d, 0xa2, 0xce,
+    0xf3, 0xb6, 0xb4, 0x89, 0x69, 0xfb, 0x6d, 0xca, 0xe5, 0x62, 0xd3, 0xf7,
     0x6b, 0xa2, 0xce, 0x5a, 0x1f, 0x39, 0xf4, 0x80, 0x3f, 0xa5, 0xf3, 0x38,
-    0x2f, 0x83, 0x2c, 0x4f, 0x7c, 0x69, 0x62, 0xf7, 0x43, 0x1b, 0x23, 0x8b,
-    0x34, 0xd6, 0xf0, 0xb8, 0xda, 0x33, 0xc2, 0x30, 0x78, 0xb6, 0xa1, 0x5a,
-    0x2e, 0x3a, 0x8f, 0x2b, 0xd1, 0xe7, 0x6f, 0x1a, 0x19, 0x08, 0x59, 0xff,
-    0xdd, 0xcb, 0x41, 0xcc, 0x78, 0x73, 0xae, 0x5a, 0x77, 0x73, 0x7e, 0xb4,
-    0xb3, 0xa7, 0xd2, 0x74, 0xa9, 0xff, 0xb3, 0x82, 0xd6, 0x19, 0xce, 0x56,
-    0xfd, 0x69, 0xfa, 0xae, 0x1b, 0x6d, 0xb5, 0x69, 0x05, 0x9b, 0x06, 0x8c,
+    0x2f, 0x83, 0x2c, 0x4f, 0x9c, 0x69, 0x62, 0xf7, 0x43, 0x1b, 0x23, 0x8b,
+    0x34, 0xd6, 0xf0, 0xb8, 0xe2, 0x33, 0xc2, 0x30, 0x78, 0xb6, 0xa1, 0x5a,
+    0x2e, 0x3a, 0x8f, 0x2b, 0xd1, 0xe7, 0x73, 0x1a, 0x19, 0x08, 0x59, 0xff,
+    0xdd, 0xcb, 0x41, 0xbc, 0x78, 0x73, 0xae, 0x5a, 0x77, 0x73, 0x9e, 0xb4,
+    0xb3, 0xa7, 0xd2, 0x74, 0xa9, 0xff, 0xb3, 0x62, 0xd6, 0x19, 0xbd, 0xd7,
+    0x3d, 0x69, 0xfa, 0xae, 0x1b, 0x6d, 0xb5, 0x69, 0x05, 0x9b, 0x06, 0x8c,
     0x4c, 0x35, 0x72, 0xf2, 0x87, 0x3a, 0x53, 0x54, 0xb5, 0x21, 0x8e, 0x2b,
-    0x44, 0xef, 0xd2, 0x21, 0xcd, 0xce, 0xc0, 0x25, 0xb2, 0x6d, 0x18, 0x27,
-    0x49, 0xce, 0x7d, 0xc8, 0x48, 0x7f, 0x0a, 0xed, 0x47, 0xd1, 0xe8, 0x5c,
+    0x44, 0xef, 0xd2, 0x21, 0xcd, 0xce, 0xc0, 0x25, 0xb2, 0x71, 0x18, 0x27,
+    0x49, 0xce, 0x7d, 0xb8, 0x48, 0x7f, 0x0a, 0xed, 0x47, 0xd1, 0xe8, 0x5c,
     0xfd, 0x58, 0x47, 0x4f, 0xe0, 0xe7, 0x6f, 0x55, 0x75, 0x44, 0x6b, 0x3e,
     0xed, 0xea, 0xae, 0xa8, 0xa9, 0x66, 0xab, 0xaa, 0x21, 0xa9, 0x07, 0x0f,
     0x56, 0x8c, 0xe7, 0xdd, 0xbd, 0x55, 0xd5, 0x11, 0xf4, 0xff, 0xf0, 0xd7,
-    0x9c, 0xc6, 0xee, 0x6f, 0x69, 0xbf, 0x5a, 0x78, 0x34, 0xe0, 0xe2, 0x21,
-    0x0c, 0x33, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x58, 0xf3, 0xfc, 0x34, 0x6f,
-    0x09, 0x86, 0xc5, 0xa7, 0xec, 0x2f, 0x78, 0xbc, 0xb4, 0x83, 0x88, 0x95,
-    0x78, 0xcf, 0x46, 0xf3, 0x9f, 0x34, 0xe6, 0xad, 0x3f, 0x3e, 0x23, 0x9c,
+    0x9c, 0xc6, 0xf2, 0x6f, 0x69, 0xbf, 0x5a, 0x78, 0x34, 0xe0, 0xe2, 0x21,
+    0x0c, 0x33, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x58, 0xf3, 0xfc, 0x34, 0x6e,
+    0xc9, 0x86, 0xc5, 0xa7, 0xec, 0x2f, 0x78, 0xbc, 0xb4, 0x83, 0x88, 0x95,
+    0x78, 0xcf, 0x46, 0xf3, 0x9f, 0x34, 0xe6, 0xad, 0x3f, 0x3e, 0x23, 0x9b,
     0xaf, 0x2d, 0x3f, 0xc3, 0x5f, 0xea, 0xbd, 0xea, 0x5a, 0x7b, 0xf3, 0x97,
-    0x56, 0x9f, 0xff, 0xe6, 0xe8, 0xe0, 0x33, 0x9c, 0x2b, 0xf0, 0xbd, 0xaa,
-    0xd9, 0x68, 0x24, 0x43, 0x70, 0x8a, 0x7f, 0x98, 0x7d, 0xbe, 0x57, 0xbe,
+    0x56, 0x9f, 0xff, 0xe6, 0xe8, 0xe0, 0x33, 0x7b, 0x2b, 0xec, 0xbd, 0xaa,
+    0xe1, 0x68, 0x24, 0x43, 0x6c, 0x8a, 0x7f, 0x98, 0x7d, 0xce, 0x57, 0xbe,
     0x65, 0xa7, 0x55, 0x5d, 0x51, 0x68, 0x4f, 0xfa, 0xad, 0x7d, 0xbd, 0x76,
-    0xcc, 0xb5, 0x68, 0xb9, 0xf5, 0x74, 0x9e, 0x7f, 0xff, 0xf8, 0xbb, 0xca,
-    0x12, 0x6f, 0x17, 0x1b, 0x5c, 0xcd, 0x77, 0x2f, 0x9e, 0x5a, 0x00, 0x9a,
-    0x07, 0x61, 0x4d, 0xc2, 0x29, 0xf0, 0xde, 0xb4, 0x4b, 0x4f, 0xff, 0xdd,
-    0x16, 0x6d, 0xb7, 0x6b, 0xc2, 0xc0, 0xe3, 0x79, 0x69, 0xe7, 0x0d, 0x18,
-    0xb4, 0x32, 0x28, 0x70, 0x96, 0x96, 0xe7, 0x56, 0xfc, 0x96, 0x9f, 0xf1,
-    0x9e, 0x34, 0x8d, 0x6c, 0x16, 0x5a, 0x7c, 0x67, 0x85, 0xbf, 0x5a, 0x36,
-    0x44, 0x27, 0x47, 0x9f, 0x9f, 0x4f, 0x9b, 0xd9, 0xc6, 0x5a, 0x7f, 0xff,
-    0xf7, 0x08, 0x18, 0x56, 0x71, 0x9b, 0x76, 0x6c, 0xed, 0x17, 0xa8, 0x71,
-    0x69, 0xff, 0xec, 0xf6, 0xb1, 0xee, 0x33, 0xfc, 0xe7, 0x19, 0x68, 0xb1,
+    0xcc, 0xb5, 0x68, 0xb9, 0xf5, 0x74, 0x9e, 0x7f, 0xff, 0xf8, 0xbb, 0xba,
+    0x12, 0x6f, 0x16, 0xdb, 0x5b, 0xcd, 0x77, 0x2f, 0x9e, 0x5a, 0x00, 0x9a,
+    0x07, 0x61, 0x4d, 0xb2, 0x29, 0xf0, 0xde, 0xb4, 0x4b, 0x4f, 0xff, 0xdd,
+    0x16, 0x6e, 0x39, 0x6b, 0xc2, 0xc0, 0xdb, 0x79, 0x69, 0xe7, 0x0d, 0x18,
+    0xb4, 0x32, 0x28, 0x70, 0x96, 0x96, 0xe7, 0x57, 0x3c, 0x96, 0x9f, 0xf1,
+    0x9e, 0x34, 0x8d, 0x6c, 0x16, 0x5a, 0x7c, 0x67, 0x85, 0xbf, 0x5a, 0x38,
+    0x44, 0x27, 0x47, 0x9f, 0x9f, 0x4f, 0x9b, 0xd9, 0xb6, 0x5a, 0x7f, 0xff,
+    0xf6, 0xc8, 0x18, 0x56, 0x6d, 0x9b, 0x96, 0x70, 0xed, 0x17, 0xa8, 0x71,
+    0x69, 0xff, 0xec, 0xf6, 0xb1, 0xed, 0xb3, 0xfc, 0xde, 0xd9, 0x68, 0xb1,
     0x1d, 0x77, 0x25, 0x17, 0x68, 0x64, 0xd3, 0xb2, 0x31, 0xe9, 0xff, 0xf9,
-    0x9e, 0xc0, 0x71, 0x9f, 0xf3, 0x3c, 0x2c, 0xc7, 0x5a, 0x7f, 0x9a, 0xfc,
+    0x9e, 0xc0, 0x6d, 0x9f, 0xef, 0x3c, 0x2c, 0xc7, 0x5a, 0x7f, 0x9a, 0xfb,
     0x27, 0xaa, 0xac, 0x5a, 0x0e, 0x89, 0x2f, 0xd6, 0xa7, 0xf8, 0xbe, 0x17,
-    0x75, 0xb0, 0x0b, 0x4f, 0xff, 0xf7, 0x31, 0xd7, 0xe1, 0x71, 0xb3, 0xae,
+    0x75, 0xb0, 0x0b, 0x4f, 0xff, 0xf6, 0xf1, 0xd7, 0xd9, 0x6d, 0xb3, 0xae,
     0xf0, 0xb1, 0xf4, 0xb4, 0xb3, 0x48, 0x9b, 0xf1, 0xbc, 0xfd, 0xd6, 0xf5,
-    0x99, 0x6a, 0xd3, 0xef, 0x72, 0x84, 0xd5, 0xa7, 0xf8, 0x7e, 0xb0, 0x1c,
+    0x99, 0x6a, 0xd3, 0xef, 0x6e, 0x84, 0xd5, 0xa7, 0xf8, 0x7e, 0xb0, 0x1b,
     0xc1, 0x65, 0xa5, 0x98, 0x7b, 0xf7, 0x27, 0x82, 0x45, 0x98, 0xc2, 0x36,
-    0x7f, 0x67, 0x1e, 0x2e, 0x11, 0xd6, 0x9f, 0xce, 0xce, 0xe8, 0x5a, 0xc5,
-    0xa7, 0x67, 0x29, 0x69, 0xf3, 0xc0, 0xfb, 0xe1, 0x5a, 0x00, 0x78, 0x94,
+    0x7f, 0x66, 0xde, 0x2d, 0x91, 0xd6, 0x9f, 0xce, 0xce, 0xe8, 0x5a, 0xc5,
+    0xa7, 0x66, 0xe9, 0x69, 0xf3, 0xc0, 0xfb, 0xe1, 0x5a, 0x00, 0x78, 0x94,
     0x35, 0x3f, 0xb2, 0xdc, 0xbd, 0xf2, 0xd5, 0xa7, 0x1a, 0xef, 0x96, 0x9f,
-    0xf6, 0xd6, 0xfd, 0x43, 0xcc, 0xa7, 0x96, 0x82, 0x3d, 0xea, 0x1e, 0x8c,
-    0x45, 0x88, 0xc2, 0x46, 0x31, 0x3a, 0x0d, 0x8c, 0xa9, 0xdb, 0x50, 0xd2,
-    0x9e, 0xce, 0x57, 0x96, 0x9f, 0x88, 0x7e, 0x76, 0x69, 0x69, 0xff, 0x67,
-    0x9b, 0x6f, 0xeb, 0x9c, 0x25, 0xa4, 0x75, 0xa7, 0xe2, 0x1f, 0x9d, 0x9a,
-    0xdc, 0x79, 0xcf, 0xcf, 0xa0, 0xd4, 0x61, 0x79, 0xea, 0x41, 0x7c, 0x19,
-    0x55, 0x7b, 0xe1, 0x23, 0x18, 0x02, 0x17, 0xb9, 0x1b, 0x11, 0xe1, 0xaf,
-    0xc8, 0xff, 0xff, 0x85, 0xd0, 0xc2, 0xff, 0x50, 0xdb, 0xf4, 0x78, 0xdb,
-    0xc8, 0x46, 0x43, 0x8a, 0x7e, 0x7d, 0x20, 0x0b, 0x3c, 0xb4, 0xf6, 0x72,
+    0xf7, 0x16, 0xfd, 0x43, 0xbc, 0xa7, 0x96, 0x82, 0x3d, 0xea, 0x1e, 0x8c,
+    0x45, 0x88, 0xc2, 0x46, 0x31, 0x3a, 0x0e, 0x0c, 0xa9, 0xdb, 0x50, 0xd2,
+    0x9e, 0xcd, 0xd7, 0x96, 0x9f, 0x88, 0x7e, 0x76, 0x69, 0x69, 0xff, 0x67,
+    0x9b, 0x8f, 0xeb, 0x7b, 0x25, 0xa4, 0x75, 0xa7, 0xe2, 0x1f, 0x9d, 0x9a,
+    0xe4, 0x79, 0xcf, 0xcf, 0xa0, 0xd4, 0x61, 0x79, 0xea, 0x41, 0x7c, 0x19,
+    0x55, 0x7c, 0xe1, 0x23, 0x18, 0x02, 0x17, 0xb9, 0x1b, 0x11, 0xe1, 0xaf,
+    0xb8, 0xff, 0xff, 0x85, 0xd0, 0xc2, 0xff, 0x50, 0xdb, 0xf4, 0x78, 0xdc,
+    0xc8, 0x46, 0x43, 0x8a, 0x7e, 0x7d, 0x20, 0x0b, 0x3c, 0xb4, 0xf6, 0x6e,
     0xbc, 0xb4, 0x9f, 0x1c, 0x3c, 0xf1, 0x2f, 0x9f, 0x76, 0xf5, 0x57, 0x54,
     0x5a, 0xd3, 0xba, 0xc7, 0x5a, 0x7f, 0xd4, 0xe0, 0xe7, 0x6f, 0x55, 0x75,
-    0x45, 0x07, 0x20, 0xef, 0x88, 0xc2, 0xc2, 0xbe, 0x99, 0x9c, 0x6e, 0x7f,
+    0x45, 0x07, 0x20, 0xf3, 0x88, 0xc2, 0xc2, 0xbe, 0x99, 0x9c, 0x6e, 0x7f,
     0xf0, 0x4f, 0x4e, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x51, 0x73, 0xf8, 0x39,
     0xdb, 0xd5, 0x5d, 0x51, 0x75, 0x43, 0x99, 0xfa, 0x17, 0x84, 0x99, 0x42,
-    0xa8, 0xf0, 0xcf, 0xe5, 0x2b, 0x87, 0xf9, 0x41, 0x3a, 0x47, 0xf2, 0xac,
-    0xfb, 0xb7, 0xaa, 0xba, 0xa2, 0x21, 0x9d, 0xcc, 0x02, 0xd3, 0xd8, 0xfc,
-    0xba, 0xb4, 0x83, 0x87, 0xe1, 0xb1, 0x9e, 0xfc, 0x6e, 0x7f, 0x07, 0x3b,
+    0xa8, 0xf0, 0xcf, 0xdd, 0x2b, 0x87, 0xf9, 0x41, 0x3a, 0x47, 0xf2, 0xac,
+    0xfb, 0xb7, 0xaa, 0xba, 0xa2, 0x21, 0x9d, 0xbc, 0x02, 0xd3, 0xd8, 0xfc,
+    0xba, 0xb4, 0x83, 0x87, 0xe1, 0xc1, 0x9f, 0x3c, 0x6e, 0x7f, 0x07, 0x3b,
     0x7a, 0xab, 0xaa, 0x23, 0x69, 0xfc, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0xa6,
     0xe7, 0xf0, 0x73, 0xb7, 0xaa, 0xba, 0xa2, 0xa0, 0x9f, 0xc1, 0xce, 0xde,
     0xaa, 0xea, 0x8a, 0x9a, 0x7d, 0xdb, 0xd5, 0x5d, 0x51, 0x58, 0x4f, 0x8a,
     0xd0, 0x7d, 0xd5, 0xa7, 0xf9, 0xc1, 0xce, 0xde, 0xaa, 0xea, 0x88, 0xfe,
-    0x76, 0x35, 0x8b, 0x4f, 0xec, 0xd9, 0xd4, 0xe2, 0x15, 0xa6, 0xaf, 0x2d,
-    0x20, 0xe2, 0x60, 0x37, 0x33, 0xa2, 0x91, 0x44, 0xf0, 0xde, 0xf1, 0x8c,
+    0x76, 0x35, 0x8b, 0x4f, 0xec, 0xe1, 0xd4, 0xe2, 0x15, 0xa6, 0xaf, 0x2d,
+    0x20, 0xe2, 0x60, 0x37, 0x33, 0xa2, 0x91, 0x44, 0xf0, 0xdf, 0x31, 0x8c,
     0xff, 0xe0, 0x9e, 0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0x9b, 0xe7, 0xfe,
     0x3d, 0x38, 0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x3f, 0x4e, 0x61, 0xba, 0xd3,
-    0xed, 0xf4, 0xf8, 0xef, 0x66, 0xcb, 0x4b, 0x8b, 0x46, 0xfb, 0x9e, 0x37,
-    0xc6, 0xd3, 0xfc, 0xf9, 0x9c, 0xaf, 0x7a, 0xaa, 0xd5, 0xa7, 0xef, 0x70,
-    0xab, 0xf7, 0xcc, 0x5a, 0x7d, 0xbe, 0x3e, 0x7f, 0xcd, 0x65, 0xa7, 0xff,
-    0x99, 0x99, 0x99, 0x99, 0x99, 0xb6, 0xd2, 0xd3, 0xee, 0x95, 0x94, 0x74,
+    0xee, 0x74, 0xf8, 0xf3, 0x67, 0x0b, 0x4b, 0x6b, 0x47, 0x3b, 0x9e, 0x37,
+    0xc6, 0xd3, 0xfc, 0xf9, 0x9b, 0xaf, 0x7a, 0xaa, 0xd5, 0xa7, 0xef, 0x6c,
+    0xab, 0xf7, 0xcc, 0x5a, 0x7d, 0xce, 0x3e, 0x7f, 0xcd, 0x65, 0xa7, 0xff,
+    0x99, 0x99, 0x99, 0x99, 0x99, 0xb8, 0xd2, 0xd3, 0xee, 0x95, 0x94, 0x74,
     0xa6, 0xb6, 0xd4, 0xa3, 0x0d, 0xfd, 0xa4, 0xf2, 0xf9, 0x20, 0x9a, 0x18,
-    0x64, 0x65, 0x6a, 0x15, 0x33, 0xff, 0x71, 0x8b, 0xbf, 0x0f, 0xde, 0xfb,
+    0x64, 0x65, 0x6a, 0x15, 0x33, 0xff, 0x6d, 0x8b, 0xbf, 0x0f, 0xde, 0xfb,
     0xcb, 0x4f, 0x74, 0x49, 0x96, 0x9f, 0xff, 0xb0, 0xfa, 0xf7, 0x6a, 0xc0,
-    0x71, 0xb5, 0xef, 0xb8, 0xb4, 0xfa, 0xaf, 0xc2, 0xea, 0xd3, 0xff, 0xff,
-    0x86, 0x8d, 0x2e, 0xb3, 0xb7, 0x70, 0x8e, 0x06, 0xdd, 0xbd, 0xf7, 0x0b,
-    0xab, 0x4f, 0x53, 0x8b, 0x65, 0xa0, 0x09, 0x91, 0x75, 0x77, 0x84, 0xc2,
-    0xfd, 0x3f, 0xfe, 0x7d, 0x6d, 0x9d, 0x43, 0xfe, 0xef, 0xcb, 0xa4, 0x6a,
-    0xd3, 0xd6, 0x1c, 0x9c, 0xb4, 0xfb, 0x43, 0xf5, 0x39, 0x69, 0xfd, 0xc6,
+    0x6d, 0xb5, 0xef, 0xb6, 0xb4, 0xfa, 0xaf, 0xb2, 0xea, 0xd3, 0xff, 0xff,
+    0x86, 0x8d, 0x2e, 0xb3, 0xb9, 0x6c, 0x8e, 0x06, 0xe5, 0xcd, 0xf6, 0xcb,
+    0xab, 0x4f, 0x53, 0x8b, 0x85, 0xa0, 0x09, 0x91, 0x75, 0x77, 0x64, 0xc2,
+    0xfd, 0x3f, 0xfe, 0x7d, 0x6e, 0x1d, 0x43, 0xff, 0x2f, 0xcb, 0xa4, 0x6a,
+    0xd3, 0xd6, 0x1c, 0x9c, 0xb4, 0xfb, 0x43, 0xf5, 0x39, 0x69, 0xfd, 0xb6,
     0x38, 0x7f, 0xfd, 0x96, 0x97, 0x58, 0xff, 0x70, 0x88, 0x89, 0xe7, 0xfb,
-    0x60, 0x18, 0xdb, 0xfe, 0x7d, 0xa5, 0xa7, 0xff, 0xe2, 0x07, 0x1b, 0xa5,
+    0x80, 0x18, 0xdc, 0xfd, 0xfd, 0xa5, 0xa7, 0xff, 0xe2, 0x06, 0xdb, 0xa5,
     0xfb, 0x18, 0xcf, 0x0b, 0x1d, 0x68, 0x64, 0x4d, 0x0a, 0x04, 0xec, 0xc7,
-    0x96, 0x96, 0xfd, 0x69, 0xde, 0x76, 0x2d, 0x36, 0xb1, 0x68, 0x23, 0xc4,
-    0xa1, 0x4f, 0xc6, 0xe7, 0xfe, 0x03, 0x7a, 0xb5, 0x9c, 0xe1, 0x1d, 0x69,
+    0x96, 0x97, 0x3d, 0x69, 0xde, 0x76, 0x2d, 0x36, 0xb1, 0x68, 0x23, 0xc4,
+    0xa1, 0x4f, 0xc6, 0xe7, 0xfe, 0x03, 0x7a, 0xb5, 0x9b, 0xd9, 0x1d, 0x69,
     0xde, 0x27, 0x96, 0x86, 0x3d, 0xed, 0x21, 0x43, 0x2e, 0x5e, 0x80, 0x9f,
     0x11, 0x8a, 0x34, 0x17, 0x8f, 0x7b, 0x0d, 0x93, 0xc3, 0x33, 0xf2, 0x11,
-    0x5f, 0xf4, 0x23, 0x27, 0x3c, 0x61, 0xd6, 0x9f, 0xec, 0xf6, 0xb3, 0xdc,
+    0x5f, 0xf4, 0x23, 0x27, 0x3c, 0x61, 0xd6, 0x9f, 0xec, 0xf6, 0xb3, 0xdb,
     0xcf, 0xd6, 0x8a, 0x3d, 0x61, 0x1e, 0x9a, 0x85, 0x68, 0xe9, 0xb4, 0x39,
-    0x04, 0xeb, 0x6d, 0xb5, 0x69, 0xeb, 0x04, 0x99, 0x20, 0x97, 0xf3, 0x6c,
-    0xe5, 0xa7, 0xf1, 0xad, 0xed, 0x72, 0xbe, 0x5a, 0x7c, 0x56, 0x66, 0xde,
-    0x5a, 0x62, 0xba, 0xd2, 0xd9, 0x91, 0x10, 0x68, 0xb9, 0xcd, 0x3c, 0x51,
-    0x0c, 0x99, 0xdf, 0x10, 0x3e, 0x85, 0xbc, 0xff, 0xb5, 0x46, 0xf0, 0x98,
+    0x04, 0xeb, 0x6d, 0xb5, 0x69, 0xeb, 0x04, 0x99, 0x20, 0x97, 0xf3, 0x70,
+    0xe5, 0xa7, 0xf1, 0xad, 0xed, 0x6e, 0xbe, 0x5a, 0x7c, 0x56, 0x67, 0x1e,
+    0x5a, 0x62, 0xba, 0xd2, 0xe1, 0x91, 0x10, 0x68, 0xb9, 0xcd, 0x3c, 0x51,
+    0x0c, 0x99, 0xde, 0xd0, 0x3e, 0x85, 0xbc, 0xff, 0xb5, 0x46, 0xec, 0x98,
     0x7e, 0x65, 0xa7, 0xd5, 0xdf, 0xab, 0x4b, 0x4f, 0xfd, 0x67, 0xdf, 0xe0,
-    0xfc, 0xf3, 0x7a, 0x96, 0x9f, 0xf9, 0xb8, 0x60, 0x3c, 0x58, 0xf3, 0x1d,
+    0xfc, 0xf3, 0x7a, 0x96, 0x9f, 0xf9, 0xb6, 0x60, 0x3c, 0x58, 0xf3, 0x1d,
     0x69, 0xe6, 0xf7, 0xc7, 0x5a, 0x6d, 0x5d, 0x51, 0x07, 0x49, 0x8e, 0x78,
-    0xbe, 0x22, 0x9f, 0xab, 0x8d, 0xbf, 0xf9, 0xcb, 0x45, 0xd3, 0x4f, 0xe9,
+    0xbe, 0x22, 0x9f, 0xab, 0x6d, 0xcf, 0xf9, 0xcb, 0x45, 0xd3, 0x4f, 0xe9,
     0x39, 0xd1, 0xfd, 0x08, 0x97, 0xe4, 0xf3, 0x74, 0x0b, 0x4d, 0x6d, 0xab,
     0x43, 0x8d, 0x75, 0xa2, 0xd3, 0xda, 0x62, 0x7e, 0x90, 0x4d, 0x14, 0xfb,
     0x46, 0x03, 0xfd, 0x2d, 0x04, 0x7b, 0x86, 0x19, 0x4e, 0xb6, 0xdb, 0x52,
     0x84, 0x82, 0x5f, 0xcf, 0x15, 0xa4, 0xf2, 0x51, 0x73, 0x76, 0x43, 0x33,
-    0xec, 0xeb, 0x3f, 0xc5, 0xa3, 0x15, 0xa6, 0x94, 0x71, 0x9c, 0x87, 0xf0,
-    0xbe, 0x68, 0x82, 0x7f, 0x0e, 0xde, 0x33, 0x95, 0xfa, 0xd3, 0x84, 0xb4,
-    0xb4, 0xf0, 0x28, 0x7f, 0x5a, 0x7f, 0xe6, 0x1f, 0xc5, 0xb6, 0x79, 0x87,
-    0xf5, 0xa3, 0x11, 0x68, 0x46, 0xbf, 0x8d, 0x68, 0x86, 0x7c, 0xd9, 0xde,
-    0x12, 0xd3, 0xb0, 0xfb, 0xf5, 0xa7, 0xf6, 0x68, 0x73, 0xdc, 0x25, 0xa0,
+    0xec, 0xeb, 0x3f, 0xc5, 0xa3, 0x15, 0xa6, 0x94, 0x71, 0x9b, 0x87, 0xf0,
+    0xbe, 0x68, 0x82, 0x7f, 0x0f, 0x1e, 0x33, 0x75, 0xfa, 0xd3, 0x84, 0xb4,
+    0xb4, 0xf0, 0x28, 0x7f, 0x5a, 0x7f, 0xe6, 0x1f, 0xc5, 0xb8, 0x79, 0x87,
+    0xf5, 0xa3, 0x11, 0x68, 0x46, 0xbf, 0x8d, 0x68, 0x86, 0x7c, 0xd9, 0xdd,
+    0x92, 0xd3, 0xb0, 0xfc, 0xf5, 0xa7, 0xf6, 0x68, 0x73, 0xdb, 0x25, 0xa0,
     0x07, 0x9e, 0x23, 0xf0, 0xf2, 0x25, 0x5f, 0xba, 0x4f, 0xde, 0xc7, 0x85,
-    0x8e, 0xb8, 0x80, 0xa7, 0xd4, 0x20, 0xe3, 0x2a, 0x20, 0x20, 0x9b, 0x99,
-    0xff, 0x8b, 0x6d, 0x03, 0x05, 0x87, 0xe6, 0x5a, 0x7c, 0xce, 0xad, 0xad,
-    0x5a, 0x30, 0xfa, 0xba, 0x85, 0x3f, 0xcd, 0xdb, 0x37, 0x63, 0xc4, 0x75,
-    0xa7, 0x8e, 0xd8, 0x75, 0xa7, 0xcd, 0xf9, 0x6d, 0xa5, 0xa7, 0xf7, 0x5e,
-    0xd1, 0x0f, 0x89, 0x69, 0xed, 0xbf, 0xeb, 0x25, 0x35, 0xb6, 0xa5, 0x0c,
+    0x8e, 0xb8, 0x80, 0xa7, 0xd4, 0x20, 0xdb, 0x2a, 0x20, 0x20, 0x9b, 0x99,
+    0xff, 0x8b, 0x8d, 0x03, 0x05, 0x87, 0xe6, 0x5a, 0x7c, 0xce, 0xae, 0x2d,
+    0x5a, 0x30, 0xfa, 0xba, 0x85, 0x3f, 0xcd, 0xdb, 0x39, 0x63, 0xc4, 0x75,
+    0xa7, 0x8e, 0xd8, 0x75, 0xa7, 0xcd, 0xf9, 0x71, 0xa5, 0xa7, 0xf7, 0x5e,
+    0xd1, 0x0f, 0x89, 0x69, 0xee, 0x3f, 0xeb, 0x25, 0x35, 0xb6, 0xa5, 0x0c,
     0x6e, 0xed, 0x23, 0x9f, 0xab, 0xae, 0x70, 0x92, 0x41, 0x34, 0x30, 0xca,
-    0xd2, 0x72, 0x1a, 0x24, 0xbf, 0xd8, 0x56, 0x70, 0x86, 0x8e, 0xc4, 0x83,
+    0xd2, 0x72, 0x1a, 0x24, 0xbf, 0xd8, 0x56, 0x6c, 0x86, 0x8e, 0xc4, 0x83,
     0xc5, 0x1f, 0x42, 0x3a, 0x7d, 0xf7, 0xbe, 0xa3, 0x15, 0x10, 0x3c, 0xcd,
-    0xf2, 0xd3, 0xef, 0x87, 0xf3, 0x1c, 0xb4, 0xff, 0xf9, 0xf8, 0xe6, 0xb7,
-    0x19, 0xc6, 0xfc, 0xf4, 0xed, 0xd8, 0x78, 0x1a, 0x16, 0x9f, 0xf7, 0x3e,
-    0xb1, 0xb7, 0x37, 0x98, 0x0b, 0x4e, 0xae, 0xd8, 0xb4, 0xd6, 0xda, 0xb4,
+    0xf2, 0xd3, 0xef, 0x87, 0xf3, 0x1c, 0xb4, 0xff, 0xf9, 0xf8, 0xe6, 0xb9,
+    0x19, 0xb6, 0xfc, 0xf4, 0xee, 0x58, 0x78, 0x1a, 0x16, 0x9f, 0xf6, 0xfe,
+    0xb1, 0xb9, 0x37, 0x98, 0x0b, 0x4e, 0xae, 0xd8, 0xb4, 0xd6, 0xda, 0xb4,
     0xfe, 0xe9, 0x63, 0xf3, 0x1c, 0x1b, 0x9b, 0x4b, 0x47, 0x20, 0x08, 0xc2,
-    0xeb, 0xac, 0xff, 0xc3, 0x86, 0xf2, 0x9c, 0x65, 0x6c, 0xcb, 0x43, 0x1f,
+    0xeb, 0xac, 0xff, 0xc3, 0x86, 0xee, 0x9c, 0x65, 0x70, 0xcb, 0x43, 0x1f,
     0x3e, 0x11, 0xcf, 0xfe, 0xc3, 0x9e, 0x9d, 0x9d, 0xbd, 0x55, 0xd5, 0x10,
-    0xc4, 0x12, 0xa1, 0x0e, 0x47, 0x38, 0x24, 0x13, 0xfc, 0xdb, 0x3b, 0x95,
-    0x42, 0xcb, 0x4f, 0xbb, 0x7a, 0xab, 0xaa, 0x20, 0xa9, 0xdc, 0x63, 0xad,
+    0xc4, 0x12, 0xa1, 0x0d, 0xc7, 0x38, 0x24, 0x13, 0xfc, 0xdc, 0x3b, 0x75,
+    0x42, 0xcb, 0x4f, 0xbb, 0x7a, 0xab, 0xaa, 0x20, 0xa9, 0xdb, 0x63, 0xad,
     0x18, 0x79, 0xc4, 0x67, 0x3d, 0xed, 0x37, 0xe9, 0x4e, 0xae, 0xd8, 0x94,
     0xf7, 0xc3, 0x46, 0xa5, 0x3f, 0x8a, 0xcc, 0xf6, 0x9b, 0xf4, 0xa1, 0x29,
-    0xfb, 0x2e, 0xdc, 0x63, 0xa5, 0x35, 0xb6, 0xa5, 0x3f, 0x0b, 0x0d, 0x85,
-    0xd4, 0xa3, 0x13, 0x0b, 0xb9, 0x11, 0xc7, 0x38, 0x45, 0xf9, 0x98, 0x85,
-    0x5a, 0x56, 0x60, 0xc4, 0xc5, 0xd4, 0x82, 0x7e, 0x70, 0xe4, 0xf1, 0x39,
-    0x1c, 0xf4, 0x71, 0x53, 0x95, 0x4a, 0x48, 0x9f, 0xf3, 0x3a, 0x87, 0x35,
-    0xd6, 0xd2, 0xd3, 0xee, 0xec, 0xec, 0xf2, 0xd3, 0xfb, 0x8c, 0xfc, 0x0d,
-    0xfb, 0x2d, 0x1d, 0x3d, 0xcf, 0x14, 0x4f, 0xf3, 0x6c, 0xed, 0xe6, 0xbe,
-    0x1d, 0x68, 0x63, 0xdd, 0xe1, 0x14, 0xff, 0xfa, 0xa9, 0xd5, 0xcd, 0xd9,
-    0x67, 0xde, 0xfa, 0x8c, 0x54, 0x5f, 0x73, 0xfe, 0xd1, 0x0e, 0xce, 0xef,
+    0xfb, 0x2e, 0xdb, 0x63, 0xa5, 0x35, 0xb6, 0xa5, 0x3f, 0x0b, 0x0d, 0x85,
+    0xd4, 0xa3, 0x13, 0x0b, 0xb9, 0x11, 0xc7, 0x36, 0x45, 0xf9, 0x98, 0x85,
+    0x5a, 0x56, 0x60, 0xc4, 0xc5, 0xd4, 0x82, 0x7e, 0x70, 0xe4, 0xf1, 0x37,
+    0x1c, 0xf4, 0x6d, 0x53, 0x95, 0x4a, 0x48, 0x9f, 0xf3, 0x3a, 0x87, 0x35,
+    0xd6, 0xd2, 0xd3, 0xee, 0xf0, 0xec, 0xf2, 0xd3, 0xfb, 0x6c, 0xfc, 0x0d,
+    0xfb, 0x2d, 0x1d, 0x3d, 0xcf, 0x14, 0x4f, 0xf3, 0x70, 0xee, 0x66, 0xbe,
+    0x1d, 0x68, 0x63, 0xdd, 0xd9, 0x14, 0xff, 0xfa, 0xa9, 0xd5, 0xbe, 0x59,
+    0x67, 0xde, 0xfa, 0x8c, 0x54, 0x5f, 0x73, 0xfe, 0xd1, 0x0f, 0x0e, 0xef,
     0xcd, 0xa5, 0xa1, 0xe4, 0x52, 0xf9, 0x9a, 0x57, 0x5a, 0x7f, 0xff, 0x7e,
-    0x5d, 0x37, 0x8c, 0x77, 0x9c, 0x43, 0xc6, 0xeb, 0x2d, 0x2f, 0x61, 0xf8,
+    0x5d, 0x37, 0x6c, 0x77, 0x9c, 0x43, 0xb6, 0xeb, 0x2d, 0x2f, 0x61, 0xf8,
     0xe8, 0x42, 0x7b, 0xc3, 0x8e, 0x5a, 0x18, 0xf1, 0xc8, 0x9e, 0x7f, 0xc2,
-    0x4e, 0xf7, 0x33, 0xa5, 0xa5, 0xa7, 0xf7, 0x19, 0xc3, 0x99, 0x62, 0xd3,
-    0xfb, 0xdf, 0x98, 0x2e, 0xe6, 0x2e, 0x20, 0x19, 0xd5, 0xd7, 0x2e, 0x20,
-    0x18, 0xc3, 0xe9, 0xf9, 0x06, 0x6a, 0x72, 0xe2, 0x01, 0x9e, 0xa1, 0xd9,
-    0xcb, 0x88, 0x06, 0x7f, 0x15, 0xf9, 0x9f, 0xfe, 0xcb, 0x88, 0x06, 0x71,
-    0x08, 0x17, 0x10, 0x0c, 0x01, 0x16, 0xe7, 0x22, 0xe1, 0x73, 0xf4, 0x09,
+    0x4e, 0xf6, 0xf3, 0xa5, 0xa5, 0xa7, 0xf6, 0xd9, 0xc3, 0x99, 0x62, 0xd3,
+    0xfb, 0xdf, 0x98, 0x2e, 0xde, 0x2e, 0x20, 0x19, 0xd5, 0xd7, 0x2e, 0x20,
+    0x18, 0xc3, 0xe9, 0xf9, 0x06, 0x6a, 0x72, 0xe2, 0x01, 0x9e, 0xa1, 0xe1,
+    0xcb, 0x88, 0x06, 0x7f, 0x15, 0xf7, 0x9f, 0xfe, 0xcb, 0x88, 0x06, 0x71,
+    0x08, 0x17, 0x10, 0x0c, 0x01, 0x16, 0xe7, 0x22, 0xd9, 0x73, 0xf4, 0x09,
     0xdd, 0x6f, 0x2e, 0x20, 0x18, 0x5c, 0x40, 0x33, 0x31, 0xd7, 0x10, 0x0c,
     0x00, 0xdc, 0x90, 0xbc, 0xf1, 0x1c, 0x0c, 0xb8, 0x80, 0x67, 0x7a, 0xae,
-    0xb8, 0x80, 0x67, 0xfc, 0x34, 0xe0, 0xf0, 0xaf, 0x5d, 0x5c, 0x40, 0x33,
+    0xb8, 0x80, 0x67, 0xfc, 0x34, 0xe0, 0xec, 0xaf, 0x5d, 0x5c, 0x40, 0x33,
     0x50, 0x17, 0x10, 0x0c, 0xfe, 0x1a, 0xb7, 0x5a, 0x6f, 0xd7, 0x10, 0x0c,
     0xf8, 0x8d, 0xa1, 0xfd, 0x71, 0x00, 0xcc, 0x5a, 0x5c, 0x40, 0x31, 0xd3,
-    0xd6, 0xf1, 0xa4, 0xfb, 0xc2, 0xdb, 0x39, 0x51, 0x00, 0xcd, 0xfb, 0x2e,
+    0xd6, 0xf1, 0xa4, 0xfb, 0xc2, 0xdc, 0x39, 0x51, 0x00, 0xcd, 0xfb, 0x2e,
     0x20, 0x10, 0x9b, 0x49, 0xf1, 0x31, 0xe8, 0x0b, 0x88, 0x06, 0x7b, 0xea,
-    0x1b, 0xae, 0x20, 0x19, 0xcd, 0xdb, 0xae, 0x20, 0x19, 0xff, 0x66, 0xda,
+    0x1b, 0xae, 0x20, 0x19, 0xcd, 0xdb, 0xae, 0x20, 0x19, 0xff, 0x67, 0x1a,
     0x03, 0x60, 0xff, 0xa5, 0xc4, 0x03, 0x3e, 0xfa, 0x9c, 0xe6, 0x5c, 0x40,
-    0x31, 0x87, 0xff, 0xc4, 0xb9, 0xbb, 0xfa, 0xe2, 0x01, 0x87, 0x2a, 0x8d,
-    0x01, 0x16, 0x42, 0x58, 0x95, 0xb8, 0x67, 0xf9, 0x96, 0x8b, 0xbd, 0x0a,
-    0x7f, 0x88, 0x67, 0xd9, 0xed, 0x56, 0xcb, 0x88, 0x06, 0x7f, 0x01, 0xae,
+    0x31, 0x87, 0xff, 0xb4, 0xb9, 0xbb, 0xfa, 0xe2, 0x01, 0x87, 0x2a, 0x8d,
+    0x01, 0x16, 0x42, 0x58, 0x95, 0xb6, 0x67, 0xf9, 0x96, 0x8b, 0xbd, 0x0a,
+    0x7f, 0x88, 0x67, 0xd9, 0xed, 0x57, 0x0b, 0x88, 0x06, 0x7f, 0x01, 0xae,
     0xf7, 0x5b, 0xcb, 0x88, 0x04, 0x06, 0xd2, 0x77, 0x5a, 0xd5, 0xc4, 0x03,
     0x17, 0x3f, 0x9e, 0xa8, 0x4f, 0x10, 0xbb, 0x4b, 0x88, 0x06, 0x7e, 0xa7,
-    0x9d, 0x9b, 0x39, 0x71, 0x00, 0xc6, 0x22, 0x2b, 0xf2, 0x2f, 0x8b, 0x67,
-    0xfb, 0x8d, 0xa0, 0xeb, 0xee, 0xfe, 0xb8, 0x80, 0x64, 0x4b, 0x88, 0x06,
-    0x6a, 0xb0, 0x07, 0xc9, 0xc4, 0x89, 0xbb, 0xfa, 0xe2, 0x01, 0x9f, 0x57,
-    0xb4, 0x5b, 0x2e, 0x20, 0x19, 0xf8, 0x87, 0xe7, 0x66, 0x97, 0x10, 0x0c,
+    0x9d, 0x9c, 0x39, 0x71, 0x00, 0xc6, 0x22, 0x2b, 0xf2, 0x2f, 0x8b, 0x67,
+    0xfb, 0x6d, 0xa0, 0xeb, 0xee, 0xfe, 0xb8, 0x80, 0x64, 0x4b, 0x88, 0x06,
+    0x6a, 0xb0, 0x07, 0xc9, 0xb4, 0x89, 0xbb, 0xfa, 0xe2, 0x01, 0x9f, 0x57,
+    0xb4, 0x5c, 0x2e, 0x20, 0x19, 0xf8, 0x87, 0xe7, 0x66, 0x97, 0x10, 0x0c,
     0x32, 0x24, 0x44, 0x93, 0xc6, 0x90, 0x06, 0x40, 0x3e, 0x43, 0x65, 0xe3,
-    0xfe, 0xb0, 0xf0, 0x82, 0x8b, 0x7f, 0x28, 0xd4, 0xad, 0x6f, 0x47, 0x83,
-    0xbd, 0x09, 0x53, 0x21, 0x7f, 0x3a, 0xaa, 0xea, 0x88, 0x04, 0x28, 0xbc,
-    0x9e, 0x35, 0xf3, 0x80, 0xeb, 0x4f, 0xfc, 0xe1, 0xcb, 0xf0, 0xbf, 0xff,
-    0xe7, 0x96, 0x9b, 0xf6, 0x4a, 0x46, 0xa5, 0x3b, 0x8d, 0x62, 0xd3, 0x5b,
-    0x6a, 0x50, 0x47, 0xb7, 0xbf, 0x16, 0x78, 0x46, 0xd1, 0xc9, 0xc7, 0x2d,
-    0x24, 0x13, 0xc1, 0x9f, 0xfd, 0xed, 0x61, 0xa5, 0xd3, 0x3f, 0xfd, 0xb6,
+    0xfe, 0xb0, 0xec, 0x82, 0x8b, 0x7f, 0x28, 0xd4, 0xad, 0x6f, 0x47, 0x83,
+    0xcd, 0x09, 0x53, 0x21, 0x7f, 0x3a, 0xaa, 0xea, 0x88, 0x04, 0x28, 0xbc,
+    0x9e, 0x35, 0xf3, 0x80, 0xeb, 0x4f, 0xfc, 0xe1, 0xcb, 0xec, 0xbf, 0xff,
+    0xe7, 0x96, 0x9b, 0xf6, 0x4a, 0x46, 0xa5, 0x3b, 0x6d, 0x62, 0xd3, 0x5b,
+    0x6a, 0x50, 0x47, 0xb7, 0xcf, 0x16, 0x78, 0x46, 0xd1, 0xc9, 0xc7, 0x2d,
+    0x24, 0x13, 0xc1, 0x9f, 0xfd, 0xed, 0x61, 0xa5, 0xd3, 0x3f, 0xfd, 0xb8,
     0x5a, 0x2c, 0x65, 0xbc, 0x38, 0x80, 0x07, 0x97, 0xa4, 0xae, 0x91, 0xcd,
     0x14, 0xff, 0x0e, 0x33, 0x0b, 0x27, 0xef, 0x18, 0xfd, 0xfb, 0x69, 0x69,
-    0xc3, 0xb3, 0x97, 0x18, 0x04, 0x11, 0xed, 0xd1, 0x94, 0xfd, 0xd7, 0x9c,
-    0xdd, 0xb1, 0x69, 0xff, 0x3b, 0x5b, 0x87, 0x0e, 0xdd, 0x35, 0x69, 0xf9,
+    0xc3, 0xc3, 0x97, 0x18, 0x04, 0x11, 0xed, 0xd1, 0x94, 0xfd, 0xd7, 0x9c,
+    0xdd, 0xb1, 0x69, 0xff, 0x3b, 0x5c, 0x87, 0x0e, 0xdd, 0x35, 0x69, 0xf9,
     0xcd, 0xdf, 0xcc, 0x3a, 0xd3, 0xff, 0xb1, 0xf8, 0xe6, 0xbd, 0x82, 0xec,
-    0xd2, 0xd3, 0x72, 0xb0, 0xfd, 0xc4, 0xbe, 0x7f, 0x9a, 0xec, 0x72, 0x3f,
-    0xe4, 0xb4, 0x74, 0xf8, 0x9f, 0x4a, 0xe7, 0xe3, 0x2d, 0xe9, 0x67, 0x12,
-    0x9e, 0x7b, 0x94, 0xf2, 0xd3, 0xec, 0x79, 0xcd, 0xb2, 0xd3, 0xb8, 0x4f,
+    0xd2, 0xd3, 0x6e, 0xb0, 0xfd, 0xc4, 0xbe, 0x7f, 0x9a, 0xec, 0x72, 0x3f,
+    0xe4, 0xb4, 0x74, 0xf8, 0x9f, 0x4a, 0xe7, 0xe3, 0x2d, 0xe9, 0x66, 0xd2,
+    0x9e, 0x7b, 0x74, 0xf2, 0xd3, 0xec, 0x79, 0xcd, 0xc2, 0xd3, 0xb6, 0x4f,
     0x2d, 0x3d, 0x82, 0xce, 0x4a, 0x3a, 0x88, 0x61, 0x23, 0xf1, 0x43, 0xe8,
-    0xe4, 0x32, 0xa6, 0x0c, 0x2f, 0x28, 0xc6, 0x78, 0x4b, 0x50, 0xb4, 0x9f,
-    0xb6, 0xd7, 0x7a, 0xde, 0x5a, 0x78, 0x87, 0xda, 0x5a, 0x77, 0xcf, 0x96,
-    0x96, 0x9f, 0xfb, 0xf3, 0xe5, 0xf8, 0x40, 0xe7, 0xc6, 0xad, 0x3f, 0xf6,
-    0x8d, 0x36, 0xb7, 0x31, 0xc4, 0x8d, 0x5a, 0x7f, 0xb3, 0xde, 0x23, 0x7d,
-    0x97, 0x5a, 0x7f, 0x01, 0xb6, 0xd6, 0x6d, 0xe5, 0xa7, 0xf9, 0xbc, 0x57,
-    0x20, 0x37, 0x96, 0x96, 0x80, 0x8a, 0x2c, 0x38, 0xe9, 0xa4, 0x6c, 0x98,
-    0xf7, 0xa1, 0xd3, 0x3f, 0x66, 0x8c, 0xd7, 0xb1, 0x69, 0xfb, 0x7b, 0x3d,
+    0xe4, 0x32, 0xa6, 0x0c, 0x2f, 0x28, 0xc6, 0x76, 0x4b, 0x50, 0xb4, 0x9f,
+    0xb8, 0xd7, 0x7a, 0xde, 0x5a, 0x78, 0x87, 0xda, 0x5a, 0x77, 0xcf, 0x96,
+    0x96, 0x9f, 0xfb, 0xf3, 0xe5, 0xf6, 0x40, 0xdf, 0xc6, 0xad, 0x3f, 0xf6,
+    0x8d, 0x36, 0xb9, 0x31, 0xc4, 0x8d, 0x5a, 0x7f, 0xb3, 0xde, 0x23, 0x7d,
+    0x97, 0x5a, 0x7f, 0x01, 0xb8, 0xd6, 0x71, 0xe5, 0xa7, 0xf9, 0xbc, 0x57,
+    0x20, 0x37, 0x96, 0x96, 0x80, 0x8a, 0x2c, 0x38, 0xe9, 0xa4, 0x70, 0x98,
+    0xf7, 0xa1, 0xd3, 0x3f, 0x66, 0x8c, 0xd7, 0xb1, 0x69, 0xfb, 0x9b, 0x3d,
     0xfd, 0x79, 0x68, 0x71, 0xee, 0xf8, 0xb6, 0x19, 0x51, 0x81, 0x11, 0x54,
-    0x6c, 0xbf, 0x42, 0x1e, 0x7b, 0x85, 0x4e, 0x5a, 0x7f, 0x9c, 0x3f, 0x7f,
-    0xba, 0xe7, 0xfd, 0x69, 0x35, 0x87, 0xbd, 0xb1, 0x04, 0xfa, 0x9f, 0x80,
+    0x6c, 0xbf, 0x42, 0x1e, 0x7b, 0x65, 0x4e, 0x5a, 0x7f, 0x9c, 0x3f, 0x7f,
+    0xca, 0xe7, 0xfd, 0x69, 0x35, 0x87, 0xbd, 0xc1, 0x04, 0xfa, 0x9f, 0x80,
     0x0c, 0xb4, 0xda, 0xd2, 0xd3, 0xad, 0xb6, 0xd5, 0xa6, 0xfe, 0x92, 0x09,
     0x7f, 0x1d, 0x3d, 0xad, 0x1a, 0xcf, 0xfb, 0x34, 0x34, 0x6b, 0xc2, 0x4f,
-    0x2d, 0x3a, 0xbf, 0xc4, 0xa1, 0xc8, 0xfd, 0x3c, 0x20, 0xb8, 0x45, 0xbc,
-    0x7d, 0x3f, 0xbc, 0x39, 0xb7, 0xe6, 0x39, 0x69, 0xa8, 0xeb, 0x4f, 0xf0,
+    0x2d, 0x3a, 0xbf, 0xc4, 0xa1, 0xc8, 0xfd, 0x3c, 0x20, 0xb6, 0x45, 0xcc,
+    0x7d, 0x3f, 0xbc, 0x39, 0xc7, 0xe6, 0x39, 0x69, 0xa8, 0xeb, 0x4f, 0xf0,
     0xe3, 0xfc, 0x0d, 0xb6, 0xda, 0x94, 0xe3, 0xd7, 0x96, 0x87, 0x8f, 0xb3,
-    0x82, 0xaf, 0xcf, 0x27, 0xe1, 0x7f, 0x47, 0x23, 0xad, 0x3e, 0xc7, 0x9c,
+    0x62, 0xaf, 0xcf, 0x27, 0xe1, 0x7f, 0x47, 0x23, 0xad, 0x3e, 0xc7, 0x9c,
     0xce, 0x5a, 0x7e, 0xb0, 0x0d, 0x82, 0x05, 0xa1, 0x93, 0x98, 0x24, 0x5a,
     0x85, 0x0e, 0x8c, 0xbc, 0x5a, 0x61, 0x3c, 0xe6, 0xe8, 0xad, 0x3e, 0xfa,
-    0xac, 0xd7, 0x16, 0x99, 0xf2, 0x2e, 0x9e, 0x1f, 0x86, 0xa7, 0xff, 0x60,
-    0x28, 0xf9, 0xb3, 0xb3, 0xfc, 0x02, 0xd3, 0x72, 0x96, 0x99, 0xed, 0x2d,
-    0x3e, 0xc7, 0xe4, 0x20, 0xc3, 0x58, 0x60, 0xac, 0xfb, 0x99, 0xb7, 0x99,
-    0x69, 0xf3, 0xed, 0x58, 0x63, 0x96, 0x8d, 0xc9, 0x83, 0x05, 0xd7, 0x0f,
-    0xbe, 0x27, 0x9e, 0x06, 0x72, 0x96, 0x9f, 0xf9, 0xb0, 0x5d, 0xcc, 0x11,
+    0xac, 0xd6, 0xd6, 0x99, 0xf2, 0x2e, 0x9e, 0x1f, 0x86, 0xa7, 0xff, 0x60,
+    0x28, 0xf9, 0xc3, 0xb3, 0xfc, 0x02, 0xd3, 0x6e, 0x96, 0x99, 0xed, 0x2d,
+    0x3e, 0xc7, 0xe4, 0x20, 0xc3, 0x58, 0x60, 0xac, 0xfb, 0x79, 0xc7, 0x99,
+    0x69, 0xf3, 0xed, 0x58, 0x63, 0x96, 0x8e, 0x49, 0x83, 0x05, 0xd7, 0x0f,
+    0xbe, 0x27, 0x9e, 0x06, 0x6e, 0x96, 0x9f, 0xf9, 0xb0, 0x5d, 0xbc, 0x11,
     0x6b, 0x56, 0x91, 0x75, 0x14, 0x02, 0x81, 0xe2, 0x09, 0xfe, 0xc0, 0x60,
     0xb0, 0xfc, 0xcb, 0x45, 0x8d, 0xfe, 0xdb, 0xa5, 0x43, 0x82, 0x55, 0x41,
-    0xa5, 0x77, 0x87, 0xa1, 0x43, 0x1b, 0xb4, 0xc5, 0x43, 0xc3, 0x7f, 0x92,
+    0xa5, 0x77, 0x87, 0xa1, 0x43, 0x1b, 0xb4, 0xc5, 0x43, 0xc3, 0x7f, 0x72,
     0xa0, 0x29, 0x4f, 0xf2, 0xe1, 0x94, 0xa1, 0xa8, 0x53, 0x7a, 0x55, 0xf7,
     0xd0, 0x9e, 0x7f, 0x1e, 0xc1, 0x86, 0x93, 0xfd, 0x9d, 0x16, 0xf3, 0xb3,
-    0x8b, 0x4f, 0xc3, 0xfe, 0x02, 0x9c, 0xb4, 0xfa, 0x9d, 0xb8, 0x04, 0xb4,
-    0xf8, 0xc7, 0x9c, 0xdb, 0xf4, 0xa1, 0x91, 0x6f, 0x86, 0xdc, 0x2c, 0x30,
-    0xa2, 0x7f, 0xe3, 0x47, 0xea, 0x33, 0x34, 0x4c, 0x2b, 0x4e, 0xde, 0x20,
+    0x6b, 0x4f, 0xc3, 0xfe, 0x02, 0x9c, 0xb4, 0xfa, 0x9d, 0xc8, 0x04, 0xb4,
+    0xf8, 0xc7, 0x9c, 0xdc, 0xf4, 0xa1, 0x91, 0x6f, 0x86, 0xdb, 0x2c, 0x30,
+    0xa2, 0x7f, 0xe3, 0x47, 0xea, 0x33, 0x34, 0x4c, 0x2b, 0x4e, 0xe6, 0x20,
     0x2d, 0x1d, 0x3e, 0x13, 0xa1, 0xc8, 0xc5, 0x44, 0x0b, 0x2f, 0x2a, 0x81,
-    0x49, 0x17, 0x8d, 0xdd, 0xa3, 0xd3, 0xfe, 0x2d, 0xee, 0x11, 0x81, 0xff,
-    0xf6, 0x4a, 0x7f, 0x87, 0xda, 0xfb, 0x6f, 0x15, 0xab, 0x4f, 0xef, 0xab,
-    0x59, 0x82, 0x6a, 0xd0, 0xc7, 0xdb, 0xc3, 0xb9, 0xff, 0xc4, 0x6e, 0xe1,
-    0xa3, 0x77, 0x6b, 0xff, 0xd9, 0x69, 0xfd, 0x5c, 0xdb, 0x4c, 0x3e, 0x5a,
-    0x34, 0x88, 0x2f, 0x28, 0x4f, 0xf8, 0x5b, 0x63, 0xd7, 0xb3, 0x6f, 0x2d,
-    0x3f, 0xcd, 0x7f, 0x7d, 0xce, 0xb5, 0xab, 0x4f, 0xff, 0xee, 0x37, 0x7f,
-    0xc7, 0xe5, 0xad, 0x60, 0xde, 0x87, 0xf5, 0xa7, 0xcd, 0x83, 0xed, 0xe5,
-    0xa4, 0x75, 0xa7, 0xb5, 0x5b, 0x6e, 0x39, 0xb8, 0x12, 0x88, 0x02, 0xa1,
-    0x1d, 0xa1, 0x47, 0xd2, 0x33, 0x9f, 0x70, 0xeb, 0xd0, 0x99, 0x86, 0x56,
+    0x49, 0x17, 0x8d, 0xdd, 0xa3, 0xd3, 0xfe, 0x2e, 0x6d, 0x91, 0x81, 0xff,
+    0xf6, 0x4a, 0x7f, 0x87, 0xda, 0xfb, 0x8f, 0x15, 0xab, 0x4f, 0xef, 0xab,
+    0x59, 0x82, 0x6a, 0xd0, 0xc7, 0xdb, 0xb3, 0xb9, 0xff, 0xc4, 0x6f, 0x21,
+    0xa3, 0x79, 0x6b, 0xff, 0xd9, 0x69, 0xfd, 0x5b, 0xe3, 0x4c, 0x3e, 0x5a,
+    0x34, 0x88, 0x2f, 0x28, 0x4f, 0xf8, 0x5b, 0x83, 0xd7, 0xb3, 0x8f, 0x2d,
+    0x3f, 0xcd, 0x7f, 0x7d, 0xbe, 0xb5, 0xab, 0x4f, 0xff, 0xed, 0xb7, 0x7f,
+    0xc7, 0xe5, 0xad, 0x60, 0xde, 0x87, 0xf5, 0xa7, 0xcd, 0x83, 0xee, 0x65,
+    0xa4, 0x75, 0xa7, 0xb5, 0x5c, 0x72, 0x39, 0xb8, 0x12, 0x88, 0x02, 0xa1,
+    0x1e, 0x21, 0x47, 0xd2, 0x33, 0x9f, 0x6c, 0xeb, 0xd0, 0x99, 0x86, 0x56,
     0x4a, 0x52, 0xb0, 0x67, 0xff, 0xfb, 0xb6, 0x61, 0x3d, 0x54, 0xed, 0x31,
-    0xc1, 0xc6, 0x1b, 0xad, 0x3c, 0x0e, 0x7d, 0x62, 0xd3, 0xf8, 0x14, 0xec,
+    0xc1, 0xb6, 0x1b, 0xad, 0x3c, 0x0d, 0xfd, 0x62, 0xd3, 0xf8, 0x14, 0xec,
     0xb3, 0xe1, 0x5a, 0x6c, 0xb1, 0x8f, 0x5f, 0x09, 0x27, 0xf7, 0x5b, 0xc5,
-    0xfb, 0x39, 0x69, 0xd6, 0x0b, 0x96, 0x86, 0x5d, 0x09, 0x3a, 0xef, 0x25,
+    0xfb, 0x39, 0x69, 0xd6, 0x0b, 0x96, 0x86, 0x5d, 0x09, 0x3a, 0xee, 0xe5,
     0xa6, 0x51, 0x50, 0xc2, 0xf3, 0xc5, 0x9f, 0x19, 0x4f, 0xe7, 0x3d, 0x83,
-    0x5c, 0xc5, 0xa7, 0xf3, 0xfa, 0x78, 0x0c, 0x36, 0x2d, 0x3f, 0xfb, 0xb7,
-    0x1c, 0x7e, 0x38, 0xdd, 0x6d, 0x2d, 0x3f, 0xa9, 0xee, 0x61, 0xf3, 0x4b,
+    0x5b, 0xc5, 0xa7, 0xf3, 0xfa, 0x78, 0x0c, 0x36, 0x2d, 0x3f, 0xfb, 0xb7,
+    0x1c, 0x7e, 0x38, 0xdd, 0x6d, 0x2d, 0x3f, 0xa9, 0xed, 0xe1, 0xf3, 0x4b,
     0x46, 0x1f, 0xd3, 0xf4, 0x89, 0xf8, 0x8d, 0xf6, 0x61, 0xd6, 0x9f, 0x60,
-    0x0b, 0xba, 0x5a, 0x77, 0xbd, 0xb2, 0xd3, 0xff, 0x6d, 0x72, 0xe6, 0xe7,
-    0xdc, 0xe1, 0x1d, 0x69, 0x6b, 0xf3, 0xe6, 0xf0, 0xec, 0x9a, 0xc4, 0x5a,
-    0xdb, 0x08, 0xc8, 0x64, 0xf8, 0x8a, 0x15, 0xfc, 0x23, 0xf4, 0x38, 0x67,
-    0xfc, 0x24, 0x66, 0xd8, 0xff, 0x66, 0x31, 0x69, 0xc0, 0xae, 0xad, 0x36,
+    0x0b, 0xba, 0x5a, 0x77, 0xbd, 0xc2, 0xd3, 0xff, 0x71, 0x72, 0xdf, 0x27,
+    0xdc, 0xd9, 0x1d, 0x69, 0x6b, 0xf3, 0xe6, 0xf0, 0xec, 0x9a, 0xc4, 0x5a,
+    0xdb, 0x08, 0xc8, 0x64, 0xf8, 0x8a, 0x15, 0xfb, 0x23, 0xf4, 0x38, 0x67,
+    0xfc, 0x24, 0x67, 0x18, 0xff, 0x86, 0x31, 0x69, 0xc0, 0xae, 0xad, 0x36,
     0x3e, 0xad, 0x3d, 0xf9, 0x82, 0xe5, 0xa6, 0xfe, 0x84, 0xdd, 0xf8, 0x62,
-    0x19, 0x10, 0x5f, 0x2b, 0x4d, 0xb3, 0xcb, 0x4f, 0x8c, 0xf7, 0xe7, 0xc5,
+    0x19, 0x10, 0x5f, 0x2b, 0x4d, 0xc3, 0xcb, 0x4f, 0x8c, 0xf7, 0xe7, 0xc5,
     0xa0, 0xe7, 0x83, 0xf8, 0xc4, 0xeb, 0xb6, 0x96, 0x9f, 0xf8, 0xfa, 0x3b,
     0x01, 0xbb, 0xf3, 0x7e, 0xb4, 0xff, 0x9a, 0x87, 0x3d, 0xe2, 0xb7, 0xcb,
     0x43, 0x22, 0xff, 0x08, 0xc8, 0x6f, 0xe4, 0x58, 0x65, 0xc7, 0xb7, 0x4a,
     0x13, 0xc4, 0x6d, 0x43, 0x13, 0xd1, 0x91, 0xcf, 0xfe, 0xd6, 0x0f, 0xd7,
-    0xe6, 0x1d, 0xea, 0xba, 0xd3, 0xf7, 0x19, 0xec, 0xcb, 0xad, 0x3f, 0xf1,
-    0x73, 0x1e, 0x27, 0xbe, 0x7d, 0xfb, 0x4b, 0x4f, 0x99, 0xf7, 0x8c, 0xf2,
-    0xd0, 0xe3, 0xf4, 0xd2, 0x54, 0xf8, 0x14, 0x2d, 0xc5, 0xa7, 0xff, 0xb5,
-    0xed, 0x17, 0xec, 0xed, 0x66, 0x6d, 0xe5, 0xa4, 0x5c, 0x3f, 0x5b, 0x49,
+    0xde, 0x1d, 0xea, 0xba, 0xd3, 0xf6, 0xd9, 0xec, 0xcb, 0xad, 0x3f, 0xf1,
+    0x6f, 0x1e, 0x27, 0xbe, 0x7d, 0xfb, 0x4b, 0x4f, 0x99, 0xf7, 0x6c, 0xf2,
+    0xd0, 0xe3, 0xf4, 0xd2, 0x54, 0xf8, 0x14, 0x2d, 0xb5, 0xa7, 0xff, 0xb5,
+    0xed, 0x17, 0xec, 0xed, 0x66, 0x71, 0xe5, 0xa4, 0x5b, 0x3f, 0x5b, 0x49,
     0x67, 0xe1, 0xa2, 0x03, 0x0a, 0xd3, 0xd6, 0x33, 0xeb, 0x2d, 0x39, 0xff,
-    0xdc, 0x5a, 0x18, 0xf0, 0xc8, 0x92, 0x7f, 0x98, 0xcf, 0x17, 0x4b, 0xdf,
-    0x2d, 0x3f, 0x7c, 0x67, 0x33, 0x6d, 0x2d, 0x3f, 0xff, 0xfe, 0xeb, 0x78,
-    0x6a, 0xe5, 0xa7, 0x8b, 0xb7, 0x6d, 0x79, 0xb6, 0x63, 0x33, 0x4b, 0x4f,
-    0xff, 0xff, 0xfb, 0x5a, 0xcf, 0x72, 0x8f, 0xae, 0xf4, 0xb6, 0xf5, 0x51,
-    0xb9, 0x8f, 0x7d, 0xe6, 0xb7, 0x66, 0x5a, 0x19, 0x31, 0xfa, 0x84, 0x14,
-    0xf7, 0xf5, 0xb6, 0x96, 0x9a, 0xdb, 0x56, 0x8d, 0x1b, 0xbb, 0x48, 0xe7,
-    0xc5, 0xca, 0xce, 0x24, 0x13, 0x45, 0x0e, 0x5c, 0x76, 0x02, 0x56, 0xd0,
-    0xa0, 0x7a, 0x12, 0xe7, 0x28, 0xe3, 0x67, 0xe4, 0x0f, 0x91, 0xc8, 0xc6,
-    0x9f, 0xa8, 0x42, 0x4f, 0x9e, 0x67, 0xb8, 0xcb, 0x4e, 0xb6, 0xdb, 0x52,
+    0xdb, 0x5a, 0x18, 0xf0, 0xc8, 0x92, 0x7f, 0x98, 0xcf, 0x17, 0x4b, 0xdf,
+    0x2d, 0x3f, 0x7c, 0x66, 0xf3, 0x8d, 0x2d, 0x3f, 0xff, 0xfe, 0xeb, 0x78,
+    0x6a, 0xe5, 0xa7, 0x8b, 0xb7, 0x6d, 0x79, 0xb8, 0x63, 0x33, 0x4b, 0x4f,
+    0xff, 0xff, 0xfb, 0x5a, 0xcf, 0x6e, 0x8f, 0xae, 0xf4, 0xb8, 0xf5, 0x51,
+    0xb9, 0x8f, 0x7d, 0xe6, 0xb7, 0x86, 0x5a, 0x19, 0x31, 0xfa, 0x84, 0x14,
+    0xf7, 0xf5, 0xc6, 0x96, 0x9a, 0xdb, 0x56, 0x8d, 0x1b, 0xbb, 0x48, 0xe7,
+    0xc5, 0xba, 0xcd, 0xa4, 0x13, 0x45, 0x0e, 0x5c, 0x76, 0x02, 0x57, 0x10,
+    0xa0, 0x7a, 0x12, 0xe7, 0x28, 0xdb, 0x67, 0xe4, 0x0f, 0x91, 0xc8, 0xc6,
+    0x9f, 0xa8, 0x42, 0x4f, 0x9e, 0x67, 0xb6, 0xcb, 0x4e, 0xb6, 0xdb, 0x52,
     0x9e, 0x77, 0xb2, 0xe9, 0x04, 0xbf, 0x9f, 0x60, 0x0b, 0xfd, 0x2e, 0xef,
     0xe8, 0xe9, 0xf2, 0x09, 0x84, 0x32, 0x33, 0x2a, 0x15, 0x73, 0x77, 0x4b,
-    0x4e, 0x7e, 0xd7, 0x5a, 0x18, 0xd9, 0x90, 0xb4, 0xfc, 0x3b, 0x69, 0xec,
-    0xf2, 0xd3, 0xff, 0xc3, 0xe7, 0xb3, 0xb9, 0x47, 0x6e, 0x10, 0x16, 0x9f,
-    0x59, 0xbf, 0x2c, 0xd9, 0x69, 0xf1, 0x69, 0xac, 0x3a, 0xec, 0xfd, 0x9f,
+    0x4e, 0x7e, 0xd7, 0x5a, 0x18, 0xd9, 0x90, 0xb4, 0xfc, 0x3c, 0x69, 0xec,
+    0xf2, 0xd3, 0xff, 0xc3, 0xe7, 0xb3, 0xb9, 0x47, 0x6d, 0x90, 0x16, 0x9f,
+    0x59, 0xcf, 0x2c, 0xe1, 0x69, 0xf1, 0x69, 0xac, 0x3a, 0xec, 0xfd, 0x9f,
     0x35, 0x77, 0xff, 0x97, 0x67, 0xec, 0xd4, 0xe5, 0xd9, 0xfb, 0x3d, 0xf3,
     0xb3, 0x4b, 0xb3, 0xf6, 0x00, 0x7a, 0x22, 0x45, 0x3e, 0x6c, 0xd6, 0x0a,
     0xec, 0xfd, 0x85, 0xd9, 0xfb, 0x35, 0x79, 0x76, 0x7e, 0x9c, 0xb7, 0x93,
-    0x84, 0xfe, 0x7e, 0x48, 0x9e, 0xcd, 0xe2, 0xfd, 0x76, 0x7e, 0xc2, 0xec,
+    0x84, 0xfe, 0x7e, 0x48, 0x9e, 0xce, 0x62, 0xfd, 0x76, 0x7e, 0xc2, 0xec,
     0xfd, 0x9b, 0xf6, 0x5d, 0x9f, 0xb3, 0xfd, 0x9f, 0xd7, 0x71, 0xfe, 0x01,
-    0x76, 0x7e, 0xcf, 0xd9, 0xc2, 0xd7, 0xd6, 0x2e, 0xcf, 0xd8, 0xfd, 0x14,
+    0x76, 0x7e, 0xcf, 0xd9, 0xb2, 0xd7, 0xd6, 0x2e, 0xcf, 0xd8, 0xfd, 0x14,
     0x82, 0x47, 0xa4, 0x69, 0xee, 0xd8, 0xde, 0x5d, 0x9f, 0xb0, 0xbb, 0x3f,
     0x70, 0xd7, 0xcd, 0x6d, 0xab, 0xb3, 0xf6, 0x1c, 0xac, 0x48, 0x06, 0x99,
-    0x08, 0x6d, 0xa1, 0x35, 0xd2, 0x83, 0x98, 0xea, 0x17, 0x7e, 0x5f, 0xb4,
+    0x08, 0x6e, 0x21, 0x35, 0xd2, 0x83, 0x98, 0xea, 0x17, 0x7e, 0x5f, 0xb4,
     0x9a, 0x7b, 0x1c, 0xc6, 0xa6, 0xcf, 0xd0, 0x51, 0x21, 0x3f, 0xec, 0xb8,
-    0x33, 0xb6, 0xd7, 0x1e, 0x5a, 0x67, 0x58, 0x94, 0x58, 0x89, 0x7d, 0x90,
+    0x33, 0xb6, 0xd6, 0xde, 0x5a, 0x67, 0x58, 0x94, 0x58, 0x89, 0x7e, 0x10,
     0x44, 0xfe, 0x3a, 0xb9, 0x2d, 0x53, 0x8c, 0xb3, 0xff, 0xe7, 0x75, 0x9f,
-    0xd3, 0xcc, 0x0e, 0x31, 0xe9, 0xe5, 0xa7, 0xf3, 0xed, 0x68, 0xb4, 0xf6,
-    0x2d, 0x1b, 0x22, 0x33, 0xaa, 0xf0, 0xcb, 0xcc, 0xce, 0x59, 0xc1, 0xf2,
-    0x2e, 0xa9, 0xce, 0x3f, 0xa1, 0x73, 0x3f, 0xff, 0xc5, 0xca, 0xe3, 0x38,
-    0x39, 0xfb, 0x3b, 0x40, 0xe3, 0x75, 0x69, 0xff, 0x1a, 0xdb, 0x0b, 0x73,
-    0x9f, 0x5d, 0x69, 0xf8, 0x68, 0xde, 0x53, 0x96, 0x9d, 0x6d, 0xb6, 0xa5,
+    0xd3, 0xcc, 0x0d, 0xb1, 0xe9, 0xe5, 0xa7, 0xf3, 0xed, 0x68, 0xb4, 0xf6,
+    0x2d, 0x1c, 0x22, 0x33, 0xaa, 0xf0, 0xcb, 0xcc, 0xce, 0x59, 0xc1, 0xf2,
+    0x2e, 0xa9, 0xce, 0x3f, 0xa1, 0x73, 0x3f, 0xff, 0xc5, 0xba, 0xdb, 0x38,
+    0x39, 0xfb, 0x3b, 0x40, 0xdb, 0x75, 0x69, 0xff, 0x1a, 0xdc, 0x0b, 0x6f,
+    0x7f, 0x5d, 0x69, 0xf8, 0x68, 0xdd, 0xd3, 0x96, 0x9d, 0x6d, 0xb6, 0xa5,
     0x38, 0xc6, 0xfd, 0x20, 0x97, 0xf3, 0xfd, 0x4e, 0x33, 0xb8, 0xfe, 0xba,
-    0xb4, 0xff, 0xfb, 0x98, 0xe3, 0x04, 0x72, 0xc7, 0x9c, 0xdd, 0xb1, 0x68,
-    0x14, 0x48, 0xf8, 0xea, 0x79, 0xcd, 0xfe, 0xcb, 0x4f, 0xdf, 0xb0, 0xe6,
-    0xde, 0x5a, 0x70, 0xd7, 0xfb, 0x8f, 0x40, 0xc2, 0x39, 0xcf, 0xdb, 0xab,
+    0xb4, 0xff, 0xfb, 0x78, 0xe3, 0x04, 0x72, 0xc7, 0x9c, 0xdd, 0xb1, 0x68,
+    0x14, 0x48, 0xf8, 0xea, 0x79, 0xcd, 0xff, 0x0b, 0x4f, 0xdf, 0xb0, 0xe7,
+    0x1e, 0x5a, 0x70, 0xd7, 0xfc, 0x8f, 0x40, 0xc2, 0x39, 0xcf, 0xdb, 0xab,
     0x43, 0x95, 0x39, 0x01, 0x94, 0xe8, 0x1f, 0xa5, 0x8c, 0x2f, 0x7c, 0xe4,
-    0xfc, 0xda, 0x7f, 0xc2, 0x25, 0xf3, 0xe5, 0xad, 0xe6, 0xf2, 0xd3, 0xad,
+    0xfc, 0xda, 0x7f, 0xc2, 0x25, 0xf3, 0xe5, 0xae, 0x66, 0xf2, 0xd3, 0xad,
     0xb6, 0xd4, 0xc4, 0x20, 0x9f, 0x76, 0xf5, 0x57, 0x4c, 0x42, 0x00, 0x9a,
     0xd9, 0xd6, 0xdb, 0x6a, 0x62, 0x0f, 0x42, 0x62, 0x0f, 0x04, 0xd6, 0xc8,
-    0xf8, 0x89, 0x9b, 0xb8, 0xcf, 0xfd, 0xc6, 0x71, 0x7b, 0x39, 0x76, 0x3a,
+    0xf8, 0x89, 0x9b, 0xb8, 0xcf, 0xfd, 0xb6, 0x71, 0x7b, 0x37, 0x76, 0x3a,
     0xd0, 0xc7, 0xd4, 0xfc, 0xa2, 0x7d, 0xe6, 0xf3, 0x1d, 0x69, 0xe1, 0x26,
-    0x35, 0x69, 0xc6, 0x37, 0xeb, 0x45, 0x86, 0xf4, 0xe4, 0x13, 0xeb, 0xb7,
-    0x18, 0xe9, 0x4f, 0x86, 0x84, 0x71, 0x29, 0xb2, 0xe9, 0x4d, 0x6d, 0xa9,
-    0x46, 0x1f, 0xb6, 0x89, 0xbc, 0x47, 0x68, 0xac, 0xfe, 0x13, 0x1b, 0xfe,
-    0x63, 0xea, 0x41, 0x37, 0x90, 0xc9, 0xbe, 0x93, 0x1d, 0x43, 0x52, 0x7f,
+    0x35, 0x69, 0xc6, 0x37, 0xeb, 0x45, 0x86, 0xf4, 0xe4, 0x13, 0xeb, 0xb6,
+    0xd8, 0xe9, 0x4f, 0x86, 0x84, 0x71, 0x29, 0xb2, 0xe9, 0x4d, 0x6d, 0xa9,
+    0x46, 0x1f, 0xb6, 0x89, 0xbc, 0x47, 0x68, 0xac, 0xfe, 0x13, 0x1b, 0xfd,
+    0xe3, 0xea, 0x41, 0x37, 0x90, 0xc9, 0xbe, 0x93, 0x1d, 0x43, 0x52, 0x7f,
     0xe6, 0xff, 0x59, 0x80, 0xa1, 0xa3, 0x56, 0x9f, 0xfe, 0x76, 0xad, 0x6e,
-    0xf1, 0x8b, 0x8c, 0x24, 0xb4, 0xff, 0xf6, 0x87, 0x36, 0xfe, 0xb5, 0x5d,
-    0x2d, 0xbf, 0x5a, 0x36, 0x44, 0xf5, 0xa9, 0xb1, 0xc4, 0xc1, 0x6a, 0x1c,
-    0x13, 0xfb, 0x46, 0x1f, 0x56, 0xd6, 0xcb, 0x43, 0x2e, 0x8c, 0x63, 0x69,
+    0xed, 0x8b, 0x6c, 0x24, 0xb4, 0xff, 0xf6, 0x87, 0x38, 0xfe, 0xb5, 0x5d,
+    0x2e, 0x3f, 0x5a, 0x38, 0x44, 0xf5, 0xa9, 0xb1, 0xb4, 0xc1, 0x6a, 0x1c,
+    0x13, 0xfb, 0x46, 0x1f, 0x56, 0xd7, 0x0b, 0x43, 0x2e, 0x8c, 0x63, 0x69,
     0x46, 0xeb, 0xd8, 0xe8, 0x2a, 0x33, 0x8d, 0x14, 0x4e, 0xb6, 0xdb, 0x52,
-    0x9e, 0xfe, 0x84, 0x09, 0x04, 0xbf, 0x9e, 0xee, 0xe3, 0x77, 0x96, 0x9f,
+    0x9e, 0xfe, 0x84, 0x09, 0x04, 0xbf, 0x9e, 0xef, 0x23, 0x79, 0x96, 0x9f,
     0x8c, 0xce, 0xe6, 0x3c, 0xb4, 0x39, 0x10, 0xa4, 0x61, 0xa2, 0x89, 0xff,
-    0xe1, 0xdb, 0x76, 0xdf, 0xb0, 0xde, 0xac, 0x1a, 0x5a, 0x7f, 0xff, 0xff,
-    0xbd, 0xa3, 0x35, 0xbb, 0x94, 0x5e, 0x2e, 0x5c, 0xb7, 0x38, 0xb9, 0x43,
+    0xe1, 0xe3, 0x97, 0x1f, 0xb0, 0xde, 0xac, 0x1a, 0x5a, 0x7f, 0xff, 0xff,
+    0xbd, 0xa3, 0x35, 0xcb, 0x74, 0x5e, 0x2d, 0xdc, 0xb9, 0x38, 0xb7, 0x43,
     0xe7, 0x67, 0x71, 0x69, 0xff, 0xec, 0x60, 0x80, 0x5b, 0xeb, 0xe1, 0xa5,
-    0xd5, 0xa7, 0x9f, 0x79, 0xfb, 0xf5, 0xa1, 0xc7, 0xed, 0x49, 0xd3, 0xff,
-    0x67, 0x7f, 0x39, 0x68, 0xcd, 0x36, 0x2d, 0x3a, 0xb7, 0xfa, 0x5a, 0x1c,
+    0xd5, 0xa7, 0x9f, 0x77, 0xfb, 0xf5, 0xa1, 0xc7, 0xed, 0x49, 0xd3, 0xff,
+    0x67, 0x7f, 0x39, 0x68, 0xcd, 0x36, 0x2d, 0x3a, 0xb9, 0xfa, 0x5a, 0x1c,
     0x7c, 0x74, 0x89, 0x38, 0x18, 0x75, 0xa7, 0xbf, 0xf6, 0x7e, 0xb4, 0x11,
     0xbe, 0xe8, 0xdc, 0x32, 0xa9, 0x07, 0x17, 0xe2, 0x95, 0x46, 0x18, 0x30,
-    0x84, 0xf9, 0x7e, 0x7e, 0xdf, 0xe8, 0x87, 0x67, 0x2d, 0x3f, 0xea, 0xee,
-    0xf1, 0x73, 0x2b, 0x6f, 0xd6, 0x9f, 0xff, 0x17, 0x98, 0xf9, 0xb7, 0x98,
+    0x84, 0xf9, 0x7e, 0x7e, 0xe7, 0xe8, 0x87, 0x87, 0x2d, 0x3f, 0xea, 0xef,
+    0x31, 0x6f, 0x2b, 0x8f, 0xd6, 0x9f, 0xff, 0x17, 0x98, 0xf9, 0xc7, 0x98,
     0xe6, 0x76, 0xbf, 0x5a, 0x7f, 0xb4, 0xdd, 0xf3, 0x04, 0x04, 0xb4, 0x39,
-    0x11, 0xba, 0x56, 0x9f, 0xc4, 0x6f, 0x9b, 0x8d, 0xa5, 0xa7, 0xfc, 0x34,
-    0xf7, 0x2b, 0xbd, 0x20, 0x2d, 0x18, 0x9c, 0x8d, 0xcc, 0x4a, 0x19, 0xdd,
+    0x11, 0xba, 0x56, 0x9f, 0xc4, 0x6f, 0x9b, 0x6d, 0xa5, 0xa7, 0xfc, 0x34,
+    0xf6, 0xeb, 0xbd, 0x20, 0x2d, 0x18, 0x9c, 0x8d, 0xcc, 0x4a, 0x19, 0xdd,
     0x23, 0x39, 0x94, 0xf3, 0x8c, 0xc7, 0x96, 0x9f, 0x74, 0x19, 0x82, 0xb4,
-    0xff, 0xdb, 0xf2, 0xce, 0x33, 0xfd, 0x71, 0xb6, 0x5a, 0x28, 0xfb, 0xb4,
-    0x4b, 0x3f, 0xd8, 0x3d, 0xcd, 0x61, 0x71, 0x69, 0xff, 0xde, 0xd6, 0x3d,
-    0xc6, 0x7f, 0x9c, 0xe3, 0x2d, 0x3f, 0x70, 0x04, 0xfd, 0x80, 0xb4, 0x61,
-    0xfc, 0xd2, 0x5c, 0xf6, 0x78, 0xb4, 0xb4, 0xfd, 0xbe, 0xef, 0x0b, 0xe1,
+    0xff, 0xdc, 0xf2, 0xcd, 0xb3, 0xfd, 0x6d, 0xb8, 0x5a, 0x28, 0xfb, 0xb4,
+    0x4b, 0x3f, 0xd8, 0x3d, 0xcd, 0x61, 0x6d, 0x69, 0xff, 0xde, 0xd6, 0x3d,
+    0xb6, 0x7f, 0x9b, 0xdb, 0x2d, 0x3f, 0x6c, 0x04, 0xfd, 0x80, 0xb4, 0x61,
+    0xfc, 0xd2, 0x5c, 0xf6, 0x78, 0xb4, 0xb4, 0xfd, 0xce, 0xef, 0x0b, 0xe1,
     0x67, 0xcb, 0x46, 0x8f, 0x73, 0xc4, 0x10, 0x04, 0xdb, 0x6e, 0x42, 0x30,
-    0xae, 0xf3, 0xe4, 0xff, 0xf1, 0x6b, 0x01, 0xed, 0x11, 0xbc, 0xa1, 0x25,
-    0xa7, 0xff, 0xf0, 0xfb, 0x38, 0x4d, 0xe2, 0xfd, 0x9c, 0x1b, 0x6d, 0xb5,
+    0xae, 0xf3, 0xe4, 0xff, 0xf1, 0x6b, 0x01, 0xed, 0x11, 0xbb, 0xa1, 0x25,
+    0xa7, 0xff, 0xf0, 0xfb, 0x36, 0x4d, 0xe2, 0xfd, 0x9c, 0x1b, 0x6d, 0xb5,
     0x29, 0xe0, 0x66, 0x1a, 0x94, 0xf5, 0x7f, 0x5e, 0x5a, 0x73, 0x87, 0xe5,
     0x44, 0x33, 0x3a, 0xdb, 0x6d, 0x4a, 0x76, 0x0d, 0xd2, 0x09, 0x7f, 0x3f,
     0xec, 0xb3, 0x01, 0x4e, 0x16, 0xb1, 0x68, 0xfc, 0xf9, 0x84, 0xa6, 0x7f,
-    0x38, 0x73, 0xde, 0xfa, 0xc5, 0xa7, 0x56, 0xfd, 0x96, 0x86, 0x4f, 0x1a,
-    0xc6, 0x20, 0x10, 0xec, 0x43, 0xd8, 0x57, 0x51, 0x17, 0xc6, 0x93, 0xc5,
-    0xe2, 0x65, 0xa7, 0xff, 0xdc, 0xae, 0xeb, 0x08, 0x6b, 0xda, 0xb5, 0x85,
-    0x69, 0xfd, 0xf6, 0x77, 0xff, 0xc8, 0x56, 0x9f, 0xfe, 0x71, 0x03, 0x86,
+    0x38, 0x73, 0xde, 0xfa, 0xc5, 0xa7, 0x57, 0x3d, 0x96, 0x86, 0x4f, 0x1a,
+    0xc6, 0x20, 0x10, 0xf0, 0x43, 0xd8, 0x57, 0x51, 0x17, 0xc6, 0x93, 0xc5,
+    0xe2, 0x65, 0xa7, 0xff, 0xdb, 0xae, 0xeb, 0x08, 0x6b, 0xda, 0xb5, 0x85,
+    0x69, 0xfd, 0xf6, 0x77, 0xff, 0xc8, 0x56, 0x9f, 0xfe, 0x71, 0x03, 0x66,
     0x6b, 0x3b, 0x7a, 0xab, 0xaa, 0x20, 0xc9, 0xff, 0xdd, 0xa7, 0xf8, 0x37,
-    0xe1, 0x7e, 0x67, 0x96, 0x86, 0x45, 0x47, 0xcb, 0x91, 0xc4, 0x7f, 0xfa,
-    0x1b, 0x93, 0x73, 0xe5, 0xa7, 0x15, 0xbb, 0xf5, 0xa7, 0xf6, 0x3f, 0xd7,
-    0xbc, 0xc7, 0x5a, 0x3a, 0x7a, 0x9c, 0x20, 0x82, 0x44, 0x6e, 0x9a, 0xe7,
-    0xf6, 0xf6, 0x6b, 0x7b, 0x3a, 0xcb, 0x4f, 0xff, 0x0b, 0x5f, 0x77, 0x70,
+    0xd9, 0x7e, 0x67, 0x96, 0x86, 0x45, 0x47, 0xcb, 0x91, 0xb4, 0x7f, 0xfa,
+    0x1b, 0x93, 0x6f, 0xe5, 0xa7, 0x15, 0xbc, 0xf5, 0xa7, 0xf6, 0x3f, 0xd7,
+    0xbc, 0xc7, 0x5a, 0x3a, 0x7a, 0x9b, 0x20, 0x82, 0x44, 0x6e, 0x9a, 0xe7,
+    0xf7, 0x36, 0x6b, 0x9b, 0x3a, 0xcb, 0x4f, 0xff, 0x0b, 0x5f, 0x97, 0x70,
     0xbe, 0x7c, 0xb1, 0xf7, 0x16, 0x87, 0x2a, 0xd5, 0x00, 0xf9, 0x46, 0xa3,
-    0xa8, 0x5a, 0xf8, 0x89, 0xf4, 0xde, 0x7f, 0xf9, 0x86, 0xcd, 0xcf, 0x10,
+    0xa8, 0x5a, 0xf8, 0x89, 0xf4, 0xde, 0x7f, 0xf9, 0x86, 0xce, 0x4f, 0x10,
     0xe0, 0x30, 0x4d, 0x5a, 0x73, 0x75, 0xe5, 0xa1, 0x99, 0x16, 0x60, 0x4e,
-    0xc8, 0xff, 0xae, 0x84, 0x52, 0x84, 0xea, 0x5a, 0x07, 0xcc, 0xdb, 0xca,
+    0xc8, 0xff, 0xae, 0x84, 0x52, 0x84, 0xea, 0x5a, 0x07, 0xcc, 0xdc, 0xca,
     0x13, 0xea, 0x3d, 0x66, 0x96, 0x9f, 0xfe, 0xbb, 0x6b, 0x4c, 0x7c, 0xf1,
-    0x6b, 0x94, 0xb4, 0xfe, 0x11, 0x67, 0x1d, 0x89, 0x69, 0xfd, 0xe2, 0x7e,
+    0x6b, 0x74, 0xb4, 0xfe, 0x11, 0x67, 0x1d, 0x89, 0x69, 0xfd, 0xe2, 0x7e,
     0x5e, 0x73, 0x25, 0x23, 0xad, 0x3f, 0x60, 0x9b, 0xd6, 0x09, 0x1e, 0x19,
-    0x86, 0x71, 0x89, 0x81, 0x92, 0x6e, 0x9c, 0xa7, 0xbb, 0xf6, 0xce, 0x5a,
+    0x86, 0x71, 0x89, 0x81, 0x92, 0x6e, 0x9c, 0xa7, 0xbb, 0xf7, 0x0e, 0x5a,
     0x7f, 0x0b, 0x87, 0x0e, 0xd7, 0x5a, 0x71, 0xc1, 0x8b, 0x43, 0x1f, 0x7e,
-    0x12, 0x51, 0x84, 0xf9, 0x8f, 0xec, 0xba, 0xd3, 0x15, 0x8b, 0x47, 0x0d,
+    0x12, 0x51, 0x84, 0xf9, 0x8f, 0xec, 0xba, 0xd3, 0x15, 0x8b, 0x46, 0xcd,
     0xe7, 0x89, 0xa7, 0xd6, 0x90, 0x1f, 0x49, 0x69, 0xff, 0x7d, 0xed, 0x19,
     0xdf, 0xce, 0x5a, 0x5a, 0x5a, 0x5a, 0x7e, 0x6b, 0x30, 0x48, 0xd5, 0xa3,
-    0xa6, 0xf3, 0x42, 0x13, 0xff, 0x30, 0xd7, 0x1b, 0xf0, 0xf7, 0x5a, 0x4a,
+    0xa6, 0xf3, 0x42, 0x13, 0xff, 0x30, 0xd6, 0xdb, 0xf0, 0xf7, 0x5a, 0x4a,
     0x00, 0x8e, 0x2c, 0x7e, 0x39, 0x0c, 0xfe, 0xa0, 0x6b, 0x4d, 0xd7, 0x2d,
-    0x3f, 0xb6, 0x76, 0xda, 0x2f, 0x32, 0xd3, 0x8b, 0x6d, 0x2d, 0x2e, 0x61,
-    0xe8, 0x11, 0xa4, 0xff, 0xfb, 0x01, 0xba, 0x8b, 0x34, 0xd6, 0x60, 0x91,
+    0x3f, 0xb8, 0x77, 0x1a, 0x2f, 0x32, 0xd3, 0x8b, 0x8d, 0x2d, 0x2d, 0xe1,
+    0xe8, 0x11, 0xa4, 0xff, 0xfb, 0x01, 0xca, 0x8b, 0x34, 0xd6, 0x60, 0x91,
     0xab, 0x4f, 0xcc, 0xf1, 0x1d, 0x9e, 0x5a, 0x6c, 0xb5, 0x68, 0x4a, 0x7e,
     0xd1, 0x8d, 0xd6, 0x3a, 0x50, 0x94, 0x25, 0x09, 0x42, 0x50, 0xe3, 0xdf,
-    0x20, 0xaf, 0xcb, 0x7e, 0x0a, 0xde, 0x0a, 0x7d, 0x0a, 0x9b, 0x98, 0x94,
-    0xfd, 0x55, 0xe7, 0x98, 0xe9, 0x6e, 0x2d, 0x64, 0xf9, 0x25, 0x09, 0x42,
+    0x20, 0xaf, 0xcb, 0x7e, 0x0a, 0xe6, 0x0a, 0x7d, 0x0a, 0x9b, 0x78, 0x94,
+    0xfd, 0x55, 0xe7, 0x98, 0xe9, 0x72, 0x2d, 0x64, 0xf9, 0x25, 0x09, 0x42,
     0x50, 0xe2, 0xd0, 0x82, 0xa1, 0x28, 0x4a, 0x12, 0x84, 0xa1, 0x28, 0x4a,
     0x2c, 0x37, 0x80, 0x0a, 0x20, 0xaf, 0xc2, 0xb4, 0x14, 0xfc, 0x2a, 0x12,
     0x84, 0xa1, 0xc5, 0xa6, 0x82, 0xa1, 0x28, 0x4a, 0x12, 0x84, 0xa1, 0xc6,
     0xa3, 0xf0, 0xaf, 0x05, 0x3e, 0x85, 0x42, 0x50, 0x94, 0x25, 0x09, 0x45,
-    0x86, 0xa0, 0xd0, 0xae, 0x85, 0x70, 0x2a, 0x46, 0xa5, 0x09, 0x42, 0x50,
-    0x94, 0x25, 0x00, 0x35, 0x1b, 0x05, 0x7e, 0x15, 0xf0, 0x54, 0x25, 0x09,
-    0x42, 0x53, 0xee, 0x37, 0xfa, 0xc4, 0xa1, 0x28, 0x71, 0xe7, 0xdc, 0x2b,
-    0x81, 0x54, 0x15, 0xf9, 0x3c, 0xae, 0x94, 0x25, 0x09, 0x42, 0x50, 0x94,
-    0x38, 0xd4, 0x6c, 0x14, 0x41, 0x4f, 0xc2, 0xa1, 0x28, 0x4a, 0x12, 0x84,
-    0xa1, 0xc6, 0xa0, 0x01, 0x5c, 0x0a, 0x10, 0xa9, 0x71, 0x28, 0x4a, 0x12,
-    0x93, 0x92, 0x84, 0xb6, 0x2c, 0x21, 0x28, 0x4a, 0x12, 0x84, 0xa2, 0xc3,
+    0x86, 0xa0, 0xd0, 0xae, 0x85, 0x6c, 0x2a, 0x46, 0xa5, 0x09, 0x42, 0x50,
+    0x94, 0x25, 0x00, 0x35, 0x1c, 0x05, 0x7e, 0x15, 0xf0, 0x54, 0x25, 0x09,
+    0x42, 0x53, 0xed, 0xb7, 0xfa, 0xc4, 0xa1, 0x28, 0x71, 0xe7, 0xdc, 0x2b,
+    0x61, 0x54, 0x15, 0xf9, 0x3c, 0xae, 0x94, 0x25, 0x09, 0x42, 0x50, 0x94,
+    0x38, 0xd4, 0x70, 0x14, 0x41, 0x4f, 0xc2, 0xa1, 0x28, 0x4a, 0x12, 0x84,
+    0xa1, 0xc6, 0xa0, 0x01, 0x5b, 0x0a, 0x10, 0xa9, 0x6d, 0x28, 0x4a, 0x12,
+    0x93, 0x92, 0x84, 0xb8, 0x2c, 0x21, 0x28, 0x4a, 0x12, 0x84, 0xa2, 0xc3,
     0xe6, 0x70, 0x51, 0xa3, 0x57, 0x1a, 0x78, 0x2b, 0xf0, 0xaf, 0x05, 0x4b,
-    0x12, 0x84, 0xa1, 0x29, 0x39, 0x28, 0x4b, 0x62, 0xc2, 0x12, 0x84, 0xa1,
+    0x12, 0x84, 0xa1, 0x29, 0x39, 0x28, 0x4b, 0x82, 0xc2, 0x12, 0x84, 0xa1,
     0x8f, 0x49, 0xc1, 0x44, 0x35, 0xd1, 0xa3, 0x85, 0x42, 0x50, 0x94, 0x25,
-    0x09, 0x42, 0x50, 0xc6, 0xcb, 0x60, 0xae, 0x85, 0x1c, 0x28, 0x42, 0xa1,
+    0x09, 0x42, 0x50, 0xc6, 0xcb, 0x80, 0xae, 0x85, 0x1c, 0x28, 0x42, 0xa1,
     0x28, 0x4a, 0x12, 0x8e, 0x97, 0xda, 0x0a, 0xf0, 0x54, 0x25, 0x09, 0x42,
     0x50, 0x72, 0xf8, 0x42, 0xbc, 0x15, 0x23, 0xa5, 0x09, 0x42, 0x51, 0xf9,
     0x69, 0xf0, 0x54, 0x25, 0x09, 0x42, 0x50, 0x94, 0x31, 0xa8, 0x78, 0x2b,
-    0x81, 0x5f, 0x05, 0x43, 0x2f, 0xd7, 0x58, 0xe4, 0xe3, 0xd0, 0x14, 0xe2,
-    0xdd, 0xdb, 0x36, 0x48, 0x26, 0x7d, 0xf9, 0xeb, 0xc7, 0x3d, 0x84, 0x69,
-    0xd2, 0x78, 0x71, 0x4c, 0xbf, 0x9d, 0x0b, 0xb6, 0x99, 0xfc, 0xc3, 0xf3,
-    0xcb, 0xf6, 0x5b, 0x52, 0xf7, 0x8b, 0x0c, 0x24, 0x7d, 0x4a, 0x9f, 0x38,
+    0x61, 0x5f, 0x05, 0x43, 0x2f, 0xd7, 0x58, 0xe4, 0xe3, 0xd0, 0x14, 0xe2,
+    0xdd, 0xdb, 0x38, 0x48, 0x26, 0x7e, 0x79, 0xeb, 0xc7, 0x3d, 0x84, 0x69,
+    0xd2, 0x76, 0x71, 0x4c, 0xbf, 0x9d, 0x0b, 0xb6, 0x99, 0xfc, 0xc3, 0xf3,
+    0xcb, 0xf6, 0x5b, 0x52, 0xf9, 0x8b, 0x0c, 0x24, 0x7d, 0x4a, 0x9f, 0x38,
     0x5b, 0x34, 0x90, 0x53, 0x57, 0x9c, 0x4c, 0x74, 0xa7, 0x8a, 0xf5, 0xd5,
-    0xa7, 0x10, 0x31, 0x69, 0x07, 0x64, 0x46, 0x74, 0xe3, 0x83, 0x5e, 0x20,
-    0x93, 0x3f, 0x65, 0x25, 0x19, 0x4a, 0x06, 0x9f, 0xdc, 0x2b, 0x41, 0xd6,
-    0xb1, 0x68, 0xd8, 0xfb, 0x1e, 0x38, 0x9f, 0x66, 0x3d, 0x46, 0x2d, 0x37,
+    0xa7, 0x10, 0x31, 0x69, 0x07, 0x84, 0x46, 0x74, 0xe3, 0x63, 0x5e, 0x20,
+    0x93, 0x3f, 0x65, 0x25, 0x19, 0x4a, 0x06, 0x9f, 0xdb, 0x2b, 0x41, 0xd6,
+    0xb1, 0x68, 0xe0, 0xfb, 0x1e, 0x38, 0x9f, 0x66, 0x3d, 0x46, 0x2d, 0x37,
     0xec, 0xb4, 0xfd, 0xda, 0x13, 0x1b, 0xf5, 0xa6, 0xae, 0xad, 0x22, 0x5a,
-    0xe5, 0xac, 0xae, 0xb4, 0x8e, 0xb4, 0xdb, 0xc1, 0x24, 0x4b, 0x74, 0x5b,
-    0x88, 0x54, 0x3c, 0x60, 0x84, 0xff, 0x71, 0x8f, 0x8f, 0x30, 0xd8, 0xb4,
-    0x62, 0x24, 0x3f, 0x58, 0x9f, 0x8e, 0x5b, 0xd4, 0x3f, 0xad, 0x3f, 0xb6,
-    0xff, 0x07, 0x36, 0xb5, 0x69, 0xea, 0x05, 0x0a, 0xd3, 0xff, 0xcd, 0xba,
+    0xe5, 0xac, 0xae, 0xb4, 0x8e, 0xb4, 0xdc, 0xc1, 0x24, 0x4b, 0x74, 0x5b,
+    0x68, 0x54, 0x3c, 0x60, 0x84, 0xff, 0x6d, 0x8f, 0x8f, 0x30, 0xd8, 0xb4,
+    0x62, 0x24, 0x3f, 0x58, 0x9f, 0x8e, 0x5c, 0xd4, 0x3f, 0xad, 0x3f, 0xb8,
+    0xff, 0x07, 0x38, 0xb5, 0x69, 0xea, 0x05, 0x0a, 0xd3, 0xff, 0xcd, 0xca,
     0xbb, 0xda, 0xfd, 0x83, 0x6d, 0xb6, 0xad, 0x0c, 0x8d, 0x7a, 0x2f, 0xd1,
     0xaf, 0xc3, 0xf1, 0x63, 0x64, 0x20, 0xe8, 0xca, 0x01, 0x09, 0xeb, 0xb4,
-    0x6c, 0x42, 0x51, 0x86, 0x6f, 0xcb, 0xde, 0x84, 0x11, 0xc9, 0xb8, 0xa9,
+    0x70, 0x42, 0x51, 0x86, 0x73, 0xcb, 0xde, 0x84, 0x11, 0xc9, 0xb6, 0xa9,
     0x54, 0xae, 0x3f, 0xc9, 0x06, 0x34, 0xdf, 0x43, 0xea, 0x7c, 0x72, 0x1b,
-    0xb2, 0xd3, 0xf9, 0x87, 0xd4, 0x3f, 0x62, 0xd3, 0xf3, 0x84, 0x9d, 0xcc,
-    0x5a, 0x7d, 0x61, 0x75, 0x9c, 0xb4, 0x74, 0xf4, 0x84, 0xae, 0x7f, 0xdc,
-    0x6e, 0xfe, 0x39, 0x6d, 0x7e, 0xb4, 0xec, 0xc7, 0x96, 0x96, 0x70, 0xf6,
+    0xb2, 0xd3, 0xf9, 0x87, 0xd4, 0x3f, 0x62, 0xd3, 0xf3, 0x84, 0x9d, 0xbc,
+    0x5a, 0x7d, 0x61, 0x75, 0x9c, 0xb4, 0x74, 0xf4, 0x84, 0xae, 0x7f, 0xdb,
+    0x6e, 0xfe, 0x39, 0x6d, 0x7e, 0xb4, 0xec, 0xc7, 0x96, 0x96, 0x6c, 0xf6,
     0xbf, 0x3f, 0x9f, 0xbe, 0xef, 0xc7, 0xa1, 0x5a, 0x78, 0xed, 0x96, 0x2d,
-    0x3e, 0xc7, 0xe2, 0xdb, 0x2d, 0x04, 0x79, 0x3a, 0x21, 0x9e, 0x61, 0xf5,
-    0xd6, 0x93, 0x62, 0x6b, 0x62, 0xf7, 0xa2, 0x8f, 0x3b, 0xef, 0x10, 0xcf,
+    0x3e, 0xc7, 0xe2, 0xdc, 0x2d, 0x04, 0x79, 0x3a, 0x21, 0x9e, 0x61, 0xf5,
+    0xd6, 0x93, 0x62, 0x6b, 0x62, 0xf7, 0xa2, 0x8f, 0x3b, 0xf3, 0x10, 0xcf,
     0x7d, 0x5d, 0x72, 0xd3, 0xcd, 0xf3, 0xe5, 0xa4, 0xa7, 0xce, 0x0d, 0xb6,
     0xda, 0xb4, 0x09, 0xe9, 0x7c, 0x4f, 0x3d, 0x5e, 0x3f, 0x96, 0x80, 0x22,
     0xdf, 0x1d, 0x3f, 0x22, 0x86, 0x56, 0x74, 0x72, 0x5f, 0xe3, 0xdd, 0xfa,
-    0x30, 0x89, 0xdb, 0xeb, 0x7d, 0xaf, 0x8a, 0xd3, 0x9c, 0x34, 0xb4, 0xb6,
-    0xdf, 0x47, 0x92, 0xc5, 0xf3, 0xef, 0xbf, 0xc1, 0xba, 0xd3, 0xba, 0xcf,
+    0x30, 0x89, 0xdc, 0xeb, 0x9d, 0xaf, 0x8a, 0xd3, 0x9c, 0x34, 0xb4, 0xb8,
+    0xe7, 0x47, 0x92, 0xc5, 0xf3, 0xef, 0xbf, 0xc1, 0xba, 0xd3, 0xba, 0xcf,
     0x2d, 0x3f, 0xbd, 0x86, 0x33, 0xc5, 0xa5, 0xa3, 0xa7, 0xee, 0x72, 0x87,
-    0xe3, 0x93, 0xf3, 0x70, 0xac, 0xa1, 0x5a, 0x7f, 0xac, 0x1c, 0x77, 0x33,
-    0xfd, 0x2d, 0x3f, 0xf1, 0x60, 0xec, 0xed, 0x77, 0xe6, 0xfd, 0x69, 0xe0,
-    0x01, 0x9f, 0xad, 0x3f, 0xfd, 0xc6, 0x3d, 0xdb, 0xd9, 0xdb, 0xd5, 0x5d,
+    0xe3, 0x93, 0xf3, 0x6c, 0xac, 0xa1, 0x5a, 0x7f, 0xac, 0x1c, 0x76, 0xf3,
+    0xfd, 0x2d, 0x3f, 0xf1, 0x60, 0xf0, 0xed, 0x77, 0xe6, 0xfd, 0x69, 0xe0,
+    0x01, 0x9f, 0xad, 0x3f, 0xfd, 0xb6, 0x3d, 0xdb, 0xd9, 0xdb, 0xd5, 0x5d,
     0x51, 0x7c, 0x4e, 0xae, 0xd8, 0xa8, 0xbf, 0xe1, 0x91, 0x07, 0x75, 0xa9,
     0xdf, 0xb3, 0xea, 0xd3, 0xfe, 0x7d, 0x2c, 0x10, 0x1a, 0x59, 0xd5, 0xa7,
-    0xea, 0xee, 0xce, 0xcf, 0x2d, 0x3f, 0xef, 0x68, 0xbb, 0xe1, 0xfa, 0x9c,
-    0xb4, 0xea, 0xd9, 0xf5, 0x69, 0xfa, 0xbd, 0xa6, 0xcb, 0x16, 0x86, 0x45,
-    0xb6, 0x16, 0xbf, 0x3e, 0xb4, 0x7e, 0x71, 0x70, 0x96, 0x9f, 0xf9, 0xff,
-    0x8d, 0x61, 0x76, 0x70, 0x8e, 0xb4, 0xff, 0x63, 0xfe, 0x53, 0x9b, 0x05,
-    0x69, 0xfb, 0x2d, 0x33, 0x95, 0xe5, 0xa7, 0xff, 0xde, 0x31, 0xcc, 0xfc,
-    0x87, 0xfe, 0x93, 0x1f, 0x12, 0x9e, 0xe1, 0x9b, 0x79, 0x69, 0xd6, 0x3d,
-    0xbf, 0x5a, 0x2c, 0x46, 0x56, 0x17, 0x5d, 0x5b, 0xe2, 0x49, 0xbd, 0x8b,
+    0xea, 0xef, 0x0e, 0xcf, 0x2d, 0x3f, 0xef, 0x68, 0xbb, 0xe1, 0xfa, 0x9c,
+    0xb4, 0xea, 0xe1, 0xf5, 0x69, 0xfa, 0xbd, 0xa6, 0xcb, 0x16, 0x86, 0x45,
+    0xb6, 0x16, 0xbf, 0x3e, 0xb4, 0x7e, 0x71, 0x6c, 0x96, 0x9f, 0xf9, 0xff,
+    0x8d, 0x61, 0x76, 0x6c, 0x8e, 0xb4, 0xff, 0x63, 0xfd, 0xd3, 0x9b, 0x05,
+    0x69, 0xfb, 0x2d, 0x33, 0x75, 0xe5, 0xa7, 0xff, 0xde, 0x31, 0xcc, 0xfc,
+    0x87, 0xfe, 0x93, 0x1f, 0x12, 0x9e, 0xd9, 0x9c, 0x79, 0x69, 0xd6, 0x3d,
+    0xcf, 0x5a, 0x2c, 0x46, 0x56, 0x17, 0x5d, 0x5b, 0xe2, 0x49, 0xbd, 0x8b,
     0x4f, 0xba, 0x56, 0x51, 0xd5, 0x30, 0x9c, 0xf7, 0xbe, 0x6a, 0x54, 0xc2,
     0x73, 0x7e, 0xca, 0xa0, 0x4e, 0x7f, 0x0d, 0x1b, 0xe6, 0xef, 0xea, 0xa0,
-    0x4e, 0x7f, 0x6b, 0x38, 0x5a, 0xfa, 0xc5, 0x4c, 0x27, 0x36, 0x01, 0x53,
-    0x09, 0xcd, 0x6d, 0xab, 0x98, 0x4e, 0x31, 0x34, 0xdd, 0x8d, 0x08, 0xb8,
+    0x4e, 0x7f, 0x6b, 0x36, 0x5a, 0xfa, 0xc5, 0x4c, 0x27, 0x36, 0x01, 0x53,
+    0x09, 0xcd, 0x6d, 0xab, 0x98, 0x4e, 0x31, 0x34, 0xde, 0x0d, 0x08, 0xb8,
     0xe4, 0x7a, 0x40, 0x7e, 0x83, 0x69, 0x14, 0xbc, 0x99, 0x84, 0xc1, 0x3e,
-    0x79, 0x16, 0xc9, 0xfc, 0x5b, 0x1e, 0x4c, 0x12, 0xbc, 0x7a, 0x1a, 0x14,
+    0x79, 0x17, 0x09, 0xfc, 0x5b, 0x1e, 0x4c, 0x12, 0xbc, 0x7a, 0x1a, 0x14,
     0x5d, 0x46, 0x03, 0xe9, 0x46, 0xd3, 0x87, 0xf3, 0xad, 0x3f, 0xcd, 0xf7,
     0x84, 0x9f, 0x98, 0x75, 0xa7, 0x0e, 0x6b, 0x0f, 0x5f, 0xa3, 0x90, 0xcb,
-    0xe7, 0xb6, 0x1c, 0x81, 0x16, 0xf0, 0xa6, 0x22, 0x2e, 0x8f, 0xf2, 0x1e,
-    0xbe, 0x9c, 0x05, 0x7f, 0x0a, 0xd9, 0xf0, 0x33, 0xa4, 0x6a, 0xd3, 0xf7,
-    0x18, 0xc1, 0xdb, 0xf5, 0xa7, 0xe6, 0xf6, 0x3c, 0x47, 0x5a, 0x7c, 0x38,
-    0x66, 0x3c, 0xb4, 0xff, 0x72, 0x8f, 0xc2, 0x6d, 0x9c, 0xb4, 0xfc, 0x60,
-    0xbb, 0x7a, 0xac, 0x5a, 0x08, 0xfa, 0x7f, 0x38, 0x8b, 0x11, 0xbc, 0x25,
-    0x9e, 0x84, 0x84, 0xff, 0xb2, 0xb8, 0xf1, 0x0f, 0x33, 0x65, 0xa7, 0x85,
-    0xb2, 0xd5, 0xa1, 0x99, 0x3f, 0x00, 0x31, 0xc9, 0xf6, 0x82, 0x84, 0xce,
-    0xfc, 0x9e, 0xa3, 0x0d, 0x13, 0x57, 0xe7, 0xb3, 0xfd, 0xc2, 0x77, 0x29,
-    0xc4, 0x75, 0xa7, 0xfd, 0xa2, 0x1d, 0x9d, 0xe2, 0xd1, 0x2d, 0x18, 0x7e,
+    0xe7, 0xb6, 0x1c, 0x81, 0x16, 0xf0, 0xa6, 0x22, 0x2e, 0x8f, 0xee, 0x1e,
+    0xbe, 0x9c, 0x05, 0x7f, 0x0a, 0xd9, 0xf0, 0x33, 0xa4, 0x6a, 0xd3, 0xf6,
+    0xd8, 0xc1, 0xe3, 0xf5, 0xa7, 0xe6, 0xf6, 0x3c, 0x47, 0x5a, 0x7c, 0x38,
+    0x66, 0x3c, 0xb4, 0xff, 0x6e, 0x8f, 0xb2, 0x6e, 0x1c, 0xb4, 0xfc, 0x60,
+    0xbb, 0x9a, 0xac, 0x5a, 0x08, 0xfa, 0x7f, 0x38, 0x8b, 0x11, 0xbc, 0x25,
+    0x9e, 0x84, 0x84, 0xff, 0xb2, 0xb6, 0xf1, 0x0e, 0xf3, 0x85, 0xa7, 0x85,
+    0xb2, 0xd5, 0xa1, 0x99, 0x3f, 0x00, 0x31, 0xc9, 0xf6, 0x82, 0x84, 0xcf,
+    0x3c, 0x9e, 0xa3, 0x0d, 0x13, 0x57, 0xe7, 0xb3, 0xfd, 0xb2, 0x76, 0xe9,
+    0xc4, 0x75, 0xa7, 0xfd, 0xa2, 0x1e, 0x1d, 0xe2, 0xd1, 0x2d, 0x18, 0x7e,
     0x9e, 0x37, 0x9f, 0x68, 0xec, 0x06, 0x5a, 0x0e, 0x78, 0xfe, 0x21, 0x9f,
-    0xff, 0xc3, 0x9b, 0x66, 0xa8, 0x78, 0x4d, 0xee, 0x7d, 0x47, 0x5a, 0x7e,
-    0x1f, 0x8e, 0x5c, 0x65, 0xa7, 0xff, 0xd4, 0x3e, 0xaa, 0xe0, 0xb7, 0xb5,
-    0x6e, 0x71, 0x69, 0xff, 0x59, 0x43, 0xaf, 0x70, 0x9b, 0x4b, 0x4e, 0xee,
-    0xb4, 0xb8, 0x80, 0xe7, 0xff, 0x75, 0xb8, 0xdb, 0xb3, 0xb7, 0xaa, 0xba,
+    0xff, 0xc3, 0x9c, 0x66, 0xa8, 0x76, 0x4d, 0xed, 0xfd, 0x47, 0x5a, 0x7e,
+    0x1f, 0x8e, 0x5b, 0x65, 0xa7, 0xff, 0xd4, 0x3e, 0xaa, 0xd8, 0xb7, 0xb5,
+    0x6e, 0x6d, 0x69, 0xff, 0x59, 0x43, 0xaf, 0x6c, 0x9b, 0x4b, 0x4e, 0xee,
+    0xb4, 0xb8, 0x80, 0xe7, 0xff, 0x75, 0xb6, 0xdc, 0xb3, 0xb7, 0xaa, 0xba,
     0xa2, 0x03, 0x09, 0xa9, 0x8a, 0x46, 0x61, 0x8c, 0xd1, 0xfa, 0x68, 0x3e,
-    0x8c, 0x7a, 0x31, 0x39, 0x82, 0x8d, 0xb6, 0x77, 0x2b, 0x7e, 0xb4, 0xf3,
-    0x7f, 0xbd, 0x8b, 0x47, 0xe7, 0x87, 0xe1, 0xf9, 0xe1, 0xf7, 0xff, 0x2d,
-    0x3f, 0xff, 0x3b, 0x34, 0x5d, 0xe9, 0x1c, 0xcf, 0x73, 0x2d, 0xf2, 0xd1,
-    0x87, 0xff, 0x44, 0x73, 0xff, 0xfd, 0x4e, 0xc1, 0x79, 0xd9, 0xb3, 0x8c,
-    0x6f, 0xc3, 0x6d, 0xb6, 0xa5, 0x0c, 0x99, 0x77, 0x21, 0x44, 0x24, 0x13,
+    0x8c, 0x7a, 0x31, 0x39, 0x82, 0x8d, 0xb6, 0x76, 0xeb, 0x9e, 0xb4, 0xf3,
+    0x7f, 0xcd, 0x8b, 0x47, 0xe7, 0x87, 0xe1, 0xf9, 0xe1, 0xf7, 0xff, 0x2d,
+    0x3f, 0xff, 0x3b, 0x34, 0x5d, 0xe9, 0x1c, 0xcf, 0x6f, 0x2d, 0xf2, 0xd1,
+    0x87, 0xff, 0x44, 0x73, 0xff, 0xfd, 0x4e, 0xc1, 0x79, 0xd9, 0xc3, 0x8c,
+    0x6f, 0xc3, 0x6d, 0xb6, 0xa5, 0x0c, 0x99, 0x76, 0xe1, 0x44, 0x24, 0x13,
     0xf9, 0xb3, 0xb7, 0xaa, 0xba, 0xa2, 0x09, 0x9f, 0xe6, 0xf6, 0x76, 0xf5,
     0x57, 0x54, 0x5f, 0x33, 0xe2, 0x7e, 0x39, 0xa0, 0x22, 0x02, 0xe7, 0xb3,
-    0xfb, 0xee, 0x6f, 0x70, 0xaf, 0xc5, 0xa7, 0xfd, 0x65, 0x6d, 0xbd, 0x9e,
-    0x1a, 0x7e, 0xb4, 0xff, 0xb0, 0x0c, 0xe6, 0xe6, 0x7f, 0x4b, 0x4e, 0xb6,
-    0xdb, 0x52, 0x9f, 0x73, 0x2f, 0x5d, 0x48, 0x25, 0xfc, 0xff, 0xfe, 0x7b,
-    0xac, 0xff, 0x71, 0xb5, 0xb8, 0xc7, 0xb5, 0xbb, 0x36, 0xf2, 0xd1, 0xe4,
+    0xfb, 0xed, 0xf3, 0x6c, 0xaf, 0xb5, 0xa7, 0xfd, 0x65, 0x71, 0xcd, 0x9e,
+    0x1a, 0x7e, 0xb4, 0xff, 0xb0, 0x0c, 0xe6, 0xde, 0x7f, 0x4b, 0x4e, 0xb6,
+    0xdb, 0x52, 0x9f, 0x6f, 0x2f, 0x5d, 0x48, 0x25, 0xfc, 0xff, 0xfe, 0x7b,
+    0xac, 0xff, 0x91, 0xb5, 0xc8, 0xc7, 0xb5, 0xcb, 0x38, 0xf2, 0xd1, 0xe4,
     0x51, 0x98, 0x6b, 0x16, 0x26, 0xca, 0x74, 0x31, 0x87, 0xf4, 0xff, 0xd6,
-    0x1d, 0xb6, 0xd0, 0xe5, 0x86, 0x39, 0x69, 0xfb, 0x7f, 0x8e, 0xe3, 0x75,
-    0x69, 0xff, 0xdb, 0x0e, 0xeb, 0xf0, 0xbf, 0xfd, 0xba, 0xe5, 0xa1, 0x8f,
+    0x1d, 0xb8, 0xd0, 0xe5, 0x86, 0x39, 0x69, 0xfb, 0x9f, 0x8e, 0xdb, 0x75,
+    0x69, 0xff, 0xdc, 0x0f, 0x2b, 0xec, 0xbf, 0xfd, 0xba, 0xe5, 0xa1, 0x8f,
     0xf8, 0xc3, 0x09, 0xff, 0x7d, 0xa6, 0xc1, 0xf3, 0xe3, 0x6d, 0xd6, 0x9f,
-    0xff, 0xed, 0x59, 0x96, 0x8e, 0x03, 0xa5, 0xf5, 0x80, 0xe3, 0x0d, 0x8b,
-    0x4e, 0xb6, 0xdb, 0x52, 0x9f, 0xc6, 0x7e, 0xc3, 0x9b, 0x79, 0x20, 0x97,
-    0xf3, 0xff, 0xf3, 0xe5, 0xba, 0xfc, 0x20, 0x03, 0x2d, 0xdd, 0xed, 0x65,
-    0x8b, 0x47, 0x11, 0x53, 0xf2, 0x1c, 0x12, 0x6a, 0xb5, 0x19, 0x8c, 0x39,
+    0xff, 0xed, 0x59, 0x96, 0x8e, 0x03, 0xa5, 0xf5, 0x80, 0xdb, 0x0d, 0x8b,
+    0x4e, 0xb6, 0xdb, 0x52, 0x9f, 0xc6, 0x7e, 0xc3, 0x9c, 0x79, 0x20, 0x97,
+    0xf3, 0xff, 0xf3, 0xe5, 0xca, 0xfb, 0x20, 0x03, 0x2d, 0xe5, 0xed, 0x65,
+    0x8b, 0x46, 0xd1, 0x53, 0xf2, 0x1c, 0x12, 0x6a, 0xb5, 0x19, 0x8c, 0x39,
     0x72, 0xe8, 0x08, 0x05, 0x1c, 0xe7, 0x4d, 0x8f, 0x0c, 0x51, 0x22, 0xf4,
-    0x73, 0x33, 0xfe, 0xe1, 0x5a, 0x0e, 0x67, 0xb7, 0xb1, 0x69, 0xf7, 0x33,
-    0xcf, 0x39, 0x69, 0xff, 0x67, 0x9b, 0xba, 0xe3, 0x0f, 0x97, 0x10, 0x44,
-    0xfe, 0x6c, 0xed, 0xea, 0xae, 0xa8, 0x82, 0x02, 0x79, 0x33, 0xf5, 0x73,
-    0xbd, 0x2b, 0x56, 0x9f, 0xb7, 0xe0, 0xb6, 0x85, 0xcb, 0x40, 0x9e, 0xf7,
-    0xc5, 0xb1, 0xc4, 0xcf, 0xff, 0x71, 0x18, 0x56, 0x4f, 0xdc, 0xcf, 0x78,
-    0x8e, 0xb4, 0xfd, 0xe3, 0x33, 0x95, 0xb2, 0xd2, 0x6d, 0x8f, 0x68, 0x4b,
-    0x26, 0x63, 0x56, 0x9f, 0xf6, 0x71, 0x8b, 0xc3, 0x9c, 0x25, 0xa6, 0xab,
-    0x77, 0x1e, 0x87, 0x85, 0xa1, 0xc8, 0xa7, 0x17, 0x79, 0xfe, 0xe3, 0x77,
-    0xc2, 0xd9, 0xb2, 0xd3, 0xff, 0xfc, 0x56, 0x33, 0xb5, 0xde, 0x96, 0xcf,
-    0x3a, 0xba, 0x7c, 0xd9, 0x69, 0xef, 0x73, 0x04, 0x08, 0xa0, 0xe1, 0xbc,
-    0xfc, 0x61, 0xf5, 0x6d, 0x6c, 0xb4, 0x31, 0xf4, 0xf8, 0xe6, 0x78, 0x9e,
-    0x2b, 0x56, 0x9f, 0xff, 0x7d, 0x60, 0xb6, 0x70, 0x2d, 0xec, 0xe7, 0xdd,
-    0x5a, 0x7d, 0x9e, 0xd1, 0x9a, 0x01, 0xfc, 0x18, 0x45, 0x3f, 0x5f, 0x85,
-    0xbc, 0x46, 0xad, 0x3d, 0xbf, 0xe6, 0x58, 0xb4, 0xfe, 0x60, 0x67, 0x8b,
-    0x9f, 0x2d, 0x23, 0xb1, 0xec, 0x00, 0x9a, 0x7f, 0xff, 0xf8, 0xbd, 0x47,
-    0xa1, 0xfd, 0xc5, 0xaf, 0x8f, 0xc2, 0x6f, 0x73, 0xea, 0x3a, 0xd0, 0x04,
-    0xcb, 0x0a, 0x11, 0x14, 0x4f, 0x3f, 0x0f, 0xf8, 0x76, 0xe2, 0xd3, 0xba,
+    0x73, 0x33, 0xfe, 0xd9, 0x5a, 0x0d, 0xe7, 0xb9, 0xb1, 0x69, 0xf6, 0xf3,
+    0xcf, 0x39, 0x69, 0xff, 0x67, 0x9b, 0xba, 0xdb, 0x0f, 0x97, 0x10, 0x44,
+    0xfe, 0x6c, 0xed, 0xea, 0xae, 0xa8, 0x82, 0x02, 0x79, 0x33, 0xf5, 0x6f,
+    0xbd, 0x2b, 0x56, 0x9f, 0xb9, 0xe0, 0xb6, 0x85, 0xcb, 0x40, 0x9e, 0xf7,
+    0xc5, 0xb1, 0xb4, 0xcf, 0xff, 0x71, 0x18, 0x56, 0x4f, 0xdb, 0xcf, 0x78,
+    0x8e, 0xb4, 0xfd, 0xe3, 0x33, 0x75, 0xc2, 0xd2, 0x6e, 0x0f, 0x68, 0x4b,
+    0x26, 0x63, 0x56, 0x9f, 0xf6, 0x6d, 0x8b, 0xc3, 0x9b, 0x25, 0xa6, 0xab,
+    0x79, 0x1e, 0x87, 0x85, 0xa1, 0xc8, 0xa7, 0x17, 0x79, 0xfe, 0xdb, 0x77,
+    0xc2, 0xd9, 0xc2, 0xd3, 0xff, 0xfc, 0x56, 0x33, 0xb5, 0xde, 0x97, 0x0f,
+    0x3a, 0xba, 0x7c, 0xe1, 0x69, 0xef, 0x6f, 0x04, 0x08, 0xa0, 0xd9, 0xbc,
+    0xfc, 0x61, 0xf5, 0x6d, 0x70, 0xb4, 0x31, 0xf4, 0xf8, 0xe6, 0x78, 0x9e,
+    0x2b, 0x56, 0x9f, 0xff, 0x7d, 0x60, 0xb6, 0x6c, 0x2d, 0xec, 0xdf, 0xdd,
+    0x5a, 0x7d, 0x9e, 0xd1, 0x9a, 0x01, 0xfc, 0x18, 0x45, 0x3f, 0x5f, 0x65,
+    0xcc, 0x46, 0xad, 0x3d, 0xcf, 0xde, 0x58, 0xb4, 0xfe, 0x60, 0x67, 0x8b,
+    0x7f, 0x2d, 0x23, 0xb1, 0xec, 0x00, 0x9a, 0x7f, 0xff, 0xf8, 0xbd, 0x47,
+    0xa1, 0xfd, 0xc5, 0xaf, 0x8f, 0xb2, 0x6f, 0x6f, 0xea, 0x3a, 0xd0, 0x04,
+    0xcb, 0x0a, 0x11, 0x14, 0x4f, 0x3f, 0x0f, 0xf8, 0x76, 0xda, 0xd3, 0xba,
     0x66, 0x96, 0x99, 0x9f, 0x3a, 0xd3, 0xfe, 0xa1, 0xbe, 0x76, 0xf5, 0x57,
-    0x54, 0x42, 0x30, 0xc8, 0x80, 0xfc, 0x77, 0xe1, 0xb9, 0xff, 0xcd, 0x9b,
-    0x19, 0xe2, 0x1f, 0x9d, 0x9a, 0x5a, 0x7f, 0xfa, 0xaa, 0xc3, 0x33, 0x94,
+    0x54, 0x42, 0x30, 0xc8, 0x80, 0xfc, 0x77, 0xe1, 0xb9, 0xff, 0xcd, 0x9c,
+    0x19, 0xe2, 0x1f, 0x9d, 0x9a, 0x5a, 0x7f, 0xfa, 0xaa, 0xc3, 0x33, 0x74,
     0x70, 0xdb, 0x6d, 0xab, 0x40, 0x11, 0x3a, 0xf2, 0x54, 0xff, 0x01, 0xb5,
     0xa6, 0xb3, 0x2d, 0x5a, 0x30, 0xf7, 0x68, 0x92, 0x75, 0xb6, 0xda, 0x94,
     0xff, 0x67, 0xf5, 0xdc, 0x7f, 0x80, 0x48, 0x25, 0xfc, 0xd6, 0xda, 0x94,
-    0xeb, 0x6d, 0xb5, 0x29, 0xfa, 0xac, 0x07, 0x1b, 0x49, 0x04, 0xbf, 0x81,
-    0x45, 0xdd, 0xa9, 0x1b, 0xc6, 0xf3, 0xeb, 0x90, 0x0c, 0x3a, 0x41, 0x36,
+    0xeb, 0x6d, 0xb5, 0x29, 0xfa, 0xac, 0x06, 0xdb, 0x49, 0x04, 0xbf, 0x81,
+    0x45, 0xdd, 0xa9, 0x1c, 0xc6, 0xf3, 0xeb, 0x90, 0x0c, 0x3a, 0x41, 0x36,
     0x73, 0xad, 0xb6, 0xd4, 0xa7, 0x69, 0xba, 0x90, 0x4b, 0xf9, 0x7e, 0x48,
     0x81, 0xf2, 0xbc, 0xfc, 0x01, 0x67, 0x55, 0xd6, 0x9f, 0x00, 0x9f, 0xb0,
-    0x16, 0x9e, 0xc1, 0xcd, 0xfa, 0xd2, 0x33, 0xa7, 0x96, 0x72, 0x89, 0xff,
-    0xb9, 0x4e, 0x1a, 0x35, 0xe1, 0x27, 0x96, 0x9f, 0xb9, 0x8f, 0xf3, 0xda,
-    0x5a, 0x2e, 0x7e, 0x5e, 0x44, 0x8e, 0xa6, 0x67, 0xc7, 0x61, 0x84, 0xdc,
-    0x32, 0x72, 0x39, 0x1a, 0xe4, 0xed, 0xb7, 0xd9, 0xbe, 0xeb, 0x4f, 0xfe,
-    0xcd, 0xfd, 0x0f, 0xf9, 0x76, 0xe3, 0x1d, 0x69, 0xfa, 0xe5, 0xae, 0x13,
-    0xc9, 0x4f, 0xe1, 0xcd, 0x9d, 0xa2, 0x35, 0x69, 0xea, 0xae, 0x9a, 0xb4,
-    0x6e, 0x3d, 0x4b, 0x0c, 0xe7, 0x9a, 0x8e, 0xfd, 0x29, 0xfb, 0x98, 0x73,
-    0xd3, 0x96, 0x9d, 0x6d, 0xb6, 0xa5, 0x3f, 0x87, 0xdc, 0x26, 0x1b, 0x12,
+    0x16, 0x9e, 0xc1, 0xce, 0x7a, 0xd2, 0x33, 0xa7, 0x96, 0x72, 0x89, 0xff,
+    0xb7, 0x4e, 0x1a, 0x35, 0xe1, 0x27, 0x96, 0x9f, 0xb7, 0x8f, 0xf3, 0xda,
+    0x5a, 0x2e, 0x7e, 0x5e, 0x44, 0x8e, 0xa6, 0x67, 0xb7, 0x61, 0x84, 0xdc,
+    0x32, 0x72, 0x39, 0x1a, 0xe4, 0xee, 0x39, 0xd9, 0xce, 0xeb, 0x4f, 0xfe,
+    0xce, 0x7d, 0x0f, 0xf9, 0x76, 0xdb, 0x1d, 0x69, 0xfa, 0xe5, 0xad, 0x93,
+    0xc9, 0x4f, 0xe1, 0xce, 0x1d, 0xa2, 0x35, 0x69, 0xea, 0xae, 0x9a, 0xb4,
+    0x72, 0x3d, 0x4b, 0x0c, 0xe7, 0x9a, 0x8e, 0xfd, 0x29, 0xfb, 0x78, 0x73,
+    0xd3, 0x96, 0x9d, 0x6d, 0xb6, 0xa5, 0x3f, 0x87, 0xdb, 0x26, 0x1b, 0x12,
     0x09, 0x7f, 0x3c, 0x63, 0x7f, 0x8e, 0x44, 0x5e, 0x25, 0xcb, 0x2e, 0x8e,
     0x73, 0xc2, 0xb6, 0x7f, 0x31, 0x9d, 0xe9, 0x7b, 0x4b, 0x43, 0x2a, 0x64,
-    0x71, 0x66, 0xc9, 0x65, 0x08, 0x1e, 0x46, 0x18, 0x25, 0xb3, 0xff, 0xe3,
+    0x71, 0x67, 0x09, 0x65, 0x08, 0x1d, 0xc6, 0x18, 0x25, 0xb3, 0xff, 0xe3,
     0xe5, 0xd8, 0x6b, 0xda, 0x07, 0xd5, 0xd7, 0x2d, 0x3d, 0x97, 0x6d, 0x2d,
     0x3e, 0x6f, 0x69, 0xfe, 0x2d, 0x0c, 0x89, 0xc1, 0x54, 0xf1, 0x04, 0xff,
-    0xf1, 0x3f, 0xc1, 0x00, 0xd5, 0xb8, 0x34, 0x6a, 0xd3, 0xff, 0xfc, 0x0e,
-    0x15, 0xb8, 0xfa, 0x47, 0xaf, 0x6b, 0xe7, 0x66, 0xde, 0x5a, 0x31, 0x17,
+    0xf1, 0x3f, 0xc1, 0x00, 0xd5, 0xb8, 0x34, 0x6a, 0xd3, 0xff, 0xfc, 0x0d,
+    0x95, 0xb8, 0xfa, 0x47, 0xaf, 0x6b, 0xe7, 0x67, 0x1e, 0x5a, 0x31, 0x17,
     0x34, 0x9d, 0x0c, 0xdc, 0xac, 0xd8, 0x44, 0xe8, 0xfd, 0x81, 0x1c, 0xce,
     0x4e, 0xb7, 0x5d, 0xd8, 0xa3, 0x80, 0x7a, 0x33, 0x7e, 0xc6, 0x56, 0x78,
-    0x4c, 0xf2, 0x36, 0xda, 0x34, 0xfe, 0x15, 0x83, 0x19, 0x5e, 0xa5, 0x9f,
+    0x4c, 0xee, 0x36, 0xda, 0x34, 0xfe, 0x15, 0x83, 0x19, 0x5e, 0xa5, 0x9f,
     0xfa, 0x54, 0x67, 0xd0, 0xfe, 0xb6, 0x1c, 0xf3, 0x9f, 0xb0, 0xad, 0x3f,
-    0xfd, 0x82, 0x1f, 0x1a, 0x5b, 0x6b, 0x99, 0xfd, 0x2d, 0x2d, 0x9c, 0x7d,
-    0x80, 0x1c, 0x9b, 0x7d, 0xaf, 0x9d, 0x69, 0xfb, 0x8f, 0x17, 0x08, 0xeb,
-    0x4f, 0xf8, 0x5b, 0xdc, 0xaf, 0xeb, 0x6d, 0x2d, 0x39, 0xef, 0xba, 0xb4,
-    0xff, 0xa8, 0xbb, 0x9b, 0x06, 0xdb, 0x6d, 0x5a, 0x28, 0xf7, 0xf4, 0x3d,
-    0x3f, 0xfc, 0x2c, 0xed, 0xc6, 0x78, 0x87, 0xe7, 0x66, 0x96, 0x8c, 0x4c,
-    0xf2, 0xe5, 0xbc, 0x84, 0xef, 0xe4, 0x33, 0xf9, 0x85, 0xc3, 0x99, 0xfa,
+    0xfd, 0x82, 0x1f, 0x1a, 0x5c, 0x6b, 0x79, 0xfd, 0x2d, 0x2e, 0x1c, 0x7d,
+    0x80, 0x1c, 0x9b, 0x9d, 0xaf, 0x9d, 0x69, 0xfb, 0x6f, 0x16, 0xc8, 0xeb,
+    0x4f, 0xf8, 0x5b, 0xdb, 0xaf, 0xeb, 0x8d, 0x2d, 0x39, 0xef, 0xba, 0xb4,
+    0xff, 0xa8, 0xbb, 0x9c, 0x06, 0xdb, 0x6d, 0x5a, 0x28, 0xf7, 0xf4, 0x3d,
+    0x3f, 0xfc, 0x2c, 0xee, 0x46, 0x78, 0x87, 0xe7, 0x66, 0x96, 0x8c, 0x4c,
+    0xf2, 0xe5, 0xbb, 0x84, 0xef, 0xe4, 0x33, 0xf9, 0x85, 0xc3, 0x99, 0xfa,
     0xd3, 0xf9, 0xd9, 0xdd, 0x0b, 0x58, 0xb4, 0xff, 0xf6, 0x79, 0x86, 0xf9,
-    0xee, 0x17, 0x7f, 0xba, 0xd3, 0xf9, 0x8f, 0xad, 0x50, 0xd8, 0xb4, 0xfa,
-    0xb5, 0x83, 0xe5, 0xa7, 0x7a, 0xb7, 0xeb, 0x4f, 0xef, 0x70, 0x83, 0xfd,
-    0x12, 0xd0, 0x04, 0x7b, 0x71, 0x38, 0x4c, 0xbc, 0x4b, 0xf0, 0xfc, 0xfe,
-    0xfb, 0xf6, 0xe1, 0x53, 0x96, 0x9f, 0xeb, 0xe7, 0x99, 0xf9, 0x0f, 0xeb,
-    0x4f, 0xff, 0xdf, 0x37, 0xb5, 0x83, 0xbb, 0xac, 0x40, 0x1a, 0x72, 0xd0,
-    0x48, 0x93, 0x13, 0x99, 0xff, 0xf8, 0x73, 0x5f, 0x6e, 0xce, 0x50, 0xe3,
-    0xce, 0x6d, 0x96, 0x9f, 0xf6, 0x6b, 0xec, 0xed, 0xea, 0xae, 0xa8, 0x81,
-    0xa7, 0xbd, 0xca, 0x76, 0xe4, 0x53, 0x0a, 0xe4, 0x32, 0x60, 0x59, 0x0c,
-    0x79, 0xff, 0x57, 0x70, 0x7f, 0xd7, 0xb5, 0xb2, 0xd3, 0xff, 0xff, 0xf6,
-    0x7b, 0x94, 0x26, 0xee, 0xd7, 0x6a, 0xbc, 0xff, 0x01, 0xbb, 0x0f, 0x43,
-    0xb3, 0x97, 0x10, 0x5c, 0xff, 0xaa, 0x8d, 0xbd, 0x03, 0x71, 0xb6, 0xae,
-    0x20, 0xb9, 0xff, 0xb8, 0x5c, 0x26, 0x1f, 0x6e, 0x36, 0xd5, 0xc4, 0x17,
-    0x3f, 0x98, 0x87, 0xdb, 0x8d, 0xb5, 0x71, 0x05, 0xcf, 0xc7, 0xc0, 0x6e,
-    0x36, 0xd5, 0xc4, 0x17, 0x3f, 0xff, 0xd4, 0x22, 0x47, 0xdd, 0xab, 0xf0,
-    0xba, 0x46, 0xd9, 0x96, 0xae, 0x20, 0xb9, 0xb6, 0xdc, 0x04, 0xe5, 0xee,
-    0x9f, 0xd5, 0x3a, 0x42, 0x13, 0xe8, 0x65, 0x59, 0x1c, 0x3f, 0x19, 0x46,
-    0x33, 0xf8, 0x98, 0xdd, 0x7b, 0x5b, 0x2d, 0x3d, 0x5d, 0xfe, 0x96, 0x9f,
-    0xfb, 0x85, 0xc2, 0x61, 0xf6, 0xe3, 0x6d, 0x5c, 0x41, 0x73, 0xfc, 0xf6,
-    0xae, 0x56, 0x6e, 0x36, 0xd5, 0xc4, 0x17, 0x3e, 0xf6, 0x98, 0xfb, 0xae,
-    0x89, 0xf3, 0x14, 0xe7, 0xff, 0x6e, 0xbf, 0x0a, 0xed, 0xed, 0x6e, 0x36,
-    0xd5, 0xc4, 0x17, 0x3f, 0xff, 0xf0, 0x89, 0x1f, 0x77, 0xd9, 0xbb, 0x57,
-    0xe1, 0x74, 0x8d, 0xb3, 0x2d, 0x5c, 0x41, 0x71, 0x89, 0x92, 0x6c, 0x87,
-    0x4b, 0xb3, 0xfd, 0xc2, 0xe9, 0x1b, 0x66, 0x5a, 0xb8, 0x82, 0xe7, 0xff,
-    0xaa, 0xb6, 0x76, 0xb8, 0x40, 0x03, 0x75, 0x92, 0x9f, 0xf6, 0x39, 0xdf,
-    0x6b, 0xa3, 0x66, 0xf2, 0xe2, 0x0b, 0x8b, 0xa3, 0x97, 0xa8, 0xfa, 0x4e,
-    0x9f, 0xf5, 0xcb, 0xbe, 0xaf, 0xf5, 0xba, 0xd5, 0xc4, 0x17, 0x3f, 0x70,
-    0xb9, 0xc2, 0xfd, 0x50, 0x05, 0xcf, 0xb3, 0xfd, 0xc6, 0xda, 0xb8, 0x82,
-    0xe6, 0xcf, 0x5c, 0xfc, 0xf8, 0x75, 0x1b, 0x23, 0xbf, 0x50, 0xc2, 0x9f,
-    0x8f, 0x80, 0xdc, 0x6d, 0xab, 0x88, 0x2e, 0x7f, 0xd7, 0xe1, 0x74, 0x8d,
-    0xb3, 0x2d, 0x5c, 0x41, 0x73, 0x66, 0xea, 0x44, 0x5e, 0x8f, 0xa7, 0xf7,
-    0xc4, 0x7a, 0x1d, 0x9c, 0xb8, 0x82, 0xe7, 0xfd, 0x84, 0xff, 0x07, 0x01,
+    0xed, 0x97, 0x7f, 0xba, 0xd3, 0xf9, 0x8f, 0xad, 0x50, 0xd8, 0xb4, 0xfa,
+    0xb5, 0x83, 0xe5, 0xa7, 0x7a, 0xb9, 0xeb, 0x4f, 0xef, 0x6c, 0x83, 0xfd,
+    0x12, 0xd0, 0x04, 0x7b, 0x6d, 0x38, 0x4c, 0xbc, 0x4b, 0xf0, 0xfc, 0xfe,
+    0xfb, 0xf6, 0xd9, 0x53, 0x96, 0x9f, 0xeb, 0xe7, 0x99, 0xf9, 0x0f, 0xeb,
+    0x4f, 0xff, 0xdf, 0x37, 0xb5, 0x83, 0xcb, 0xac, 0x40, 0x1a, 0x72, 0xd0,
+    0x48, 0x93, 0x13, 0x99, 0xff, 0xf8, 0x73, 0x5f, 0x72, 0xcd, 0xd0, 0xe3,
+    0xce, 0x6e, 0x16, 0x9f, 0xf6, 0x6b, 0xec, 0xed, 0xea, 0xae, 0xa8, 0x81,
+    0xa7, 0xbd, 0xba, 0x77, 0x24, 0x53, 0x0a, 0xe4, 0x32, 0x60, 0x59, 0x0c,
+    0x79, 0xff, 0x57, 0x70, 0x7f, 0xd7, 0xb5, 0xc2, 0xd3, 0xff, 0xff, 0xf6,
+    0x7b, 0x74, 0x26, 0xf2, 0xd7, 0x6a, 0xbc, 0xff, 0x01, 0xcb, 0x0f, 0x43,
+    0xc3, 0x97, 0x10, 0x5c, 0xff, 0xaa, 0x8d, 0xbd, 0x03, 0x91, 0xb6, 0xae,
+    0x20, 0xb9, 0xff, 0xb6, 0x5b, 0x26, 0x1f, 0x72, 0x36, 0xd5, 0xc4, 0x17,
+    0x3f, 0x98, 0x87, 0xdc, 0x8d, 0xb5, 0x71, 0x05, 0xcf, 0xc7, 0xc0, 0x72,
+    0x36, 0xd5, 0xc4, 0x17, 0x3f, 0xff, 0xd4, 0x22, 0x47, 0xe5, 0xab, 0xec,
+    0xba, 0x46, 0xd9, 0x96, 0xae, 0x20, 0xb9, 0xb8, 0xe4, 0x04, 0xe5, 0xee,
+    0x9f, 0xd5, 0x3a, 0x42, 0x13, 0xe8, 0x65, 0x59, 0x1b, 0x3f, 0x19, 0x46,
+    0x33, 0xf8, 0x98, 0xdd, 0x7b, 0x5c, 0x2d, 0x3d, 0x5d, 0xfe, 0x96, 0x9f,
+    0xfb, 0x65, 0xb2, 0x61, 0xf7, 0x23, 0x6d, 0x5c, 0x41, 0x73, 0xfc, 0xf6,
+    0xae, 0x56, 0x72, 0x36, 0xd5, 0xc4, 0x17, 0x3e, 0xf6, 0x98, 0xfc, 0xae,
+    0x89, 0xf3, 0x14, 0xe7, 0xff, 0x72, 0xbe, 0xca, 0xed, 0xed, 0x72, 0x36,
+    0xd5, 0xc4, 0x17, 0x3f, 0xff, 0xf0, 0x89, 0x1f, 0x97, 0xd9, 0xcb, 0x57,
+    0xd9, 0x74, 0x8d, 0xb3, 0x2d, 0x5c, 0x41, 0x71, 0x89, 0x92, 0x70, 0x87,
+    0x4b, 0xb3, 0xfd, 0xb2, 0xe9, 0x1b, 0x66, 0x5a, 0xb8, 0x82, 0xe7, 0xff,
+    0xaa, 0xb8, 0x76, 0xb6, 0x40, 0x03, 0x75, 0x92, 0x9f, 0xf6, 0x39, 0xdf,
+    0x6b, 0xa3, 0x67, 0x32, 0xe2, 0x0b, 0x8b, 0xa3, 0x97, 0xa8, 0xfa, 0x4e,
+    0x9f, 0xf5, 0xcb, 0xbe, 0xaf, 0xf5, 0xca, 0xd5, 0xc4, 0x17, 0x3f, 0x6c,
+    0xb7, 0xb2, 0xfd, 0x50, 0x05, 0xcf, 0xb3, 0xfe, 0x46, 0xda, 0xb8, 0x82,
+    0xe6, 0xcf, 0x5c, 0xfc, 0xf6, 0x75, 0x1c, 0x23, 0xbf, 0x50, 0xc2, 0x9f,
+    0x8f, 0x80, 0xe4, 0x6d, 0xab, 0x88, 0x2e, 0x7f, 0xd7, 0xd9, 0x74, 0x8d,
+    0xb3, 0x2d, 0x5c, 0x41, 0x73, 0x67, 0x2a, 0x44, 0x5e, 0x8f, 0xa7, 0xf7,
+    0xc4, 0x7a, 0x1e, 0x1c, 0xb8, 0x82, 0xe7, 0xfd, 0x84, 0xff, 0x07, 0x01,
     0x4e, 0x5c, 0x41, 0x67, 0x3c, 0x08, 0x02, 0xf0, 0x07, 0x4d, 0xff, 0x34,
     0x18, 0xf9, 0x35, 0x18, 0xaf, 0xa3, 0x1f, 0xfa, 0x16, 0x96, 0xb7, 0xcd,
     0xfb, 0x2a, 0x20, 0xb0, 0xa2, 0x36, 0x6a, 0xba, 0xd3, 0x7f, 0xaf, 0xcf,
-    0x2b, 0xc6, 0xb3, 0xfe, 0xc7, 0x72, 0x9c, 0xfa, 0xfb, 0xf5, 0x8b, 0x4e,
-    0x3b, 0x39, 0x69, 0xf6, 0x03, 0x9f, 0x5a, 0xb4, 0x51, 0xe2, 0x08, 0xdc,
-    0xe0, 0x57, 0x96, 0x8e, 0x9b, 0xba, 0x20, 0x9c, 0x5b, 0xd8, 0xb4, 0xea,
-    0xef, 0xeb, 0x4b, 0x66, 0x37, 0x3e, 0x1d, 0x9f, 0xb2, 0xca, 0xe3, 0x6f,
+    0x2b, 0xc6, 0xb3, 0xfe, 0xc7, 0x6e, 0x9c, 0xfa, 0xfb, 0xf5, 0x8b, 0x4e,
+    0x3b, 0x39, 0x69, 0xf6, 0x03, 0x7f, 0x5a, 0xb4, 0x51, 0xe2, 0x08, 0xdc,
+    0xe0, 0x57, 0x96, 0x8e, 0x9b, 0xba, 0x20, 0x9c, 0x5c, 0xd8, 0xb4, 0xea,
+    0xef, 0xeb, 0x4b, 0x86, 0x37, 0x3e, 0x1d, 0x9f, 0xb2, 0xca, 0xdb, 0x73,
     0xd6, 0x82, 0x45, 0x85, 0x2c, 0x89, 0x3c, 0xea, 0xed, 0x8b, 0x4d, 0xfb,
     0x2d, 0x3c, 0x4c, 0x7c, 0x5a, 0x16, 0x9f, 0xa8, 0xdf, 0x37, 0x7f, 0x5a,
     0x00, 0x6e, 0x04, 0x2a, 0x7f, 0xff, 0x31, 0x00, 0x5b, 0xeb, 0xfb, 0x2e,
     0x06, 0xb9, 0x87, 0x5a, 0x6f, 0xd9, 0x69, 0x98, 0xd5, 0xa7, 0xfb, 0x3d,
-    0xa6, 0x39, 0x98, 0xf2, 0xd3, 0xfb, 0x59, 0xc2, 0xd7, 0xd6, 0x2d, 0x35,
+    0xa6, 0x39, 0x98, 0xf2, 0xd3, 0xfb, 0x59, 0xb2, 0xd7, 0xd6, 0x2d, 0x35,
     0xb6, 0xa5, 0x3f, 0xc3, 0x8f, 0xe9, 0xda, 0x20, 0x2d, 0x17, 0x4f, 0xc8,
-    0x87, 0x3a, 0x2c, 0x75, 0x7e, 0x10, 0x7e, 0xc0, 0xf9, 0x0b, 0x08, 0xb6,
+    0x87, 0x3a, 0x2c, 0x75, 0x7d, 0x90, 0x7e, 0xc0, 0xf9, 0x0b, 0x08, 0xb6,
     0x8e, 0x6d, 0x34, 0x30, 0x62, 0x75, 0xb6, 0xda, 0x94, 0x8e, 0x90, 0x4b,
-    0xf9, 0xf5, 0x3d, 0x55, 0xd4, 0x82, 0x8d, 0xd3, 0x21, 0x7d, 0x3f, 0xb6,
-    0xf3, 0x67, 0x32, 0xc5, 0xa1, 0x9b, 0x41, 0xb7, 0x14, 0xe4, 0x6c, 0x06,
-    0xa1, 0xec, 0x60, 0x51, 0x94, 0x75, 0x2b, 0x91, 0xe4, 0x55, 0x27, 0x18,
+    0xf9, 0xf5, 0x3d, 0x55, 0xd4, 0x82, 0x8d, 0xd3, 0x21, 0x7d, 0x3f, 0xb8,
+    0xf3, 0x66, 0xf2, 0xc5, 0xa1, 0x9b, 0x41, 0xb7, 0x14, 0xe4, 0x6c, 0x06,
+    0xa1, 0xf0, 0x60, 0x51, 0x94, 0x75, 0x2b, 0x71, 0xe4, 0x55, 0x27, 0x18,
     0x4c, 0x35, 0x1b, 0xaf, 0xd2, 0xbf, 0x1f, 0xa5, 0xcd, 0x96, 0xad, 0x33,
     0x79, 0x68, 0xb0, 0xd5, 0xdc, 0x5a, 0x47, 0x5a, 0x30, 0xd9, 0x78, 0x8a,
-    0x7b, 0x0f, 0x9e, 0x5a, 0x7f, 0xff, 0xeb, 0xd3, 0xdf, 0x3b, 0xeb, 0xee,
-    0xe1, 0x7b, 0xcd, 0xb3, 0x58, 0xda, 0x5a, 0x7f, 0xee, 0x7d, 0x67, 0xc1,
-    0xe1, 0x5e, 0xba, 0xb4, 0xf5, 0x58, 0x63, 0x96, 0x86, 0x4c, 0x8b, 0x62,
+    0x7b, 0x0f, 0x9e, 0x5a, 0x7f, 0xff, 0xeb, 0xd3, 0xdf, 0x3b, 0xeb, 0xf2,
+    0xd9, 0x7b, 0xcd, 0xc3, 0x58, 0xda, 0x5a, 0x7f, 0xed, 0xfd, 0x67, 0xc1,
+    0xd9, 0x5e, 0xba, 0xb4, 0xf5, 0x58, 0x63, 0x96, 0x86, 0x4c, 0x8b, 0x82,
     0x0e, 0x90, 0x79, 0xe1, 0xf5, 0x1e, 0x7e, 0x1f, 0x80, 0x7a, 0x72, 0xd3,
-    0xff, 0x9c, 0x2d, 0xb8, 0xe2, 0xc6, 0xf8, 0xac, 0x5a, 0x7d, 0x47, 0xce,
-    0x1d, 0x68, 0xd8, 0xfc, 0x7c, 0x97, 0x3b, 0xdd, 0x65, 0xa7, 0xf6, 0xbc,
+    0xff, 0x9c, 0x2d, 0xc8, 0xe2, 0xc6, 0xf8, 0xac, 0x5a, 0x7d, 0x47, 0xcd,
+    0x9d, 0x68, 0xe0, 0xfc, 0x7c, 0x97, 0x3b, 0xdd, 0x65, 0xa7, 0xf6, 0xbc,
     0xff, 0x07, 0x00, 0xb4, 0x32, 0x61, 0xe5, 0x09, 0xe3, 0x91, 0xd0, 0xdc,
-    0xea, 0xdc, 0x62, 0xd3, 0xff, 0xc2, 0xed, 0xd8, 0x02, 0xd6, 0xde, 0xfa,
-    0xb4, 0xb4, 0xfd, 0xd3, 0x75, 0xa2, 0x79, 0x69, 0xf6, 0xee, 0x37, 0x5c,
-    0xb4, 0x6e, 0x46, 0x46, 0x0f, 0x52, 0x95, 0xa5, 0xf3, 0xf5, 0x8f, 0x3b,
-    0x7c, 0xed, 0x8b, 0x4f, 0x1f, 0x2b, 0x8b, 0x4c, 0xeb, 0x16, 0x9f, 0x73,
-    0x09, 0xda, 0x5a, 0x7f, 0xfb, 0x8c, 0x60, 0x7c, 0x35, 0xfe, 0xb4, 0xdf,
+    0xea, 0xe4, 0x62, 0xd3, 0xff, 0xc2, 0xee, 0x58, 0x02, 0xd7, 0x1e, 0xfa,
+    0xb4, 0xb4, 0xfd, 0xd3, 0x75, 0xa2, 0x79, 0x69, 0xf7, 0x2d, 0xb7, 0x5c,
+    0xb4, 0x72, 0x46, 0x46, 0x0f, 0x52, 0x95, 0xa5, 0xf3, 0xf5, 0x8f, 0x3b,
+    0x9c, 0xed, 0x8b, 0x4f, 0x1f, 0x2b, 0x6b, 0x4c, 0xeb, 0x16, 0x9f, 0x6f,
+    0x09, 0xda, 0x5a, 0x7f, 0xfb, 0x6c, 0x60, 0x7c, 0x35, 0xfe, 0xb4, 0xdf,
     0xa5, 0x3f, 0xbf, 0xce, 0xde, 0xaa, 0xeb, 0x88, 0x12, 0x77, 0x8a, 0xc5,
     0xa1, 0xc8, 0xd2, 0x01, 0x37, 0x54, 0x4e, 0x7f, 0x33, 0xeb, 0x96, 0x9e,
-    0xf5, 0x94, 0x2b, 0x4f, 0x6d, 0xaa, 0xf2, 0xd1, 0x61, 0xee, 0x60, 0xcf,
-    0x08, 0x67, 0xf0, 0xd5, 0xba, 0xd3, 0x7e, 0xb4, 0xeb, 0x6d, 0xb5, 0x75,
+    0xf5, 0x94, 0x2b, 0x4f, 0x71, 0xaa, 0xf2, 0xd1, 0x61, 0xee, 0x60, 0xce,
+    0xc8, 0x67, 0xf0, 0xd5, 0xba, 0xd3, 0x7e, 0xb4, 0xeb, 0x6d, 0xb5, 0x75,
     0x7d, 0x4e, 0xa1, 0x02, 0x6a, 0xfa, 0x04, 0xd6, 0x4f, 0xb1, 0xdf, 0x31,
     0xab, 0x40, 0x11, 0x7f, 0xfa, 0xff, 0x8e, 0x27, 0xff, 0xd8, 0x24, 0x68,
-    0x75, 0xee, 0x7d, 0x4e, 0x73, 0x2d, 0x16, 0x22, 0x18, 0xd3, 0x19, 0xec,
+    0x75, 0xed, 0xfd, 0x4e, 0x73, 0x2d, 0x16, 0x22, 0x18, 0xd3, 0x19, 0xec,
     0x05, 0x39, 0x69, 0xf5, 0x08, 0x91, 0xd6, 0x9f, 0x11, 0xe8, 0x71, 0x68,
-    0xdf, 0x4a, 0xf6, 0x58, 0xd9, 0xc4, 0x19, 0x0e, 0x92, 0x84, 0xcd, 0x46,
-    0xe9, 0xf9, 0x28, 0x90, 0x6f, 0x12, 0x4f, 0x8b, 0xcd, 0xed, 0x25, 0x3d,
+    0xe7, 0x4a, 0xf6, 0x58, 0xd9, 0xc4, 0x19, 0x0e, 0x92, 0x84, 0xcd, 0x46,
+    0xe9, 0xf9, 0x28, 0x90, 0x73, 0x12, 0x4f, 0x8b, 0xcd, 0xed, 0x25, 0x3d,
     0x76, 0xf6, 0x92, 0x9a, 0xdb, 0x52, 0x87, 0x1e, 0xfe, 0x13, 0x5a, 0x43,
-    0x36, 0x5a, 0x90, 0x4d, 0x7c, 0xff, 0xfb, 0x3c, 0xd7, 0xcc, 0x1f, 0x70,
+    0x36, 0x5a, 0x90, 0x4d, 0x7c, 0xff, 0xfb, 0x3c, 0xd7, 0xcc, 0x1f, 0x6c,
     0x98, 0x6c, 0x5a, 0x3f, 0x3f, 0x7b, 0x49, 0xa7, 0xff, 0xf3, 0x3f, 0x21,
-    0xff, 0x77, 0xb5, 0x97, 0xe5, 0x0f, 0xb4, 0xb4, 0xf9, 0xdc, 0xf9, 0xba,
-    0xb4, 0xff, 0xd4, 0x7e, 0xb3, 0xf2, 0x7e, 0x3f, 0x69, 0x68, 0xd8, 0xfb,
-    0xf8, 0x51, 0x3e, 0xcf, 0x8e, 0xda, 0x5a, 0x16, 0x9d, 0x98, 0xf7, 0x4d,
+    0xff, 0x97, 0xb5, 0x97, 0xdd, 0x0f, 0xb4, 0xb4, 0xf9, 0xdb, 0xf9, 0xba,
+    0xb4, 0xff, 0xd4, 0x7e, 0xb3, 0xf2, 0x7e, 0x3f, 0x69, 0x68, 0xe0, 0xfb,
+    0xf6, 0x51, 0x3e, 0xcf, 0x8e, 0xda, 0x5a, 0x16, 0x9d, 0x98, 0xf7, 0x4d,
     0x8e, 0x89, 0xe1, 0xe4, 0x43, 0x75, 0x52, 0x73, 0xab, 0xab, 0x4f, 0x9d,
     0x9e, 0xc0, 0x25, 0x22, 0x78, 0xf0, 0x7c, 0x37, 0x3f, 0x7c, 0xf9, 0x17,
-    0x7b, 0x8b, 0x4f, 0x78, 0xdc, 0xe2, 0xd3, 0x7e, 0xcb, 0x4d, 0xfb, 0x2d,
+    0x7b, 0x8b, 0x4f, 0x78, 0xdc, 0xda, 0xd3, 0x7e, 0xcb, 0x4d, 0xfb, 0x2d,
     0x3e, 0xf9, 0xd8, 0x21, 0x23, 0x59, 0xf8, 0xb4, 0x32, 0x23, 0x05, 0x3a,
-    0x7f, 0xfb, 0x06, 0xb6, 0xd1, 0xa6, 0xd6, 0xeb, 0x9f, 0xf5, 0xa7, 0xfe,
-    0xf7, 0x30, 0xe6, 0x37, 0xf9, 0xb7, 0x96, 0x9f, 0xdb, 0x5c, 0xba, 0x63,
-    0x01, 0x69, 0xef, 0xcf, 0x4e, 0xdc, 0x8c, 0xed, 0x2a, 0x79, 0x1a, 0x76,
-    0xf6, 0x6c, 0xb4, 0xf7, 0xfd, 0x2e, 0xad, 0x0c, 0x88, 0xba, 0x4a, 0xb4,
-    0x7e, 0x7b, 0x5f, 0x6d, 0xe5, 0xa7, 0x77, 0x2d, 0x5a, 0x7f, 0xcf, 0x35,
+    0x7f, 0xfb, 0x06, 0xb8, 0xd1, 0xa6, 0xd7, 0x2b, 0x9f, 0xf5, 0xa7, 0xfe,
+    0xf6, 0xf0, 0xe6, 0x37, 0xf9, 0xc7, 0x96, 0x9f, 0xdc, 0x5c, 0xba, 0x63,
+    0x01, 0x69, 0xef, 0xcf, 0x4e, 0xe4, 0x8c, 0xed, 0x2a, 0x79, 0x1a, 0x77,
+    0x36, 0x70, 0xb4, 0xf7, 0xfd, 0x2e, 0xad, 0x0c, 0x88, 0xba, 0x4a, 0xb4,
+    0x7e, 0x7b, 0x5f, 0x71, 0xe5, 0xa7, 0x77, 0x2d, 0x5a, 0x7f, 0xcf, 0x35,
     0x9a, 0xee, 0x5d, 0xac, 0x5a, 0x7f, 0x1f, 0x0f, 0x80, 0x2b, 0x56, 0x8c,
     0x3f, 0x2a, 0x40, 0x9d, 0x6d, 0xb6, 0xa5, 0x3e, 0xc3, 0x47, 0xed, 0x24,
     0x12, 0xfe, 0x7d, 0x81, 0xb6, 0xdb, 0x56, 0x86, 0x3e, 0x1f, 0x1b, 0xcf,
-    0xf8, 0x73, 0x57, 0xe1, 0x5d, 0x85, 0x69, 0xff, 0xfc, 0x6d, 0xb5, 0xd3,
+    0xf8, 0x73, 0x57, 0xd9, 0x5d, 0x85, 0x69, 0xff, 0xfc, 0x6d, 0xb5, 0xd3,
     0x43, 0xac, 0x70, 0xfd, 0x5a, 0xc1, 0x5a, 0x31, 0x13, 0xa4, 0x77, 0x3d,
-    0x6d, 0x0b, 0x96, 0x9f, 0xef, 0x36, 0xdf, 0xd7, 0x38, 0x4b, 0x4b, 0x8b,
-    0x46, 0x1e, 0x4d, 0xce, 0x67, 0x5b, 0x6d, 0xa9, 0x4f, 0xc5, 0x46, 0xf0,
-    0xbc, 0x90, 0x4b, 0xf9, 0xea, 0xe3, 0x01, 0x69, 0xf6, 0x7f, 0x98, 0xf2,
-    0xd2, 0xd6, 0xe4, 0x61, 0x75, 0x03, 0x87, 0x62, 0x41, 0x37, 0xce, 0x02,
-    0x6e, 0x19, 0x19, 0x64, 0x8b, 0x89, 0xe3, 0x8c, 0x72, 0x33, 0xd5, 0xd2,
-    0xe2, 0xd0, 0xcc, 0x89, 0x57, 0x12, 0x64, 0x6b, 0x06, 0xb2, 0x91, 0x47,
-    0x61, 0x7d, 0xc8, 0xde, 0xa8, 0xbf, 0xf2, 0x41, 0x84, 0x76, 0xa1, 0x41,
-    0xe9, 0x50, 0xbb, 0xc5, 0xd3, 0xf3, 0x1f, 0xbd, 0xaf, 0x2d, 0x3f, 0xfc,
-    0x47, 0xd9, 0xda, 0xdd, 0x82, 0x38, 0x5f, 0xad, 0x1c, 0x3f, 0xcf, 0x16,
-    0x48, 0x2f, 0x8c, 0x31, 0xe8, 0x1f, 0x06, 0x07, 0xce, 0x55, 0xbe, 0xd3,
+    0x6d, 0x0b, 0x96, 0x9f, 0xef, 0x37, 0x1f, 0xd6, 0xf6, 0x4b, 0x4b, 0x6b,
+    0x46, 0x1e, 0x4d, 0xce, 0x67, 0x5b, 0x6d, 0xa9, 0x4f, 0xc5, 0x46, 0xec,
+    0xbc, 0x90, 0x4b, 0xf9, 0xea, 0xdb, 0x01, 0x69, 0xf6, 0x7f, 0x98, 0xf2,
+    0xd2, 0xd7, 0x24, 0x61, 0x75, 0x03, 0x67, 0x62, 0x41, 0x37, 0xce, 0x02,
+    0x6e, 0x19, 0x19, 0x64, 0x8b, 0x69, 0xe3, 0x8c, 0x72, 0x33, 0xd5, 0xd2,
+    0xda, 0xd0, 0xcc, 0x89, 0x57, 0x12, 0x64, 0x6b, 0x06, 0xb2, 0x91, 0x47,
+    0x61, 0x7d, 0xb8, 0xde, 0xa8, 0xbf, 0xf2, 0x41, 0x84, 0x76, 0xa1, 0x41,
+    0xe9, 0x50, 0xbc, 0xc5, 0xd3, 0xf3, 0x1f, 0xbd, 0xaf, 0x2d, 0x3f, 0xfc,
+    0x47, 0xe1, 0xda, 0xe5, 0x82, 0x38, 0x5f, 0xad, 0x1b, 0x3f, 0xcf, 0x16,
+    0x48, 0x2f, 0x8c, 0x31, 0xe8, 0x1f, 0x06, 0x07, 0xce, 0x55, 0xce, 0xd3,
     0xf7, 0xcd, 0x34, 0x69, 0xdb, 0xbb, 0x25, 0x33, 0x3a, 0x5c, 0x18, 0x27,
-    0x14, 0x72, 0xd0, 0x33, 0x1b, 0x1f, 0x8d, 0xe7, 0x69, 0xb6, 0x9c, 0xec,
-    0x29, 0xcd, 0x37, 0xa7, 0xf7, 0x3b, 0x3f, 0xdc, 0x79, 0x73, 0x9c, 0xa5,
+    0x14, 0x72, 0xd0, 0x33, 0x1b, 0x1f, 0x8d, 0xe7, 0x69, 0xb8, 0x9c, 0xec,
+    0x29, 0xcd, 0x37, 0xa7, 0xf7, 0x3b, 0x3f, 0xdc, 0x79, 0x73, 0x9b, 0xa5,
     0x52, 0xd5, 0x65, 0x33, 0xfc, 0xe1, 0x6b, 0xe5, 0x09, 0x51, 0xa5, 0x4e,
     0xea, 0xba, 0x9d, 0xf5, 0x6b, 0xa7, 0xf4, 0xa5, 0xe7, 0xf1, 0xae, 0xdb,
-    0x18, 0x46, 0xf4, 0xe0, 0xb9, 0x94, 0xa2, 0xf7, 0xd8, 0xd5, 0x20, 0x30,
+    0x18, 0x47, 0x34, 0xe0, 0xb9, 0x94, 0xa2, 0xf7, 0xd8, 0xd5, 0x20, 0x30,
     0xca, 0x80, 0x76, 0x31, 0xfb, 0xe7, 0xf0, 0x73, 0xb7, 0xaa, 0xba, 0xa2,
     0xe3, 0x9f, 0xc1, 0xce, 0xde, 0xaa, 0xea, 0x8b, 0xae, 0x7f, 0xe0, 0xd3,
     0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x12, 0x8c, 0x34, 0x33, 0x9a, 0x1c, 0x72,
-    0x03, 0xe3, 0x4f, 0xae, 0x7c, 0x51, 0x9f, 0xef, 0xd7, 0xab, 0x19, 0x5a,
+    0x03, 0xe3, 0x4f, 0xae, 0x7c, 0x51, 0x9f, 0xf3, 0xd7, 0xab, 0x19, 0x5a,
     0xba, 0x38, 0xf1, 0xf3, 0xe9, 0xf4, 0xff, 0xe0, 0x9e, 0x9c, 0x1c, 0xed,
     0xea, 0xae, 0xa8, 0x96, 0xa7, 0xdd, 0xbd, 0x55, 0xd5, 0x11, 0xbc, 0xff,
     0xa9, 0xc1, 0xce, 0xde, 0xaa, 0xea, 0x89, 0x7e, 0x41, 0xc3, 0xfc, 0x39,
     0x9c, 0xfe, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x55, 0x73, 0x85, 0xf6, 0xd5,
-    0xa7, 0xf0, 0x2b, 0x8f, 0x0b, 0x01, 0x69, 0xfe, 0x37, 0x07, 0x36, 0xf5,
-    0x1d, 0x69, 0x80, 0x4b, 0x4f, 0xd9, 0xdb, 0xd5, 0x5d, 0x51, 0x20, 0x46,
-    0xe3, 0xcd, 0x08, 0xac, 0xfb, 0x54, 0x35, 0xb2, 0xd0, 0xe3, 0xca, 0xd8,
-    0x92, 0x7f, 0xb3, 0xb6, 0x1d, 0x85, 0xda, 0x5a, 0x7f, 0xf6, 0x3d, 0xca,
-    0x3f, 0xb8, 0xde, 0xf8, 0xeb, 0x40, 0x13, 0xc6, 0xc1, 0xf3, 0x4c, 0xaa,
-    0x19, 0xe2, 0x46, 0x61, 0xc4, 0xff, 0xc3, 0x96, 0x07, 0x79, 0x84, 0x73,
+    0xa7, 0xf0, 0x2b, 0x6f, 0x0b, 0x01, 0x69, 0xfe, 0x37, 0x07, 0x38, 0xf5,
+    0x1d, 0x69, 0x80, 0x4b, 0x4f, 0xd9, 0xdb, 0xd5, 0x5d, 0x51, 0x20, 0x47,
+    0x23, 0xcd, 0x08, 0xac, 0xfb, 0x54, 0x35, 0xc2, 0xd0, 0xe3, 0xca, 0xe0,
+    0x92, 0x7f, 0xb3, 0xb6, 0x1d, 0x85, 0xda, 0x5a, 0x7f, 0xf6, 0x3d, 0xba,
+    0x3f, 0xb6, 0xde, 0xf8, 0xeb, 0x40, 0x13, 0xc6, 0xc1, 0xf3, 0x4c, 0xaa,
+    0x19, 0xe2, 0x46, 0x61, 0xc4, 0xff, 0xc3, 0x96, 0x07, 0x99, 0x84, 0x73,
     0xcb, 0x40, 0x51, 0x41, 0xd5, 0xc9, 0xfc, 0x1c, 0xed, 0xea, 0xae, 0xa8,
     0xb2, 0x27, 0xf0, 0x73, 0xb7, 0xaa, 0xba, 0xa2, 0xd7, 0x9f, 0xc1, 0xce,
     0xde, 0xaa, 0xea, 0x8b, 0x92, 0x7d, 0xdb, 0xd5, 0x5d, 0x51, 0x76, 0x4f,
     0xfa, 0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0xa3, 0xa4, 0x1c, 0x3f, 0xc3,
     0x99, 0xcf, 0x0b, 0x38, 0xeb, 0x49, 0xcb, 0x4f, 0xbb, 0x7a, 0xab, 0xaa,
-    0x29, 0x59, 0xff, 0xff, 0xee, 0x35, 0x8d, 0xc2, 0x7f, 0x40, 0xe1, 0x06,
-    0xfc, 0x27, 0xb8, 0xd6, 0x62, 0xd3, 0x38, 0x38, 0x8b, 0x27, 0x8c, 0xe3,
-    0x64, 0x78, 0xd4, 0x30, 0xa4, 0x10, 0x26, 0x72, 0x78, 0xc4, 0x62, 0xc5,
-    0xe8, 0x57, 0x43, 0x47, 0x62, 0x72, 0x95, 0x48, 0x72, 0x3f, 0xcf, 0xb4,
+    0x29, 0x59, 0xff, 0xff, 0xed, 0xb5, 0x8d, 0xb2, 0x7f, 0x40, 0xd9, 0x06,
+    0xfb, 0x27, 0xb6, 0xd6, 0x62, 0xd3, 0x38, 0x38, 0x8b, 0x27, 0x8c, 0xe3,
+    0x84, 0x78, 0xd4, 0x30, 0xa4, 0x10, 0x26, 0x72, 0x78, 0xc4, 0x62, 0xc5,
+    0xe8, 0x57, 0x43, 0x47, 0x82, 0x72, 0x95, 0x48, 0x72, 0x3f, 0xcf, 0xb4,
     0x7d, 0xe8, 0x5d, 0x3f, 0x8d, 0x82, 0x7f, 0x9c, 0x1c, 0xed, 0xea, 0xae,
     0xa8, 0x8e, 0x27, 0xf8, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x15, 0xac, 0x83,
     0x48, 0x87, 0xf2, 0x24, 0xff, 0xe0, 0x9e, 0x9c, 0x1c, 0xed, 0xea, 0xae,
-    0xa8, 0x96, 0xe6, 0xcd, 0x96, 0x9f, 0xc0, 0xc3, 0x9d, 0x8b, 0x4b, 0x46,
-    0xc7, 0x8e, 0x42, 0xd3, 0xb8, 0x07, 0x2d, 0x39, 0xc7, 0xba, 0xd3, 0xff,
-    0xf7, 0x2b, 0x6f, 0xc1, 0xca, 0x76, 0x68, 0x7e, 0xa3, 0x16, 0x85, 0x44,
+    0xa8, 0x96, 0xe6, 0xce, 0x16, 0x9f, 0xc0, 0xc3, 0x9d, 0x8b, 0x4b, 0x47,
+    0x07, 0x8e, 0x42, 0xd3, 0xb6, 0x07, 0x2d, 0x39, 0xc7, 0xba, 0xd3, 0xff,
+    0xf6, 0xeb, 0x8f, 0xc1, 0xba, 0x76, 0x68, 0x7e, 0xa3, 0x16, 0x85, 0x44,
     0x37, 0x3f, 0x9c, 0x42, 0x0c, 0x20, 0x2d, 0x3f, 0xea, 0x70, 0x73, 0xb7,
-    0xaa, 0xba, 0xa2, 0x60, 0x9f, 0xfe, 0xb6, 0xac, 0xe1, 0x30, 0xe3, 0xc5,
+    0xaa, 0xba, 0xa2, 0x60, 0x9f, 0xfe, 0xb6, 0xac, 0xd9, 0x30, 0xe3, 0xc5,
     0xd2, 0x5a, 0x6b, 0xe2, 0xd3, 0x7d, 0xfa, 0x50, 0x46, 0xb7, 0xa2, 0xb3,
     0xf8, 0x73, 0xde, 0x6e, 0xe9, 0x69, 0x05, 0x95, 0x09, 0xd8, 0x44, 0x01,
-    0xcc, 0x1c, 0xba, 0xee, 0xc2, 0xe7, 0x30, 0xd2, 0x4f, 0x9e, 0xcc, 0x20,
+    0xcc, 0x1c, 0xba, 0xef, 0x02, 0xe7, 0x30, 0xd2, 0x4f, 0x9e, 0xcc, 0x20,
     0x9c, 0x7f, 0xba, 0x94, 0xff, 0xa9, 0xc1, 0xce, 0xde, 0xaa, 0xea, 0x89,
-    0x8e, 0x41, 0x23, 0xe6, 0x38, 0xec, 0xfc, 0xda, 0xdd, 0xec, 0xe2, 0xd3,
-    0x98, 0x6c, 0x5a, 0x7f, 0xff, 0xf9, 0xda, 0xe6, 0x7d, 0x6e, 0xb9, 0x97,
-    0xcd, 0x6e, 0xcd, 0x9d, 0xa2, 0x12, 0x5a, 0x7a, 0xf5, 0x57, 0x54, 0x56,
-    0x33, 0xfe, 0xde, 0xcf, 0x0e, 0x7c, 0xc3, 0xe5, 0xa0, 0xd4, 0xcd, 0x6e,
-    0x5d, 0xb0, 0xdf, 0x61, 0x01, 0xe2, 0xd9, 0xff, 0xcc, 0x3e, 0x37, 0xe6,
-    0x1c, 0xbb, 0x75, 0x69, 0xfe, 0x07, 0x33, 0x5b, 0x39, 0xb8, 0xb4, 0xff,
-    0x36, 0xce, 0x79, 0xd5, 0xdb, 0x16, 0x8c, 0x3f, 0x42, 0x39, 0x9f, 0xf6,
-    0x03, 0xa5, 0xf5, 0x9f, 0xe9, 0xcb, 0x4f, 0xfe, 0xed, 0x8d, 0xed, 0xc6,
+    0x8e, 0x41, 0x23, 0xe6, 0x38, 0xec, 0xfc, 0xda, 0xe5, 0xec, 0xda, 0xd3,
+    0x98, 0x6c, 0x5a, 0x7f, 0xff, 0xf9, 0xda, 0xde, 0x7d, 0x6e, 0xb7, 0x97,
+    0xcd, 0x72, 0xce, 0x1d, 0xa2, 0x12, 0x5a, 0x7a, 0xf5, 0x57, 0x54, 0x56,
+    0x33, 0xfe, 0xe6, 0xcf, 0x0e, 0x7c, 0xc3, 0xe5, 0xa0, 0xd4, 0xcd, 0x6e,
+    0x5d, 0xc0, 0xdf, 0x61, 0x01, 0xe2, 0xd9, 0xff, 0xcc, 0x3e, 0x37, 0xe6,
+    0x1c, 0xbb, 0x75, 0x69, 0xfe, 0x06, 0xf3, 0x5c, 0x39, 0xb6, 0xb4, 0xff,
+    0x37, 0x0e, 0x79, 0xd5, 0xdb, 0x16, 0x8c, 0x3f, 0x42, 0x39, 0x9f, 0xf6,
+    0x03, 0xa5, 0xf5, 0x9f, 0xe9, 0xcb, 0x4f, 0xfe, 0xed, 0x8d, 0xee, 0x46,
     0x5c, 0xc7, 0xd3, 0x34, 0xb4, 0xc0, 0xf2, 0xd1, 0x87, 0xd4, 0x2a, 0x93,
-    0xff, 0xff, 0x0f, 0xd6, 0x65, 0xf7, 0x0f, 0xcf, 0xbb, 0xb3, 0x67, 0x67,
-    0xc3, 0xfe, 0x96, 0x9f, 0xdb, 0xea, 0xc7, 0x9d, 0xbe, 0x76, 0xc5, 0xa7,
-    0xeb, 0x0c, 0xde, 0xa1, 0xfd, 0x69, 0xff, 0x9e, 0xe5, 0x1f, 0xdc, 0x6f,
-    0x7c, 0x75, 0xa1, 0x8f, 0xe3, 0x0c, 0xa7, 0x70, 0x83, 0x8a, 0xd4, 0x2e,
-    0xa5, 0xc8, 0x5e, 0x09, 0x06, 0xa1, 0x57, 0xe2, 0x0d, 0xe7, 0xd3, 0x21,
+    0xff, 0xff, 0x0f, 0xd6, 0x65, 0xf9, 0x0f, 0xcf, 0xbc, 0xb3, 0x87, 0x67,
+    0xc3, 0xfe, 0x96, 0x9f, 0xdc, 0xea, 0xc7, 0x9d, 0xce, 0x76, 0xc5, 0xa7,
+    0xeb, 0x0c, 0xe6, 0xa1, 0xfd, 0x69, 0xff, 0x9e, 0xdd, 0x1f, 0xdb, 0x6f,
+    0x7c, 0x75, 0xa1, 0x8f, 0xe3, 0x0c, 0xa7, 0x6c, 0x83, 0x8a, 0xd4, 0x2e,
+    0xa5, 0xb8, 0x5e, 0x09, 0x06, 0xa1, 0x57, 0xe2, 0x0e, 0x67, 0xd3, 0x21,
     0x75, 0x31, 0x9f, 0xad, 0x37, 0xcf, 0x2d, 0x3e, 0xa1, 0xb3, 0xed, 0x2d,
-    0x1f, 0x9e, 0xc0, 0x8c, 0x68, 0x62, 0x76, 0x8c, 0xd2, 0xd3, 0xdb, 0x58,
+    0x1f, 0x9e, 0xc0, 0x8c, 0x68, 0x62, 0x76, 0x8c, 0xd2, 0xd3, 0xdc, 0x58,
     0xc6, 0xad, 0x00, 0x3c, 0x1f, 0x87, 0x66, 0xf3, 0x2d, 0x3e, 0x3e, 0x0b,
     0x39, 0x69, 0xef, 0xbd, 0x9a, 0x5a, 0x79, 0xbb, 0x42, 0xb4, 0xf6, 0xb4,
-    0xdf, 0xad, 0x0c, 0x88, 0x82, 0x25, 0xe1, 0x15, 0xa3, 0xf3, 0xe6, 0x77,
+    0xdf, 0xad, 0x0c, 0x88, 0x82, 0x25, 0xd9, 0x15, 0xa3, 0xf3, 0xe6, 0x77,
     0xff, 0x39, 0x69, 0xeb, 0x7a, 0xc7, 0x5a, 0x73, 0x0b, 0x96, 0x87, 0x8f,
-    0x94, 0x4a, 0x7c, 0x45, 0x3b, 0x86, 0x1d, 0x69, 0xf9, 0x8f, 0x82, 0x46,
+    0x94, 0x4a, 0x7c, 0x45, 0x3b, 0x66, 0x1d, 0x69, 0xf9, 0x8f, 0x82, 0x46,
     0xad, 0x2d, 0x2d, 0x04, 0x6e, 0xe8, 0xae, 0x6f, 0xd9, 0x29, 0xad, 0xb5,
-    0x28, 0x23, 0x5d, 0x68, 0xb4, 0xfe, 0xa7, 0x0e, 0x7b, 0x84, 0x90, 0x4d,
-    0x0c, 0xf0, 0x2b, 0x6d, 0x2d, 0x39, 0x85, 0xe5, 0xa6, 0x16, 0x5a, 0x4f,
+    0x28, 0x23, 0x5d, 0x68, 0xb4, 0xfe, 0xa7, 0x0e, 0x7b, 0x64, 0x90, 0x4d,
+    0x0c, 0xf0, 0x2b, 0x8d, 0x2d, 0x39, 0x85, 0xe5, 0xa6, 0x16, 0x5a, 0x4f,
     0x2d, 0x0f, 0x1e, 0x07, 0x46, 0xe8, 0x52, 0x7f, 0xe3, 0x48, 0x6f, 0xa6,
-    0x3d, 0x6d, 0xe5, 0xa7, 0x7f, 0x8f, 0xab, 0x41, 0xa7, 0xc7, 0xd4, 0x49,
+    0x3d, 0x71, 0xe5, 0xa7, 0x7f, 0x8f, 0xab, 0x41, 0xa7, 0xc7, 0xd4, 0x49,
     0x1a, 0xb4, 0xdd, 0x35, 0x68, 0x23, 0x50, 0x60, 0x8c, 0xff, 0xef, 0x33,
-    0x8b, 0xdc, 0xcd, 0xbd, 0xf3, 0x2d, 0x2c, 0x5a, 0x08, 0xf6, 0x77, 0x92,
-    0xa1, 0x93, 0xdd, 0xc6, 0x22, 0x84, 0x97, 0x12, 0xe9, 0xea, 0x16, 0x91,
-    0xd6, 0x97, 0x3c, 0x5d, 0x3f, 0x0a, 0x9f, 0xbf, 0x6e, 0x63, 0xfa, 0x5a,
-    0x7e, 0x2f, 0x67, 0x28, 0xd5, 0xa7, 0x5b, 0x6d, 0xa9, 0x4f, 0xfd, 0x97,
-    0x03, 0x58, 0x66, 0x72, 0xb6, 0x48, 0x25, 0xfc, 0xf1, 0x75, 0xf4, 0x0b,
-    0x4f, 0xdc, 0xe3, 0x6d, 0x5b, 0xf5, 0xa5, 0xd5, 0xa6, 0xfb, 0x4b, 0x4e,
-    0xaa, 0x15, 0xa7, 0xc7, 0x2f, 0x7f, 0x4b, 0x45, 0x88, 0xbc, 0xc2, 0x4d,
-    0xf9, 0x8d, 0x08, 0x78, 0x5f, 0xe1, 0x99, 0xfd, 0x72, 0xd0, 0xe3, 0xfa,
-    0x5a, 0x6c, 0xf2, 0xd3, 0xec, 0xb0, 0x73, 0x65, 0xa3, 0x63, 0xee, 0xe1,
+    0x8b, 0xdb, 0xce, 0x3d, 0xf3, 0x2d, 0x2c, 0x5a, 0x08, 0xf6, 0x79, 0x92,
+    0xa1, 0x93, 0xdd, 0xc6, 0x22, 0x84, 0x96, 0xd2, 0xe9, 0xea, 0x16, 0x91,
+    0xd6, 0x96, 0xfc, 0x5d, 0x3f, 0x0a, 0x9f, 0xbf, 0x6d, 0xe3, 0xfa, 0x5a,
+    0x7e, 0x2f, 0x66, 0xe8, 0xd5, 0xa7, 0x5b, 0x6d, 0xa9, 0x4f, 0xfd, 0x97,
+    0x03, 0x58, 0x66, 0x6e, 0xb8, 0x48, 0x25, 0xfc, 0xf1, 0x75, 0xf4, 0x0b,
+    0x4f, 0xdb, 0xdb, 0x71, 0x5c, 0xf5, 0xa5, 0xd5, 0xa6, 0xfb, 0x4b, 0x4e,
+    0xaa, 0x15, 0xa7, 0xc7, 0x2f, 0x7f, 0x4b, 0x45, 0x88, 0xbc, 0xc2, 0x4e,
+    0x79, 0x8d, 0x08, 0x78, 0x5f, 0xe1, 0x99, 0xfd, 0x72, 0xd0, 0xe3, 0xfa,
+    0x5a, 0x6c, 0xf2, 0xd3, 0xec, 0xb0, 0x73, 0x85, 0xa3, 0x83, 0xee, 0xd9,
     0x90, 0x8a, 0xcf, 0xfd, 0xfb, 0x0d, 0xeb, 0xdf, 0x98, 0x2e, 0x5a, 0x7b,
-    0xc3, 0x8e, 0x5a, 0x7f, 0xf1, 0x6b, 0x98, 0x01, 0xc7, 0xe3, 0x9a, 0x5a,
-    0x36, 0x45, 0x6f, 0x51, 0x7c, 0x41, 0x20, 0xb3, 0x23, 0x42, 0xc7, 0x07,
-    0x11, 0x82, 0x13, 0xb9, 0x0a, 0x12, 0x2f, 0xea, 0xc1, 0xdb, 0xb9, 0x28,
+    0xc3, 0x8e, 0x5a, 0x7f, 0xf1, 0x6b, 0x78, 0x01, 0xc7, 0xe3, 0x9a, 0x5a,
+    0x38, 0x45, 0x6f, 0x51, 0x7c, 0x41, 0x20, 0xb3, 0x23, 0x42, 0xc7, 0x07,
+    0x11, 0x82, 0x13, 0xb9, 0x0a, 0x12, 0x2f, 0xea, 0xc1, 0xdb, 0xb7, 0x28,
     0x76, 0x99, 0xbf, 0x28, 0x7c, 0x8b, 0x85, 0x2f, 0x51, 0x88, 0x7a, 0x19,
     0x26, 0x43, 0x72, 0x7d, 0xdb, 0xd5, 0x5d, 0x51, 0x5b, 0xcf, 0xfa, 0x9c,
     0x1c, 0xed, 0xea, 0xae, 0xa8, 0x9c, 0x24, 0x1c, 0x3f, 0xc3, 0x99, 0xcd,
-    0xf7, 0x16, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x5a, 0x33, 0xf3, 0x50, 0xfc,
-    0x0c, 0x5a, 0x62, 0x35, 0x69, 0xff, 0xf5, 0x8d, 0xe6, 0x07, 0x5b, 0x95,
-    0xa1, 0xcf, 0xd6, 0x9f, 0xf3, 0x6d, 0x5b, 0xfe, 0x0d, 0x6f, 0xec, 0x5a,
-    0x7f, 0xcd, 0xdf, 0xf0, 0xb3, 0xbe, 0xf9, 0x68, 0x64, 0x6c, 0x71, 0x53,
-    0x48, 0xd2, 0xc7, 0x93, 0x1d, 0xd4, 0x3b, 0xa7, 0xfb, 0x39, 0xcf, 0x8d,
-    0x3b, 0x69, 0x69, 0x05, 0xf0, 0x4f, 0xef, 0x05, 0xce, 0x67, 0xc8, 0xd2,
-    0x3c, 0x5f, 0x3d, 0x7a, 0xab, 0xaa, 0x2d, 0xa9, 0xfb, 0x8c, 0x76, 0x20,
-    0x2d, 0x1d, 0x3d, 0x8f, 0x16, 0xcf, 0xf3, 0x77, 0x76, 0x9b, 0x94, 0x4b,
-    0x4f, 0xee, 0x15, 0x3b, 0xc5, 0x62, 0xd1, 0x88, 0x8c, 0xb4, 0x8b, 0x78,
-    0xe2, 0x7e, 0x36, 0xba, 0x0f, 0x85, 0x69, 0xf5, 0x6c, 0xe3, 0x34, 0xb4,
+    0xf6, 0xd6, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x5a, 0x33, 0xf3, 0x50, 0xfc,
+    0x0c, 0x5a, 0x62, 0x35, 0x69, 0xff, 0xf5, 0x8d, 0xe6, 0x07, 0x5b, 0x75,
+    0xa1, 0xcf, 0xd6, 0x9f, 0xf3, 0x71, 0x5c, 0xfd, 0x8d, 0x73, 0xec, 0x5a,
+    0x7f, 0xcd, 0xdf, 0xf0, 0xb3, 0xbe, 0xf9, 0x68, 0x64, 0x6c, 0x6d, 0x53,
+    0x48, 0xd2, 0xc7, 0x93, 0x1d, 0xd4, 0x3b, 0xa7, 0xfb, 0x37, 0xbf, 0x8d,
+    0x3b, 0x69, 0x69, 0x05, 0xf0, 0x4f, 0xef, 0x05, 0xce, 0x67, 0xb8, 0xd2,
+    0x3c, 0x5f, 0x3d, 0x7a, 0xab, 0xaa, 0x2d, 0xa9, 0xfb, 0x6c, 0x76, 0x20,
+    0x2d, 0x1d, 0x3d, 0x8f, 0x16, 0xcf, 0xf3, 0x77, 0x96, 0x9b, 0x74, 0x4b,
+    0x4f, 0xed, 0x95, 0x3b, 0xc5, 0x62, 0xd1, 0x88, 0x8c, 0xb4, 0x8b, 0x98,
+    0xe2, 0x7e, 0x36, 0xba, 0x0f, 0x85, 0x69, 0xf5, 0x70, 0xe3, 0x34, 0xb4,
     0x82, 0x69, 0xeb, 0xbf, 0x2e, 0x80, 0xa2, 0xd7, 0xf8, 0x45, 0x4f, 0xe0,
     0xe7, 0x6f, 0x55, 0x75, 0x45, 0xcb, 0x3f, 0x83, 0x9d, 0xbd, 0x55, 0xd5,
     0x17, 0x6c, 0x33, 0x70, 0xf1, 0x61, 0x43, 0xa5, 0x57, 0x82, 0x1f, 0x46,
-    0xce, 0x56, 0x15, 0x26, 0xf7, 0x7f, 0x1c, 0x1f, 0x25, 0x11, 0x0c, 0x7e,
+    0xce, 0x56, 0x15, 0x26, 0xf7, 0x9f, 0x1c, 0x1e, 0xe5, 0x11, 0x0c, 0x7e,
     0x9a, 0x29, 0xf1, 0xf4, 0xfe, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x54, 0xf3,
     0x17, 0x96, 0x9e, 0xbd, 0x55, 0xd5, 0x15, 0xcc, 0xfe, 0x0e, 0x76, 0xf5,
-    0x57, 0x54, 0x59, 0xd1, 0xd3, 0xeb, 0xe1, 0x6c, 0xff, 0xf6, 0x79, 0xb5,
-    0xbb, 0xed, 0x79, 0x9e, 0x78, 0x96, 0x9f, 0xf7, 0x0b, 0xa0, 0xe6, 0x10,
+    0x57, 0x54, 0x59, 0xd1, 0xd3, 0xeb, 0xd9, 0x6c, 0xff, 0xf6, 0x79, 0xb5,
+    0xcb, 0xed, 0x79, 0x9e, 0x78, 0x96, 0x9f, 0xf6, 0xcb, 0xa0, 0xde, 0x10,
     0x9a, 0xb4, 0xfb, 0xb9, 0xe2, 0x3a, 0xd3, 0xd4, 0x2c, 0x05, 0xa7, 0xdd,
-    0xbd, 0x55, 0xd5, 0x12, 0x14, 0xff, 0xb0, 0xf9, 0x5d, 0xb3, 0x76, 0xc7,
-    0x5a, 0x7f, 0x8b, 0x6d, 0x78, 0x58, 0xfa, 0x5a, 0x67, 0x07, 0x11, 0x59,
-    0xc3, 0x3f, 0xd0, 0x67, 0x9f, 0x0e, 0x31, 0xd6, 0x9f, 0xd9, 0xcd, 0x6b,
+    0xbd, 0x55, 0xd5, 0x12, 0x14, 0xff, 0xb0, 0xf9, 0x5d, 0xb3, 0x97, 0x07,
+    0x5a, 0x7f, 0x8b, 0x8d, 0x78, 0x58, 0xfa, 0x5a, 0x67, 0x07, 0x11, 0x59,
+    0xb3, 0x3f, 0xd0, 0x67, 0x9f, 0x0d, 0xb1, 0xd6, 0x9f, 0xd9, 0xbd, 0x6b,
     0x3b, 0x62, 0xd2, 0x08, 0x15, 0x5c, 0x64, 0x20, 0x08, 0x89, 0xe5, 0x3e,
     0x9e, 0x9c, 0x9a, 0xa1, 0x97, 0xa3, 0xcf, 0x13, 0x4f, 0xfe, 0x09, 0xe9,
-    0xc1, 0xce, 0xde, 0xaa, 0xea, 0x89, 0xf2, 0x2e, 0xaf, 0x67, 0x7f, 0x2b,
-    0xdb, 0x90, 0x8e, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x56, 0x53, 0xfe, 0xa7,
+    0xc1, 0xce, 0xde, 0xaa, 0xea, 0x89, 0xf2, 0x2e, 0xaf, 0x67, 0x9f, 0x2b,
+    0xdb, 0x70, 0x8e, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x56, 0x53, 0xfe, 0xa7,
     0x07, 0x3b, 0x7a, 0xab, 0xaa, 0x26, 0xe9, 0x88, 0x38, 0x7f, 0x87, 0x33,
-    0x9f, 0x76, 0xf5, 0x57, 0x54, 0x4a, 0xd3, 0xe1, 0x60, 0x57, 0x16, 0x9f,
+    0x9f, 0x76, 0xf5, 0x57, 0x54, 0x4a, 0xd3, 0xe1, 0x60, 0x56, 0xd6, 0x9f,
     0x04, 0xf4, 0xe0, 0xe1, 0xec, 0x11, 0x9c, 0xff, 0xce, 0x2d, 0x7c, 0x71,
     0x27, 0xff, 0x52, 0xd3, 0xee, 0xde, 0xaa, 0xea, 0x89, 0x72, 0x7f, 0x8a,
     0x9d, 0xe1, 0x63, 0xe9, 0x69, 0xe7, 0xe4, 0x2c, 0xb4, 0xff, 0xfa, 0xbc,
     0x5f, 0x38, 0x49, 0xcd, 0xe1, 0xf9, 0xcb, 0x4f, 0x82, 0x7a, 0x70, 0x59,
-    0x32, 0x4c, 0x46, 0xe1, 0x9d, 0x1b, 0x68, 0x86, 0x7f, 0xe0, 0xd3, 0x83,
-    0x9d, 0xbd, 0x55, 0xd5, 0x11, 0xdc, 0xff, 0x7b, 0x98, 0xe0, 0xd0, 0xe9,
-    0x69, 0xfb, 0x7c, 0x16, 0xbe, 0x0a, 0xd3, 0x7d, 0xc5, 0xa7, 0xe7, 0xc4,
-    0x73, 0x95, 0xe5, 0xa7, 0xf6, 0x5b, 0xa6, 0x1f, 0x69, 0x69, 0xf7, 0x6f,
+    0x32, 0x4c, 0x46, 0xd9, 0x9d, 0x1b, 0x68, 0x86, 0x7f, 0xe0, 0xd3, 0x83,
+    0x9d, 0xbd, 0x55, 0xd5, 0x11, 0xdc, 0xff, 0x7b, 0x78, 0xe0, 0xd0, 0xe9,
+    0x69, 0xfb, 0x9c, 0x16, 0xbe, 0x0a, 0xd3, 0x7d, 0xb5, 0xa7, 0xe7, 0xc4,
+    0x73, 0x75, 0xe5, 0xa7, 0xf6, 0x5b, 0xa6, 0x1f, 0x69, 0x69, 0xf7, 0x6f,
     0x55, 0x75, 0x45, 0x43, 0x33, 0xb1, 0x69, 0xff, 0xe6, 0xa3, 0xe7, 0x47,
-    0x09, 0xf9, 0x76, 0x96, 0x9e, 0xe7, 0xb2, 0xc5, 0xa7, 0xd9, 0x65, 0x0b,
-    0xf5, 0xa7, 0xff, 0xff, 0xf3, 0x3b, 0x77, 0xb8, 0x4d, 0xad, 0xd9, 0xf5,
-    0xd9, 0xfe, 0xec, 0xef, 0x71, 0xff, 0xd5, 0xc5, 0xa3, 0x11, 0xc4, 0x24,
-    0x5a, 0x28, 0x9f, 0xff, 0xef, 0xab, 0x7e, 0x56, 0x72, 0x8b, 0xcd, 0xc2,
-    0xf6, 0xb0, 0x0b, 0x48, 0x2f, 0x8a, 0xab, 0x57, 0xc0, 0xbf, 0x7c, 0x17,
-    0xb0, 0xc3, 0x0c, 0xae, 0x67, 0xd1, 0x5f, 0x46, 0x5d, 0xbc, 0x5d, 0x3e,
+    0x09, 0xf9, 0x76, 0x96, 0x9e, 0xdf, 0xb2, 0xc5, 0xa7, 0xd9, 0x65, 0x0b,
+    0xf5, 0xa7, 0xff, 0xff, 0xf3, 0x3b, 0x97, 0xb6, 0x4d, 0xae, 0x59, 0xf5,
+    0xd9, 0xff, 0x2c, 0xef, 0x71, 0xff, 0xd5, 0xb5, 0xa3, 0x11, 0xc4, 0x24,
+    0x5a, 0x28, 0x9f, 0xff, 0xef, 0xab, 0x9e, 0x56, 0x6e, 0x8b, 0xcd, 0xb2,
+    0xf6, 0xb0, 0x0b, 0x48, 0x2f, 0x8a, 0xab, 0x57, 0xc0, 0xbf, 0x9c, 0x17,
+    0xb0, 0xc3, 0x0c, 0xae, 0x67, 0xd1, 0x5f, 0x46, 0x5d, 0xcc, 0x5d, 0x3e,
     0xed, 0xea, 0xae, 0xa8, 0xaa, 0x27, 0xfd, 0x4e, 0x0e, 0x76, 0xf5, 0x57,
     0x54, 0x4d, 0x72, 0x0e, 0x1f, 0xe1, 0xcc, 0xe7, 0xf0, 0x73, 0xb7, 0xaa,
-    0xba, 0xa2, 0xac, 0x9f, 0xf0, 0x78, 0xdb, 0x79, 0xb9, 0x5b, 0xf5, 0xa7,
+    0xba, 0xa2, 0xac, 0x9f, 0xf0, 0x76, 0xdc, 0x79, 0xb7, 0x5c, 0xf5, 0xa7,
     0xfe, 0x0d, 0x38, 0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x22, 0x4f, 0xbb, 0x7a,
     0xab, 0xaa, 0x2d, 0x29, 0xff, 0x53, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x13,
     0xec, 0x83, 0x87, 0xf8, 0x73, 0x39, 0xff, 0xc1, 0x3d, 0x38, 0x39, 0xdb,
-    0xd5, 0x5d, 0x51, 0x42, 0x4f, 0xb8, 0x56, 0x37, 0x56, 0x9f, 0x76, 0xf5,
-    0x57, 0x54, 0x51, 0xf3, 0xfe, 0x16, 0x79, 0x85, 0x9e, 0xe3, 0x2d, 0x3f,
-    0xff, 0x60, 0x38, 0xc6, 0x07, 0xc3, 0x5f, 0xeb, 0x4d, 0xfa, 0x53, 0xe0,
-    0x9e, 0x9c, 0x16, 0x47, 0x96, 0x13, 0xf8, 0xcf, 0x78, 0xf2, 0x19, 0x92,
-    0xad, 0x64, 0x6c, 0x2e, 0x65, 0xc4, 0xf3, 0x65, 0x76, 0x5e, 0x31, 0xad,
-    0x89, 0xc8, 0xf9, 0xe4, 0x2e, 0x43, 0x2f, 0xf2, 0x7f, 0x46, 0x8f, 0x3f,
+    0xd5, 0x5d, 0x51, 0x42, 0x4f, 0xb6, 0x56, 0x37, 0x56, 0x9f, 0x76, 0xf5,
+    0x57, 0x54, 0x51, 0xf3, 0xfe, 0x16, 0x79, 0x85, 0x9e, 0xdb, 0x2d, 0x3f,
+    0xff, 0x60, 0x36, 0xc6, 0x07, 0xc3, 0x5f, 0xeb, 0x4d, 0xfa, 0x53, 0xe0,
+    0x9e, 0x9c, 0x16, 0x47, 0x96, 0x13, 0xf8, 0xcf, 0x98, 0xf2, 0x19, 0x92,
+    0xad, 0x64, 0x6c, 0x2e, 0x65, 0xc4, 0xf3, 0x65, 0x76, 0x5e, 0x31, 0xae,
+    0x09, 0xc8, 0xf9, 0xe4, 0x2d, 0xc3, 0x2f, 0xf2, 0x7f, 0x46, 0x8f, 0x3f,
     0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x11, 0x14, 0xfd, 0x9d, 0xbd, 0x55, 0xd5,
-    0x11, 0x5c, 0xff, 0x6f, 0xc3, 0x9d, 0xbd, 0x55, 0xd5, 0x15, 0xc4, 0x05,
+    0x11, 0x5c, 0xff, 0x73, 0xc3, 0x9d, 0xbd, 0x55, 0xd5, 0x15, 0xc4, 0x05,
     0x10, 0x14, 0x71, 0x3f, 0xfb, 0xf6, 0x1b, 0x86, 0xbd, 0xf9, 0x82, 0xe5,
-    0xa7, 0x83, 0x63, 0xdb, 0xf5, 0xa4, 0x5b, 0x1f, 0x9f, 0xe9, 0x73, 0xd9,
-    0xca, 0xf2, 0xd3, 0xfb, 0x0b, 0xbf, 0xff, 0xf6, 0x96, 0x93, 0xe2, 0x27,
-    0xab, 0xa2, 0x09, 0xff, 0xfb, 0xda, 0x2e, 0x51, 0x38, 0x87, 0xdf, 0x73,
+    0xa7, 0x83, 0x63, 0xdc, 0xf5, 0xa4, 0x5c, 0x1f, 0x9f, 0xe9, 0x73, 0xd9,
+    0xba, 0xf2, 0xd3, 0xfb, 0x0b, 0xbf, 0xff, 0xf6, 0x96, 0x93, 0xe2, 0x27,
+    0xab, 0xa2, 0x09, 0xff, 0xfb, 0xda, 0x2d, 0xd1, 0x38, 0x87, 0xdf, 0x6f,
     0xb8, 0xb4, 0xfb, 0xb7, 0xaa, 0xba, 0xa2, 0x9e, 0x9d, 0x73, 0x92, 0xd3,
-    0xdb, 0x86, 0x9c, 0xb4, 0xfa, 0xb3, 0x84, 0x75, 0xa5, 0x9a, 0x3c, 0x77,
-    0xe4, 0x53, 0xfc, 0xc3, 0x7c, 0x7f, 0x9e, 0xd2, 0xd1, 0xbe, 0x26, 0xf6,
+    0xdc, 0x86, 0x9c, 0xb4, 0xfa, 0xb3, 0x64, 0x75, 0xa5, 0x9a, 0x3c, 0x77,
+    0xe4, 0x53, 0xfc, 0xc3, 0x7c, 0x7f, 0x9e, 0xd2, 0xd1, 0xce, 0x26, 0xf6,
     0xc5, 0x98, 0xb3, 0x73, 0x31, 0x62, 0x7e, 0x53, 0x3f, 0xfa, 0x9c, 0x10,
     0x0d, 0x5b, 0xad, 0x37, 0xeb, 0x40, 0x51, 0x4c, 0x05, 0xc9, 0xc1, 0x06,
-    0x2d, 0x3f, 0xfe, 0xe3, 0x77, 0x1c, 0x55, 0xec, 0x01, 0x7d, 0x75, 0xa7,
+    0x2d, 0x3f, 0xfe, 0xdb, 0x77, 0x1c, 0x55, 0xec, 0x01, 0x7d, 0x75, 0xa7,
     0xcc, 0xef, 0xfe, 0x72, 0xd3, 0xd7, 0xaa, 0xba, 0xa2, 0xb3, 0x87, 0x8f,
     0x5b, 0xa5, 0x33, 0xf5, 0x87, 0x61, 0x76, 0x96, 0x9c, 0xd6, 0x12, 0xd3,
     0xef, 0x7e, 0x60, 0xb9, 0x69, 0xdf, 0x56, 0x96, 0x9f, 0x63, 0xd6, 0x7d,
-    0xfa, 0xd2, 0x08, 0x13, 0x84, 0xc8, 0x54, 0xf4, 0x8f, 0x85, 0xb4, 0x36,
+    0xfa, 0xd2, 0x08, 0x13, 0x84, 0xc8, 0x54, 0xf4, 0x8f, 0x65, 0xb4, 0x36,
     0x25, 0x26, 0x0e, 0x4f, 0xfc, 0x1a, 0x70, 0x73, 0xb7, 0xaa, 0xba, 0xa2,
     0x45, 0x9f, 0xb3, 0xb7, 0xaa, 0xba, 0xa2, 0xc9, 0x9f, 0xfd, 0xed, 0x19,
     0xa0, 0xe5, 0x84, 0x2e, 0xd2, 0xd0, 0x14, 0x41, 0xe1, 0xc4, 0xfe, 0x0e,
     0x76, 0xf5, 0x57, 0x54, 0x5b, 0x13, 0xb3, 0x58, 0xb4, 0xf5, 0xea, 0xae,
     0xa8, 0xb6, 0xe7, 0xb5, 0x65, 0x75, 0x68, 0xe9, 0xe7, 0xf8, 0xb6, 0x41,
-    0x72, 0x22, 0xf1, 0xa6, 0x76, 0xf9, 0xc0, 0x2d, 0x3a, 0xce, 0x12, 0xd3,
-    0xfe, 0xdf, 0x37, 0xc7, 0xcf, 0xee, 0x60, 0xb7, 0xeb, 0x4f, 0xcc, 0x2e,
-    0x07, 0xc7, 0x5a, 0x7d, 0xdb, 0xd5, 0x5d, 0x51, 0x78, 0x4f, 0x7b, 0x7b,
-    0x38, 0xb4, 0xff, 0xcc, 0x61, 0xf6, 0x77, 0xb0, 0x7d, 0xa5, 0xa7, 0xd8,
+    0x72, 0x22, 0xf1, 0xa6, 0x77, 0x39, 0xb0, 0x2d, 0x3a, 0xcd, 0x92, 0xd3,
+    0xfe, 0xe7, 0x39, 0xc7, 0xcf, 0xed, 0xe0, 0xb7, 0xeb, 0x4f, 0xcc, 0x2e,
+    0x07, 0xc7, 0x5a, 0x7d, 0xdb, 0xd5, 0x5d, 0x51, 0x78, 0x4f, 0x7b, 0x9b,
+    0x36, 0xb4, 0xff, 0xcc, 0x61, 0xf8, 0x77, 0xb0, 0x7d, 0xa5, 0xa7, 0xd8,
     0x3f, 0xbe, 0xb2, 0xd3, 0xe6, 0x33, 0xea, 0xd2, 0xd3, 0xbc, 0xdf, 0xad,
-    0x20, 0xbe, 0x29, 0xe0, 0xef, 0x84, 0x3b, 0xed, 0x1c, 0xb1, 0x3b, 0x0b,
-    0x76, 0x33, 0xe1, 0x27, 0xe8, 0xa2, 0x53, 0xe2, 0x89, 0xc5, 0x9b, 0x2d,
-    0x3d, 0x7a, 0xab, 0xaa, 0x2f, 0x49, 0xff, 0x17, 0xb8, 0x34, 0xef, 0xeb,
+    0x20, 0xbe, 0x29, 0xe0, 0xf3, 0x84, 0x3c, 0xed, 0x1c, 0xb1, 0x3b, 0x0b,
+    0x78, 0x33, 0xd9, 0x27, 0xe8, 0xa2, 0x53, 0xe2, 0x89, 0xc5, 0x9c, 0x2d,
+    0x3d, 0x7a, 0xab, 0xaa, 0x2f, 0x49, 0xff, 0x17, 0xb6, 0x34, 0xef, 0xeb,
     0xf5, 0xa3, 0xa7, 0xd6, 0x72, 0xd9, 0xff, 0x0d, 0x79, 0xcc, 0x6f, 0x8a,
-    0xc5, 0xa7, 0xd8, 0x7e, 0x50, 0x16, 0x90, 0x40, 0x98, 0x66, 0x42, 0x18,
+    0xc5, 0xa7, 0xd8, 0x7d, 0xd0, 0x16, 0x90, 0x40, 0x98, 0x66, 0x42, 0x18,
     0xc2, 0x27, 0xd3, 0xe8, 0x66, 0x5e, 0x2d, 0x90, 0xbe, 0x74, 0x25, 0x72,
     0x54, 0xfd, 0xc8, 0xca, 0x39, 0xfe, 0xb7, 0x1e, 0x1a, 0x5f, 0x91, 0x0c,
-    0x32, 0x7e, 0x8f, 0xa6, 0xd8, 0xec, 0x66, 0xfb, 0x8b, 0x4f, 0x67, 0x2b,
+    0x32, 0x7e, 0x8f, 0xa6, 0xd8, 0xec, 0x66, 0xfb, 0x6b, 0x4f, 0x66, 0xeb,
     0xcb, 0x4f, 0xec, 0x2e, 0xff, 0xff, 0xda, 0x5a, 0x4f, 0x88, 0x9e, 0xae,
-    0x88, 0x27, 0xc5, 0xe6, 0xda, 0xc5, 0xa7, 0xdd, 0xbd, 0x55, 0xd5, 0x11,
-    0x1c, 0xff, 0xf7, 0x3e, 0xd6, 0xb0, 0xe3, 0x8f, 0xf7, 0x36, 0xfb, 0xad,
+    0x88, 0x27, 0xc5, 0xe6, 0xe2, 0xc5, 0xa7, 0xdd, 0xbd, 0x55, 0xd5, 0x11,
+    0x1c, 0xff, 0xf6, 0xfe, 0xd6, 0xb0, 0xe3, 0x8f, 0xf9, 0x37, 0x3b, 0xad,
     0x3f, 0xe7, 0x35, 0x9d, 0xcf, 0x78, 0xbc, 0xb4, 0xff, 0x67, 0x73, 0x59,
-    0x43, 0x62, 0xd3, 0xff, 0xfc, 0xdc, 0xa2, 0xf3, 0x70, 0x73, 0xdc, 0x2e,
-    0x61, 0x5a, 0xb4, 0xd6, 0x1d, 0x29, 0xad, 0xb5, 0x29, 0xff, 0x06, 0xfc,
-    0x27, 0xb8, 0xd6, 0x07, 0xf3, 0x5f, 0x68, 0xbc, 0xf3, 0xf6, 0xf3, 0x2d,
-    0x0e, 0x3f, 0xb2, 0x59, 0x9f, 0xb2, 0x8f, 0x9b, 0x79, 0x69, 0xb7, 0xd6,
-    0xfd, 0x69, 0xfb, 0x7e, 0x39, 0xca, 0xf2, 0xd3, 0xf3, 0x1b, 0x82, 0x46,
-    0xad, 0x3f, 0x67, 0x9f, 0x7d, 0x9c, 0x5a, 0x31, 0x11, 0x42, 0x5f, 0xa2,
+    0x43, 0x62, 0xd3, 0xff, 0xfc, 0xdb, 0xa2, 0xf3, 0x6c, 0x73, 0xdb, 0x2d,
+    0xe1, 0x5a, 0xb4, 0xd6, 0x1d, 0x29, 0xad, 0xb5, 0x29, 0xff, 0x06, 0xfb,
+    0x27, 0xb6, 0xd6, 0x07, 0xf3, 0x5f, 0x68, 0xbc, 0xf3, 0xf6, 0xf3, 0x2d,
+    0x0e, 0x3f, 0xb2, 0x59, 0x9f, 0xb2, 0x8f, 0x9c, 0x79, 0x69, 0xb9, 0xd7,
+    0x3d, 0x69, 0xfb, 0x9e, 0x39, 0xba, 0xf2, 0xd3, 0xf3, 0x1b, 0x82, 0x46,
+    0xad, 0x3f, 0x67, 0x9f, 0x7d, 0x9b, 0x5a, 0x31, 0x11, 0x42, 0x5f, 0xa2,
     0xc9, 0xff, 0xfa, 0xe1, 0xed, 0x09, 0x78, 0x3d, 0x6f, 0x0d, 0x7e, 0xb4,
-    0x3e, 0x0b, 0x89, 0x7b, 0xe3, 0x63, 0x16, 0x61, 0x45, 0xcc, 0xf6, 0x5a,
+    0x3e, 0x0b, 0x89, 0x7c, 0xe3, 0x63, 0x16, 0x61, 0x45, 0xcc, 0xf8, 0x5a,
     0x23, 0xee, 0x9b, 0x54, 0x61, 0x9f, 0x90, 0xe8, 0xb3, 0xd0, 0xaa, 0x7e,
     0x5d, 0x3e, 0xed, 0xea, 0xae, 0xa8, 0x8b, 0xa7, 0xde, 0xfc, 0xc1, 0x72,
     0x52, 0x0e, 0x1e, 0xe5, 0x19, 0xc0, 0x53, 0x4c, 0x74, 0x64, 0xd3, 0xff,
     0x1e, 0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0x99, 0xa6, 0xd3, 0x96, 0x9f,
     0x3e, 0x77, 0xc7, 0x4d, 0xe5, 0xa1, 0xf3, 0x9e, 0x43, 0xe6, 0x8b, 0x4e,
-    0xd9, 0xb4, 0xb4, 0xeb, 0x91, 0xd6, 0x9b, 0x7b, 0x7d, 0x2d, 0x3f, 0xfb,
-    0x8c, 0x7a, 0xf6, 0x6d, 0xf9, 0xcc, 0xe2, 0xd3, 0xf7, 0xb2, 0xcc, 0xfd,
-    0xe5, 0xa7, 0xf6, 0xec, 0x1a, 0xef, 0xfb, 0xcb, 0x4f, 0xff, 0x8f, 0x5c,
-    0xcd, 0xbf, 0xe5, 0xb5, 0xc7, 0xb9, 0xf2, 0xd0, 0x04, 0x48, 0xe1, 0xbc,
-    0xda, 0xc5, 0xa3, 0x7d, 0x26, 0xc4, 0x43, 0xef, 0x26, 0x72, 0x18, 0x14,
-    0x45, 0x3e, 0xbd, 0x70, 0x9e, 0x5a, 0x57, 0x5a, 0x7f, 0xfd, 0xfe, 0xce,
-    0xe7, 0xcd, 0xd0, 0xf0, 0xaf, 0x5d, 0x5a, 0x7f, 0xe7, 0xd2, 0xf1, 0x5c,
-    0x81, 0xec, 0xba, 0xd3, 0xff, 0xfd, 0xf5, 0x0d, 0xc7, 0x01, 0xcf, 0xb7,
-    0x71, 0x9d, 0xd6, 0xb1, 0x68, 0x64, 0xc1, 0xb1, 0x63, 0x88, 0xb3, 0xfd,
-    0xc2, 0xf6, 0x1f, 0x6c, 0x3a, 0xd3, 0xff, 0xe0, 0x10, 0xd9, 0x9a, 0x2a,
-    0xe0, 0x6d, 0xb6, 0xd4, 0xa7, 0xed, 0x9d, 0xaf, 0xfe, 0x79, 0x69, 0xd5,
+    0xe1, 0xb4, 0xb4, 0xeb, 0x91, 0xd6, 0x9b, 0x9b, 0x9d, 0x2d, 0x3f, 0xfb,
+    0x6c, 0x7a, 0xf6, 0x71, 0xf9, 0xcc, 0xda, 0xd3, 0xf7, 0xb2, 0xcc, 0xfd,
+    0xe5, 0xa7, 0xf7, 0x2c, 0x1a, 0xef, 0xfc, 0xcb, 0x4f, 0xff, 0x8f, 0x5b,
+    0xce, 0x3f, 0xdd, 0xb5, 0xb7, 0xb7, 0xf2, 0xd0, 0x04, 0x48, 0xe1, 0xbc,
+    0xda, 0xc5, 0xa3, 0x9d, 0x26, 0xc4, 0x43, 0xef, 0x26, 0x6e, 0x18, 0x14,
+    0x45, 0x3e, 0xbd, 0x6c, 0x9e, 0x5a, 0x57, 0x5a, 0x7f, 0xfd, 0xff, 0x0e,
+    0xdf, 0xcd, 0xd0, 0xec, 0xaf, 0x5d, 0x5a, 0x7f, 0xe7, 0xd2, 0xf1, 0x5c,
+    0x81, 0xec, 0xba, 0xd3, 0xff, 0xfd, 0xf5, 0x0d, 0xc7, 0x01, 0xbf, 0xb9,
+    0x6d, 0x9d, 0xd6, 0xb1, 0x68, 0x64, 0xc1, 0xb1, 0x63, 0x68, 0xb3, 0xfd,
+    0xb2, 0xf6, 0x1f, 0x8c, 0x3a, 0xd3, 0xff, 0xe0, 0x10, 0xd9, 0x9a, 0x2a,
+    0xd8, 0x6d, 0xb6, 0xd4, 0xa7, 0xee, 0x1d, 0xaf, 0xfe, 0x79, 0x69, 0xd5,
     0x57, 0x54, 0x59, 0xf3, 0xf7, 0xf4, 0x2e, 0x23, 0xad, 0x17, 0x3d, 0x3f,
-    0xc9, 0xe7, 0xfc, 0x5c, 0x67, 0x17, 0x9b, 0xda, 0x5a, 0x7f, 0xf8, 0xbb,
-    0xca, 0xdd, 0x8f, 0xc7, 0x35, 0x82, 0xb4, 0xeb, 0x6d, 0xb5, 0x29, 0xff,
-    0x53, 0xb4, 0x39, 0x70, 0x67, 0x52, 0x09, 0x7f, 0x3f, 0xf3, 0x6c, 0xe1,
-    0xfb, 0xfd, 0xd7, 0x3f, 0xeb, 0x4f, 0xf3, 0x70, 0x80, 0x35, 0x4f, 0x2d,
+    0xc9, 0xe7, 0xfc, 0x5b, 0x67, 0x17, 0x9b, 0xda, 0x5a, 0x7f, 0xf8, 0xbb,
+    0xba, 0xe5, 0x8f, 0xc7, 0x35, 0x82, 0xb4, 0xeb, 0x6d, 0xb5, 0x29, 0xff,
+    0x53, 0xb4, 0x39, 0x70, 0x67, 0x52, 0x09, 0x7f, 0x3f, 0xf3, 0x70, 0xe1,
+    0xfb, 0xfe, 0x57, 0x3f, 0xeb, 0x4f, 0xf3, 0x6c, 0x80, 0x35, 0x4f, 0x2d,
     0x3f, 0xc4, 0x20, 0x30, 0x1f, 0x7b, 0x4b, 0x43, 0x95, 0x73, 0x5c, 0xe1,
-    0xe5, 0x7e, 0xc2, 0x14, 0xe4, 0x7c, 0x3c, 0xfd, 0xbb, 0x49, 0x7e, 0x4a,
-    0x7e, 0x6d, 0x3f, 0xdd, 0xab, 0x78, 0x4d, 0xd6, 0x5a, 0x7b, 0xa4, 0xc7,
-    0x5a, 0x7f, 0xfd, 0xe3, 0x7e, 0x6c, 0x07, 0x33, 0x5b, 0x39, 0xb8, 0xb4,
+    0xe5, 0x7e, 0xc2, 0x14, 0xe4, 0x7b, 0x3c, 0xfd, 0xbb, 0x49, 0x7e, 0x4a,
+    0x7e, 0x6d, 0x3f, 0xdd, 0xab, 0x76, 0x4d, 0xd6, 0x5a, 0x7b, 0xa4, 0xc7,
+    0x5a, 0x7f, 0xfd, 0xe3, 0x7e, 0x6c, 0x06, 0xf3, 0x5c, 0x39, 0xb6, 0xb4,
     0x98, 0xe7, 0xee, 0x24, 0x13, 0xff, 0xff, 0xec, 0xff, 0x59, 0x82, 0x69,
-    0x9d, 0x16, 0xce, 0x07, 0x43, 0x96, 0x1f, 0x0e, 0xb4, 0xf9, 0xfd, 0x71,
-    0x8c, 0x5a, 0x31, 0x15, 0x65, 0x08, 0x29, 0xda, 0x6b, 0x16, 0x9d, 0xbd,
-    0x9c, 0x5a, 0x1c, 0x7c, 0x1a, 0x26, 0xf0, 0xe4, 0xee, 0x7d, 0x6a, 0xd3,
-    0xbf, 0xfa, 0xeb, 0x4e, 0xf6, 0x18, 0xb4, 0xfc, 0xdb, 0x6b, 0x77, 0x59,
-    0x69, 0xe2, 0x07, 0xd7, 0x5a, 0x79, 0xbd, 0xa6, 0x5a, 0x00, 0x8c, 0x9d,
-    0x87, 0x88, 0x78, 0xe3, 0x9c, 0x2f, 0xde, 0x22, 0x9f, 0xba, 0x4f, 0xf0,
-    0x74, 0xb4, 0xfb, 0x82, 0xd9, 0xd5, 0x27, 0xfc, 0x5d, 0x76, 0x76, 0xcf,
+    0x9d, 0x16, 0xcd, 0x87, 0x43, 0x96, 0x1f, 0x0e, 0xb4, 0xf9, 0xfd, 0x6d,
+    0x8c, 0x5a, 0x31, 0x15, 0x65, 0x08, 0x29, 0xda, 0x6b, 0x16, 0x9d, 0xcd,
+    0x9b, 0x5a, 0x1c, 0x7c, 0x1a, 0x26, 0xf0, 0xe4, 0xed, 0xfd, 0x6a, 0xd3,
+    0xbf, 0xfa, 0xeb, 0x4e, 0xf6, 0x18, 0xb4, 0xfc, 0xdc, 0x6b, 0x97, 0x59,
+    0x69, 0xe2, 0x07, 0xd7, 0x5a, 0x79, 0xbd, 0xa6, 0x5a, 0x00, 0x8c, 0x9e,
+    0x07, 0x88, 0x78, 0xe3, 0x9b, 0x2f, 0xe6, 0x22, 0x9f, 0xba, 0x4f, 0xf0,
+    0x74, 0xb4, 0xfb, 0x62, 0xd9, 0xd5, 0x27, 0xfc, 0x5d, 0x76, 0x76, 0xcf,
     0xa8, 0x0a, 0x88, 0x34, 0x26, 0x96, 0x7d, 0x57, 0x23, 0xb2, 0xd3, 0xf3,
-    0xb8, 0x35, 0xb7, 0x96, 0x96, 0x5c, 0xf4, 0x84, 0x96, 0x7f, 0xff, 0xd5,
-    0x6f, 0xb3, 0x9e, 0x6c, 0x1d, 0xbd, 0x9d, 0x2b, 0x28, 0xeb, 0x43, 0x27,
-    0x22, 0xe5, 0x2c, 0x85, 0x70, 0x93, 0x4f, 0xf3, 0x1f, 0x0b, 0x67, 0x10,
-    0xad, 0x3e, 0xc0, 0x6b, 0x8c, 0xb4, 0x11, 0xee, 0x74, 0xda, 0x77, 0x30,
-    0x56, 0x9f, 0xff, 0x86, 0x8d, 0xdc, 0x60, 0xd7, 0xb4, 0x5b, 0x70, 0xad,
+    0xb6, 0x35, 0xc7, 0x96, 0x96, 0x5c, 0xf4, 0x84, 0x96, 0x7f, 0xff, 0xd5,
+    0x6f, 0xb3, 0x7e, 0x6c, 0x1e, 0x3d, 0x9d, 0x2b, 0x28, 0xeb, 0x43, 0x27,
+    0x22, 0xe5, 0x2c, 0x85, 0x70, 0x93, 0x4f, 0xf3, 0x1f, 0x0b, 0x87, 0x10,
+    0xad, 0x3e, 0xc0, 0x6b, 0x6c, 0xb4, 0x11, 0xee, 0x74, 0xda, 0x76, 0xf0,
+    0x56, 0x9f, 0xff, 0x86, 0x8d, 0xe4, 0x60, 0xd7, 0xb4, 0x5c, 0x6c, 0xad,
     0x5a, 0x30, 0xfe, 0x3e, 0x1a, 0x9f, 0xf1, 0xf3, 0xda, 0x33, 0x54, 0xec,
-    0x5a, 0x7e, 0xd6, 0x9b, 0xbe, 0x65, 0x82, 0x6f, 0xa7, 0xb9, 0x84, 0xfd,
-    0x69, 0xfb, 0x45, 0xdc, 0xfb, 0xab, 0x41, 0x22, 0x2f, 0x87, 0x9e, 0x22,
+    0x5a, 0x7e, 0xd6, 0x9b, 0xbe, 0x65, 0x82, 0x6f, 0xa7, 0xb7, 0x84, 0xfd,
+    0x69, 0xfb, 0x45, 0xdc, 0xfb, 0xab, 0x41, 0x22, 0x2f, 0x67, 0x9e, 0x22,
     0x98, 0xef, 0x8a, 0xa2, 0xfc, 0x9f, 0xf9, 0xde, 0xf9, 0xce, 0x3f, 0xbc,
-    0xc7, 0x5a, 0x7f, 0xfb, 0x8c, 0x0c, 0xbe, 0xec, 0x1c, 0x07, 0x19, 0x69,
-    0xff, 0x8b, 0x07, 0x33, 0x5b, 0x9d, 0xa3, 0xad, 0x1b, 0x23, 0x5d, 0xe4,
-    0x6d, 0x27, 0x4e, 0x35, 0xba, 0xb4, 0xfc, 0x06, 0xff, 0xb9, 0xb2, 0xd0,
+    0xc7, 0x5a, 0x7f, 0xfb, 0x6c, 0x0c, 0xbf, 0x2c, 0x1c, 0x06, 0xd9, 0x69,
+    0xff, 0x8b, 0x07, 0x33, 0x5c, 0x9d, 0xa3, 0xad, 0x1c, 0x23, 0x5d, 0xe4,
+    0x6d, 0x27, 0x4e, 0x35, 0xba, 0xb4, 0xfc, 0x06, 0xff, 0xb9, 0xc2, 0xd0,
     0x13, 0xca, 0x38, 0xe4, 0xc4, 0xf2, 0xd3, 0xbc, 0x56, 0x2d, 0x38, 0x5a,
-    0xeb, 0x4f, 0xff, 0x0b, 0x5f, 0x77, 0x70, 0xbe, 0x7c, 0xb1, 0xf7, 0x16,
-    0x8d, 0xc8, 0xa3, 0x38, 0xb0, 0x8e, 0x3e, 0x8d, 0x43, 0x2a, 0xb0, 0x01,
-    0x7e, 0x43, 0xfc, 0x9f, 0x2a, 0x18, 0xb3, 0xee, 0x13, 0xfc, 0x02, 0xd3,
-    0xd5, 0xe6, 0xd9, 0x69, 0xff, 0xff, 0x37, 0x08, 0xe1, 0x79, 0xd5, 0xd3,
-    0x38, 0x5b, 0x6a, 0xe5, 0x62, 0xd0, 0x6a, 0x2e, 0x1e, 0x28, 0x7d, 0x21,
-    0x9f, 0xab, 0xad, 0xc3, 0x00, 0xb4, 0xf5, 0xb5, 0x9a, 0x5a, 0x7f, 0x9c,
+    0xeb, 0x4f, 0xff, 0x0b, 0x5f, 0x97, 0x70, 0xbe, 0x7c, 0xb1, 0xf7, 0x16,
+    0x8e, 0x48, 0xa3, 0x38, 0xb0, 0x8e, 0x3e, 0x8d, 0x43, 0x2a, 0xb0, 0x01,
+    0x7e, 0x43, 0xfc, 0x9f, 0x2a, 0x18, 0xb3, 0xed, 0x93, 0xfc, 0x02, 0xd3,
+    0xd5, 0xe6, 0xe1, 0x69, 0xff, 0xff, 0x36, 0xc8, 0xe1, 0x79, 0xd5, 0xd3,
+    0x36, 0x5c, 0x6a, 0xe5, 0x62, 0xd0, 0x6a, 0x2e, 0x1e, 0x28, 0x7d, 0x21,
+    0x9f, 0xab, 0xad, 0xb3, 0x00, 0xb4, 0xf5, 0xb5, 0x9a, 0x5a, 0x7f, 0x9c,
     0x7a, 0x77, 0xb4, 0xc6, 0xad, 0x0c, 0x7b, 0x54, 0x43, 0x31, 0x5a, 0xb4,
     0xc4, 0x2b, 0x4b, 0x00, 0x6a, 0xb4, 0x2d, 0x14, 0x8f, 0x71, 0x84, 0x3f,
-    0xc9, 0x53, 0xb6, 0xb1, 0xcb, 0x4f, 0x19, 0xf5, 0x69, 0x69, 0xef, 0xff,
-    0xcd, 0x96, 0x9f, 0x9f, 0x7d, 0x94, 0x37, 0x5a, 0x7f, 0xbe, 0xa7, 0x03,
-    0x33, 0xfd, 0x2d, 0x3f, 0xbf, 0x2d, 0xb6, 0x76, 0x79, 0x68, 0xe1, 0xf7,
-    0x68, 0xe6, 0x7e, 0x6d, 0xb4, 0x72, 0xdf, 0xad, 0x1d, 0x4c, 0x4f, 0x84,
+    0xc9, 0x53, 0xb8, 0xb1, 0xcb, 0x4f, 0x19, 0xf5, 0x69, 0x69, 0xef, 0xff,
+    0xce, 0x16, 0x9f, 0x9f, 0x7d, 0x94, 0x37, 0x5a, 0x7f, 0xbe, 0xa7, 0x03,
+    0x33, 0xfd, 0x2d, 0x3f, 0xbf, 0x2e, 0x38, 0x76, 0x79, 0x68, 0xd9, 0xf7,
+    0x68, 0xe6, 0x7e, 0x6e, 0x34, 0x72, 0xe7, 0xad, 0x1d, 0x4c, 0x4f, 0x64,
     0x9e, 0x84, 0xe3, 0xf2, 0x28, 0x64, 0xde, 0xf2, 0x33, 0x69, 0x9e, 0xf9,
-    0x69, 0x1d, 0x68, 0xd8, 0xd4, 0x3e, 0x43, 0x13, 0xfa, 0xab, 0x67, 0x0b,
-    0x01, 0x69, 0xff, 0xc2, 0x4d, 0xa1, 0xa7, 0x6e, 0xd6, 0x75, 0x69, 0xff,
-    0xff, 0x70, 0xb6, 0xf6, 0x6d, 0xee, 0xf5, 0xbd, 0xf9, 0x82, 0xee, 0x62,
-    0xd3, 0xc0, 0x05, 0x0a, 0xd3, 0xfe, 0xae, 0x5c, 0xab, 0x76, 0xab, 0x8b,
-    0x4f, 0xf7, 0x0a, 0xbf, 0xc0, 0x53, 0x96, 0x86, 0x4e, 0x90, 0x04, 0xf8,
-    0x63, 0x74, 0x82, 0x6f, 0xe1, 0x07, 0x8f, 0xa7, 0xfe, 0xc1, 0xdb, 0xd9,
+    0x69, 0x1d, 0x68, 0xe0, 0xd4, 0x3e, 0x43, 0x13, 0xfa, 0xab, 0x87, 0x0b,
+    0x01, 0x69, 0xff, 0xc2, 0x4d, 0xa1, 0xa7, 0x72, 0xd6, 0x75, 0x69, 0xff,
+    0xff, 0x6c, 0xb8, 0xf6, 0x71, 0xee, 0xf5, 0xbd, 0xf9, 0x82, 0xed, 0xe2,
+    0xd3, 0xc0, 0x05, 0x0a, 0xd3, 0xfe, 0xad, 0xdc, 0xab, 0x96, 0xab, 0x6b,
+    0x4f, 0xf6, 0xca, 0xbf, 0xc0, 0x53, 0x96, 0x86, 0x4e, 0x90, 0x04, 0xf8,
+    0x63, 0x74, 0x82, 0x6f, 0xd9, 0x07, 0x8f, 0xa7, 0xfe, 0xc1, 0xe3, 0xd9,
     0xd2, 0xb2, 0x8e, 0xb4, 0xfd, 0xfe, 0xae, 0xdd, 0x35, 0x69, 0xfd, 0x8e,
-    0xc2, 0x07, 0x09, 0x69, 0xef, 0x38, 0xfc, 0x5a, 0x78, 0x8d, 0x2b, 0x56,
-    0x8c, 0x3c, 0x41, 0x22, 0x9b, 0x76, 0x2d, 0x3d, 0xa1, 0x63, 0xad, 0x0c,
-    0x6d, 0xf6, 0x17, 0x9e, 0xce, 0x38, 0xeb, 0x43, 0x26, 0xff, 0x48, 0x62,
-    0x5f, 0xa7, 0x4f, 0x95, 0xf7, 0x88, 0x65, 0xc5, 0xa6, 0xd3, 0x96, 0x8f,
-    0xcd, 0x37, 0xc2, 0x13, 0xf5, 0x8f, 0x3b, 0x7c, 0xed, 0x8b, 0x4f, 0x01,
-    0xb6, 0xd2, 0xd3, 0xf8, 0xde, 0x67, 0xb8, 0x5e, 0x5a, 0x62, 0xea, 0xd1,
-    0xbe, 0x91, 0x41, 0x86, 0xc4, 0x45, 0xc3, 0x39, 0xff, 0x80, 0xc0, 0xeb,
+    0xc2, 0x06, 0xc9, 0x69, 0xef, 0x38, 0xfb, 0x5a, 0x78, 0x8d, 0x2b, 0x56,
+    0x8c, 0x3c, 0x41, 0x22, 0x9b, 0x96, 0x2d, 0x3d, 0xa1, 0x63, 0xad, 0x0c,
+    0x6d, 0xf8, 0x17, 0x9e, 0xcd, 0xb8, 0xeb, 0x43, 0x26, 0xff, 0x48, 0x62,
+    0x5f, 0xa7, 0x4f, 0x95, 0xf9, 0x88, 0x65, 0xb5, 0xa6, 0xd3, 0x96, 0x8f,
+    0xcd, 0x37, 0xc2, 0x13, 0xf5, 0x8f, 0x3b, 0x9c, 0xed, 0x8b, 0x4f, 0x01,
+    0xb8, 0xd2, 0xd3, 0xf8, 0xdd, 0xe7, 0xb6, 0x5e, 0x5a, 0x62, 0xea, 0xd1,
+    0xce, 0x91, 0x41, 0x86, 0xc4, 0x45, 0xb3, 0x39, 0xff, 0x80, 0xc0, 0xeb,
     0x78, 0xae, 0x40, 0x5a, 0x7f, 0x9e, 0xd3, 0xfa, 0x1b, 0x3e, 0x02, 0xd3,
-    0xf7, 0x3e, 0xb1, 0xee, 0x12, 0xd3, 0xc2, 0x39, 0xe5, 0xa0, 0x08, 0xce,
-    0x24, 0x2e, 0x1e, 0xf8, 0xc2, 0x7f, 0xfb, 0x05, 0x9f, 0xf5, 0xbb, 0x85,
-    0xc6, 0x02, 0xd2, 0x0e, 0xfa, 0x6e, 0xd2, 0x5f, 0x02, 0xf7, 0xce, 0x38,
+    0xf6, 0xfe, 0xb1, 0xed, 0x92, 0xd3, 0xc2, 0x39, 0xe5, 0xa0, 0x08, 0xce,
+    0x24, 0x2d, 0x9e, 0xf8, 0xc2, 0x7f, 0xfb, 0x05, 0x9f, 0xf5, 0xbb, 0x85,
+    0xb6, 0x02, 0xd2, 0x0f, 0x3a, 0x6e, 0xd2, 0x5f, 0x02, 0xf7, 0xce, 0x38,
     0xd1, 0xbe, 0xd8, 0xaa, 0xe2, 0x70, 0x46, 0x2d, 0x92, 0xc5, 0x4d, 0x84,
-    0xb5, 0xe1, 0x5d, 0xb4, 0x66, 0x25, 0x0f, 0x87, 0xa3, 0xeb, 0xec, 0x27,
-    0x0f, 0x09, 0x9e, 0x4b, 0x9b, 0xa8, 0xc2, 0xbf, 0x8c, 0xb1, 0xf2, 0x34,
-    0x18, 0xe6, 0x75, 0x1f, 0xa7, 0xa3, 0xe1, 0xb5, 0xff, 0x7a, 0x18, 0x66,
-    0x46, 0x1e, 0xfa, 0x79, 0x01, 0x6f, 0x73, 0x39, 0x5d, 0xf0, 0x4f, 0xfe,
+    0xb5, 0xe1, 0x5d, 0xc4, 0x66, 0x25, 0x0f, 0x87, 0xa3, 0xeb, 0xec, 0x27,
+    0x0f, 0x09, 0x9d, 0xcb, 0x9b, 0xa8, 0xc2, 0xbf, 0x8c, 0xb1, 0xf2, 0x34,
+    0x18, 0xe6, 0x75, 0x1f, 0xa7, 0xa3, 0xe1, 0xb5, 0xff, 0x9a, 0x18, 0x66,
+    0x46, 0x1e, 0xfa, 0x79, 0x01, 0x6f, 0x73, 0x37, 0x5d, 0xf0, 0x4f, 0xfe,
     0x09, 0xe9, 0xc1, 0xce, 0xde, 0xaa, 0xea, 0x89, 0xb2, 0x7f, 0x07, 0x3b,
     0x7a, 0xab, 0xaa, 0x2a, 0xd9, 0xfc, 0xe7, 0x7d, 0xe1, 0x67, 0x2d, 0x3d,
-    0x9c, 0xaf, 0x2d, 0x27, 0xc7, 0x0f, 0x4c, 0x4c, 0xe7, 0x55, 0x5d, 0x51,
-    0x5a, 0x4f, 0x8d, 0xdb, 0x5f, 0x6c, 0xb4, 0xbb, 0x73, 0xd3, 0x22, 0x79,
+    0x9b, 0xaf, 0x2d, 0x27, 0xc7, 0x0f, 0x4c, 0x4c, 0xe7, 0x55, 0x5d, 0x51,
+    0x5a, 0x4f, 0x8d, 0xe3, 0x5f, 0x70, 0xb4, 0xbb, 0x73, 0xd3, 0x22, 0x79,
     0xff, 0xeb, 0xb7, 0x73, 0xfa, 0x3d, 0x39, 0x8a, 0xeb, 0x4f, 0xff, 0x3b,
     0x58, 0x2c, 0x7a, 0xcf, 0x79, 0x8e, 0xb4, 0xde, 0xf3, 0x22, 0x66, 0x93,
-    0x67, 0x9d, 0xe2, 0xb1, 0x69, 0xf1, 0x9e, 0x16, 0xfd, 0x69, 0x36, 0xc7,
-    0x94, 0xfc, 0x8a, 0x7e, 0xb3, 0x38, 0x58, 0x75, 0xa7, 0xcf, 0x60, 0x0f,
-    0xbf, 0x5a, 0x7e, 0x16, 0xb7, 0x67, 0x69, 0x69, 0x63, 0xc7, 0xb4, 0xfa,
-    0x59, 0x3f, 0x9d, 0x9e, 0xa1, 0xff, 0xe5, 0xa4, 0x1d, 0xf1, 0x56, 0x1e,
-    0x3f, 0x14, 0x30, 0x38, 0xef, 0x45, 0x43, 0x08, 0x6f, 0x15, 0xcf, 0xe0,
+    0x67, 0x9d, 0xe2, 0xb1, 0x69, 0xf1, 0x9e, 0x16, 0xfd, 0x69, 0x37, 0x07,
+    0x94, 0xfc, 0x8a, 0x7e, 0xb3, 0x36, 0x58, 0x75, 0xa7, 0xcf, 0x60, 0x0f,
+    0xcf, 0x5a, 0x7e, 0x16, 0xb7, 0x87, 0x69, 0x69, 0x63, 0xc7, 0xb4, 0xfa,
+    0x59, 0x3f, 0x9d, 0x9e, 0xa1, 0xff, 0xe5, 0xa4, 0x1e, 0x71, 0x56, 0x1e,
+    0x3f, 0x14, 0x30, 0x36, 0xef, 0x45, 0x43, 0x08, 0x6f, 0x15, 0xcf, 0xe0,
     0xe7, 0x6f, 0x55, 0x75, 0x45, 0x81, 0x3e, 0xed, 0xea, 0xae, 0xa8, 0x9d,
-    0x67, 0xff, 0xf7, 0x1a, 0xc0, 0x61, 0xc3, 0xad, 0x67, 0x0b, 0x5f, 0x58,
+    0x67, 0xff, 0xf6, 0xda, 0xc0, 0x61, 0xc3, 0xad, 0x66, 0xcb, 0x5f, 0x58,
     0xb4, 0xf8, 0x27, 0xa7, 0x07, 0x11, 0x2c, 0xf1, 0x9c, 0xfe, 0x76, 0x82,
     0x69, 0xac, 0x75, 0xa7, 0xdd, 0xbd, 0x55, 0xd5, 0x16, 0xcc, 0xff, 0xfe,
     0xa7, 0x07, 0x4c, 0x58, 0x02, 0xcd, 0x67, 0x85, 0x96, 0x9f, 0xf7, 0xed,
-    0xa0, 0xf0, 0xaf, 0x5d, 0x5a, 0x41, 0xc4, 0x66, 0x1c, 0xce, 0x96, 0xe7,
+    0xa0, 0xec, 0xaf, 0x5d, 0x5a, 0x41, 0xc4, 0x66, 0x1c, 0xce, 0x96, 0xe7,
     0xaf, 0x55, 0x75, 0x45, 0xcd, 0x23, 0xad, 0x1d, 0x37, 0xb6, 0x96, 0xcc,
     0xf1, 0xd6, 0x90, 0x70, 0xdc, 0xda, 0x45, 0x3e, 0xed, 0xea, 0xae, 0xa8,
     0xbb, 0xe7, 0xf3, 0x83, 0xae, 0xf4, 0xad, 0x5a, 0x41, 0xc3, 0xe7, 0xa3,
     0x39, 0xe0, 0x9a, 0x0f, 0xd6, 0x86, 0x74, 0xbb, 0xf9, 0x5e, 0xfb, 0x5c,
-    0xcb, 0x65, 0x52, 0x96, 0xa9, 0xd7, 0x73, 0xc3, 0x52, 0x90, 0x3f, 0x8c,
+    0xcb, 0x85, 0x52, 0x96, 0xa9, 0xd7, 0x73, 0xc3, 0x52, 0x90, 0x3f, 0x8c,
     0x07, 0x50, 0xaa, 0xf4, 0x23, 0x8c, 0x26, 0x9f, 0x6b, 0x47, 0xa7, 0x2d,
-    0x3f, 0xf6, 0xd7, 0xfa, 0xb6, 0xf6, 0x60, 0x9a, 0xb4, 0xe1, 0xa0, 0xb1,
+    0x3f, 0xf7, 0x17, 0xfa, 0xb8, 0xf6, 0x60, 0x9a, 0xb4, 0xe1, 0xa0, 0xb1,
     0xf6, 0xf8, 0x9e, 0x7f, 0x0d, 0x98, 0xfb, 0xe6, 0x35, 0x69, 0xff, 0xb3,
-    0x54, 0x6f, 0x09, 0x87, 0xe6, 0x5a, 0x7f, 0xff, 0xfb, 0xa3, 0x56, 0x8e,
+    0x54, 0x6e, 0xc9, 0x87, 0xe6, 0x5a, 0x7f, 0xff, 0xfb, 0xa3, 0x56, 0x8e,
     0x7b, 0xd9, 0x63, 0x0d, 0x1b, 0xa3, 0xb6, 0x08, 0x16, 0x9f, 0x76, 0xf5,
-    0x57, 0x54, 0x4a, 0xf3, 0x3a, 0xc5, 0xa7, 0xfc, 0x35, 0x68, 0x38, 0xd6,
-    0x65, 0xab, 0x4f, 0xff, 0xce, 0xc2, 0xff, 0x0e, 0x5e, 0xd3, 0x1e, 0xb6,
+    0x57, 0x54, 0x4a, 0xf3, 0x3a, 0xc5, 0xa7, 0xfc, 0x35, 0x68, 0x36, 0xd6,
+    0x65, 0xab, 0x4f, 0xff, 0xce, 0xc2, 0xff, 0x0e, 0x5e, 0xd3, 0x1e, 0xb8,
     0xf2, 0xd3, 0xdf, 0x57, 0x5c, 0xb4, 0xff, 0xfe, 0x2f, 0x51, 0xe8, 0x7f,
-    0xe5, 0xb4, 0x36, 0x5c, 0xac, 0x5a, 0x00, 0x88, 0x3a, 0x22, 0x80, 0x26,
-    0x7d, 0x87, 0xfc, 0x86, 0xec, 0xff, 0xbd, 0x4e, 0xd7, 0x1b, 0xa6, 0x01,
+    0xdd, 0xb4, 0x36, 0x5c, 0xac, 0x5a, 0x00, 0x88, 0x3a, 0x22, 0x80, 0x26,
+    0x7d, 0x87, 0xfb, 0x86, 0xec, 0xff, 0xbd, 0x4e, 0xd6, 0xdb, 0xa6, 0x01,
     0x69, 0xff, 0xbc, 0x4f, 0xcb, 0xcf, 0x39, 0x87, 0xcb, 0x4e, 0xa7, 0x05,
     0x95, 0x99, 0xb8, 0xd8, 0x08, 0x58, 0xf4, 0x73, 0x3d, 0x46, 0xab, 0xe3,
-    0x53, 0x0f, 0xe7, 0xff, 0x67, 0x02, 0x38, 0x02, 0x1f, 0x67, 0x16, 0x9d,
-    0xe6, 0x02, 0x53, 0xff, 0xb9, 0x45, 0xe6, 0x71, 0x76, 0xcf, 0xbf, 0x4a,
+    0x53, 0x0f, 0xe7, 0xff, 0x66, 0xc2, 0x38, 0x02, 0x1f, 0x66, 0xd6, 0x9d,
+    0xe6, 0x02, 0x53, 0xff, 0xb7, 0x45, 0xe6, 0x71, 0x76, 0xcf, 0xbf, 0x4a,
     0x7f, 0x55, 0xc2, 0x0e, 0xbe, 0x41, 0x63, 0xe6, 0xd0, 0xdc, 0x83, 0x75,
     0xce, 0x83, 0xcb, 0x64, 0xf4, 0x22, 0x5f, 0xc2, 0xb2, 0x7f, 0xf0, 0x4f,
     0x4e, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x4c, 0x33, 0xff, 0x85, 0x9f, 0x8b,
-    0x6c, 0x1d, 0xf4, 0xf9, 0xf4, 0xe5, 0xa7, 0xff, 0x76, 0xbf, 0x09, 0xbe,
+    0x70, 0x1e, 0x74, 0xf9, 0xf4, 0xe5, 0xa7, 0xff, 0x76, 0xbf, 0x09, 0xbe,
     0x75, 0xad, 0xa7, 0x2d, 0x36, 0x04, 0x08, 0xa4, 0x31, 0x5e, 0x7f, 0xe7,
-    0xc7, 0x36, 0x77, 0xd5, 0xe1, 0x67, 0x2d, 0x3f, 0x9a, 0xac, 0xf1, 0xb5,
+    0xc7, 0x38, 0x77, 0xd5, 0xe1, 0x67, 0x2d, 0x3f, 0x9a, 0xac, 0xf1, 0xb5,
     0xd5, 0xa7, 0xd5, 0xd7, 0x51, 0xd6, 0x81, 0x3d, 0x9f, 0x8c, 0xe7, 0xe3,
-    0xb6, 0x0f, 0xb7, 0x96, 0x96, 0x96, 0x9f, 0x36, 0x0f, 0xb7, 0x96, 0x9f,
-    0xb8, 0x5e, 0xd5, 0x6d, 0xb8, 0xf9, 0x9e, 0x2e, 0x38, 0x84, 0xff, 0xfe,
-    0xb5, 0xf7, 0xd9, 0xcd, 0xc3, 0x96, 0x16, 0x0e, 0xce, 0xd2, 0xd3, 0xeb,
-    0x7d, 0x9c, 0xba, 0xd2, 0x1c, 0x44, 0x75, 0x33, 0x4f, 0xff, 0x37, 0x86,
-    0xbf, 0xe6, 0x10, 0x3e, 0xf6, 0x96, 0x9f, 0x9f, 0x99, 0xe1, 0x6f, 0xd6,
-    0x9f, 0xfc, 0xde, 0xfd, 0x9d, 0x9b, 0x7a, 0xcf, 0xad, 0x5a, 0x18, 0xff,
+    0xb6, 0x0f, 0xb9, 0x96, 0x96, 0x96, 0x9f, 0x36, 0x0f, 0xb9, 0x96, 0x9f,
+    0xb6, 0x5e, 0xd5, 0x71, 0xc8, 0xf9, 0x9e, 0x2e, 0x38, 0x84, 0xff, 0xfe,
+    0xb5, 0xf7, 0xd9, 0xbe, 0x43, 0x96, 0x16, 0x0f, 0x0e, 0xd2, 0xd3, 0xeb,
+    0x7d, 0x9b, 0xba, 0xd2, 0x1c, 0x44, 0x75, 0x33, 0x4f, 0xff, 0x37, 0x86,
+    0xbf, 0xde, 0x10, 0x3e, 0xf6, 0x96, 0x9f, 0x9f, 0x99, 0xe1, 0x6f, 0xd6,
+    0x9f, 0xfc, 0xde, 0xfd, 0x9d, 0x9c, 0x7a, 0xcf, 0xad, 0x5a, 0x18, 0xff,
     0x88, 0xc2, 0x7f, 0x7f, 0x5f, 0xf7, 0xa5, 0xd5, 0xa7, 0xed, 0x11, 0xa7,
-    0xaf, 0x2d, 0x3f, 0xff, 0xee, 0xe3, 0xf6, 0x1f, 0x6e, 0x30, 0x5b, 0x94,
-    0x5e, 0x6f, 0x69, 0x68, 0xd9, 0x13, 0x5d, 0x2d, 0x9e, 0xdc, 0xfd, 0xf6,
-    0xc5, 0xa3, 0x0f, 0x38, 0xd2, 0x49, 0xff, 0xf7, 0x07, 0x3d, 0xc2, 0xcd,
-    0xbd, 0x8f, 0xdb, 0x4b, 0x4e, 0x6f, 0xcc, 0x5a, 0x30, 0xfc, 0x85, 0x56,
-    0x7a, 0x8c, 0x06, 0x2d, 0x20, 0xef, 0x8b, 0xa2, 0x20, 0x85, 0x15, 0xe1,
-    0x10, 0xf4, 0x38, 0xfa, 0x4d, 0xc8, 0x66, 0xfe, 0x40, 0x31, 0x8e, 0x7a,
-    0x12, 0x9f, 0x10, 0x4f, 0xff, 0x74, 0xb6, 0x76, 0x8b, 0xdc, 0x6e, 0xf9,
+    0xaf, 0x2d, 0x3f, 0xff, 0xee, 0xe3, 0xf6, 0x1f, 0x72, 0x30, 0x5b, 0x74,
+    0x5e, 0x6f, 0x69, 0x68, 0xe1, 0x13, 0x5d, 0x2d, 0x9e, 0xe4, 0xfd, 0xf6,
+    0xc5, 0xa3, 0x0f, 0x38, 0xd2, 0x49, 0xff, 0xf6, 0xc7, 0x3d, 0xb2, 0xce,
+    0x3d, 0x8f, 0xdb, 0x4b, 0x4e, 0x6f, 0xcc, 0x5a, 0x30, 0xfc, 0x85, 0x56,
+    0x7a, 0x8c, 0x06, 0x2d, 0x20, 0xf3, 0x8b, 0xa2, 0x20, 0x85, 0x15, 0xe1,
+    0x10, 0xf4, 0x38, 0xfa, 0x4d, 0xb8, 0x66, 0xfe, 0x40, 0x31, 0x8e, 0x7a,
+    0x12, 0x9f, 0x10, 0x4f, 0xff, 0x74, 0xb8, 0x76, 0x8b, 0xdb, 0x6e, 0xf9,
     0x96, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x55, 0xd3, 0xba, 0xc7, 0x5a, 0x7f,
     0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0x8f, 0x24, 0x10, 0x23, 0x87, 0x13,
     0xfa, 0x67, 0x43, 0x73, 0xf8, 0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x60, 0xcf,
     0xe0, 0xe7, 0x6f, 0x55, 0x75, 0x45, 0x95, 0x3f, 0x83, 0x9d, 0xbd, 0x55,
-    0xd5, 0x16, 0x9c, 0xff, 0x6f, 0x9c, 0x2b, 0x41, 0x55, 0x75, 0xa7, 0xff,
-    0xf3, 0x8b, 0x94, 0x3e, 0x76, 0x77, 0x18, 0x47, 0x3c, 0xb4, 0xfe, 0xcd,
-    0x6e, 0xef, 0x7e, 0xe2, 0xd3, 0xff, 0xe6, 0x1b, 0xf0, 0xb8, 0x5d, 0x67,
-    0xad, 0xce, 0x2d, 0x3d, 0x7a, 0xab, 0xaa, 0x2d, 0xc9, 0xff, 0x9b, 0x3b,
-    0x83, 0xbb, 0x99, 0xb5, 0xab, 0x47, 0x4f, 0xcb, 0x45, 0xb3, 0x8e, 0x24,
-    0xb4, 0x85, 0x69, 0xff, 0x88, 0x1c, 0xfa, 0xb5, 0x98, 0x26, 0xad, 0x0c,
-    0x7d, 0x98, 0x35, 0xc0, 0xf9, 0xfe, 0x61, 0xf8, 0xed, 0xaf, 0x12, 0xd3,
+    0xd5, 0x16, 0x9c, 0xff, 0x73, 0x9b, 0x2b, 0x41, 0x55, 0x75, 0xa7, 0xff,
+    0xf3, 0x8b, 0x74, 0x3e, 0x76, 0x77, 0x18, 0x47, 0x3c, 0xb4, 0xfe, 0xcd,
+    0x72, 0xef, 0x7e, 0xda, 0xd3, 0xff, 0xe6, 0x1b, 0xec, 0xb6, 0x5d, 0x67,
+    0xad, 0xcd, 0xad, 0x3d, 0x7a, 0xab, 0xaa, 0x2d, 0xc9, 0xff, 0x9b, 0x3b,
+    0x83, 0xcb, 0x79, 0xc5, 0xab, 0x47, 0x4f, 0xcb, 0x45, 0xb3, 0x8e, 0x24,
+    0xb4, 0x85, 0x69, 0xff, 0x88, 0x1b, 0xfa, 0xb5, 0x98, 0x26, 0xad, 0x0c,
+    0x7d, 0x98, 0x35, 0xb0, 0xf9, 0xfe, 0x61, 0xf8, 0xed, 0xaf, 0x12, 0xd3,
     0xfa, 0xaa, 0xc0, 0x36, 0x75, 0x68, 0x5a, 0x77, 0xd5, 0xe5, 0xa6, 0xf9,
-    0x96, 0x87, 0x1b, 0x2f, 0x0e, 0x42, 0xd3, 0xdb, 0x5c, 0xba, 0xb4, 0xf1,
-    0x31, 0xc3, 0x88, 0x89, 0xd8, 0xf0, 0xe4, 0x3e, 0x0a, 0x9f, 0xf5, 0x38,
+    0x96, 0x87, 0x1b, 0x2f, 0x0e, 0x42, 0xd3, 0xdc, 0x5c, 0xba, 0xb4, 0xf1,
+    0x31, 0xc3, 0x88, 0x89, 0xe0, 0xf0, 0xe4, 0x3e, 0x0a, 0x9f, 0xf5, 0x38,
     0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x43, 0xcf, 0xe6, 0xef, 0xed, 0x7c, 0x3a,
-    0xd3, 0xff, 0x1e, 0x80, 0xc0, 0xe1, 0x09, 0x1d, 0x69, 0xff, 0xbe, 0x77,
-    0x2b, 0xbb, 0x79, 0x8a, 0xc5, 0xa7, 0xfd, 0xf7, 0x7f, 0xed, 0x0f, 0xb3,
-    0xf5, 0xa7, 0xfb, 0x04, 0x6b, 0xc6, 0x35, 0xd6, 0x9f, 0xf1, 0x6d, 0xad,
-    0xde, 0xdc, 0x0c, 0x15, 0xa7, 0xd5, 0xb6, 0xbc, 0x4b, 0x47, 0x11, 0x3e,
-    0x26, 0xda, 0x42, 0x9f, 0x87, 0x7c, 0xce, 0xb6, 0x96, 0x9f, 0xff, 0x03,
-    0x8c, 0x60, 0x7c, 0x35, 0xfe, 0xb4, 0xdf, 0xa5, 0x30, 0x31, 0x68, 0xc3,
-    0xec, 0x25, 0x79, 0xfd, 0xca, 0xfd, 0xe1, 0x60, 0x2d, 0x20, 0xbe, 0x0b,
+    0xd3, 0xff, 0x1e, 0x80, 0xc0, 0xd9, 0x09, 0x1d, 0x69, 0xff, 0xbe, 0x76,
+    0xeb, 0xbc, 0x79, 0x8a, 0xc5, 0xa7, 0xfd, 0xf7, 0x7f, 0xed, 0x0f, 0xb3,
+    0xf5, 0xa7, 0xfb, 0x04, 0x6b, 0xc6, 0x35, 0xd6, 0x9f, 0xf1, 0x71, 0xae,
+    0x5e, 0xe4, 0x0c, 0x15, 0xa7, 0xd5, 0xc6, 0xbc, 0x4b, 0x46, 0xd1, 0x3e,
+    0x26, 0xda, 0x42, 0x9f, 0x87, 0x9c, 0xce, 0xb6, 0x96, 0x9f, 0xff, 0x03,
+    0x6c, 0x60, 0x7c, 0x35, 0xfe, 0xb4, 0xdf, 0xa5, 0x30, 0x31, 0x68, 0xc3,
+    0xec, 0x25, 0x79, 0xfd, 0xba, 0xfd, 0xe1, 0x60, 0x2d, 0x20, 0xbe, 0x0b,
     0xd2, 0x4c, 0x7a, 0xe5, 0xb0, 0x1b, 0x64, 0x31, 0xaf, 0x08, 0xe2, 0x2d,
-    0x78, 0xdf, 0xb0, 0xba, 0x3a, 0x9f, 0x12, 0x68, 0xcb, 0xf4, 0x1d, 0x23,
-    0x7a, 0x1b, 0xff, 0x17, 0xef, 0x42, 0x70, 0xc2, 0x09, 0xfc, 0x1c, 0xed,
+    0x78, 0xdf, 0xb0, 0xba, 0x3a, 0x9e, 0xd2, 0x68, 0xcb, 0xf4, 0x1d, 0x23,
+    0x7a, 0x1b, 0xff, 0x17, 0xf3, 0x42, 0x70, 0xc2, 0x09, 0xfc, 0x1c, 0xed,
     0xea, 0xae, 0xa8, 0xbc, 0xe1, 0x9b, 0x11, 0x57, 0x2c, 0x82, 0x1f, 0x39,
-    0x3a, 0x81, 0xb4, 0x73, 0x7d, 0x41, 0x39, 0xf7, 0x0f, 0x86, 0x7b, 0x41,
+    0x3a, 0x81, 0xc4, 0x73, 0x7d, 0x41, 0x39, 0xf6, 0xcf, 0x86, 0x7b, 0x41,
     0xfc, 0x63, 0xb3, 0xf8, 0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x4b, 0x4f, 0xbb,
-    0x7a, 0xab, 0xaa, 0x2c, 0x29, 0xff, 0xf3, 0x60, 0x81, 0xb8, 0xdb, 0x7b,
+    0x7a, 0xab, 0xaa, 0x2c, 0x29, 0xff, 0xf3, 0x60, 0x81, 0xb6, 0xdc, 0x7b,
     0x06, 0x8e, 0xb4, 0x83, 0x88, 0x84, 0x39, 0x9c, 0xfe, 0x0e, 0x76, 0xf5,
-    0x57, 0x54, 0x59, 0x73, 0xc1, 0x35, 0xf3, 0x77, 0xdd, 0x69, 0xc1, 0x30,
+    0x57, 0x54, 0x59, 0x73, 0xc1, 0x35, 0xf3, 0x79, 0xdd, 0x69, 0xc1, 0x30,
     0xeb, 0x4b, 0x57, 0x3c, 0xef, 0x8c, 0x65, 0xfa, 0xd3, 0x1b, 0x62, 0xd3,
-    0x57, 0x5c, 0x6a, 0x70, 0x46, 0x7f, 0x59, 0xb6, 0x98, 0x1e, 0x25, 0xa7,
+    0x57, 0x5c, 0x6a, 0x70, 0x46, 0x7f, 0x59, 0xc6, 0x98, 0x1e, 0x25, 0xa7,
     0xcd, 0x8f, 0xdb, 0x4b, 0x4c, 0x2d, 0x47, 0xb7, 0x69, 0xa4, 0xf5, 0x39,
-    0xc0, 0x5a, 0x7e, 0xae, 0xb7, 0xab, 0x8b, 0x4c, 0xda, 0x5a, 0x00, 0x7c,
-    0x3c, 0x21, 0x7e, 0x57, 0x3f, 0x15, 0x6b, 0xfd, 0x7c, 0xb4, 0xff, 0xfd,
-    0xc2, 0xf7, 0x33, 0x43, 0x87, 0xfb, 0x5c, 0xa7, 0x2d, 0x3f, 0x99, 0xde,
-    0xae, 0x50, 0x16, 0x8c, 0x44, 0x5f, 0x56, 0xa4, 0xe5, 0xa4, 0xe5, 0xa4,
-    0xe5, 0xa1, 0x8d, 0x81, 0xa2, 0x04, 0x21, 0x3f, 0xfe, 0xf6, 0x8a, 0xf8,
-    0x3d, 0x61, 0xc0, 0x61, 0xab, 0x4b, 0x65, 0xa6, 0xd7, 0xcb, 0x46, 0x1f,
-    0xd6, 0x94, 0xbc, 0x23, 0x31, 0x32, 0xd3, 0xf0, 0xb1, 0xc5, 0xb1, 0x69,
-    0x9c, 0xcb, 0x40, 0x0f, 0x49, 0xe1, 0x4f, 0x8a, 0x67, 0xff, 0xff, 0xb8,
-    0xde, 0xd1, 0x6b, 0x4d, 0xee, 0x13, 0x77, 0xf3, 0xd3, 0x9d, 0xe3, 0x56,
-    0x99, 0x8e, 0xb4, 0xff, 0xfc, 0xe6, 0x1f, 0x9c, 0xe6, 0xf7, 0x30, 0xe6,
-    0x37, 0xeb, 0x4d, 0x70, 0xb2, 0xe7, 0x45, 0x8a, 0x40, 0x84, 0x36, 0x42,
-    0x36, 0xe6, 0x3d, 0x85, 0xcd, 0x29, 0x0c, 0x29, 0xf5, 0x08, 0x0f, 0x18,
-    0xef, 0x3f, 0x18, 0x2b, 0x3f, 0xf8, 0x27, 0xa7, 0x07, 0x3b, 0x7a, 0xab,
-    0xaa, 0x28, 0xc9, 0xf3, 0xf6, 0xeb, 0x7e, 0xb4, 0xf8, 0x6b, 0xf6, 0x31,
-    0x69, 0x05, 0x8f, 0x43, 0x85, 0x30, 0xe5, 0xff, 0x8e, 0xc3, 0x08, 0xe5,
-    0x15, 0x3a, 0xe9, 0xa8, 0x67, 0x7a, 0x14, 0xb3, 0xff, 0x06, 0x9c, 0x1c,
-    0xed, 0xea, 0xae, 0xa8, 0x8e, 0x67, 0xff, 0x04, 0xf4, 0xe0, 0xe7, 0x6f,
-    0x55, 0x75, 0x44, 0xe5, 0x3f, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x16, 0x64,
-    0xff, 0x3b, 0x54, 0x70, 0xf3, 0xdf, 0xad, 0x3f, 0xcf, 0x98, 0x0c, 0x3b,
-    0x6c, 0xc7, 0x5a, 0x7f, 0xbf, 0xeb, 0x7b, 0x95, 0xb3, 0x96, 0x9f, 0xd9,
-    0xae, 0xf7, 0x36, 0xd2, 0xd2, 0x0e, 0xfb, 0xa2, 0xb1, 0x8f, 0xdc, 0x75,
-    0x3f, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x16, 0xec, 0xfc, 0x34, 0x1e, 0xd9,
-    0xc5, 0xa7, 0xfd, 0x4e, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x52, 0x12, 0x0d,
-    0xcf, 0xd8, 0xe5, 0xb3, 0xff, 0x06, 0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8,
-    0x94, 0xa2, 0xc5, 0x65, 0xef, 0x27, 0x9d, 0x56, 0x8f, 0xbf, 0x87, 0x30,
-    0x9e, 0x7a, 0x16, 0x8f, 0xa5, 0x13, 0xfe, 0xa7, 0x07, 0x3b, 0x7a, 0xab,
-    0xaa, 0x27, 0x69, 0xac, 0xdf, 0xad, 0x3f, 0xfe, 0x20, 0x7c, 0x76, 0x7d,
-    0xa0, 0x0b, 0x0e, 0x5d, 0x69, 0x04, 0xe8, 0xb9, 0xe2, 0x4d, 0x0d, 0xcf,
-    0xff, 0x04, 0xcd, 0xea, 0x1f, 0xf3, 0xcc, 0x25, 0xfa, 0xd3, 0xff, 0x51,
-    0xf9, 0x97, 0xae, 0x31, 0x8e, 0x5a, 0x7f, 0xe1, 0xce, 0x55, 0x99, 0xa1,
-    0xad, 0x96, 0x9b, 0x78, 0x20, 0x44, 0x2e, 0x90, 0xe0, 0xe9, 0x9f, 0x68,
-    0xd7, 0x7a, 0x1a, 0xf3, 0xee, 0xde, 0xaa, 0xea, 0x88, 0xb2, 0x7f, 0xd4,
-    0xe0, 0xe7, 0x6f, 0x55, 0x75, 0x44, 0xbb, 0x3f, 0xff, 0x60, 0x38, 0xc6,
-    0x07, 0xc3, 0x5f, 0xeb, 0x4d, 0xfa, 0x52, 0x0e, 0x23, 0x60, 0xe6, 0x7b,
-    0xc9, 0x33, 0xff, 0x82, 0x7a, 0x70, 0x73, 0xb7, 0xaa, 0xba, 0xa2, 0x62,
-    0x9f, 0x76, 0xf5, 0x57, 0x54, 0x55, 0x32, 0xc5, 0xa3, 0x0f, 0x08, 0xd3,
-    0x39, 0xff, 0xff, 0xdd, 0xae, 0x6c, 0xec, 0xf0, 0x79, 0x5a, 0xe9, 0x5b,
-    0xee, 0x17, 0x5c, 0xb4, 0x05, 0x13, 0x74, 0x45, 0x3f, 0xf8, 0x27, 0xa7,
-    0x07, 0x3b, 0x7a, 0xab, 0xaa, 0x27, 0x49, 0xfc, 0x55, 0xd6, 0x75, 0x3f,
-    0x5a, 0x7c, 0xf3, 0x3c, 0xda, 0x5a, 0x7e, 0xb7, 0x09, 0xfe, 0x0a, 0xd0,
-    0x47, 0xab, 0xa2, 0x89, 0xc5, 0xc6, 0x58, 0x26, 0x86, 0x7f, 0xef, 0xb5,
-    0xe6, 0xd8, 0x3f, 0x3c, 0x47, 0x5a, 0x0e, 0x7e, 0x5c, 0x2a, 0x9f, 0xf5,
-    0x38, 0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x3b, 0xcf, 0xbd, 0xf5, 0x1c, 0x92,
-    0x90, 0x40, 0x9d, 0x66, 0x46, 0x34, 0x72, 0x2a, 0x49, 0x9f, 0xfc, 0x13,
-    0xd3, 0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x14, 0x2c, 0xff, 0xff, 0xf1, 0x73,
-    0xe3, 0x9e, 0x9c, 0x1b, 0xf3, 0xef, 0x30, 0xe5, 0x86, 0xfc, 0xcf, 0x2d,
-    0x3f, 0xf8, 0x27, 0xa7, 0x07, 0x3b, 0x7a, 0xab, 0xaa, 0x29, 0x29, 0xff,
-    0xfd, 0x9a, 0x09, 0x98, 0xf7, 0x28, 0xfe, 0xe3, 0x7b, 0xe3, 0xad, 0x16,
-    0x2e, 0xb2, 0x38, 0xe2, 0xf1, 0x99, 0x74, 0x88, 0xf2, 0x80, 0x7f, 0x48,
-    0xf2, 0xaf, 0xd0, 0x82, 0xde, 0x55, 0x9f, 0xc1, 0xce, 0xde, 0xaa, 0xea,
-    0x88, 0x92, 0x7f, 0xf0, 0x4f, 0x4e, 0x0e, 0x76, 0xf5, 0x57, 0x54, 0x4b,
-    0xd3, 0xdd, 0xfb, 0xda, 0x5a, 0x7d, 0xdf, 0xce, 0x5a, 0x5a, 0x7f, 0x10,
-    0xf8, 0xc3, 0x8b, 0x2d, 0x3f, 0xfb, 0x8f, 0xd8, 0x7f, 0xdf, 0xe0, 0x33,
-    0xbb, 0x2d, 0x33, 0x79, 0x69, 0xff, 0xdf, 0x00, 0xba, 0x63, 0xcf, 0xbf,
-    0x50, 0xbc, 0xb4, 0x39, 0x1c, 0xe4, 0x63, 0xfa, 0x86, 0x85, 0x67, 0xfe,
-    0xa7, 0xb4, 0xcf, 0xeb, 0x8d, 0xb3, 0xcb, 0x4b, 0x16, 0x9b, 0xac, 0xb4,
-    0x01, 0x3d, 0xa2, 0x23, 0x7a, 0x30, 0xfe, 0x9e, 0xbf, 0x47, 0x7d, 0x0f,
-    0x9f, 0xe2, 0xa1, 0xfb, 0xcf, 0x31, 0xab, 0x4f, 0xbb, 0x7a, 0xab, 0xaa,
-    0x29, 0x79, 0xf0, 0xfa, 0xcc, 0xb5, 0x69, 0xdf, 0x56, 0x96, 0x9e, 0xd6,
-    0x7b, 0x4b, 0x43, 0xc6, 0xf7, 0xc3, 0x93, 0xfd, 0xfe, 0xba, 0xdb, 0x38,
-    0x8e, 0xb4, 0xff, 0x55, 0x9b, 0xac, 0xf9, 0xe6, 0xdf, 0xad, 0x27, 0x31,
-    0xfe, 0x7c, 0x75, 0x3f, 0xf7, 0x5f, 0xb7, 0xfb, 0xb8, 0xc7, 0xa1, 0x5a,
-    0x30, 0xfb, 0xc8, 0xa2, 0x77, 0x58, 0xeb, 0x4f, 0xdd, 0xfd, 0xaf, 0x87,
-    0x5a, 0x67, 0x62, 0xd3, 0x9b, 0xfd, 0x2d, 0x0c, 0x7b, 0xc0, 0x2d, 0xb4,
-    0x56, 0x7f, 0xe2, 0x3d, 0x00, 0xc7, 0x9c, 0xc3, 0xb2, 0xd3, 0xda, 0x2b,
-    0x7c, 0xb4, 0x70, 0xf9, 0xfc, 0x8b, 0x3f, 0xff, 0x1d, 0x9c, 0x2d, 0x61,
-    0x8f, 0x6b, 0x98, 0xfb, 0xdf, 0xd6, 0x9e, 0xbb, 0x3f, 0xd2, 0xd3, 0xfd,
-    0xfb, 0x7e, 0x63, 0xf6, 0x17, 0xd5, 0xa3, 0x0f, 0x88, 0x88, 0xe7, 0xf7,
-    0x29, 0xe2, 0xeb, 0x58, 0xb4, 0xd8, 0x75, 0xa2, 0x8f, 0x21, 0xf9, 0x9c,
-    0xf9, 0xf4, 0x8e, 0xda, 0x5a, 0x7d, 0x4f, 0xf3, 0xba, 0x5a, 0x4c, 0x03,
-    0xd1, 0x39, 0x4c, 0xf8, 0x8d, 0xd7, 0xf7, 0x5a, 0x41, 0x65, 0xd8, 0x10,
-    0x31, 0xe1, 0xce, 0xc6, 0x64, 0xca, 0xf4, 0x61, 0xbd, 0x20, 0xe3, 0xcd,
-    0x42, 0x5b, 0xf2, 0x21, 0x86, 0x1e, 0x9a, 0x3c, 0xe9, 0xbc, 0x4f, 0x3f,
-    0x83, 0x9d, 0xbd, 0x55, 0xd5, 0x14, 0xe4, 0xff, 0x8b, 0xb5, 0xae, 0x66,
-    0x0d, 0xd6, 0x9f, 0xf8, 0xd2, 0xdb, 0x43, 0x80, 0xe5, 0x5a, 0xb4, 0xff,
-    0xb3, 0x8d, 0xdc, 0x0d, 0xb6, 0xda, 0x94, 0xc5, 0x6a, 0xd3, 0x58, 0x10,
-    0x23, 0x5b, 0xc7, 0x7b, 0xc8, 0xaf, 0xa8, 0x13, 0xee, 0xde, 0xaa, 0xea,
-    0x8a, 0xf2, 0x7f, 0xfe, 0xc0, 0x71, 0x8c, 0x0f, 0x86, 0xbf, 0xd6, 0x9b,
-    0xf4, 0xa4, 0x1c, 0x44, 0x7e, 0xf1, 0x9c, 0xff, 0xc1, 0xa7, 0x07, 0x3b,
-    0x7a, 0xab, 0xaa, 0x24, 0x79, 0xc5, 0xfb, 0x2d, 0x3a, 0xf8, 0x74, 0x82,
-    0x5d, 0xcf, 0xbb, 0x7a, 0xab, 0xaa, 0x24, 0x89, 0xff, 0xf9, 0x8b, 0xa4,
-    0xfa, 0xc3, 0x63, 0x78, 0xb9, 0x56, 0x2d, 0x3c, 0x1a, 0x70, 0x59, 0x17,
-    0x18, 0x55, 0xc3, 0x39, 0xff, 0x83, 0x4e, 0x0e, 0x76, 0xf5, 0x57, 0x54,
-    0x49, 0x53, 0xee, 0xde, 0xaa, 0xea, 0x8b, 0xc6, 0x7d, 0xad, 0x7b, 0x2e,
-    0xb4, 0xff, 0x38, 0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x26, 0xc8, 0x38, 0x89,
-    0xee, 0x99, 0xd1, 0x3c, 0xfb, 0x78, 0x2d, 0x6f, 0x16, 0x86, 0x66, 0xcc,
-    0x58, 0x7c, 0xe9, 0xfb, 0x00, 0x43, 0x64, 0xa3, 0x14, 0x7a, 0x1b, 0xe7,
-    0x2b, 0xe4, 0x64, 0x5e, 0x45, 0xfa, 0x1d, 0xa6, 0x1a, 0x43, 0x4a, 0x45,
-    0xcb, 0xb2, 0x3c, 0xe7, 0x4e, 0x0a, 0x82, 0x52, 0x16, 0x5e, 0xa4, 0x09,
-    0xb2, 0xb5, 0xef, 0x6c, 0xfa, 0x76, 0xa5, 0x03, 0x14, 0xff, 0x16, 0xfe,
-    0x50, 0x8b, 0xd5, 0xd0, 0xef, 0x6b, 0x08, 0xf3, 0xe3, 0x3d, 0xb3, 0x93,
-    0xe7, 0xb5, 0x5c, 0xcd, 0xff, 0x39, 0x52, 0xf9, 0x42, 0xf8, 0x69, 0x3b,
-    0x5a, 0xa6, 0x47, 0x7a, 0xd6, 0x26, 0x7d, 0x5b, 0x9d, 0x3f, 0xa4, 0x54,
-    0xdb, 0x2e, 0x7f, 0x7a, 0x53, 0xc1, 0x93, 0xce, 0xaf, 0xb4, 0xe3, 0x18,
+    0xc0, 0x5a, 0x7e, 0xae, 0xb7, 0xab, 0x6b, 0x4e, 0xb6, 0xdb, 0x52, 0x9f,
+    0xe6, 0x77, 0x1e, 0xeb, 0x67, 0x09, 0x04, 0xbf, 0x99, 0xb4, 0xb4, 0x01,
+    0x19, 0x1b, 0x21, 0x14, 0xa7, 0xe9, 0x13, 0xf1, 0x56, 0xbf, 0xd7, 0xcb,
+    0x4f, 0xff, 0xdb, 0x2f, 0x6f, 0x34, 0x38, 0x7f, 0xb5, 0xba, 0x72, 0xd3,
+    0xf9, 0x9d, 0xea, 0xdd, 0x01, 0x68, 0xc4, 0x45, 0xf5, 0x6a, 0x4e, 0x5a,
+    0x4e, 0x5a, 0x4e, 0x5a, 0x18, 0xd8, 0x1a, 0x20, 0x42, 0x13, 0xff, 0xef,
+    0x68, 0xaf, 0x83, 0xd6, 0x1c, 0x06, 0x1a, 0xb4, 0xb8, 0x5a, 0x6d, 0x7c,
+    0xb4, 0x61, 0xfd, 0x69, 0x4b, 0xc2, 0x33, 0x13, 0x2d, 0x3f, 0x0b, 0x1c,
+    0x5b, 0x16, 0x99, 0xcc, 0xb4, 0x00, 0xf4, 0x9e, 0x14, 0xf8, 0xa6, 0x7f,
+    0xff, 0xfb, 0x6d, 0xed, 0x16, 0xb4, 0xde, 0xd9, 0x37, 0x7f, 0x3d, 0x39,
+    0xde, 0x35, 0x69, 0x98, 0xeb, 0x4f, 0xff, 0xce, 0x61, 0xf9, 0xce, 0x6f,
+    0x6f, 0x0e, 0x63, 0x7e, 0xb4, 0xd7, 0x0b, 0x2e, 0xae, 0x58, 0xa4, 0x08,
+    0x43, 0x64, 0x3d, 0x2e, 0x7d, 0xd8, 0x5c, 0xd2, 0x90, 0xc2, 0x9f, 0x50,
+    0x80, 0xf1, 0x8f, 0x33, 0xf1, 0x82, 0xb3, 0xff, 0x82, 0x7a, 0x70, 0x73,
+    0xb7, 0xaa, 0xba, 0xa2, 0x8c, 0x9f, 0x3f, 0x6e, 0xb7, 0xeb, 0x4f, 0x86,
+    0xbf, 0x63, 0x16, 0x90, 0x58, 0xf4, 0x36, 0x53, 0x0e, 0x64, 0x36, 0x76,
+    0x18, 0x47, 0x28, 0xa9, 0xde, 0x8d, 0x43, 0x3b, 0xd0, 0xa5, 0x9f, 0xf8,
+    0x34, 0xe0, 0xe7, 0x6f, 0x55, 0x75, 0x44, 0x73, 0x3f, 0xf8, 0x27, 0xa7,
+    0x07, 0x3b, 0x7a, 0xab, 0xaa, 0x27, 0x29, 0xfc, 0x1c, 0xed, 0xea, 0xae,
+    0xa8, 0xb3, 0x27, 0xf9, 0xda, 0xa3, 0x87, 0x7e, 0xfd, 0x69, 0xfe, 0x7c,
+    0xc0, 0x61, 0xdb, 0x86, 0x3a, 0xd3, 0xfd, 0xff, 0x5b, 0xdb, 0xae, 0x1c,
+    0xb4, 0xfe, 0xcd, 0x77, 0xb9, 0xc6, 0x96, 0x90, 0x79, 0xdd, 0x15, 0x8c,
+    0x7e, 0xe3, 0xa9, 0xfc, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0xb7, 0x67, 0xe1,
+    0xa0, 0xf6, 0xcd, 0xad, 0x3f, 0xea, 0x70, 0x73, 0xb7, 0xaa, 0xba, 0xa2,
+    0x90, 0x90, 0x6e, 0x7e, 0xc7, 0x2d, 0x9f, 0xf8, 0x34, 0xe0, 0xe7, 0x6f,
+    0x55, 0x75, 0x44, 0xa5, 0x16, 0x2b, 0x2f, 0x79, 0x3c, 0xea, 0xb4, 0x7d,
+    0xfc, 0x39, 0x84, 0xf3, 0xd0, 0xb4, 0x7d, 0x28, 0x9f, 0xf5, 0x38, 0x39,
+    0xdb, 0xd5, 0x5d, 0x51, 0x3b, 0x4d, 0x67, 0x3d, 0x69, 0xff, 0xf1, 0x03,
+    0xe3, 0xb3, 0xed, 0x00, 0x58, 0x72, 0xeb, 0x48, 0x27, 0x45, 0xce, 0xd2,
+    0x68, 0x6e, 0x7f, 0xf8, 0x26, 0x73, 0x50, 0xff, 0x9e, 0x61, 0x2f, 0xd6,
+    0x9f, 0xfa, 0x8f, 0xbc, 0xbd, 0x6d, 0x8c, 0x72, 0xd3, 0xff, 0x0e, 0x6e,
+    0xac, 0xcd, 0x0d, 0x70, 0xb4, 0xdc, 0xc1, 0x02, 0x21, 0x74, 0x87, 0x07,
+    0x4c, 0xfb, 0x46, 0xbc, 0xd0, 0xd7, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x45,
+    0x93, 0xfe, 0xa7, 0x07, 0x3b, 0x7a, 0xab, 0xaa, 0x25, 0xd9, 0xff, 0xfb,
+    0x01, 0xb6, 0x30, 0x3e, 0x1a, 0xff, 0x5a, 0x6f, 0xd2, 0x90, 0x71, 0x1b,
+    0x07, 0x33, 0xe6, 0x49, 0x9f, 0xfc, 0x13, 0xd3, 0x83, 0x9d, 0xbd, 0x55,
+    0xd5, 0x13, 0x14, 0xfb, 0xb7, 0xaa, 0xba, 0xa2, 0xa9, 0x96, 0x2d, 0x18,
+    0x78, 0x46, 0x99, 0xcf, 0xff, 0xfe, 0xed, 0x6f, 0x87, 0x67, 0x83, 0xba,
+    0xd7, 0x4a, 0xdf, 0x6c, 0xba, 0xe5, 0xa0, 0x28, 0x9b, 0xa2, 0x29, 0xff,
+    0xc1, 0x3d, 0x38, 0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x3a, 0x4f, 0xe2, 0xae,
+    0xb3, 0xa9, 0xfa, 0xd3, 0xe7, 0x99, 0xe6, 0xd2, 0xd3, 0xf5, 0xb8, 0x4f,
+    0xf0, 0x56, 0x82, 0x3d, 0x5d, 0x14, 0x4e, 0x2d, 0xb2, 0xc1, 0x34, 0x33,
+    0xff, 0x7d, 0xaf, 0x37, 0x01, 0xf9, 0xe2, 0x3a, 0xd0, 0x73, 0xf2, 0xd9,
+    0x54, 0xff, 0xa9, 0xc1, 0xce, 0xde, 0xaa, 0xea, 0x89, 0xde, 0x7d, 0xef,
+    0xa8, 0xe4, 0x94, 0x82, 0x04, 0xeb, 0x32, 0x31, 0xa3, 0x91, 0x52, 0x4c,
+    0xff, 0xe0, 0x9e, 0x9c, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0xa1, 0x67, 0xff,
+    0xff, 0x8b, 0x7f, 0x1c, 0xf4, 0xe0, 0xdf, 0x7f, 0x79, 0x87, 0x2c, 0x37,
+    0xe6, 0x79, 0x69, 0xff, 0xc1, 0x3d, 0x38, 0x39, 0xdb, 0xd5, 0x5d, 0x51,
+    0x49, 0x4f, 0xff, 0xec, 0xd0, 0x4c, 0xc7, 0xb7, 0x47, 0xf6, 0xdb, 0xdf,
+    0x1d, 0x68, 0xb1, 0x75, 0x91, 0xc7, 0x17, 0x8c, 0xcb, 0xa4, 0x47, 0x94,
+    0x03, 0xfa, 0x47, 0x95, 0x7e, 0x84, 0x17, 0x32, 0xac, 0xfe, 0x0e, 0x76,
+    0xf5, 0x57, 0x54, 0x44, 0x93, 0xff, 0x82, 0x7a, 0x70, 0x73, 0xb7, 0xaa,
+    0xba, 0xa2, 0x5e, 0x9e, 0xef, 0xde, 0xd2, 0xd3, 0xee, 0xfe, 0x72, 0xd2,
+    0xd3, 0xf8, 0x87, 0xc6, 0x1c, 0x59, 0x69, 0xff, 0xdb, 0x7e, 0xc3, 0xff,
+    0x3f, 0x01, 0x9d, 0xe1, 0x69, 0x9b, 0xcb, 0x4f, 0xfe, 0xf8, 0x05, 0xd3,
+    0x1e, 0x7d, 0xfa, 0x85, 0xe5, 0xa1, 0xc8, 0xe7, 0x23, 0x1f, 0xd4, 0x34,
+    0x2b, 0x3f, 0xf5, 0x3d, 0xa6, 0x7f, 0x5b, 0x6e, 0x1e, 0x5a, 0x58, 0xb4,
+    0xdd, 0x65, 0xa0, 0x09, 0xed, 0x11, 0x1b, 0xd1, 0x87, 0xf4, 0xf5, 0xfa,
+    0x3b, 0xe8, 0x7c, 0xff, 0x15, 0x0f, 0xde, 0x79, 0x8d, 0x5a, 0x7d, 0xdb,
+    0xd5, 0x5d, 0x51, 0x4b, 0xcf, 0x87, 0xd6, 0x65, 0xab, 0x4e, 0xfa, 0xb4,
+    0xb4, 0xf6, 0xb3, 0xda, 0x5a, 0x1e, 0x37, 0xbe, 0x1c, 0x9f, 0xef, 0xf5,
+    0xd6, 0xe1, 0xc4, 0x75, 0xa7, 0xfa, 0xac, 0xe5, 0x67, 0xcf, 0x37, 0x3d,
+    0x69, 0x39, 0x8f, 0xf3, 0xe3, 0xa9, 0xff, 0xba, 0xfd, 0xbf, 0xe5, 0xb6,
+    0x3d, 0x0a, 0xd1, 0x87, 0xde, 0x45, 0x13, 0xba, 0xc7, 0x5a, 0x7e, 0xef,
+    0xed, 0x7c, 0x3a, 0xd3, 0x3b, 0x16, 0x9c, 0xdf, 0xe9, 0x68, 0x63, 0xde,
+    0x01, 0x6d, 0xa2, 0xb3, 0xff, 0x11, 0xe8, 0x06, 0x3c, 0xe6, 0x1e, 0x16,
+    0x9e, 0xd1, 0x5b, 0xe5, 0xa3, 0x67, 0xcf, 0xe4, 0x59, 0xff, 0xf8, 0xec,
+    0xe1, 0x6b, 0x0c, 0x7b, 0x5b, 0xc7, 0xde, 0xfe, 0xb4, 0xf5, 0xd9, 0xfe,
+    0x96, 0x9f, 0xef, 0xdb, 0xf3, 0x1f, 0xb0, 0xbe, 0xad, 0x18, 0x7c, 0x44,
+    0x47, 0x3f, 0xb7, 0x4f, 0x17, 0x5a, 0xc5, 0xa6, 0xc3, 0xad, 0x14, 0x79,
+    0x0f, 0xcc, 0xe7, 0xcf, 0xa4, 0x76, 0xd2, 0xd3, 0xea, 0x7f, 0x9d, 0xd2,
+    0xd2, 0x60, 0x1e, 0x89, 0xca, 0x67, 0xc4, 0x6e, 0xbf, 0xba, 0xd2, 0x0b,
+    0x2e, 0xc0, 0x81, 0x8f, 0x0e, 0x78, 0x33, 0x26, 0x57, 0xa3, 0x0d, 0xe9,
+    0x06, 0xde, 0x6a, 0x12, 0xdf, 0x91, 0x0c, 0x30, 0xf4, 0xd1, 0xe7, 0x4e,
+    0x62, 0x79, 0xfc, 0x1c, 0xed, 0xea, 0xae, 0xa8, 0xa7, 0x27, 0xfc, 0x5d,
+    0xad, 0x6f, 0x30, 0x6e, 0xb4, 0xff, 0xc6, 0x97, 0x1a, 0x1c, 0x06, 0xea,
+    0xd5, 0xa7, 0xfd, 0x9b, 0x6e, 0xe0, 0x6d, 0xb6, 0xd4, 0xa6, 0x2b, 0x56,
+    0x9a, 0xc0, 0x81, 0x1a, 0xde, 0x3b, 0xe6, 0x45, 0x7d, 0x40, 0x9f, 0x76,
+    0xf5, 0x57, 0x54, 0x57, 0x93, 0xff, 0xf6, 0x03, 0x6c, 0x60, 0x7c, 0x35,
+    0xfe, 0xb4, 0xdf, 0xa5, 0x20, 0xe2, 0x23, 0xf9, 0x8c, 0xe7, 0xfe, 0x0d,
+    0x38, 0x39, 0xdb, 0xd5, 0x5d, 0x51, 0x23, 0xce, 0x2f, 0xd9, 0x69, 0xd7,
+    0xc3, 0xa4, 0x12, 0xee, 0x7d, 0xdb, 0xd5, 0x5d, 0x51, 0x24, 0x4f, 0xff,
+    0xcc, 0x5d, 0x27, 0xd6, 0x1b, 0x1b, 0xc5, 0xba, 0xb1, 0x69, 0xe0, 0xd3,
+    0x82, 0xc8, 0xb8, 0xc2, 0xad, 0x99, 0xcf, 0xfc, 0x1a, 0x70, 0x73, 0xb7,
+    0xaa, 0xba, 0xa2, 0x4a, 0x9f, 0x76, 0xf5, 0x57, 0x54, 0x5e, 0x33, 0xed,
+    0x6b, 0xd9, 0x75, 0xa7, 0xf9, 0xc1, 0xce, 0xde, 0xaa, 0xea, 0x89, 0x36,
+    0x41, 0xc4, 0x4f, 0x74, 0xce, 0x89, 0xe7, 0xdc, 0xc1, 0x6b, 0x76, 0xb4,
+    0x33, 0x36, 0x62, 0xc3, 0xe7, 0x4f, 0xd8, 0x02, 0x1b, 0x25, 0x18, 0xa3,
+    0xd0, 0xdf, 0x39, 0x5e, 0xe3, 0x22, 0xf2, 0x2f, 0xd0, 0xed, 0x30, 0xd2,
+    0x1a, 0x52, 0x32, 0x05, 0x91, 0xe7, 0x3a, 0x70, 0x54, 0x12, 0x90, 0xb2,
+    0xf5, 0x20, 0x4d, 0x95, 0xaf, 0x7b, 0x67, 0xd3, 0xc5, 0x28, 0x18, 0xa7,
+    0xf8, 0xb9, 0xf2, 0x84, 0x5e, 0xae, 0x87, 0x7b, 0x58, 0x47, 0x9f, 0x19,
+    0xed, 0x9b, 0x9f, 0x3d, 0xaa, 0xe6, 0x6f, 0xf9, 0xca, 0x97, 0xca, 0x17,
+    0xc3, 0x49, 0xda, 0xd5, 0x32, 0x3b, 0xd6, 0xb1, 0x33, 0xea, 0xdc, 0xe9,
+    0xfd, 0x24, 0x76, 0xd9, 0x74, 0x1c, 0xd2, 0x9e, 0x0c, 0x9e, 0x75, 0x7d,
+    0xa7, 0x18, 0xc0,
 };
 
-static const unsigned kPreloadedHSTSBits = 151487;
+static const unsigned kPreloadedHSTSBits = 151604;
 
-static const unsigned kHSTSRootPosition = 150892;
+static const unsigned kHSTSRootPosition = 151009;
 
 #endif // NET_HTTP_TRANSPORT_SECURITY_STATE_STATIC_H_
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index d337a87..9139ca9 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -2259,7 +2259,9 @@
     { "name": "xf-liam.com", "include_subdomains": true, "mode": "force-https" },
     { "name": "yksityisyydensuoja.fi", "include_subdomains": true, "mode": "force-https" },
     { "name": "yokeepo.com", "include_subdomains": true, "mode": "force-https" },
-    { "name": "zravypapir.cz", "include_subdomains": true, "mode": "force-https" }
+    { "name": "zravypapir.cz", "include_subdomains": true, "mode": "force-https" },
+    { "name": "healthcare.gov", "mode": "force-https" },
+    { "name": "www.healthcare.gov", "mode": "force-https" }
   ],
 
   // |ReportUMAOnPinFailure| uses these to report which domain was associated
diff --git a/net/net.gyp b/net/net.gyp
index 551dabd7..39886bb 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -114,7 +114,6 @@
         'net_derived_sources',
         'net_extras',
         'net_test_support',
-        'quic_tools',
         'simple_quic_tools',
       ],
       'sources': [
@@ -123,9 +122,9 @@
       'conditions': [
         ['os_posix == 1 and OS != "mac" and OS != "ios" and OS != "android"', {
           'dependencies': [
+            'epoll_quic_tools',
             'epoll_server',
             'flip_in_mem_edsm_server_base',
-            'quic_base',
           ],
           'sources': [
             '<@(net_linux_test_sources)',
@@ -773,36 +772,6 @@
       'msvs_disabled_warnings': [4267, ],
     },
     {
-      # This is a temporary target which will be merged into 'net' once the
-      # dependency on balsa is eliminated and the classes are actually used.
-      'target_name': 'quic_tools',
-      'type': 'static_library',
-      'dependencies': [
-        '../base/base.gyp:base',
-        '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
-        '../url/url.gyp:url_lib',
-        'net',
-      ],
-      'sources': [
-        'quic/quic_dispatcher.cc',
-        'quic/quic_dispatcher.h',
-        'quic/quic_in_memory_cache.cc',
-        'quic/quic_in_memory_cache.h',
-        'quic/quic_per_connection_packet_writer.cc',
-        'quic/quic_per_connection_packet_writer.h',
-        'quic/quic_server.cc',
-        'quic/quic_server.h',
-        'quic/quic_server_packet_writer.cc',
-        'quic/quic_server_packet_writer.h',
-        'quic/quic_server_session.cc',
-        'quic/quic_server_session.h',
-        'quic/quic_spdy_server_stream.cc',
-        'quic/quic_spdy_server_stream.h',
-        'quic/quic_time_wait_list_manager.cc',
-        'quic/quic_time_wait_list_manager.h',
-      ],
-    },
-    {
       'target_name': 'simple_quic_tools',
       'type': 'static_library',
       'dependencies': [
@@ -812,12 +781,30 @@
         'net',
       ],
       'sources': [
+        'tools/quic/quic_client_session.cc',
+        'tools/quic/quic_client_session.h',
+        'tools/quic/quic_dispatcher.cc',
+        'tools/quic/quic_dispatcher.h',
+        'tools/quic/quic_in_memory_cache.cc',
+        'tools/quic/quic_in_memory_cache.h',
+        'tools/quic/quic_per_connection_packet_writer.cc',
+        'tools/quic/quic_per_connection_packet_writer.h',
+        'tools/quic/quic_server_session.cc',
+        'tools/quic/quic_server_session.h',
         'tools/quic/quic_simple_client.cc',
         'tools/quic/quic_simple_client.h',
-        'tools/quic/quic_simple_client_session.cc',
-        'tools/quic/quic_simple_client_session.h',
-        'tools/quic/quic_simple_client_stream.cc',
-        'tools/quic/quic_simple_client_stream.h',
+        'tools/quic/quic_simple_per_connection_packet_writer.cc',
+        'tools/quic/quic_simple_per_connection_packet_writer.h',
+        'tools/quic/quic_simple_server.cc',
+        'tools/quic/quic_simple_server.h',
+        'tools/quic/quic_simple_server_packet_writer.cc',
+        'tools/quic/quic_simple_server_packet_writer.h',
+        'tools/quic/quic_spdy_client_stream.cc',
+        'tools/quic/quic_spdy_client_stream.h',
+        'tools/quic/quic_spdy_server_stream.cc',
+        'tools/quic/quic_spdy_server_stream.h',
+        'tools/quic/quic_time_wait_list_manager.cc',
+        'tools/quic/quic_time_wait_list_manager.h',
         'tools/quic/synchronous_host_resolver.cc',
         'tools/quic/synchronous_host_resolver.h',
       ],
@@ -1084,7 +1071,7 @@
           ],
         },
         {
-          'target_name': 'simple_quic_client',
+          'target_name': 'quic_client',
           'type': 'executable',
           'dependencies': [
             '../base/base.gyp:base',
@@ -1097,6 +1084,18 @@
           ],
         },
         {
+          'target_name': 'quic_server',
+          'type': 'executable',
+          'dependencies': [
+            '../base/base.gyp:base',
+            'net',
+            'simple_quic_tools',
+          ],
+          'sources': [
+            'tools/quic/quic_simple_server_bin.cc',
+          ],
+        },
+        {
           'target_name': 'stress_cache',
           'type': 'executable',
           'dependencies': [
@@ -1224,7 +1223,7 @@
           ],
         },
         {
-          'target_name': 'quic_base',
+          'target_name': 'epoll_quic_tools',
           'type': 'static_library',
           'dependencies': [
             '../base/base.gyp:base',
@@ -1237,65 +1236,46 @@
           'sources': [
             'tools/quic/quic_client.cc',
             'tools/quic/quic_client.h',
-            'tools/quic/quic_client_session.cc',
-            'tools/quic/quic_client_session.h',
             'tools/quic/quic_default_packet_writer.cc',
             'tools/quic/quic_default_packet_writer.h',
-            'tools/quic/quic_dispatcher.cc',
-            'tools/quic/quic_dispatcher.h',
             'tools/quic/quic_epoll_clock.cc',
             'tools/quic/quic_epoll_clock.h',
             'tools/quic/quic_epoll_connection_helper.cc',
             'tools/quic/quic_epoll_connection_helper.h',
-            'tools/quic/quic_in_memory_cache.cc',
-            'tools/quic/quic_in_memory_cache.h',
             'tools/quic/quic_packet_reader.cc',
             'tools/quic/quic_packet_reader.h',
             'tools/quic/quic_packet_writer_wrapper.cc',
             'tools/quic/quic_packet_writer_wrapper.h',
-            'tools/quic/quic_per_connection_packet_writer.cc',
-            'tools/quic/quic_per_connection_packet_writer.h',
             'tools/quic/quic_server.cc',
             'tools/quic/quic_server.h',
-            'tools/quic/quic_server_session.cc',
-            'tools/quic/quic_server_session.h',
             'tools/quic/quic_socket_utils.cc',
             'tools/quic/quic_socket_utils.h',
-            'tools/quic/quic_spdy_client_stream.cc',
-            'tools/quic/quic_spdy_client_stream.h',
-            'tools/quic/quic_spdy_server_stream.cc',
-            'tools/quic/quic_spdy_server_stream.h',
-            'tools/quic/quic_time_wait_list_manager.cc',
-            'tools/quic/quic_time_wait_list_manager.h',
           ],
         },
         {
-          'target_name': 'quic_client',
+          'target_name': 'epoll_quic_client',
           'type': 'executable',
           'dependencies': [
             '../base/base.gyp:base',
             'net',
-            'quic_base',
+            'epoll_quic_tools',
             'simple_quic_tools',
           ],
           'sources': [
             'tools/quic/quic_client_bin.cc',
           ],
         },
-      ]
-    }],
-    ['os_posix == 1 and OS != "ios" and OS != "android"', {
-      'targets': [
         {
-          'target_name': 'quic_server',
+          'target_name': 'epoll_quic_server',
           'type': 'executable',
           'dependencies': [
             '../base/base.gyp:base',
             'net',
-            'quic_tools',
+            'epoll_quic_tools',
+            'simple_quic_tools',
           ],
           'sources': [
-            'quic/quic_server_bin.cc',
+            'tools/quic/quic_server_bin.cc',
           ],
         },
       ]
diff --git a/net/net.gypi b/net/net.gypi
index bd58e35..b06535b 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -120,8 +120,6 @@
       'http/transport_security_state.h',
       'socket/client_socket_handle.cc',
       'socket/client_socket_handle.h',
-      'socket/client_socket_pool_histograms.cc',
-      'socket/client_socket_pool_histograms.h',
       'socket/next_proto.cc',
       'socket/next_proto.h',
       'socket/socket.h',
@@ -1042,8 +1040,6 @@
       'spdy/hpack_entry.h',
       'spdy/hpack_header_table.cc',
       'spdy/hpack_header_table.h',
-      'spdy/hpack_huffman_aggregator.cc',
-      'spdy/hpack_huffman_aggregator.h',
       'spdy/hpack_huffman_table.cc',
       'spdy/hpack_huffman_table.h',
       'spdy/hpack_input_stream.cc',
@@ -1526,7 +1522,6 @@
       'quic/quic_sent_entropy_manager_test.cc',
       'quic/quic_sent_packet_manager_test.cc',
       'quic/quic_server_id_test.cc',
-      'quic/quic_server_test.cc',
       'quic/quic_session_test.cc',
       'quic/quic_socket_address_coder_test.cc',
       'quic/quic_stream_factory_test.cc',
@@ -1587,6 +1582,8 @@
       'quic/test_tools/quic_test_utils.h',
       'quic/test_tools/reliable_quic_stream_peer.cc',
       'quic/test_tools/reliable_quic_stream_peer.h',
+      'quic/test_tools/rtt_stats_peer.cc',
+      'quic/test_tools/rtt_stats_peer.h',
       'quic/test_tools/simple_quic_framer.cc',
       'quic/test_tools/simple_quic_framer.h',
       'quic/test_tools/test_task_runner.cc',
@@ -1628,7 +1625,6 @@
       'spdy/hpack_encoder_test.cc',
       'spdy/hpack_entry_test.cc',
       'spdy/hpack_header_table_test.cc',
-      'spdy/hpack_huffman_aggregator_test.cc',
       'spdy/hpack_huffman_table_test.cc',
       'spdy/hpack_input_stream_test.cc',
       'spdy/hpack_output_stream_test.cc',
@@ -1695,8 +1691,6 @@
       'tools/dump_cache/url_utilities.cc',
       'tools/dump_cache/url_utilities.h',
       'tools/dump_cache/url_utilities_unittest.cc',
-      'tools/quic/quic_simple_client_session_test.cc',
-      'tools/quic/quic_simple_client_stream_test.cc',
       'tools/quic/quic_simple_client_test.cc',
       'tools/tld_cleanup/tld_cleanup_util_unittest.cc',
       'udp/udp_socket_unittest.cc',
@@ -1749,6 +1743,7 @@
       'tools/quic/quic_in_memory_cache_test.cc',
       'tools/quic/quic_server_session_test.cc',
       'tools/quic/quic_server_test.cc',
+      'tools/quic/quic_simple_server_test.cc',
       'tools/quic/quic_spdy_client_stream_test.cc',
       'tools/quic/quic_spdy_server_stream_test.cc',
       'tools/quic/quic_time_wait_list_manager_test.cc',
diff --git a/net/quic/congestion_control/pacing_sender.cc b/net/quic/congestion_control/pacing_sender.cc
index addf426b..f656001e 100644
--- a/net/quic/congestion_control/pacing_sender.cc
+++ b/net/quic/congestion_control/pacing_sender.cc
@@ -28,14 +28,20 @@
 }
 
 bool PacingSender::ResumeConnectionState(
-    const CachedNetworkParameters& cached_network_params) {
-  return sender_->ResumeConnectionState(cached_network_params);
+    const CachedNetworkParameters& cached_network_params,
+    bool max_bandwidth_resumption) {
+  return sender_->ResumeConnectionState(cached_network_params,
+                                        max_bandwidth_resumption);
 }
 
 void PacingSender::SetNumEmulatedConnections(int num_connections) {
   sender_->SetNumEmulatedConnections(num_connections);
 }
 
+void PacingSender::SetMaxCongestionWindow(QuicByteCount max_congestion_window) {
+  sender_->SetMaxCongestionWindow(max_congestion_window);
+}
+
 void PacingSender::OnCongestionEvent(bool rtt_updated,
                                      QuicByteCount bytes_in_flight,
                                      const CongestionVector& acked_packets,
diff --git a/net/quic/congestion_control/pacing_sender.h b/net/quic/congestion_control/pacing_sender.h
index 9f64648..b16ff16 100644
--- a/net/quic/congestion_control/pacing_sender.h
+++ b/net/quic/congestion_control/pacing_sender.h
@@ -40,8 +40,10 @@
                      Perspective perspective,
                      bool using_pacing) override;
   bool ResumeConnectionState(
-      const CachedNetworkParameters& cached_network_params) override;
+      const CachedNetworkParameters& cached_network_params,
+      bool max_bandwidth_resumption) override;
   void SetNumEmulatedConnections(int num_connections) override;
+  void SetMaxCongestionWindow(QuicByteCount max_congestion_window) override;
   void OnCongestionEvent(bool rtt_updated,
                          QuicByteCount bytes_in_flight,
                          const CongestionVector& acked_packets,
diff --git a/net/quic/congestion_control/rtt_stats_test.cc b/net/quic/congestion_control/rtt_stats_test.cc
index 7fc07a35..86c7973 100644
--- a/net/quic/congestion_control/rtt_stats_test.cc
+++ b/net/quic/congestion_control/rtt_stats_test.cc
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "base/test/mock_log.h"
+#include "net/quic/test_tools/rtt_stats_peer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using logging::LOG_WARNING;
@@ -19,17 +20,6 @@
 namespace net {
 namespace test {
 
-class RttStatsPeer {
- public:
-  static QuicTime::Delta GetHalfWindowRtt(const RttStats* rtt_stats) {
-    return rtt_stats->half_window_rtt_.rtt;
-  }
-
-  static QuicTime::Delta GetQuarterWindowRtt(const RttStats* rtt_stats) {
-    return rtt_stats->quarter_window_rtt_.rtt;
-  }
-};
-
 class RttStatsTest : public ::testing::Test {
  protected:
   RttStats rtt_stats_;
diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h
index 0fbefec..7a49454 100644
--- a/net/quic/congestion_control/send_algorithm_interface.h
+++ b/net/quic/congestion_control/send_algorithm_interface.h
@@ -47,6 +47,9 @@
   // particularly for congestion avoidance.  Can be set any time.
   virtual void SetNumEmulatedConnections(int num_connections) = 0;
 
+  // Sets the maximum congestion window in bytes.
+  virtual void SetMaxCongestionWindow(QuicByteCount max_congestion_window) = 0;
+
   // Indicates an update to the congestion state, caused either by an incoming
   // ack or loss event timeout.  |rtt_updated| indicates whether a new
   // latest_rtt sample has been taken, |byte_in_flight| the bytes in flight
@@ -114,9 +117,11 @@
   virtual CongestionControlType GetCongestionControlType() const = 0;
 
   // Called by the Session when we get a bandwidth estimate from the client.
+  // Uses the max bandwidth in the params if |max_bandwidth_resumption| is true.
   // Returns true if initial connection state is changed as a result.
   virtual bool ResumeConnectionState(
-      const CachedNetworkParameters& cached_network_params) = 0;
+      const CachedNetworkParameters& cached_network_params,
+      bool max_bandwidth_resumption) = 0;
 };
 
 }  // namespace net
diff --git a/net/quic/congestion_control/tcp_cubic_bytes_sender.cc b/net/quic/congestion_control/tcp_cubic_bytes_sender.cc
index 6473722..c9d71ee 100644
--- a/net/quic/congestion_control/tcp_cubic_bytes_sender.cc
+++ b/net/quic/congestion_control/tcp_cubic_bytes_sender.cc
@@ -77,7 +77,8 @@
 }
 
 bool TcpCubicBytesSender::ResumeConnectionState(
-    const CachedNetworkParameters& cached_network_params) {
+    const CachedNetworkParameters& cached_network_params,
+    bool max_bandwidth_resumption) {
   // If the previous bandwidth estimate is less than an hour old, store in
   // preparation for doing bandwidth resumption.
   int64 seconds_since_estimate =
@@ -87,7 +88,9 @@
   }
 
   QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond(
-      cached_network_params.bandwidth_estimate_bytes_per_second());
+      max_bandwidth_resumption
+          ? cached_network_params.max_bandwidth_estimate_bytes_per_second()
+          : cached_network_params.bandwidth_estimate_bytes_per_second());
   QuicTime::Delta rtt_ms =
       QuicTime::Delta::FromMilliseconds(cached_network_params.min_rtt_ms());
 
@@ -107,6 +110,11 @@
   cubic_.SetNumConnections(num_connections_);
 }
 
+void TcpCubicBytesSender::SetMaxCongestionWindow(
+    QuicByteCount max_congestion_window) {
+  max_congestion_window_ = max_congestion_window;
+}
+
 float TcpCubicBytesSender::RenoBeta() const {
   // kNConnectionBeta is the backoff factor after loss for our N-connection
   // emulation, which emulates the effective backoff of an ensemble of N
@@ -197,6 +205,10 @@
     QuicPacketSequenceNumber sequence_number,
     QuicByteCount bytes,
     HasRetransmittableData is_retransmittable) {
+  if (InSlowStart()) {
+    ++(stats_->slowstart_packets_sent);
+  }
+
   // Only update bytes_in_flight_ for data packets.
   if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) {
     return false;
diff --git a/net/quic/congestion_control/tcp_cubic_bytes_sender.h b/net/quic/congestion_control/tcp_cubic_bytes_sender.h
index d601e6a..4a3d67c6 100644
--- a/net/quic/congestion_control/tcp_cubic_bytes_sender.h
+++ b/net/quic/congestion_control/tcp_cubic_bytes_sender.h
@@ -41,8 +41,10 @@
                      Perspective perspective,
                      bool using_pacing) override;
   bool ResumeConnectionState(
-      const CachedNetworkParameters& cached_network_params) override;
+      const CachedNetworkParameters& cached_network_params,
+      bool max_bandwidth_resumption) override;
   void SetNumEmulatedConnections(int num_connections) override;
+  void SetMaxCongestionWindow(QuicByteCount max_congestion_window) override;
   void OnCongestionEvent(bool rtt_updated,
                          QuicByteCount bytes_in_flight,
                          const CongestionVector& acked_packets,
diff --git a/net/quic/congestion_control/tcp_cubic_bytes_sender_test.cc b/net/quic/congestion_control/tcp_cubic_bytes_sender_test.cc
index 7b835779..63b5232 100644
--- a/net/quic/congestion_control/tcp_cubic_bytes_sender_test.cc
+++ b/net/quic/congestion_control/tcp_cubic_bytes_sender_test.cc
@@ -127,11 +127,11 @@
   // At startup make sure we are at the default.
   EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
   // At startup make sure we can send.
-  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA)
-                  .IsZero());
+  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0,
+                                     HAS_RETRANSMITTABLE_DATA).IsZero());
   // Make sure we can send.
-  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA)
-                  .IsZero());
+  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0,
+                                     HAS_RETRANSMITTABLE_DATA).IsZero());
   // And that window is un-affected.
   EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
 
@@ -146,11 +146,11 @@
   // Send exactly 10 packets and ensure the CWND ends at 14 packets.
   const int kNumberOfAcks = 5;
   // At startup make sure we can send.
-  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA)
-                  .IsZero());
+  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0,
+                                     HAS_RETRANSMITTABLE_DATA).IsZero());
   // Make sure we can send.
-  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA)
-                  .IsZero());
+  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0,
+                                     HAS_RETRANSMITTABLE_DATA).IsZero());
 
   SendAvailableSendWindow();
   for (int i = 0; i < kNumberOfAcks; ++i) {
@@ -165,13 +165,13 @@
 TEST_F(TcpCubicBytesSenderTest, ExponentialSlowStart) {
   const int kNumberOfAcks = 20;
   // At startup make sure we can send.
-  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA)
-                  .IsZero());
+  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0,
+                                     HAS_RETRANSMITTABLE_DATA).IsZero());
   EXPECT_FALSE(sender_->HasReliableBandwidthEstimate());
   EXPECT_EQ(QuicBandwidth::Zero(), sender_->BandwidthEstimate());
   // Make sure we can send.
-  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA)
-                  .IsZero());
+  EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0,
+                                     HAS_RETRANSMITTABLE_DATA).IsZero());
 
   for (int i = 0; i < kNumberOfAcks; ++i) {
     // Send our full send window.
@@ -480,8 +480,7 @@
   QuicTagVector options;
   options.push_back(kIW10);
   QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
-  sender_->SetFromConfig(config,
-                         /* is_server= */ Perspective::IS_SERVER,
+  sender_->SetFromConfig(config, Perspective::IS_SERVER,
                          /* using_pacing= */ false);
   EXPECT_EQ(10u * kDefaultTCPMSS, sender_->GetCongestionWindow());
 }
@@ -490,8 +489,7 @@
   EXPECT_TRUE(sender_->hybrid_slow_start().ack_train_detection());
 
   QuicConfig config;
-  sender_->SetFromConfig(config,
-                         /* is_server= */ Perspective::IS_SERVER,
+  sender_->SetFromConfig(config, Perspective::IS_SERVER,
                          /* using_pacing= */ true);
   EXPECT_FALSE(sender_->hybrid_slow_start().ack_train_detection());
 }
@@ -615,27 +613,34 @@
   // Ensure that an old estimate is not used for bandwidth resumption.
   cached_network_params.set_timestamp(clock_.WallNow().ToUNIXSeconds() -
                                       (kNumSecondsPerHour + 1));
-  EXPECT_FALSE(sender_->ResumeConnectionState(cached_network_params));
+  EXPECT_FALSE(sender_->ResumeConnectionState(cached_network_params, false));
   EXPECT_EQ(10u * kDefaultTCPMSS, sender_->GetCongestionWindow());
 
   // If the estimate is new enough, make sure it is used.
   cached_network_params.set_timestamp(clock_.WallNow().ToUNIXSeconds() -
                                       (kNumSecondsPerHour - 1));
-  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params));
+  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params, false));
   EXPECT_EQ(kNumberOfPackets * kDefaultTCPMSS, sender_->GetCongestionWindow());
 
   // Resumed CWND is limited to be in a sensible range.
   cached_network_params.set_bandwidth_estimate_bytes_per_second(
       (kMaxTcpCongestionWindow + 1) * kDefaultTCPMSS);
-  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params));
+  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params, false));
   EXPECT_EQ(kMaxTcpCongestionWindow * kDefaultTCPMSS,
             sender_->GetCongestionWindow());
 
   cached_network_params.set_bandwidth_estimate_bytes_per_second(
       (kMinCongestionWindowForBandwidthResumption - 1) * kDefaultTCPMSS);
-  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params));
+  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params, false));
   EXPECT_EQ(kMinCongestionWindowForBandwidthResumption * kDefaultTCPMSS,
             sender_->GetCongestionWindow());
+
+  // Resume to the max value.
+  cached_network_params.set_max_bandwidth_estimate_bytes_per_second(
+      (kMinCongestionWindowForBandwidthResumption + 10) * kDefaultTCPMSS);
+  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params, true));
+  EXPECT_EQ((kMinCongestionWindowForBandwidthResumption + 10) * kDefaultTCPMSS,
+            sender_->GetCongestionWindow());
 }
 
 }  // namespace test
diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc
index 2a381c7..e89666c 100644
--- a/net/quic/congestion_control/tcp_cubic_sender.cc
+++ b/net/quic/congestion_control/tcp_cubic_sender.cc
@@ -78,7 +78,8 @@
 }
 
 bool TcpCubicSender::ResumeConnectionState(
-    const CachedNetworkParameters& cached_network_params) {
+    const CachedNetworkParameters& cached_network_params,
+    bool max_bandwidth_resumption) {
   // If the previous bandwidth estimate is less than an hour old, store in
   // preparation for doing bandwidth resumption.
   int64 seconds_since_estimate =
@@ -88,7 +89,9 @@
   }
 
   QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond(
-      cached_network_params.bandwidth_estimate_bytes_per_second());
+      max_bandwidth_resumption
+          ? cached_network_params.max_bandwidth_estimate_bytes_per_second()
+          : cached_network_params.bandwidth_estimate_bytes_per_second());
   QuicTime::Delta rtt_ms =
       QuicTime::Delta::FromMilliseconds(cached_network_params.min_rtt_ms());
 
@@ -108,6 +111,11 @@
   cubic_.SetNumConnections(num_connections_);
 }
 
+void TcpCubicSender::SetMaxCongestionWindow(
+    QuicByteCount max_congestion_window) {
+  max_tcp_congestion_window_ = max_congestion_window / kMaxPacketSize;
+}
+
 float TcpCubicSender::RenoBeta() const {
   // kNConnectionBeta is the backoff factor after loss for our N-connection
   // emulation, which emulates the effective backoff of an ensemble of N
@@ -197,6 +205,10 @@
                                   QuicPacketSequenceNumber sequence_number,
                                   QuicByteCount bytes,
                                   HasRetransmittableData is_retransmittable) {
+  if (InSlowStart()) {
+    ++(stats_->slowstart_packets_sent);
+  }
+
   // Only update bytes_in_flight_ for data packets.
   if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) {
     return false;
diff --git a/net/quic/congestion_control/tcp_cubic_sender.h b/net/quic/congestion_control/tcp_cubic_sender.h
index 3667f675..e62f3785 100644
--- a/net/quic/congestion_control/tcp_cubic_sender.h
+++ b/net/quic/congestion_control/tcp_cubic_sender.h
@@ -44,8 +44,10 @@
                      Perspective perspective,
                      bool using_pacing) override;
   bool ResumeConnectionState(
-      const CachedNetworkParameters& cached_network_params) override;
+      const CachedNetworkParameters& cached_network_params,
+      bool max_bandwidth_resumption) override;
   void SetNumEmulatedConnections(int num_connections) override;
+  void SetMaxCongestionWindow(QuicByteCount max_congestion_window) override;
   void OnCongestionEvent(bool rtt_updated,
                          QuicByteCount bytes_in_flight,
                          const CongestionVector& acked_packets,
diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc
index 296350aab..fd30ef0 100644
--- a/net/quic/congestion_control/tcp_cubic_sender_test.cc
+++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc
@@ -712,26 +712,33 @@
   // Ensure that an old estimate is not used for bandwidth resumption.
   cached_network_params.set_timestamp(clock_.WallNow().ToUNIXSeconds() -
                                       (kNumSecondsPerHour + 1));
-  EXPECT_FALSE(sender_->ResumeConnectionState(cached_network_params));
+  EXPECT_FALSE(sender_->ResumeConnectionState(cached_network_params, false));
   EXPECT_EQ(10u, sender_->congestion_window());
 
   // If the estimate is new enough, make sure it is used.
   cached_network_params.set_timestamp(clock_.WallNow().ToUNIXSeconds() -
                                       (kNumSecondsPerHour - 1));
-  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params));
+  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params, false));
   EXPECT_EQ(kNumberOfPackets, sender_->congestion_window());
 
   // Resumed CWND is limited to be in a sensible range.
   cached_network_params.set_bandwidth_estimate_bytes_per_second(
       (kMaxTcpCongestionWindow + 1) * kMaxPacketSize);
-  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params));
+  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params, false));
   EXPECT_EQ(kMaxTcpCongestionWindow, sender_->congestion_window());
 
   cached_network_params.set_bandwidth_estimate_bytes_per_second(
       (kMinCongestionWindowForBandwidthResumption - 1) * kMaxPacketSize);
-  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params));
+  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params, false));
   EXPECT_EQ(kMinCongestionWindowForBandwidthResumption,
             sender_->congestion_window());
+
+  // Resume to the max value.
+  cached_network_params.set_max_bandwidth_estimate_bytes_per_second(
+      (kMinCongestionWindowForBandwidthResumption + 10) * kDefaultTCPMSS);
+  EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params, true));
+  EXPECT_EQ((kMinCongestionWindowForBandwidthResumption + 10) * kDefaultTCPMSS,
+            sender_->GetCongestionWindow());
 }
 
 }  // namespace test
diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h
index bdac1b3..b3dabab6 100644
--- a/net/quic/crypto/crypto_protocol.h
+++ b/net/quic/crypto/crypto_protocol.h
@@ -73,6 +73,7 @@
 
 // Enable bandwidth resumption experiment.
 const QuicTag kBWRE = TAG('B', 'W', 'R', 'E');  // Bandwidth resumption.
+const QuicTag kBWMX = TAG('B', 'W', 'M', 'X');  // Max bandwidth resumption.
 
 // Proof types (i.e. certificate types)
 // NOTE: although it would be silly to do so, specifying both kX509 and kX59R
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index b3e8b3df..e6a3d8ce 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -324,11 +324,13 @@
 }
 
 bool QuicConnection::ResumeConnectionState(
-    const CachedNetworkParameters& cached_network_params) {
+    const CachedNetworkParameters& cached_network_params,
+    bool max_bandwidth_resumption) {
   if (debug_visitor_ != nullptr) {
     debug_visitor_->OnResumeConnectionState(cached_network_params);
   }
-  return sent_packet_manager_.ResumeConnectionState(cached_network_params);
+  return sent_packet_manager_.ResumeConnectionState(cached_network_params,
+                                                    max_bandwidth_resumption);
 }
 
 void QuicConnection::SetNumOpenStreams(size_t num_streams) {
@@ -1905,6 +1907,7 @@
   if (debug_visitor_ != nullptr) {
     debug_visitor_->OnConnectionClosed(error, from_peer);
   }
+  DCHECK(visitor_ != nullptr);
   visitor_->OnConnectionClosed(error, from_peer);
   // Cancel the alarms so they don't trigger any action now that the
   // connection is closed.
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
index 2b2a363..4ad1d3e 100644
--- a/net/quic/quic_connection.h
+++ b/net/quic/quic_connection.h
@@ -270,7 +270,8 @@
   // Called by the Session when the client has provided CachedNetworkParameters.
   // Returns true if this changes the initial connection state.
   virtual bool ResumeConnectionState(
-      const CachedNetworkParameters& cached_network_params);
+      const CachedNetworkParameters& cached_network_params,
+      bool max_bandwidth_resumption);
 
   // Sets the number of active streams on the connection for congestion control.
   void SetNumOpenStreams(size_t num_streams);
diff --git a/net/quic/quic_connection_stats.h b/net/quic/quic_connection_stats.h
index 433085a..cd321a9 100644
--- a/net/quic/quic_connection_stats.h
+++ b/net/quic/quic_connection_stats.h
@@ -45,6 +45,9 @@
   QuicPacketCount packets_spuriously_retransmitted;
   // Number of packets abandoned as lost by the loss detection algorithm.
   QuicPacketCount packets_lost;
+
+  // Number of packets sent in slow start.
+  QuicPacketCount slowstart_packets_sent;
   // Number of packets lost exiting slow start.
   QuicPacketCount slowstart_packets_lost;
 
diff --git a/net/quic/quic_dispatcher.cc b/net/quic/quic_dispatcher.cc
index f682908..663092f8 100644
--- a/net/quic/quic_dispatcher.cc
+++ b/net/quic/quic_dispatcher.cc
@@ -13,7 +13,6 @@
 #include "net/quic/quic_connection_helper.h"
 #include "net/quic/quic_flags.h"
 #include "net/quic/quic_per_connection_packet_writer.h"
-#include "net/quic/quic_time_wait_list_manager.h"
 #include "net/quic/quic_utils.h"
 
 namespace net {
@@ -21,6 +20,8 @@
 using base::StringPiece;
 using std::make_pair;
 using std::find;
+using tools::QuicServerSession;
+using tools::QuicTimeWaitListManager;
 
 class DeleteSessionsAlarm : public QuicAlarm::Delegate {
  public:
@@ -213,7 +214,7 @@
 
   // The session that we have identified as the one to which this packet
   // belongs.
-  QuicSession* session = nullptr;
+  QuicServerSession* session = nullptr;
   QuicConnectionId connection_id = header.connection_id;
   SessionMap::iterator it = session_map_.find(connection_id);
   if (it == session_map_.end()) {
@@ -260,10 +261,10 @@
   return false;
 }
 
-QuicSession* QuicDispatcher::AdditionalValidityChecksThenCreateSession(
+QuicServerSession* QuicDispatcher::AdditionalValidityChecksThenCreateSession(
     const QuicPacketPublicHeader& header,
     QuicConnectionId connection_id) {
-  QuicSession* session = CreateQuicSession(
+  QuicServerSession* session = CreateQuicSession(
       connection_id, current_server_address_, current_client_address_);
 
   if (session == nullptr) {
@@ -336,7 +337,7 @@
 
 void QuicDispatcher::Shutdown() {
   while (!session_map_.empty()) {
-    QuicSession* session = session_map_.begin()->second;
+    QuicServerSession* session = session_map_.begin()->second;
     session->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
     // Validate that the session removes itself from the session map on close.
     DCHECK(session_map_.empty() || session_map_.begin()->second != session);
@@ -387,11 +388,11 @@
   DVLOG(1) << "Connection " << connection_id << " removed from time wait list.";
 }
 
-QuicSession* QuicDispatcher::CreateQuicSession(
+QuicServerSession* QuicDispatcher::CreateQuicSession(
     QuicConnectionId connection_id,
     const IPEndPoint& server_address,
     const IPEndPoint& client_address) {
-  // The QuicSession takes ownership of |connection| below.
+  // The QuicServerSession takes ownership of |connection| below.
   QuicConnection* connection = new QuicConnection(
       connection_id, client_address, helper_, connection_writer_factory_,
       /* owns_writer= */ true, Perspective::IS_SERVER,
diff --git a/net/quic/quic_dispatcher.h b/net/quic/quic_dispatcher.h
index c508cb8..2be3ca6 100644
--- a/net/quic/quic_dispatcher.h
+++ b/net/quic/quic_dispatcher.h
@@ -19,8 +19,8 @@
 #include "net/quic/quic_connection_helper.h"
 #include "net/quic/quic_protocol.h"
 #include "net/quic/quic_server_packet_writer.h"
-#include "net/quic/quic_server_session.h"
-#include "net/quic/quic_time_wait_list_manager.h"
+#include "net/tools/quic/quic_server_session.h"
+#include "net/tools/quic/quic_time_wait_list_manager.h"
 
 namespace net {
 namespace test {
@@ -30,7 +30,6 @@
 class DeleteSessionsAlarm;
 class QuicConfig;
 class QuicCryptoServerConfig;
-class QuicSession;
 
 class ProcessPacketInterface {
  public:
@@ -41,7 +40,7 @@
 };
 
 class QuicDispatcher : public QuicBlockedWriterInterface,
-                       public QuicServerSessionVisitor,
+                       public tools::QuicServerSessionVisitor,
                        public ProcessPacketInterface {
  public:
   // Creates per-connection packet writers out of the QuicDispatcher's shared
@@ -117,7 +116,8 @@
   void OnConnectionRemovedFromTimeWaitList(
       QuicConnectionId connection_id) override;
 
-  typedef base::hash_map<QuicConnectionId, QuicSession*> SessionMap;
+  typedef base::hash_map<QuicConnectionId,
+                         tools::QuicServerSession*> SessionMap;
 
   // Deletes all sessions on the closed session list and clears the list.
   void DeleteSessions();
@@ -125,9 +125,10 @@
   const SessionMap& session_map() const { return session_map_; }
 
  protected:
-  virtual QuicSession* CreateQuicSession(QuicConnectionId connection_id,
-                                         const IPEndPoint& server_address,
-                                         const IPEndPoint& client_address);
+  virtual tools::QuicServerSession* CreateQuicSession(
+      QuicConnectionId connection_id,
+      const IPEndPoint& server_address,
+      const IPEndPoint& client_address);
 
   // Called by |framer_visitor_| when the public header has been parsed.
   virtual bool OnUnauthenticatedPublicHeader(
@@ -138,17 +139,17 @@
   // certain simple processing rules.  This method may apply validity checks to
   // reject stray packets.  If the packet appears to be valid, it calls
   // CreateQuicSession to create a new session for the packet.  Returns the
-  // QuicSession that was created, or nullptr if the packet failed the validity
-  // checks.
-  virtual QuicSession* AdditionalValidityChecksThenCreateSession(
+  // QuicServerSession that was created, or nullptr if the packet failed the
+  // validity checks.
+  virtual tools::QuicServerSession* AdditionalValidityChecksThenCreateSession(
       const QuicPacketPublicHeader& header,
       QuicConnectionId connection_id);
 
   // Create and return the time wait list manager for this dispatcher, which
   // will be owned by the dispatcher as time_wait_list_manager_
-  virtual QuicTimeWaitListManager* CreateQuicTimeWaitListManager();
+  virtual tools::QuicTimeWaitListManager* CreateQuicTimeWaitListManager();
 
-  QuicTimeWaitListManager* time_wait_list_manager() {
+  tools::QuicTimeWaitListManager* time_wait_list_manager() {
     return time_wait_list_manager_.get();
   }
 
@@ -219,7 +220,7 @@
   SessionMap session_map_;
 
   // Entity that manages connection_ids in time wait state.
-  scoped_ptr<QuicTimeWaitListManager> time_wait_list_manager_;
+  scoped_ptr<tools::QuicTimeWaitListManager> time_wait_list_manager_;
 
   // The helper used for all connections. Owned by the server.
   QuicConnectionHelperInterface* helper_;
@@ -228,7 +229,7 @@
   scoped_ptr<QuicAlarm> delete_sessions_alarm_;
 
   // The list of closed but not-yet-deleted sessions.
-  std::list<QuicSession*> closed_session_list_;
+  std::list<tools::QuicServerSession*> closed_session_list_;
 
   // The writer to write to the socket with.
   scoped_ptr<QuicServerPacketWriter> writer_;
diff --git a/net/quic/quic_flags.cc b/net/quic/quic_flags.cc
index 9f64f60..b2c4294 100644
--- a/net/quic/quic_flags.cc
+++ b/net/quic/quic_flags.cc
@@ -25,10 +25,6 @@
 // connection options.
 bool FLAGS_quic_allow_bbr = false;
 
-// If true, enables the QUIC bandwidth resumption experiment (triggered by
-// Chrome/Finch).
-bool FLAGS_quic_enable_bandwidth_resumption_experiment = true;
-
 // If true, then the source address tokens generated for QUIC connects will
 // store multiple addresses.
 bool FLAGS_quic_use_multiple_address_in_source_tokens = false;
@@ -52,3 +48,7 @@
 
 // Do not retransmit data for streams that have been reset.
 bool FLAGS_quic_do_not_retransmit_for_reset_streams = true;
+
+// If true, use the peer's receive buffer size to set the max CWND used by the
+// send algorithms.
+bool FLAGS_quic_limit_max_cwnd_to_receive_buffer = true;
diff --git a/net/quic/quic_flags.h b/net/quic/quic_flags.h
index 99b052d..cb52279 100644
--- a/net/quic/quic_flags.h
+++ b/net/quic/quic_flags.h
@@ -15,12 +15,11 @@
 NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_bbr_congestion_control;
 NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_bbr;
 NET_EXPORT_PRIVATE extern bool FLAGS_quic_too_many_outstanding_packets;
-NET_EXPORT_PRIVATE
-extern bool FLAGS_quic_enable_bandwidth_resumption_experiment;
 NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_multiple_address_in_source_tokens;
 NET_EXPORT_PRIVATE extern int64 FLAGS_quic_time_wait_list_seconds;
 NET_EXPORT_PRIVATE extern int64 FLAGS_quic_time_wait_list_max_connections;
 NET_EXPORT_PRIVATE extern bool FLAGS_quic_small_default_packet_size;
 NET_EXPORT_PRIVATE extern bool FLAGS_quic_do_not_retransmit_for_reset_streams;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_limit_max_cwnd_to_receive_buffer;
 
 #endif  // NET_QUIC_QUIC_FLAGS_H_
diff --git a/net/quic/quic_headers_stream.cc b/net/quic/quic_headers_stream.cc
index 435267b..48fb3a01c 100644
--- a/net/quic/quic_headers_stream.cc
+++ b/net/quic/quic_headers_stream.cc
@@ -92,6 +92,10 @@
     CloseConnection("SPDY DATA frame received.");
   }
 
+  void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {
+    CloseConnection("SPDY frame padding received.");
+  }
+
   void OnError(SpdyFramer* framer) override {
     CloseConnection(base::StringPrintf(
         "SPDY framing error: %s",
diff --git a/net/quic/quic_headers_stream_test.cc b/net/quic/quic_headers_stream_test.cc
index 1016f16..3a2c7c4 100644
--- a/net/quic/quic_headers_stream_test.cc
+++ b/net/quic/quic_headers_stream_test.cc
@@ -36,6 +36,7 @@
                                        const char* data,
                                        size_t len,
                                        bool fin));
+  MOCK_METHOD2(OnStreamPadding, void(SpdyStreamId stream_id, size_t len));
   MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id,
                                               const char* header_data,
                                               size_t len));
diff --git a/net/quic/quic_in_memory_cache.cc b/net/quic/quic_in_memory_cache.cc
deleted file mode 100644
index 31e346f6..0000000
--- a/net/quic/quic_in_memory_cache.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/quic_in_memory_cache.h"
-
-#include "base/files/file_enumerator.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "net/http/http_util.h"
-#include "url/gurl.h"
-
-using base::FilePath;
-using base::StringPiece;
-using std::string;
-
-// Specifies the directory used during QuicInMemoryCache
-// construction to seed the cache. Cache directory can be
-// generated using `wget -p --save-headers <url>
-
-namespace net {
-
-FilePath::StringType g_quic_in_memory_cache_dir = FILE_PATH_LITERAL("");
-
-QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {
-}
-
-QuicInMemoryCache::Response::~Response() {
-}
-
-// static
-QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
-  return Singleton<QuicInMemoryCache>::get();
-}
-
-const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
-    const GURL& url) const {
-  ResponseMap::const_iterator it = responses_.find(GetKey(url));
-  if (it == responses_.end()) {
-    return nullptr;
-  }
-  return it->second;
-}
-
-void QuicInMemoryCache::AddSimpleResponse(StringPiece path,
-                                          StringPiece version,
-                                          StringPiece response_code,
-                                          StringPiece response_detail,
-                                          StringPiece body) {
-  GURL url("http://" + path.as_string());
-
-  string status_line = version.as_string() + " " +
-                       response_code.as_string() + " " +
-                       response_detail.as_string();
-
-  string header = "content-length: " +
-                  base::Uint64ToString(static_cast<uint64>(body.length()));
-
-  scoped_refptr<HttpResponseHeaders> response_headers =
-      new HttpResponseHeaders(status_line + '\0' + header + '\0' + '\0');
-
-  AddResponse(url, response_headers, body);
-}
-
-void QuicInMemoryCache::AddResponse(
-    const GURL& url,
-    scoped_refptr<HttpResponseHeaders> response_headers,
-    StringPiece response_body) {
-  string key = GetKey(url);
-  VLOG(1) << "Adding response for: " << key;
-  if (ContainsKey(responses_, key)) {
-    LOG(DFATAL) << "Response for given request already exists!";
-    return;
-  }
-  Response* new_response = new Response();
-  new_response->set_headers(response_headers);
-  new_response->set_body(response_body);
-  responses_[key] = new_response;
-}
-
-void QuicInMemoryCache::AddSpecialResponse(StringPiece path,
-                                           SpecialResponseType response_type) {
-  GURL url("http://" + path.as_string());
-
-  AddResponse(url, nullptr, string());
-  responses_[GetKey(url)]->response_type_ = response_type;
-}
-
-QuicInMemoryCache::QuicInMemoryCache() {
-  Initialize();
-}
-
-void QuicInMemoryCache::ResetForTests() {
-  STLDeleteValues(&responses_);
-  Initialize();
-}
-
-void QuicInMemoryCache::Initialize() {
-  // If there's no defined cache dir, we have no initialization to do.
-  if (g_quic_in_memory_cache_dir.size() == 0) {
-    VLOG(1) << "No cache directory found. Skipping initialization.";
-    return;
-  }
-  VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
-          << g_quic_in_memory_cache_dir;
-
-  FilePath directory(g_quic_in_memory_cache_dir);
-  base::FileEnumerator file_list(directory,
-                                 true,
-                                 base::FileEnumerator::FILES);
-
-  FilePath file = file_list.Next();
-  for (; !file.empty(); file = file_list.Next()) {
-    // Need to skip files in .svn directories
-    if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) {
-      continue;
-    }
-
-    string file_contents;
-    base::ReadFileToString(file, &file_contents);
-
-    if (file_contents.length() > INT_MAX) {
-      LOG(WARNING) << "File '" << file.value() << "' too large: "
-                   << file_contents.length();
-      continue;
-    }
-    int file_len = static_cast<int>(file_contents.length());
-
-    int headers_end = HttpUtil::LocateEndOfHeaders(file_contents.data(),
-                                                   file_len);
-    if (headers_end < 1) {
-      LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file.value();
-      continue;
-    }
-
-    string raw_headers =
-        HttpUtil::AssembleRawHeaders(file_contents.data(), headers_end);
-
-    scoped_refptr<HttpResponseHeaders> response_headers =
-        new HttpResponseHeaders(raw_headers);
-
-    string base;
-    if (response_headers->GetNormalizedHeader("X-Original-Url", &base)) {
-      response_headers->RemoveHeader("X-Original-Url");
-      // Remove the protocol so we can add it below.
-      if (StartsWithASCII(base, "https://", false)) {
-        base = base.substr(8);
-      } else if (StartsWithASCII(base, "http://", false)) {
-        base = base.substr(7);
-      }
-    } else {
-      base = file.AsUTF8Unsafe();
-    }
-    if (base.length() == 0 || base[0] == '/') {
-      LOG(DFATAL) << "Invalid path, ignoring: " << base;
-      continue;
-    }
-    if (base[base.length() - 1] == ',') {
-      base = base.substr(0, base.length() - 1);
-    }
-
-    GURL url("http://" + base);
-
-    VLOG(1) << "Inserting '" << GetKey(url) << "' into QuicInMemoryCache.";
-
-    StringPiece body(file_contents.data() + headers_end,
-                     file_contents.size() - headers_end);
-
-    AddResponse(url, response_headers, body);
-  }
-}
-
-QuicInMemoryCache::~QuicInMemoryCache() {
-  STLDeleteValues(&responses_);
-}
-
-string QuicInMemoryCache::GetKey(const GURL& url) const {
-  // Take everything but the scheme portion of the URL.
-  return url.host() + url.PathForRequest();
-}
-
-}  // namespace net
diff --git a/net/quic/quic_in_memory_cache.h b/net/quic/quic_in_memory_cache.h
deleted file mode 100644
index ee3a9de..0000000
--- a/net/quic/quic_in_memory_cache.h
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_QUIC_IN_MEMORY_CACHE_H_
-#define NET_QUIC_QUIC_IN_MEMORY_CACHE_H_
-
-#include <string>
-
-#include "base/containers/hash_tables.h"
-#include "base/files/file_util.h"
-#include "base/memory/singleton.h"
-#include "base/strings/string_piece.h"
-#include "net/http/http_response_headers.h"
-
-template <typename T> struct DefaultSingletonTraits;
-class GURL;
-
-namespace net {
-
-extern base::FilePath::StringType g_quic_in_memory_cache_dir;
-
-namespace test {
-class QuicInMemoryCachePeer;
-}  // namespace
-
-class QuicServer;
-
-// In-memory cache for HTTP responses.
-// Reads from disk cache generated by:
-// `wget -p --save_headers <url>`
-class QuicInMemoryCache {
- public:
-  enum SpecialResponseType {
-    REGULAR_RESPONSE,  // Send the headers and body like a server should.
-    CLOSE_CONNECTION,  // Close the connection (sending the close packet).
-    IGNORE_REQUEST,    // Do nothing, expect the client to time out.
-  };
-
-  // Container for response header/body pairs.
-  class Response {
-   public:
-    Response();
-    ~Response();
-
-    SpecialResponseType response_type() const { return response_type_; }
-    const HttpResponseHeaders& headers() const { return *headers_.get(); }
-    const base::StringPiece body() const { return base::StringPiece(body_); }
-
-   private:
-    friend class QuicInMemoryCache;
-
-    void set_headers(scoped_refptr<HttpResponseHeaders> headers) {
-      headers_ = headers;
-    }
-    void set_body(base::StringPiece body) {
-      body.CopyToString(&body_);
-    }
-
-    SpecialResponseType response_type_;
-    scoped_refptr<HttpResponseHeaders> headers_;
-    std::string body_;
-
-    DISALLOW_COPY_AND_ASSIGN(Response);
-  };
-
-  // Returns the singleton instance of the cache.
-  static QuicInMemoryCache* GetInstance();
-
-  // Retrieve a response from this cache for a given request.
-  // If no appropriate response exists, nullptr is returned.
-  // Currently, responses are selected based on request URI only.
-  const Response* GetResponse(const GURL& url) const;
-
-  // Adds a simple response to the cache.  The response headers will
-  // only contain the "content-length" header with the length of |body|.
-  void AddSimpleResponse(base::StringPiece path,
-                         base::StringPiece version,
-                         base::StringPiece response_code,
-                         base::StringPiece response_detail,
-                         base::StringPiece body);
-
-  // Add a response to the cache.
-  void AddResponse(const GURL& url,
-                   scoped_refptr<HttpResponseHeaders> response_headers,
-                   base::StringPiece response_body);
-
-  // Simulate a special behavior at a particular path.
-  void AddSpecialResponse(base::StringPiece path,
-                          SpecialResponseType response_type);
-
- private:
-  typedef base::hash_map<std::string, Response*> ResponseMap;
-  friend struct DefaultSingletonTraits<QuicInMemoryCache>;
-  friend class test::QuicInMemoryCachePeer;
-
-  QuicInMemoryCache();
-  ~QuicInMemoryCache();
-
-  void ResetForTests();
-
-  void Initialize();
-
-  std::string GetKey(const GURL& url) const;
-
-  // Cached responses.
-  ResponseMap responses_;
-
-  DISALLOW_COPY_AND_ASSIGN(QuicInMemoryCache);
-};
-
-}  // namespace net
-
-#endif  // NET_QUIC_QUIC_IN_MEMORY_CACHE_H_
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 80de2f5..2e5382a 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -527,6 +527,53 @@
   SendRequestAndExpectQuicResponse("hello!");
 }
 
+TEST_P(QuicNetworkTransactionTest, ConfirmAlternativeService) {
+  MockRead http_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"),
+      MockRead(kQuicAlternateProtocolHttpHeader),
+      MockRead("hello world"),
+      MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+      MockRead(ASYNC, OK)};
+
+  StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr,
+                                     0);
+  socket_factory_.AddSocketDataProvider(&http_data);
+
+  MockQuicData mock_quic_data;
+  mock_quic_data.AddWrite(
+      ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+                                    GetRequestHeaders("GET", "http", "/")));
+  mock_quic_data.AddRead(ConstructResponseHeadersPacket(
+      1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
+  mock_quic_data.AddRead(
+      ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+  mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+  mock_quic_data.AddRead(SYNCHRONOUS, 0);  // EOF
+
+  mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
+
+  // The non-alternate protocol job needs to hang in order to guarantee that
+  // the alternate-protocol job will "win".
+  AddHangingNonAlternateProtocolSocketData();
+
+  CreateSessionWithNextProtos();
+
+  AlternativeService alternative_service(QUIC,
+                                         HostPortPair::FromURL(request_.url));
+  session_->http_server_properties()->MarkAlternativeServiceRecentlyBroken(
+      alternative_service);
+  EXPECT_TRUE(
+      session_->http_server_properties()->WasAlternativeServiceRecentlyBroken(
+          alternative_service));
+
+  SendRequestAndExpectHttpResponse("hello world");
+  SendRequestAndExpectQuicResponse("hello!");
+
+  EXPECT_FALSE(
+      session_->http_server_properties()->WasAlternativeServiceRecentlyBroken(
+          alternative_service));
+}
+
 TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolProbabilityForQuic) {
   MockRead http_reads[] = {
     MockRead("HTTP/1.1 200 OK\r\n"),
diff --git a/net/quic/quic_per_connection_packet_writer.cc b/net/quic/quic_per_connection_packet_writer.cc
deleted file mode 100644
index 3882da8e..0000000
--- a/net/quic/quic_per_connection_packet_writer.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/quic_per_connection_packet_writer.h"
-
-#include "net/quic/quic_server_packet_writer.h"
-
-namespace net {
-
-QuicPerConnectionPacketWriter::QuicPerConnectionPacketWriter(
-    QuicServerPacketWriter* shared_writer,
-    QuicConnection* connection)
-    : shared_writer_(shared_writer),
-      connection_(connection),
-      weak_factory_(this){
-}
-
-QuicPerConnectionPacketWriter::~QuicPerConnectionPacketWriter() {
-}
-
-QuicPacketWriter* QuicPerConnectionPacketWriter::shared_writer() const {
-  return shared_writer_;
-}
-
-WriteResult QuicPerConnectionPacketWriter::WritePacket(
-    const char* buffer,
-    size_t buf_len,
-    const IPAddressNumber& self_address,
-    const IPEndPoint& peer_address) {
-  return shared_writer_->WritePacketWithCallback(
-      buffer,
-      buf_len,
-      self_address,
-      peer_address,
-      base::Bind(&QuicPerConnectionPacketWriter::OnWriteComplete,
-                 weak_factory_.GetWeakPtr()));
-}
-
-bool QuicPerConnectionPacketWriter::IsWriteBlockedDataBuffered() const {
-  return shared_writer_->IsWriteBlockedDataBuffered();
-}
-
-bool QuicPerConnectionPacketWriter::IsWriteBlocked() const {
-  return shared_writer_->IsWriteBlocked();
-}
-
-void QuicPerConnectionPacketWriter::SetWritable() {
-  shared_writer_->SetWritable();
-}
-
-void QuicPerConnectionPacketWriter::OnWriteComplete(WriteResult result) {
-  if (result.status == WRITE_STATUS_ERROR) {
-    connection_->OnWriteError(result.error_code);
-  }
-}
-
-}  // namespace net
diff --git a/net/quic/quic_sent_packet_manager.cc b/net/quic/quic_sent_packet_manager.cc
index 1694da2..829b9cb 100644
--- a/net/quic/quic_sent_packet_manager.cc
+++ b/net/quic/quic_sent_packet_manager.cc
@@ -172,6 +172,10 @@
     receive_buffer_bytes_ =
         max(kMinSocketReceiveBuffer,
             static_cast<QuicByteCount>(config.ReceivedSocketReceiveBuffer()));
+    if (FLAGS_quic_limit_max_cwnd_to_receive_buffer) {
+      send_algorithm_->SetMaxCongestionWindow(receive_buffer_bytes_ *
+                                              kUsableRecieveBufferFraction);
+    }
   }
   send_algorithm_->SetFromConfig(config, perspective_, using_pacing_);
 
@@ -181,7 +185,8 @@
 }
 
 bool QuicSentPacketManager::ResumeConnectionState(
-    const CachedNetworkParameters& cached_network_params) {
+    const CachedNetworkParameters& cached_network_params,
+    bool max_bandwidth_resumption) {
   if (cached_network_params.has_min_rtt_ms()) {
     uint32 initial_rtt_us =
         kNumMicrosPerMilli * cached_network_params.min_rtt_ms();
@@ -189,7 +194,8 @@
         max(kMinInitialRoundTripTimeUs,
             min(kMaxInitialRoundTripTimeUs, initial_rtt_us)));
   }
-  return send_algorithm_->ResumeConnectionState(cached_network_params);
+  return send_algorithm_->ResumeConnectionState(cached_network_params,
+                                                max_bandwidth_resumption);
 }
 
 void QuicSentPacketManager::SetNumOpenStreams(size_t num_streams) {
@@ -791,8 +797,9 @@
   if (pending_timer_transmission_count_ > 0) {
     return QuicTime::Delta::Zero();
   }
-  if (unacked_packets_.bytes_in_flight() >=
-      kUsableRecieveBufferFraction * receive_buffer_bytes_) {
+  if (!FLAGS_quic_limit_max_cwnd_to_receive_buffer &&
+      unacked_packets_.bytes_in_flight() >=
+          kUsableRecieveBufferFraction * receive_buffer_bytes_) {
     return QuicTime::Delta::Infinite();
   }
   return send_algorithm_->TimeUntilSend(
diff --git a/net/quic/quic_sent_packet_manager.h b/net/quic/quic_sent_packet_manager.h
index 7483abf..06fcfc8b 100644
--- a/net/quic/quic_sent_packet_manager.h
+++ b/net/quic/quic_sent_packet_manager.h
@@ -106,7 +106,8 @@
   // Pass the CachedNetworkParameters to the send algorithm.
   // Returns true if this changes the initial connection state.
   bool ResumeConnectionState(
-      const CachedNetworkParameters& cached_network_params);
+      const CachedNetworkParameters& cached_network_params,
+      bool max_bandwidth_resumption);
 
   void SetNumOpenStreams(size_t num_streams);
 
diff --git a/net/quic/quic_sent_packet_manager_test.cc b/net/quic/quic_sent_packet_manager_test.cc
index dad154b6..c3ef4d5 100644
--- a/net/quic/quic_sent_packet_manager_test.cc
+++ b/net/quic/quic_sent_packet_manager_test.cc
@@ -1637,6 +1637,10 @@
   QuicConfig client_config;
   QuicConfigPeer::SetReceivedSocketReceiveBuffer(&client_config, 1024);
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _, _));
+  if (FLAGS_quic_limit_max_cwnd_to_receive_buffer) {
+    EXPECT_CALL(*send_algorithm_,
+                SetMaxCongestionWindow(kMinSocketReceiveBuffer * 0.95));
+  }
   EXPECT_CALL(*send_algorithm_, PacingRate())
       .WillRepeatedly(Return(QuicBandwidth::Zero()));
   EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange());
@@ -1659,6 +1663,10 @@
     manager_.OnPacketSent(&packet, 0, clock_.Now(), 1024,
                           NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
   }
+  if (FLAGS_quic_limit_max_cwnd_to_receive_buffer) {
+    EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _))
+        .WillOnce(Return(QuicTime::Delta::Infinite()));
+  }
   EXPECT_EQ(QuicTime::Delta::Infinite(),
             manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
 }
@@ -1680,6 +1688,10 @@
     manager_.OnPacketSent(&packet, 0, clock_.Now(), 1024,
                           NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
   }
+  if (FLAGS_quic_limit_max_cwnd_to_receive_buffer) {
+    EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _))
+        .WillOnce(Return(QuicTime::Delta::Infinite()));
+  }
   EXPECT_EQ(QuicTime::Delta::Infinite(),
             manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
 }
@@ -1707,8 +1719,8 @@
   CachedNetworkParameters cached_network_params;
   cached_network_params.set_min_rtt_ms(kRttMs);
 
-  EXPECT_CALL(*send_algorithm_, ResumeConnectionState(_));
-  manager_.ResumeConnectionState(cached_network_params);
+  EXPECT_CALL(*send_algorithm_, ResumeConnectionState(_, false));
+  manager_.ResumeConnectionState(cached_network_params, false);
   EXPECT_EQ(kRttMs * kNumMicrosPerMilli,
             static_cast<uint64>(manager_.GetRttStats()->initial_rtt_us()));
 }
diff --git a/net/quic/quic_server_session.cc b/net/quic/quic_server_session.cc
index 796d53e..4545b08 100644
--- a/net/quic/quic_server_session.cc
+++ b/net/quic/quic_server_session.cc
@@ -47,11 +47,14 @@
   // bandwidth resumption.
   const CachedNetworkParameters* cached_network_params =
       crypto_stream_->previous_cached_network_params();
-  if (FLAGS_quic_enable_bandwidth_resumption_experiment &&
-      cached_network_params != nullptr &&
-      ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWRE) &&
+  const bool max_bandwidth_resumption =
+      ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWMX);
+  if (cached_network_params != nullptr &&
+      (ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWRE) ||
+       max_bandwidth_resumption) &&
       cached_network_params->serving_region() == serving_region_) {
-    connection()->ResumeConnectionState(*cached_network_params);
+    connection()->ResumeConnectionState(*cached_network_params,
+                                        max_bandwidth_resumption);
   }
 
   if (FLAGS_enable_quic_fec &&
diff --git a/net/quic/quic_server_session.h b/net/quic/quic_server_session.h
deleted file mode 100644
index 44c20b93..0000000
--- a/net/quic/quic_server_session.h
+++ /dev/null
@@ -1,119 +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.
-//
-// A server specific QuicSession subclass.
-
-#ifndef NET_QUIC_QUIC_SERVER_SESSION_H_
-#define NET_QUIC_QUIC_SERVER_SESSION_H_
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/containers/hash_tables.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/quic/quic_crypto_server_stream.h"
-#include "net/quic/quic_per_connection_packet_writer.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_session.h"
-
-namespace net {
-
-namespace test {
-class QuicServerSessionPeer;
-}  // namespace test
-
-class QuicBlockedWriterInterface;
-class QuicConfig;
-class QuicConnection;
-class QuicCryptoServerConfig;
-class ReliableQuicStream;
-
-// An interface from the session to the entity owning the session.
-// This lets the session notify its owner (the Dispatcher) when the connection
-// is closed, blocked, or added/removed from the time-wait list.
-class QuicServerSessionVisitor {
- public:
-  virtual ~QuicServerSessionVisitor() {}
-
-  virtual void OnConnectionClosed(QuicConnectionId connection_id,
-                                  QuicErrorCode error) = 0;
-  virtual void OnWriteBlocked(QuicBlockedWriterInterface* blocked_writer) = 0;
-  // Called after the given connection is added to the time-wait list.
-  virtual void OnConnectionAddedToTimeWaitList(QuicConnectionId connection_id) {
-  }
-  // Called after the given connection is removed from the time-wait list.
-  virtual void OnConnectionRemovedFromTimeWaitList(
-      QuicConnectionId connection_id) {}
-};
-
-class QuicServerSession : public QuicSession {
- public:
-  QuicServerSession(const QuicConfig& config,
-                    QuicConnection* connection,
-                    QuicServerSessionVisitor* visitor);
-
-  // Override the base class to notify the owner of the connection close.
-  void OnConnectionClosed(QuicErrorCode error, bool from_peer) override;
-  void OnWriteBlocked() override;
-
-  // Sends a server config update to the client, containing new bandwidth
-  // estimate.
-  void OnCongestionWindowChange(QuicTime now) override;
-
-  ~QuicServerSession() override;
-
-  virtual void InitializeSession(const QuicCryptoServerConfig* crypto_config);
-
-  const QuicCryptoServerStream* crypto_stream() const {
-    return crypto_stream_.get();
-  }
-
-  // Override base class to process FEC config received from client.
-  void OnConfigNegotiated() override;
-
-  void set_serving_region(std::string serving_region) {
-    serving_region_ = serving_region;
-  }
-
- protected:
-  // QuicSession methods:
-  QuicDataStream* CreateIncomingDataStream(QuicStreamId id) override;
-  QuicDataStream* CreateOutgoingDataStream() override;
-  QuicCryptoServerStream* GetCryptoStream() override;
-
-  // If we should create an incoming stream, returns true. Otherwise
-  // does error handling, including communicating the error to the client and
-  // possibly closing the connection, and returns false.
-  virtual bool ShouldCreateIncomingDataStream(QuicStreamId id);
-
-  virtual QuicCryptoServerStream* CreateQuicCryptoServerStream(
-      const QuicCryptoServerConfig* crypto_config);
-
- private:
-  friend class test::QuicServerSessionPeer;
-
-  scoped_ptr<QuicCryptoServerStream> crypto_stream_;
-  QuicServerSessionVisitor* visitor_;
-
-  // The most recent bandwidth estimate sent to the client.
-  QuicBandwidth bandwidth_estimate_sent_to_client_;
-
-  // Text describing server location. Sent to the client as part of the bandwith
-  // estimate in the source-address token. Optional, can be left empty.
-  std::string serving_region_;
-
-  // Time at which we send the last SCUP to the client.
-  QuicTime last_scup_time_;
-
-  // Number of packets sent to the peer, at the time we last sent a SCUP.
-  int64 last_scup_sequence_number_;
-
-  DISALLOW_COPY_AND_ASSIGN(QuicServerSession);
-};
-
-}  // namespace net
-
-#endif  // NET_QUIC_QUIC_SERVER_SESSION_H_
diff --git a/net/quic/quic_server_test.cc b/net/quic/quic_server_test.cc
index 9e4e894..cac66b6 100644
--- a/net/quic/quic_server_test.cc
+++ b/net/quic/quic_server_test.cc
@@ -24,9 +24,9 @@
       : crypto_config_("blah", QuicRandom::GetInstance()),
         dispatcher_(config_,
                     crypto_config_,
-                    new QuicDispatcher::DefaultPacketWriterFactory(),
-                    &helper_) {
-    dispatcher_.Initialize(nullptr);
+                    new tools::QuicDispatcher::DefaultPacketWriterFactory(),
+                    new MockHelper) {
+    dispatcher_.InitializeWithWriter(nullptr);
   }
 
   void DispatchPacket(const QuicEncryptedPacket& packet) {
@@ -37,7 +37,6 @@
  protected:
   QuicConfig config_;
   QuicCryptoServerConfig crypto_config_;
-  MockHelper helper_;
   MockQuicDispatcher dispatcher_;
 };
 
diff --git a/net/quic/quic_spdy_server_stream.cc b/net/quic/quic_spdy_server_stream.cc
deleted file mode 100644
index b0f8abb..0000000
--- a/net/quic/quic_spdy_server_stream.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/quic_spdy_server_stream.h"
-
-#include "base/memory/singleton.h"
-#include "base/strings/string_number_conversions.h"
-#include "net/quic/quic_in_memory_cache.h"
-#include "net/quic/quic_session.h"
-#include "net/quic/spdy_utils.h"
-#include "net/spdy/spdy_framer.h"
-#include "net/spdy/spdy_header_block.h"
-#include "net/spdy/spdy_http_utils.h"
-
-using base::StringPiece;
-using std::string;
-
-namespace net {
-
-static const size_t kHeaderBufInitialSize = 4096;
-
-QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id,
-                                           QuicSession* session)
-    : QuicDataStream(id, session),
-      read_buf_(new GrowableIOBuffer()),
-      request_headers_received_(false) {
-  read_buf_->SetCapacity(kHeaderBufInitialSize);
-}
-
-QuicSpdyServerStream::~QuicSpdyServerStream() {
-}
-
-uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 data_len) {
-  if (data_len > INT_MAX) {
-    LOG(DFATAL) << "Data length too long: " << data_len;
-    return 0;
-  }
-  // Are we still reading the request headers.
-  if (!request_headers_received_) {
-    // Grow the read buffer if necessary.
-    while (read_buf_->RemainingCapacity() < static_cast<int>(data_len)) {
-      read_buf_->SetCapacity(read_buf_->capacity() * 2);
-    }
-    memcpy(read_buf_->data(), data, data_len);
-    read_buf_->set_offset(read_buf_->offset() + data_len);
-    // Try parsing the request headers. This will set request_headers_received_
-    // if successful; if not, it will be tried again with more data.
-    ParseRequestHeaders();
-  } else {
-    body_.append(data, data_len);
-  }
-  return data_len;
-}
-
-void QuicSpdyServerStream::OnFinRead() {
-  ReliableQuicStream::OnFinRead();
-  if (write_side_closed() || fin_buffered()) {
-    return;
-  }
-
-  if (!request_headers_received_) {
-    SendErrorResponse();  // We're not done reading headers.
-    return;
-  }
-
-  SpdyHeaderBlock::const_iterator it = headers_.find("content-length");
-  size_t content_length;
-  if (it != headers_.end() &&
-      (!base::StringToSizeT(it->second, &content_length) ||
-       body_.size() != content_length)) {
-    SendErrorResponse();  // Invalid content length
-    return;
-  }
-
-  SendResponse();
-}
-
-// Try parsing the request headers. If successful, sets
-// request_headers_received_. If not successful, it can just be tried again once
-// there's more data.
-void QuicSpdyServerStream::ParseRequestHeaders() {
-  SpdyFramer framer((kDefaultSpdyMajorVersion));
-  const char* data = read_buf_->StartOfBuffer();
-  size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
-  size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_len, &headers_);
-  if (len == 0) {
-    // Not enough data yet, presumably. (If we still don't succeed by the end of
-    // the stream, then we'll error in OnFinRead().)
-    return;
-  }
-
-  // Headers received and parsed: extract the request URL.
-  request_url_ = GetUrlFromHeaderBlock(headers_,
-                                       kDefaultSpdyMajorVersion,
-                                       false);
-  if (!request_url_.is_valid()) {
-    SendErrorResponse();
-    return;
-  }
-
-  // Add any data past the headers to the request body.
-  size_t delta = read_buf_len - len;
-  if (delta > 0) {
-    body_.append(data + len, delta);
-  }
-
-  request_headers_received_ = true;
-}
-
-void QuicSpdyServerStream::SendResponse() {
-  // Find response in cache. If not found, send error response.
-  const QuicInMemoryCache::Response* response =
-      QuicInMemoryCache::GetInstance()->GetResponse(request_url_);
-  if (response == nullptr) {
-    SendErrorResponse();
-    return;
-  }
-
-  if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) {
-    DVLOG(1) << "Special response: closing connection.";
-    CloseConnection(QUIC_NO_ERROR);
-    return;
-  }
-
-  if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) {
-    DVLOG(1) << "Special response: ignoring request.";
-    return;
-  }
-
-  DVLOG(1) << "Sending response for stream " << id();
-  SendHeadersAndBody(response->headers(), response->body());
-}
-
-void QuicSpdyServerStream::SendErrorResponse() {
-  DVLOG(1) << "Sending error response for stream " << id();
-  scoped_refptr<HttpResponseHeaders> headers
-      = new HttpResponseHeaders(string("HTTP/1.1 500 Server Error") + '\0' +
-                                "content-length: 3" + '\0' + '\0');
-  SendHeadersAndBody(*headers.get(), "bad");
-}
-
-void QuicSpdyServerStream::SendHeadersAndBody(
-    const HttpResponseHeaders& response_headers,
-    StringPiece body) {
-  // We only support SPDY and HTTP, and neither handles bidirectional streaming.
-  if (!read_side_closed()) {
-    CloseReadSide();
-  }
-
-  SpdyHeaderBlock header_block;
-  CreateSpdyHeadersFromHttpResponse(response_headers,
-                                    kDefaultSpdyMajorVersion,
-                                    &header_block);
-
-  WriteHeaders(header_block, body.empty(), nullptr);
-
-  if (!body.empty()) {
-    WriteOrBufferData(body, true, nullptr);
-  }
-}
-
-}  // namespace net
diff --git a/net/quic/quic_spdy_server_stream.h b/net/quic/quic_spdy_server_stream.h
deleted file mode 100644
index a8bd9f3..0000000
--- a/net/quic/quic_spdy_server_stream.h
+++ /dev/null
@@ -1,66 +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 NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_
-#define NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "net/base/io_buffer.h"
-#include "net/quic/quic_data_stream.h"
-#include "net/quic/quic_protocol.h"
-#include "url/gurl.h"
-
-namespace net {
-
-class HttpResponseHeaders;
-class QuicSession;
-
-namespace test {
-class QuicSpdyServerStreamPeer;
-}  // namespace test
-
-// All this does right now is aggregate data, and on fin, send an HTTP
-// response.
-class QuicSpdyServerStream : public QuicDataStream {
- public:
-  QuicSpdyServerStream(QuicStreamId id, QuicSession* session);
-  ~QuicSpdyServerStream() override;
-
-  // ReliableQuicStream implementation called by the session when there's
-  // data for us.
-  uint32 ProcessData(const char* data, uint32 data_len) override;
-  void OnFinRead() override;
-
-  void ParseRequestHeaders();
-
- private:
-  friend class test::QuicSpdyServerStreamPeer;
-
-  // Sends a basic 200 response using SendHeaders for the headers and WriteData
-  // for the body.
-  void SendResponse();
-
-  // Sends a basic 500 response using SendHeaders for the headers and WriteData
-  // for the body
-  void SendErrorResponse();
-
-  void SendHeadersAndBody(const HttpResponseHeaders& response_headers,
-                          base::StringPiece body);
-
-  SpdyHeaderBlock headers_;
-  std::string body_;
-  GURL request_url_;
-
-  // Buffer into which response header data is read.
-  scoped_refptr<GrowableIOBuffer> read_buf_;
-  bool request_headers_received_;
-
-  DISALLOW_COPY_AND_ASSIGN(QuicSpdyServerStream);
-};
-
-}  // namespace net
-
-#endif  // NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index cbcd45e..78ccf104 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -1134,7 +1134,10 @@
     return;
 
   const QuicConnectionStats& stats = session->connection()->GetStats();
+  const AlternativeService alternative_service(QUIC,
+                                               server_id.host_port_pair());
   if (session->IsCryptoHandshakeConfirmed()) {
+    http_server_properties_->ConfirmAlternativeService(alternative_service);
     ServerNetworkStats network_stats;
     network_stats.srtt = base::TimeDelta::FromMicroseconds(stats.srtt_us);
     network_stats.bandwidth_estimate = stats.estimated_bandwidth;
@@ -1161,9 +1164,8 @@
   // job also fails. So to avoid not using QUIC when we otherwise could, we mark
   // it as recently broken, which means that 0-RTT will be disabled but we'll
   // still race.
-  const HostPortPair& server = server_id.host_port_pair();
   http_server_properties_->MarkAlternativeServiceRecentlyBroken(
-      AlternativeService(QUIC, server.host(), server.port()));
+      alternative_service);
 }
 
 }  // namespace net
diff --git a/net/quic/quic_time_wait_list_manager.cc b/net/quic/quic_time_wait_list_manager.cc
index a5384b7..0311913 100644
--- a/net/quic/quic_time_wait_list_manager.cc
+++ b/net/quic/quic_time_wait_list_manager.cc
@@ -81,7 +81,7 @@
     QuicConnectionHelperInterface* helper,
     const QuicVersionVector& supported_versions)
     : helper_(helper),
-      kTimeWaitPeriod_(
+      time_wait_period_(
           QuicTime::Delta::FromSeconds(FLAGS_quic_time_wait_list_seconds)),
       connection_id_clean_up_alarm_(
           helper_->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
@@ -254,14 +254,14 @@
   if (!connection_id_map_.empty()) {
     QuicTime oldest_connection_id =
         connection_id_map_.begin()->second.time_added;
-    if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
-      next_alarm_time = oldest_connection_id.Add(kTimeWaitPeriod_);
+    if (now.Subtract(oldest_connection_id) < time_wait_period_) {
+      next_alarm_time = oldest_connection_id.Add(time_wait_period_);
     } else {
       LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod";
     }
   } else {
-    // No connection_ids added so none will expire before kTimeWaitPeriod_.
-    next_alarm_time = now.Add(kTimeWaitPeriod_);
+    // No connection_ids added so none will expire before time_wait_period_.
+    next_alarm_time = now.Add(time_wait_period_);
   }
 
   connection_id_clean_up_alarm_->Set(next_alarm_time);
@@ -288,7 +288,7 @@
 
 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
   QuicTime now = helper_->GetClock()->ApproximateNow();
-  QuicTime expiration = now.Subtract(kTimeWaitPeriod_);
+  QuicTime expiration = now.Subtract(time_wait_period_);
 
   while (MaybeExpireOldestConnection(expiration)) {
   }
diff --git a/net/quic/quic_time_wait_list_manager.h b/net/quic/quic_time_wait_list_manager.h
index 0fff158..3a4bafc 100644
--- a/net/quic/quic_time_wait_list_manager.h
+++ b/net/quic/quic_time_wait_list_manager.h
@@ -31,7 +31,7 @@
 }  // namespace test
 
 // Maintains a list of all connection_ids that have been recently closed. A
-// connection_id lives in this state for kTimeWaitPeriod. All packets received
+// connection_id lives in this state for time_wait_period_. All packets received
 // for connection_ids in this state are handed over to the
 // QuicTimeWaitListManager by the QuicDispatcher.  Decides whether to send a
 // public reset packet, a copy of the previously sent connection close packet,
@@ -50,15 +50,17 @@
                           const QuicVersionVector& supported_versions);
   ~QuicTimeWaitListManager() override;
 
-  // Adds the given connection_id to time wait state for kTimeWaitPeriod.
+  // Adds the given connection_id to time wait state for time_wait_period_.
   // Henceforth, any packet bearing this connection_id should not be processed
   // while the connection_id remains in this list. If a non-nullptr
-  // |close_packet| is provided, it is sent again when packets are received for
-  // added connection_ids. If nullptr, a public reset packet is sent with the
-  // specified |version|. DCHECKs that connection_id is not already on the list.
-  void AddConnectionIdToTimeWait(QuicConnectionId connection_id,
-                                 QuicVersion version,
-                                 QuicEncryptedPacket* close_packet);  // Owned.
+  // |close_packet| is provided, the TimeWaitListManager takes ownership of it
+  // and sends it again when packets are received for added connection_ids. If
+  // nullptr, a public reset packet is sent with the specified |version|.
+  // DCHECKs that connection_id is not already on the list. "virtual" to
+  // override in tests.
+  virtual void AddConnectionIdToTimeWait(QuicConnectionId connection_id,
+                                         QuicVersion version,
+                                         QuicEncryptedPacket* close_packet);
 
   // Returns true if the connection_id is in time wait state, false otherwise.
   // Packets received for this connection_id should not lead to creation of new
@@ -169,11 +171,10 @@
   QuicConnectionHelperInterface* helper_;
 
   // Time period for which connection_ids should remain in time wait state.
-  const QuicTime::Delta kTimeWaitPeriod_;
+  const QuicTime::Delta time_wait_period_;
 
   // Alarm registered with the connection helper to clean up connection_ids that
-  // have
-  // out lived their duration in time wait state.
+  // have out lived their duration in time wait state.
   scoped_ptr<QuicAlarm> connection_id_clean_up_alarm_;
 
   // Interface that writes given buffer to the socket.
diff --git a/net/quic/test_tools/mock_quic_dispatcher.h b/net/quic/test_tools/mock_quic_dispatcher.h
index 4945724..87835dc 100644
--- a/net/quic/test_tools/mock_quic_dispatcher.h
+++ b/net/quic/test_tools/mock_quic_dispatcher.h
@@ -8,14 +8,14 @@
 #include "net/base/ip_endpoint.h"
 #include "net/quic/crypto/quic_crypto_server_config.h"
 #include "net/quic/quic_config.h"
-#include "net/quic/quic_dispatcher.h"
 #include "net/quic/quic_protocol.h"
+#include "net/tools/quic/quic_dispatcher.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace net {
 namespace test {
 
-class MockQuicDispatcher : public QuicDispatcher {
+class MockQuicDispatcher : public tools::QuicDispatcher {
  public:
   MockQuicDispatcher(const QuicConfig& config,
                      const QuicCryptoServerConfig& crypto_config,
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index 6d6896b..5fb8acf 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -19,6 +19,7 @@
 #include "net/quic/quic_utils.h"
 #include "net/quic/test_tools/quic_connection_peer.h"
 #include "net/spdy/spdy_frame_builder.h"
+#include "net/tools/quic/quic_per_connection_packet_writer.h"
 
 using base::StringPiece;
 using std::max;
@@ -667,7 +668,7 @@
 TestWriterFactory::TestWriterFactory() : current_writer_(nullptr) {}
 TestWriterFactory::~TestWriterFactory() {}
 
-QuicPacketWriter* TestWriterFactory::Create(QuicServerPacketWriter* writer,
+QuicPacketWriter* TestWriterFactory::Create(QuicPacketWriter* writer,
                                             QuicConnection* connection) {
   return new PerConnectionPacketWriter(this, writer, connection);
 }
@@ -687,7 +688,7 @@
 
 TestWriterFactory::PerConnectionPacketWriter::PerConnectionPacketWriter(
     TestWriterFactory* factory,
-    QuicServerPacketWriter* writer,
+    QuicPacketWriter* writer,
     QuicConnection* connection)
     : QuicPerConnectionPacketWriter(writer, connection),
       factory_(factory) {
@@ -707,7 +708,7 @@
   // in a different way, so TestWriterFactory::OnPacketSent might never be
   // called.
   factory_->current_writer_ = this;
-  return QuicPerConnectionPacketWriter::WritePacket(buffer,
+  return tools::QuicPerConnectionPacketWriter::WritePacket(buffer,
                                                     buf_len,
                                                     self_address,
                                                     peer_address);
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index 3d5dd057..8da940d 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -17,15 +17,15 @@
 #include "net/quic/quic_ack_notifier.h"
 #include "net/quic/quic_client_session_base.h"
 #include "net/quic/quic_connection.h"
-#include "net/quic/quic_dispatcher.h"
 #include "net/quic/quic_framer.h"
-#include "net/quic/quic_per_connection_packet_writer.h"
 #include "net/quic/quic_protocol.h"
 #include "net/quic/quic_sent_packet_manager.h"
 #include "net/quic/quic_session.h"
 #include "net/quic/test_tools/mock_clock.h"
 #include "net/quic/test_tools/mock_random.h"
 #include "net/spdy/spdy_framer.h"
+#include "net/tools/quic/quic_dispatcher.h"
+#include "net/tools/quic/quic_per_connection_packet_writer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace net {
@@ -338,7 +338,8 @@
   MOCK_METHOD0(OnCanWrite, void());
 
   MOCK_METHOD1(OnSendConnectionState, void(const CachedNetworkParameters&));
-  MOCK_METHOD1(ResumeConnectionState, bool(const CachedNetworkParameters&));
+  MOCK_METHOD2(ResumeConnectionState,
+               bool(const CachedNetworkParameters&, bool));
 
   void ReallyProcessUdpPacket(const IPEndPoint& self_address,
                               const IPEndPoint& peer_address,
@@ -477,7 +478,8 @@
                     Perspective perspective,
                     bool using_pacing));
   MOCK_METHOD1(SetNumEmulatedConnections, void(int num_connections));
-  MOCK_METHOD1(SetMaxPacketSize, void(QuicByteCount max_packet_size));
+  MOCK_METHOD1(SetMaxCongestionWindow,
+               void(QuicByteCount max_congestion_window));
   MOCK_METHOD4(OnCongestionEvent, void(bool rtt_updated,
                                        QuicByteCount bytes_in_flight,
                                        const CongestionVector& acked_packets,
@@ -501,7 +503,8 @@
   MOCK_CONST_METHOD0(InRecovery, bool());
   MOCK_CONST_METHOD0(GetSlowStartThreshold, QuicByteCount());
   MOCK_CONST_METHOD0(GetCongestionControlType, CongestionControlType());
-  MOCK_METHOD1(ResumeConnectionState, bool(const CachedNetworkParameters&));
+  MOCK_METHOD2(ResumeConnectionState,
+               bool(const CachedNetworkParameters&, bool));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockSendAlgorithm);
@@ -583,12 +586,12 @@
 // Creates per-connection packet writers that register themselves with the
 // TestWriterFactory on each write so that TestWriterFactory::OnPacketSent can
 // be routed to the appropriate QuicConnection.
-class TestWriterFactory : public QuicDispatcher::PacketWriterFactory {
+class TestWriterFactory : public tools::QuicDispatcher::PacketWriterFactory {
  public:
   TestWriterFactory();
   ~TestWriterFactory() override;
 
-  QuicPacketWriter* Create(QuicServerPacketWriter* writer,
+  QuicPacketWriter* Create(QuicPacketWriter* writer,
                            QuicConnection* connection) override;
 
   // Calls OnPacketSent on the last QuicConnection to write through one of the
@@ -596,10 +599,11 @@
   void OnPacketSent(WriteResult result);
 
  private:
-  class PerConnectionPacketWriter : public QuicPerConnectionPacketWriter {
+  class PerConnectionPacketWriter
+      : public tools::QuicPerConnectionPacketWriter {
    public:
     PerConnectionPacketWriter(TestWriterFactory* factory,
-                              QuicServerPacketWriter* writer,
+                              QuicPacketWriter* writer,
                               QuicConnection* connection);
     ~PerConnectionPacketWriter() override;
 
diff --git a/net/quic/test_tools/rtt_stats_peer.cc b/net/quic/test_tools/rtt_stats_peer.cc
new file mode 100644
index 0000000..7edb313
--- /dev/null
+++ b/net/quic/test_tools/rtt_stats_peer.cc
@@ -0,0 +1,31 @@
+// 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 "net/quic/test_tools/rtt_stats_peer.h"
+
+namespace net {
+namespace test {
+
+// static
+QuicTime::Delta RttStatsPeer::GetHalfWindowRtt(const RttStats* rtt_stats) {
+  return rtt_stats->half_window_rtt_.rtt;
+}
+
+// static
+QuicTime::Delta RttStatsPeer::GetQuarterWindowRtt(const RttStats* rtt_stats) {
+  return rtt_stats->quarter_window_rtt_.rtt;
+}
+
+// static
+void RttStatsPeer::SetSmoothedRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms) {
+  rtt_stats->smoothed_rtt_ = rtt_ms;
+}
+
+// static
+void RttStatsPeer::SetMinRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms) {
+  rtt_stats->min_rtt_ = rtt_ms;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/quic/test_tools/rtt_stats_peer.h b/net/quic/test_tools/rtt_stats_peer.h
new file mode 100644
index 0000000..640d9b6c
--- /dev/null
+++ b/net/quic/test_tools/rtt_stats_peer.h
@@ -0,0 +1,31 @@
+// 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 NET_QUIC_TEST_TOOLS_RTT_STATS_PEER_H_
+#define NET_QUIC_TEST_TOOLS_RTT_STATS_PEER_H_
+
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+namespace test {
+
+class RttStatsPeer {
+ public:
+  static QuicTime::Delta GetHalfWindowRtt(const RttStats* rtt_stats);
+
+  static QuicTime::Delta GetQuarterWindowRtt(const RttStats* rtt_stats);
+
+  static void SetSmoothedRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms);
+
+  static void SetMinRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RttStatsPeer);
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_QUIC_TEST_TOOLS_RTT_STATS_PEER_H_
diff --git a/net/socket/client_socket_handle.cc b/net/socket/client_socket_handle.cc
index 53bcd77..734e58a 100644
--- a/net/socket/client_socket_handle.cc
+++ b/net/socket/client_socket_handle.cc
@@ -11,7 +11,6 @@
 #include "base/logging.h"
 #include "net/base/net_errors.h"
 #include "net/socket/client_socket_pool.h"
-#include "net/socket/client_socket_pool_histograms.h"
 
 namespace net {
 
@@ -149,8 +148,6 @@
 
 void ClientSocketHandle::HandleInitCompletion(int result) {
   CHECK_NE(ERR_IO_PENDING, result);
-  ClientSocketPoolHistograms* histograms = pool_->histograms();
-  histograms->AddErrorCode(result);
   if (result != OK) {
     if (!socket_.get())
       ResetInternal(false);  // Nothing to cancel since the request failed.
@@ -162,22 +159,6 @@
   CHECK_NE(-1, pool_id_) << "Pool should have set |pool_id_| to a valid value.";
   setup_time_ = base::TimeTicks::Now() - init_time_;
 
-  histograms->AddSocketType(reuse_type());
-  switch (reuse_type()) {
-    case ClientSocketHandle::UNUSED:
-      histograms->AddRequestTime(setup_time());
-      break;
-    case ClientSocketHandle::UNUSED_IDLE:
-      histograms->AddUnusedIdleTime(idle_time());
-      break;
-    case ClientSocketHandle::REUSED_IDLE:
-      histograms->AddReusedIdleTime(idle_time());
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-
   // Broadcast that the socket has been acquired.
   // TODO(eroman): This logging is not complete, in particular set_socket() and
   // release() socket. It ends up working though, since those methods are being
diff --git a/net/socket/client_socket_pool.h b/net/socket/client_socket_pool.h
index 2a2be36..5404db6 100644
--- a/net/socket/client_socket_pool.h
+++ b/net/socket/client_socket_pool.h
@@ -26,7 +26,6 @@
 namespace net {
 
 class ClientSocketHandle;
-class ClientSocketPoolHistograms;
 class StreamSocket;
 
 // ClientSocketPools are layered. This defines an interface for lower level
@@ -170,10 +169,6 @@
   // Returns the maximum amount of time to wait before retrying a connect.
   static const int kMaxConnectRetryIntervalMs = 250;
 
-  // The set of histograms specific to this pool.  We can't use the standard
-  // UMA_HISTOGRAM_* macros because they are callsite static.
-  virtual ClientSocketPoolHistograms* histograms() const = 0;
-
   static base::TimeDelta unused_idle_socket_timeout();
   static void set_unused_idle_socket_timeout(base::TimeDelta timeout);
 
diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h
index 76ae919..5270155 100644
--- a/net/socket/client_socket_pool_base.h
+++ b/net/socket/client_socket_pool_base.h
@@ -702,17 +702,17 @@
   // long to leave an unused idle socket open before closing it.
   // |used_idle_socket_timeout| specifies how long to leave a previously used
   // idle socket open before closing it.
-  ClientSocketPoolBase(
-      HigherLayeredPool* self,
-      int max_sockets,
-      int max_sockets_per_group,
-      ClientSocketPoolHistograms* histograms,
-      base::TimeDelta unused_idle_socket_timeout,
-      base::TimeDelta used_idle_socket_timeout,
-      ConnectJobFactory* connect_job_factory)
-      : histograms_(histograms),
-        helper_(self, max_sockets, max_sockets_per_group,
-                unused_idle_socket_timeout, used_idle_socket_timeout,
+  ClientSocketPoolBase(HigherLayeredPool* self,
+                       int max_sockets,
+                       int max_sockets_per_group,
+                       base::TimeDelta unused_idle_socket_timeout,
+                       base::TimeDelta used_idle_socket_timeout,
+                       ConnectJobFactory* connect_job_factory)
+      : helper_(self,
+                max_sockets,
+                max_sockets_per_group,
+                unused_idle_socket_timeout,
+                used_idle_socket_timeout,
                 new ConnectJobFactoryAdaptor(connect_job_factory)) {}
 
   virtual ~ClientSocketPoolBase() {}
@@ -824,10 +824,6 @@
     return helper_.ConnectionTimeout();
   }
 
-  ClientSocketPoolHistograms* histograms() const {
-    return histograms_;
-  }
-
   void EnableConnectBackupJobs() { helper_.EnableConnectBackupJobs(); }
 
   bool CloseOneIdleSocket() { return helper_.CloseOneIdleSocket(); }
@@ -868,8 +864,6 @@
     const scoped_ptr<ConnectJobFactory> connect_job_factory_;
   };
 
-  // Histograms for the pool
-  ClientSocketPoolHistograms* const histograms_;
   internal::ClientSocketPoolBaseHelper helper_;
 
   DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolBase);
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index c7cbda1..1606b42 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -28,7 +28,6 @@
 #include "net/http/http_response_headers.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
-#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/socket_test_util.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/socket/stream_socket.h"
@@ -482,12 +481,14 @@
   TestClientSocketPool(
       int max_sockets,
       int max_sockets_per_group,
-      ClientSocketPoolHistograms* histograms,
       base::TimeDelta unused_idle_socket_timeout,
       base::TimeDelta used_idle_socket_timeout,
       TestClientSocketPoolBase::ConnectJobFactory* connect_job_factory)
-      : base_(NULL, max_sockets, max_sockets_per_group, histograms,
-              unused_idle_socket_timeout, used_idle_socket_timeout,
+      : base_(NULL,
+              max_sockets,
+              max_sockets_per_group,
+              unused_idle_socket_timeout,
+              used_idle_socket_timeout,
               connect_job_factory) {}
 
   ~TestClientSocketPool() override {}
@@ -561,10 +562,6 @@
     return base_.ConnectionTimeout();
   }
 
-  ClientSocketPoolHistograms* histograms() const override {
-    return base_.histograms();
-  }
-
   const TestClientSocketPoolBase* base() const { return &base_; }
 
   int NumUnassignedConnectJobsInGroup(const std::string& group_name) const {
@@ -658,8 +655,7 @@
 class ClientSocketPoolBaseTest : public testing::Test {
  protected:
   ClientSocketPoolBaseTest()
-      : params_(new TestSocketParams(false /* ignore_limits */)),
-        histograms_("ClientSocketPoolTest") {
+      : params_(new TestSocketParams(false /* ignore_limits */)) {
     connect_backup_jobs_enabled_ =
         internal::ClientSocketPoolBaseHelper::connect_backup_jobs_enabled();
     internal::ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(true);
@@ -691,7 +687,6 @@
                                                      &net_log_);
     pool_.reset(new TestClientSocketPool(max_sockets,
                                          max_sockets_per_group,
-                                         &histograms_,
                                          unused_idle_socket_timeout,
                                          used_idle_socket_timeout,
                                          connect_job_factory_));
@@ -732,7 +727,6 @@
   MockClientSocketFactory client_socket_factory_;
   TestConnectJobFactory* connect_job_factory_;
   scoped_refptr<TestSocketParams> params_;
-  ClientSocketPoolHistograms histograms_;
   scoped_ptr<TestClientSocketPool> pool_;
   ClientSocketPoolTest test_base_;
 };
diff --git a/net/socket/client_socket_pool_histograms.cc b/net/socket/client_socket_pool_histograms.cc
deleted file mode 100644
index 9af8649..0000000
--- a/net/socket/client_socket_pool_histograms.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2011 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 "net/socket/client_socket_pool_histograms.h"
-
-#include <string>
-
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram.h"
-#include "net/base/net_errors.h"
-#include "net/socket/client_socket_handle.h"
-
-namespace net {
-
-using base::Histogram;
-using base::HistogramBase;
-using base::LinearHistogram;
-using base::CustomHistogram;
-
-ClientSocketPoolHistograms::ClientSocketPoolHistograms(
-    const std::string& pool_name)
-    : is_http_proxy_connection_(false),
-      is_socks_connection_(false) {
-  // UMA_HISTOGRAM_ENUMERATION
-  socket_type_ = LinearHistogram::FactoryGet("Net.SocketType_" + pool_name, 1,
-      ClientSocketHandle::NUM_TYPES, ClientSocketHandle::NUM_TYPES + 1,
-      HistogramBase::kUmaTargetedHistogramFlag);
-  // UMA_HISTOGRAM_CUSTOM_TIMES
-  request_time_ = Histogram::FactoryTimeGet(
-      "Net.SocketRequestTime_" + pool_name,
-      base::TimeDelta::FromMilliseconds(1),
-      base::TimeDelta::FromMinutes(10),
-      100, HistogramBase::kUmaTargetedHistogramFlag);
-  // UMA_HISTOGRAM_CUSTOM_TIMES
-  unused_idle_time_ = Histogram::FactoryTimeGet(
-      "Net.SocketIdleTimeBeforeNextUse_UnusedSocket_" + pool_name,
-      base::TimeDelta::FromMilliseconds(1),
-      base::TimeDelta::FromMinutes(6),
-      100, HistogramBase::kUmaTargetedHistogramFlag);
-  // UMA_HISTOGRAM_CUSTOM_TIMES
-  reused_idle_time_ = Histogram::FactoryTimeGet(
-      "Net.SocketIdleTimeBeforeNextUse_ReusedSocket_" + pool_name,
-      base::TimeDelta::FromMilliseconds(1),
-      base::TimeDelta::FromMinutes(6),
-      100, HistogramBase::kUmaTargetedHistogramFlag);
-  // UMA_HISTOGRAM_CUSTOM_ENUMERATION
-  error_code_ = CustomHistogram::FactoryGet(
-      "Net.SocketInitErrorCodes_" + pool_name,
-      GetAllErrorCodesForUma(),
-      HistogramBase::kUmaTargetedHistogramFlag);
-
-  if (pool_name == "HTTPProxy")
-    is_http_proxy_connection_ = true;
-  else if (pool_name == "SOCK")
-    is_socks_connection_ = true;
-}
-
-ClientSocketPoolHistograms::~ClientSocketPoolHistograms() {
-}
-
-void ClientSocketPoolHistograms::AddSocketType(int type) const {
-  socket_type_->Add(type);
-}
-
-void ClientSocketPoolHistograms::AddRequestTime(base::TimeDelta time) const {
-  request_time_->AddTime(time);
-}
-
-void ClientSocketPoolHistograms::AddUnusedIdleTime(base::TimeDelta time) const {
-  unused_idle_time_->AddTime(time);
-}
-
-void ClientSocketPoolHistograms::AddReusedIdleTime(base::TimeDelta time) const {
-  reused_idle_time_->AddTime(time);
-}
-
-void ClientSocketPoolHistograms::AddErrorCode(int error_code) const {
-  // Error codes are positive (since histograms expect positive sample values).
-  error_code_->Add(-error_code);
-}
-
-}  // namespace net
diff --git a/net/socket/client_socket_pool_histograms.h b/net/socket/client_socket_pool_histograms.h
deleted file mode 100644
index 26a4063..0000000
--- a/net/socket/client_socket_pool_histograms.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2011 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 NET_SOCKET_CLIENT_SOCKET_POOL_HISTOGRAMS_H_
-#define NET_SOCKET_CLIENT_SOCKET_POOL_HISTOGRAMS_H_
-
-#include <string>
-
-#include "base/memory/ref_counted.h"
-#include "base/time/time.h"
-#include "net/base/net_export.h"
-
-namespace base {
-class HistogramBase;
-}
-
-namespace net {
-
-class NET_EXPORT_PRIVATE ClientSocketPoolHistograms {
- public:
-  ClientSocketPoolHistograms(const std::string& pool_name);
-  ~ClientSocketPoolHistograms();
-
-  void AddSocketType(int socket_reuse_type) const;
-  void AddRequestTime(base::TimeDelta time) const;
-  void AddUnusedIdleTime(base::TimeDelta time) const;
-  void AddReusedIdleTime(base::TimeDelta time) const;
-  void AddErrorCode(int error_code) const;
-
- private:
-  base::HistogramBase* socket_type_;
-  base::HistogramBase* request_time_;
-  base::HistogramBase* unused_idle_time_;
-  base::HistogramBase* reused_idle_time_;
-  base::HistogramBase* error_code_;
-
-  bool is_http_proxy_connection_;
-  bool is_socks_connection_;
-
-  DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolHistograms);
-};
-
-}  // namespace net
-
-#endif  // NET_SOCKET_CLIENT_SOCKET_POOL_HISTOGRAMS_H_
diff --git a/net/socket/client_socket_pool_manager_impl.cc b/net/socket/client_socket_pool_manager_impl.cc
index 34599aa4..c43678d 100644
--- a/net/socket/client_socket_pool_manager_impl.cc
+++ b/net/socket/client_socket_pool_manager_impl.cc
@@ -57,26 +57,21 @@
       ssl_session_cache_shard_(ssl_session_cache_shard),
       ssl_config_service_(ssl_config_service),
       pool_type_(pool_type),
-      transport_pool_histograms_("TCP"),
       transport_socket_pool_(
           pool_type == HttpNetworkSession::WEBSOCKET_SOCKET_POOL
               ? new WebSocketTransportClientSocketPool(
                     max_sockets_per_pool(pool_type),
                     max_sockets_per_group(pool_type),
-                    &transport_pool_histograms_,
                     host_resolver,
                     socket_factory_,
                     net_log)
               : new TransportClientSocketPool(max_sockets_per_pool(pool_type),
                                               max_sockets_per_group(pool_type),
-                                              &transport_pool_histograms_,
                                               host_resolver,
                                               socket_factory_,
                                               net_log)),
-      ssl_pool_histograms_("SSL2"),
       ssl_socket_pool_(new SSLClientSocketPool(max_sockets_per_pool(pool_type),
                                                max_sockets_per_group(pool_type),
-                                               &ssl_pool_histograms_,
                                                cert_verifier,
                                                channel_id_service,
                                                transport_security_state,
@@ -88,14 +83,7 @@
                                                NULL /* no socks proxy */,
                                                NULL /* no http proxy */,
                                                ssl_config_service,
-                                               net_log)),
-      transport_for_socks_pool_histograms_("TCPforSOCKS"),
-      socks_pool_histograms_("SOCK"),
-      transport_for_http_proxy_pool_histograms_("TCPforHTTPProxy"),
-      transport_for_https_proxy_pool_histograms_("TCPforHTTPSProxy"),
-      ssl_for_https_proxy_pool_histograms_("SSLforHTTPSProxy"),
-      http_proxy_pool_histograms_("HTTPProxy"),
-      ssl_socket_pool_for_proxies_histograms_("SSLForProxies") {
+                                               net_log)) {
   CertDatabase::GetInstance()->AddObserver(this);
 }
 
@@ -228,7 +216,6 @@
               new TransportClientSocketPool(
                   max_sockets_per_proxy_server(pool_type_),
                   max_sockets_per_group(pool_type_),
-                  &transport_for_socks_pool_histograms_,
                   host_resolver_,
                   socket_factory_,
                   net_log_)));
@@ -239,7 +226,6 @@
           std::make_pair(socks_proxy, new SOCKSClientSocketPool(
               max_sockets_per_proxy_server(pool_type_),
               max_sockets_per_group(pool_type_),
-              &socks_pool_histograms_,
               host_resolver_,
               tcp_ret.first->second,
               net_log_)));
@@ -270,7 +256,6 @@
               new TransportClientSocketPool(
                   max_sockets_per_proxy_server(pool_type_),
                   max_sockets_per_group(pool_type_),
-                  &transport_for_http_proxy_pool_histograms_,
                   host_resolver_,
                   socket_factory_,
                   net_log_)));
@@ -283,7 +268,6 @@
               new TransportClientSocketPool(
                   max_sockets_per_proxy_server(pool_type_),
                   max_sockets_per_group(pool_type_),
-                  &transport_for_https_proxy_pool_histograms_,
                   host_resolver_,
                   socket_factory_,
                   net_log_)));
@@ -293,8 +277,7 @@
       ssl_socket_pools_for_https_proxies_.insert(std::make_pair(
           http_proxy, new SSLClientSocketPool(
                           max_sockets_per_proxy_server(pool_type_),
-                          max_sockets_per_group(pool_type_),
-                          &ssl_for_https_proxy_pool_histograms_, cert_verifier_,
+                          max_sockets_per_group(pool_type_), cert_verifier_,
                           channel_id_service_, transport_security_state_,
                           cert_transparency_verifier_, cert_policy_enforcer_,
                           ssl_session_cache_shard_, socket_factory_,
@@ -310,7 +293,6 @@
               new HttpProxyClientSocketPool(
                   max_sockets_per_proxy_server(pool_type_),
                   max_sockets_per_group(pool_type_),
-                  &http_proxy_pool_histograms_,
                   tcp_http_ret.first->second,
                   ssl_https_ret.first->second,
                   net_log_)));
@@ -327,10 +309,9 @@
 
   SSLClientSocketPool* new_pool = new SSLClientSocketPool(
       max_sockets_per_proxy_server(pool_type_),
-      max_sockets_per_group(pool_type_), &ssl_pool_histograms_, cert_verifier_,
-      channel_id_service_, transport_security_state_,
-      cert_transparency_verifier_, cert_policy_enforcer_,
-      ssl_session_cache_shard_, socket_factory_,
+      max_sockets_per_group(pool_type_), cert_verifier_, channel_id_service_,
+      transport_security_state_, cert_transparency_verifier_,
+      cert_policy_enforcer_, ssl_session_cache_shard_, socket_factory_,
       NULL, /* no tcp pool, we always go through a proxy */
       GetSocketPoolForSOCKSProxy(proxy_server),
       GetSocketPoolForHTTPProxy(proxy_server), ssl_config_service_.get(),
diff --git a/net/socket/client_socket_pool_manager_impl.h b/net/socket/client_socket_pool_manager_impl.h
index 1c6295b2..a38cae1a 100644
--- a/net/socket/client_socket_pool_manager_impl.h
+++ b/net/socket/client_socket_pool_manager_impl.h
@@ -15,7 +15,6 @@
 #include "base/threading/non_thread_safe.h"
 #include "net/cert/cert_database.h"
 #include "net/http/http_network_session.h"
-#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/client_socket_pool_manager.h"
 
 namespace net {
@@ -23,7 +22,6 @@
 class CertVerifier;
 class ChannelIDService;
 class ClientSocketFactory;
-class ClientSocketPoolHistograms;
 class CTVerifier;
 class HttpProxyClientSocketPool;
 class HostResolver;
@@ -116,31 +114,14 @@
 
   // Note: this ordering is important.
 
-  ClientSocketPoolHistograms transport_pool_histograms_;
   scoped_ptr<TransportClientSocketPool> transport_socket_pool_;
-
-  ClientSocketPoolHistograms ssl_pool_histograms_;
   scoped_ptr<SSLClientSocketPool> ssl_socket_pool_;
-
-  ClientSocketPoolHistograms transport_for_socks_pool_histograms_;
   TransportSocketPoolMap transport_socket_pools_for_socks_proxies_;
-
-  ClientSocketPoolHistograms socks_pool_histograms_;
   SOCKSSocketPoolMap socks_socket_pools_;
-
-  ClientSocketPoolHistograms transport_for_http_proxy_pool_histograms_;
   TransportSocketPoolMap transport_socket_pools_for_http_proxies_;
-
-  ClientSocketPoolHistograms transport_for_https_proxy_pool_histograms_;
   TransportSocketPoolMap transport_socket_pools_for_https_proxies_;
-
-  ClientSocketPoolHistograms ssl_for_https_proxy_pool_histograms_;
   SSLSocketPoolMap ssl_socket_pools_for_https_proxies_;
-
-  ClientSocketPoolHistograms http_proxy_pool_histograms_;
   HTTPProxySocketPoolMap http_proxy_socket_pools_;
-
-  ClientSocketPoolHistograms ssl_socket_pool_for_proxies_histograms_;
   SSLSocketPoolMap ssl_socket_pools_for_proxies_;
 
   DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolManagerImpl);
diff --git a/net/socket/deterministic_socket_data_unittest.cc b/net/socket/deterministic_socket_data_unittest.cc
index bdeba2b..9a95e1e 100644
--- a/net/socket/deterministic_socket_data_unittest.cc
+++ b/net/socket/deterministic_socket_data_unittest.cc
@@ -58,7 +58,6 @@
 
   HostPortPair endpoint_;
   scoped_refptr<TransportSocketParams> tcp_params_;
-  ClientSocketPoolHistograms histograms_;
   DeterministicMockClientSocketFactory socket_factory_;
   MockTransportClientSocketPool socket_pool_;
   ClientSocketHandle connection_;
@@ -72,13 +71,13 @@
       connect_data_(SYNCHRONOUS, OK),
       endpoint_("www.google.com", 443),
       tcp_params_(new TransportSocketParams(
-              endpoint_,
-              false,
-              false,
-              OnHostResolutionCallback(),
-              TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT)),
-      histograms_(std::string()),
-      socket_pool_(10, 10, &histograms_, &socket_factory_) {}
+          endpoint_,
+          false,
+          false,
+          OnHostResolutionCallback(),
+          TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT)),
+      socket_pool_(10, 10, &socket_factory_) {
+}
 
 void DeterministicSocketDataTest::TearDown() {
   // Empty the current queue.
diff --git a/net/socket/next_proto.cc b/net/socket/next_proto.cc
index 6b5ee3e..4632e97 100644
--- a/net/socket/next_proto.cc
+++ b/net/socket/next_proto.cc
@@ -6,12 +6,6 @@
 
 namespace net {
 
-NextProtoVector NextProtosHttpOnly() {
-  NextProtoVector next_protos;
-  next_protos.push_back(kProtoHTTP11);
-  return next_protos;
-}
-
 NextProtoVector NextProtosDefaults() {
   NextProtoVector next_protos;
   next_protos.push_back(kProtoHTTP11);
@@ -43,14 +37,4 @@
   return next_protos;
 }
 
-NextProtoVector NextProtosSpdy4Http2() {
-  NextProtoVector next_protos;
-  next_protos.push_back(kProtoHTTP11);
-  next_protos.push_back(kProtoQUIC1SPDY3);
-  next_protos.push_back(kProtoSPDY31);
-  next_protos.push_back(kProtoSPDY4_14);
-  next_protos.push_back(kProtoSPDY4);
-  return next_protos;
-}
-
 }  // namespace net
diff --git a/net/socket/next_proto.h b/net/socket/next_proto.h
index 22cfbd16..b4b1d72 100644
--- a/net/socket/next_proto.h
+++ b/net/socket/next_proto.h
@@ -47,19 +47,16 @@
 
 // Convenience functions to create NextProtoVector.
 
-NET_EXPORT NextProtoVector NextProtosHttpOnly();
-
-// Default values, which are subject to change over time.  Currently just
-// SPDY 3 and 3.1.
+// Default values, which are subject to change over time.
 NET_EXPORT NextProtoVector NextProtosDefaults();
 
+// Enable SPDY/3.1 and QUIC, but not HTTP/2.
+NET_EXPORT NextProtoVector NextProtosSpdy31();
+
+// Control SPDY/3.1 and HTTP/2 separately.
 NET_EXPORT NextProtoVector NextProtosWithSpdyAndQuic(bool spdy_enabled,
                                                      bool quic_enabled);
 
-// All of these also enable QUIC.
-NET_EXPORT NextProtoVector NextProtosSpdy31();
-NET_EXPORT NextProtoVector NextProtosSpdy4Http2();
-
 }  // namespace net
 
 #endif  // NET_SOCKET_NEXT_PROTO_H_
diff --git a/net/socket/nss_ssl_util.cc b/net/socket/nss_ssl_util.cc
index a238a25d..627a033 100644
--- a/net/socket/nss_ssl_util.cc
+++ b/net/socket/nss_ssl_util.cc
@@ -108,7 +108,7 @@
       disableECDSA = true;
 #endif
 
-    // Explicitly enable exactly those ciphers with keys of at least 80 bits
+    // Explicitly enable exactly those ciphers with keys of at least 80 bits.
     for (int i = 0; i < num_ciphers; i++) {
       SSLCipherSuiteInfo info;
       if (SSL_GetCipherSuiteInfo(ssl_ciphers[i], &info,
@@ -130,10 +130,6 @@
           enabled = false;
         }
 
-        if (ssl_ciphers[i] == TLS_DHE_DSS_WITH_AES_128_CBC_SHA) {
-          // Enabled to allow servers with only a DSA certificate to function.
-          enabled = true;
-        }
         SSL_CipherPrefSetDefault(ssl_ciphers[i], enabled);
       }
     }
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index fb22055..4de30dd 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -21,7 +21,6 @@
 #include "net/http/http_network_session.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
-#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/socket.h"
 #include "net/socket/websocket_endpoint_lock_manager.h"
 #include "net/ssl/ssl_cert_request_info.h"
@@ -1756,10 +1755,12 @@
 MockTransportClientSocketPool::MockTransportClientSocketPool(
     int max_sockets,
     int max_sockets_per_group,
-    ClientSocketPoolHistograms* histograms,
     ClientSocketFactory* socket_factory)
-    : TransportClientSocketPool(max_sockets, max_sockets_per_group, histograms,
-                                NULL, NULL, NULL),
+    : TransportClientSocketPool(max_sockets,
+                                max_sockets_per_group,
+                                NULL,
+                                NULL,
+                                NULL),
       client_socket_factory_(socket_factory),
       last_request_priority_(DEFAULT_PRIORITY),
       release_count_(0),
@@ -1875,10 +1876,12 @@
 MockSOCKSClientSocketPool::MockSOCKSClientSocketPool(
     int max_sockets,
     int max_sockets_per_group,
-    ClientSocketPoolHistograms* histograms,
     TransportClientSocketPool* transport_pool)
-    : SOCKSClientSocketPool(max_sockets, max_sockets_per_group, histograms,
-                            NULL, transport_pool, NULL),
+    : SOCKSClientSocketPool(max_sockets,
+                            max_sockets_per_group,
+                            NULL,
+                            transport_pool,
+                            NULL),
       transport_pool_(transport_pool) {
 }
 
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index ef15a84..b6395fc8 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -1158,7 +1158,6 @@
 
   MockTransportClientSocketPool(int max_sockets,
                                 int max_sockets_per_group,
-                                ClientSocketPoolHistograms* histograms,
                                 ClientSocketFactory* socket_factory);
 
   ~MockTransportClientSocketPool() override;
@@ -1249,7 +1248,6 @@
  public:
   MockSOCKSClientSocketPool(int max_sockets,
                             int max_sockets_per_group,
-                            ClientSocketPoolHistograms* histograms,
                             TransportClientSocketPool* transport_pool);
 
   ~MockSOCKSClientSocketPool() override;
diff --git a/net/socket/socks_client_socket_pool.cc b/net/socket/socks_client_socket_pool.cc
index e11b7a48..ee613ac4 100644
--- a/net/socket/socks_client_socket_pool.cc
+++ b/net/socket/socks_client_socket_pool.cc
@@ -192,17 +192,17 @@
 SOCKSClientSocketPool::SOCKSClientSocketPool(
     int max_sockets,
     int max_sockets_per_group,
-    ClientSocketPoolHistograms* histograms,
     HostResolver* host_resolver,
     TransportClientSocketPool* transport_pool,
     NetLog* net_log)
     : transport_pool_(transport_pool),
-      base_(this, max_sockets, max_sockets_per_group, histograms,
-            ClientSocketPool::unused_idle_socket_timeout(),
-            ClientSocketPool::used_idle_socket_timeout(),
-            new SOCKSConnectJobFactory(transport_pool,
-                                       host_resolver,
-                                       net_log)) {
+      base_(
+          this,
+          max_sockets,
+          max_sockets_per_group,
+          ClientSocketPool::unused_idle_socket_timeout(),
+          ClientSocketPool::used_idle_socket_timeout(),
+          new SOCKSConnectJobFactory(transport_pool, host_resolver, net_log)) {
   // We should always have a |transport_pool_| except in unit tests.
   if (transport_pool_)
     base_.AddLowerLayeredPool(transport_pool_);
@@ -285,10 +285,6 @@
   return base_.ConnectionTimeout();
 }
 
-ClientSocketPoolHistograms* SOCKSClientSocketPool::histograms() const {
-  return base_.histograms();
-};
-
 bool SOCKSClientSocketPool::IsStalled() const {
   return base_.IsStalled();
 }
diff --git a/net/socket/socks_client_socket_pool.h b/net/socket/socks_client_socket_pool.h
index 35f7146f..69bcf00 100644
--- a/net/socket/socks_client_socket_pool.h
+++ b/net/socket/socks_client_socket_pool.h
@@ -16,7 +16,6 @@
 #include "net/dns/host_resolver.h"
 #include "net/socket/client_socket_pool.h"
 #include "net/socket/client_socket_pool_base.h"
-#include "net/socket/client_socket_pool_histograms.h"
 
 namespace net {
 
@@ -112,7 +111,6 @@
   SOCKSClientSocketPool(
       int max_sockets,
       int max_sockets_per_group,
-      ClientSocketPoolHistograms* histograms,
       HostResolver* host_resolver,
       TransportClientSocketPool* transport_pool,
       NetLog* net_log);
@@ -157,8 +155,6 @@
 
   base::TimeDelta ConnectionTimeout() const override;
 
-  ClientSocketPoolHistograms* histograms() const override;
-
   // LowerLayeredPool implementation.
   bool IsStalled() const override;
 
diff --git a/net/socket/socks_client_socket_pool_unittest.cc b/net/socket/socks_client_socket_pool_unittest.cc
index 391d31b..e841c83 100644
--- a/net/socket/socks_client_socket_pool_unittest.cc
+++ b/net/socket/socks_client_socket_pool_unittest.cc
@@ -14,7 +14,6 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
-#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/socket_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -90,18 +89,14 @@
   };
 
   SOCKSClientSocketPoolTest()
-      : transport_histograms_("MockTCP"),
-        transport_socket_pool_(
-            kMaxSockets, kMaxSocketsPerGroup,
-            &transport_histograms_,
-            &transport_client_socket_factory_),
-        socks_histograms_("SOCKSUnitTest"),
-        pool_(kMaxSockets, kMaxSocketsPerGroup,
-              &socks_histograms_,
+      : transport_socket_pool_(kMaxSockets,
+                               kMaxSocketsPerGroup,
+                               &transport_client_socket_factory_),
+        pool_(kMaxSockets,
+              kMaxSocketsPerGroup,
               &host_resolver_,
               &transport_socket_pool_,
-              NULL) {
-  }
+              NULL) {}
 
   ~SOCKSClientSocketPoolTest() override {}
 
@@ -116,11 +111,9 @@
 
   ScopedVector<TestSocketRequest>* requests() { return test_base_.requests(); }
 
-  ClientSocketPoolHistograms transport_histograms_;
   MockClientSocketFactory transport_client_socket_factory_;
   MockTransportClientSocketPool transport_socket_pool_;
 
-  ClientSocketPoolHistograms socks_histograms_;
   MockHostResolver host_resolver_;
   SOCKSClientSocketPool pool_;
   ClientSocketPoolTest test_base_;
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 78e936b2..547ab76 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -1857,14 +1857,6 @@
         base::Bind(&AddLogEventWithCallback, weak_net_log_,
                    NetLog::TYPE_SSL_HANDSHAKE_ERROR,
                    CreateNetLogSSLErrorCallback(net_error, 0)));
-
-    // If the handshake already succeeded (because the server requests but
-    // doesn't require a client cert), we need to invalidate the SSL session
-    // so that we won't try to resume the non-client-authenticated session in
-    // the next handshake.  This will cause the server to ask for a client
-    // cert again.
-    if (rv == SECSuccess && SSL_InvalidateSession(nss_fd_) != SECSuccess)
-      LOG(WARNING) << "Couldn't invalidate SSL session: " << PR_GetError();
   } else if (rv == SECSuccess) {
     if (!handshake_callback_called_) {
       false_started_ = true;
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index f5e858d..34ddeb8 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -926,27 +926,7 @@
     }
   }
 
-  if (client_auth_cert_needed_) {
-    // TODO(vadimt): Remove ScopedTracker below once crbug.com/424386 is fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "424386 SSLClientSocketOpenSSL::DoHandshake2"));
-
-    net_error = ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
-    // If the handshake already succeeded (because the server requests but
-    // doesn't require a client cert), we need to invalidate the SSL session
-    // so that we won't try to resume the non-client-authenticated session in
-    // the next handshake.  This will cause the server to ask for a client
-    // cert again.
-    if (rv == 1) {
-      // Remove from session cache but don't clear this connection.
-      SSL_SESSION* session = SSL_get_session(ssl_);
-      if (session) {
-        int rv = SSL_CTX_remove_session(SSL_get_SSL_CTX(ssl_), session);
-        LOG_IF(WARNING, !rv) << "Couldn't invalidate SSL session: " << session;
-      }
-    }
-  } else if (rv == 1) {
+  if (rv == 1) {
     // TODO(vadimt): Remove ScopedTracker below once crbug.com/424386 is fixed.
     tracked_objects::ScopedTracker tracking_profile3(
         FROM_HERE_WITH_EXPLICIT_FUNCTION(
@@ -1004,6 +984,9 @@
         FROM_HERE_WITH_EXPLICIT_FUNCTION(
             "424386 SSLClientSocketOpenSSL::DoHandshake4"));
 
+    if (client_auth_cert_needed_)
+      return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
+
     int ssl_error = SSL_get_error(ssl_, rv);
 
     if (ssl_error == SSL_ERROR_WANT_CHANNEL_ID_LOOKUP) {
@@ -1434,6 +1417,9 @@
 int SSLClientSocketOpenSSL::DoPayloadRead() {
   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
 
+  DCHECK_LT(0, user_read_buf_len_);
+  DCHECK(user_read_buf_.get());
+
   int rv;
   if (pending_read_error_ != kNoPendingReadResult) {
     rv = pending_read_error_;
@@ -1453,71 +1439,61 @@
   }
 
   int total_bytes_read = 0;
+  int ssl_ret;
   do {
-    rv = SSL_read(ssl_, user_read_buf_->data() + total_bytes_read,
-                  user_read_buf_len_ - total_bytes_read);
-    if (rv > 0)
-      total_bytes_read += rv;
-  } while (total_bytes_read < user_read_buf_len_ && rv > 0);
+    ssl_ret = SSL_read(ssl_, user_read_buf_->data() + total_bytes_read,
+                       user_read_buf_len_ - total_bytes_read);
+    if (ssl_ret > 0)
+      total_bytes_read += ssl_ret;
+  } while (total_bytes_read < user_read_buf_len_ && ssl_ret > 0);
 
-  if (total_bytes_read == user_read_buf_len_) {
-    rv = total_bytes_read;
-  } else {
-    // Otherwise, an error occurred (rv <= 0). The error needs to be handled
-    // immediately, while the OpenSSL errors are still available in
-    // thread-local storage. However, the handled/remapped error code should
-    // only be returned if no application data was already read; if it was, the
-    // error code should be deferred until the next call of DoPayloadRead.
+  // Although only the final SSL_read call may have failed, the failure needs to
+  // processed immediately, while the information still available in OpenSSL's
+  // error queue.
+  if (client_auth_cert_needed_) {
+    pending_read_error_ = ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
+  } else if (ssl_ret <= 0) {
+    // A zero return from SSL_read may mean any of:
+    // - The underlying BIO_read returned 0.
+    // - The peer sent a close_notify.
+    // - Any arbitrary error. https://crbug.com/466303
     //
-    // If no data was read, |*next_result| will point to the return value of
-    // this function. If at least some data was read, |*next_result| will point
-    // to |pending_read_error_|, to be returned in a future call to
-    // DoPayloadRead() (e.g.: after the current data is handled).
-    int *next_result = &rv;
-    if (total_bytes_read > 0) {
-      pending_read_error_ = rv;
-      rv = total_bytes_read;
-      next_result = &pending_read_error_;
+    // TransportReadComplete converts the first to an ERR_CONNECTION_CLOSED
+    // error, so it does not occur. The second and third are distinguished by
+    // SSL_ERROR_ZERO_RETURN.
+    pending_read_ssl_error_ = SSL_get_error(ssl_, ssl_ret);
+    if (pending_read_ssl_error_ == SSL_ERROR_ZERO_RETURN) {
+      pending_read_error_ = 0;
+    } else {
+      pending_read_error_ = MapOpenSSLErrorWithDetails(
+          pending_read_ssl_error_, err_tracer, &pending_read_error_info_);
     }
 
-    if (client_auth_cert_needed_) {
-      *next_result = ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
-    } else if (*next_result <= 0) {
-      // A zero return from SSL_read may mean any of:
-      // - The underlying BIO_read returned 0.
-      // - The peer sent a close_notify.
-      // - Any arbitrary error. https://crbug.com/466303
-      //
-      // TransportReadComplete converts the first to an ERR_CONNECTION_CLOSED
-      // error, so it does not occur. The second and third are distinguished by
-      // SSL_ERROR_ZERO_RETURN.
-      pending_read_ssl_error_ = SSL_get_error(ssl_, *next_result);
-      if (pending_read_ssl_error_ == SSL_ERROR_ZERO_RETURN) {
-        *next_result = 0;
-      } else {
-        *next_result = MapOpenSSLErrorWithDetails(
-            pending_read_ssl_error_, err_tracer, &pending_read_error_info_);
-      }
+    // Many servers do not reliably send a close_notify alert when shutting down
+    // a connection, and instead terminate the TCP connection. This is reported
+    // as ERR_CONNECTION_CLOSED. Because of this, map the unclean shutdown to a
+    // graceful EOF, instead of treating it as an error as it should be.
+    if (pending_read_error_ == ERR_CONNECTION_CLOSED)
+      pending_read_error_ = 0;
+  }
 
-      // Many servers do not reliably send a close_notify alert when shutting
-      // down a connection, and instead terminate the TCP connection. This is
-      // reported as ERR_CONNECTION_CLOSED. Because of this, map the unclean
-      // shutdown to a graceful EOF, instead of treating it as an error as it
-      // should be.
-      if (*next_result == ERR_CONNECTION_CLOSED)
-        *next_result = 0;
+  if (total_bytes_read > 0) {
+    // Return any bytes read to the caller. The error will be deferred to the
+    // next call of DoPayloadRead.
+    rv = total_bytes_read;
 
-      if (rv > 0 && *next_result == ERR_IO_PENDING) {
-          // If at least some data was read from SSL_read(), do not treat
-          // insufficient data as an error to return in the next call to
-          // DoPayloadRead() - instead, let the call fall through to check
-          // SSL_read() again. This is because DoTransportIO() may complete
-          // in between the next call to DoPayloadRead(), and thus it is
-          // important to check SSL_read() on subsequent invocations to see
-          // if a complete record may now be read.
-        *next_result = kNoPendingReadResult;
-      }
-    }
+    // Do not treat insufficient data as an error to return in the next call to
+    // DoPayloadRead() - instead, let the call fall through to check SSL_read()
+    // again. This is because DoTransportIO() may complete in between the next
+    // call to DoPayloadRead(), and thus it is important to check SSL_read() on
+    // subsequent invocations to see if a complete record may now be read.
+    if (pending_read_error_ == ERR_IO_PENDING)
+      pending_read_error_ = kNoPendingReadResult;
+  } else {
+    // No bytes were returned. Return the pending read error immediately.
+    DCHECK_NE(kNoPendingReadResult, pending_read_error_);
+    rv = pending_read_error_;
+    pending_read_error_ = kNoPendingReadResult;
   }
 
   if (rv >= 0) {
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index 342e5ea8..0b0df9a6 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -505,7 +505,6 @@
 SSLClientSocketPool::SSLClientSocketPool(
     int max_sockets,
     int max_sockets_per_group,
-    ClientSocketPoolHistograms* histograms,
     CertVerifier* cert_verifier,
     ChannelIDService* channel_id_service,
     TransportSecurityState* transport_security_state,
@@ -524,7 +523,6 @@
       base_(this,
             max_sockets,
             max_sockets_per_group,
-            histograms,
             ClientSocketPool::unused_idle_socket_timeout(),
             ClientSocketPool::used_idle_socket_timeout(),
             new SSLConnectJobFactory(
@@ -665,10 +663,6 @@
   return base_.ConnectionTimeout();
 }
 
-ClientSocketPoolHistograms* SSLClientSocketPool::histograms() const {
-  return base_.histograms();
-}
-
 bool SSLClientSocketPool::IsStalled() const {
   return base_.IsStalled();
 }
diff --git a/net/socket/ssl_client_socket_pool.h b/net/socket/ssl_client_socket_pool.h
index ae7036b..d071c73 100644
--- a/net/socket/ssl_client_socket_pool.h
+++ b/net/socket/ssl_client_socket_pool.h
@@ -14,7 +14,6 @@
 #include "net/http/http_response_info.h"
 #include "net/socket/client_socket_pool.h"
 #include "net/socket/client_socket_pool_base.h"
-#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/ssl/ssl_config_service.h"
 
@@ -183,7 +182,6 @@
   // try to create an SSL over SOCKS socket, |socks_pool| may be NULL.
   SSLClientSocketPool(int max_sockets,
                       int max_sockets_per_group,
-                      ClientSocketPoolHistograms* histograms,
                       CertVerifier* cert_verifier,
                       ChannelIDService* channel_id_service,
                       TransportSecurityState* transport_security_state,
@@ -237,8 +235,6 @@
 
   base::TimeDelta ConnectionTimeout() const override;
 
-  ClientSocketPoolHistograms* histograms() const override;
-
   // LowerLayeredPool implementation.
   bool IsStalled() const override;
 
diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc
index 333a524f..f9e680c 100644
--- a/net/socket/ssl_client_socket_pool_unittest.cc
+++ b/net/socket/ssl_client_socket_pool_unittest.cc
@@ -24,7 +24,6 @@
 #include "net/http/transport_security_state.h"
 #include "net/proxy/proxy_service.h"
 #include "net/socket/client_socket_handle.h"
-#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/next_proto.h"
 #include "net/socket/socket_test_util.h"
 #include "net/spdy/spdy_session.h"
@@ -91,10 +90,8 @@
             false,
             OnHostResolutionCallback(),
             TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT)),
-        transport_histograms_("MockTCP"),
         transport_socket_pool_(kMaxSockets,
                                kMaxSocketsPerGroup,
-                               &transport_histograms_,
                                &socket_factory_),
         proxy_transport_socket_params_(new TransportSocketParams(
             HostPortPair("proxy", 443),
@@ -106,10 +103,8 @@
             new SOCKSSocketParams(proxy_transport_socket_params_,
                                   true,
                                   HostPortPair("sockshost", 443))),
-        socks_histograms_("MockSOCKS"),
         socks_socket_pool_(kMaxSockets,
                            kMaxSocketsPerGroup,
-                           &socks_histograms_,
                            &transport_socket_pool_),
         http_proxy_socket_params_(
             new HttpProxySocketParams(proxy_transport_socket_params_,
@@ -122,10 +117,8 @@
                                       session_->spdy_session_pool(),
                                       true,
                                       NULL)),
-        http_proxy_histograms_("MockHttpProxy"),
         http_proxy_socket_pool_(kMaxSockets,
                                 kMaxSocketsPerGroup,
-                                &http_proxy_histograms_,
                                 &transport_socket_pool_,
                                 NULL,
                                 NULL) {
@@ -135,11 +128,9 @@
   }
 
   void CreatePool(bool transport_pool, bool http_proxy_pool, bool socks_pool) {
-    ssl_histograms_.reset(new ClientSocketPoolHistograms("SSLUnitTest"));
     pool_.reset(new SSLClientSocketPool(
-        kMaxSockets, kMaxSocketsPerGroup, ssl_histograms_.get(),
-        NULL /* cert_verifier */, NULL /* channel_id_service */,
-        NULL /* transport_security_state */,
+        kMaxSockets, kMaxSocketsPerGroup, NULL /* cert_verifier */,
+        NULL /* channel_id_service */, NULL /* transport_security_state */,
         NULL /* cert_transparency_verifier */, NULL /* cert_policy_enforcer */,
         std::string() /* ssl_session_cache_shard */, &socket_factory_,
         transport_pool ? &transport_socket_pool_ : NULL,
@@ -202,21 +193,17 @@
   const scoped_refptr<HttpNetworkSession> session_;
 
   scoped_refptr<TransportSocketParams> direct_transport_socket_params_;
-  ClientSocketPoolHistograms transport_histograms_;
   MockTransportClientSocketPool transport_socket_pool_;
 
   scoped_refptr<TransportSocketParams> proxy_transport_socket_params_;
 
   scoped_refptr<SOCKSSocketParams> socks_socket_params_;
-  ClientSocketPoolHistograms socks_histograms_;
   MockSOCKSClientSocketPool socks_socket_pool_;
 
   scoped_refptr<HttpProxySocketParams> http_proxy_socket_params_;
-  ClientSocketPoolHistograms http_proxy_histograms_;
   HttpProxyClientSocketPool http_proxy_socket_pool_;
 
   SSLConfig ssl_config_;
-  scoped_ptr<ClientSocketPoolHistograms> ssl_histograms_;
   scoped_ptr<SSLClientSocketPool> pool_;
 };
 
diff --git a/net/socket/transport_client_socket_pool.cc b/net/socket/transport_client_socket_pool.cc
index 6e54434..fdc0ef4 100644
--- a/net/socket/transport_client_socket_pool.cc
+++ b/net/socket/transport_client_socket_pool.cc
@@ -476,15 +476,17 @@
 TransportClientSocketPool::TransportClientSocketPool(
     int max_sockets,
     int max_sockets_per_group,
-    ClientSocketPoolHistograms* histograms,
     HostResolver* host_resolver,
     ClientSocketFactory* client_socket_factory,
     NetLog* net_log)
-    : base_(NULL, max_sockets, max_sockets_per_group, histograms,
+    : base_(NULL,
+            max_sockets,
+            max_sockets_per_group,
             ClientSocketPool::unused_idle_socket_timeout(),
             ClientSocketPool::used_idle_socket_timeout(),
             new TransportConnectJobFactory(client_socket_factory,
-                                           host_resolver, net_log)) {
+                                           host_resolver,
+                                           net_log)) {
   base_.EnableConnectBackupJobs();
 }
 
@@ -583,10 +585,6 @@
   return base_.ConnectionTimeout();
 }
 
-ClientSocketPoolHistograms* TransportClientSocketPool::histograms() const {
-  return base_.histograms();
-}
-
 bool TransportClientSocketPool::IsStalled() const {
   return base_.IsStalled();
 }
diff --git a/net/socket/transport_client_socket_pool.h b/net/socket/transport_client_socket_pool.h
index 2deb9474..cb51d84 100644
--- a/net/socket/transport_client_socket_pool.h
+++ b/net/socket/transport_client_socket_pool.h
@@ -18,7 +18,6 @@
 #include "net/dns/single_request_host_resolver.h"
 #include "net/socket/client_socket_pool.h"
 #include "net/socket/client_socket_pool_base.h"
-#include "net/socket/client_socket_pool_histograms.h"
 
 namespace net {
 
@@ -219,7 +218,6 @@
   TransportClientSocketPool(
       int max_sockets,
       int max_sockets_per_group,
-      ClientSocketPoolHistograms* histograms,
       HostResolver* host_resolver,
       ClientSocketFactory* client_socket_factory,
       NetLog* net_log);
@@ -253,7 +251,6 @@
       const std::string& type,
       bool include_nested_pools) const override;
   base::TimeDelta ConnectionTimeout() const override;
-  ClientSocketPoolHistograms* histograms() const override;
 
   // HigherLayeredPool implementation.
   bool IsStalled() const override;
diff --git a/net/socket/transport_client_socket_pool_unittest.cc b/net/socket/transport_client_socket_pool_unittest.cc
index c0687ef..c28ef65 100644
--- a/net/socket/transport_client_socket_pool_unittest.cc
+++ b/net/socket/transport_client_socket_pool_unittest.cc
@@ -18,7 +18,6 @@
 #include "net/base/test_completion_callback.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/socket/client_socket_handle.h"
-#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/socket_test_util.h"
 #include "net/socket/stream_socket.h"
 #include "net/socket/transport_client_socket_pool_test_util.h"
@@ -46,12 +45,10 @@
                 false,
                 OnHostResolutionCallback(),
                 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT)),
-        histograms_(new ClientSocketPoolHistograms("TCPUnitTest")),
         host_resolver_(new MockHostResolver),
         client_socket_factory_(&net_log_),
         pool_(kMaxSockets,
               kMaxSocketsPerGroup,
-              histograms_.get(),
               host_resolver_.get(),
               &client_socket_factory_,
               NULL) {
@@ -95,7 +92,6 @@
   bool connect_backup_jobs_enabled_;
   CapturingNetLog net_log_;
   scoped_refptr<TransportSocketParams> params_;
-  scoped_ptr<ClientSocketPoolHistograms> histograms_;
   scoped_ptr<MockHostResolver> host_resolver_;
   MockTransportClientSocketFactory client_socket_factory_;
   TransportClientSocketPool pool_;
@@ -829,7 +825,6 @@
   ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
   TransportClientSocketPool pool(kMaxSockets,
                                  kMaxSocketsPerGroup,
-                                 histograms_.get(),
                                  host_resolver_.get(),
                                  &client_socket_factory_,
                                  NULL);
@@ -872,7 +867,6 @@
   ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
   TransportClientSocketPool pool(kMaxSockets,
                                  kMaxSocketsPerGroup,
-                                 histograms_.get(),
                                  host_resolver_.get(),
                                  &client_socket_factory_,
                                  NULL);
@@ -914,7 +908,6 @@
   ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
   TransportClientSocketPool pool(kMaxSockets,
                                  kMaxSocketsPerGroup,
-                                 histograms_.get(),
                                  host_resolver_.get(),
                                  &client_socket_factory_,
                                  NULL);
@@ -948,7 +941,6 @@
   ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
   TransportClientSocketPool pool(kMaxSockets,
                                  kMaxSocketsPerGroup,
-                                 histograms_.get(),
                                  host_resolver_.get(),
                                  &client_socket_factory_,
                                  NULL);
@@ -983,7 +975,6 @@
   ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
   TransportClientSocketPool pool(kMaxSockets,
                                  kMaxSocketsPerGroup,
-                                 histograms_.get(),
                                  host_resolver_.get(),
                                  &client_socket_factory_,
                                  NULL);
@@ -1008,7 +999,6 @@
   ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
   TransportClientSocketPool pool(kMaxSockets,
                                  kMaxSocketsPerGroup,
-                                 histograms_.get(),
                                  host_resolver_.get(),
                                  &client_socket_factory_,
                                  NULL);
@@ -1036,7 +1026,6 @@
   ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
   TransportClientSocketPool pool(kMaxSockets,
                                  kMaxSocketsPerGroup,
-                                 histograms_.get(),
                                  host_resolver_.get(),
                                  &client_socket_factory_,
                                  NULL);
@@ -1076,7 +1065,6 @@
   ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
   TransportClientSocketPool pool(kMaxSockets,
                                  kMaxSocketsPerGroup,
-                                 histograms_.get(),
                                  host_resolver_.get(),
                                  &client_socket_factory_,
                                  NULL);
diff --git a/net/socket/websocket_transport_client_socket_pool.cc b/net/socket/websocket_transport_client_socket_pool.cc
index ec83529d..1dce34d3 100644
--- a/net/socket/websocket_transport_client_socket_pool.cc
+++ b/net/socket/websocket_transport_client_socket_pool.cc
@@ -229,18 +229,15 @@
 WebSocketTransportClientSocketPool::WebSocketTransportClientSocketPool(
     int max_sockets,
     int max_sockets_per_group,
-    ClientSocketPoolHistograms* histograms,
     HostResolver* host_resolver,
     ClientSocketFactory* client_socket_factory,
     NetLog* net_log)
     : TransportClientSocketPool(max_sockets,
                                 max_sockets_per_group,
-                                histograms,
                                 host_resolver,
                                 client_socket_factory,
                                 net_log),
       connect_job_delegate_(this),
-      histograms_(histograms),
       pool_net_log_(net_log),
       client_socket_factory_(client_socket_factory),
       host_resolver_(host_resolver),
@@ -456,11 +453,6 @@
   return TimeDelta::FromSeconds(kTransportConnectJobTimeoutInSeconds);
 }
 
-ClientSocketPoolHistograms* WebSocketTransportClientSocketPool::histograms()
-    const {
-  return histograms_;
-}
-
 bool WebSocketTransportClientSocketPool::IsStalled() const {
   return !stalled_request_queue_.empty();
 }
diff --git a/net/socket/websocket_transport_client_socket_pool.h b/net/socket/websocket_transport_client_socket_pool.h
index f0a94be..b951980 100644
--- a/net/socket/websocket_transport_client_socket_pool.h
+++ b/net/socket/websocket_transport_client_socket_pool.h
@@ -25,7 +25,6 @@
 namespace net {
 
 class ClientSocketFactory;
-class ClientSocketPoolHistograms;
 class HostResolver;
 class NetLog;
 class WebSocketEndpointLockManager;
@@ -118,7 +117,6 @@
  public:
   WebSocketTransportClientSocketPool(int max_sockets,
                                      int max_sockets_per_group,
-                                     ClientSocketPoolHistograms* histograms,
                                      HostResolver* host_resolver,
                                      ClientSocketFactory* client_socket_factory,
                                      NetLog* net_log);
@@ -159,7 +157,6 @@
       const std::string& type,
       bool include_nested_pools) const override;
   base::TimeDelta ConnectionTimeout() const override;
-  ClientSocketPoolHistograms* histograms() const override;
 
   // HigherLayeredPool implementation.
   bool IsStalled() const override;
@@ -228,7 +225,6 @@
   PendingConnectsMap pending_connects_;
   StalledRequestQueue stalled_request_queue_;
   StalledRequestMap stalled_request_map_;
-  ClientSocketPoolHistograms* const histograms_;
   NetLog* const pool_net_log_;
   ClientSocketFactory* const client_socket_factory_;
   HostResolver* const host_resolver_;
diff --git a/net/socket/websocket_transport_client_socket_pool_unittest.cc b/net/socket/websocket_transport_client_socket_pool_unittest.cc
index 39fbe3a..55c8f2309 100644
--- a/net/socket/websocket_transport_client_socket_pool_unittest.cc
+++ b/net/socket/websocket_transport_client_socket_pool_unittest.cc
@@ -24,7 +24,6 @@
 #include "net/base/test_completion_callback.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/socket/client_socket_handle.h"
-#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/socket_test_util.h"
 #include "net/socket/stream_socket.h"
 #include "net/socket/transport_client_socket_pool_test_util.h"
@@ -57,12 +56,10 @@
             false,
             OnHostResolutionCallback(),
             TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT)),
-        histograms_(new ClientSocketPoolHistograms("TCPUnitTest")),
         host_resolver_(new MockHostResolver),
         client_socket_factory_(&net_log_),
         pool_(kMaxSockets,
               kMaxSocketsPerGroup,
-              histograms_.get(),
               host_resolver_.get(),
               &client_socket_factory_,
               NULL) {}
@@ -108,7 +105,6 @@
 
   CapturingNetLog net_log_;
   scoped_refptr<TransportSocketParams> params_;
-  scoped_ptr<ClientSocketPoolHistograms> histograms_;
   scoped_ptr<MockHostResolver> host_resolver_;
   MockTransportClientSocketFactory client_socket_factory_;
   WebSocketTransportClientSocketPool pool_;
@@ -565,7 +561,6 @@
        IPv6FallbackSocketIPv4FinishesFirst) {
   WebSocketTransportClientSocketPool pool(kMaxSockets,
                                           kMaxSocketsPerGroup,
-                                          histograms_.get(),
                                           host_resolver_.get(),
                                           &client_socket_factory_,
                                           NULL);
@@ -606,7 +601,6 @@
        IPv6FallbackSocketIPv6FinishesFirst) {
   WebSocketTransportClientSocketPool pool(kMaxSockets,
                                           kMaxSocketsPerGroup,
-                                          histograms_.get(),
                                           host_resolver_.get(),
                                           &client_socket_factory_,
                                           NULL);
@@ -646,7 +640,6 @@
        IPv6NoIPv4AddressesToFallbackTo) {
   WebSocketTransportClientSocketPool pool(kMaxSockets,
                                           kMaxSocketsPerGroup,
-                                          histograms_.get(),
                                           host_resolver_.get(),
                                           &client_socket_factory_,
                                           NULL);
@@ -678,7 +671,6 @@
 TEST_F(WebSocketTransportClientSocketPoolTest, IPv4HasNoFallback) {
   WebSocketTransportClientSocketPool pool(kMaxSockets,
                                           kMaxSocketsPerGroup,
-                                          histograms_.get(),
                                           host_resolver_.get(),
                                           &client_socket_factory_,
                                           NULL);
@@ -711,7 +703,6 @@
 TEST_F(WebSocketTransportClientSocketPoolTest, IPv6InstantFail) {
   WebSocketTransportClientSocketPool pool(kMaxSockets,
                                           kMaxSocketsPerGroup,
-                                          histograms_.get(),
                                           host_resolver_.get(),
                                           &client_socket_factory_,
                                           NULL);
@@ -748,7 +739,6 @@
 TEST_F(WebSocketTransportClientSocketPoolTest, IPv6RapidFail) {
   WebSocketTransportClientSocketPool pool(kMaxSockets,
                                           kMaxSocketsPerGroup,
-                                          histograms_.get(),
                                           host_resolver_.get(),
                                           &client_socket_factory_,
                                           NULL);
@@ -793,7 +783,6 @@
 TEST_F(WebSocketTransportClientSocketPoolTest, FirstSuccessWins) {
   WebSocketTransportClientSocketPool pool(kMaxSockets,
                                           kMaxSocketsPerGroup,
-                                          histograms_.get(),
                                           host_resolver_.get(),
                                           &client_socket_factory_,
                                           NULL);
@@ -832,7 +821,6 @@
 TEST_F(WebSocketTransportClientSocketPoolTest, LastFailureWins) {
   WebSocketTransportClientSocketPool pool(kMaxSockets,
                                           kMaxSocketsPerGroup,
-                                          histograms_.get(),
                                           host_resolver_.get(),
                                           &client_socket_factory_,
                                           NULL);
@@ -875,7 +863,6 @@
 TEST_F(WebSocketTransportClientSocketPoolTest, DISABLED_OverallTimeoutApplies) {
   WebSocketTransportClientSocketPool pool(kMaxSockets,
                                           kMaxSocketsPerGroup,
-                                          histograms_.get(),
                                           host_resolver_.get(),
                                           &client_socket_factory_,
                                           NULL);
diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc
index 33492b4e2..4a8a46dd 100644
--- a/net/spdy/buffered_spdy_framer.cc
+++ b/net/spdy/buffered_spdy_framer.cc
@@ -190,6 +190,10 @@
   visitor_->OnStreamFrameData(stream_id, data, len, fin);
 }
 
+void BufferedSpdyFramer::OnStreamPadding(SpdyStreamId stream_id, size_t len) {
+  visitor_->OnStreamPadding(stream_id, len);
+}
+
 void BufferedSpdyFramer::OnSettings(bool clear_persisted) {
   visitor_->OnSettings(clear_persisted);
 }
diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h
index b8428ce..7025aad 100644
--- a/net/spdy/buffered_spdy_framer.h
+++ b/net/spdy/buffered_spdy_framer.h
@@ -71,6 +71,11 @@
                                  size_t len,
                                  bool fin) = 0;
 
+  // Called when padding is received (padding length field or padding octets).
+  // |stream_id| The stream receiving data.
+  // |len| The number of padding octets.
+  virtual void OnStreamPadding(SpdyStreamId stream_id, size_t len) = 0;
+
   // Called when a SETTINGS frame is received.
   // |clear_persisted| True if the respective flag is set on the SETTINGS frame.
   virtual void OnSettings(bool clear_persisted) = 0;
@@ -156,6 +161,7 @@
                          const char* data,
                          size_t len,
                          bool fin) override;
+  void OnStreamPadding(SpdyStreamId stream_id, size_t len) override;
   void OnSettings(bool clear_persisted) override;
   void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) override;
   void OnSettingsAck() override;
diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc
index 2f87551..9a967d2 100644
--- a/net/spdy/buffered_spdy_framer_unittest.cc
+++ b/net/spdy/buffered_spdy_framer_unittest.cc
@@ -82,6 +82,10 @@
     LOG(FATAL) << "Unexpected OnStreamFrameData call.";
   }
 
+  void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {
+    LOG(FATAL) << "Unexpected OnStreamPadding call.";
+  }
+
   void OnSettings(bool clear_persisted) override {}
 
   void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) override {
diff --git a/net/spdy/hpack_encoder.h b/net/spdy/hpack_encoder.h
index db012e4..752f2f57 100644
--- a/net/spdy/hpack_encoder.h
+++ b/net/spdy/hpack_encoder.h
@@ -45,7 +45,6 @@
   // Encodes the given header set into the given string. Only non-indexed
   // literal representations are emitted, bypassing the header table. Huffman
   // coding is also not used. Returns whether the encoding was successful.
-  // TODO(jgraettinger): Enable Huffman coding once the table as stablized.
   bool EncodeHeaderSetWithoutCompression(
       const std::map<std::string, std::string>& header_set,
       std::string* output);
diff --git a/net/spdy/hpack_huffman_aggregator.cc b/net/spdy/hpack_huffman_aggregator.cc
deleted file mode 100644
index 8080a9b..0000000
--- a/net/spdy/hpack_huffman_aggregator.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#include "net/spdy/hpack_huffman_aggregator.h"
-
-#include "base/metrics/bucket_ranges.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/sample_vector.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_request_info.h"
-#include "net/http/http_response_headers.h"
-#include "net/spdy/hpack_encoder.h"
-#include "net/spdy/spdy_http_utils.h"
-
-namespace net {
-
-namespace {
-
-const char kHistogramName[] = "Net.SpdyHpackEncodedCharacterFrequency";
-
-const size_t kTotalCountsPublishThreshold = 50000;
-
-// Each encoder uses the default dynamic table size of 4096 total bytes.
-const size_t kMaxEncoders = 20;
-
-}  // namespace
-
-HpackHuffmanAggregator::HpackHuffmanAggregator()
-  : counts_(256, 0),
-    total_counts_(0),
-    max_encoders_(kMaxEncoders) {
-}
-
-HpackHuffmanAggregator::~HpackHuffmanAggregator() {
-  STLDeleteContainerPairSecondPointers(encoders_.begin(), encoders_.end());
-  encoders_.clear();
-}
-
-void HpackHuffmanAggregator::AggregateTransactionCharacterCounts(
-    const HttpRequestInfo& request,
-    const HttpRequestHeaders& request_headers,
-    const ProxyServer& proxy,
-    const HttpResponseHeaders& response_headers) {
-  if (IsCrossOrigin(request)) {
-    return;
-  }
-  HpackEncoder* encoder = ObtainEncoder(SpdySessionKey(
-      HostPortPair::FromURL(request.url), proxy, request.privacy_mode));
-
-  // Convert and encode the request and response header sets.
-  {
-    SpdyHeaderBlock headers;
-    CreateSpdyHeadersFromHttpRequest(
-        request, request_headers, SPDY4, false, &headers);
-
-    std::string tmp_out;
-    encoder->EncodeHeaderSet(headers, &tmp_out);
-  }
-  {
-    SpdyHeaderBlock headers;
-    CreateSpdyHeadersFromHttpResponse(response_headers, &headers);
-
-    std::string tmp_out;
-    encoder->EncodeHeaderSet(headers, &tmp_out);
-  }
-  if (total_counts_ >= kTotalCountsPublishThreshold) {
-    PublishCounts();
-  }
-}
-
-// static
-bool HpackHuffmanAggregator::UseAggregator() {
-  const std::string group_name =
-      base::FieldTrialList::FindFullName("HpackHuffmanAggregator");
-  if (group_name == "Enabled") {
-    return true;
-  }
-  return false;
-}
-
-// static
-void HpackHuffmanAggregator::CreateSpdyHeadersFromHttpResponse(
-    const HttpResponseHeaders& headers,
-    SpdyHeaderBlock* headers_out) {
-  // Lower-case header names, and coalesce multiple values delimited by \0.
-  // Also add the fixed status header.
-  std::string name, value;
-  void* it = NULL;
-  while (headers.EnumerateHeaderLines(&it, &name, &value)) {
-    base::StringToLowerASCII(&name);
-    if (headers_out->find(name) == headers_out->end()) {
-      (*headers_out)[name] = value;
-    } else {
-      (*headers_out)[name] += std::string(1, '\0') + value;
-    }
-  }
-  (*headers_out)[":status"] = base::IntToString(headers.response_code());
-}
-
-// static
-bool HpackHuffmanAggregator::IsCrossOrigin(const HttpRequestInfo& request) {
-  // Require that the request is top-level, or that it shares
-  // an origin with its referer.
-  if ((request.load_flags & LOAD_MAIN_FRAME) == 0) {
-    std::string referer_str;
-    if (!request.extra_headers.GetHeader(HttpRequestHeaders::kReferer,
-                                         &referer_str)) {
-      // Require a referer.
-      return true;
-    }
-    GURL referer(referer_str);
-    if (!HostPortPair::FromURL(request.url).Equals(
-        HostPortPair::FromURL(referer))) {
-      // Cross-origin request.
-      return true;
-    }
-  }
-  return false;
-}
-
-HpackEncoder* HpackHuffmanAggregator::ObtainEncoder(const SpdySessionKey& key) {
-  for (OriginEncoders::iterator it = encoders_.begin();
-       it != encoders_.end(); ++it) {
-    if (key.Equals(it->first)) {
-      // Move to head of list and return.
-      OriginEncoder origin_encoder = *it;
-      encoders_.erase(it);
-      encoders_.push_front(origin_encoder);
-      return origin_encoder.second;
-    }
-  }
-  // Not found. Create a new encoder, evicting one if needed.
-  encoders_.push_front(std::make_pair(
-      key, new HpackEncoder(ObtainHpackHuffmanTable())));
-  if (encoders_.size() > max_encoders_) {
-    delete encoders_.back().second;
-    encoders_.pop_back();
-  }
-  encoders_.front().second->SetCharCountsStorage(&counts_, &total_counts_);
-  return encoders_.front().second;
-}
-
-void HpackHuffmanAggregator::PublishCounts() {
-  // base::Histogram requires that values be 1-indexed.
-  const size_t kRangeMin = 1;
-  const size_t kRangeMax = counts_.size() + 1;
-  const size_t kBucketCount = kRangeMax + 1;
-
-  base::BucketRanges ranges(kBucketCount + 1);
-  for (size_t i = 0; i != ranges.size(); ++i) {
-    ranges.set_range(i, i);
-  }
-  ranges.ResetChecksum();
-
-  // Copy |counts_| into a SampleVector.
-  base::SampleVector samples(&ranges);
-  for (size_t i = 0; i != counts_.size(); ++i) {
-    samples.Accumulate(i + 1, counts_[i]);
-  }
-
-  STATIC_HISTOGRAM_POINTER_BLOCK(
-      kHistogramName,
-      AddSamples(samples),
-      base::LinearHistogram::FactoryGet(
-          kHistogramName, kRangeMin, kRangeMax, kBucketCount,
-          base::HistogramBase::kUmaTargetedHistogramFlag));
-
-  // Clear counts.
-  counts_.assign(counts_.size(), 0);
-  total_counts_ = 0;
-}
-
-}  // namespace net
diff --git a/net/spdy/hpack_huffman_aggregator.h b/net/spdy/hpack_huffman_aggregator.h
deleted file mode 100644
index 46ba094..0000000
--- a/net/spdy/hpack_huffman_aggregator.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <list>
-#include <vector>
-
-#include "base/macros.h"
-#include "net/base/net_export.h"
-#include "net/spdy/spdy_header_block.h"
-#include "net/spdy/spdy_protocol.h"
-#include "net/spdy/spdy_session_key.h"
-
-namespace net {
-
-class HpackEncoder;
-class HttpRequestHeaders;
-struct HttpRequestInfo;
-class HttpResponseHeaders;
-class ProxyServer;
-
-namespace test {
-class HpackHuffmanAggregatorPeer;
-}  // namespace test
-
-class NET_EXPORT_PRIVATE HpackHuffmanAggregator {
- public:
-  friend class test::HpackHuffmanAggregatorPeer;
-
-  HpackHuffmanAggregator();
-  ~HpackHuffmanAggregator();
-
-  // Encodes the request and response headers of the transaction with an
-  // HpackEncoder keyed on the transaction's SpdySessionKey. Literal headers
-  // emitted by that encoder are aggregated into internal character counts,
-  // which are periodically published to a UMA histogram.
-  void AggregateTransactionCharacterCounts(
-    const HttpRequestInfo& request,
-    const HttpRequestHeaders& request_headers,
-    const ProxyServer& proxy,
-    const HttpResponseHeaders& response_headers);
-
-  // Returns whether the aggregator is enabled for the session by a field trial.
-  static bool UseAggregator();
-
- private:
-  typedef std::pair<SpdySessionKey, HpackEncoder*> OriginEncoder;
-  typedef std::list<OriginEncoder> OriginEncoders;
-
-  // Returns true if the request is considered cross-origin,
-  // and should not be aggregated.
-  static bool IsCrossOrigin(const HttpRequestInfo& request);
-
-  // Converts |headers| into SPDY headers block |headers_out|.
-  static void CreateSpdyHeadersFromHttpResponse(
-      const HttpResponseHeaders& headers,
-      SpdyHeaderBlock* headers_out);
-
-  // Creates or returns an encoder for the origin key.
-  HpackEncoder* ObtainEncoder(const SpdySessionKey& key);
-
-  // Publishes aggregated counts to a UMA histogram.
-  void PublishCounts();
-
-  std::vector<size_t> counts_;
-  size_t total_counts_;
-
-  OriginEncoders encoders_;
-  size_t max_encoders_;
-
-  DISALLOW_COPY_AND_ASSIGN(HpackHuffmanAggregator);
-};
-
-}  // namespace net
diff --git a/net/spdy/hpack_huffman_aggregator_test.cc b/net/spdy/hpack_huffman_aggregator_test.cc
deleted file mode 100644
index 23a9a43..0000000
--- a/net/spdy/hpack_huffman_aggregator_test.cc
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#include "net/spdy/hpack_huffman_aggregator.h"
-
-#include "base/metrics/histogram.h"
-#include "base/metrics/statistics_recorder.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_request_info.h"
-#include "net/http/http_response_headers.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-
-using ::testing::Each;
-using ::testing::ElementsAre;
-using ::testing::Eq;
-using ::testing::Pair;
-
-namespace {
-const char kHistogramName[] = "Net.SpdyHpackEncodedCharacterFrequency";
-}  // namespace
-
-namespace test {
-
-class HpackHuffmanAggregatorPeer {
- public:
-  explicit HpackHuffmanAggregatorPeer(HpackHuffmanAggregator* agg)
-      : agg_(agg) {}
-
-  std::vector<size_t>* counts() {
-    return &agg_->counts_;
-  }
-  HpackHuffmanAggregator::OriginEncoders* encoders() {
-    return &agg_->encoders_;
-  }
-  size_t total_counts() {
-    return agg_->total_counts_;
-  }
-  void set_total_counts(size_t total_counts) {
-    agg_->total_counts_ = total_counts;
-  }
-  void set_max_encoders(size_t max_encoders) {
-    agg_->max_encoders_ = max_encoders;
-  }
-  static bool IsCrossOrigin(const HttpRequestInfo& request) {
-    return HpackHuffmanAggregator::IsCrossOrigin(request);
-  }
-  static void CreateSpdyHeadersFromHttpResponse(
-      const HttpResponseHeaders& headers,
-      SpdyHeaderBlock* headers_out) {
-    HpackHuffmanAggregator::CreateSpdyHeadersFromHttpResponse(
-        headers, headers_out);
-  }
-  HpackEncoder* ObtainEncoder(const SpdySessionKey& key) {
-    return agg_->ObtainEncoder(key);
-  }
-  void PublishCounts() {
-    agg_->PublishCounts();
-  }
-
- private:
-  HpackHuffmanAggregator* agg_;
-};
-
-}  // namespace test
-
-class HpackHuffmanAggregatorTest : public ::testing::Test {
- protected:
-  HpackHuffmanAggregatorTest()
-      : peer_(&agg_) {}
-
-  HpackHuffmanAggregator agg_;
-  test::HpackHuffmanAggregatorPeer peer_;
-};
-
-TEST_F(HpackHuffmanAggregatorTest, CrossOriginDetermination) {
-  HttpRequestInfo request;
-  request.url = GURL("https://www.foo.com/a/page");
-
-  // Main load without referer.
-  request.load_flags = LOAD_MAIN_FRAME;
-  EXPECT_FALSE(peer_.IsCrossOrigin(request));
-
-  // Non-main load without referer. Treated as cross-origin.
-  request.load_flags = 0;
-  EXPECT_TRUE(peer_.IsCrossOrigin(request));
-
-  // Main load with different referer origin.
-  request.load_flags = LOAD_MAIN_FRAME;
-  request.extra_headers.SetHeader(HttpRequestHeaders::kReferer,
-                                  "https://www.bar.com/other/page");
-  EXPECT_FALSE(peer_.IsCrossOrigin(request));
-
-  // Non-main load with different referer orign.
-  request.load_flags = 0;
-  EXPECT_TRUE(peer_.IsCrossOrigin(request));
-
-  // Non-main load with same referer orign.
-  request.extra_headers.SetHeader(HttpRequestHeaders::kReferer,
-                                  "https://www.foo.com/other/page");
-  EXPECT_FALSE(peer_.IsCrossOrigin(request));
-
-  // Non-main load with same referer host but different schemes.
-  request.extra_headers.SetHeader(HttpRequestHeaders::kReferer,
-                                  "http://www.foo.com/other/page");
-  EXPECT_TRUE(peer_.IsCrossOrigin(request));
-}
-
-TEST_F(HpackHuffmanAggregatorTest, EncoderLRUQueue) {
-  peer_.set_max_encoders(2);
-
-  SpdySessionKey key1(HostPortPair("one.com", 443), ProxyServer::Direct(),
-                      PRIVACY_MODE_ENABLED);
-  SpdySessionKey key2(HostPortPair("two.com", 443), ProxyServer::Direct(),
-                      PRIVACY_MODE_ENABLED);
-  SpdySessionKey key3(HostPortPair("three.com", 443), ProxyServer::Direct(),
-                      PRIVACY_MODE_ENABLED);
-
-  // Creates one.com.
-  HpackEncoder* one = peer_.ObtainEncoder(key1);
-  EXPECT_EQ(1u, peer_.encoders()->size());
-
-  // Creates two.com. No evictions.
-  HpackEncoder* two = peer_.ObtainEncoder(key2);
-  EXPECT_EQ(2u, peer_.encoders()->size());
-  EXPECT_NE(one, two);
-
-  // Touch one.com.
-  EXPECT_EQ(one, peer_.ObtainEncoder(key1));
-
-  // Creates three.com. Evicts two.com, as it's least-recently used.
-  HpackEncoder* three = peer_.ObtainEncoder(key3);
-  EXPECT_EQ(one, peer_.ObtainEncoder(key1));
-  EXPECT_NE(one, three);
-  EXPECT_EQ(2u, peer_.encoders()->size());
-}
-
-TEST_F(HpackHuffmanAggregatorTest, PublishCounts) {
-  (*peer_.counts())[0] = 1;
-  (*peer_.counts())[255] = 10;
-  (*peer_.counts())[128] = 101;
-  peer_.set_total_counts(112);
-
-  peer_.PublishCounts();
-
-  // Internal counts were reset after being published.
-  EXPECT_THAT(*peer_.counts(), Each(Eq(0u)));
-  EXPECT_EQ(0u, peer_.total_counts());
-
-  // Verify histogram counts match the expectation.
-  scoped_ptr<base::HistogramSamples> samples =
-      base::StatisticsRecorder::FindHistogram(kHistogramName)
-      ->SnapshotSamples();
-
-  EXPECT_EQ(0, samples->GetCount(0));
-  EXPECT_EQ(1, samples->GetCount(1));
-  EXPECT_EQ(101, samples->GetCount(129));
-  EXPECT_EQ(10, samples->GetCount(256));
-  EXPECT_EQ(112, samples->TotalCount());
-
-  // Publish a second round of counts;
-  (*peer_.counts())[1] = 32;
-  (*peer_.counts())[128] = 5;
-  peer_.set_total_counts(37);
-
-  peer_.PublishCounts();
-
-  // Verify they've been aggregated into the previous counts.
-  samples = base::StatisticsRecorder::FindHistogram(kHistogramName)
-      ->SnapshotSamples();
-
-  EXPECT_EQ(0, samples->GetCount(0));
-  EXPECT_EQ(1, samples->GetCount(1));
-  EXPECT_EQ(32, samples->GetCount(2));
-  EXPECT_EQ(106, samples->GetCount(129));
-  EXPECT_EQ(10, samples->GetCount(256));
-  EXPECT_EQ(149, samples->TotalCount());
-}
-
-TEST_F(HpackHuffmanAggregatorTest, CreateSpdyResponseHeaders) {
-  char kRawHeaders[] =
-    "HTTP/1.1    202   Accepted  \0"
-    "Content-TYPE  : text/html; charset=utf-8  \0"
-    "Set-Cookie: foo=bar \0"
-    "Set-Cookie:   baz=bing \0"
-    "Cache-Control: pragma=no-cache \0"
-    "Cache-CONTROL: expires=12345 \0\0";
-
-  scoped_refptr<HttpResponseHeaders> parsed_headers(new HttpResponseHeaders(
-      std::string(kRawHeaders, arraysize(kRawHeaders) - 1)));
-
-  SpdyHeaderBlock headers;
-  peer_.CreateSpdyHeadersFromHttpResponse(*parsed_headers, &headers);
-  EXPECT_THAT(headers, ElementsAre(
-      Pair(":status", "202"),
-      Pair("cache-control", std::string("pragma=no-cache\0expires=12345", 29)),
-      Pair("content-type", "text/html; charset=utf-8"),
-      Pair("set-cookie", std::string("foo=bar\0baz=bing", 16))));
-}
-
-}  // namespace net
diff --git a/net/spdy/mock_spdy_framer_visitor.h b/net/spdy/mock_spdy_framer_visitor.h
index 480c367..ea59d7d 100644
--- a/net/spdy/mock_spdy_framer_visitor.h
+++ b/net/spdy/mock_spdy_framer_visitor.h
@@ -25,6 +25,7 @@
                                        const char* data,
                                        size_t len,
                                        bool fin));
+  MOCK_METHOD2(OnStreamPadding, void(SpdyStreamId stream_id, size_t len));
   MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id,
                                               const char* header_data,
                                               size_t len));
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
index 5ae6e94..ea5eaa5 100644
--- a/net/spdy/spdy_framer.cc
+++ b/net/spdy/spdy_framer.cc
@@ -2160,10 +2160,13 @@
         return 0;
       }
 
+      static_assert(kPadLengthFieldSize == 1,
+                    "Unexpected pad length field size.");
       remaining_padding_payload_length_ = *reinterpret_cast<const uint8*>(data);
       ++data;
       --len;
       --remaining_data_length_;
+      visitor_->OnStreamPadding(current_frame_stream_id_, kPadLengthFieldSize);
     } else {
       // We don't have the data available for parsing the pad length field. Keep
       // waiting.
@@ -2187,11 +2190,8 @@
     DCHECK_EQ(remaining_padding_payload_length_, remaining_data_length_);
     size_t amount_to_discard = std::min(remaining_padding_payload_length_, len);
     if (current_frame_type_ == DATA && amount_to_discard > 0) {
-      // The visitor needs to know about padding so it can send window updates.
-      // Communicate the padding to the visitor through a NULL data pointer,
-      // with a nonzero size.
-      visitor_->OnStreamFrameData(
-          current_frame_stream_id_, NULL, amount_to_discard, false);
+      DCHECK_LE(SPDY4, protocol_version());
+      visitor_->OnStreamPadding(current_frame_stream_id_, amount_to_discard);
     }
     data += amount_to_discard;
     len -= amount_to_discard;
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index 7f6452f..55c3fa7 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -180,6 +180,11 @@
                                  size_t len,
                                  bool fin) = 0;
 
+  // Called when padding is received (padding length field or padding octets).
+  // |stream_id| The stream receiving data.
+  // |len| The number of padding octets.
+  virtual void OnStreamPadding(SpdyStreamId stream_id, size_t len) = 0;
+
   // Called when a chunk of header data is available. This is called
   // after OnSynStream, OnSynReply, OnHeaders(), or OnPushPromise.
   // |stream_id| The stream receiving the header data.
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
index cda2bfcf..b87748b 100644
--- a/net/spdy/spdy_framer_test.cc
+++ b/net/spdy/spdy_framer_test.cc
@@ -89,6 +89,10 @@
       LOG(FATAL);
     }
 
+    void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {
+      LOG(FATAL);
+    }
+
     bool OnControlFrameHeaderData(SpdyStreamId stream_id,
                                   const char* header_data,
                                   size_t len) override {
@@ -291,14 +295,20 @@
       ++zero_length_data_frame_count_;
 
     data_bytes_ += len;
-    std::cerr << "OnStreamFrameData(" << stream_id << ", \"";
+    LOG(INFO) << "OnStreamFrameData(" << stream_id << ", \"";
     if (len > 0) {
       for (size_t i = 0 ; i < len; ++i) {
-        std::cerr << std::hex << (0xFF & static_cast<unsigned int>(data[i]))
+        LOG(INFO) << std::hex << (0xFF & static_cast<unsigned int>(data[i]))
                   << std::dec;
       }
     }
-    std::cerr << "\", " << len << ")\n";
+    LOG(INFO) << "\", " << len << ")\n";
+  }
+
+  void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {
+    EXPECT_EQ(header_stream_id_, stream_id);
+    data_bytes_ += len;
+    LOG(INFO) << "OnStreamPadding(" << stream_id << ", " << len << ")\n";
   }
 
   bool OnControlFrameHeaderData(SpdyStreamId stream_id,
@@ -4129,6 +4139,7 @@
   bytes_consumed += framer.GetDataFrameMinimumSize();
 
   // Send the padding length field.
+  EXPECT_CALL(visitor, OnStreamPadding(1, 1));
   CHECK_EQ(1u, framer.ProcessInput(frame->data() + bytes_consumed, 1));
   CHECK_EQ(framer.state(), SpdyFramer::SPDY_FORWARD_STREAM_FRAME);
   CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
@@ -4149,14 +4160,14 @@
   bytes_consumed += 3;
 
   // Send the first 100 bytes of the padding payload.
-  EXPECT_CALL(visitor, OnStreamFrameData(1, NULL, 100, false));
+  EXPECT_CALL(visitor, OnStreamPadding(1, 100));
   CHECK_EQ(100u, framer.ProcessInput(frame->data() + bytes_consumed, 100));
   CHECK_EQ(framer.state(), SpdyFramer::SPDY_CONSUME_PADDING);
   CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
   bytes_consumed += 100;
 
   // Send rest of the padding payload.
-  EXPECT_CALL(visitor, OnStreamFrameData(1, NULL, 18, false));
+  EXPECT_CALL(visitor, OnStreamPadding(1, 18));
   CHECK_EQ(18u, framer.ProcessInput(frame->data() + bytes_consumed, 18));
   CHECK_EQ(framer.state(), SpdyFramer::SPDY_RESET);
   CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
@@ -4934,7 +4945,9 @@
     } else {
       EXPECT_CALL(visitor, OnDataFrameHeader(1, 5, flags & DATA_FLAG_FIN));
       if (flags & DATA_FLAG_PADDED) {
-        // Expect Error since we don't set padded in payload.
+        // The first byte of payload is parsed as padding length.
+        EXPECT_CALL(visitor, OnStreamPadding(_, 1));
+        // Expect Error since the frame ends prematurely.
         EXPECT_CALL(visitor, OnError(_));
       } else {
         EXPECT_CALL(visitor, OnStreamFrameData(_, _, 5, false));
@@ -4946,10 +4959,9 @@
 
     framer.ProcessInput(frame->data(), frame->size());
     if ((flags & ~valid_data_flags) || (flags & DATA_FLAG_PADDED)) {
-        EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
-        EXPECT_EQ(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS,
-                  framer.error_code())
-            << SpdyFramer::ErrorCodeToString(framer.error_code());
+      EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+      EXPECT_EQ(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS, framer.error_code())
+          << SpdyFramer::ErrorCodeToString(framer.error_code());
     } else {
       EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
       EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
diff --git a/net/spdy/spdy_http_utils.h b/net/spdy/spdy_http_utils.h
index 5318d33..a0db4752 100644
--- a/net/spdy/spdy_http_utils.h
+++ b/net/spdy/spdy_http_utils.h
@@ -24,13 +24,13 @@
 // |response| output parameter for the HttpResponseInfo.
 // Returns true if successfully converted.  False if the SpdyHeaderBlock is
 // incomplete (e.g. missing 'status' or 'version').
-NET_EXPORT_PRIVATE bool SpdyHeadersToHttpResponse(
+NET_EXPORT bool SpdyHeadersToHttpResponse(
     const SpdyHeaderBlock& headers,
     SpdyMajorVersion protocol_version,
     HttpResponseInfo* response);
 
 // Create a SpdyHeaderBlock from HttpRequestInfo and HttpRequestHeaders.
-NET_EXPORT_PRIVATE void CreateSpdyHeadersFromHttpRequest(
+NET_EXPORT void CreateSpdyHeadersFromHttpRequest(
     const HttpRequestInfo& info,
     const HttpRequestHeaders& request_headers,
     SpdyMajorVersion protocol_version,
@@ -38,22 +38,22 @@
     SpdyHeaderBlock* headers);
 
 // Create a SpdyHeaderBlock from HttpResponseHeaders.
-NET_EXPORT_PRIVATE void CreateSpdyHeadersFromHttpResponse(
+NET_EXPORT void CreateSpdyHeadersFromHttpResponse(
     const HttpResponseHeaders& response_headers,
     SpdyMajorVersion protocol_version,
     SpdyHeaderBlock* headers);
 
 // Returns the URL associated with the |headers| by assembling the
 // scheme, host and path from the protocol specific keys.
-NET_EXPORT_PRIVATE GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers,
+NET_EXPORT GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers,
                                               SpdyMajorVersion protocol_version,
                                               bool pushed);
 
-NET_EXPORT_PRIVATE SpdyPriority ConvertRequestPriorityToSpdyPriority(
+NET_EXPORT SpdyPriority ConvertRequestPriorityToSpdyPriority(
     RequestPriority priority,
     SpdyMajorVersion protocol_version);
 
-NET_EXPORT_PRIVATE RequestPriority ConvertSpdyPriorityToRequestPriority(
+NET_EXPORT RequestPriority ConvertSpdyPriorityToRequestPriority(
     SpdyPriority priority,
     SpdyMajorVersion protocol_version);
 
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index fa0ae4d..680be8a 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -2045,14 +2045,6 @@
                                     size_t len,
                                     bool fin) {
   CHECK(in_io_loop_);
-
-  if (data == NULL && len != 0) {
-    // This is notification of consumed data padding.
-    // TODO(jgraettinger): Properly flow padding into WINDOW_UPDATE frames.
-    // See crbug.com/353012.
-    return;
-  }
-
   DCHECK_LT(len, 1u << 24);
   if (net_log().IsLogging()) {
     net_log().AddEvent(
@@ -2102,6 +2094,25 @@
   stream->OnDataReceived(buffer.Pass());
 }
 
+void SpdySession::OnStreamPadding(SpdyStreamId stream_id, size_t len) {
+  CHECK(in_io_loop_);
+
+  if (flow_control_state_ != FLOW_CONTROL_STREAM_AND_SESSION)
+    return;
+
+  // Decrease window size because padding bytes are received.
+  // Increase window size because padding bytes are consumed (by discarding).
+  // Net result: |session_unacked_recv_window_bytes_| increases by |len|,
+  // |session_recv_window_size_| does not change.
+  DecreaseRecvWindowSize(static_cast<int32>(len));
+  IncreaseRecvWindowSize(static_cast<int32>(len));
+
+  ActiveStreamMap::iterator it = active_streams_.find(stream_id);
+  if (it == active_streams_.end())
+    return;
+  it->second.stream->OnPaddingConsumed(len);
+}
+
 void SpdySession::OnSettings(bool clear_persisted) {
   CHECK(in_io_loop_);
 
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 234e318..52ecad76 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -540,6 +540,7 @@
   FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, AdjustRecvWindowSize);
   FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, AdjustSendWindowSize);
   FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlInactiveStream);
+  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlPadding);
   FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlNoReceiveLeaks);
   FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlNoSendLeaks);
   FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlEndToEnd);
@@ -833,6 +834,7 @@
                          const char* data,
                          size_t len,
                          bool fin) override;
+  void OnStreamPadding(SpdyStreamId stream_id, size_t len) override;
   void OnSettings(bool clear_persisted) override;
   void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) override;
   void OnWindowUpdate(SpdyStreamId stream_id,
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index 5b4862a..5713d26b 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -3630,6 +3630,46 @@
   data.RunFor(1);
 }
 
+// The frame header is not included in flow control, but frame payload
+// (including optional pad length and padding) is.
+TEST_P(SpdySessionTest, SessionFlowControlPadding) {
+  // Padding only exists in HTTP/2.
+  if (GetParam() < kProtoSPDY4MinimumVersion)
+    return;
+
+  session_deps_.host_resolver->set_synchronous_mode(true);
+
+  const int padding_length = 42;
+  MockConnect connect_data(SYNCHRONOUS, OK);
+  scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyBodyFrame(
+      1, kUploadData, kUploadDataSize, false, padding_length));
+  MockRead reads[] = {
+      CreateMockRead(*resp, 0), MockRead(ASYNC, 0, 1)  // EOF
+  };
+  DeterministicSocketData data(reads, arraysize(reads), NULL, 0);
+  data.set_connect_data(connect_data);
+  session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
+
+  CreateDeterministicNetworkSession();
+  base::WeakPtr<SpdySession> session =
+      CreateInsecureSpdySession(http_session_, key_, BoundNetLog());
+  EXPECT_EQ(SpdySession::FLOW_CONTROL_STREAM_AND_SESSION,
+            session->flow_control_state());
+
+  EXPECT_EQ(SpdySession::GetInitialWindowSize(GetParam()),
+            session->session_recv_window_size_);
+  EXPECT_EQ(0, session->session_unacked_recv_window_bytes_);
+
+  data.RunFor(1);
+
+  EXPECT_EQ(SpdySession::GetInitialWindowSize(GetParam()),
+            session->session_recv_window_size_);
+  EXPECT_EQ(kUploadDataSize + padding_length,
+            session->session_unacked_recv_window_bytes_);
+
+  data.RunFor(1);
+}
+
 // A delegate that drops any received data.
 class DropReceivedDataDelegate : public test::StreamDelegateSendImmediate {
  public:
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index fc2866b..85b2016 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -526,6 +526,17 @@
   delegate_->OnDataReceived(buffer.Pass());
 }
 
+void SpdyStream::OnPaddingConsumed(size_t len) {
+  if (session_->flow_control_state() >= SpdySession::FLOW_CONTROL_STREAM) {
+    // Decrease window size because padding bytes are received.
+    // Increase window size because padding bytes are consumed (by discarding).
+    // Net result: |session_unacked_recv_window_bytes_| increases by |len|,
+    // |session_recv_window_size_| does not change.
+    DecreaseRecvWindowSize(static_cast<int32>(len));
+    IncreaseRecvWindowSize(static_cast<int32>(len));
+  }
+}
+
 void SpdyStream::OnFrameWriteComplete(SpdyFrameType frame_type,
                                       size_t frame_size) {
   DCHECK_NE(type_, SPDY_PUSH_STREAM);
diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h
index ad916bc..26da6674 100644
--- a/net/spdy/spdy_stream.h
+++ b/net/spdy/spdy_stream.h
@@ -262,8 +262,8 @@
   // If stream flow control is turned off, this must not be called.
   void IncreaseRecvWindowSize(int32 delta_window_size);
 
-  // Called by OnDataReceived (which is in turn called by the session)
-  // to decrease this stream's receive window size by
+  // Called by OnDataReceived or OnPaddingConsumed (which are in turn called by
+  // the session) to decrease this stream's receive window size by
   // |delta_window_size|, which must be at least 1 and must not cause
   // this stream's receive window size to go negative.
   //
@@ -315,6 +315,10 @@
   //          the stream is being closed.
   void OnDataReceived(scoped_ptr<SpdyBuffer> buffer);
 
+  // Called by the SpdySession when padding is consumed to allow for the stream
+  // receiving window to be updated.
+  void OnPaddingConsumed(size_t len);
+
   // Called by the SpdySession when a frame has been successfully and
   // completely written. |frame_size| is the total size of the frame
   // in bytes, including framing overhead.
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 48c50d41..becdb910 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -243,6 +243,7 @@
                          const char* data,
                          size_t len,
                          bool fin) override {}
+  void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {}
   void OnSettings(bool clear_persisted) override {}
   void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) override {}
   void OnPing(SpdyPingId unique_id, bool is_ack) override {}
@@ -1224,6 +1225,18 @@
   return framer.SerializeData(data_ir);
 }
 
+SpdyFrame* SpdyTestUtil::ConstructSpdyBodyFrame(int stream_id,
+                                                const char* data,
+                                                uint32 len,
+                                                bool fin,
+                                                int padding_length) {
+  SpdyFramer framer(spdy_version_);
+  SpdyDataIR data_ir(stream_id, base::StringPiece(data, len));
+  data_ir.set_fin(fin);
+  data_ir.set_padding_len(padding_length);
+  return framer.SerializeData(data_ir);
+}
+
 SpdyFrame* SpdyTestUtil::ConstructWrappedSpdyFrame(
     const scoped_ptr<SpdyFrame>& frame,
     int stream_id) {
diff --git a/net/spdy/spdy_test_util_common.h b/net/spdy/spdy_test_util_common.h
index 58e1b00..c244074 100644
--- a/net/spdy/spdy_test_util_common.h
+++ b/net/spdy/spdy_test_util_common.h
@@ -537,6 +537,13 @@
   SpdyFrame* ConstructSpdyBodyFrame(int stream_id, const char* data,
                                     uint32 len, bool fin);
 
+  // Constructs a single SPDY data frame with the given content and padding.
+  SpdyFrame* ConstructSpdyBodyFrame(int stream_id,
+                                    const char* data,
+                                    uint32 len,
+                                    bool fin,
+                                    int padding_length);
+
   // Wraps |frame| in the payload of a data frame in stream |stream_id|.
   SpdyFrame* ConstructWrappedSpdyFrame(const scoped_ptr<SpdyFrame>& frame,
                                        int stream_id);
diff --git a/net/tools/flip_server/spdy_interface.cc b/net/tools/flip_server/spdy_interface.cc
index 22da7db..facbe5b 100644
--- a/net/tools/flip_server/spdy_interface.cc
+++ b/net/tools/flip_server/spdy_interface.cc
@@ -240,6 +240,11 @@
     interface->ProcessWriteInput(data, len);
 }
 
+void SpdySM::OnStreamPadding(SpdyStreamId stream_id, size_t len) {
+  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: StreamPadding(" << stream_id
+          << ", [" << len << "])";
+}
+
 void SpdySM::OnSynStream(SpdyStreamId stream_id,
                          SpdyStreamId associated_stream_id,
                          SpdyPriority priority,
diff --git a/net/tools/flip_server/spdy_interface.h b/net/tools/flip_server/spdy_interface.h
index 15cf0e9..20bc0c9 100644
--- a/net/tools/flip_server/spdy_interface.h
+++ b/net/tools/flip_server/spdy_interface.h
@@ -102,6 +102,11 @@
                          size_t len,
                          bool fin) override;
 
+  // Called when padding is received (padding length field or padding octets).
+  // |stream_id| The stream receiving data.
+  // |len| The number of padding octets.
+  void OnStreamPadding(SpdyStreamId stream_id, size_t len) override;
+
   // Called when a SETTINGS frame is received.
   // |clear_persisted| True if the respective flag is set on the SETTINGS frame.
   void OnSettings(bool clear_persisted) override {}
diff --git a/net/tools/flip_server/spdy_interface_test.cc b/net/tools/flip_server/spdy_interface_test.cc
index d8454382..6aa3d30 100644
--- a/net/tools/flip_server/spdy_interface_test.cc
+++ b/net/tools/flip_server/spdy_interface_test.cc
@@ -63,6 +63,7 @@
                                        const char*,
                                        size_t,
                                        bool));
+  MOCK_METHOD2(OnStreamPadding, void(SpdyStreamId, size_t));
   MOCK_METHOD1(OnSettings, void(bool clear_persisted));
   MOCK_METHOD3(OnSetting, void(SpdySettingsIds, uint8, uint32));
   MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc
index f8f75314..e99c62d 100644
--- a/net/tools/quic/quic_client.cc
+++ b/net/tools/quic/quic_client.cc
@@ -21,6 +21,7 @@
 #include "net/tools/quic/quic_epoll_connection_helper.h"
 #include "net/tools/quic/quic_socket_utils.h"
 #include "net/tools/quic/quic_spdy_client_stream.h"
+#include "net/tools/quic/spdy_utils.h"
 
 #ifndef SO_RXQ_OVFL
 #define SO_RXQ_OVFL 40
@@ -249,7 +250,8 @@
     LOG(DFATAL) << "stream creation failed!";
     return;
   }
-  stream->SendRequest(headers, body, fin);
+  stream->SendRequest(
+      SpdyUtils::RequestHeadersToSpdyHeaders(headers), body, fin);
   stream->set_visitor(this);
 }
 
@@ -321,15 +323,18 @@
 void QuicClient::OnClose(QuicDataStream* stream) {
   QuicSpdyClientStream* client_stream =
       static_cast<QuicSpdyClientStream*>(stream);
+  BalsaHeaders headers;
+  SpdyUtils::FillBalsaResponseHeaders(client_stream->headers(), &headers);
+
   if (response_listener_.get() != nullptr) {
     response_listener_->OnCompleteResponse(
-        stream->id(), client_stream->headers(), client_stream->data());
+        stream->id(), headers, client_stream->data());
   }
 
   // Store response headers and body.
   if (store_response_) {
-    latest_response_code_ = client_stream->headers().parsed_response_code();
-    client_stream->headers().DumpHeadersToString(&latest_response_headers_);
+    latest_response_code_ = headers.parsed_response_code();
+    headers.DumpHeadersToString(&latest_response_headers_);
     latest_response_body_ = client_stream->data();
   }
 }
diff --git a/net/tools/quic/quic_client_bin.cc b/net/tools/quic/quic_client_bin.cc
index 234f526..8154fc8 100644
--- a/net/tools/quic/quic_client_bin.cc
+++ b/net/tools/quic/quic_client_bin.cc
@@ -269,7 +269,8 @@
       cout << " " << kv.first << ": " << kv.second << endl;
     }
     cout << "body: " << FLAGS_body << endl;
-    cout << endl << "Response:";
+    cout << endl;
+    cout << "Response:" << endl;
     cout << "headers: " << client.latest_response_headers() << endl;
     cout << "body: " << client.latest_response_body() << endl;
   }
diff --git a/net/tools/quic/quic_dispatcher.cc b/net/tools/quic/quic_dispatcher.cc
index 363a2cb1..c25c495 100644
--- a/net/tools/quic/quic_dispatcher.cc
+++ b/net/tools/quic/quic_dispatcher.cc
@@ -8,15 +8,10 @@
 
 #include "base/debug/stack_trace.h"
 #include "base/logging.h"
-#include "base/stl_util.h"
 #include "net/quic/quic_blocked_writer_interface.h"
 #include "net/quic/quic_flags.h"
 #include "net/quic/quic_utils.h"
-#include "net/tools/epoll_server/epoll_server.h"
-#include "net/tools/quic/quic_default_packet_writer.h"
-#include "net/tools/quic/quic_epoll_connection_helper.h"
 #include "net/tools/quic/quic_per_connection_packet_writer.h"
-#include "net/tools/quic/quic_socket_utils.h"
 #include "net/tools/quic/quic_time_wait_list_manager.h"
 
 namespace net {
@@ -25,22 +20,30 @@
 
 using base::StringPiece;
 
-class DeleteSessionsAlarm : public EpollAlarm {
+namespace {
+
+// An alarm that informs the QuicDispatcher to delete old sessions.
+class DeleteSessionsAlarm : public QuicAlarm::Delegate {
  public:
   explicit DeleteSessionsAlarm(QuicDispatcher* dispatcher)
       : dispatcher_(dispatcher) {
   }
 
-  int64 OnAlarm() override {
-    EpollAlarm::OnAlarm();
+  QuicTime OnAlarm() override {
     dispatcher_->DeleteSessions();
-    return 0;
+    // Let the dispatcher register the alarm at appropriate time.
+    return QuicTime::Zero();
   }
 
  private:
+  // Not owned.
   QuicDispatcher* dispatcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeleteSessionsAlarm);
 };
 
+}  // namespace
+
 class QuicDispatcher::QuicFramerVisitor : public QuicFramerVisitorInterface {
  public:
   explicit QuicFramerVisitor(QuicDispatcher* dispatcher)
@@ -164,12 +167,12 @@
                                const QuicCryptoServerConfig& crypto_config,
                                const QuicVersionVector& supported_versions,
                                PacketWriterFactory* packet_writer_factory,
-                               EpollServer* epoll_server)
+                               QuicConnectionHelperInterface* helper)
     : config_(config),
       crypto_config_(crypto_config),
-      delete_sessions_alarm_(new DeleteSessionsAlarm(this)),
-      epoll_server_(epoll_server),
-      helper_(new QuicEpollConnectionHelper(epoll_server_)),
+      helper_(helper),
+      delete_sessions_alarm_(
+          helper_->CreateAlarm(new DeleteSessionsAlarm(this))),
       packet_writer_factory_(packet_writer_factory),
       connection_writer_factory_(this),
       supported_versions_(supported_versions),
@@ -186,9 +189,9 @@
   STLDeleteElements(&closed_session_list_);
 }
 
-void QuicDispatcher::Initialize(int fd) {
+void QuicDispatcher::InitializeWithWriter(QuicPacketWriter* writer) {
   DCHECK(writer_ == nullptr);
-  writer_.reset(CreateWriter(fd));
+  writer_.reset(writer);
   time_wait_list_manager_.reset(CreateQuicTimeWaitListManager());
 }
 
@@ -217,7 +220,7 @@
 
   // The session that we have identified as the one to which this packet
   // belongs.
-  QuicSession* session = nullptr;
+  QuicServerSession* session = nullptr;
   QuicConnectionId connection_id = header.connection_id;
   SessionMap::iterator it = session_map_.find(connection_id);
   if (it == session_map_.end()) {
@@ -264,10 +267,10 @@
   return false;
 }
 
-QuicSession* QuicDispatcher::AdditionalValidityChecksThenCreateSession(
+QuicServerSession* QuicDispatcher::AdditionalValidityChecksThenCreateSession(
     const QuicPacketPublicHeader& header,
     QuicConnectionId connection_id) {
-  QuicSession* session = CreateQuicSession(
+  QuicServerSession* session = CreateQuicSession(
       connection_id, current_server_address_, current_client_address_);
 
   if (session == nullptr) {
@@ -340,7 +343,7 @@
 
 void QuicDispatcher::Shutdown() {
   while (!session_map_.empty()) {
-    QuicSession* session = session_map_.begin()->second;
+    QuicServerSession* session = session_map_.begin()->second;
     session->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
     // Validate that the session removes itself from the session map on close.
     DCHECK(session_map_.empty() || session_map_.begin()->second != session);
@@ -365,8 +368,8 @@
                                         << QuicUtils::ErrorToString(error);
 
   if (closed_session_list_.empty()) {
-    epoll_server_->RegisterAlarmApproximateDelta(
-        0, delete_sessions_alarm_.get());
+    delete_sessions_alarm_->Cancel();
+    delete_sessions_alarm_->Set(helper()->GetClock()->ApproximateNow());
   }
   closed_session_list_.push_back(it->second);
   CleanUpSession(it);
@@ -394,15 +397,11 @@
   DVLOG(1) << "Connection " << connection_id << " removed from time wait list.";
 }
 
-QuicPacketWriter* QuicDispatcher::CreateWriter(int fd) {
-  return new QuicDefaultPacketWriter(fd);
-}
-
-QuicSession* QuicDispatcher::CreateQuicSession(
+QuicServerSession* QuicDispatcher::CreateQuicSession(
     QuicConnectionId connection_id,
     const IPEndPoint& server_address,
     const IPEndPoint& client_address) {
-  // The QuicSession takes ownership of |connection| below.
+  // The QuicServerSession takes ownership of |connection| below.
   QuicConnection* connection = new QuicConnection(
       connection_id, client_address, helper_.get(), connection_writer_factory_,
       /* owns_writer= */ true, Perspective::IS_SERVER,
@@ -415,7 +414,7 @@
 
 QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() {
   return new QuicTimeWaitListManager(
-      writer_.get(), this, epoll_server(), supported_versions());
+      writer_.get(), this, helper_.get(), supported_versions());
 }
 
 bool QuicDispatcher::HandlePacketForTimeWait(
diff --git a/net/tools/quic/quic_dispatcher.h b/net/tools/quic/quic_dispatcher.h
index fb9df23..7559c22d0 100644
--- a/net/tools/quic/quic_dispatcher.h
+++ b/net/tools/quic/quic_dispatcher.h
@@ -22,17 +22,12 @@
 
 namespace net {
 
-class EpollServer;
 class QuicConfig;
 class QuicCryptoServerConfig;
-class QuicSession;
+class QuicServerSession;
 
 namespace tools {
 
-class DeleteSessionsAlarm;
-class QuicEpollConnectionHelper;
-class QuicPacketWriterWrapper;
-
 namespace test {
 class QuicDispatcherPeer;
 }  // namespace test
@@ -46,7 +41,8 @@
 };
 
 class QuicDispatcher : public QuicServerSessionVisitor,
-                       public ProcessPacketInterface {
+                       public ProcessPacketInterface,
+                       public QuicBlockedWriterInterface {
  public:
   // Creates per-connection packet writers out of the QuicDispatcher's shared
   // QuicPacketWriter. The per-connection writers' IsWriteBlocked() state must
@@ -81,11 +77,12 @@
                  const QuicCryptoServerConfig& crypto_config,
                  const QuicVersionVector& supported_versions,
                  PacketWriterFactory* packet_writer_factory,
-                 EpollServer* epoll_server);
+                 QuicConnectionHelperInterface* helper);
 
   ~QuicDispatcher() override;
 
-  virtual void Initialize(int fd);
+  // Takes ownership of |writer|.
+  void InitializeWithWriter(QuicPacketWriter* writer);
 
   // Process the incoming packet by creating a new session, passing it to
   // an existing session, or passing it to the TimeWaitListManager.
@@ -94,7 +91,7 @@
                      const QuicEncryptedPacket& packet) override;
 
   // Called when the socket becomes writable to allow queued writes to happen.
-  virtual void OnCanWrite();
+  void OnCanWrite() override;
 
   // Returns true if there's anything in the blocked writer list.
   virtual bool HasPendingWrites() const;
@@ -119,21 +116,18 @@
   void OnConnectionRemovedFromTimeWaitList(
       QuicConnectionId connection_id) override;
 
-  typedef base::hash_map<QuicConnectionId, QuicSession*> SessionMap;
+  typedef base::hash_map<QuicConnectionId, QuicServerSession*> SessionMap;
+
+  const SessionMap& session_map() const { return session_map_; }
 
   // Deletes all sessions on the closed session list and clears the list.
   void DeleteSessions();
 
-  const SessionMap& session_map() const { return session_map_; }
-
  protected:
-  // Instantiates a new low-level packet writer. Caller takes ownership of the
-  // returned object.
-  virtual QuicPacketWriter* CreateWriter(int fd);
-
-  virtual QuicSession* CreateQuicSession(QuicConnectionId connection_id,
-                                         const IPEndPoint& server_address,
-                                         const IPEndPoint& client_address);
+  virtual QuicServerSession* CreateQuicSession(
+      QuicConnectionId connection_id,
+      const IPEndPoint& server_address,
+      const IPEndPoint& client_address);
 
   // Called by |framer_visitor_| when the public header has been parsed.
   virtual bool OnUnauthenticatedPublicHeader(
@@ -144,9 +138,9 @@
   // certain simple processing rules.  This method may apply validity checks to
   // reject stray packets.  If the packet appears to be valid, it calls
   // CreateQuicSession to create a new session for the packet.  Returns the
-  // QuicSession that was created, or nullptr if the packet failed the validity
-  // checks.
-  virtual QuicSession* AdditionalValidityChecksThenCreateSession(
+  // QuicServerSession that was created, or nullptr if the packet failed the
+  // validity checks.
+  virtual QuicServerSession* AdditionalValidityChecksThenCreateSession(
       const QuicPacketPublicHeader& header,
       QuicConnectionId connection_id);
 
@@ -158,8 +152,6 @@
     return time_wait_list_manager_.get();
   }
 
-  EpollServer* epoll_server() { return epoll_server_; }
-
   const QuicVersionVector& supported_versions() const {
     return supported_versions_;
   }
@@ -180,7 +172,7 @@
 
   QuicFramer* framer() { return &framer_; }
 
-  QuicEpollConnectionHelper* helper() { return helper_.get(); }
+  QuicConnectionHelperInterface* helper() { return helper_.get(); }
 
   QuicPacketWriter* writer() { return writer_.get(); }
 
@@ -229,16 +221,14 @@
   // Entity that manages connection_ids in time wait state.
   scoped_ptr<QuicTimeWaitListManager> time_wait_list_manager_;
 
-  // An alarm which deletes closed sessions.
-  scoped_ptr<DeleteSessionsAlarm> delete_sessions_alarm_;
-
   // The list of closed but not-yet-deleted sessions.
-  std::list<QuicSession*> closed_session_list_;
-
-  EpollServer* epoll_server_;  // Owned by the server.
+  std::list<QuicServerSession*> closed_session_list_;
 
   // The helper used for all connections.
-  scoped_ptr<QuicEpollConnectionHelper> helper_;
+  scoped_ptr<QuicConnectionHelperInterface> helper_;
+
+  // An alarm which deletes closed sessions.
+  scoped_ptr<QuicAlarm> delete_sessions_alarm_;
 
   // The writer to write to the socket with.
   scoped_ptr<QuicPacketWriter> writer_;
diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc
index 083d85a..57f21d8 100644
--- a/net/tools/quic/quic_dispatcher_test.cc
+++ b/net/tools/quic/quic_dispatcher_test.cc
@@ -10,11 +10,13 @@
 #include "net/quic/crypto/crypto_handshake.h"
 #include "net/quic/crypto/quic_crypto_server_config.h"
 #include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_connection_helper.h"
 #include "net/quic/quic_crypto_stream.h"
 #include "net/quic/quic_flags.h"
 #include "net/quic/quic_utils.h"
 #include "net/quic/test_tools/quic_test_utils.h"
 #include "net/tools/epoll_server/epoll_server.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
 #include "net/tools/quic/quic_packet_writer_wrapper.h"
 #include "net/tools/quic/quic_time_wait_list_manager.h"
 #include "net/tools/quic/test_tools/quic_dispatcher_peer.h"
@@ -28,7 +30,6 @@
 using net::test::MockConnection;
 using net::test::MockSession;
 using net::test::ValueRestore;
-using std::make_pair;
 using std::string;
 using testing::DoAll;
 using testing::InSequence;
@@ -41,6 +42,20 @@
 namespace test {
 namespace {
 
+class TestServerSession : public QuicServerSession {
+ public:
+  TestServerSession(const QuicConfig& config, QuicConnection* connection)
+      : QuicServerSession(config, connection, nullptr) {}
+  ~TestServerSession() override{};
+
+  MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer));
+  MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id));
+  MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestServerSession);
+};
+
 class TestDispatcher : public QuicDispatcher {
  public:
   explicit TestDispatcher(const QuicConfig& config,
@@ -50,13 +65,13 @@
                        crypto_config,
                        QuicSupportedVersions(),
                        new QuicDispatcher::DefaultPacketWriterFactory(),
-                       eps) {
+                       new QuicEpollConnectionHelper(eps)) {
   }
 
-  MOCK_METHOD3(CreateQuicSession, QuicSession*(
-      QuicConnectionId connection_id,
-      const IPEndPoint& server_address,
-      const IPEndPoint& client_address));
+  MOCK_METHOD3(CreateQuicSession,
+               QuicServerSession*(QuicConnectionId connection_id,
+                                  const IPEndPoint& server_address,
+                                  const IPEndPoint& client_address));
 
   using QuicDispatcher::current_server_address;
   using QuicDispatcher::current_client_address;
@@ -77,17 +92,20 @@
     LOG(ERROR) << "Unregistering " << connection_id();
     dispatcher_->OnConnectionClosed(connection_id(), QUIC_NO_ERROR);
   }
+
  private:
   QuicDispatcher* dispatcher_;
 };
 
-QuicSession* CreateSession(QuicDispatcher* dispatcher,
-                           QuicConnectionId connection_id,
-                           const IPEndPoint& client_address,
-                           MockSession** session) {
+QuicServerSession* CreateSession(QuicDispatcher* dispatcher,
+                                 const QuicConfig& config,
+                                 QuicConnectionId connection_id,
+                                 const IPEndPoint& client_address,
+                                 TestServerSession** session) {
   MockServerConnection* connection =
       new MockServerConnection(connection_id, dispatcher);
-  *session = new MockSession(connection);
+  *session = new TestServerSession(config, connection);
+  connection->set_visitor(*session);
   ON_CALL(*connection, SendConnectionClose(_)).WillByDefault(
       WithoutArgs(Invoke(
           connection, &MockServerConnection::UnregisterOnConnectionClosed)));
@@ -97,32 +115,17 @@
   return *session;
 }
 
-class MockTimeWaitListManager : public QuicTimeWaitListManager {
- public:
-  MockTimeWaitListManager(QuicPacketWriter* writer,
-                          QuicServerSessionVisitor* visitor,
-                          EpollServer* eps)
-      : QuicTimeWaitListManager(writer, visitor, eps, QuicSupportedVersions()) {
-  }
-
-  MOCK_METHOD5(ProcessPacket,
-               void(const IPEndPoint& server_address,
-                    const IPEndPoint& client_address,
-                    QuicConnectionId connection_id,
-                    QuicPacketSequenceNumber sequence_number,
-                    const QuicEncryptedPacket& packet));
-};
-
 class QuicDispatcherTest : public ::testing::Test {
  public:
   QuicDispatcherTest()
-      : crypto_config_(QuicCryptoServerConfig::TESTING,
+      : helper_(&eps_),
+        crypto_config_(QuicCryptoServerConfig::TESTING,
                        QuicRandom::GetInstance()),
         dispatcher_(config_, crypto_config_, &eps_),
         time_wait_list_manager_(nullptr),
         session1_(nullptr),
         session2_(nullptr) {
-    dispatcher_.Initialize(1);
+    dispatcher_.InitializeWithWriter(new QuicDefaultPacketWriter(1));
   }
 
   ~QuicDispatcherTest() override {}
@@ -163,20 +166,21 @@
 
   void CreateTimeWaitListManager() {
     time_wait_list_manager_ = new MockTimeWaitListManager(
-        QuicDispatcherPeer::GetWriter(&dispatcher_), &dispatcher_, &eps_);
+        QuicDispatcherPeer::GetWriter(&dispatcher_), &dispatcher_, &helper_);
     // dispatcher_ takes the ownership of time_wait_list_manager_.
     QuicDispatcherPeer::SetTimeWaitListManager(&dispatcher_,
                                                time_wait_list_manager_);
   }
 
   EpollServer eps_;
+  QuicEpollConnectionHelper helper_;
   QuicConfig config_;
   QuicCryptoServerConfig crypto_config_;
   IPEndPoint server_address_;
   TestDispatcher dispatcher_;
   MockTimeWaitListManager* time_wait_list_manager_;
-  MockSession* session1_;
-  MockSession* session2_;
+  TestServerSession* session1_;
+  TestServerSession* session2_;
   string data_;
 };
 
@@ -187,16 +191,15 @@
   server_address_ = IPEndPoint(any4, 5);
 
   EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, client_address))
-      .WillOnce(testing::Return(CreateSession(
-          &dispatcher_, 1, client_address, &session1_)));
+      .WillOnce(testing::Return(
+          CreateSession(&dispatcher_, config_, 1, client_address, &session1_)));
   ProcessPacket(client_address, 1, true, "foo");
   EXPECT_EQ(client_address, dispatcher_.current_client_address());
   EXPECT_EQ(server_address_, dispatcher_.current_server_address());
 
-
   EXPECT_CALL(dispatcher_, CreateQuicSession(2, _, client_address))
-      .WillOnce(testing::Return(CreateSession(
-                    &dispatcher_, 2, client_address, &session2_)));
+      .WillOnce(testing::Return(
+          CreateSession(&dispatcher_, config_, 2, client_address, &session2_)));
   ProcessPacket(client_address, 2, true, "bar");
 
   EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()),
@@ -210,8 +213,8 @@
   IPEndPoint client_address(net::test::Loopback4(), 1);
 
   EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address))
-      .WillOnce(testing::Return(CreateSession(
-                    &dispatcher_, 1, client_address, &session1_)));
+      .WillOnce(testing::Return(
+          CreateSession(&dispatcher_, config_, 1, client_address, &session1_)));
 
   ProcessPacket(client_address, 1, true, "foo");
 
@@ -229,7 +232,7 @@
   QuicConnectionId connection_id = 1;
   EXPECT_CALL(dispatcher_, CreateQuicSession(connection_id, _, client_address))
       .WillOnce(testing::Return(CreateSession(
-                    &dispatcher_, connection_id, client_address, &session1_)));
+          &dispatcher_, config_, connection_id, client_address, &session1_)));
   ProcessPacket(client_address, connection_id, true, "foo");
 
   // Close the connection by sending public reset packet.
@@ -257,6 +260,8 @@
   // wait list manager.
   EXPECT_CALL(*time_wait_list_manager_,
               ProcessPacket(_, _, connection_id, _, _)).Times(1);
+  EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
+      .Times(0);
   ProcessPacket(client_address, connection_id, true, "foo");
 }
 
@@ -270,6 +275,8 @@
   EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, _)).Times(0);
   EXPECT_CALL(*time_wait_list_manager_,
               ProcessPacket(_, _, connection_id, _, _)).Times(1);
+  EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
+      .Times(1);
   ProcessPacket(client_address, connection_id, false, "data");
 }
 
@@ -284,6 +291,8 @@
   // dispatcher_ should drop this packet.
   EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, client_address)).Times(0);
   EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0);
+  EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
+      .Times(0);
   ProcessPacket(client_address, 1, true, "foo");
   EXPECT_EQ(client_address, dispatcher_.current_client_address());
   EXPECT_EQ(server_address_, dispatcher_.current_server_address());
@@ -321,13 +330,13 @@
     IPEndPoint client_address(net::test::Loopback4(), 1);
 
     EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address))
-        .WillOnce(testing::Return(CreateSession(
-                      &dispatcher_, 1, client_address, &session1_)));
+        .WillOnce(testing::Return(CreateSession(&dispatcher_, config_, 1,
+                                                client_address, &session1_)));
     ProcessPacket(client_address, 1, true, "foo");
 
     EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address))
-        .WillOnce(testing::Return(CreateSession(
-                      &dispatcher_, 2, client_address, &session2_)));
+        .WillOnce(testing::Return(CreateSession(&dispatcher_, config_, 2,
+                                                client_address, &session2_)));
     ProcessPacket(client_address, 2, true, "bar");
 
     blocked_list_ = QuicDispatcherPeer::GetWriteBlockedList(&dispatcher_);
diff --git a/net/tools/quic/quic_in_memory_cache.cc b/net/tools/quic/quic_in_memory_cache.cc
index 4f55f73..5cc3619 100644
--- a/net/tools/quic/quic_in_memory_cache.cc
+++ b/net/tools/quic/quic_in_memory_cache.cc
@@ -8,49 +8,22 @@
 #include "base/files/file_util.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "net/tools/balsa/balsa_headers.h"
+#include "base/strings/string_util.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/spdy/spdy_http_utils.h"
 
 using base::FilePath;
+using base::IntToString;
 using base::StringPiece;
 using std::string;
 
 namespace net {
 namespace tools {
 
-// Specifies the directory used during QuicInMemoryCache
-// construction to seed the cache. Cache directory can be
-// generated using `wget -p --save-headers <url>
-string FLAGS_quic_in_memory_cache_dir = "";
+QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {}
 
-namespace {
-
-// BalsaVisitor implementation (glue) which caches response bodies.
-class CachingBalsaVisitor : public NoOpBalsaVisitor {
- public:
-  CachingBalsaVisitor() : done_framing_(false) {}
-  void ProcessBodyData(const char* input, size_t size) override {
-    AppendToBody(input, size);
-  }
-  void MessageDone() override { done_framing_ = true; }
-  void HandleHeaderError(BalsaFrame* framer) override { UnhandledError(); }
-  void HandleHeaderWarning(BalsaFrame* framer) override { UnhandledError(); }
-  void HandleChunkingError(BalsaFrame* framer) override { UnhandledError(); }
-  void HandleBodyError(BalsaFrame* framer) override { UnhandledError(); }
-  void UnhandledError() {
-    LOG(DFATAL) << "Unhandled error framing HTTP.";
-  }
-  void AppendToBody(const char* input, size_t size) {
-    body_.append(input, size);
-  }
-  bool done_framing() const { return done_framing_; }
-  const string& body() const { return body_; }
-
- private:
-  bool done_framing_;
-  string body_;
-};
-
-}  // namespace
+QuicInMemoryCache::Response::~Response() {}
 
 // static
 QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
@@ -72,18 +45,19 @@
                                           int response_code,
                                           StringPiece response_detail,
                                           StringPiece body) {
-  BalsaHeaders response_headers;
-  response_headers.SetRequestFirstlineFromStringPieces(
-      "HTTP/1.1", base::IntToString(response_code), response_detail);
-  response_headers.AppendHeader("content-length",
-                                base::IntToString(body.length()));
-
+  SpdyHeaderBlock response_headers;
+  response_headers[":version"] = "HTTP/1.1";
+  string status = IntToString(response_code) + " ";
+  response_detail.AppendToString(&status);
+  response_headers[":status"] = status;
+  response_headers["content-length"] =
+      IntToString(static_cast<int>(body.length()));
   AddResponse(host, path, response_headers, body);
 }
 
 void QuicInMemoryCache::AddResponse(StringPiece host,
                                     StringPiece path,
-                                    const BalsaHeaders& response_headers,
+                                    const SpdyHeaderBlock& response_headers,
                                     StringPiece response_body) {
   AddResponseImpl(host, path, REGULAR_RESPONSE, response_headers,
                   response_body);
@@ -92,90 +66,82 @@
 void QuicInMemoryCache::AddSpecialResponse(StringPiece host,
                                            StringPiece path,
                                            SpecialResponseType response_type) {
-  AddResponseImpl(host, path, response_type, BalsaHeaders(), "");
+  AddResponseImpl(host, path, response_type, SpdyHeaderBlock(), "");
 }
 
-QuicInMemoryCache::QuicInMemoryCache() {
-  Initialize();
-}
+QuicInMemoryCache::QuicInMemoryCache() {}
 
 void QuicInMemoryCache::ResetForTests() {
   STLDeleteValues(&responses_);
-  Initialize();
 }
 
-void QuicInMemoryCache::Initialize() {
-  // If there's no defined cache dir, we have no initialization to do.
-  if (FLAGS_quic_in_memory_cache_dir.empty()) {
-    VLOG(1) << "No cache directory found. Skipping initialization.";
+void QuicInMemoryCache::InitializeFromDirectory(const string& cache_directory) {
+  if (cache_directory.empty()) {
+    LOG(DFATAL) << "cache_directory must not be empty.";
     return;
   }
   VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
-          << FLAGS_quic_in_memory_cache_dir;
-
-  FilePath directory(FLAGS_quic_in_memory_cache_dir);
+          << cache_directory;
+  FilePath directory(FilePath::FromUTF8Unsafe(cache_directory));
   base::FileEnumerator file_list(directory,
                                  true,
                                  base::FileEnumerator::FILES);
 
-  for (FilePath file = file_list.Next(); !file.empty();
-       file = file_list.Next()) {
+  for (FilePath file_iter = file_list.Next(); !file_iter.empty();
+       file_iter = file_list.Next()) {
     // Need to skip files in .svn directories
-    if (file.value().find("/.svn/") != string::npos) {
+    if (file_iter.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) {
       continue;
     }
 
-    BalsaHeaders request_headers, response_headers;
+    // Tease apart filename into host and path.
+    string file = file_iter.AsUTF8Unsafe();
+    file.erase(0, cache_directory.length());
+    if (file[0] == '/') {
+      file.erase(0, 1);
+    }
 
     string file_contents;
-    base::ReadFileToString(file, &file_contents);
-
-    // Frame HTTP.
-    CachingBalsaVisitor caching_visitor;
-    BalsaFrame framer;
-    framer.set_balsa_headers(&response_headers);
-    framer.set_balsa_visitor(&caching_visitor);
-    size_t processed = 0;
-    while (processed < file_contents.length() &&
-           !caching_visitor.done_framing()) {
-      processed += framer.ProcessInput(file_contents.c_str() + processed,
-                                       file_contents.length() - processed);
+    base::ReadFileToString(file_iter, &file_contents);
+    int file_len = static_cast<int>(file_contents.length());
+    int headers_end = HttpUtil::LocateEndOfHeaders(file_contents.data(),
+                                                   file_len);
+    if (headers_end < 1) {
+      LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file;
+      continue;
     }
 
-    if (!caching_visitor.done_framing()) {
-      LOG(DFATAL) << "Did not frame entire message from file: " << file.value()
-                  << " (" << processed << " of " << file_contents.length()
-                  << " bytes).";
-    }
-    if (processed < file_contents.length()) {
-      // Didn't frame whole file. Assume remainder is body.
-      // This sometimes happens as a result of incompatibilities between
-      // BalsaFramer and wget's serialization of HTTP sans content-length.
-      caching_visitor.AppendToBody(file_contents.c_str() + processed,
-                                   file_contents.length() - processed);
-      processed += file_contents.length();
-    }
+    string raw_headers =
+        HttpUtil::AssembleRawHeaders(file_contents.data(), headers_end);
 
-    StringPiece base = file.value();
-    if (response_headers.HasHeader("X-Original-Url")) {
-      base = response_headers.GetHeader("X-Original-Url");
-      response_headers.RemoveAllOfHeader("X-Original-Url");
-      // Remove the protocol so that the string is of the form host + path,
-      // which is parsed properly below.
-      if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
-        base.remove_prefix(8);
-      } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
-        base.remove_prefix(7);
+    scoped_refptr<HttpResponseHeaders> response_headers =
+        new HttpResponseHeaders(raw_headers);
+
+    string base;
+    if (response_headers->GetNormalizedHeader("X-Original-Url", &base)) {
+      response_headers->RemoveHeader("X-Original-Url");
+      // Remove the protocol so we can add it below.
+      if (StartsWithASCII(base, "https://", false)) {
+        base = base.substr(8);
+      } else if (StartsWithASCII(base, "http://", false)) {
+        base = base.substr(7);
       }
+    } else {
+      base = file;
     }
-    int path_start = base.find_first_of('/');
-    DCHECK_LT(0, path_start);
-    StringPiece host(base.substr(0, path_start));
-    StringPiece path(base.substr(path_start));
+
+    size_t path_start = base.find_first_of('/');
+    StringPiece host(StringPiece(base).substr(0, path_start));
+    StringPiece path(StringPiece(base).substr(path_start));
     if (path[path.length() - 1] == ',') {
       path.remove_suffix(1);
     }
-    AddResponse(host, path, response_headers, caching_visitor.body());
+
+    StringPiece body(file_contents.data() + headers_end,
+                     file_contents.size() - headers_end);
+    SpdyHeaderBlock header_block;
+    CreateSpdyHeadersFromHttpResponse(*response_headers, SPDY3, &header_block);
+    AddResponse(host, path, header_block, body);
   }
 }
 
@@ -187,7 +153,7 @@
     StringPiece host,
     StringPiece path,
     SpecialResponseType response_type,
-    const BalsaHeaders& response_headers,
+    const SpdyHeaderBlock& response_headers,
     StringPiece response_body) {
   string key = GetKey(host, path);
   VLOG(1) << "Adding response for: " << key;
diff --git a/net/tools/quic/quic_in_memory_cache.h b/net/tools/quic/quic_in_memory_cache.h
index ce5b43d..73d784b 100644
--- a/net/tools/quic/quic_in_memory_cache.h
+++ b/net/tools/quic/quic_in_memory_cache.h
@@ -10,9 +10,7 @@
 #include "base/containers/hash_tables.h"
 #include "base/memory/singleton.h"
 #include "base/strings/string_piece.h"
-#include "net/tools/balsa/balsa_frame.h"
-#include "net/tools/balsa/balsa_headers.h"
-#include "net/tools/balsa/noop_balsa_visitor.h"
+#include "net/spdy/spdy_framer.h"
 
 template <typename T> struct DefaultSingletonTraits;
 
@@ -21,9 +19,7 @@
 
 namespace test {
 class QuicInMemoryCachePeer;
-}  // namespace
-
-extern std::string FLAGS_quic_in_memory_cache_dir;
+}  // namespace test
 
 class QuicServer;
 
@@ -41,12 +37,12 @@
   // Container for response header/body pairs.
   class Response {
    public:
-    Response() : response_type_(REGULAR_RESPONSE) {}
-    ~Response() {}
+    Response();
+    ~Response();
 
     SpecialResponseType response_type() const { return response_type_; }
-    const BalsaHeaders& headers() const { return headers_; }
-    const base::StringPiece body() const { return base::StringPiece(body_); }
+    const SpdyHeaderBlock& headers() const { return headers_; }
+    const StringPiece body() const { return StringPiece(body_); }
 
    private:
     friend class QuicInMemoryCache;
@@ -54,15 +50,15 @@
     void set_response_type(SpecialResponseType response_type) {
       response_type_ = response_type;
     }
-    void set_headers(const BalsaHeaders& headers) {
-      headers_.CopyFrom(headers);
+    void set_headers(const SpdyHeaderBlock& headers) {
+      headers_ = headers;
     }
     void set_body(base::StringPiece body) {
       body.CopyToString(&body_);
     }
 
     SpecialResponseType response_type_;
-    BalsaHeaders headers_;
+    SpdyHeaderBlock headers_;
     std::string body_;
 
     DISALLOW_COPY_AND_ASSIGN(Response);
@@ -87,7 +83,7 @@
   // Add a response to the cache.
   void AddResponse(base::StringPiece host,
                    base::StringPiece path,
-                   const BalsaHeaders& response_headers,
+                   const SpdyHeaderBlock& response_headers,
                    base::StringPiece response_body);
 
   // Simulate a special behavior at a particular path.
@@ -95,6 +91,9 @@
                           base::StringPiece path,
                           SpecialResponseType response_type);
 
+  // |cache_cirectory| can be generated using `wget -p --save-headers <url>`.
+  void InitializeFromDirectory(const std::string& cache_directory);
+
  private:
   typedef base::hash_map<std::string, Response*> ResponseMap;
   friend struct DefaultSingletonTraits<QuicInMemoryCache>;
@@ -105,12 +104,10 @@
 
   void ResetForTests();
 
-  void Initialize();
-
   void AddResponseImpl(base::StringPiece host,
                        base::StringPiece path,
                        SpecialResponseType response_type,
-                       const BalsaHeaders& response_headers,
+                       const SpdyHeaderBlock& response_headers,
                        base::StringPiece response_body);
 
   std::string GetKey(base::StringPiece host, base::StringPiece path) const;
diff --git a/net/tools/quic/quic_in_memory_cache_test.cc b/net/tools/quic/quic_in_memory_cache_test.cc
index e8e603ef..d93a344 100644
--- a/net/tools/quic/quic_in_memory_cache_test.cc
+++ b/net/tools/quic/quic_in_memory_cache_test.cc
@@ -7,6 +7,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/singleton.h"
 #include "base/path_service.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "net/tools/balsa/balsa_headers.h"
@@ -25,27 +26,33 @@
 class QuicInMemoryCacheTest : public ::testing::Test {
  protected:
   QuicInMemoryCacheTest() {
+    QuicInMemoryCachePeer::ResetForTests();
+  }
+
+  ~QuicInMemoryCacheTest() override { QuicInMemoryCachePeer::ResetForTests(); }
+
+  void CreateRequest(string host, string path, BalsaHeaders* headers) {
+    headers->SetRequestFirstlineFromStringPieces("GET", path, "HTTP/1.1");
+    headers->ReplaceOrAppendHeader("host", host);
+  }
+
+  string CacheDirectory() {
     base::FilePath path;
     PathService::Get(base::DIR_SOURCE_ROOT, &path);
     path = path.AppendASCII("net").AppendASCII("data")
         .AppendASCII("quic_in_memory_cache_data");
     // The file path is known to be an ascii string.
-    FLAGS_quic_in_memory_cache_dir = path.MaybeAsASCII();
+    return path.MaybeAsASCII();
   }
-
-  ~QuicInMemoryCacheTest() override { QuicInMemoryCachePeer::ResetForTests(); }
-
-  void CreateRequest(StringPiece host,
-                     StringPiece path,
-                     BalsaHeaders* headers) {
-    headers->SetRequestFirstlineFromStringPieces("GET", path, "HTTP/1.1");
-    headers->ReplaceOrAppendHeader("host", host);
-  }
-
-  void SetUp() override { QuicInMemoryCachePeer::ResetForTests(); }
-
 };
 
+TEST_F(QuicInMemoryCacheTest, GetResponseNoMatch) {
+  const QuicInMemoryCache::Response* response =
+      QuicInMemoryCache::GetInstance()->GetResponse("mail.google.com",
+                                                    "/index.html");
+  ASSERT_FALSE(response);
+}
+
 TEST_F(QuicInMemoryCacheTest, AddSimpleResponseGetResponse) {
   string response_body("hello response");
   QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance();
@@ -56,40 +63,37 @@
   const QuicInMemoryCache::Response* response =
       cache->GetResponse("www.google.com", "/");
   ASSERT_TRUE(response);
-  EXPECT_EQ("200", response->headers().response_code());
+  ASSERT_TRUE(ContainsKey(response->headers(), ":status"));
+  EXPECT_EQ("200 OK", response->headers().find(":status")->second);
   EXPECT_EQ(response_body.size(), response->body().length());
 }
 
 TEST_F(QuicInMemoryCacheTest, ReadsCacheDir) {
+  QuicInMemoryCache::GetInstance()->InitializeFromDirectory(CacheDirectory());
   const QuicInMemoryCache::Response* response =
       QuicInMemoryCache::GetInstance()->GetResponse("quic.test.url",
                                                     "/index.html");
   ASSERT_TRUE(response);
-  string value;
-  response->headers().GetAllOfHeaderAsString("Connection", &value);
-  EXPECT_EQ("200", response->headers().response_code());
-  EXPECT_EQ("Keep-Alive", value);
+  ASSERT_TRUE(ContainsKey(response->headers(), ":status"));
+  EXPECT_EQ("200 OK", response->headers().find(":status")->second);
+  ASSERT_TRUE(ContainsKey(response->headers(), "connection"));
+  EXPECT_EQ("close", response->headers().find("connection")->second);
   EXPECT_LT(0U, response->body().length());
 }
 
 TEST_F(QuicInMemoryCacheTest, UsesOriginalUrl) {
+  QuicInMemoryCache::GetInstance()->InitializeFromDirectory(CacheDirectory());
   const QuicInMemoryCache::Response* response =
       QuicInMemoryCache::GetInstance()->GetResponse("quic.test.url",
                                                     "/index.html");
   ASSERT_TRUE(response);
-  EXPECT_EQ("200", response->headers().response_code());
-  string value;
-  response->headers().GetAllOfHeaderAsString("Connection", &value);
+  ASSERT_TRUE(ContainsKey(response->headers(), ":status"));
+  EXPECT_EQ("200 OK", response->headers().find(":status")->second);
+  ASSERT_TRUE(ContainsKey(response->headers(), "connection"));
+  EXPECT_EQ("close", response->headers().find("connection")->second);
   EXPECT_LT(0U, response->body().length());
 }
 
-TEST_F(QuicInMemoryCacheTest, GetResponseNoMatch) {
-  const QuicInMemoryCache::Response* response =
-      QuicInMemoryCache::GetInstance()->GetResponse("mail.google.com",
-                                                    "/index.html");
-  ASSERT_FALSE(response);
-}
-
 }  // namespace test
 }  // namespace tools
 }  // namespace net
diff --git a/net/tools/quic/quic_server.cc b/net/tools/quic/quic_server.cc
index ef3ae4f96..87c1c53 100644
--- a/net/tools/quic/quic_server.cc
+++ b/net/tools/quic/quic_server.cc
@@ -21,6 +21,8 @@
 #include "net/quic/quic_data_reader.h"
 #include "net/quic/quic_protocol.h"
 #include "net/tools/quic/quic_dispatcher.h"
+#include "net/tools/quic/quic_epoll_clock.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
 #include "net/tools/quic/quic_in_memory_cache.h"
 #include "net/tools/quic/quic_packet_reader.h"
 #include "net/tools/quic/quic_socket_utils.h"
@@ -38,9 +40,13 @@
 
 namespace net {
 namespace tools {
-
 namespace {
 
+// Specifies the directory used during QuicInMemoryCache
+// construction to seed the cache. Cache directory can be
+// generated using `wget -p --save-headers <url>`
+std::string FLAGS_quic_in_memory_cache_dir = "";
+
 const PollBits kEpollFlags = PollBits(NET_POLLIN | NET_POLLOUT | NET_POLLET);
 const char kSourceAddressTokenSecret[] = "secret";
 
@@ -93,8 +99,11 @@
   }
 
   epoll_server_.set_timeout_in_us(50 * 1000);
-  // Initialize the in memory cache now.
-  QuicInMemoryCache::GetInstance();
+
+  if (!FLAGS_quic_in_memory_cache_dir.empty()) {
+    QuicInMemoryCache::GetInstance()->InitializeFromDirectory(
+        FLAGS_quic_in_memory_cache_dir);
+  }
 
   QuicEpollClock clock(&epoll_server_);
 
@@ -174,18 +183,22 @@
 
   epoll_server_.RegisterFD(fd_, this, kEpollFlags);
   dispatcher_.reset(CreateQuicDispatcher());
-  dispatcher_->Initialize(fd_);
+  dispatcher_->InitializeWithWriter(CreateWriter(fd_));
 
   return true;
 }
 
+QuicDefaultPacketWriter* QuicServer::CreateWriter(int fd) {
+  return new QuicDefaultPacketWriter(fd);
+}
+
 QuicDispatcher* QuicServer::CreateQuicDispatcher() {
   return new QuicDispatcher(
       config_,
       crypto_config_,
       supported_versions_,
       new QuicDispatcher::DefaultPacketWriterFactory(),
-      &epoll_server_);
+      new QuicEpollConnectionHelper(&epoll_server_));
 }
 
 void QuicServer::WaitForEvents() {
@@ -274,6 +287,8 @@
   IPEndPoint server_address(server_ip, port);
   processor->ProcessPacket(server_address, client_address, packet);
 
+  // The socket read was successful, so return true even if packet dispatch
+  // failed.
   return true;
 }
 
diff --git a/net/tools/quic/quic_server.h b/net/tools/quic/quic_server.h
index d0d173b..6ff8c53 100644
--- a/net/tools/quic/quic_server.h
+++ b/net/tools/quic/quic_server.h
@@ -13,11 +13,12 @@
 #include "net/base/ip_endpoint.h"
 #include "net/quic/crypto/quic_crypto_server_config.h"
 #include "net/quic/quic_config.h"
+#include "net/quic/quic_connection_helper.h"
 #include "net/quic/quic_framer.h"
 #include "net/tools/epoll_server/epoll_server.h"
+#include "net/tools/quic/quic_default_packet_writer.h"
 
 namespace net {
-
 namespace tools {
 
 namespace test {
@@ -51,12 +52,16 @@
   void OnEvent(int fd, EpollEvent* event) override;
   void OnUnregistration(int fd, bool replaced) override {}
 
-  // Reads a packet from the given fd, and then passes it off to
-  // the QuicDispatcher.  Returns true if a packet is read, false
+  // Reads a number of packets from the given fd, and then passes them off to
+  // the QuicDispatcher.  Returns true if some packets are read, false
   // otherwise.
   // If packets_dropped is non-null, the socket is configured to track
   // dropped packets, and some packets are read, it will be set to the number of
   // dropped packets.
+  static bool ReadAndDispatchPackets(int fd, int port,
+                                     ProcessPacketInterface* processor,
+                                     QuicPacketCount* packets_dropped);
+  // Same as ReadAndDispatchPackets, only does one packet at a time.
   static bool ReadAndDispatchSinglePacket(int fd, int port,
                                           ProcessPacketInterface* processor,
                                           QuicPacketCount* packets_dropped);
@@ -80,6 +85,8 @@
   int port() { return port_; }
 
  protected:
+  virtual QuicDefaultPacketWriter* CreateWriter(int fd);
+
   virtual QuicDispatcher* CreateQuicDispatcher();
 
   const QuicConfig& config() const { return config_; }
diff --git a/net/quic/quic_server_bin.cc b/net/tools/quic/quic_server_bin.cc
similarity index 84%
copy from net/quic/quic_server_bin.cc
copy to net/tools/quic/quic_server_bin.cc
index 53ec9bb..d4124bb 100644
--- a/net/quic/quic_server_bin.cc
+++ b/net/tools/quic/quic_server_bin.cc
@@ -14,14 +14,17 @@
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "net/base/ip_endpoint.h"
-#include "net/quic/quic_in_memory_cache.h"
 #include "net/quic/quic_protocol.h"
-#include "net/quic/quic_server.h"
+#include "net/tools/quic/quic_in_memory_cache.h"
+#include "net/tools/quic/quic_server.h"
 
 // The port the quic server will listen on.
 int32 FLAGS_port = 6121;
 
 int main(int argc, char *argv[]) {
+  base::AtExitManager exit_manager;
+  base::MessageLoopForIO message_loop;
+
   base::CommandLine::Init(argc, argv);
   base::CommandLine* line = base::CommandLine::ForCurrentProcess();
 
@@ -43,8 +46,8 @@
   }
 
   if (line->HasSwitch("quic_in_memory_cache_dir")) {
-    net::g_quic_in_memory_cache_dir =
-        line->GetSwitchValueNative("quic_in_memory_cache_dir");
+    net::tools::QuicInMemoryCache::GetInstance()->InitializeFromDirectory(
+        line->GetSwitchValueASCII("quic_in_memory_cache_dir"));
   }
 
   if (line->HasSwitch("port")) {
@@ -54,21 +57,18 @@
     }
   }
 
-  base::AtExitManager exit_manager;
-  base::MessageLoopForIO message_loop;
-
   net::IPAddressNumber ip;
   CHECK(net::ParseIPLiteralToNumber("::", &ip));
 
   net::QuicConfig config;
-  net::QuicServer server(config, net::QuicSupportedVersions());
+  net::tools::QuicServer server(config, net::QuicSupportedVersions());
 
   int rc = server.Listen(net::IPEndPoint(ip, FLAGS_port));
   if (rc < 0) {
     return 1;
   }
 
-  base::RunLoop().Run();
-
-  return 0;
+  while (1) {
+    server.WaitForEvents();
+  }
 }
diff --git a/net/tools/quic/quic_server_session.cc b/net/tools/quic/quic_server_session.cc
index bfa20bfb..6c1105e 100644
--- a/net/tools/quic/quic_server_session.cc
+++ b/net/tools/quic/quic_server_session.cc
@@ -48,11 +48,14 @@
   // bandwidth resumption.
   const CachedNetworkParameters* cached_network_params =
       crypto_stream_->previous_cached_network_params();
-  if (FLAGS_quic_enable_bandwidth_resumption_experiment &&
-      cached_network_params != nullptr &&
-      ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWRE) &&
+  const bool max_bandwidth_resumption =
+      ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWMX);
+  if (cached_network_params != nullptr &&
+      (ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWRE) ||
+       max_bandwidth_resumption) &&
       cached_network_params->serving_region() == serving_region_) {
-    connection()->ResumeConnectionState(*cached_network_params);
+    connection()->ResumeConnectionState(*cached_network_params,
+                                        max_bandwidth_resumption);
   }
 
   if (FLAGS_enable_quic_fec &&
diff --git a/net/tools/quic/quic_server_session_test.cc b/net/tools/quic/quic_server_session_test.cc
index e284d34..4c42e53 100644
--- a/net/tools/quic/quic_server_session_test.cc
+++ b/net/tools/quic/quic_server_session_test.cc
@@ -382,9 +382,6 @@
 }
 
 TEST_P(QuicServerSessionTest, BandwidthResumptionExperiment) {
-  ValueRestore<bool> old_flag(
-      &FLAGS_quic_enable_bandwidth_resumption_experiment, true);
-
   // Test that if a client provides a CachedNetworkParameters with the same
   // serving region as the current server, that this data is passed down to the
   // send algorithm.
@@ -402,7 +399,7 @@
           QuicSessionPeer::GetCryptoStream(session_.get()));
 
   // No effect if no CachedNetworkParameters provided.
-  EXPECT_CALL(*connection_, ResumeConnectionState(_)).Times(0);
+  EXPECT_CALL(*connection_, ResumeConnectionState(_, _)).Times(0);
   session_->OnConfigNegotiated();
 
   // No effect if CachedNetworkParameters provided, but different serving
@@ -411,13 +408,13 @@
   cached_network_params.set_bandwidth_estimate_bytes_per_second(1);
   cached_network_params.set_serving_region("different serving region");
   crypto_stream->set_previous_cached_network_params(cached_network_params);
-  EXPECT_CALL(*connection_, ResumeConnectionState(_)).Times(0);
+  EXPECT_CALL(*connection_, ResumeConnectionState(_, _)).Times(0);
   session_->OnConfigNegotiated();
 
   // Same serving region results in CachedNetworkParameters being stored.
   cached_network_params.set_serving_region(kTestServingRegion);
   crypto_stream->set_previous_cached_network_params(cached_network_params);
-  EXPECT_CALL(*connection_, ResumeConnectionState(_)).Times(1);
+  EXPECT_CALL(*connection_, ResumeConnectionState(_, _)).Times(1);
   session_->OnConfigNegotiated();
 }
 
diff --git a/net/tools/quic/quic_server_test.cc b/net/tools/quic/quic_server_test.cc
index 1107dedb..958f7ce3 100644
--- a/net/tools/quic/quic_server_test.cc
+++ b/net/tools/quic/quic_server_test.cc
@@ -6,6 +6,7 @@
 
 #include "net/quic/crypto/quic_random.h"
 #include "net/quic/quic_utils.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
 #include "net/tools/quic/test_tools/mock_quic_dispatcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -24,8 +25,8 @@
         dispatcher_(config_,
                     crypto_config_,
                     new QuicDispatcher::DefaultPacketWriterFactory(),
-                    &eps_) {
-    dispatcher_.Initialize(1234);
+                    new QuicEpollConnectionHelper(&eps_)) {
+    dispatcher_.InitializeWithWriter(new QuicDefaultPacketWriter(1234));
   }
 
   void DispatchPacket(const QuicEncryptedPacket& packet) {
diff --git a/net/tools/quic/quic_simple_client.cc b/net/tools/quic/quic_simple_client.cc
index 237d70b..703f22f 100644
--- a/net/tools/quic/quic_simple_client.cc
+++ b/net/tools/quic/quic_simple_client.cc
@@ -8,12 +8,14 @@
 #include "base/run_loop.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
 #include "net/quic/crypto/quic_random.h"
 #include "net/quic/quic_connection.h"
 #include "net/quic/quic_connection_helper.h"
 #include "net/quic/quic_default_packet_writer.h"
 #include "net/quic/quic_protocol.h"
 #include "net/quic/quic_server_id.h"
+#include "net/spdy/spdy_http_utils.h"
 #include "net/udp/udp_client_socket.h"
 
 using std::string;
@@ -154,7 +156,7 @@
                                    Perspective::IS_CLIENT,
                                    server_id_.is_https(),
                                    supported_versions_);
-  session_.reset(new QuicSimpleClientSession(config_, connection_));
+  session_.reset(new QuicClientSession(config_, connection_));
   session_->InitializeSession(server_id_, &crypto_config_);
   session_->CryptoConnect();
 }
@@ -180,12 +182,15 @@
 void QuicSimpleClient::SendRequest(const HttpRequestInfo& headers,
                                    base::StringPiece body,
                                    bool fin) {
-  QuicSimpleClientStream* stream = CreateReliableClientStream();
+  QuicSpdyClientStream* stream = CreateReliableClientStream();
   if (stream == nullptr) {
     LOG(DFATAL) << "stream creation failed!";
     return;
   }
-  stream->SendRequest(headers, body, fin);
+  SpdyHeaderBlock header_block;
+  CreateSpdyHeadersFromHttpRequest(headers, headers.extra_headers, SPDY3, true,
+                                   &header_block);
+  stream->SendRequest(header_block, body, fin);
   stream->set_visitor(this);
 }
 
@@ -209,7 +214,7 @@
   while (WaitForEvents()) {}
 }
 
-QuicSimpleClientStream* QuicSimpleClient::CreateReliableClientStream() {
+QuicSpdyClientStream* QuicSimpleClient::CreateReliableClientStream() {
   if (!connected()) {
     return nullptr;
   }
@@ -241,17 +246,19 @@
 }
 
 void QuicSimpleClient::OnClose(QuicDataStream* stream) {
-  QuicSimpleClientStream* client_stream =
-      static_cast<QuicSimpleClientStream*>(stream);
+  QuicSpdyClientStream* client_stream =
+      static_cast<QuicSpdyClientStream*>(stream);
+  HttpResponseInfo response;
+  SpdyHeadersToHttpResponse(client_stream->headers(), SPDY3, &response);
   if (response_listener_.get() != nullptr) {
     response_listener_->OnCompleteResponse(
-        stream->id(), *client_stream->headers(), client_stream->data());
+        stream->id(), *response.headers, client_stream->data());
   }
 
   // Store response headers and body.
   if (store_response_) {
-    latest_response_code_ = client_stream->headers()->response_code();
-    client_stream->headers()->GetNormalizedHeaders(&latest_response_headers_);
+    latest_response_code_ = client_stream->response_code();
+    response.headers->GetNormalizedHeaders(&latest_response_headers_);
     latest_response_body_ = client_stream->data();
   }
 }
diff --git a/net/tools/quic/quic_simple_client.h b/net/tools/quic/quic_simple_client.h
index b44e693..2a53da08 100644
--- a/net/tools/quic/quic_simple_client.h
+++ b/net/tools/quic/quic_simple_client.h
@@ -23,8 +23,8 @@
 #include "net/quic/quic_framer.h"
 #include "net/quic/quic_packet_creator.h"
 #include "net/quic/quic_packet_reader.h"
-#include "net/tools/quic/quic_simple_client_session.h"
-#include "net/tools/quic/quic_simple_client_stream.h"
+#include "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/quic_spdy_client_stream.h"
 
 namespace net {
 
@@ -101,9 +101,9 @@
   void SendRequestsAndWaitForResponse(
       const base::CommandLine::StringVector& url_list);
 
-  // Returns a newly created QuicSimpleClientStream, owned by the
+  // Returns a newly created QuicSpdyClientStream, owned by the
   // QuicSimpleClient.
-  QuicSimpleClientStream* CreateReliableClientStream();
+  QuicSpdyClientStream* CreateReliableClientStream();
 
   // Wait for events until the stream with the given ID is closed.
   void WaitForStreamToClose(QuicStreamId id);
@@ -124,7 +124,7 @@
   // QuicDataStream::Visitor
   void OnClose(QuicDataStream* stream) override;
 
-  QuicSimpleClientSession* session() { return session_.get(); }
+  QuicClientSession* session() { return session_.get(); }
 
   bool connected() const;
   bool goaway_received() const;
@@ -238,7 +238,7 @@
   scoped_ptr<QuicPacketWriter> writer_;
 
   // Session which manages streams.
-  scoped_ptr<QuicSimpleClientSession> session_;
+  scoped_ptr<QuicClientSession> session_;
 
   // UDP socket connected to the server.
   scoped_ptr<UDPClientSocket> socket_;
diff --git a/net/tools/quic/quic_simple_client_session.cc b/net/tools/quic/quic_simple_client_session.cc
deleted file mode 100644
index 0859fbf..0000000
--- a/net/tools/quic/quic_simple_client_session.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/tools/quic/quic_simple_client_session.h"
-
-#include "base/logging.h"
-#include "net/quic/crypto/crypto_protocol.h"
-#include "net/quic/quic_server_id.h"
-#include "net/tools/quic/quic_simple_client_stream.h"
-
-using std::string;
-
-namespace net {
-namespace tools {
-
-QuicSimpleClientSession::QuicSimpleClientSession(const QuicConfig& config,
-                                                 QuicConnection* connection)
-    : QuicClientSessionBase(connection, config), respect_goaway_(true) {
-}
-
-QuicSimpleClientSession::~QuicSimpleClientSession() {
-}
-
-void QuicSimpleClientSession::InitializeSession(
-    const QuicServerId& server_id,
-    QuicCryptoClientConfig* crypto_config) {
-  crypto_stream_.reset(
-      new QuicCryptoClientStream(server_id, this, nullptr, crypto_config));
-  QuicClientSessionBase::InitializeSession();
-}
-
-void QuicSimpleClientSession::OnProofValid(
-    const QuicCryptoClientConfig::CachedState& /*cached*/) {}
-
-void QuicSimpleClientSession::OnProofVerifyDetailsAvailable(
-    const ProofVerifyDetails& /*verify_details*/) {}
-
-QuicSimpleClientStream* QuicSimpleClientSession::CreateOutgoingDataStream() {
-  if (!crypto_stream_->encryption_established()) {
-    DVLOG(1) << "Encryption not active so no outgoing stream created.";
-    return nullptr;
-  }
-  if (GetNumOpenStreams() >= get_max_open_streams()) {
-    DVLOG(1) << "Failed to create a new outgoing stream. "
-             << "Already " << GetNumOpenStreams() << " open.";
-    return nullptr;
-  }
-  if (goaway_received() && respect_goaway_) {
-    DVLOG(1) << "Failed to create a new outgoing stream. "
-             << "Already received goaway.";
-    return nullptr;
-  }
-  QuicSimpleClientStream* stream
-      = new QuicSimpleClientStream(GetNextStreamId(), this);
-  ActivateStream(stream);
-  return stream;
-}
-
-QuicCryptoClientStream* QuicSimpleClientSession::GetCryptoStream() {
-  return crypto_stream_.get();
-}
-
-void QuicSimpleClientSession::CryptoConnect() {
-  DCHECK(flow_controller());
-  crypto_stream_->CryptoConnect();
-}
-
-int QuicSimpleClientSession::GetNumSentClientHellos() const {
-  return crypto_stream_->num_sent_client_hellos();
-}
-
-QuicDataStream* QuicSimpleClientSession::CreateIncomingDataStream(
-    QuicStreamId id) {
-  DLOG(ERROR) << "Server push not supported";
-  return nullptr;
-}
-
-}  // namespace tools
-}  // namespace net
diff --git a/net/tools/quic/quic_simple_client_session.h b/net/tools/quic/quic_simple_client_session.h
deleted file mode 100644
index 9bf1942..0000000
--- a/net/tools/quic/quic_simple_client_session.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// A client specific QuicSession subclass.
-
-#ifndef NET_TOOLS_QUIC_QUIC_SIMPLE_CLIENT_SESSION_H_
-#define NET_TOOLS_QUIC_QUIC_SIMPLE_CLIENT_SESSION_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "net/quic/quic_client_session_base.h"
-#include "net/quic/quic_crypto_client_stream.h"
-#include "net/quic/quic_protocol.h"
-#include "net/tools/quic/quic_simple_client_stream.h"
-
-namespace net {
-
-class QuicConnection;
-class QuicServerId;
-class ReliableQuicStream;
-
-namespace tools {
-
-class QuicSimpleClientSession : public QuicClientSessionBase {
- public:
-  QuicSimpleClientSession(const QuicConfig& config, QuicConnection* connection);
-  ~QuicSimpleClientSession() override;
-
-  void InitializeSession(const QuicServerId& server_id,
-                         QuicCryptoClientConfig* config);
-
-  // QuicSession methods:
-  QuicSimpleClientStream* CreateOutgoingDataStream() override;
-  QuicCryptoClientStream* GetCryptoStream() override;
-
-  // QuicSimpleClientSessionBase methods:
-  void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override;
-  void OnProofVerifyDetailsAvailable(
-      const ProofVerifyDetails& verify_details) override;
-
-  // Performs a crypto handshake with the server.
-  void CryptoConnect();
-
-  // Returns the number of client hello messages that have been sent on the
-  // crypto stream. If the handshake has completed then this is one greater
-  // than the number of round-trips needed for the handshake.
-  int GetNumSentClientHellos() const;
-
-  void set_respect_goaway(bool respect_goaway) {
-    respect_goaway_ = respect_goaway;
-  }
-
- protected:
-  // QuicSession methods:
-  QuicDataStream* CreateIncomingDataStream(QuicStreamId id) override;
-
- private:
-  scoped_ptr<QuicCryptoClientStream> crypto_stream_;
-
-  // If this is set to false, the client will ignore server GOAWAYs and allow
-  // the creation of streams regardless of the high chance they will fail.
-  bool respect_goaway_;
-
-  DISALLOW_COPY_AND_ASSIGN(QuicSimpleClientSession);
-};
-
-}  // namespace tools
-}  // namespace net
-
-#endif  // NET_TOOLS_QUIC_QUIC_SIMPLE_CLIENT_SESSION_H_
diff --git a/net/tools/quic/quic_simple_client_session_test.cc b/net/tools/quic/quic_simple_client_session_test.cc
deleted file mode 100644
index 866ff91d..0000000
--- a/net/tools/quic/quic_simple_client_session_test.cc
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/tools/quic/quic_simple_client_session.h"
-
-#include <vector>
-
-#include "net/base/ip_endpoint.h"
-#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
-#include "net/quic/quic_flags.h"
-#include "net/quic/test_tools/crypto_test_utils.h"
-#include "net/quic/test_tools/quic_session_peer.h"
-#include "net/quic/test_tools/quic_test_utils.h"
-#include "net/tools/quic/quic_simple_client_stream.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using net::test::CryptoTestUtils;
-using net::test::DefaultQuicConfig;
-using net::test::MockConnection;
-using net::test::PacketSavingConnection;
-using net::test::QuicSessionPeer;
-using net::test::SupportedVersions;
-using net::test::TestPeerIPAddress;
-using net::test::ValueRestore;
-using net::test::kTestPort;
-using testing::Invoke;
-using testing::_;
-
-namespace net {
-namespace tools {
-namespace test {
-namespace {
-
-const char kServerHostname[] = "www.example.org";
-const uint16 kPort = 80;
-
-class QuicSimpleClientSessionTest
-    : public ::testing::TestWithParam<QuicVersion> {
- protected:
-  QuicSimpleClientSessionTest()
-      : connection_(new PacketSavingConnection(Perspective::IS_CLIENT,
-                                               SupportedVersions(GetParam()))) {
-    session_.reset(new QuicSimpleClientSession(DefaultQuicConfig(),
-                                               connection_));
-    session_->InitializeSession(
-        QuicServerId(kServerHostname, kPort, false, PRIVACY_MODE_DISABLED),
-        &crypto_config_);
-    // Advance the time, because timers do not like uninitialized times.
-    connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
-  }
-
-  void CompleteCryptoHandshake() {
-    session_->CryptoConnect();
-    CryptoTestUtils::HandshakeWithFakeServer(
-        connection_, session_->GetCryptoStream());
-  }
-
-  PacketSavingConnection* connection_;
-  scoped_ptr<QuicSimpleClientSession> session_;
-  QuicCryptoClientConfig crypto_config_;
-};
-
-INSTANTIATE_TEST_CASE_P(Tests, QuicSimpleClientSessionTest,
-                        ::testing::ValuesIn(QuicSupportedVersions()));
-
-TEST_P(QuicSimpleClientSessionTest, CryptoConnect) {
-  CompleteCryptoHandshake();
-}
-
-TEST_P(QuicSimpleClientSessionTest, MaxNumStreams) {
-  session_->config()->SetMaxStreamsPerConnection(1, 1);
-
-  // Initialize crypto before the client session will create a stream.
-  CompleteCryptoHandshake();
-
-  QuicSimpleClientStream* stream = session_->CreateOutgoingDataStream();
-  ASSERT_TRUE(stream);
-  EXPECT_FALSE(session_->CreateOutgoingDataStream());
-
-  // Close a stream and ensure I can now open a new one.
-  session_->CloseStream(stream->id());
-  stream = session_->CreateOutgoingDataStream();
-  EXPECT_TRUE(stream);
-}
-
-TEST_P(QuicSimpleClientSessionTest, GoAwayReceived) {
-  CompleteCryptoHandshake();
-
-  // After receiving a GoAway, I should no longer be able to create outgoing
-  // streams.
-  session_->OnGoAway(QuicGoAwayFrame(QUIC_PEER_GOING_AWAY, 1u, "Going away."));
-  EXPECT_EQ(nullptr, session_->CreateOutgoingDataStream());
-}
-
-TEST_P(QuicSimpleClientSessionTest, SetFecProtectionFromConfig) {
-  ValueRestore<bool> old_flag(&FLAGS_enable_quic_fec, true);
-
-  // Set FEC config in client's connection options.
-  QuicTagVector copt;
-  copt.push_back(kFHDR);
-  session_->config()->SetConnectionOptionsToSend(copt);
-
-  // Doing the handshake should set up FEC config correctly.
-  CompleteCryptoHandshake();
-
-  // Verify that headers stream is always protected and data streams are
-  // optionally protected.
-  EXPECT_EQ(FEC_PROTECT_ALWAYS,
-            QuicSessionPeer::GetHeadersStream(session_.get())->fec_policy());
-  QuicSimpleClientStream* stream = session_->CreateOutgoingDataStream();
-  ASSERT_TRUE(stream);
-  EXPECT_EQ(FEC_PROTECT_OPTIONAL, stream->fec_policy());
-}
-
-// Regression test for b/17206611.
-TEST_P(QuicSimpleClientSessionTest, InvalidPacketReceived) {
-  // Create Packet with 0 length.
-  QuicEncryptedPacket invalid_packet(nullptr, 0, false);
-  IPEndPoint server_address(TestPeerIPAddress(), kTestPort);
-  IPEndPoint client_address(TestPeerIPAddress(), kTestPort);
-
-  EXPECT_CALL(*reinterpret_cast<MockConnection*>(session_->connection()),
-              ProcessUdpPacket(server_address, client_address, _))
-      .WillRepeatedly(
-          Invoke(reinterpret_cast<MockConnection*>(session_->connection()),
-                 &MockConnection::ReallyProcessUdpPacket));
-
-  // Validate that empty packets don't close the connection.
-  EXPECT_CALL(*connection_, SendConnectionCloseWithDetails(_, _)).Times(0);
-  session_->connection()->ProcessUdpPacket(client_address, server_address,
-                                           invalid_packet);
-
-  // Verifiy that small, invalid packets don't close the connection.
-  char buf[2] = {0x00, 0x01};
-  QuicEncryptedPacket valid_packet(buf, 2, false);
-  // Close connection shouldn't be called.
-  EXPECT_CALL(*connection_, SendConnectionCloseWithDetails(_, _)).Times(0);
-  session_->connection()->ProcessUdpPacket(client_address, server_address,
-                                           valid_packet);
-}
-
-}  // namespace
-}  // namespace test
-}  // namespace tools
-}  // namespace net
diff --git a/net/tools/quic/quic_simple_client_stream.cc b/net/tools/quic/quic_simple_client_stream.cc
deleted file mode 100644
index 528d5f7..0000000
--- a/net/tools/quic/quic_simple_client_stream.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/tools/quic/quic_simple_client_stream.h"
-
-#include "net/http/http_response_info.h"
-#include "net/spdy/spdy_framer.h"
-#include "net/spdy/spdy_http_utils.h"
-#include "net/tools/quic/quic_simple_client_session.h"
-
-using base::StringPiece;
-using std::string;
-
-namespace net {
-namespace tools {
-
-static const size_t kHeaderBufInitialSize = 4096;
-
-QuicSimpleClientStream::QuicSimpleClientStream(QuicStreamId id,
-                                               QuicSimpleClientSession* session)
-    : QuicDataStream(id, session),
-      read_buf_(new GrowableIOBuffer()),
-      response_headers_received_(false),
-      header_bytes_read_(0),
-      header_bytes_written_(0) {
-  read_buf_->SetCapacity(kHeaderBufInitialSize);
-}
-
-QuicSimpleClientStream::~QuicSimpleClientStream() {
-}
-
-void QuicSimpleClientStream::OnStreamFrame(const QuicStreamFrame& frame) {
-  if (!write_side_closed()) {
-    DVLOG(1) << "Got a response before the request was complete.  "
-             << "Aborting request.";
-    CloseWriteSide();
-  }
-  QuicDataStream::OnStreamFrame(frame);
-}
-
-void QuicSimpleClientStream::OnStreamHeadersComplete(bool fin,
-                                                   size_t frame_len) {
-  header_bytes_read_ = frame_len;
-  QuicDataStream::OnStreamHeadersComplete(fin, frame_len);
-}
-
-uint32 QuicSimpleClientStream::ProcessData(const char* data,
-                                           uint32 data_len) {
-  int total_bytes_processed = 0;
-
-  // Are we still reading the response headers.
-  if (!response_headers_received_) {
-    // Grow the read buffer if necessary.
-    if (read_buf_->RemainingCapacity() < (int)data_len) {
-      read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
-    }
-    memcpy(read_buf_->data(), data, data_len);
-    read_buf_->set_offset(read_buf_->offset() + data_len);
-    ParseResponseHeaders();
-  } else {
-    data_.append(data + total_bytes_processed,
-                 data_len - total_bytes_processed);
-  }
-  return data_len;
-}
-
-void QuicSimpleClientStream::OnFinRead() {
-  ReliableQuicStream::OnFinRead();
-  if (!response_headers_received_) {
-    Reset(QUIC_BAD_APPLICATION_PAYLOAD);
-  } else if (headers()->GetContentLength() != -1 &&
-             data_.size() !=
-                 static_cast<size_t>(headers()->GetContentLength())) {
-    Reset(QUIC_BAD_APPLICATION_PAYLOAD);
-  }
-}
-
-size_t QuicSimpleClientStream::SendRequest(const HttpRequestInfo& headers,
-                                           StringPiece body,
-                                           bool fin) {
-  SpdyHeaderBlock header_block;
-  CreateSpdyHeadersFromHttpRequest(headers,
-                                   headers.extra_headers,
-                                   SPDY3,
-                                   /*direct=*/ true,
-                                   &header_block);
-
-  bool send_fin_with_headers = fin && body.empty();
-  size_t bytes_sent = body.size();
-  header_bytes_written_ =
-      WriteHeaders(header_block, send_fin_with_headers, nullptr);
-  bytes_sent += header_bytes_written_;
-
-  if (!body.empty()) {
-    WriteOrBufferData(body, fin, nullptr);
-  }
-
-  return bytes_sent;
-}
-
-int QuicSimpleClientStream::ParseResponseHeaders() {
-  size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
-  SpdyFramer framer(SPDY3);
-  SpdyHeaderBlock headers;
-  char* data = read_buf_->StartOfBuffer();
-  size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(),
-                                               &headers);
-  if (len == 0) {
-    return -1;
-  }
-
-  HttpResponseInfo info;
-  if (!SpdyHeadersToHttpResponse(headers, SPDY3, &info)) {
-    Reset(QUIC_BAD_APPLICATION_PAYLOAD);
-    return -1;
-  }
-  headers_ = info.headers;
-
-  response_headers_received_ = true;
-
-  size_t delta = read_buf_len - len;
-  if (delta > 0) {
-    data_.append(data + len, delta);
-  }
-
-  return static_cast<int>(len);
-}
-
-void QuicSimpleClientStream::SendBody(const string& data, bool fin) {
-  SendBody(data, fin, nullptr);
-}
-
-void QuicSimpleClientStream::SendBody(
-    const string& data,
-    bool fin,
-    QuicAckNotifier::DelegateInterface* delegate) {
-  WriteOrBufferData(data, fin, delegate);
-}
-
-}  // namespace tools
-}  // namespace net
diff --git a/net/tools/quic/quic_simple_client_stream.h b/net/tools/quic/quic_simple_client_stream.h
deleted file mode 100644
index b4669a8..0000000
--- a/net/tools/quic/quic_simple_client_stream.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_TOOLS_QUIC_QUIC_SIMPLE_CLIENT_STREAM_H_
-#define NET_TOOLS_QUIC_QUIC_SIMPLE_CLIENT_STREAM_H_
-
-#include <sys/types.h>
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/strings/string_piece.h"
-#include "net/base/io_buffer.h"
-#include "net/http/http_request_info.h"
-#include "net/http/http_response_headers.h"
-#include "net/quic/quic_data_stream.h"
-#include "net/quic/quic_protocol.h"
-#include "net/tools/balsa/balsa_frame.h"
-#include "net/tools/balsa/balsa_headers.h"
-
-namespace net {
-
-namespace tools {
-
-class QuicSimpleClientSession;
-
-// All this does right now is send an SPDY request, and aggregate the
-// SPDY response.
-class QuicSimpleClientStream : public QuicDataStream {
- public:
-  QuicSimpleClientStream(QuicStreamId id, QuicSimpleClientSession* session);
-  ~QuicSimpleClientStream() override;
-
-  // Override the base class to close the write side as soon as we get a
-  // response.
-  // SPDY/HTTP does not support bidirectional streaming.
-  void OnStreamFrame(const QuicStreamFrame& frame) override;
-
-  // Override the base class to store the size of the headers.
-  void OnStreamHeadersComplete(bool fin, size_t frame_len) override;
-
-  // ReliableQuicStream implementation called by the session when there's
-  // data for us.
-  uint32 ProcessData(const char* data, uint32 data_len) override;
-
-  void OnFinRead() override;
-
-  // Serializes the headers and body, sends it to the server, and
-  // returns the number of bytes sent.
-  size_t SendRequest(const HttpRequestInfo& headers,
-                     base::StringPiece body,
-                     bool fin);
-
-  // Sends body data to the server, or buffers if it can't be sent immediately.
-  void SendBody(const std::string& data, bool fin);
-  // As above, but |delegate| will be notified once |data| is ACKed.
-  void SendBody(const std::string& data,
-                bool fin,
-                QuicAckNotifier::DelegateInterface* delegate);
-
-  // Returns the response data.
-  const std::string& data() { return data_; }
-
-  // Returns whatever headers have been received for this stream.
-  scoped_refptr<HttpResponseHeaders> headers() { return headers_; }
-
-  size_t header_bytes_read() const { return header_bytes_read_; }
-
-  size_t header_bytes_written() const { return header_bytes_written_; }
-
-  // While the server's set_priority shouldn't be called externally, the creator
-  // of client-side streams should be able to set the priority.
-  using QuicDataStream::set_priority;
-
- private:
-  int ParseResponseHeaders();
-
-  scoped_refptr<HttpResponseHeaders> headers_;
-  std::string data_;
-
-  scoped_refptr<GrowableIOBuffer> read_buf_;
-  bool response_headers_received_;
-  size_t header_bytes_read_;
-  size_t header_bytes_written_;
-
-  DISALLOW_COPY_AND_ASSIGN(QuicSimpleClientStream);
-};
-
-}  // namespace tools
-}  // namespace net
-
-#endif  // NET_TOOLS_QUIC_QUIC_SIMPLE_CLIENT_STREAM_H_
diff --git a/net/tools/quic/quic_simple_client_stream_test.cc b/net/tools/quic/quic_simple_client_stream_test.cc
deleted file mode 100644
index d753514..0000000
--- a/net/tools/quic/quic_simple_client_stream_test.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/tools/quic/quic_simple_client_stream.h"
-
-#include "base/strings/string_number_conversions.h"
-#include "net/quic/quic_utils.h"
-#include "net/quic/test_tools/quic_test_utils.h"
-#include "net/spdy/spdy_http_utils.h"
-#include "net/tools/quic/quic_simple_client_session.h"
-#include "net/tools/quic/spdy_utils.h"
-#include "net/tools/quic/test_tools/quic_test_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using net::test::DefaultQuicConfig;
-using net::test::MockConnection;
-using net::test::SupportedVersions;
-using std::string;
-using testing::StrictMock;
-using testing::TestWithParam;
-
-namespace net {
-namespace tools {
-namespace test {
-namespace {
-
-class QuicSimpleClientStreamTest : public TestWithParam<QuicVersion> {
- public:
-  QuicSimpleClientStreamTest()
-      : connection_(
-            new StrictMock<MockConnection>(Perspective::IS_CLIENT,
-                                           SupportedVersions(GetParam()))),
-        session_(DefaultQuicConfig(), connection_),
-        headers_(new HttpResponseHeaders("")),
-        body_("hello world") {
-    session_.InitializeSession(
-        QuicServerId("example.com", 80, false, PRIVACY_MODE_DISABLED),
-        &crypto_config_);
-
-    headers_->ReplaceStatusLine("HTTP/1.1 200 Ok");
-    headers_->AddHeader("content-length: 11");
-
-    SpdyHeaderBlock header_block;
-    CreateSpdyHeadersFromHttpResponse(*headers_, SPDY3, &header_block);
-    headers_string_ = SpdyUtils::SerializeUncompressedHeaders(header_block);
-
-    // New streams rely on having the peer's flow control receive window
-    // negotiated in the config.
-    session_.config()->SetInitialStreamFlowControlWindowToSend(
-        kInitialStreamFlowControlWindowForTest);
-    session_.config()->SetInitialSessionFlowControlWindowToSend(
-        kInitialSessionFlowControlWindowForTest);
-    stream_.reset(new QuicSimpleClientStream(3, &session_));
-  }
-
-  StrictMock<MockConnection>* connection_;
-  QuicSimpleClientSession session_;
-  scoped_ptr<QuicSimpleClientStream> stream_;
-  scoped_refptr<HttpResponseHeaders> headers_;
-  string headers_string_;
-  string body_;
-  QuicCryptoClientConfig crypto_config_;
-};
-
-INSTANTIATE_TEST_CASE_P(Tests, QuicSimpleClientStreamTest,
-                        ::testing::ValuesIn(QuicSupportedVersions()));
-
-TEST_P(QuicSimpleClientStreamTest, TestFraming) {
-  EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
-      headers_string_.c_str(), headers_string_.size()));
-  EXPECT_EQ(body_.size(),
-            stream_->ProcessData(body_.c_str(), body_.size()));
-  EXPECT_EQ(200, stream_->headers()->response_code());
-  EXPECT_EQ(body_, stream_->data());
-}
-
-TEST_P(QuicSimpleClientStreamTest, TestFramingOnePacket) {
-  string message = headers_string_ + body_;
-
-  EXPECT_EQ(message.size(), stream_->ProcessData(
-      message.c_str(), message.size()));
-  EXPECT_EQ(200, stream_->headers()->response_code());
-  EXPECT_EQ(body_, stream_->data());
-}
-
-TEST_P(QuicSimpleClientStreamTest, DISABLED_TestFramingExtraData) {
-  string large_body = "hello world!!!!!!";
-
-  EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
-      headers_string_.c_str(), headers_string_.size()));
-  // The headers should parse successfully.
-  EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error());
-  EXPECT_EQ(200, stream_->headers()->response_code());
-
-  EXPECT_CALL(*connection_,
-              SendRstStream(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD, 0));
-  stream_->ProcessData(large_body.c_str(), large_body.size());
-
-  EXPECT_NE(QUIC_STREAM_NO_ERROR, stream_->stream_error());
-}
-
-TEST_P(QuicSimpleClientStreamTest, TestNoBidirectionalStreaming) {
-  QuicStreamFrame frame(3, false, 3, MakeIOVector("asd"));
-
-  EXPECT_FALSE(stream_->write_side_closed());
-  stream_->OnStreamFrame(frame);
-  EXPECT_TRUE(stream_->write_side_closed());
-}
-
-}  // namespace
-}  // namespace test
-}  // namespace tools
-}  // namespace net
diff --git a/net/tools/quic/quic_simple_per_connection_packet_writer.cc b/net/tools/quic/quic_simple_per_connection_packet_writer.cc
new file mode 100644
index 0000000..713278f
--- /dev/null
+++ b/net/tools/quic/quic_simple_per_connection_packet_writer.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/quic/quic_simple_per_connection_packet_writer.h"
+
+#include "net/tools/quic/quic_simple_server_packet_writer.h"
+
+namespace net {
+namespace tools {
+
+QuicSimplePerConnectionPacketWriter::QuicSimplePerConnectionPacketWriter(
+    QuicSimpleServerPacketWriter* shared_writer,
+    QuicConnection* connection)
+    : shared_writer_(shared_writer),
+      connection_(connection),
+      weak_factory_(this){
+}
+
+QuicSimplePerConnectionPacketWriter::~QuicSimplePerConnectionPacketWriter() {
+}
+
+QuicPacketWriter* QuicSimplePerConnectionPacketWriter::shared_writer() const {
+  return shared_writer_;
+}
+
+WriteResult QuicSimplePerConnectionPacketWriter::WritePacket(
+    const char* buffer,
+    size_t buf_len,
+    const IPAddressNumber& self_address,
+    const IPEndPoint& peer_address) {
+  return shared_writer_->WritePacketWithCallback(
+      buffer,
+      buf_len,
+      self_address,
+      peer_address,
+      base::Bind(&QuicSimplePerConnectionPacketWriter::OnWriteComplete,
+                 weak_factory_.GetWeakPtr()));
+}
+
+bool QuicSimplePerConnectionPacketWriter::IsWriteBlockedDataBuffered() const {
+  return shared_writer_->IsWriteBlockedDataBuffered();
+}
+
+bool QuicSimplePerConnectionPacketWriter::IsWriteBlocked() const {
+  return shared_writer_->IsWriteBlocked();
+}
+
+void QuicSimplePerConnectionPacketWriter::SetWritable() {
+  shared_writer_->SetWritable();
+}
+
+void QuicSimplePerConnectionPacketWriter::OnWriteComplete(WriteResult result) {
+  if (result.status == WRITE_STATUS_ERROR) {
+    connection_->OnWriteError(result.error_code);
+  }
+}
+
+}  // namespace tools
+}  // namespace net
diff --git a/net/quic/quic_per_connection_packet_writer.h b/net/tools/quic/quic_simple_per_connection_packet_writer.h
similarity index 64%
rename from net/quic/quic_per_connection_packet_writer.h
rename to net/tools/quic/quic_simple_per_connection_packet_writer.h
index 6d91312..5cb4f4a 100644
--- a/net/quic/quic_per_connection_packet_writer.h
+++ b/net/tools/quic/quic_simple_per_connection_packet_writer.h
@@ -2,27 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef NET_QUIC_QUIC_PER_CONNECTION_PACKET_WRITER_H_
-#define NET_QUIC_QUIC_PER_CONNECTION_PACKET_WRITER_H_
+#ifndef NET_QUIC_TOOLS_QUIC_SIMPLE_PER_CONNECTION_PACKET_WRITER_H_
+#define NET_QUIC_TOOLS_QUIC_SIMPLE_PER_CONNECTION_PACKET_WRITER_H_
 
 #include "base/memory/weak_ptr.h"
 #include "net/quic/quic_connection.h"
 #include "net/quic/quic_packet_writer.h"
 
 namespace net {
+namespace tools {
 
-class QuicServerPacketWriter;
+class QuicSimpleServerPacketWriter;
 
 // A connection-specific packet writer that notifies its connection when its
 // writes to the shared QuicServerPacketWriter complete.
 // This class is necessary because multiple connections can share the same
 // QuicServerPacketWriter, so it has no way to know which connection to notify.
-class QuicPerConnectionPacketWriter : public QuicPacketWriter {
+class QuicSimplePerConnectionPacketWriter : public QuicPacketWriter {
  public:
   // Does not take ownership of |shared_writer| or |connection|.
-  QuicPerConnectionPacketWriter(QuicServerPacketWriter* shared_writer,
-                                QuicConnection* connection);
-  ~QuicPerConnectionPacketWriter() override;
+  QuicSimplePerConnectionPacketWriter(
+      QuicSimpleServerPacketWriter* shared_writer,
+      QuicConnection* connection);
+  ~QuicSimplePerConnectionPacketWriter() override;
 
   QuicPacketWriter* shared_writer() const;
   QuicConnection* connection() const { return connection_; }
@@ -40,14 +42,15 @@
  private:
   void OnWriteComplete(WriteResult result);
 
-  QuicServerPacketWriter* shared_writer_;  // Not owned.
+  QuicSimpleServerPacketWriter* shared_writer_;  // Not owned.
   QuicConnection* connection_;  // Not owned.
 
-  base::WeakPtrFactory<QuicPerConnectionPacketWriter> weak_factory_;
+  base::WeakPtrFactory<QuicSimplePerConnectionPacketWriter> weak_factory_;
 
-  DISALLOW_COPY_AND_ASSIGN(QuicPerConnectionPacketWriter);
+  DISALLOW_COPY_AND_ASSIGN(QuicSimplePerConnectionPacketWriter);
 };
 
+}  // namespace tools
 }  // namespace net
 
-#endif  // NET_QUIC_QUIC_PER_CONNECTION_PACKET_WRITER_H_
+#endif  // NET_QUIC_TOOLS_QUIC_SIMPLE_PER_CONNECTION_PACKET_WRITER_H_
diff --git a/net/quic/quic_server.cc b/net/tools/quic/quic_simple_server.cc
similarity index 66%
rename from net/quic/quic_server.cc
rename to net/tools/quic/quic_simple_server.cc
index 610b305f..864411f 100644
--- a/net/quic/quic_server.cc
+++ b/net/tools/quic/quic_simple_server.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "net/quic/quic_server.h"
+#include "net/tools/quic/quic_simple_server.h"
 
 #include <string.h>
 
@@ -12,13 +12,14 @@
 #include "net/quic/crypto/quic_random.h"
 #include "net/quic/quic_crypto_stream.h"
 #include "net/quic/quic_data_reader.h"
-#include "net/quic/quic_dispatcher.h"
-#include "net/quic/quic_in_memory_cache.h"
 #include "net/quic/quic_protocol.h"
-#include "net/quic/quic_server_packet_writer.h"
+#include "net/tools/quic/quic_dispatcher.h"
+#include "net/tools/quic/quic_simple_per_connection_packet_writer.h"
+#include "net/tools/quic/quic_simple_server_packet_writer.h"
 #include "net/udp/udp_server_socket.h"
 
 namespace net {
+namespace tools {
 
 namespace {
 
@@ -28,10 +29,39 @@
 // the limit.
 const int kReadBufferSize = 2 * kMaxPacketSize;
 
+// A packet writer factory which wraps a shared QuicSimpleServerPacketWriter
+// inside of a QuicPerConnectionPacketWriter. Instead of checking that
+// the shared_writer is the expected writer, this could instead cast
+// from QuicPacketWriter to QuicSimpleServerPacketWriter.
+class CustomPacketWriterFactory : public QuicDispatcher::PacketWriterFactory {
+ public:
+  ~CustomPacketWriterFactory() override {}
+
+  QuicPacketWriter* Create(QuicPacketWriter* writer,
+                           QuicConnection* connection) override {
+    if (writer == nullptr) {
+      LOG(DFATAL) << "shared_writer not initialized";
+      return nullptr;
+    }
+    if (writer != shared_writer_) {
+      LOG(DFATAL) << "writer mismatch";
+      return nullptr;
+    }
+    return new QuicSimplePerConnectionPacketWriter(shared_writer_, connection);
+  }
+
+  void set_shared_writer(QuicSimpleServerPacketWriter* shared_writer) {
+    shared_writer_ = shared_writer;
+  }
+
+ private:
+  QuicSimpleServerPacketWriter* shared_writer_;  // Not owned.
+};
+
 } // namespace
 
-QuicServer::QuicServer(const QuicConfig& config,
-                       const QuicVersionVector& supported_versions)
+QuicSimpleServer::QuicSimpleServer(const QuicConfig& config,
+                                   const QuicVersionVector& supported_versions)
     : helper_(base::MessageLoop::current()->message_loop_proxy().get(),
               &clock_,
               QuicRandom::GetInstance()),
@@ -45,7 +75,7 @@
   Initialize();
 }
 
-void QuicServer::Initialize() {
+void QuicSimpleServer::Initialize() {
 #if MMSG_MORE
   use_recvmmsg_ = true;
 #endif
@@ -65,19 +95,16 @@
         kInitialSessionFlowControlWindow);
   }
 
-  // Initialize the in memory cache now.
-  QuicInMemoryCache::GetInstance();
-
   scoped_ptr<CryptoHandshakeMessage> scfg(
       crypto_config_.AddDefaultConfig(
           helper_.GetRandomGenerator(), helper_.GetClock(),
           QuicCryptoServerConfig::ConfigOptions()));
 }
 
-QuicServer::~QuicServer() {
+QuicSimpleServer::~QuicSimpleServer() {
 }
 
-int QuicServer::Listen(const IPEndPoint& address) {
+int QuicSimpleServer::Listen(const IPEndPoint& address) {
   scoped_ptr<UDPServerSocket> socket(
       new UDPServerSocket(&net_log_, NetLog::Source()));
 
@@ -90,8 +117,8 @@
   }
 
   // These send and receive buffer sizes are sized for a single connection,
-  // because the default usage of QuicServer is as a test server with one or
-  // two clients.  Adjust higher for use with many clients.
+  // because the default usage of QuicSimpleServer is as a test server with
+  // one or two clients.  Adjust higher for use with many clients.
   rc = socket->SetReceiveBufferSize(
       static_cast<int32>(kDefaultSocketReceiveBuffer));
   if (rc < 0) {
@@ -115,23 +142,25 @@
 
   socket_.swap(socket);
 
+  CustomPacketWriterFactory* factory = new CustomPacketWriterFactory();
   dispatcher_.reset(
       new QuicDispatcher(config_,
                          crypto_config_,
                          supported_versions_,
-                         new QuicDispatcher::DefaultPacketWriterFactory(),
+                         factory,
                          &helper_));
-  QuicServerPacketWriter* writer = new QuicServerPacketWriter(
+  QuicSimpleServerPacketWriter* writer = new QuicSimpleServerPacketWriter(
       socket_.get(),
       dispatcher_.get());
-  dispatcher_->Initialize(writer);
+  factory->set_shared_writer(writer);
+  dispatcher_->InitializeWithWriter(writer);
 
   StartReading();
 
   return OK;
 }
 
-void QuicServer::Shutdown() {
+void QuicSimpleServer::Shutdown() {
   // Before we shut down the epoll server, give all active sessions a chance to
   // notify clients that they're closing.
   dispatcher_->Shutdown();
@@ -140,7 +169,7 @@
   socket_.reset();
 }
 
-void QuicServer::StartReading() {
+void QuicSimpleServer::StartReading() {
   if (read_pending_) {
     return;
   }
@@ -150,7 +179,7 @@
       read_buffer_.get(),
       read_buffer_->size(),
       &client_address_,
-      base::Bind(&QuicServer::OnReadComplete, base::Unretained(this)));
+      base::Bind(&QuicSimpleServer::OnReadComplete, base::Unretained(this)));
 
   if (result == ERR_IO_PENDING) {
     synchronous_read_count_ = 0;
@@ -163,7 +192,7 @@
     // recursion and 2) avoid blocking the thread for too long.
     base::MessageLoop::current()->PostTask(
         FROM_HERE,
-        base::Bind(&QuicServer::OnReadComplete,
+        base::Bind(&QuicSimpleServer::OnReadComplete,
                    weak_factory_.GetWeakPtr(),
                    result));
   } else {
@@ -171,13 +200,13 @@
   }
 }
 
-void QuicServer::OnReadComplete(int result) {
+void QuicSimpleServer::OnReadComplete(int result) {
   read_pending_ = false;
   if (result == 0)
     result = ERR_CONNECTION_CLOSED;
 
   if (result < 0) {
-    LOG(ERROR) << "QuicServer read failed: " << ErrorToString(result);
+    LOG(ERROR) << "QuicSimpleServer read failed: " << ErrorToString(result);
     Shutdown();
     return;
   }
@@ -188,4 +217,5 @@
   StartReading();
 }
 
+}  // namespace tools
 }  // namespace net
diff --git a/net/quic/quic_server.h b/net/tools/quic/quic_simple_server.h
similarity index 85%
rename from net/quic/quic_server.h
rename to net/tools/quic/quic_simple_server.h
index 82d6d4e..c46765d 100644
--- a/net/quic/quic_server.h
+++ b/net/tools/quic/quic_simple_server.h
@@ -5,8 +5,8 @@
 // A toy server, which listens on a specified address for QUIC traffic and
 // handles incoming responses.
 
-#ifndef NET_QUIC_QUIC_SERVER_H_
-#define NET_QUIC_QUIC_SERVER_H_
+#ifndef NET_QUIC_TOOLS_QUIC_SIMPLE_SERVER_H_
+#define NET_QUIC_TOOLS_QUIC_SIMPLE_SERVER_H_
 
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
@@ -20,20 +20,22 @@
 
 namespace net {
 
-
-namespace test {
-class QuicServerPeer;
-}  // namespace test
-
-class QuicDispatcher;
 class UDPServerSocket;
 
-class QuicServer {
- public:
-  QuicServer(const QuicConfig& config,
-             const QuicVersionVector& supported_versions);
+namespace tools {
 
-  virtual ~QuicServer();
+class QuicDispatcher;
+
+namespace test {
+class QuicSimpleServerPeer;
+}  // namespace test
+
+class QuicSimpleServer {
+ public:
+  QuicSimpleServer(const QuicConfig& config,
+                   const QuicVersionVector& supported_versions);
+
+  virtual ~QuicSimpleServer();
 
   // Start listening on the specified address. Returns an error code.
   int Listen(const IPEndPoint& address);
@@ -62,7 +64,7 @@
   QuicDispatcher* dispatcher() { return dispatcher_.get(); }
 
  private:
-  friend class net::test::QuicServerPeer;
+  friend class test::QuicSimpleServerPeer;
 
   // Initialize the internal state of the server.
   void Initialize();
@@ -111,11 +113,12 @@
   // The log to use for the socket.
   NetLog net_log_;
 
-  base::WeakPtrFactory<QuicServer> weak_factory_;
+  base::WeakPtrFactory<QuicSimpleServer> weak_factory_;
 
-  DISALLOW_COPY_AND_ASSIGN(QuicServer);
+  DISALLOW_COPY_AND_ASSIGN(QuicSimpleServer);
 };
 
+}  // namespace tools
 }  // namespace net
 
-#endif  // NET_QUIC_QUIC_SERVER_H_
+#endif  // NET_QUIC_TOOLS_QUIC_SIMPLE_SERVER_H_
diff --git a/net/quic/quic_server_bin.cc b/net/tools/quic/quic_simple_server_bin.cc
similarity index 85%
rename from net/quic/quic_server_bin.cc
rename to net/tools/quic/quic_simple_server_bin.cc
index 53ec9bb..10440e27 100644
--- a/net/quic/quic_server_bin.cc
+++ b/net/tools/quic/quic_simple_server_bin.cc
@@ -14,14 +14,17 @@
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "net/base/ip_endpoint.h"
-#include "net/quic/quic_in_memory_cache.h"
 #include "net/quic/quic_protocol.h"
-#include "net/quic/quic_server.h"
+#include "net/tools/quic/quic_in_memory_cache.h"
+#include "net/tools/quic/quic_simple_server.h"
 
 // The port the quic server will listen on.
 int32 FLAGS_port = 6121;
 
 int main(int argc, char *argv[]) {
+  base::AtExitManager exit_manager;
+  base::MessageLoopForIO message_loop;
+
   base::CommandLine::Init(argc, argv);
   base::CommandLine* line = base::CommandLine::ForCurrentProcess();
 
@@ -43,8 +46,8 @@
   }
 
   if (line->HasSwitch("quic_in_memory_cache_dir")) {
-    net::g_quic_in_memory_cache_dir =
-        line->GetSwitchValueNative("quic_in_memory_cache_dir");
+    net::tools::QuicInMemoryCache::GetInstance()->InitializeFromDirectory(
+        line->GetSwitchValueASCII("quic_in_memory_cache_dir"));
   }
 
   if (line->HasSwitch("port")) {
@@ -54,14 +57,11 @@
     }
   }
 
-  base::AtExitManager exit_manager;
-  base::MessageLoopForIO message_loop;
-
   net::IPAddressNumber ip;
   CHECK(net::ParseIPLiteralToNumber("::", &ip));
 
   net::QuicConfig config;
-  net::QuicServer server(config, net::QuicSupportedVersions());
+  net::tools::QuicSimpleServer server(config, net::QuicSupportedVersions());
 
   int rc = server.Listen(net::IPEndPoint(ip, FLAGS_port));
   if (rc < 0) {
diff --git a/net/quic/quic_server_packet_writer.cc b/net/tools/quic/quic_simple_server_packet_writer.cc
similarity index 71%
rename from net/quic/quic_server_packet_writer.cc
rename to net/tools/quic/quic_simple_server_packet_writer.cc
index 38d93d96..aaa8689 100644
--- a/net/quic/quic_server_packet_writer.cc
+++ b/net/tools/quic/quic_simple_server_packet_writer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "net/quic/quic_server_packet_writer.h"
+#include "net/tools/quic/quic_simple_server_packet_writer.h"
 
 #include "base/callback_helpers.h"
 #include "base/location.h"
@@ -13,8 +13,9 @@
 #include "net/udp/udp_server_socket.h"
 
 namespace net {
+namespace tools {
 
-QuicServerPacketWriter::QuicServerPacketWriter(
+QuicSimpleServerPacketWriter::QuicSimpleServerPacketWriter(
     UDPServerSocket* socket,
     QuicBlockedWriterInterface* blocked_writer)
     : socket_(socket),
@@ -23,10 +24,10 @@
       weak_factory_(this) {
 }
 
-QuicServerPacketWriter::~QuicServerPacketWriter() {
+QuicSimpleServerPacketWriter::~QuicSimpleServerPacketWriter() {
 }
 
-WriteResult QuicServerPacketWriter::WritePacketWithCallback(
+WriteResult QuicSimpleServerPacketWriter::WritePacketWithCallback(
     const char* buffer,
     size_t buf_len,
     const IPAddressNumber& self_address,
@@ -41,7 +42,7 @@
   return result;
 }
 
-void QuicServerPacketWriter::OnWriteComplete(int rv) {
+void QuicSimpleServerPacketWriter::OnWriteComplete(int rv) {
   DCHECK_NE(rv, ERR_IO_PENDING);
   write_blocked_ = false;
   WriteResult result(rv < 0 ? WRITE_STATUS_ERROR : WRITE_STATUS_OK, rv);
@@ -49,20 +50,20 @@
   blocked_writer_->OnCanWrite();
 }
 
-bool QuicServerPacketWriter::IsWriteBlockedDataBuffered() const {
+bool QuicSimpleServerPacketWriter::IsWriteBlockedDataBuffered() const {
   // UDPServerSocket::SendTo buffers the data until the Write is permitted.
   return true;
 }
 
-bool QuicServerPacketWriter::IsWriteBlocked() const {
+bool QuicSimpleServerPacketWriter::IsWriteBlocked() const {
   return write_blocked_;
 }
 
-void QuicServerPacketWriter::SetWritable() {
+void QuicSimpleServerPacketWriter::SetWritable() {
   write_blocked_ = false;
 }
 
-WriteResult QuicServerPacketWriter::WritePacket(
+WriteResult QuicSimpleServerPacketWriter::WritePacket(
     const char* buffer,
     size_t buf_len,
     const IPAddressNumber& self_address,
@@ -73,11 +74,13 @@
   DCHECK(!callback_.is_null());
   int rv;
   if (buf_len <= static_cast<size_t>(std::numeric_limits<int>::max())) {
-    rv = socket_->SendTo(buf.get(),
-                         static_cast<int>(buf_len),
-                         peer_address,
-                         base::Bind(&QuicServerPacketWriter::OnWriteComplete,
-                                    weak_factory_.GetWeakPtr()));
+    rv = socket_->SendTo(
+        buf.get(),
+        static_cast<int>(buf_len),
+        peer_address,
+        base::Bind(
+            &QuicSimpleServerPacketWriter::OnWriteComplete,
+            weak_factory_.GetWeakPtr()));
   } else {
     rv = ERR_MSG_TOO_BIG;
   }
@@ -94,4 +97,5 @@
   return WriteResult(status, rv);
 }
 
+}  // namespace tools
 }  // namespace net
diff --git a/net/quic/quic_server_packet_writer.h b/net/tools/quic/quic_simple_server_packet_writer.h
similarity index 73%
rename from net/quic/quic_server_packet_writer.h
rename to net/tools/quic/quic_simple_server_packet_writer.h
index 2e4646d..62eea8c 100644
--- a/net/quic/quic_server_packet_writer.h
+++ b/net/tools/quic/quic_simple_server_packet_writer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_
-#define NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_
+#ifndef NET_QUIC_TOOLS_QUIC_SIMPLE_SERVER_PACKET_WRITER_H_
+#define NET_QUIC_TOOLS_QUIC_SIMPLE_SERVER_PACKET_WRITER_H_
 
 #include "base/basictypes.h"
 #include "base/memory/weak_ptr.h"
@@ -18,19 +18,21 @@
 class UDPServerSocket;
 struct WriteResult;
 
+namespace tools {
+
 // Chrome specific packet writer which uses a UDPServerSocket for writing
 // data.
-class QuicServerPacketWriter : public QuicPacketWriter {
+class QuicSimpleServerPacketWriter : public QuicPacketWriter {
  public:
   typedef base::Callback<void(WriteResult)> WriteCallback;
 
-  QuicServerPacketWriter(UDPServerSocket* socket,
+  QuicSimpleServerPacketWriter(UDPServerSocket* socket,
                          QuicBlockedWriterInterface* blocked_writer);
-  ~QuicServerPacketWriter() override;
+  ~QuicSimpleServerPacketWriter() override;
 
   // Use this method to write packets rather than WritePacket:
-  // QuicServerPacketWriter requires a callback to exist for every write, which
-  // will be called once the write completes.
+  // QuicSimpleServerPacketWriter requires a callback to exist for every
+  // write, which will be called once the write completes.
   virtual WriteResult WritePacketWithCallback(
       const char* buffer,
       size_t buf_len,
@@ -64,11 +66,12 @@
   // Whether a write is currently in flight.
   bool write_blocked_;
 
-  base::WeakPtrFactory<QuicServerPacketWriter> weak_factory_;
+  base::WeakPtrFactory<QuicSimpleServerPacketWriter> weak_factory_;
 
-  DISALLOW_COPY_AND_ASSIGN(QuicServerPacketWriter);
+  DISALLOW_COPY_AND_ASSIGN(QuicSimpleServerPacketWriter);
 };
 
+}  // namespace tools
 }  // namespace net
 
-#endif  // NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_
+#endif  // NET_QUIC_TOOLS_QUIC_SIMPLE_SERVER_PACKET_WRITER_H_
diff --git a/net/tools/quic/quic_simple_server_test.cc b/net/tools/quic/quic_simple_server_test.cc
new file mode 100644
index 0000000..e9d00f73
--- /dev/null
+++ b/net/tools/quic/quic_simple_server_test.cc
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/quic/quic_simple_server.h"
+
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/mock_quic_dispatcher.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+
+namespace net {
+namespace tools {
+namespace test {
+
+// TODO(dmz) Remove "Chrome" part of name once net/tools/quic is deleted.
+class QuicChromeServerDispatchPacketTest : public ::testing::Test {
+ public:
+  QuicChromeServerDispatchPacketTest()
+      : crypto_config_("blah", QuicRandom::GetInstance()),
+        dispatcher_(config_,
+                    crypto_config_,
+                    new tools::QuicDispatcher::DefaultPacketWriterFactory(),
+                    new net::test::MockHelper) {
+    dispatcher_.InitializeWithWriter(nullptr);
+  }
+
+  void DispatchPacket(const QuicEncryptedPacket& packet) {
+    IPEndPoint client_addr, server_addr;
+    dispatcher_.ProcessPacket(server_addr, client_addr, packet);
+  }
+
+ protected:
+  QuicConfig config_;
+  QuicCryptoServerConfig crypto_config_;
+  net::test::MockQuicDispatcher dispatcher_;
+};
+
+TEST_F(QuicChromeServerDispatchPacketTest, DispatchPacket) {
+  unsigned char valid_packet[] = {
+    // public flags (8 byte connection_id)
+    0x3C,
+    // connection_id
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags
+    0x00 };
+  QuicEncryptedPacket encrypted_valid_packet(QuicUtils::AsChars(valid_packet),
+                                             arraysize(valid_packet), false);
+
+  EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _)).Times(1);
+  DispatchPacket(encrypted_valid_packet);
+}
+
+}  // namespace tools
+}  // namespace test
+}  // namespace net
diff --git a/net/tools/quic/quic_spdy_client_stream.cc b/net/tools/quic/quic_spdy_client_stream.cc
index 2d649cd..aa9559b 100644
--- a/net/tools/quic/quic_spdy_client_stream.cc
+++ b/net/tools/quic/quic_spdy_client_stream.cc
@@ -4,23 +4,26 @@
 
 #include "net/tools/quic/quic_spdy_client_stream.h"
 
-#include "net/spdy/spdy_framer.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/quic/spdy_utils.h"
+#include "net/spdy/spdy_protocol.h"
 #include "net/tools/quic/quic_client_session.h"
 #include "net/tools/quic/spdy_utils.h"
 
 using base::StringPiece;
 using std::string;
+using base::StringToInt;
 
 namespace net {
 namespace tools {
 
-static const size_t kHeaderBufInitialSize = 4096;
-
 QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id,
                                            QuicClientSession* session)
     : QuicDataStream(id, session),
-      read_buf_(new GrowableIOBuffer()),
-      response_headers_received_(false),
+      content_length_(-1),
+      response_code_(0),
       header_bytes_read_(0),
       header_bytes_written_(0) {
 }
@@ -43,47 +46,67 @@
   QuicDataStream::OnStreamHeadersComplete(fin, frame_len);
 }
 
-uint32 QuicSpdyClientStream::ProcessData(const char* data,
-                                         uint32 data_len) {
-  int total_bytes_processed = 0;
-
-  // Are we still reading the response headers.
-  if (!response_headers_received_) {
-    // Grow the read buffer if necessary.
-    if (read_buf_->RemainingCapacity() < (int)data_len) {
-      read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
-    }
-    memcpy(read_buf_->data(), data, data_len);
-    read_buf_->set_offset(read_buf_->offset() + data_len);
-    ParseResponseHeaders();
-  } else {
-    data_.append(data + total_bytes_processed,
-                 data_len - total_bytes_processed);
+uint32 QuicSpdyClientStream::ProcessData(const char* data, uint32 data_len) {
+  if (!headers_decompressed()) {
+    // Let the headers data accumulate in the underlying QuicDataStream.
+    return 0;
   }
+  if (response_headers_.empty()) {
+    if (!ParseResponseHeaders(data, data_len)) {
+      // Headers were invalid.
+      Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+      return 0;
+    }
+  } else {
+    data_.append(data, data_len);
+  }
+  DCHECK(!response_headers_.empty());
+  if (content_length_ >= 0 &&
+      static_cast<int>(data_.size()) > content_length_) {
+    Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+    return 0;
+  }
+  DVLOG(1) << "Client processed " << data_len << " bytes for stream " << id();
   return data_len;
 }
 
-void QuicSpdyClientStream::OnFinRead() {
-  ReliableQuicStream::OnFinRead();
-  if (!response_headers_received_) {
-    Reset(QUIC_BAD_APPLICATION_PAYLOAD);
-  } else if ((headers().content_length_status() ==
-             BalsaHeadersEnums::VALID_CONTENT_LENGTH) &&
-             data_.size() != headers().content_length()) {
-    Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+bool QuicSpdyClientStream::ParseResponseHeaders(const char* data,
+                                                uint32 data_len) {
+  DCHECK(headers_decompressed());
+  SpdyFramer framer(SPDY3);
+  size_t len = framer.ParseHeaderBlockInBuffer(data,
+                                               data_len,
+                                               &response_headers_);
+  DCHECK_LE(len, data_len);
+  if (len == 0 || response_headers_.empty()) {
+    return false;  // Headers were invalid.
   }
+
+  if (data_len > len) {
+    data_.append(data + len, data_len - len);
+  }
+  if (ContainsKey(response_headers_, "content-length") &&
+      !StringToInt(response_headers_["content-length"], &content_length_)) {
+    return false;  // Invalid content-length.
+  }
+  string status = response_headers_[":status"];
+  size_t end = status.find(" ");
+  if (end != string::npos) {
+    status.erase(end);
+  }
+  if (!StringToInt(status, &response_code_)) {
+    return false;  // Invalid response code.
+  }
+  return true;
 }
 
-ssize_t QuicSpdyClientStream::SendRequest(const BalsaHeaders& headers,
-                                          StringPiece body,
-                                          bool fin) {
-  SpdyHeaderBlock header_block =
-      SpdyUtils::RequestHeadersToSpdyHeaders(headers);
-
+size_t QuicSpdyClientStream::SendRequest(const SpdyHeaderBlock& headers,
+                                         StringPiece body,
+                                         bool fin) {
   bool send_fin_with_headers = fin && body.empty();
   size_t bytes_sent = body.size();
   header_bytes_written_ =
-      WriteHeaders(header_block, send_fin_with_headers, nullptr);
+      WriteHeaders(headers, send_fin_with_headers, nullptr);
   bytes_sent += header_bytes_written_;
 
   if (!body.empty()) {
@@ -93,38 +116,12 @@
   return bytes_sent;
 }
 
-int QuicSpdyClientStream::ParseResponseHeaders() {
-  size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
-  SpdyFramer framer(SPDY3);
-  SpdyHeaderBlock headers;
-  char* data = read_buf_->StartOfBuffer();
-  size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(),
-                                               &headers);
-  if (len == 0) {
-    return -1;
-  }
-
-  if (!SpdyUtils::FillBalsaResponseHeaders(headers, &headers_)) {
-    Reset(QUIC_BAD_APPLICATION_PAYLOAD);
-    return -1;
-  }
-  response_headers_received_ = true;
-
-  size_t delta = read_buf_len - len;
-  if (delta > 0) {
-    data_.append(data + len, delta);
-  }
-
-  return len;
-}
-
 void QuicSpdyClientStream::SendBody(const string& data, bool fin) {
   SendBody(data, fin, nullptr);
 }
 
 void QuicSpdyClientStream::SendBody(
-    const string& data,
-    bool fin,
+    const string& data, bool fin,
     QuicAckNotifier::DelegateInterface* delegate) {
   WriteOrBufferData(data, fin, delegate);
 }
diff --git a/net/tools/quic/quic_spdy_client_stream.h b/net/tools/quic/quic_spdy_client_stream.h
index 466279a1..dd2f9d2 100644
--- a/net/tools/quic/quic_spdy_client_stream.h
+++ b/net/tools/quic/quic_spdy_client_stream.h
@@ -10,14 +10,11 @@
 
 #include "base/basictypes.h"
 #include "base/strings/string_piece.h"
-#include "net/base/io_buffer.h"
 #include "net/quic/quic_data_stream.h"
 #include "net/quic/quic_protocol.h"
-#include "net/tools/balsa/balsa_frame.h"
-#include "net/tools/balsa/balsa_headers.h"
+#include "net/spdy/spdy_framer.h"
 
 namespace net {
-
 namespace tools {
 
 class QuicClientSession;
@@ -41,13 +38,11 @@
   // data for us.
   uint32 ProcessData(const char* data, uint32 data_len) override;
 
-  void OnFinRead() override;
-
   // Serializes the headers and body, sends it to the server, and
   // returns the number of bytes sent.
-  ssize_t SendRequest(const BalsaHeaders& headers,
-                      base::StringPiece body,
-                      bool fin);
+  size_t SendRequest(const SpdyHeaderBlock& headers,
+                     base::StringPiece body,
+                     bool fin);
 
   // Sends body data to the server, or buffers if it can't be sent immediately.
   void SendBody(const std::string& data, bool fin);
@@ -60,24 +55,27 @@
   const std::string& data() { return data_; }
 
   // Returns whatever headers have been received for this stream.
-  const BalsaHeaders& headers() { return headers_; }
+  const SpdyHeaderBlock& headers() { return response_headers_; }
 
   size_t header_bytes_read() const { return header_bytes_read_; }
 
   size_t header_bytes_written() const { return header_bytes_written_; }
 
+  int response_code() const { return response_code_; }
+
   // While the server's set_priority shouldn't be called externally, the creator
   // of client-side streams should be able to set the priority.
   using QuicDataStream::set_priority;
 
  private:
-  int ParseResponseHeaders();
+  bool ParseResponseHeaders(const char* data, uint32 data_len);
 
-  BalsaHeaders headers_;
+  // The parsed headers received from the server.
+  SpdyHeaderBlock response_headers_;
+  // The parsed content-length, or -1 if none is specified.
+  int content_length_;
+  int response_code_;
   std::string data_;
-
-  scoped_refptr<GrowableIOBuffer> read_buf_;
-  bool response_headers_received_;
   size_t header_bytes_read_;
   size_t header_bytes_written_;
 
diff --git a/net/tools/quic/quic_spdy_client_stream_test.cc b/net/tools/quic/quic_spdy_client_stream_test.cc
index 323018e..c5032c8 100644
--- a/net/tools/quic/quic_spdy_client_stream_test.cc
+++ b/net/tools/quic/quic_spdy_client_stream_test.cc
@@ -65,31 +65,32 @@
                         ::testing::ValuesIn(QuicSupportedVersions()));
 
 TEST_P(QuicSpdyClientStreamTest, TestFraming) {
-  EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
-      headers_string_.c_str(), headers_string_.size()));
-  EXPECT_EQ(body_.size(),
-            stream_->ProcessData(body_.c_str(), body_.size()));
-  EXPECT_EQ(200u, stream_->headers().parsed_response_code());
+  stream_->OnStreamHeaders(headers_string_);
+  stream_->OnStreamHeadersComplete(false, headers_string_.size());
+  EXPECT_EQ(body_.size(), stream_->ProcessData(body_.c_str(), body_.size()));
+  EXPECT_EQ("200 Ok", stream_->headers().find(":status")->second);
+  EXPECT_EQ(200, stream_->response_code());
   EXPECT_EQ(body_, stream_->data());
 }
 
 TEST_P(QuicSpdyClientStreamTest, TestFramingOnePacket) {
-  string message = headers_string_ + body_;
-
-  EXPECT_EQ(message.size(), stream_->ProcessData(
-      message.c_str(), message.size()));
-  EXPECT_EQ(200u, stream_->headers().parsed_response_code());
+  stream_->OnStreamHeaders(headers_string_);
+  stream_->OnStreamHeadersComplete(false, headers_string_.size());
+  EXPECT_EQ(body_.size(), stream_->ProcessData(body_.c_str(), body_.size()));
+  EXPECT_EQ("200 Ok", stream_->headers().find(":status")->second);
+  EXPECT_EQ(200, stream_->response_code());
   EXPECT_EQ(body_, stream_->data());
 }
 
 TEST_P(QuicSpdyClientStreamTest, DISABLED_TestFramingExtraData) {
   string large_body = "hello world!!!!!!";
 
-  EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
-      headers_string_.c_str(), headers_string_.size()));
+  stream_->OnStreamHeaders(headers_string_);
+  stream_->OnStreamHeadersComplete(false, headers_string_.size());
   // The headers should parse successfully.
   EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error());
-  EXPECT_EQ(200u, stream_->headers().parsed_response_code());
+  EXPECT_EQ("200 Ok", stream_->headers().find(":status")->second);
+  EXPECT_EQ(200, stream_->response_code());
 
   EXPECT_CALL(*connection_,
               SendRstStream(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD, 0));
diff --git a/net/tools/quic/quic_spdy_server_stream.cc b/net/tools/quic/quic_spdy_server_stream.cc
index 5937d6d6..68b5c19 100644
--- a/net/tools/quic/quic_spdy_server_stream.cc
+++ b/net/tools/quic/quic_spdy_server_stream.cc
@@ -4,7 +4,7 @@
 
 #include "net/tools/quic/quic_spdy_server_stream.h"
 
-#include "base/memory/singleton.h"
+#include "base/strings/string_number_conversions.h"
 #include "net/quic/quic_session.h"
 #include "net/spdy/spdy_framer.h"
 #include "net/tools/quic/quic_in_memory_cache.h"
@@ -12,39 +12,42 @@
 #include "url/gurl.h"
 
 using base::StringPiece;
+using base::StringToInt;
 using std::string;
 
 namespace net {
 namespace tools {
 
-static const size_t kHeaderBufInitialSize = 4096;
-
 QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id,
                                            QuicSession* session)
     : QuicDataStream(id, session),
-      read_buf_(new GrowableIOBuffer()),
-      request_headers_received_(false) {
+      content_length_(-1) {
 }
 
 QuicSpdyServerStream::~QuicSpdyServerStream() {
 }
 
 uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 data_len) {
-  uint32 total_bytes_processed = 0;
-
-  // Are we still reading the request headers.
-  if (!request_headers_received_) {
-    // Grow the read buffer if necessary.
-    if (read_buf_->RemainingCapacity() < (int)data_len) {
-      read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
-    }
-    memcpy(read_buf_->data(), data, data_len);
-    read_buf_->set_offset(read_buf_->offset() + data_len);
-    ParseRequestHeaders();
-  } else {
-    body_.append(data + total_bytes_processed,
-                 data_len - total_bytes_processed);
+  if (!headers_decompressed()) {
+    // Let the headers data accumulate in the underlying QuicDataStream.
+    return 0;
   }
+  if (request_headers_.empty()) {
+    if (!ParseRequestHeaders(data, data_len)) {
+      // Headers were invalid.
+      SendErrorResponse();
+      return 0;
+    }
+  } else {
+    body_.append(data, data_len);
+  }
+  DCHECK(!request_headers_.empty());
+  if (content_length_ >= 0 &&
+      static_cast<int>(body_.size()) > content_length_) {
+    SendErrorResponse();
+    return 0;
+  }
+  DVLOG(1) << "Processed " << data_len << " bytes for stream " << id();
   return data_len;
 }
 
@@ -54,52 +57,54 @@
     return;
   }
 
-  if (!request_headers_received_) {
-    SendErrorResponse();  // We're not done reading headers.
-  } else if ((headers_.content_length_status() ==
-              BalsaHeadersEnums::VALID_CONTENT_LENGTH) &&
-             body_.size() != headers_.content_length()) {
-    SendErrorResponse();  // Invalid content length
-  } else {
-    SendResponse();
-  }
-}
-
-void QuicSpdyServerStream::ParseRequestHeaders() {
-  size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
-  SpdyFramer framer(SPDY3);
-  SpdyHeaderBlock headers;
-  char* data = read_buf_->StartOfBuffer();
-  size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(),
-                                               &headers);
-  if (len == 0) {
-    return;
-  }
-
-  if (!SpdyUtils::FillBalsaRequestHeaders(headers, &headers_)) {
+  if (request_headers_.empty()) {
     SendErrorResponse();
     return;
   }
 
-  size_t delta = read_buf_len - len;
-  if (delta > 0) {
-    body_.append(data + len, delta);
+  if (content_length_ > 0 &&
+      content_length_ != static_cast<int>(body_.size())) {
+    SendErrorResponse();
+    return;
   }
 
-  request_headers_received_ = true;
+  SendResponse();
+}
+
+bool QuicSpdyServerStream::ParseRequestHeaders(const char* data,
+                                               uint32 data_len) {
+  DCHECK(headers_decompressed());
+  SpdyFramer framer(SPDY3);
+  size_t len = framer.ParseHeaderBlockInBuffer(data,
+                                               data_len,
+                                               &request_headers_);
+  DCHECK_LE(len, data_len);
+  if (len == 0 || request_headers_.empty()) {
+    return false;  // Headers were invalid.
+  }
+
+  if (data_len > len) {
+    body_.append(data + len, data_len - len);
+  }
+  if (ContainsKey(request_headers_, "content-length") &&
+      !StringToInt(request_headers_["content-length"], &content_length_)) {
+    return false;  // Invalid content-length.
+  }
+  return true;
 }
 
 void QuicSpdyServerStream::SendResponse() {
-  // Find response in cache. If not found, send error response.
-  GURL url(headers_.request_uri().as_string());
-  if (!url.is_valid()) {
+  if (!ContainsKey(request_headers_, ":host") ||
+      !ContainsKey(request_headers_, ":path")) {
     SendErrorResponse();
     return;
   }
+
+  // Find response in cache. If not found, send error response.
   const QuicInMemoryCache::Response* response =
       QuicInMemoryCache::GetInstance()->GetResponse(
-          url.host(),
-          url.PathForRequest());
+          request_headers_[":host"],
+          request_headers_[":path"]);
   if (response == nullptr) {
     SendErrorResponse();
     return;
@@ -122,25 +127,22 @@
 
 void QuicSpdyServerStream::SendErrorResponse() {
   DVLOG(1) << "Sending error response for stream " << id();
-  BalsaHeaders headers;
-  headers.SetResponseFirstlineFromStringPieces(
-      "HTTP/1.1", "500", "Server Error");
-  headers.ReplaceOrAppendHeader("content-length", "3");
+  SpdyHeaderBlock headers;
+  headers[":version"] = "HTTP/1.1";
+  headers[":status"] = "500 Server Error";
+  headers["content-length"] = "3";
   SendHeadersAndBody(headers, "bad");
 }
 
 void QuicSpdyServerStream::SendHeadersAndBody(
-    const BalsaHeaders& response_headers,
+    const SpdyHeaderBlock& response_headers,
     StringPiece body) {
   // We only support SPDY and HTTP, and neither handles bidirectional streaming.
   if (!read_side_closed()) {
     CloseReadSide();
   }
 
-  SpdyHeaderBlock header_block =
-      SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers);
-
-  WriteHeaders(header_block, body.empty(), nullptr);
+  WriteHeaders(response_headers, body.empty(), nullptr);
 
   if (!body.empty()) {
     WriteOrBufferData(body, true, nullptr);
diff --git a/net/tools/quic/quic_spdy_server_stream.h b/net/tools/quic/quic_spdy_server_stream.h
index 6fa1114..71ffb7d 100644
--- a/net/tools/quic/quic_spdy_server_stream.h
+++ b/net/tools/quic/quic_spdy_server_stream.h
@@ -8,10 +8,9 @@
 #include <string>
 
 #include "base/basictypes.h"
-#include "net/base/io_buffer.h"
 #include "net/quic/quic_data_stream.h"
 #include "net/quic/quic_protocol.h"
-#include "net/tools/balsa/balsa_headers.h"
+#include "net/spdy/spdy_framer.h"
 
 namespace net {
 
@@ -35,11 +34,13 @@
   uint32 ProcessData(const char* data, uint32 data_len) override;
   void OnFinRead() override;
 
-  void ParseRequestHeaders();
-
  private:
   friend class test::QuicSpdyServerStreamPeer;
 
+  // Parses the request headers from |data| to |request_headers_|.
+  // Returns false if there was an error parsing the headers.
+  bool ParseRequestHeaders(const char* data, uint32 data_len);
+
   // Sends a basic 200 response using SendHeaders for the headers and WriteData
   // for the body.
   void SendResponse();
@@ -48,16 +49,14 @@
   // for the body
   void SendErrorResponse();
 
-  void SendHeadersAndBody(const BalsaHeaders& response_headers,
+  void SendHeadersAndBody(const SpdyHeaderBlock& response_headers,
                           base::StringPiece body);
 
-  BalsaHeaders headers_;
+  // The parsed headers received from the client.
+  SpdyHeaderBlock request_headers_;
+  int content_length_;
   std::string body_;
 
-  // Buffer into which response header data is read.
-  scoped_refptr<GrowableIOBuffer> read_buf_;
-  bool request_headers_received_;
-
   DISALLOW_COPY_AND_ASSIGN(QuicSpdyServerStream);
 };
 
diff --git a/net/tools/quic/quic_spdy_server_stream_test.cc b/net/tools/quic/quic_spdy_server_stream_test.cc
index 0691f11..bd9e557 100644
--- a/net/tools/quic/quic_spdy_server_stream_test.cc
+++ b/net/tools/quic/quic_spdy_server_stream_test.cc
@@ -46,8 +46,8 @@
   using QuicSpdyServerStream::SendResponse;
   using QuicSpdyServerStream::SendErrorResponse;
 
-  BalsaHeaders* mutable_headers() {
-    return &headers_;
+  SpdyHeaderBlock* mutable_headers() {
+    return &request_headers_;
   }
 
   static void SendResponse(QuicSpdyServerStream* stream) {
@@ -62,8 +62,8 @@
     return stream->body_;
   }
 
-  static const BalsaHeaders& headers(QuicSpdyServerStream* stream) {
-    return stream->headers_;
+  static const SpdyHeaderBlock& headers(QuicSpdyServerStream* stream) {
+    return stream->request_headers_;
   }
 };
 
@@ -77,12 +77,14 @@
                                            SupportedVersions(GetParam()))),
         session_(connection_),
         body_("hello world") {
-    BalsaHeaders request_headers;
-    request_headers.SetRequestFirstlineFromStringPieces(
-        "POST", "https://www.google.com/", "HTTP/1.1");
-    request_headers.ReplaceOrAppendHeader("content-length", "11");
+    SpdyHeaderBlock request_headers;
+    request_headers[":host"] = "";
+    request_headers[":path"] = "/";
+    request_headers[":method"] = "POST";
+    request_headers[":version"] = "HTTP/1.1";
+    request_headers["content-length"] = "11";
 
-    headers_string_ = SpdyUtils::SerializeRequestHeaders(request_headers);
+    headers_string_ = SpdyUtils::SerializeUncompressedHeaders(request_headers);
 
     // New streams rely on having the peer's flow control receive window
     // negotiated in the config.
@@ -91,49 +93,30 @@
     session_.config()->SetInitialSessionFlowControlWindowToSend(
         kInitialSessionFlowControlWindowForTest);
     stream_.reset(new QuicSpdyServerStreamPeer(3, &session_));
-  }
 
-  static void SetUpTestCase() {
     QuicInMemoryCachePeer::ResetForTests();
-  }
-
-  void SetUp() override {
-    QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance();
-
-    BalsaHeaders response_headers;
-    StringPiece body("Yum");
-    response_headers.SetRequestFirstlineFromStringPieces("HTTP/1.1",
-                                                         "200",
-                                                         "OK");
-    response_headers.AppendHeader("content-length",
-                                  base::IntToString(body.length()));
 
     string host = "";
     string path = "/foo";
-    // Check if response already exists and matches.
-    const QuicInMemoryCache::Response* cached_response =
-        cache->GetResponse(host, path);
-    if (cached_response != nullptr) {
-      string cached_response_headers_str, response_headers_str;
-      cached_response->headers().DumpToString(&cached_response_headers_str);
-      response_headers.DumpToString(&response_headers_str);
-      CHECK_EQ(cached_response_headers_str, response_headers_str);
-      CHECK_EQ(cached_response->body(), body);
-      return;
-    }
+    SpdyHeaderBlock response_headers;
+    StringPiece body("Yum");
+    QuicInMemoryCache::GetInstance()->AddResponse(host, path, response_headers,
+                                                  body);
+  }
 
-    cache->AddResponse(host, path, response_headers, body);
+  ~QuicSpdyServerStreamTest() override {
+    QuicInMemoryCachePeer::ResetForTests();
   }
 
   const string& StreamBody() {
     return QuicSpdyServerStreamPeer::body(stream_.get());
   }
 
-  const BalsaHeaders& StreamHeaders() {
-    return QuicSpdyServerStreamPeer::headers(stream_.get());
+  const string& StreamHeadersValue(const string& key) {
+    return (*stream_->mutable_headers())[key];
   }
 
-  BalsaHeaders response_headers_;
+  SpdyHeaderBlock response_headers_;
   EpollServer eps_;
   StrictMock<MockConnection>* connection_;
   StrictMock<MockSession> session_;
@@ -158,13 +141,12 @@
 TEST_P(QuicSpdyServerStreamTest, TestFraming) {
   EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(AnyNumber()).
       WillRepeatedly(Invoke(ConsumeAllData));
-
-  EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
-      headers_string_.c_str(), headers_string_.size()));
+  stream_->OnStreamHeaders(headers_string_);
+  stream_->OnStreamHeadersComplete(false, headers_string_.size());
   EXPECT_EQ(body_.size(), stream_->ProcessData(body_.c_str(), body_.size()));
-  EXPECT_EQ(11u, StreamHeaders().content_length());
-  EXPECT_EQ("https://www.google.com/", StreamHeaders().request_uri());
-  EXPECT_EQ("POST", StreamHeaders().request_method());
+  EXPECT_EQ("11", StreamHeadersValue("content-length"));
+  EXPECT_EQ("/", StreamHeadersValue(":path"));
+  EXPECT_EQ("POST", StreamHeadersValue(":method"));
   EXPECT_EQ(body_, StreamBody());
 }
 
@@ -172,13 +154,12 @@
   EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(AnyNumber()).
       WillRepeatedly(Invoke(ConsumeAllData));
 
-  string message = headers_string_ + body_;
-
-  EXPECT_EQ(message.size(), stream_->ProcessData(
-      message.c_str(), message.size()));
-  EXPECT_EQ(11u, StreamHeaders().content_length());
-  EXPECT_EQ("https://www.google.com/", StreamHeaders().request_uri());
-  EXPECT_EQ("POST", StreamHeaders().request_method());
+  stream_->OnStreamHeaders(headers_string_);
+  stream_->OnStreamHeadersComplete(false, headers_string_.size());
+  EXPECT_EQ(body_.size(), stream_->ProcessData(body_.c_str(), body_.size()));
+  EXPECT_EQ("11", StreamHeadersValue("content-length"));
+  EXPECT_EQ("/", StreamHeadersValue(":path"));
+  EXPECT_EQ("POST", StreamHeadersValue(":method"));
   EXPECT_EQ(body_, StreamBody());
 }
 
@@ -189,26 +170,27 @@
   EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(AnyNumber()).
       WillRepeatedly(Invoke(ConsumeAllData));
 
-  EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
-      headers_string_.c_str(), headers_string_.size()));
+  stream_->OnStreamHeaders(headers_string_);
+  stream_->OnStreamHeadersComplete(false, headers_string_.size());
+  EXPECT_EQ(body_.size(), stream_->ProcessData(body_.c_str(), body_.size()));
   // Content length is still 11.  This will register as an error and we won't
   // accept the bytes.
   stream_->ProcessData(large_body.c_str(), large_body.size());
-  EXPECT_EQ(11u, StreamHeaders().content_length());
-  EXPECT_EQ("https://www.google.com/", StreamHeaders().request_uri());
-  EXPECT_EQ("POST", StreamHeaders().request_method());
+  EXPECT_EQ("11", StreamHeadersValue("content-length"));
+  EXPECT_EQ("/", StreamHeadersValue(":path"));
+  EXPECT_EQ("POST", StreamHeadersValue(":method"));
 }
 
 TEST_P(QuicSpdyServerStreamTest, TestSendResponse) {
-  BalsaHeaders* request_headers = stream_->mutable_headers();
-  request_headers->SetRequestFirstlineFromStringPieces(
-      "GET",
-      "https://www.google.com/foo",
-      "HTTP/1.1");
+  SpdyHeaderBlock* request_headers = stream_->mutable_headers();
+  (*request_headers)[":path"] = "/foo";
+  (*request_headers)[":host"] = "";
+  (*request_headers)[":version"] = "HTTP/1.1";
+  (*request_headers)[":method"] = "GET";
 
-  response_headers_.SetResponseFirstlineFromStringPieces(
-      "HTTP/1.1", "200", "OK");
-  response_headers_.ReplaceOrAppendHeader("content-length", "3");
+  response_headers_[":version"] = "HTTP/1.1";
+  response_headers_[":status"] = "200 OK";
+  response_headers_["content-length"] = "3";
 
   InSequence s;
   EXPECT_CALL(session_, WritevData(kHeadersStreamId, _, 0, false, _, nullptr));
@@ -221,9 +203,9 @@
 }
 
 TEST_P(QuicSpdyServerStreamTest, TestSendErrorResponse) {
-  response_headers_.SetResponseFirstlineFromStringPieces(
-      "HTTP/1.1", "500", "Server Error");
-  response_headers_.ReplaceOrAppendHeader("content-length", "3");
+  response_headers_[":version"] = "HTTP/1.1";
+  response_headers_[":status"] = "500 Server Error";
+  response_headers_["content-length"] = "3";
 
   InSequence s;
   EXPECT_CALL(session_, WritevData(kHeadersStreamId, _, 0, false, _, nullptr));
diff --git a/net/tools/quic/quic_time_wait_list_manager.cc b/net/tools/quic/quic_time_wait_list_manager.cc
index 98f96ec..abee58d 100644
--- a/net/tools/quic/quic_time_wait_list_manager.cc
+++ b/net/tools/quic/quic_time_wait_list_manager.cc
@@ -6,7 +6,6 @@
 
 #include <errno.h>
 
-#include "base/containers/hash_tables.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/stl_util.h"
 #include "net/base/ip_endpoint.h"
@@ -18,7 +17,6 @@
 #include "net/quic/quic_framer.h"
 #include "net/quic/quic_protocol.h"
 #include "net/quic/quic_utils.h"
-#include "net/tools/epoll_server/epoll_server.h"
 #include "net/tools/quic/quic_server_session.h"
 
 using base::StringPiece;
@@ -26,31 +24,30 @@
 namespace net {
 namespace tools {
 
-// TODO(rtenneti): Remove the duplicated code in this file. Share code with
-// "net/quic/quic_time_wait_list_manager.cc"
-
 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
-// up old connection_ids. This alarm should be unregistered and deleted before
+// up old connection_ids. This alarm should be cancelled  and deleted before
 // the QuicTimeWaitListManager is deleted.
-class ConnectionIdCleanUpAlarm : public EpollAlarm {
+class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate {
  public:
   explicit ConnectionIdCleanUpAlarm(
       QuicTimeWaitListManager* time_wait_list_manager)
       : time_wait_list_manager_(time_wait_list_manager) {
   }
 
-  int64 OnAlarm() override {
-    EpollAlarm::OnAlarm();
+  QuicTime OnAlarm() override {
     time_wait_list_manager_->CleanUpOldConnectionIds();
     // Let the time wait manager register the alarm at appropriate time.
-    return 0;
+    return QuicTime::Zero();
   }
 
  private:
   // Not owned.
   QuicTimeWaitListManager* time_wait_list_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectionIdCleanUpAlarm);
 };
 
+
 // This class stores pending public reset packets to be sent to clients.
 // server_address - server address on which a packet what was received for
 //                  a connection_id in time wait state.
@@ -83,20 +80,20 @@
 QuicTimeWaitListManager::QuicTimeWaitListManager(
     QuicPacketWriter* writer,
     QuicServerSessionVisitor* visitor,
-    EpollServer* epoll_server,
+    QuicConnectionHelperInterface* helper,
     const QuicVersionVector& supported_versions)
-    : epoll_server_(epoll_server),
-      kTimeWaitPeriod_(
+    : time_wait_period_(
           QuicTime::Delta::FromSeconds(FLAGS_quic_time_wait_list_seconds)),
-      connection_id_clean_up_alarm_(new ConnectionIdCleanUpAlarm(this)),
-      clock_(epoll_server_),
+      connection_id_clean_up_alarm_(
+          helper->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
+      clock_(helper->GetClock()),
       writer_(writer),
       visitor_(visitor) {
   SetConnectionIdCleanUpAlarm();
 }
 
 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
-  connection_id_clean_up_alarm_->UnregisterIfRegistered();
+  connection_id_clean_up_alarm_->Cancel();
   STLDeleteElements(&pending_packets_queue_);
   for (ConnectionIdMap::iterator it = connection_id_map_.begin();
        it != connection_id_map_.end();
@@ -122,7 +119,7 @@
             static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections));
   ConnectionIdData data(num_packets,
                         version,
-                        clock_.ApproximateNow(),
+                        clock_->ApproximateNow(),
                         close_packet);
   connection_id_map_.insert(std::make_pair(connection_id, data));
   if (new_connection_id) {
@@ -253,27 +250,25 @@
 }
 
 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
-  connection_id_clean_up_alarm_->UnregisterIfRegistered();
-  int64 next_alarm_interval;
+  connection_id_clean_up_alarm_->Cancel();
+  QuicTime::Delta next_alarm_interval = QuicTime::Delta::Zero();
   if (!connection_id_map_.empty()) {
     QuicTime oldest_connection_id =
         connection_id_map_.begin()->second.time_added;
-    QuicTime now = clock_.ApproximateNow();
-    if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
-      next_alarm_interval = oldest_connection_id.Add(kTimeWaitPeriod_)
-                                                .Subtract(now)
-                                                .ToMicroseconds();
+    QuicTime now = clock_->ApproximateNow();
+    if (now.Subtract(oldest_connection_id) < time_wait_period_) {
+      next_alarm_interval = oldest_connection_id.Add(time_wait_period_)
+                                                .Subtract(now);
     } else {
-      LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod";
-      next_alarm_interval = 0;
+      LOG(ERROR) << "ConnectionId lingered for longer than time_wait_period_";
     }
   } else {
-    // No connection_ids added so none will expire before kTimeWaitPeriod_.
-    next_alarm_interval = kTimeWaitPeriod_.ToMicroseconds();
+    // No connection_ids added so none will expire before time_wait_period_.
+    next_alarm_interval = time_wait_period_;
   }
 
-  epoll_server_->RegisterAlarmApproximateDelta(
-      next_alarm_interval, connection_id_clean_up_alarm_.get());
+  connection_id_clean_up_alarm_->Set(
+      clock_->ApproximateNow().Add(next_alarm_interval));
 }
 
 bool QuicTimeWaitListManager::MaybeExpireOldestConnection(
@@ -296,8 +291,8 @@
 }
 
 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
-  QuicTime now = clock_.ApproximateNow();
-  QuicTime expiration = now.Subtract(kTimeWaitPeriod_);
+  QuicTime now = clock_->ApproximateNow();
+  QuicTime expiration = now.Subtract(time_wait_period_);
 
   while (MaybeExpireOldestConnection(expiration)) {
   }
diff --git a/net/tools/quic/quic_time_wait_list_manager.h b/net/tools/quic/quic_time_wait_list_manager.h
index fe5f020a..aba6cb48 100644
--- a/net/tools/quic/quic_time_wait_list_manager.h
+++ b/net/tools/quic/quic_time_wait_list_manager.h
@@ -12,22 +12,16 @@
 #include <deque>
 
 #include "base/basictypes.h"
-#include "base/containers/hash_tables.h"
-#include "base/strings/string_piece.h"
 #include "net/base/linked_hash_map.h"
 #include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_connection.h"
 #include "net/quic/quic_framer.h"
 #include "net/quic/quic_packet_writer.h"
 #include "net/quic/quic_protocol.h"
-#include "net/tools/quic/quic_epoll_clock.h"
 
 namespace net {
-
-class EpollServer;
-
 namespace tools {
 
-class ConnectionIdCleanUpAlarm;
 class QuicServerSessionVisitor;
 
 namespace test {
@@ -35,7 +29,7 @@
 }  // namespace test
 
 // Maintains a list of all connection_ids that have been recently closed. A
-// connection_id lives in this state for kTimeWaitPeriod. All packets received
+// connection_id lives in this state for time_wait_period_. All packets received
 // for connection_ids in this state are handed over to the
 // QuicTimeWaitListManager by the QuicDispatcher.  Decides whether to send a
 // public reset packet, a copy of the previously sent connection close packet,
@@ -47,22 +41,25 @@
  public:
   // writer - the entity that writes to the socket. (Owned by the dispatcher)
   // visitor - the entity that manages blocked writers. (The dispatcher)
-  // epoll_server - used to run clean up alarms. (Owned by the dispatcher)
+  // helper - used to run clean up alarms. (Owned by the dispatcher)
   QuicTimeWaitListManager(QuicPacketWriter* writer,
                           QuicServerSessionVisitor* visitor,
-                          EpollServer* epoll_server,
+                          QuicConnectionHelperInterface* helper,
                           const QuicVersionVector& supported_versions);
   ~QuicTimeWaitListManager() override;
 
-  // Adds the given connection_id to time wait state for kTimeWaitPeriod.
-  // Henceforth, any packet bearing this connection_id should not be processed
-  // while the connection_id remains in this list. If a non-nullptr
-  // |close_packet| is provided, it is sent again when packets are received for
-  // added connection_ids. If nullptr, a public reset packet is sent with the
-  // specified |version|. DCHECKs that connection_id is not already on the list.
-  void AddConnectionIdToTimeWait(QuicConnectionId connection_id,
-                                 QuicVersion version,
-                                 QuicEncryptedPacket* close_packet);  // Owned.
+  // Adds the given connection_id to time wait state for
+  // time_wait_period_.  Henceforth, any packet bearing this
+  // connection_id should not be processed while the connection_id
+  // remains in this list. If a non-nullptr |close_packet| is
+  // provided, the TimeWaitListManager takes ownership of it and sends
+  // it again when packets are received for added connection_ids. If
+  // nullptr, a public reset packet is sent with the specified
+  // |version|. DCHECKs that connection_id is not already on the list.
+  // virtual to override in tests.
+  virtual void AddConnectionIdToTimeWait(QuicConnectionId connection_id,
+                                         QuicVersion version,
+                                         QuicEncryptedPacket* close_packet);
 
   // Returns true if the connection_id is in time wait state, false otherwise.
   // Packets received for this connection_id should not lead to creation of new
@@ -130,7 +127,7 @@
   // packet.
   bool WriteToWire(QueuedPacket* packet);
 
-  // Register the alarm with the epoll server to wake up at appropriate time.
+  // Register the alarm server to wake up at appropriate time.
   void SetConnectionIdCleanUpAlarm();
 
   // Removes the oldest connection from the time-wait list if it was added prior
@@ -168,19 +165,15 @@
   // when we are given a chance to write by the dispatcher.
   std::deque<QueuedPacket*> pending_packets_queue_;
 
-  // Used to schedule alarms to delete old connection_ids which have been in the
-  // list for too long.
-  EpollServer* epoll_server_;
-
   // Time period for which connection_ids should remain in time wait state.
-  const QuicTime::Delta kTimeWaitPeriod_;
+  const QuicTime::Delta time_wait_period_;
 
-  // Alarm registered with the epoll server to clean up connection_ids that have
-  // out lived their duration in time wait state.
-  scoped_ptr<ConnectionIdCleanUpAlarm> connection_id_clean_up_alarm_;
+  // Alarm to clean up connection_ids that have out lived their duration in
+  // time wait state.
+  scoped_ptr<QuicAlarm> connection_id_clean_up_alarm_;
 
-  // Clock to efficiently measure approximate time from the epoll server.
-  QuicEpollClock clock_;
+  // Clock to efficiently measure approximate time.
+  const QuicClock* clock_;
 
   // Interface that writes given buffer to the socket.
   QuicPacketWriter* writer_;
diff --git a/net/tools/quic/quic_time_wait_list_manager_test.cc b/net/tools/quic/quic_time_wait_list_manager_test.cc
index 724f601..3b30bf0 100644
--- a/net/tools/quic/quic_time_wait_list_manager_test.cc
+++ b/net/tools/quic/quic_time_wait_list_manager_test.cc
@@ -10,6 +10,7 @@
 #include "net/quic/crypto/null_encrypter.h"
 #include "net/quic/crypto/quic_decrypter.h"
 #include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_connection_helper.h"
 #include "net/quic/quic_data_reader.h"
 #include "net/quic/quic_flags.h"
 #include "net/quic/quic_framer.h"
@@ -17,6 +18,7 @@
 #include "net/quic/quic_protocol.h"
 #include "net/quic/quic_utils.h"
 #include "net/quic/test_tools/quic_test_utils.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
 #include "net/tools/quic/test_tools/mock_epoll_server.h"
 #include "net/tools/quic/test_tools/quic_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -69,7 +71,7 @@
   }
 
   static QuicTime::Delta time_wait_period(QuicTimeWaitListManager* manager) {
-    return manager->kTimeWaitPeriod_;
+    return manager->time_wait_period_;
   }
 
   static QuicVersion GetQuicVersionFromConnectionId(
@@ -90,9 +92,10 @@
 class QuicTimeWaitListManagerTest : public ::testing::Test {
  protected:
   QuicTimeWaitListManagerTest()
-      : time_wait_list_manager_(&writer_,
+      : helper_(&epoll_server_),
+        time_wait_list_manager_(&writer_,
                                 &visitor_,
-                                &epoll_server_,
+                                &helper_,
                                 QuicSupportedVersions()),
         framer_(QuicSupportedVersions(),
                 QuicTime::Zero(),
@@ -166,6 +169,7 @@
   }
 
   NiceMock<MockFakeTimeEpollServer> epoll_server_;
+  QuicEpollConnectionHelper helper_;
   StrictMock<MockPacketWriter> writer_;
   StrictMock<MockQuicServerSessionVisitor> visitor_;
   QuicTimeWaitListManager time_wait_list_manager_;
@@ -228,7 +232,7 @@
 }
 
 TEST_F(QuicTimeWaitListManagerTest, SendConnectionClose) {
-  size_t kConnectionCloseLength = 100;
+  const size_t kConnectionCloseLength = 100;
   EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_));
   AddConnectionId(
       connection_id_,
@@ -283,7 +287,7 @@
   const size_t kConnectionIdCount = 100;
   const size_t kOldConnectionIdCount = 31;
 
-  // Add connection_ids such that their expiry time is kTimeWaitPeriod_.
+  // Add connection_ids such that their expiry time is time_wait_period_.
   epoll_server_.set_now_in_usec(0);
   for (size_t connection_id = 1; connection_id <= kOldConnectionIdCount;
        ++connection_id) {
@@ -293,7 +297,7 @@
   EXPECT_EQ(kOldConnectionIdCount, time_wait_list_manager_.num_connections());
 
   // Add remaining connection_ids such that their add time is
-  // 2 * kTimeWaitPeriod.
+  // 2 * time_wait_period_.
   const QuicTime::Delta time_wait_period =
       QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_);
   epoll_server_.set_now_in_usec(time_wait_period.ToMicroseconds());
@@ -418,12 +422,12 @@
 }
 
 TEST_F(QuicTimeWaitListManagerTest, AddConnectionIdTwice) {
-  // Add connection_ids such that their expiry time is kTimeWaitPeriod_.
+  // Add connection_ids such that their expiry time is time_wait_period_.
   epoll_server_.set_now_in_usec(0);
   EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_));
   AddConnectionId(connection_id_);
   EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_));
-  size_t kConnectionCloseLength = 100;
+  const size_t kConnectionCloseLength = 100;
   AddConnectionId(
       connection_id_,
       QuicVersionMax(),
diff --git a/net/tools/quic/spdy_utils.cc b/net/tools/quic/spdy_utils.cc
index 2cfd381..44f0ae5 100644
--- a/net/tools/quic/spdy_utils.cc
+++ b/net/tools/quic/spdy_utils.cc
@@ -326,5 +326,12 @@
   return true;
 }
 
+// static
+void SpdyUtils::SpdyHeadersToResponseHeaders(
+    const SpdyHeaderBlock& block,
+    BalsaHeaders* headers) {
+  FillBalsaResponseHeaders(block, headers);
+}
+
 }  // namespace tools
 }  // namespace net
diff --git a/net/tools/quic/spdy_utils.h b/net/tools/quic/spdy_utils.h
index a4a03d2..6beff98 100644
--- a/net/tools/quic/spdy_utils.h
+++ b/net/tools/quic/spdy_utils.h
@@ -41,6 +41,9 @@
   static std::string SerializeUncompressedHeaders(
       const SpdyHeaderBlock& headers);
 
+  static void SpdyHeadersToResponseHeaders(const SpdyHeaderBlock& block,
+                                           BalsaHeaders* headers);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(SpdyUtils);
 };
diff --git a/net/tools/quic/test_tools/mock_quic_dispatcher.cc b/net/tools/quic/test_tools/mock_quic_dispatcher.cc
index 120c279d..341ee63 100644
--- a/net/tools/quic/test_tools/mock_quic_dispatcher.cc
+++ b/net/tools/quic/test_tools/mock_quic_dispatcher.cc
@@ -14,12 +14,12 @@
     const QuicConfig& config,
     const QuicCryptoServerConfig& crypto_config,
     QuicDispatcher::PacketWriterFactory* packet_writer_factory,
-    EpollServer* eps)
+    QuicConnectionHelperInterface* helper)
     : QuicDispatcher(config,
                      crypto_config,
                      QuicSupportedVersions(),
                      packet_writer_factory,
-                     eps) {}
+                     helper) {}
 
 MockQuicDispatcher::~MockQuicDispatcher() {}
 
diff --git a/net/tools/quic/test_tools/mock_quic_dispatcher.h b/net/tools/quic/test_tools/mock_quic_dispatcher.h
index 81f10047..591a502 100644
--- a/net/tools/quic/test_tools/mock_quic_dispatcher.h
+++ b/net/tools/quic/test_tools/mock_quic_dispatcher.h
@@ -22,7 +22,7 @@
   MockQuicDispatcher(const QuicConfig& config,
                      const QuicCryptoServerConfig& crypto_config,
                      PacketWriterFactory* packet_writer_factory,
-                     EpollServer* eps);
+                     QuicConnectionHelperInterface* helper);
 
   ~MockQuicDispatcher() override;
 
diff --git a/net/tools/quic/test_tools/packet_dropping_test_writer.cc b/net/tools/quic/test_tools/packet_dropping_test_writer.cc
index 9e01fd4b..e31a728 100644
--- a/net/tools/quic/test_tools/packet_dropping_test_writer.cc
+++ b/net/tools/quic/test_tools/packet_dropping_test_writer.cc
@@ -61,7 +61,7 @@
 PacketDroppingTestWriter::~PacketDroppingTestWriter() {}
 
 void PacketDroppingTestWriter::Initialize(
-    QuicEpollConnectionHelper* helper,
+    QuicConnectionHelperInterface* helper,
     Delegate* on_can_write) {
   clock_ = helper->GetClock();
   write_unblocked_alarm_.reset(
diff --git a/net/tools/quic/test_tools/packet_dropping_test_writer.h b/net/tools/quic/test_tools/packet_dropping_test_writer.h
index b39539f0..e181fdbf 100644
--- a/net/tools/quic/test_tools/packet_dropping_test_writer.h
+++ b/net/tools/quic/test_tools/packet_dropping_test_writer.h
@@ -43,7 +43,8 @@
   // called after connecting if the helper is not available before.
   // |on_can_write| will be triggered when fake-unblocking; ownership will be
   // assumed.
-  void Initialize(QuicEpollConnectionHelper* helper, Delegate* on_can_write);
+  void Initialize(QuicConnectionHelperInterface* helper,
+                  Delegate* on_can_write);
 
   // QuicPacketWriter methods:
   WriteResult WritePacket(const char* buffer,
diff --git a/net/tools/quic/test_tools/quic_dispatcher_peer.cc b/net/tools/quic/test_tools/quic_dispatcher_peer.cc
index 5d3ce7a..6826f47 100644
--- a/net/tools/quic/test_tools/quic_dispatcher_peer.cc
+++ b/net/tools/quic/test_tools/quic_dispatcher_peer.cc
@@ -38,7 +38,7 @@
 }
 
 // static
-QuicEpollConnectionHelper* QuicDispatcherPeer::GetHelper(
+QuicConnectionHelperInterface* QuicDispatcherPeer::GetHelper(
     QuicDispatcher* dispatcher) {
   return dispatcher->helper_.get();
 }
diff --git a/net/tools/quic/test_tools/quic_dispatcher_peer.h b/net/tools/quic/test_tools/quic_dispatcher_peer.h
index e5edf16..7675152 100644
--- a/net/tools/quic/test_tools/quic_dispatcher_peer.h
+++ b/net/tools/quic/test_tools/quic_dispatcher_peer.h
@@ -32,7 +32,7 @@
       QuicDispatcher* dispatcher,
       QuicDispatcher::PacketWriterFactory* packet_writer_factory);
 
-  static QuicEpollConnectionHelper* GetHelper(QuicDispatcher* dispatcher);
+  static QuicConnectionHelperInterface* GetHelper(QuicDispatcher* dispatcher);
 
   static QuicDispatcher::WriteBlockedList* GetWriteBlockedList(
       QuicDispatcher* dispatcher);
diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc
index 94d55b4..75c9a01 100644
--- a/net/tools/quic/test_tools/quic_test_client.cc
+++ b/net/tools/quic/test_tools/quic_test_client.cc
@@ -19,6 +19,7 @@
 #include "net/tools/quic/quic_epoll_connection_helper.h"
 #include "net/tools/quic/quic_packet_writer_wrapper.h"
 #include "net/tools/quic/quic_spdy_client_stream.h"
+#include "net/tools/quic/spdy_utils.h"
 #include "net/tools/quic/test_tools/http_message.h"
 #include "net/tools/quic/test_tools/quic_client_peer.h"
 #include "url/gurl.h"
@@ -255,7 +256,8 @@
   scoped_ptr<BalsaHeaders> munged_headers(MungeHeaders(message.headers(),
                                           secure_));
   ssize_t ret = GetOrCreateStream()->SendRequest(
-      munged_headers.get() ? *munged_headers : *message.headers(),
+      SpdyUtils::RequestHeadersToSpdyHeaders(
+          munged_headers.get() ? *munged_headers : *message.headers()),
       message.body(), message.has_complete_message());
   WaitForWriteToFlush();
   return ret;
@@ -456,14 +458,14 @@
 bool QuicTestClient::response_headers_complete() const {
   if (stream_ != nullptr) {
     return stream_->headers_decompressed();
-  } else {
-    return response_headers_complete_;
   }
+  return response_headers_complete_;
 }
 
 const BalsaHeaders* QuicTestClient::response_headers() const {
   if (stream_ != nullptr) {
-    return &stream_->headers();
+    SpdyUtils::SpdyHeadersToResponseHeaders(stream_->headers(), &headers_);
+    return &headers_;
   } else {
     return &headers_;
   }
@@ -491,7 +493,8 @@
   }
   response_complete_ = true;
   response_headers_complete_ = stream_->headers_decompressed();
-  headers_.CopyFrom(stream_->headers());
+  SpdyUtils::SpdyHeadersToResponseHeaders(stream_->headers(),
+                                          &headers_);
   stream_error_ = stream_->stream_error();
   bytes_read_ = stream_->stream_bytes_read() + stream_->header_bytes_read();
   bytes_written_ =
diff --git a/net/tools/quic/test_tools/quic_test_client.h b/net/tools/quic/test_tools/quic_test_client.h
index 1181bec..019d452 100644
--- a/net/tools/quic/test_tools/quic_test_client.h
+++ b/net/tools/quic/test_tools/quic_test_client.h
@@ -185,7 +185,7 @@
 
   bool response_complete_;
   bool response_headers_complete_;
-  BalsaHeaders headers_;
+  mutable BalsaHeaders headers_;
   QuicPriority priority_;
   std::string response_;
   uint64 bytes_read_;
diff --git a/net/tools/quic/test_tools/quic_test_utils.cc b/net/tools/quic/test_tools/quic_test_utils.cc
index 6a5e93c8..5e954dd 100644
--- a/net/tools/quic/test_tools/quic_test_utils.cc
+++ b/net/tools/quic/test_tools/quic_test_utils.cc
@@ -14,6 +14,9 @@
 using net::test::MakeAckFrame;
 using net::test::MockHelper;
 using net::test::QuicConnectionPeer;
+using testing::_;
+using testing::AnyNumber;
+using testing::Invoke;
 
 namespace net {
 namespace tools {
@@ -115,8 +118,16 @@
 MockTimeWaitListManager::MockTimeWaitListManager(
     QuicPacketWriter* writer,
     QuicServerSessionVisitor* visitor,
-    EpollServer* eps)
-    : QuicTimeWaitListManager(writer, visitor, eps, QuicSupportedVersions()) {
+    QuicConnectionHelperInterface* helper)
+    : QuicTimeWaitListManager(writer, visitor, helper,
+                              QuicSupportedVersions()) {
+  // Though AddConnectionIdToTimeWait is mocked, we want to retain its
+  // functionality.
+  EXPECT_CALL(*this, AddConnectionIdToTimeWait(_, _, _)).Times(AnyNumber());
+  ON_CALL(*this, AddConnectionIdToTimeWait(_, _, _))
+      .WillByDefault(
+          Invoke(this, &MockTimeWaitListManager::
+                           QuicTimeWaitListManager_AddConnectionIdToTimeWait));
 }
 
 MockTimeWaitListManager::~MockTimeWaitListManager() {
diff --git a/net/tools/quic/test_tools/quic_test_utils.h b/net/tools/quic/test_tools/quic_test_utils.h
index ad6fa201..efb9c00 100644
--- a/net/tools/quic/test_tools/quic_test_utils.h
+++ b/net/tools/quic/test_tools/quic_test_utils.h
@@ -106,6 +106,7 @@
   // Object is ref counted.
   virtual ~MockAckNotifierDelegate();
 
+ private:
   DISALLOW_COPY_AND_ASSIGN(MockAckNotifierDelegate);
 };
 
@@ -152,9 +153,23 @@
  public:
   MockTimeWaitListManager(QuicPacketWriter* writer,
                           QuicServerSessionVisitor* visitor,
-                          EpollServer* eps);
+                          QuicConnectionHelperInterface* helper);
+
   ~MockTimeWaitListManager() override;
 
+  MOCK_METHOD3(AddConnectionIdToTimeWait,
+               void(QuicConnectionId connection_id,
+                    QuicVersion version,
+                    QuicEncryptedPacket* close_packet));
+
+  void QuicTimeWaitListManager_AddConnectionIdToTimeWait(
+      QuicConnectionId connection_id,
+      QuicVersion version,
+      QuicEncryptedPacket* close_packet) {
+    QuicTimeWaitListManager::AddConnectionIdToTimeWait(connection_id, version,
+                                                       close_packet);
+  }
+
   MOCK_METHOD5(ProcessPacket,
                void(const IPEndPoint& server_address,
                     const IPEndPoint& client_address,
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 28d3a5ac..59979a84 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -19,7 +19,6 @@
 #include "base/time/time.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
-#include "net/base/mime_util.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
 #include "net/base/network_delegate.h"
@@ -616,14 +615,7 @@
 void URLRequestHttpJob::DoLoadCookies() {
   CookieOptions options;
   options.set_include_httponly();
-
-  // TODO(mkwst): Drop this `if` once we decide whether or not to ship
-  // first-party cookies: https://crbug.com/459154
-  if (network_delegate() &&
-      network_delegate()->FirstPartyOnlyCookieExperimentEnabled())
-    options.set_first_party_url(request_->first_party_for_cookies());
-  else
-    options.set_include_first_party_only();
+  options.set_first_party_url(request_->first_party_for_cookies());
 
   request_->context()->cookie_store()->GetCookiesWithOptionsAsync(
       request_->url(), options, base::Bind(&URLRequestHttpJob::OnCookiesLoaded,
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index ebccd876..107da785 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -322,7 +322,6 @@
       has_load_timing_info_before_auth_(false),
       can_access_files_(true),
       can_throttle_requests_(true),
-      first_party_only_cookies_enabled_(false),
       cancel_request_with_policy_violating_referrer_(false),
       will_be_intercepted_on_next_error_(false) {
 }
@@ -604,10 +603,6 @@
   return can_throttle_requests_;
 }
 
-bool TestNetworkDelegate::OnFirstPartyOnlyCookieExperimentEnabled() const {
-  return first_party_only_cookies_enabled_;
-}
-
 bool TestNetworkDelegate::OnCancelURLRequestWithPolicyViolatingReferrerHeader(
     const URLRequest& request,
     const GURL& target_url,
diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h
index 0208b43..9ad66a1 100644
--- a/net/url_request/url_request_test_util.h
+++ b/net/url_request/url_request_test_util.h
@@ -265,10 +265,6 @@
   void set_can_access_files(bool val) { can_access_files_ = val; }
   bool can_access_files() const { return can_access_files_; }
 
-  void set_first_party_only_cookies_enabled(bool val) {
-    first_party_only_cookies_enabled_ = val;
-  }
-
   void set_can_throttle_requests(bool val) { can_throttle_requests_ = val; }
   bool can_throttle_requests() const { return can_throttle_requests_; }
 
@@ -329,7 +325,6 @@
   bool OnCanAccessFile(const URLRequest& request,
                        const base::FilePath& path) const override;
   bool OnCanThrottleRequest(const URLRequest& request) const override;
-  bool OnFirstPartyOnlyCookieExperimentEnabled() const override;
   bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(
       const URLRequest& request,
       const GURL& target_url,
@@ -375,7 +370,6 @@
 
   bool can_access_files_;  // true by default
   bool can_throttle_requests_;  // true by default
-  bool first_party_only_cookies_enabled_;               // false by default
   bool cancel_request_with_policy_violating_referrer_;  // false by default
   bool will_be_intercepted_on_next_error_;
 };
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 37a5cb5..f19cc01 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -2551,7 +2551,6 @@
   // LocalHttpTestServer points).
   {
     TestNetworkDelegate network_delegate;
-    network_delegate.set_first_party_only_cookies_enabled(true);
     default_context_.set_network_delegate(&network_delegate);
 
     TestDelegate d;
@@ -2569,7 +2568,6 @@
   // Verify that the cookie is sent for first-party requests.
   {
     TestNetworkDelegate network_delegate;
-    network_delegate.set_first_party_only_cookies_enabled(true);
     default_context_.set_network_delegate(&network_delegate);
     TestDelegate d;
     scoped_ptr<URLRequest> req(default_context_.CreateRequest(
@@ -2587,7 +2585,6 @@
   // Verify that the cookie is not-sent for non-first-party requests.
   {
     TestNetworkDelegate network_delegate;
-    network_delegate.set_first_party_only_cookies_enabled(true);
     default_context_.set_network_delegate(&network_delegate);
     TestDelegate d;
     scoped_ptr<URLRequest> req(default_context_.CreateRequest(
@@ -2603,66 +2600,6 @@
   }
 }
 
-TEST_F(URLRequestTest, FirstPartyOnlyCookiesDisabled) {
-  LocalHttpTestServer test_server;
-  ASSERT_TRUE(test_server.Start());
-
-  // Set up a 'First-Party-Only' cookie (on '127.0.0.1', as that's where
-  // LocalHttpTestServer points).
-  {
-    TestNetworkDelegate network_delegate;
-    network_delegate.set_first_party_only_cookies_enabled(false);
-    default_context_.set_network_delegate(&network_delegate);
-
-    TestDelegate d;
-    scoped_ptr<URLRequest> req(default_context_.CreateRequest(
-        test_server.GetURL(
-            "set-cookie?FirstPartyCookieToSet=1;First-Party-Only"),
-        DEFAULT_PRIORITY, &d));
-    req->Start();
-    base::RunLoop().Run();
-    EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
-    EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
-    EXPECT_EQ(1, network_delegate.set_cookie_count());
-  }
-
-  // Verify that the cookie is sent for first-party requests.
-  {
-    TestNetworkDelegate network_delegate;
-    network_delegate.set_first_party_only_cookies_enabled(false);
-    default_context_.set_network_delegate(&network_delegate);
-    TestDelegate d;
-    scoped_ptr<URLRequest> req(default_context_.CreateRequest(
-        test_server.GetURL("echoheader?Cookie"), DEFAULT_PRIORITY, &d));
-    req->set_first_party_for_cookies(test_server.GetURL(""));
-    req->Start();
-    base::RunLoop().Run();
-
-    EXPECT_TRUE(d.data_received().find("FirstPartyCookieToSet=1") !=
-                std::string::npos);
-    EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
-    EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
-  }
-
-  // Verify that the cookie is also sent for non-first-party requests.
-  {
-    TestNetworkDelegate network_delegate;
-    network_delegate.set_first_party_only_cookies_enabled(false);
-    default_context_.set_network_delegate(&network_delegate);
-    TestDelegate d;
-    scoped_ptr<URLRequest> req(default_context_.CreateRequest(
-        test_server.GetURL("echoheader?Cookie"), DEFAULT_PRIORITY, &d));
-    req->set_first_party_for_cookies(GURL("http://third-party.test/"));
-    req->Start();
-    base::RunLoop().Run();
-
-    EXPECT_TRUE(d.data_received().find("FirstPartyCookieToSet=1") !=
-                std::string::npos);
-    EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
-    EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
-  }
-}
-
 // FixedDateNetworkDelegate swaps out the server's HTTP Date response header
 // value for the |fixed_date| argument given to the constructor.
 class FixedDateNetworkDelegate : public TestNetworkDelegate {
diff --git a/net/websockets/websocket_basic_stream_test.cc b/net/websockets/websocket_basic_stream_test.cc
index 2955ad66..29420a2f0b 100644
--- a/net/websockets/websocket_basic_stream_test.cc
+++ b/net/websockets/websocket_basic_stream_test.cc
@@ -105,8 +105,7 @@
 class WebSocketBasicStreamSocketTest : public WebSocketBasicStreamTest {
  protected:
   WebSocketBasicStreamSocketTest()
-      : histograms_("a"),
-        pool_(1, 1, &histograms_, &factory_),
+      : pool_(1, 1, &factory_),
         generator_(&GenerateNulMaskingKey),
         expect_all_io_to_complete_(true) {}
 
@@ -164,7 +163,6 @@
 
   scoped_ptr<SocketDataProvider> socket_data_;
   MockClientSocketFactory factory_;
-  ClientSocketPoolHistograms histograms_;
   MockTransportClientSocketPool pool_;
   CapturingBoundNetLog(bound_net_log_);
   ScopedVector<WebSocketFrame> frames_;
diff --git a/net/websockets/websocket_handshake_stream_create_helper_test.cc b/net/websockets/websocket_handshake_stream_create_helper_test.cc
index 8edbc66e..a269655b 100644
--- a/net/websockets/websocket_handshake_stream_create_helper_test.cc
+++ b/net/websockets/websocket_handshake_stream_create_helper_test.cc
@@ -28,8 +28,7 @@
 class MockClientSocketHandleFactory {
  public:
   MockClientSocketHandleFactory()
-      : histograms_("a"),
-        pool_(1, 1, &histograms_, socket_factory_maker_.factory()) {}
+      : pool_(1, 1, socket_factory_maker_.factory()) {}
 
   // The created socket expects |expect_written| to be written to the socket,
   // and will respond with |return_to_read|. The test will fail if the expected
@@ -51,7 +50,6 @@
 
  private:
   WebSocketDeterministicMockClientSocketFactoryMaker socket_factory_maker_;
-  ClientSocketPoolHistograms histograms_;
   MockTransportClientSocketPool pool_;
 
   DISALLOW_COPY_AND_ASSIGN(MockClientSocketHandleFactory);
diff --git a/ppapi/api/ppb_messaging.idl b/ppapi/api/ppb_messaging.idl
index 120367f..aa5c6d7 100644
--- a/ppapi/api/ppb_messaging.idl
+++ b/ppapi/api/ppb_messaging.idl
@@ -9,6 +9,8 @@
  * specific module instance.
  */
 
+[generate_thunk]
+
 label Chrome {
   M14 = 1.0,
   M39 = 1.2
diff --git a/ppapi/c/dev/ppb_messaging_deprecated.h b/ppapi/c/dev/ppb_messaging_deprecated.h
deleted file mode 100644
index 045c9d9..0000000
--- a/ppapi/c/dev/ppb_messaging_deprecated.h
+++ /dev/null
@@ -1,47 +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 PPAPI_C_DEV_IMPL_PPB_MESSAGING_DEPRECATED_H_
-#define PPAPI_C_DEV_IMPL_PPB_MESSAGING_DEPRECATED_H_
-
-#include "ppapi/c/pp_instance.h"
-#include "ppapi/c/pp_resource.h"
-#include "ppapi/c/pp_stdint.h"
-#include "ppapi/c/pp_var.h"
-
-/* dev, deprecated */
-#define PPB_MESSAGING_INTERFACE_1_1_DEPRECATED "PPB_Messaging;1.1"
-
-/**
- * This file defines a dev-channel-only API, PPB_Messaging;1.1 that is
- * deprecated, but which still should provide ABI compatibility for a little
- * bit longer to give time to transition off of it. The header is *not* provided
- * in ppapi/c/ppb_messaging.h, in order to push clients to the new API,
- * version 1.2.
- * TODO(dmichael): Delete this API altogether when all uses are gone.
- *                 crbug.com/414398
- */
-struct PPP_MessageHandler_0_1_Deprecated { /* dev */
-  void (*HandleMessage)(PP_Instance instance,
-                        void* user_data,
-                        struct PP_Var message);
-  struct PP_Var (*HandleBlockingMessage)(PP_Instance instance,
-                                void* user_data,
-                                struct PP_Var message);
-  void (*Destroy)(PP_Instance instance, void* user_data);
-};
-
-struct PPB_Messaging_1_1_Deprecated { /* dev */
-  void (*PostMessage)(PP_Instance instance, struct PP_Var message);
-  int32_t (*RegisterMessageHandler)(
-      PP_Instance instance,
-      void* user_data,
-      const struct PPP_MessageHandler_0_1_Deprecated* handler,
-      PP_Resource message_loop);
-  void (*UnregisterMessageHandler)(PP_Instance instance);
-};
-
-#endif  /* PPAPI_C_DEV_IMPL_PPB_MESSAGING_DEPRECATED_H_ */
-
diff --git a/ppapi/ppapi_proxy.gypi b/ppapi/ppapi_proxy.gypi
index 9e22654..e4d3407 100644
--- a/ppapi/ppapi_proxy.gypi
+++ b/ppapi/ppapi_proxy.gypi
@@ -287,6 +287,7 @@
               'proxy/ppb_flash_proxy.cc',
               'proxy/ppb_pdf_proxy.cc',
               'proxy/ppb_talk_private_proxy.cc',
+              'proxy/ppb_var_deprecated_proxy.cc',
               'proxy/ppb_video_capture_proxy.cc',
               'proxy/ppb_video_decoder_proxy.cc',
               'proxy/ppp_content_decryptor_private_proxy.cc',
diff --git a/ppapi/proxy/BUILD.gn b/ppapi/proxy/BUILD.gn
index 0997fe4e..e21d347 100644
--- a/ppapi/proxy/BUILD.gn
+++ b/ppapi/proxy/BUILD.gn
@@ -114,8 +114,6 @@
     "ppb_message_loop_proxy.h",
     "ppb_testing_proxy.cc",
     "ppb_testing_proxy.h",
-    "ppb_var_deprecated_proxy.cc",
-    "ppb_var_deprecated_proxy.h",
     "ppb_x509_certificate_private_proxy.cc",
     "ppb_x509_certificate_private_proxy.h",
     "ppp_class_proxy.cc",
@@ -246,6 +244,8 @@
       "ppb_buffer_proxy.h",
       "ppb_flash_message_loop_proxy.cc",
       "ppb_flash_message_loop_proxy.h",
+      "ppb_var_deprecated_proxy.cc",
+      "ppb_var_deprecated_proxy.h",
       "ppb_video_decoder_proxy.cc",
       "ppb_video_decoder_proxy.h",
       "ppp_content_decryptor_private_proxy.cc",
diff --git a/ppapi/proxy/interface_list.cc b/ppapi/proxy/interface_list.cc
index ff1e6537..767751c 100644
--- a/ppapi/proxy/interface_list.cc
+++ b/ppapi/proxy/interface_list.cc
@@ -17,7 +17,6 @@
 #include "ppapi/c/dev/ppb_gles_chromium_texture_mapping_dev.h"
 #include "ppapi/c/dev/ppb_ime_input_event_dev.h"
 #include "ppapi/c/dev/ppb_memory_dev.h"
-#include "ppapi/c/dev/ppb_messaging_deprecated.h"
 #include "ppapi/c/dev/ppb_opengles2ext_dev.h"
 #include "ppapi/c/dev/ppb_printing_dev.h"
 #include "ppapi/c/dev/ppb_scrollbar_dev.h"
diff --git a/ppapi/proxy/message_handler.cc b/ppapi/proxy/message_handler.cc
index a0f5ccc..af9a45f 100644
--- a/ppapi/proxy/message_handler.cc
+++ b/ppapi/proxy/message_handler.cc
@@ -19,8 +19,6 @@
 typedef void (*HandleMessageFunc)(PP_Instance, void*, const PP_Var*);
 typedef void (*HandleBlockingMessageFunc)(
     PP_Instance, void*, const PP_Var*, PP_Var*);
-typedef void (*HandleMessageFunc_0_1)(PP_Instance, void*, PP_Var);
-typedef PP_Var (*HandleBlockingMessageFunc_0_1)(PP_Instance, void*, PP_Var);
 
 void HandleMessageWrapper(HandleMessageFunc function,
                           PP_Instance instance,
@@ -52,37 +50,6 @@
   dispatcher->Send(reply_msg.release());
 }
 
-// TODO(dmichael): Remove the 0_1 verions; crbug.com/414398
-void HandleMessageWrapper_0_1(HandleMessageFunc_0_1 function,
-                              PP_Instance instance,
-                              void* user_data,
-                              ScopedPPVar message_data) {
-  CallWhileUnlocked(function, instance, user_data, message_data.get());
-}
-
-void HandleBlockingMessageWrapper_0_1(HandleBlockingMessageFunc_0_1 function,
-                                      PP_Instance instance,
-                                      void* user_data,
-                                      ScopedPPVar message_data,
-                                      scoped_ptr<IPC::Message> reply_msg) {
-  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
-  if (!dispatcher)
-    return;
-  MessageLoopResource::GetCurrent()->
-      set_currently_handling_blocking_message(true);
-  PP_Var return_value = CallWhileUnlocked(function,
-                                          instance,
-                                          user_data,
-                                          message_data.get());
-  MessageLoopResource::GetCurrent()->
-      set_currently_handling_blocking_message(false);
-  PpapiMsg_PPPMessageHandler_HandleBlockingMessage::WriteReplyParams(
-      reply_msg.get(),
-      SerializedVarReturnValue::Convert(dispatcher, return_value),
-      true /* was_handled */);
-  dispatcher->Send(reply_msg.release());
-}
-
 }  // namespace
 
 // static
@@ -120,57 +87,12 @@
   return result.Pass();
 }
 
-// CreateDeprecated is a near-exact copy of Create, but we'll just delete it
-// when 0.1 is deprecated, so need to get fancy to avoid code duplication.
-// TODO(dmichael) crbug.com/414398
-// static
-scoped_ptr<MessageHandler> MessageHandler::CreateDeprecated(
-      PP_Instance instance,
-      const PPP_MessageHandler_0_1_Deprecated* handler_if,
-      void* user_data,
-      PP_Resource message_loop,
-      int32_t* error) {
-  scoped_ptr<MessageHandler> result;
-  // The interface and all function pointers must be valid.
-  if (!handler_if ||
-      !handler_if->HandleMessage ||
-      !handler_if->HandleBlockingMessage ||
-      !handler_if->Destroy) {
-    *error = PP_ERROR_BADARGUMENT;
-    return result.Pass();
-  }
-  thunk::EnterResourceNoLock<thunk::PPB_MessageLoop_API>
-      enter_loop(message_loop, true);
-  if (enter_loop.failed()) {
-    *error = PP_ERROR_BADRESOURCE;
-    return result.Pass();
-  }
-  scoped_refptr<MessageLoopResource> message_loop_resource(
-      static_cast<MessageLoopResource*>(enter_loop.object()));
-  if (message_loop_resource->is_main_thread_loop()) {
-    *error = PP_ERROR_WRONG_THREAD;
-    return result.Pass();
-  }
-
-  result.reset(new MessageHandler(
-      instance, handler_if, user_data, message_loop_resource));
-  *error = PP_OK;
-  return result.Pass();
-}
-
 MessageHandler::~MessageHandler() {
   // It's possible the message_loop_proxy is NULL if that loop has been quit.
   // In that case, we unfortunately just can't call Destroy.
   if (message_loop_->message_loop_proxy().get()) {
     // The posted task won't have the proxy lock, but that's OK, it doesn't
     // touch any internal state; it's a direct call on the plugin's function.
-    if (handler_if_0_1_) {
-      message_loop_->message_loop_proxy()->PostTask(FROM_HERE,
-          base::Bind(handler_if_0_1_->Destroy,
-                     instance_,
-                     user_data_));
-      return;
-    }
     message_loop_->message_loop_proxy()->PostTask(FROM_HERE,
         base::Bind(handler_if_->Destroy,
                    instance_,
@@ -183,16 +105,6 @@
 }
 
 void MessageHandler::HandleMessage(ScopedPPVar var) {
-  if (handler_if_0_1_) {
-    // TODO(dmichael): Remove this code path. crbug.com/414398
-    message_loop_->message_loop_proxy()->PostTask(FROM_HERE,
-        RunWhileLocked(base::Bind(&HandleMessageWrapper_0_1,
-                                  handler_if_0_1_->HandleMessage,
-                                  instance_,
-                                  user_data_,
-                                  var)));
-    return;
-  }
   message_loop_->message_loop_proxy()->PostTask(FROM_HERE,
       RunWhileLocked(base::Bind(&HandleMessageWrapper,
                                 handler_if_->HandleMessage,
@@ -203,17 +115,6 @@
 
 void MessageHandler::HandleBlockingMessage(ScopedPPVar var,
                                            scoped_ptr<IPC::Message> reply_msg) {
-  if (handler_if_0_1_) {
-    // TODO(dmichael): Remove this code path. crbug.com/414398
-    message_loop_->message_loop_proxy()->PostTask(FROM_HERE,
-        RunWhileLocked(base::Bind(&HandleBlockingMessageWrapper_0_1,
-                                  handler_if_0_1_->HandleBlockingMessage,
-                                  instance_,
-                                  user_data_,
-                                  var,
-                                  base::Passed(reply_msg.Pass()))));
-    return;
-  }
   message_loop_->message_loop_proxy()->PostTask(FROM_HERE,
       RunWhileLocked(base::Bind(&HandleBlockingMessageWrapper,
                                 handler_if_->HandleBlockingMessage,
@@ -230,19 +131,6 @@
     scoped_refptr<MessageLoopResource> message_loop)
     : instance_(instance),
       handler_if_(handler_if),
-      handler_if_0_1_(NULL),
-      user_data_(user_data),
-      message_loop_(message_loop) {
-}
-
-MessageHandler::MessageHandler(
-    PP_Instance instance,
-    const PPP_MessageHandler_0_1_Deprecated* handler_if,
-    void* user_data,
-    scoped_refptr<MessageLoopResource> message_loop)
-    : instance_(instance),
-      handler_if_(NULL),
-      handler_if_0_1_(handler_if),
       user_data_(user_data),
       message_loop_(message_loop) {
 }
diff --git a/ppapi/proxy/message_handler.h b/ppapi/proxy/message_handler.h
index 1d047bb3..cb3c6ab8 100644
--- a/ppapi/proxy/message_handler.h
+++ b/ppapi/proxy/message_handler.h
@@ -7,7 +7,6 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
-#include "ppapi/c/dev/ppb_messaging_deprecated.h"
 #include "ppapi/c/pp_resource.h"
 #include "ppapi/c/ppp_message_handler.h"
 #include "ppapi/proxy/ppapi_proxy_export.h"
@@ -48,15 +47,6 @@
       void* user_data,
       PP_Resource message_loop,
       int32_t* error);
-  // Provide temporary backwards compatibility. TODO(dmichael): Remove all
-  // references to PPB_Messaging_1_1 and PPP_MessageHandler_0_1.
-  // crbug.com/414398
-  static scoped_ptr<MessageHandler> CreateDeprecated(
-      PP_Instance instance,
-      const PPP_MessageHandler_0_1_Deprecated* handler_if,
-      void* user_data,
-      PP_Resource message_loop,
-      int32_t* error);
   ~MessageHandler();
 
   bool LoopIsValid() const;
@@ -70,15 +60,8 @@
                  const PPP_MessageHandler_0_2* handler_if,
                  void* user_data,
                  scoped_refptr<MessageLoopResource> message_loop);
-  MessageHandler(PP_Instance instance,
-                 const PPP_MessageHandler_0_1_Deprecated* handler_if,
-                 void* user_data,
-                 scoped_refptr<MessageLoopResource> message_loop);
-
-
   PP_Instance instance_;
   const PPP_MessageHandler_0_2* handler_if_;
-  const PPP_MessageHandler_0_1_Deprecated* handler_if_0_1_;
   void* user_data_;
   scoped_refptr<MessageLoopResource> message_loop_;
 
diff --git a/ppapi/proxy/plugin_dispatcher.cc b/ppapi/proxy/plugin_dispatcher.cc
index 0f15645e..91ac6826 100644
--- a/ppapi/proxy/plugin_dispatcher.cc
+++ b/ppapi/proxy/plugin_dispatcher.cc
@@ -9,6 +9,7 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/trace_event/trace_event.h"
 #include "ipc/ipc_message.h"
 #include "ipc/ipc_sync_channel.h"
@@ -206,6 +207,7 @@
   if (msg->is_sync()) {
     // Synchronous messages might be re-entrant, so we need to drop the lock.
     ProxyAutoUnlock unlock;
+    SCOPED_UMA_HISTOGRAM_TIMER("Plugin.PpapiSyncIPCTime");
     return SendMessage(msg);
   }
   return SendMessage(msg);
diff --git a/ppapi/proxy/ppb_core_proxy.cc b/ppapi/proxy/ppb_core_proxy.cc
index 4c0b372..0cf2a95e 100644
--- a/ppapi/proxy/ppb_core_proxy.cc
+++ b/ppapi/proxy/ppb_core_proxy.cc
@@ -63,6 +63,13 @@
   if (!callback.func)
     return;
   ProxyAutoLock lock;
+
+  // If the plugin attempts to call CallOnMainThread from a background thread
+  // at shutdown, it's possible that the PpapiGlobals object or the main loop
+  // has been destroyed.
+  if (!PpapiGlobals::Get() || !PpapiGlobals::Get()->GetMainThreadMessageLoop())
+    return;
+
   PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostDelayedTask(
       FROM_HERE,
       RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)),
diff --git a/ppapi/proxy/ppb_instance_proxy.cc b/ppapi/proxy/ppb_instance_proxy.cc
index 08daacb2..a381c1d 100644
--- a/ppapi/proxy/ppb_instance_proxy.cc
+++ b/ppapi/proxy/ppb_instance_proxy.cc
@@ -813,25 +813,6 @@
   return result;
 }
 
-// TODO(dmichael): Remove this. crbug.com/414398
-int32_t PPB_Instance_Proxy::RegisterMessageHandler_1_1_Deprecated(
-    PP_Instance instance,
-    void* user_data,
-    const PPP_MessageHandler_0_1_Deprecated* handler,
-    PP_Resource message_loop) {
-  InstanceData* data =
-      static_cast<PluginDispatcher*>(dispatcher())->GetInstanceData(instance);
-  if (!data)
-    return PP_ERROR_BADARGUMENT;
-
-  int32_t result = PP_ERROR_FAILED;
-  scoped_ptr<MessageHandler> message_handler = MessageHandler::CreateDeprecated(
-      instance, handler, user_data, message_loop, &result);
-  if (message_handler)
-    data->message_handler = message_handler.Pass();
-  return result;
-}
-
 void PPB_Instance_Proxy::UnregisterMessageHandler(PP_Instance instance) {
   InstanceData* data =
       static_cast<PluginDispatcher*>(dispatcher())->GetInstanceData(instance);
diff --git a/ppapi/proxy/ppb_instance_proxy.h b/ppapi/proxy/ppb_instance_proxy.h
index 6cf3d06..1500ad69 100644
--- a/ppapi/proxy/ppb_instance_proxy.h
+++ b/ppapi/proxy/ppb_instance_proxy.h
@@ -85,11 +85,6 @@
                                  void* user_data,
                                  const PPP_MessageHandler_0_2* handler,
                                  PP_Resource message_loop) override;
-  int32_t RegisterMessageHandler_1_1_Deprecated(
-      PP_Instance instance,
-      void* user_data,
-      const PPP_MessageHandler_0_1_Deprecated* handler,
-      PP_Resource message_loop) override;
   void UnregisterMessageHandler(PP_Instance instance) override;
   PP_Bool SetCursor(PP_Instance instance,
                     PP_MouseCursor_Type type,
diff --git a/ppapi/thunk/interfaces_ppb_public_dev_channel.h b/ppapi/thunk/interfaces_ppb_public_dev_channel.h
index 51f7c1c..c6c0546 100644
--- a/ppapi/thunk/interfaces_ppb_public_dev_channel.h
+++ b/ppapi/thunk/interfaces_ppb_public_dev_channel.h
@@ -10,8 +10,6 @@
 // Interfaces go here.
 PROXIED_IFACE(PPB_COMPOSITOR_INTERFACE_0_1, PPB_Compositor_0_1)
 PROXIED_IFACE(PPB_COMPOSITORLAYER_INTERFACE_0_1, PPB_CompositorLayer_0_1)
-PROXIED_IFACE(PPB_MESSAGING_INTERFACE_1_1_DEPRECATED,
-              PPB_Messaging_1_1_Deprecated)
 PROXIED_IFACE(PPB_UDPSOCKET_INTERFACE_1_2, PPB_UDPSocket_1_2)
 PROXIED_IFACE(PPB_VIDEODECODER_INTERFACE_0_1, PPB_VideoDecoder_0_1)
 PROXIED_IFACE(PPB_VIDEOENCODER_INTERFACE_0_1, PPB_VideoEncoder_0_1)
diff --git a/ppapi/thunk/ppb_instance_api.h b/ppapi/thunk/ppb_instance_api.h
index 1b3b4da..5c5813b7 100644
--- a/ppapi/thunk/ppb_instance_api.h
+++ b/ppapi/thunk/ppb_instance_api.h
@@ -7,7 +7,6 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
-#include "ppapi/c/dev/ppb_messaging_deprecated.h"
 #include "ppapi/c/dev/ppb_url_util_dev.h"
 #include "ppapi/c/pp_bool.h"
 #include "ppapi/c/pp_completion_callback.h"
@@ -121,11 +120,6 @@
                                          void* user_data,
                                          const PPP_MessageHandler_0_2* handler,
                                          PP_Resource message_loop) = 0;
-  virtual int32_t RegisterMessageHandler_1_1_Deprecated(
-      PP_Instance instance,
-      void* user_data,
-      const PPP_MessageHandler_0_1_Deprecated* handler,
-      PP_Resource message_loop) = 0;
   virtual void UnregisterMessageHandler(PP_Instance instance) = 0;
 
   // Mouse cursor.
diff --git a/ppapi/thunk/ppb_messaging_thunk.cc b/ppapi/thunk/ppb_messaging_thunk.cc
index 1d5d041..94ba4aa 100644
--- a/ppapi/thunk/ppb_messaging_thunk.cc
+++ b/ppapi/thunk/ppb_messaging_thunk.cc
@@ -2,9 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// From ppb_messaging.idl modified Wed Sep 10 15:41:14 2014.
+// From ppb_messaging.idl modified Tue Mar 24 16:53:47 2015.
 
-#include "ppapi/c/dev/ppb_messaging_deprecated.h"
 #include "ppapi/c/pp_errors.h"
 #include "ppapi/c/ppb_messaging.h"
 #include "ppapi/shared_impl/tracked_callback.h"
@@ -24,32 +23,15 @@
   enter.functions()->PostMessage(instance, message);
 }
 
-int32_t RegisterMessageHandler_1_1_Deprecated(
-    PP_Instance instance,
-    void* user_data,
-    const struct PPP_MessageHandler_0_1_Deprecated* handler,
-    PP_Resource message_loop) {
-  VLOG(4) << "PPB_Messaging::RegisterMessageHandler_1_1()";
-  EnterInstance enter(instance);
-  if (enter.failed())
-    return enter.retval();
-  return enter.functions()->RegisterMessageHandler_1_1_Deprecated(instance,
-                                                                  user_data,
-                                                                  handler,
-                                                                  message_loop);
-}
-
-int32_t RegisterMessageHandler_1_2(PP_Instance instance,
-                                   void* user_data,
-                                   const struct PPP_MessageHandler_0_2* handler,
-                                   PP_Resource message_loop) {
+int32_t RegisterMessageHandler(PP_Instance instance,
+                               void* user_data,
+                               const struct PPP_MessageHandler_0_2* handler,
+                               PP_Resource message_loop) {
   VLOG(4) << "PPB_Messaging::RegisterMessageHandler()";
   EnterInstance enter(instance);
   if (enter.failed())
     return enter.retval();
-  return enter.functions()->RegisterMessageHandler(instance,
-                                                   user_data,
-                                                   handler,
+  return enter.functions()->RegisterMessageHandler(instance, user_data, handler,
                                                    message_loop);
 }
 
@@ -61,21 +43,11 @@
   enter.functions()->UnregisterMessageHandler(instance);
 }
 
-const PPB_Messaging_1_0 g_ppb_messaging_thunk_1_0 = {
-  &PostMessage
-};
+const PPB_Messaging_1_0 g_ppb_messaging_thunk_1_0 = {&PostMessage};
 
-const PPB_Messaging_1_1_Deprecated g_ppb_messaging_thunk_1_1_deprecated = {
-  &PostMessage,
-  &RegisterMessageHandler_1_1_Deprecated,
-  &UnregisterMessageHandler
-};
-
-const PPB_Messaging_1_2 g_ppb_messaging_thunk_1_2 = {
-  &PostMessage,
-  &RegisterMessageHandler_1_2,
-  &UnregisterMessageHandler
-};
+const PPB_Messaging_1_2 g_ppb_messaging_thunk_1_2 = {&PostMessage,
+                                                     &RegisterMessageHandler,
+                                                     &UnregisterMessageHandler};
 
 }  // namespace
 
@@ -83,11 +55,6 @@
   return &g_ppb_messaging_thunk_1_0;
 }
 
-PPAPI_THUNK_EXPORT
-const PPB_Messaging_1_1_Deprecated* GetPPB_Messaging_1_1_Deprecated_Thunk() {
-  return &g_ppb_messaging_thunk_1_1_deprecated;
-}
-
 PPAPI_THUNK_EXPORT const PPB_Messaging_1_2* GetPPB_Messaging_1_2_Thunk() {
   return &g_ppb_messaging_thunk_1_2;
 }
diff --git a/remoting/BUILD.gn b/remoting/BUILD.gn
index ecf6fb53..2fcdf64 100644
--- a/remoting/BUILD.gn
+++ b/remoting/BUILD.gn
@@ -28,6 +28,7 @@
     "test/access_token_fetcher.h",
     "test/app_remoting_test_driver_environment.cc",
     "test/app_remoting_test_driver_environment.h",
+    "test/app_remoting_test_driver_environment_app_details.cc",
     "test/fake_access_token_fetcher.cc",
     "test/fake_access_token_fetcher.h",
     "test/fake_network_dispatcher.cc",
@@ -46,6 +47,7 @@
     "test/mock_access_token_fetcher.h",
     "test/refresh_token_store.cc",
     "test/refresh_token_store.h",
+    "test/remote_application_details.h",
     "test/remote_connection_observer.h",
     "test/remote_host_info.cc",
     "test/remote_host_info.h",
@@ -111,7 +113,10 @@
     if (is_android) {
       deps += [ "//testing/android:native_test_native_code" ]
     } else {
-      deps += [ "//remoting/client/plugin" ]
+      deps += [
+        "//remoting/client/plugin",
+        "//remoting/client/plugin:unit_tests",
+      ]
     }
 
     if (enable_remoting_host) {
diff --git a/remoting/app_remoting_test.gyp b/remoting/app_remoting_test.gyp
index f4825df..8c33043 100644
--- a/remoting/app_remoting_test.gyp
+++ b/remoting/app_remoting_test.gyp
@@ -31,10 +31,23 @@
       ],
       'sources': [
         'test/access_token_fetcher.cc',
+        'test/access_token_fetcher.h',
+        'test/app_remoting_connected_client_fixture.cc',
+        'test/app_remoting_connected_client_fixture.h',
         'test/app_remoting_test_driver_environment.cc',
+        'test/app_remoting_test_driver_environment.h',
         'test/refresh_token_store.cc',
+        'test/refresh_token_store.h',
+        'test/remote_application_details.h',
+        'test/remote_connection_observer.h',
         'test/remote_host_info.cc',
+        'test/remote_host_info.h',
         'test/remote_host_info_fetcher.cc',
+        'test/remote_host_info_fetcher.h',
+        'test/test_chromoting_client.cc',
+        'test/test_chromoting_client.h',
+        'test/test_video_renderer.cc',
+        'test/test_video_renderer.h',
       ],
     },  # end of target 'ar_test_driver_common'
     {
@@ -49,6 +62,7 @@
       ],
       'sources': [
         'test/app_remoting_test_driver.cc',
+        'test/app_remoting_test_driver_environment_app_details.cc',
       ],
       'include_dirs': [
         '../testing/gtest/include',
diff --git a/remoting/app_remoting_webapp_build.gypi b/remoting/app_remoting_webapp_build.gypi
index 4450de0..9a59c908 100644
--- a/remoting/app_remoting_webapp_build.gypi
+++ b/remoting/app_remoting_webapp_build.gypi
@@ -11,20 +11,6 @@
     'app_remoting_webapp_files.gypi',
   ],
 
-  'variables': {
-    # The ar_service_environment variable is used to define the target
-    # environment for the app being built.
-    # The allowed values are dev, test, staging, and prod.
-    'conditions': [
-      ['buildtype == "Dev"', {
-        'ar_service_environment%': 'dev',
-      }, {  # buildtype != 'Dev'
-        # Non-dev build must have this set to 'prod'.
-        'ar_service_environment': 'prod',
-      }],
-    ],  # conditions
-  },  # end of variables
-
   'target_defaults': {
     'type': 'none',
 
diff --git a/remoting/app_remoting_webapp_files.gypi b/remoting/app_remoting_webapp_files.gypi
index 69c8baf..2b263be 100644
--- a/remoting/app_remoting_webapp_files.gypi
+++ b/remoting/app_remoting_webapp_files.gypi
@@ -27,6 +27,7 @@
       'webapp/app_remoting/js/feedback_consent.js',
       'webapp/base/js/base.js',
       'webapp/crd/js/error.js',
+      'webapp/crd/js/identity.js',
       'webapp/crd/js/oauth2_api.js',
       'webapp/crd/js/oauth2_api_impl.js',
       'webapp/crd/js/plugin_settings.js',
diff --git a/remoting/client/BUILD.gn b/remoting/client/BUILD.gn
index b47888a..ecbb86d3 100644
--- a/remoting/client/BUILD.gn
+++ b/remoting/client/BUILD.gn
@@ -30,15 +30,9 @@
     "audio_player_unittest.cc",
     "client_status_logger_unittest.cc",
     "key_event_mapper_unittest.cc",
-    "plugin/empty_cursor_filter_unittest.cc",
-    "plugin/normalizing_input_filter_mac_unittest.cc",
     "server_log_entry_client_unittest.cc",
   ]
 
-  if (is_chromeos) {
-    sources += [ "plugin/normalizing_input_filter_cros_unittest.cc" ]
-  }
-
   deps = [
     ":client",
     "//remoting/proto",
diff --git a/remoting/client/plugin/BUILD.gn b/remoting/client/plugin/BUILD.gn
index 9594824..9fb1fe7 100644
--- a/remoting/client/plugin/BUILD.gn
+++ b/remoting/client/plugin/BUILD.gn
@@ -40,3 +40,25 @@
     "//ui/events:dom4_keycode_converter",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "empty_cursor_filter_unittest.cc",
+    "normalizing_input_filter_mac_unittest.cc",
+    "touch_input_scaler_unittest.cc",
+  ]
+
+  if (is_chromeos) {
+    sources += [ "normalizing_input_filter_cros_unittest.cc" ]
+  }
+
+  deps = [
+    ":plugin",
+    "//remoting/proto",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/webrtc",
+  ]
+}
diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc
index d4ad35f5..262bbbd 100644
--- a/remoting/client/plugin/chromoting_instance.cc
+++ b/remoting/client/plugin/chromoting_instance.cc
@@ -91,6 +91,8 @@
     case protocol::ConnectionToHost::AUTHENTICATED:
       // Report the authenticated state as 'CONNECTING' to avoid changing
       // the interface between the plugin and webapp.
+      // TODO(garykac) Change to 'AUTHENTICATED' in M44 or once we've switched
+      // the client to NaCl.
       return "CONNECTING";
     case protocol::ConnectionToHost::CONNECTED:
       return "CONNECTED";
diff --git a/remoting/host/curtain_mode_mac.cc b/remoting/host/curtain_mode_mac.cc
index cae8e704..0ac4aa5 100644
--- a/remoting/host/curtain_mode_mac.cc
+++ b/remoting/host/curtain_mode_mac.cc
@@ -144,7 +144,10 @@
   // this, or how common it is, a crash report is useful in this case (note
   // that the connection would have to be refused in any case, so this is no
   // loss of functionality).
-  CHECK(session != nullptr);
+  CHECK(session != nullptr)
+      << "Error activating curtain-mode: "
+      << "CGSessionCopyCurrentDictionary() returned NULL. "
+      << "Logging out and back in should resolve this error.";
 
   const void* on_console = CFDictionaryGetValue(session,
                                                 kCGSessionOnConsoleKey);
diff --git a/remoting/host/input_injector_win.cc b/remoting/host/input_injector_win.cc
index 698c6862..37f628ac 100644
--- a/remoting/host/input_injector_win.cc
+++ b/remoting/host/input_injector_win.cc
@@ -15,6 +15,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "remoting/base/util.h"
 #include "remoting/host/clipboard.h"
+#include "remoting/host/touch_injector_win.h"
 #include "remoting/proto/event.pb.h"
 #include "ui/events/keycodes/dom4/keycode_converter.h"
 
@@ -87,6 +88,7 @@
     void InjectKeyEvent(const KeyEvent& event);
     void InjectTextEvent(const TextEvent& event);
     void InjectMouseEvent(const MouseEvent& event);
+    void InjectTouchEvent(const TouchEvent& event);
 
     // Mirrors the InputInjector interface.
     void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard);
@@ -100,10 +102,12 @@
     void HandleKey(const KeyEvent& event);
     void HandleText(const TextEvent& event);
     void HandleMouse(const MouseEvent& event);
+    void HandleTouch(const TouchEvent& event);
 
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
     scoped_ptr<Clipboard> clipboard_;
+    TouchInjectorWin touch_injector_;
 
     DISALLOW_COPY_AND_ASSIGN(Core);
   };
@@ -140,7 +144,7 @@
 }
 
 void InputInjectorWin::InjectTouchEvent(const TouchEvent& event) {
-  NOTIMPLEMENTED() << "Raw touch event injection not implemented for Windows.";
+  core_->InjectTouchEvent(event);
 }
 
 void InputInjectorWin::Start(
@@ -197,6 +201,16 @@
   HandleMouse(event);
 }
 
+void InputInjectorWin::Core::InjectTouchEvent(const TouchEvent& event) {
+  if (!main_task_runner_->BelongsToCurrentThread()) {
+    main_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&Core::InjectTouchEvent, this, event));
+    return;
+  }
+
+  HandleTouch(event);
+}
+
 void InputInjectorWin::Core::Start(
     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
   if (!ui_task_runner_->BelongsToCurrentThread()) {
@@ -207,6 +221,7 @@
   }
 
   clipboard_->Start(client_clipboard.Pass());
+  touch_injector_.Init();
 }
 
 void InputInjectorWin::Core::Stop() {
@@ -216,6 +231,7 @@
   }
 
   clipboard_.reset();
+  touch_injector_.Deinitialize();
 }
 
 InputInjectorWin::Core::~Core() {}
@@ -326,6 +342,10 @@
   }
 }
 
+void InputInjectorWin::Core::HandleTouch(const TouchEvent& event) {
+  touch_injector_.InjectTouchEvent(event);
+}
+
 }  // namespace
 
 scoped_ptr<InputInjector> InputInjector::Create(
diff --git a/remoting/host/touch_injector_win.cc b/remoting/host/touch_injector_win.cc
new file mode 100644
index 0000000..3eaadf47
--- /dev/null
+++ b/remoting/host/touch_injector_win.cc
@@ -0,0 +1,285 @@
+// 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 "remoting/host/touch_injector_win.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/native_library.h"
+#include "base/stl_util.h"
+#include "remoting/proto/event.pb.h"
+
+namespace remoting {
+
+using protocol::TouchEvent;
+using protocol::TouchEventPoint;
+
+namespace {
+
+typedef BOOL(NTAPI* InitializeTouchInjectionFunction)(UINT32, DWORD);
+typedef BOOL(NTAPI* InjectTouchInputFunction)(UINT32,
+                                              const POINTER_TOUCH_INFO*);
+const uint32_t kMaxSimultaneousTouchCount = 10;
+
+// This is used to reinject all points that have not changed as "move"ed points,
+// even if they have not actually moved.
+// This is required for multi-touch to work, e.g. pinching and zooming gestures
+// (handled by apps) won't work without reinjecting the points, even though the
+// user moved only one finger and held the other finger in place.
+void AppendMapValuesToVector(
+    std::map<uint32_t, POINTER_TOUCH_INFO>* touches_in_contact,
+    std::vector<POINTER_TOUCH_INFO>* output_vector) {
+  for (auto& id_and_pointer_touch_info : *touches_in_contact) {
+    POINTER_TOUCH_INFO& pointer_touch_info = id_and_pointer_touch_info.second;
+    output_vector->push_back(pointer_touch_info);
+  }
+}
+
+// The caller should set memset(0) the struct and set
+// pointer_touch_info->pointerInfo.pointerFlags.
+void ConvertToPointerTouchInfo(
+    const TouchEventPoint& touch_point,
+    POINTER_TOUCH_INFO* pointer_touch_info) {
+  pointer_touch_info->touchMask =
+      TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION;
+  pointer_touch_info->touchFlags = TOUCH_FLAG_NONE;
+
+  // Although radius_{x,y} can be undefined (i.e. has_radius_{x,y} == false),
+  // the default value (0.0) will set the area correctly.
+  // MSDN mentions that if the digitizer does not detect the size of the touch
+  // point, rcContact should be set to 0 by 0 rectangle centered at the
+  // coordinate.
+  pointer_touch_info->rcContact.left =
+      touch_point.x() - touch_point.radius_x();
+  pointer_touch_info->rcContact.top = touch_point.y() - touch_point.radius_y();
+  pointer_touch_info->rcContact.right =
+      touch_point.x() + touch_point.radius_x();
+  pointer_touch_info->rcContact.bottom =
+      touch_point.y() + touch_point.radius_y();
+
+  pointer_touch_info->orientation = touch_point.angle();
+
+  if (touch_point.has_pressure()) {
+    pointer_touch_info->touchMask |= TOUCH_MASK_PRESSURE;
+    const float kMinimumPressure = 0.0;
+    const float kMaximumPressure = 1.0;
+    const float clamped_touch_point_pressure =
+        std::max(kMinimumPressure,
+                 std::min(kMaximumPressure, touch_point.pressure()));
+
+    const int kWindowsMaxTouchPressure = 1024;  // Defined in MSDN.
+    const int pressure =
+        clamped_touch_point_pressure * kWindowsMaxTouchPressure;
+    pointer_touch_info->pressure = pressure;
+  }
+
+  pointer_touch_info->pointerInfo.pointerType = PT_TOUCH;
+  pointer_touch_info->pointerInfo.pointerId = touch_point.id();
+  pointer_touch_info->pointerInfo.ptPixelLocation.x = touch_point.x();
+  pointer_touch_info->pointerInfo.ptPixelLocation.y = touch_point.y();
+}
+
+}  // namespace
+
+TouchInjectorWinDelegate::~TouchInjectorWinDelegate() {}
+
+// static.
+scoped_ptr<TouchInjectorWinDelegate> TouchInjectorWinDelegate::Create() {
+  base::ScopedNativeLibrary library(base::FilePath(L"User32.dll"));
+  if (!library.is_valid()) {
+    PLOG(INFO) << "Failed to get library module for touch injection functions.";
+    return scoped_ptr<TouchInjectorWinDelegate>();
+  }
+
+  InitializeTouchInjectionFunction init_func =
+      reinterpret_cast<InitializeTouchInjectionFunction>(
+          library.GetFunctionPointer("InitializeTouchInjection"));
+  if (!init_func) {
+    PLOG(INFO) << "Failed to get InitializeTouchInjection function handle.";
+    return scoped_ptr<TouchInjectorWinDelegate>();
+  }
+
+  InjectTouchInputFunction inject_touch_func =
+      reinterpret_cast<InjectTouchInputFunction>(
+          library.GetFunctionPointer("InjectTouchInput"));
+  if (!inject_touch_func) {
+    PLOG(INFO) << "Failed to get InjectTouchInput.";
+    return scoped_ptr<TouchInjectorWinDelegate>();
+  }
+
+  return scoped_ptr<TouchInjectorWinDelegate>(
+      new TouchInjectorWinDelegate(
+          library.Release(), init_func, inject_touch_func));
+}
+
+TouchInjectorWinDelegate::TouchInjectorWinDelegate(
+    base::NativeLibrary library,
+    InitializeTouchInjectionFunction initialize_touch_injection_func,
+    InjectTouchInputFunction inject_touch_input_func)
+    : library_module_(library),
+      initialize_touch_injection_func_(initialize_touch_injection_func),
+      inject_touch_input_func_(inject_touch_input_func) {}
+
+BOOL TouchInjectorWinDelegate::InitializeTouchInjection(UINT32 max_count,
+                                                        DWORD dw_mode) {
+  return initialize_touch_injection_func_(max_count, dw_mode);
+}
+
+DWORD TouchInjectorWinDelegate::InjectTouchInput(
+    UINT32 count,
+    const POINTER_TOUCH_INFO* contacts) {
+  return inject_touch_input_func_(count, contacts);
+}
+
+TouchInjectorWin::TouchInjectorWin()
+    : delegate_(TouchInjectorWinDelegate::Create()) {}
+
+TouchInjectorWin::~TouchInjectorWin() {}
+
+// Note that TouchInjectorWinDelegate::Create() is not called in this method
+// so that a mock delegate can be injected in tests and set expectations on the
+// mock and return value of this method.
+bool TouchInjectorWin::Init() {
+  if (!delegate_)
+    return false;
+
+  if (!delegate_->InitializeTouchInjection(
+          kMaxSimultaneousTouchCount, TOUCH_FEEDBACK_DEFAULT)) {
+    // delagate_ is reset here so that the function that need the delegate
+    // can check if it is null.
+    delegate_.reset();
+    PLOG(INFO) << "Failed to initialize touch injection.";
+    return false;
+  }
+
+  return true;
+}
+
+void TouchInjectorWin::Deinitialize() {
+  touches_in_contact_.clear();
+  // Same reason as TouchInjectorWin::Init(). For injecting mock delegates for
+  // tests, a new delegate is created here.
+  delegate_ = TouchInjectorWinDelegate::Create();
+}
+
+void TouchInjectorWin::InjectTouchEvent(const TouchEvent& event) {
+  if (!delegate_) {
+    VLOG(3) << "Touch injection functions are not initialized.";
+    return;
+  }
+
+  switch (event.event_type()) {
+    case TouchEvent::TOUCH_POINT_START:
+      AddNewTouchPoints(event);
+      break;
+    case TouchEvent::TOUCH_POINT_MOVE:
+      MoveTouchPoints(event);
+      break;
+    case TouchEvent::TOUCH_POINT_END:
+      EndTouchPoints(event);
+      break;
+    case TouchEvent::TOUCH_POINT_CANCEL:
+      CancelTouchPoints(event);
+      break;
+    default:
+      NOTREACHED();
+      return;
+  }
+}
+
+void TouchInjectorWin::SetInjectorDelegateForTest(
+    scoped_ptr<TouchInjectorWinDelegate> functions) {
+  delegate_ = functions.Pass();
+}
+
+void TouchInjectorWin::AddNewTouchPoints(const TouchEvent& event) {
+  DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_START);
+
+  std::vector<POINTER_TOUCH_INFO> touches;
+  // Must inject already touching points as move events.
+  AppendMapValuesToVector(&touches_in_contact_, &touches);
+
+  for (const TouchEventPoint& touch_point : event.touch_points()) {
+    POINTER_TOUCH_INFO pointer_touch_info;
+    memset(&pointer_touch_info, 0, sizeof(pointer_touch_info));
+    pointer_touch_info.pointerInfo.pointerFlags =
+        POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_DOWN;
+    ConvertToPointerTouchInfo(touch_point, &pointer_touch_info);
+    touches.push_back(pointer_touch_info);
+
+    // All points in the map should be a move point.
+    pointer_touch_info.pointerInfo.pointerFlags =
+        POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
+    touches_in_contact_[touch_point.id()] = pointer_touch_info;
+  }
+
+  if (delegate_->InjectTouchInput(touches.size(),
+                                  vector_as_array(&touches)) == 0) {
+    PLOG(ERROR) << "Failed to inject a touch start event.";
+  }
+}
+
+void TouchInjectorWin::MoveTouchPoints(const TouchEvent& event) {
+  DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_MOVE);
+
+  for (const TouchEventPoint& touch_point : event.touch_points()) {
+    POINTER_TOUCH_INFO* pointer_touch_info =
+        &touches_in_contact_[touch_point.id()];
+    memset(pointer_touch_info, 0, sizeof(*pointer_touch_info));
+    pointer_touch_info->pointerInfo.pointerFlags =
+        POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
+    ConvertToPointerTouchInfo(touch_point, pointer_touch_info);
+  }
+
+  std::vector<POINTER_TOUCH_INFO> touches;
+  // Must inject already touching points as move events.
+  AppendMapValuesToVector(&touches_in_contact_, &touches);
+  if (delegate_->InjectTouchInput(touches.size(),
+                                  vector_as_array(&touches)) == 0) {
+    PLOG(ERROR) << "Failed to inject a touch move event.";
+  }
+}
+
+void TouchInjectorWin::EndTouchPoints(const TouchEvent& event) {
+  DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_END);
+
+  std::vector<POINTER_TOUCH_INFO> touches;
+  for (const TouchEventPoint& touch_point : event.touch_points()) {
+    POINTER_TOUCH_INFO pointer_touch_info =
+        touches_in_contact_[touch_point.id()];
+    pointer_touch_info.pointerInfo.pointerFlags = POINTER_FLAG_UP;
+
+    touches_in_contact_.erase(touch_point.id());
+    touches.push_back(pointer_touch_info);
+  }
+
+  AppendMapValuesToVector(&touches_in_contact_, &touches);
+  if (delegate_->InjectTouchInput(touches.size(),
+                                  vector_as_array(&touches)) == 0) {
+    PLOG(ERROR) << "Failed to inject a touch end event.";
+  }
+}
+
+void TouchInjectorWin::CancelTouchPoints(const TouchEvent& event) {
+  DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_CANCEL);
+
+  std::vector<POINTER_TOUCH_INFO> touches;
+  for (const TouchEventPoint& touch_point : event.touch_points()) {
+    POINTER_TOUCH_INFO pointer_touch_info =
+        touches_in_contact_[touch_point.id()];
+    pointer_touch_info.pointerInfo.pointerFlags =
+        POINTER_FLAG_UP | POINTER_FLAG_CANCELED;
+
+    touches_in_contact_.erase(touch_point.id());
+    touches.push_back(pointer_touch_info);
+  }
+
+  AppendMapValuesToVector(&touches_in_contact_, &touches);
+  if (delegate_->InjectTouchInput(touches.size(),
+                                  vector_as_array(&touches)) == 0) {
+    PLOG(ERROR) << "Failed to inject a touch cancel event.";
+  }
+}
+
+}  // namespace remoting
diff --git a/remoting/host/touch_injector_win.h b/remoting/host/touch_injector_win.h
new file mode 100644
index 0000000..2b429de
--- /dev/null
+++ b/remoting/host/touch_injector_win.h
@@ -0,0 +1,106 @@
+// 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 REMOTING_HOST_TOUCH_INJECTOR_WIN_H_
+#define REMOTING_HOST_TOUCH_INJECTOR_WIN_H_
+
+#include <windows.h>
+#include <map>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/scoped_native_library.h"
+
+namespace remoting {
+
+namespace protocol {
+
+class TouchEvent;
+
+}  // namespace protocol
+
+// This class calls InitializeTouchInjection() and InjectTouchInput() functions.
+// The methods are virtual for mocking.
+class TouchInjectorWinDelegate {
+ public:
+  virtual ~TouchInjectorWinDelegate();
+
+  // Determines whether Windows touch injection functions can be used.
+  // Returns a non-null TouchInjectorWinDelegate on success.
+  static scoped_ptr<TouchInjectorWinDelegate> Create();
+
+  // These match the functions in MSDN.
+  virtual BOOL InitializeTouchInjection(UINT32 max_count, DWORD dw_mode);
+  virtual DWORD InjectTouchInput(UINT32 count,
+                                 const POINTER_TOUCH_INFO* contacts);
+
+ protected:
+  // Ctor in protected scope for mocking.
+  // This object takes ownership of the |library|.
+  TouchInjectorWinDelegate(
+      base::NativeLibrary library,
+      BOOL(NTAPI* initialize_touch_injection_func)(UINT32, DWORD),
+      BOOL(NTAPI* inject_touch_input_func)(UINT32, const POINTER_TOUCH_INFO*));
+
+ private:
+  base::ScopedNativeLibrary library_module_;
+
+  // Pointers to Windows touch injection functions.
+  BOOL(NTAPI* initialize_touch_injection_func_)(UINT32, DWORD);
+  BOOL(NTAPI* inject_touch_input_func_)(UINT32, const POINTER_TOUCH_INFO*);
+
+  DISALLOW_COPY_AND_ASSIGN(TouchInjectorWinDelegate);
+};
+
+// This class converts TouchEvent objects to POINTER_TOUCH_INFO so that it can
+// be injected using the Windows touch injection API, and calls the injection
+// functions.
+// This class expects good inputs and does not sanity check the inputs.
+// This class just converts the object and hands it off to the Windows API.
+class TouchInjectorWin {
+ public:
+  TouchInjectorWin();
+  ~TouchInjectorWin();
+
+  // Returns false if initialization of touch injection APIs fails.
+  bool Init();
+
+  // Deinitializes the object so that it can be reinitialized.
+  void Deinitialize();
+
+  // Inject touch events.
+  void InjectTouchEvent(const protocol::TouchEvent& event);
+
+  void SetInjectorDelegateForTest(
+      scoped_ptr<TouchInjectorWinDelegate> functions);
+
+ private:
+  // Helper methods called from InjectTouchEvent().
+  // These helpers adapt Chromoting touch events, which convey changes to touch
+  // points, to Windows touch descriptions, which must include descriptions for
+  // all currently-active touch points, not just the changed ones.
+  void AddNewTouchPoints(const protocol::TouchEvent& event);
+  void MoveTouchPoints(const protocol::TouchEvent& event);
+  void EndTouchPoints(const protocol::TouchEvent& event);
+  void CancelTouchPoints(const protocol::TouchEvent& event);
+
+  // Set to null if touch injection is not available from the OS.
+  scoped_ptr<TouchInjectorWinDelegate> delegate_;
+
+  // TODO(rkuroiwa): crbug.com/470203
+  // This is a naive implementation. Check if we can achieve
+  // better performance by reducing the number of copies.
+  // To reduce the number of copies, we can have a vector of
+  // POINTER_TOUCH_INFO and a map from touch ID to index in the vector.
+  // When removing points from the vector, just swap it with the last element
+  // and resize the vector.
+  // All the POINTER_TOUCH_INFOs are stored as "move" points.
+  std::map<uint32_t, POINTER_TOUCH_INFO> touches_in_contact_;
+
+  DISALLOW_COPY_AND_ASSIGN(TouchInjectorWin);
+};
+
+}  // namespace remoting
+
+#endif  // REMOTING_HOST_TOUCH_INJECTOR_WIN_H_
diff --git a/remoting/host/touch_injector_win_unittest.cc b/remoting/host/touch_injector_win_unittest.cc
new file mode 100644
index 0000000..791b95fc
--- /dev/null
+++ b/remoting/host/touch_injector_win_unittest.cc
@@ -0,0 +1,514 @@
+// 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 "remoting/host/touch_injector_win.h"
+
+#include <map>
+
+#include "base/stl_util.h"
+#include "remoting/proto/event.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::InSequence;
+using ::testing::ExpectationSet;
+using ::testing::Return;
+
+namespace remoting {
+
+using protocol::TouchEvent;
+using protocol::TouchEventPoint;
+
+namespace {
+
+// Maps touch pointer ID to expected flags [start, move, end, cancel] listed
+// below.
+typedef std::map<uint32_t, uint32_t> IdFlagMap;
+
+const uint32_t kStartFlag =
+    POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_DOWN;
+
+const uint32_t kMoveFlag =
+    POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
+
+const uint32_t kEndFlag = POINTER_FLAG_UP;
+
+const uint32_t kCancelFlag = POINTER_FLAG_UP | POINTER_FLAG_CANCELED;
+
+MATCHER_P(EqualsSinglePointerTouchInfo, expected, "") {
+  return arg->touchMask == expected.touchMask &&
+         arg->rcContact.left == expected.rcContact.left &&
+         arg->rcContact.top == expected.rcContact.top &&
+         arg->rcContact.right == expected.rcContact.right &&
+         arg->rcContact.bottom == expected.rcContact.bottom &&
+         arg->orientation == expected.orientation &&
+         arg->pressure == expected.pressure &&
+         arg->pointerInfo.pointerType == expected.pointerInfo.pointerType &&
+         arg->pointerInfo.pointerId == expected.pointerInfo.pointerId &&
+         arg->pointerInfo.ptPixelLocation.x ==
+             expected.pointerInfo.ptPixelLocation.x &&
+         arg->pointerInfo.ptPixelLocation.y ==
+             expected.pointerInfo.ptPixelLocation.y;
+}
+
+// Make sure that every touch point has the right flag (pointerFlags).
+MATCHER_P(EqualsPointerTouchInfoFlag, id_to_flag_map, "") {
+  for (size_t i = 0; i < id_to_flag_map.size(); ++i) {
+    const POINTER_TOUCH_INFO* touch_info = arg + i;
+    const uint32_t id = touch_info->pointerInfo.pointerId;
+    if (!ContainsKey(id_to_flag_map, id))
+      return false;
+
+    if (id_to_flag_map.find(id)->second != touch_info->pointerInfo.pointerFlags)
+      return false;
+  }
+  return true;
+}
+
+class TouchInjectorWinDelegateMock : public TouchInjectorWinDelegate {
+ public:
+  TouchInjectorWinDelegateMock()
+      : TouchInjectorWinDelegate(nullptr, nullptr, nullptr) {}
+  ~TouchInjectorWinDelegateMock() override {};
+
+  MOCK_METHOD2(InitializeTouchInjection, BOOL(UINT32 max_count, DWORD dw_mode));
+  MOCK_METHOD2(InjectTouchInput,
+               DWORD(UINT32 count, const POINTER_TOUCH_INFO* contacts));
+};
+
+}  // namespace
+
+// A test to make sure that the touch event is converted correctly to
+// POINTER_TOUCH_INFO.
+TEST(TouchInjectorWinTest, CheckConversionWithPressure) {
+  scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
+      new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
+
+  TouchEvent event;
+  event.set_event_type(TouchEvent::TOUCH_POINT_START);
+  TouchEventPoint* point = event.add_touch_points();
+  point->set_id(1234u);
+  point->set_x(321.0f);
+  point->set_y(123.0f);
+  point->set_radius_x(10.0f);
+  point->set_radius_y(20.0f);
+  point->set_pressure(0.5f);
+  point->set_angle(45.0f);
+
+  POINTER_TOUCH_INFO expected_touch_info;
+  expected_touch_info.touchMask =
+      TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION | TOUCH_MASK_PRESSURE;
+  expected_touch_info.rcContact.left = 311;
+  expected_touch_info.rcContact.top = 103;
+  expected_touch_info.rcContact.right = 331;
+  expected_touch_info.rcContact.bottom = 143;
+  expected_touch_info.orientation = 0;
+  expected_touch_info.pressure = 512;
+  expected_touch_info.orientation = 45;
+
+  expected_touch_info.pointerInfo.pointerType = PT_TOUCH;
+  expected_touch_info.pointerInfo.pointerId = 1234u;
+  expected_touch_info.pointerInfo.ptPixelLocation.x = 321;
+  expected_touch_info.pointerInfo.ptPixelLocation.y = 123;
+
+  InSequence s;
+  EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
+      .WillOnce(Return(1));
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
+      .WillOnce(Return(1));
+
+  // Check pressure clamping as well.
+  expected_touch_info.pressure = 1024;  // Max
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
+      .WillOnce(Return(1));
+
+  expected_touch_info.pressure = 0;  // Min
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
+      .WillOnce(Return(1));
+
+  TouchInjectorWin injector;
+  injector.SetInjectorDelegateForTest(delegate_mock.Pass());
+  EXPECT_TRUE(injector.Init());
+  injector.InjectTouchEvent(event);
+
+  // Change to MOVE so that there still only one point.
+  event.set_event_type(TouchEvent::TOUCH_POINT_MOVE);
+  point->set_pressure(2.0f);
+  injector.InjectTouchEvent(event);
+
+  point->set_pressure(-3.0f);
+  injector.InjectTouchEvent(event);
+}
+
+// Some devices don't detect pressure. This test is a conversion check for
+// such devices.
+TEST(TouchInjectorWinTest, CheckConversionNoPressure) {
+  scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
+      new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
+
+  TouchEvent event;
+  event.set_event_type(TouchEvent::TOUCH_POINT_START);
+  TouchEventPoint* point = event.add_touch_points();
+  point->set_id(1234u);
+  point->set_x(321.0f);
+  point->set_y(123.0f);
+  point->set_radius_x(10.0f);
+  point->set_radius_y(20.0f);
+  point->set_angle(45.0f);
+
+  POINTER_TOUCH_INFO expected_touch_info;
+  expected_touch_info.touchMask =
+      TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION;
+  expected_touch_info.rcContact.left = 311;
+  expected_touch_info.rcContact.top = 103;
+  expected_touch_info.rcContact.right = 331;
+  expected_touch_info.rcContact.bottom = 143;
+  expected_touch_info.orientation = 0;
+  expected_touch_info.pressure = 0;
+  expected_touch_info.orientation = 45;
+
+  expected_touch_info.pointerInfo.pointerType = PT_TOUCH;
+  expected_touch_info.pointerInfo.pointerId = 1234u;
+  expected_touch_info.pointerInfo.ptPixelLocation.x = 321;
+  expected_touch_info.pointerInfo.ptPixelLocation.y = 123;
+
+  InSequence s;
+  EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
+      .WillOnce(Return(1));
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
+      .WillOnce(Return(1));
+
+  TouchInjectorWin injector;
+  injector.SetInjectorDelegateForTest(delegate_mock.Pass());
+  EXPECT_TRUE(injector.Init());
+  injector.InjectTouchEvent(event);
+}
+
+// If initialization fails, it should not call any touch injection functions.
+TEST(TouchInjectorWinTest, InitFailed) {
+  scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
+      new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
+
+  TouchEvent event;
+  event.set_event_type(TouchEvent::TOUCH_POINT_START);
+
+  InSequence s;
+  EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
+      .WillOnce(Return(0));
+  EXPECT_CALL(*delegate_mock, InjectTouchInput(_, _)).Times(0);
+
+  TouchInjectorWin injector;
+  injector.SetInjectorDelegateForTest(delegate_mock.Pass());
+  EXPECT_FALSE(injector.Init());
+  injector.InjectTouchEvent(event);
+}
+
+// Deinitialize and initialize should clean the state.
+TEST(TouchInjectorWinTest, Reinitialize) {
+  scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock_before_deinitialize(
+      new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
+  scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock_after_deinitialize(
+      new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
+
+  TouchEvent first_event;
+  first_event.set_event_type(TouchEvent::TOUCH_POINT_START);
+  TouchEventPoint* point0 = first_event.add_touch_points();
+  point0->set_id(0u);
+
+  TouchEvent second_event;
+  second_event.set_event_type(TouchEvent::TOUCH_POINT_START);
+  TouchEventPoint* point1 = second_event.add_touch_points();
+  point1->set_id(1u);
+
+  InSequence s;
+  EXPECT_CALL(*delegate_mock_before_deinitialize,
+              InitializeTouchInjection(_, _)).WillOnce(Return(1));
+
+  IdFlagMap id_to_flags;
+  id_to_flags[0u] = kStartFlag;
+  EXPECT_CALL(
+      *delegate_mock_before_deinitialize,
+      InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  EXPECT_CALL(*delegate_mock_after_deinitialize,
+              InitializeTouchInjection(_, _)).WillOnce(Return(1));
+
+  // After deinitializing and then initializing, previous touch points should be
+  // gone.
+  id_to_flags.clear();
+  id_to_flags[1u] = kStartFlag;
+  EXPECT_CALL(
+      *delegate_mock_after_deinitialize,
+      InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  TouchInjectorWin injector;
+  injector.SetInjectorDelegateForTest(delegate_mock_before_deinitialize.Pass());
+
+  EXPECT_TRUE(injector.Init());
+  injector.InjectTouchEvent(first_event);
+  injector.Deinitialize();
+
+  injector.SetInjectorDelegateForTest(delegate_mock_after_deinitialize.Pass());
+  EXPECT_TRUE(injector.Init());
+  injector.InjectTouchEvent(second_event);
+}
+
+// Make sure that the flag is set to kStartFlag.
+TEST(TouchInjectorWinTest, StartTouchPoint) {
+  scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
+      new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
+
+  TouchEvent event;
+  event.set_event_type(TouchEvent::TOUCH_POINT_START);
+  TouchEventPoint* point = event.add_touch_points();
+  point->set_id(0u);
+
+  InSequence s;
+  EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
+      .WillOnce(Return(1));
+
+  IdFlagMap id_to_flags;
+  id_to_flags[0u] = kStartFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  TouchInjectorWin injector;
+  injector.SetInjectorDelegateForTest(delegate_mock.Pass());
+  EXPECT_TRUE(injector.Init());
+  injector.InjectTouchEvent(event);
+}
+
+// Start a point and then move, make sure the flag is set to kMoveFlag.
+TEST(TouchInjectorWinTest, MoveTouchPoint) {
+  scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
+      new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
+
+  TouchEvent event;
+  event.set_event_type(TouchEvent::TOUCH_POINT_START);
+  TouchEventPoint* point = event.add_touch_points();
+  point->set_id(0u);
+
+
+  InSequence s;
+  EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
+      .WillOnce(Return(1));
+
+  IdFlagMap id_to_flags;
+  id_to_flags[0u] = kStartFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  id_to_flags[0u] = kMoveFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  TouchInjectorWin injector;
+  injector.SetInjectorDelegateForTest(delegate_mock.Pass());
+  EXPECT_TRUE(injector.Init());
+  injector.InjectTouchEvent(event);
+  event.set_event_type(TouchEvent::TOUCH_POINT_MOVE);
+  injector.InjectTouchEvent(event);
+}
+
+// Start a point and then move, make sure the flag is set to kEndFlag.
+TEST(TouchInjectorWinTest, EndTouchPoint) {
+  scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
+      new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
+
+  TouchEvent event;
+  event.set_event_type(TouchEvent::TOUCH_POINT_START);
+  TouchEventPoint* point = event.add_touch_points();
+  point->set_id(0u);
+
+  InSequence s;
+  EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
+      .WillOnce(Return(1));
+
+  IdFlagMap id_to_flags;
+  id_to_flags[0u] = kStartFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  id_to_flags[0u] = kEndFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  TouchInjectorWin injector;
+  injector.SetInjectorDelegateForTest(delegate_mock.Pass());
+  EXPECT_TRUE(injector.Init());
+  injector.InjectTouchEvent(event);
+  event.set_event_type(TouchEvent::TOUCH_POINT_END);
+  injector.InjectTouchEvent(event);
+}
+
+// Start a point and then move, make sure the flag is set to kCancelFlag.
+TEST(TouchInjectorWinTest, CancelTouchPoint) {
+  scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
+      new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
+
+  TouchEvent event;
+  event.set_event_type(TouchEvent::TOUCH_POINT_START);
+  TouchEventPoint* point = event.add_touch_points();
+  point->set_id(0u);
+
+  InSequence s;
+  EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
+      .WillOnce(Return(1));
+
+  IdFlagMap id_to_flags;
+  id_to_flags[0u] = kStartFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  id_to_flags[0u] = kCancelFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  TouchInjectorWin injector;
+  injector.SetInjectorDelegateForTest(delegate_mock.Pass());
+  EXPECT_TRUE(injector.Init());
+  injector.InjectTouchEvent(event);
+  event.set_event_type(TouchEvent::TOUCH_POINT_CANCEL);
+  injector.InjectTouchEvent(event);
+}
+
+// Note that points that haven't changed should be injected as MOVE.
+// This tests:
+// 1. Start first touch point.
+// 2. Start second touch point.
+// 3. Move both touch points.
+// 4. Start third touch point.
+// 5. End second touch point.
+// 6. Cancel remaining (first and third) touch points.
+TEST(TouchInjectorWinTest, MultiTouch) {
+  scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
+      new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
+
+  InSequence s;
+  EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
+      .WillOnce(Return(1));
+
+  IdFlagMap id_to_flags;
+  id_to_flags[0u] = kStartFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  id_to_flags[0u] = kMoveFlag;
+  id_to_flags[1u] = kStartFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  id_to_flags[0u] = kMoveFlag;
+  id_to_flags[1u] = kMoveFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  id_to_flags[0u] = kMoveFlag;
+  id_to_flags[1u] = kMoveFlag;
+  id_to_flags[2u] = kStartFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(3, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  id_to_flags[0u] = kMoveFlag;
+  id_to_flags[1u] = kEndFlag;
+  id_to_flags[2u] = kMoveFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(3, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  id_to_flags.erase(1u);
+  id_to_flags[0u] = kCancelFlag;
+  id_to_flags[2u] = kCancelFlag;
+  EXPECT_CALL(
+      *delegate_mock,
+      InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags)))
+      .WillOnce(Return(1));
+
+  TouchInjectorWin injector;
+  injector.SetInjectorDelegateForTest(delegate_mock.Pass());
+  EXPECT_TRUE(injector.Init());
+
+  // Start first touch point.
+  TouchEvent first_touch_start;
+  first_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START);
+  TouchEventPoint* point0 = first_touch_start.add_touch_points();
+  point0->set_id(0u);
+  injector.InjectTouchEvent(first_touch_start);
+
+  // Add second touch point.
+  TouchEvent second_touch_start;
+  second_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START);
+  TouchEventPoint* point1 = second_touch_start.add_touch_points();
+  point1->set_id(1u);
+  injector.InjectTouchEvent(second_touch_start);
+
+  // Move both touch points.
+  TouchEvent move_both;
+  move_both.set_event_type(TouchEvent::TOUCH_POINT_MOVE);
+  point0 = second_touch_start.add_touch_points();
+  point1 = second_touch_start.add_touch_points();
+  point0->set_id(0u);
+  point1->set_id(1u);
+  injector.InjectTouchEvent(move_both);
+
+  // Add another.
+  TouchEvent third_touch_start;
+  third_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START);
+  TouchEventPoint* point2 = third_touch_start.add_touch_points();
+  point2->set_id(2u);
+  injector.InjectTouchEvent(third_touch_start);
+
+  // Release second touch point.
+  TouchEvent release_second;
+  release_second.set_event_type(TouchEvent::TOUCH_POINT_END);
+  point1 = release_second.add_touch_points();
+  point1->set_id(1u);
+  injector.InjectTouchEvent(release_second);
+
+  // Cancel the remaining two points.
+  TouchEvent cancel_rest;
+  cancel_rest.set_event_type(TouchEvent::TOUCH_POINT_CANCEL);
+  point0 = cancel_rest.add_touch_points();
+  point0->set_id(0u);
+  point2 = cancel_rest.add_touch_points();
+  point2->set_id(2u);
+  injector.InjectTouchEvent(cancel_rest);
+}
+
+}  // namespace remoting
diff --git a/remoting/host/win/session_input_injector.cc b/remoting/host/win/session_input_injector.cc
index e229822..bf8158ee 100644
--- a/remoting/host/win/session_input_injector.cc
+++ b/remoting/host/win/session_input_injector.cc
@@ -190,7 +190,14 @@
 }
 
 void SessionInputInjectorWin::Core::InjectTouchEvent(const TouchEvent& event) {
-  NOTIMPLEMENTED();
+  if (!input_task_runner_->BelongsToCurrentThread()) {
+    input_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&Core::InjectTouchEvent, this, event));
+    return;
+  }
+
+  SwitchToInputDesktop();
+  nested_executor_->InjectTouchEvent(event);
 }
 
 SessionInputInjectorWin::Core::~Core() {
diff --git a/remoting/protocol/connection_to_host.h b/remoting/protocol/connection_to_host.h
index 3b079d1..a20bd226 100644
--- a/remoting/protocol/connection_to_host.h
+++ b/remoting/protocol/connection_to_host.h
@@ -34,7 +34,7 @@
 class ConnectionToHost {
  public:
   // The UI implementations maintain corresponding definitions of this
-  // enumeration in webapp/client_session.js and
+  // enumeration in client_session.js and
   // android/java/src/org/chromium/chromoting/jni/JniInterface.java. Be sure to
   // update these locations if you make any changes to the ordering.
   enum State {
diff --git a/remoting/remoting_all.gyp b/remoting/remoting_all.gyp
index 19d5783c..72579c0 100644
--- a/remoting/remoting_all.gyp
+++ b/remoting/remoting_all.gyp
@@ -24,6 +24,7 @@
         '../remoting/remoting.gyp:remoting_webapp',
         '../remoting/remoting.gyp:remoting_webapp_html',
         '../remoting/remoting.gyp:remoting_webapp_unittests',
+        '../remoting/app_remoting_test.gyp:ar_sample_test_driver',
         '../remoting/app_remoting_webapp.gyp:ar_sample_app',
       ],
 
diff --git a/remoting/remoting_host_srcs.gypi b/remoting/remoting_host_srcs.gypi
index 81ecafb..206664d 100644
--- a/remoting/remoting_host_srcs.gypi
+++ b/remoting/remoting_host_srcs.gypi
@@ -224,6 +224,8 @@
       'host/token_validator_base.h',
       'host/token_validator_factory_impl.cc',
       'host/token_validator_factory_impl.h',
+      'host/touch_injector_win.h',
+      'host/touch_injector_win.cc',
       'host/usage_stats_consent.h',
       'host/usage_stats_consent_mac.cc',
       'host/usage_stats_consent_win.cc',
diff --git a/remoting/remoting_options.gypi b/remoting/remoting_options.gypi
index 56d339e..826f528 100644
--- a/remoting/remoting_options.gypi
+++ b/remoting/remoting_options.gypi
@@ -7,10 +7,13 @@
     'chromium_code': 1,
 
     # Set this to run the jscompile checks after building the webapp.
-    'run_jscompile%': 1,
+    'run_jscompile%': 0,
 
     # Set this to enable cast mode on the android client.
     'enable_cast%': 0,
+ 
+    # Set this to use GCD instead of the remoting directory service.
+    'remoting_use_gcd%': 0,
 
     'variables': {
       'conditions': [
@@ -28,13 +31,17 @@
 
     'branding_path': '../remoting/branding_<(branding)',
 
+    # The ar_service_environment variable is used to define the target
+    # environment for the app being built.
+    # The allowed values are dev, test, staging, and prod.
     'conditions': [
-      ['OS=="win"', {
-        # Java is not available on Windows bots, so we need to disable
-        # JScompile checks.
-        'run_jscompile': 0,
+      ['buildtype == "Dev"', {
+        'ar_service_environment%': 'dev',
+      }, {  # buildtype != 'Dev'
+        # Non-dev build must have this set to 'prod'.
+        'ar_service_environment': 'prod',
       }],
-    ],
-  },
+    ],  # conditions
 
+  },
 }
diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi
index bd0ae5a..5a01d1f 100644
--- a/remoting/remoting_test.gypi
+++ b/remoting/remoting_test.gypi
@@ -49,6 +49,7 @@
         'test/access_token_fetcher.cc',
         'test/access_token_fetcher.h',
         'test/app_remoting_test_driver_environment.cc',
+        'test/app_remoting_test_driver_environment_app_details.cc',
         'test/app_remoting_test_driver_environment.h',
         'test/fake_access_token_fetcher.cc',
         'test/fake_access_token_fetcher.h',
@@ -68,6 +69,7 @@
         'test/mock_access_token_fetcher.h',
         'test/refresh_token_store.cc',
         'test/refresh_token_store.h',
+        'test/remote_application_details.h',
         'test/remote_connection_observer.h',
         'test/remote_host_info.cc',
         'test/remote_host_info.h',
@@ -214,6 +216,7 @@
         'host/shaped_desktop_capturer_unittest.cc',
         'host/third_party_auth_config_unittest.cc',
         'host/token_validator_factory_impl_unittest.cc',
+        'host/touch_injector_win_unittest.cc',
         'host/video_frame_pump_unittest.cc',
         'host/video_frame_recorder_unittest.cc',
         'host/win/rdp_client_unittest.cc',
@@ -246,8 +249,6 @@
         'protocol/ssl_hmac_channel_authenticator_unittest.cc',
         'protocol/third_party_authenticator_unittest.cc',
         'protocol/v2_authenticator_unittest.cc',
-        'signaling/fake_signal_strategy.cc',
-        'signaling/fake_signal_strategy.h',
         'signaling/iq_sender_unittest.cc',
         'signaling/log_to_server_unittest.cc',
         'signaling/server_log_entry_unittest.cc',
@@ -279,6 +280,9 @@
           'dependencies!': [
             'remoting_client_plugin',
           ],
+          'sources/': [
+            ['exclude', '^client/plugin/'],
+          ]
         }],
         [ 'OS=="android"', {
           'dependencies': [
diff --git a/remoting/remoting_webapp.gypi b/remoting/remoting_webapp.gypi
index d197e41..2b97a53 100644
--- a/remoting/remoting_webapp.gypi
+++ b/remoting/remoting_webapp.gypi
@@ -22,71 +22,7 @@
   ],
   'conditions': [
     ['run_jscompile != 0', {
-      'variables': {
-        'success_stamp': '<(PRODUCT_DIR)/<(_target_name)_jscompile.stamp',
-        'success_stamp_bt': '<(PRODUCT_DIR)/<(_target_name)_bt_jscompile.stamp',
-        'success_stamp_ut': '<(PRODUCT_DIR)/<(_target_name)_ut_jscompile.stamp',
-      },
-      'actions': [
-        {
-          'action_name': 'Verify remoting webapp',
-          'inputs': [
-            '<@(remoting_webapp_crd_js_files)',
-            '<@(remoting_webapp_js_proto_files)',
-          ],
-          'outputs': [
-            '<(success_stamp)',
-          ],
-          'action': [
-            'python', '../third_party/closure_compiler/checker.py',
-            '--strict',
-            '--no-single-file',
-            '--success-stamp', '<(success_stamp)',
-            '<@(remoting_webapp_crd_js_files)',
-            '<@(remoting_webapp_js_proto_files)',
-          ],
-        },
-        {
-          'action_name': 'Verify remoting webapp with browsertests',
-          'inputs': [
-            '<@(remoting_webapp_crd_js_files)',
-            '<@(remoting_webapp_browsertest_all_js_files)',
-            '<@(remoting_webapp_browsertest_js_proto_files)',
-          ],
-          'outputs': [
-            '<(success_stamp_bt)',
-          ],
-          'action': [
-            'python', '../third_party/closure_compiler/checker.py',
-            '--strict',
-            '--no-single-file',
-            '--success-stamp', '<(success_stamp_bt)',
-            '<@(remoting_webapp_crd_js_files)',
-            '<@(remoting_webapp_browsertest_all_js_files)',
-            '<@(remoting_webapp_browsertest_js_proto_files)',
-          ],
-        },
-        {
-          'action_name': 'Verify remoting webapp unittests',
-          'inputs': [
-            '<@(remoting_webapp_crd_js_files)',
-            '<@(remoting_webapp_unittests_all_js_files)',
-            '<@(remoting_webapp_unittests_js_proto_files)',
-          ],
-          'outputs': [
-            '<(success_stamp_ut)',
-          ],
-          'action': [
-            'python', '../third_party/closure_compiler/checker.py',
-            '--strict',
-            '--no-single-file',
-            '--success-stamp', '<(success_stamp_ut)',
-            '<@(remoting_webapp_crd_js_files)',
-            '<@(remoting_webapp_unittests_all_js_files)',
-            '<@(remoting_webapp_unittests_js_proto_files)',
-          ],
-        },
-      ],  # actions
+      'includes': ['remoting_webapp_compile.gypi'],
     }],
   ],
   'actions': [
@@ -137,6 +73,8 @@
         '<@(extra_files)',
         '--locales_listfile',
         '<(dr_webapp_locales_listfile)',
+        '--use_gcd',
+        '<(remoting_use_gcd)',
       ],
     },
   ],
diff --git a/remoting/remoting_webapp_compile.gypi b/remoting/remoting_webapp_compile.gypi
new file mode 100644
index 0000000..094ff92
--- /dev/null
+++ b/remoting/remoting_webapp_compile.gypi
@@ -0,0 +1,80 @@
+# 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.
+#
+# File in charge of Closure compiling remoting's webapp.
+
+{
+  'variables': {
+    'success_stamp': '<(PRODUCT_DIR)/<(_target_name)_jscompile.stamp',
+    'success_stamp_bt': '<(PRODUCT_DIR)/<(_target_name)_bt_jscompile.stamp',
+    'success_stamp_ut': '<(PRODUCT_DIR)/<(_target_name)_ut_jscompile.stamp',
+  },
+  'actions': [
+    {
+      'action_name': 'Verify remoting webapp',
+      'inputs': [
+        'remoting_webapp_compile.gypi',
+        'remoting_webapp_files.gypi',
+        '<@(remoting_webapp_crd_js_files)',
+        '<@(remoting_webapp_js_proto_files)',
+      ],
+      'outputs': [
+        '<(success_stamp)',
+      ],
+      'action': [
+        'python', '<(DEPTH)/third_party/closure_compiler/checker.py',
+        '--strict',
+        '--no-single-file',
+        '--success-stamp', '<(success_stamp)',
+        '<@(remoting_webapp_crd_js_files)',
+        '<@(remoting_webapp_js_proto_files)',
+      ],
+    },
+    {
+      'action_name': 'Verify remoting webapp with browsertests',
+      'inputs': [
+        'remoting_webapp_compile.gypi',
+        'remoting_webapp_files.gypi',
+        '<@(remoting_webapp_crd_js_files)',
+        '<@(remoting_webapp_browsertest_all_js_files)',
+        '<@(remoting_webapp_browsertest_js_proto_files)',
+      ],
+      'outputs': [
+        '<(success_stamp_bt)',
+      ],
+      'action': [
+        'python', '<(DEPTH)/third_party/closure_compiler/checker.py',
+        '--strict',
+        '--no-single-file',
+        '--success-stamp', '<(success_stamp_bt)',
+        '<@(remoting_webapp_crd_js_files)',
+        '<@(remoting_webapp_browsertest_all_js_files)',
+        '<@(remoting_webapp_browsertest_js_proto_files)',
+      ],
+    },
+    {
+      'action_name': 'Verify remoting webapp unittests',
+      'inputs': [
+        'remoting_webapp_compile.gypi',
+        'remoting_webapp_files.gypi',
+        '<@(remoting_webapp_crd_js_files)',
+        '<@(remoting_webapp_unittests_all_js_files)',
+        '<@(remoting_webapp_unittests_js_proto_files)',
+      ],
+      'outputs': [
+        '<(success_stamp_ut)',
+      ],
+      'action': [
+        'python', '<(DEPTH)/third_party/closure_compiler/checker.py',
+        '--strict',
+        '--no-single-file',
+        '--success-stamp', '<(success_stamp_ut)',
+        '<@(remoting_webapp_crd_js_files)',
+        '<@(remoting_webapp_unittests_all_js_files)',
+        '<@(remoting_webapp_unittests_js_proto_files)',
+      ],
+    },
+  ],
+  'includes': ['remoting_webapp_files.gypi'],
+}
diff --git a/remoting/test/access_token_fetcher.cc b/remoting/test/access_token_fetcher.cc
index 5fc36efb..4fa690e 100644
--- a/remoting/test/access_token_fetcher.cc
+++ b/remoting/test/access_token_fetcher.cc
@@ -21,26 +21,6 @@
 const char kOauthRedirectUrl[] =
     "https://chromoting-oauth.talkgadget."
     "google.com/talkgadget/oauth/chrome-remote-desktop/dev";
-
-// Factory function used to initialize our scope vector below, this is needed
-// because initializer lists are only supported on C++11 compilers.
-const std::vector<std::string> MakeAppRemotingScopeVector() {
-  std::vector<std::string> app_remoting_scopes;
-
-  // Populate the vector with the required permissions for app remoting.
-  app_remoting_scopes.push_back(
-      "https://www.googleapis.com/auth/appremoting.runapplication");
-  app_remoting_scopes.push_back("https://www.googleapis.com/auth/googletalk");
-  app_remoting_scopes.push_back(
-      "https://www.googleapis.com/auth/userinfo.email");
-  app_remoting_scopes.push_back("https://docs.google.com/feeds");
-  app_remoting_scopes.push_back("https://www.googleapis.com/auth/drive");
-
-  return app_remoting_scopes;
-}
-
-const std::vector<std::string> kAppRemotingScopeVector =
-    MakeAppRemotingScopeVector();
 }  // namespace
 
 namespace remoting {
@@ -53,7 +33,8 @@
       kOauthRedirectUrl};
 }
 
-AccessTokenFetcher::~AccessTokenFetcher() {}
+AccessTokenFetcher::~AccessTokenFetcher() {
+}
 
 void AccessTokenFetcher::GetAccessTokenFromAuthCode(
     const std::string& auth_code,
@@ -90,12 +71,10 @@
 
   // Create a new GaiaOAuthClient for each request to GAIA.
   CreateNewGaiaOAuthClientInstance();
-  auth_client_->RefreshToken(
-      oauth_client_info_,
-      refresh_token_,
-      kAppRemotingScopeVector,
-      kMaxGetTokensRetries,
-      this);  // GaiaOAuthClient::Delegate* delegate
+  auth_client_->RefreshToken(oauth_client_info_, refresh_token_,
+                             std::vector<std::string>(),  // scopes
+                             kMaxGetTokensRetries,
+                             this);  // GaiaOAuthClient::Delegate* delegate
 }
 
 void AccessTokenFetcher::CreateNewGaiaOAuthClientInstance() {
@@ -162,7 +141,6 @@
     token_info->GetString("error_description", &error_description);
 
     LOG(ERROR) << "OnGetTokenInfoResponse returned an error. "
-               << ", "
                << "error: " << error_string << ", "
                << "description: " << error_description;
     access_token_.clear();
@@ -201,10 +179,8 @@
 
   // Create a new GaiaOAuthClient for each request to GAIA.
   CreateNewGaiaOAuthClientInstance();
-  auth_client_->GetTokenInfo(
-      access_token_,
-      kMaxGetTokensRetries,
-      this);  // GaiaOAuthClient::Delegate* delegate
+  auth_client_->GetTokenInfo(access_token_, kMaxGetTokensRetries,
+                             this);  // GaiaOAuthClient::Delegate* delegate
 }
 
 }  // namespace test
diff --git a/remoting/test/access_token_fetcher.h b/remoting/test/access_token_fetcher.h
index 2452e9ae..50062f7 100644
--- a/remoting/test/access_token_fetcher.h
+++ b/remoting/test/access_token_fetcher.h
@@ -5,6 +5,8 @@
 #ifndef REMOTING_TEST_ACCESS_TOKEN_FETCHER_H_
 #define REMOTING_TEST_ACCESS_TOKEN_FETCHER_H_
 
+#include <string>
+
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
@@ -15,9 +17,9 @@
 
 // Supplied by the client for each request to GAIA and returns valid tokens on
 // success or empty tokens on failure.
-typedef base::Callback<void(
-    const std::string& access_token,
-    const std::string& refresh_token)> AccessTokenCallback;
+typedef base::Callback<void(const std::string& access_token,
+                            const std::string& refresh_token)>
+    AccessTokenCallback;
 
 // Retrieves an access token from either an authorization code or a refresh
 // token.  Destroying the AccessTokenFetcher while a request is outstanding will
diff --git a/remoting/test/app_remoting_connected_client_fixture.cc b/remoting/test/app_remoting_connected_client_fixture.cc
new file mode 100644
index 0000000..5c29c13
--- /dev/null
+++ b/remoting/test/app_remoting_connected_client_fixture.cc
@@ -0,0 +1,256 @@
+// 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 "remoting/test/app_remoting_connected_client_fixture.h"
+
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/timer/timer.h"
+#include "base/values.h"
+#include "remoting/protocol/host_stub.h"
+#include "remoting/test/app_remoting_test_driver_environment.h"
+#include "remoting/test/remote_application_details.h"
+#include "remoting/test/test_chromoting_client.h"
+
+namespace {
+const int kDefaultDPI = 96;
+const int kDefaultWidth = 1024;
+const int kDefaultHeight = 768;
+
+const char kHostProcessWindowTitle[] = "Host Process";
+
+void SimpleHostMessageHandler(
+    const std::string& target_message_type,
+    const std::string& target_message_data,
+    const base::Closure& done_closure,
+    bool* message_received,
+    const remoting::protocol::ExtensionMessage& message) {
+  if (message.type() == target_message_type &&
+      message.data() == target_message_data) {
+    *message_received = true;
+    done_closure.Run();
+  }
+}
+}  // namespace
+
+namespace remoting {
+namespace test {
+
+AppRemotingConnectedClientFixture::AppRemotingConnectedClientFixture()
+    : application_details_(
+          AppRemotingSharedData->GetDetailsFromAppName(GetParam())),
+      connection_is_ready_for_tests_(false),
+      timer_(new base::Timer(true, false)) {
+}
+
+AppRemotingConnectedClientFixture::~AppRemotingConnectedClientFixture() {
+}
+
+void AppRemotingConnectedClientFixture::SetUp() {
+  message_loop_.reset(new base::MessageLoopForIO);
+
+  client_.reset(new TestChromotingClient());
+  client_->AddRemoteConnectionObserver(this);
+
+  StartConnection();
+
+  if (!connection_is_ready_for_tests_) {
+    FAIL() << "Remote host connection could not be established.";
+    client_->EndConnection();
+  }
+}
+
+void AppRemotingConnectedClientFixture::TearDown() {
+  // |client_| must be destroyed before |message_loop_| as some of its
+  // members are destroyed via DeleteSoon on the message loop's TaskRunner.
+  client_->RemoveRemoteConnectionObserver(this);
+  client_.reset();
+
+  base::RunLoop().RunUntilIdle();
+
+  message_loop_.reset();
+}
+
+bool AppRemotingConnectedClientFixture::VerifyResponseForSimpleHostMessage(
+    const std::string& message_request_title,
+    const std::string& message_response_title,
+    const std::string& message_payload,
+    const base::TimeDelta& max_wait_time) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  bool message_received = false;
+
+  DCHECK(!run_loop_ || !run_loop_->running());
+  run_loop_.reset(new base::RunLoop());
+
+  host_message_received_callback_ =
+      base::Bind(&SimpleHostMessageHandler, message_response_title,
+                 message_payload, run_loop_->QuitClosure(), &message_received);
+
+  protocol::ExtensionMessage message;
+  message.set_type(message_request_title);
+  message.set_data(message_payload);
+  client_->host_stub()->DeliverClientMessage(message);
+
+  DCHECK(!timer_->IsRunning());
+  timer_->Start(FROM_HERE, max_wait_time, run_loop_->QuitClosure());
+
+  run_loop_->Run();
+  timer_->Stop();
+
+  host_message_received_callback_.Reset();
+
+  return message_received;
+}
+
+void AppRemotingConnectedClientFixture::StartConnection() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  RemoteHostInfo remote_host_info;
+  remoting::test::AppRemotingSharedData->GetRemoteHostInfoForApplicationId(
+      application_details_.application_id, &remote_host_info);
+
+  if (!remote_host_info.IsReadyForConnection()) {
+    LOG(ERROR) << "Remote Host is unavailable for connections.";
+    return;
+  }
+
+  DCHECK(!run_loop_ || !run_loop_->running());
+  run_loop_.reset(new base::RunLoop());
+
+  // We will wait up to 30 seconds to complete the remote connection and for the
+  // main application window to become visible.
+  DCHECK(!timer_->IsRunning());
+  timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(30),
+                run_loop_->QuitClosure());
+
+  client_->StartConnection(AppRemotingSharedData->user_name(),
+                           AppRemotingSharedData->access_token(),
+                           remote_host_info);
+
+  run_loop_->Run();
+  timer_->Stop();
+}
+
+void AppRemotingConnectedClientFixture::ConnectionStateChanged(
+    protocol::ConnectionToHost::State state,
+    protocol::ErrorCode error_code) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // If the connection is closed or failed then mark the connection as closed
+  // and quit the current RunLoop if it exists.
+  if (state == protocol::ConnectionToHost::CLOSED ||
+      state == protocol::ConnectionToHost::FAILED ||
+      error_code != protocol::OK) {
+    connection_is_ready_for_tests_ = false;
+
+    if (run_loop_) {
+      run_loop_->Quit();
+    }
+  }
+}
+
+void AppRemotingConnectedClientFixture::ConnectionReady(bool ready) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (ready) {
+    SendClientConnectionDetailsToHost();
+  } else {
+    // We will only get called here with a false value for |ready| if the video
+    // renderer encounters an error.
+    connection_is_ready_for_tests_ = false;
+
+    if (run_loop_) {
+      run_loop_->Quit();
+    }
+  }
+}
+
+void AppRemotingConnectedClientFixture::HostMessageReceived(
+    const protocol::ExtensionMessage& message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // If a callback is not registered, then the message is passed to a default
+  // handler for the class based on the message type.
+  if (!host_message_received_callback_.is_null()) {
+    host_message_received_callback_.Run(message);
+  } else if (message.type() == "onWindowAdded") {
+    HandleOnWindowAddedMessage(message);
+  } else {
+    DVLOG(2) << "HostMessage not handled by HostMessageReceived().";
+    DVLOG(2) << "type: " << message.type();
+    DVLOG(2) << "data: " << message.data();
+  }
+}
+
+void AppRemotingConnectedClientFixture::SendClientConnectionDetailsToHost() {
+  // First send an access token which will be used for Google Drive access.
+  protocol::ExtensionMessage message;
+  message.set_type("accessToken");
+  message.set_data(AppRemotingSharedData->access_token());
+
+  DVLOG(1) << "Sending access token to host";
+  client_->host_stub()->DeliverClientMessage(message);
+
+  // Next send the host a description of the client screen size.
+  protocol::ClientResolution client_resolution;
+  client_resolution.set_width(kDefaultWidth);
+  client_resolution.set_height(kDefaultHeight);
+  client_resolution.set_x_dpi(kDefaultDPI);
+  client_resolution.set_y_dpi(kDefaultDPI);
+  client_resolution.set_dips_width(kDefaultWidth);
+  client_resolution.set_dips_height(kDefaultHeight);
+
+  DVLOG(1) << "Sending ClientResolution details to host";
+  client_->host_stub()->NotifyClientResolution(client_resolution);
+
+  // Finally send a message to start sending us video packets.
+  protocol::VideoControl video_control;
+  video_control.set_enable(true);
+
+  DVLOG(1) << "Sending enable VideoControl message to host";
+  client_->host_stub()->ControlVideo(video_control);
+}
+
+void AppRemotingConnectedClientFixture::HandleOnWindowAddedMessage(
+    const remoting::protocol::ExtensionMessage& message) {
+  DCHECK_EQ(message.type(), "onWindowAdded");
+
+  const base::DictionaryValue* message_data = nullptr;
+  scoped_ptr<base::Value> host_message(base::JSONReader::Read(message.data()));
+  if (!host_message.get() || !host_message->GetAsDictionary(&message_data)) {
+    LOG(ERROR) << "onWindowAdded message received was not valid JSON.";
+    if (run_loop_) {
+      run_loop_->Quit();
+    }
+    return;
+  }
+
+  std::string current_window_title;
+  message_data->GetString("title", &current_window_title);
+  if (current_window_title == kHostProcessWindowTitle) {
+    LOG(ERROR) << "Host Process Window is visible, this likely means that the "
+               << "underlying application is in a bad state, YMMV.";
+  }
+
+  std::string main_window_title = application_details_.main_window_title;
+  if (current_window_title.find_first_of(main_window_title) == 0) {
+    connection_is_ready_for_tests_ = true;
+
+    if (timer_->IsRunning()) {
+      timer_->Stop();
+    }
+
+    DCHECK(run_loop_);
+    // Now that the main window is visible, give the app some time to settle
+    // before signaling that it is ready to run tests.
+    timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(2),
+                  run_loop_->QuitClosure());
+  }
+}
+
+}  // namespace test
+}  // namespace remoting
diff --git a/remoting/test/app_remoting_connected_client_fixture.h b/remoting/test/app_remoting_connected_client_fixture.h
new file mode 100644
index 0000000..1d7235d
--- /dev/null
+++ b/remoting/test/app_remoting_connected_client_fixture.h
@@ -0,0 +1,105 @@
+// 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 REMOTING_TEST_APP_REMOTING_CONNECTED_CLIENT_FIXTURE_H_
+#define REMOTING_TEST_APP_REMOTING_CONNECTED_CLIENT_FIXTURE_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "remoting/test/remote_connection_observer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+class MessageLoopForIO;
+class RunLoop;
+class Timer;
+}
+
+namespace remoting {
+namespace test {
+
+struct RemoteApplicationDetails;
+class TestChromotingClient;
+
+// Allows for custom handling of ExtensionMessage messages.
+typedef base::Callback<void(const protocol::ExtensionMessage& message)>
+    HostMessageReceivedCallback;
+
+// Creates a connection to a remote host which is available for tests to use.
+// All callbacks must occur on the same thread the object was created on.
+class AppRemotingConnectedClientFixture
+    : public testing::TestWithParam<const char*>,
+      public RemoteConnectionObserver {
+ public:
+  AppRemotingConnectedClientFixture();
+  ~AppRemotingConnectedClientFixture() override;
+
+ protected:
+  // Sends the request to the host and waits for a reply up to |max_wait_time|.
+  // Returns true if we received a response within the maximum time limit.
+  bool VerifyResponseForSimpleHostMessage(
+      const std::string& message_request_title,
+      const std::string& message_response_title,
+      const std::string& message_payload,
+      const base::TimeDelta& max_wait_time);
+
+ private:
+  // testing::Test interface.
+  void SetUp() override;
+  void TearDown() override;
+
+  // RemoteConnectionObserver interface.
+  void ConnectionStateChanged(protocol::ConnectionToHost::State state,
+                              protocol::ErrorCode error_code) override;
+  void ConnectionReady(bool ready) override;
+  void HostMessageReceived(const protocol::ExtensionMessage& message) override;
+
+  // Starts a connection with the remote host.
+  void StartConnection();
+
+  // Sends client details to the host in order to complete the connection.
+  void SendClientConnectionDetailsToHost();
+
+  // Handles onWindowAdded messages from the host.
+  void HandleOnWindowAddedMessage(
+      const remoting::protocol::ExtensionMessage& message);
+
+  // Contains the details for the application being tested.
+  const RemoteApplicationDetails& application_details_;
+
+  // Called when an ExtensionMessage is received from the host.  Used to
+  // override default message handling.
+  HostMessageReceivedCallback host_message_received_callback_;
+
+  // Indicates whether the remote connection is ready to be used for testing.
+  // True when a chromoting connection to the remote host has been established
+  // and the main application window is visible.
+  bool connection_is_ready_for_tests_;
+
+  // Used to post tasks by |client_| and tests.
+  scoped_ptr<base::MessageLoopForIO> message_loop_;
+
+  // Used to run the thread's message loop.
+  scoped_ptr<base::RunLoop> run_loop_;
+
+  // Used for setting timeouts and delays.
+  scoped_ptr<base::Timer> timer_;
+
+  // Used to ensure RemoteConnectionObserver methods are called on the same
+  // thread.
+  base::ThreadChecker thread_checker_;
+
+  // Creates and manages the connection to the remote host.
+  scoped_ptr<TestChromotingClient> client_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppRemotingConnectedClientFixture);
+};
+
+}  // namespace test
+}  // namespace remoting
+
+#endif  // REMOTING_TEST_APP_REMOTING_CONNECTED_CLIENT_FIXTURE_H_
diff --git a/remoting/test/app_remoting_test_driver.cc b/remoting/test/app_remoting_test_driver.cc
index 3b2e9978..57f2947 100644
--- a/remoting/test/app_remoting_test_driver.cc
+++ b/remoting/test/app_remoting_test_driver.cc
@@ -19,6 +19,7 @@
 const char kHelpSwitchName[] = "help";
 const char kLoggingLevelSwitchName[] = "verbosity";
 const char kServiceEnvironmentSwitchName[] = "environment";
+const char kShowHostAvailabilitySwitchName[] = "show-host-availability";
 const char kSingleProcessTestsSwitchName[] = "single-process-tests";
 const char kUserNameSwitchName[] = "username";
 }
@@ -39,16 +40,17 @@
   bool use_plus = true;
   return base::StringPrintf(
       "https://accounts.google.com/o/oauth2/auth"
-          "?scope=%s"
-          "&redirect_uri=https://chromoting-oauth.talkgadget.google.com/"
-              "talkgadget/oauth/chrome-remote-desktop/dev"
-          "&response_type=code"
-          "&client_id=%s"
-          "&access_type=offline"
-          "&approval_prompt=force",
+      "?scope=%s"
+      "&redirect_uri=https://chromoting-oauth.talkgadget.google.com/"
+      "talkgadget/oauth/chrome-remote-desktop/dev"
+      "&response_type=code"
+      "&client_id=%s"
+      "&access_type=offline"
+      "&approval_prompt=force",
       net::EscapeUrlEncodedData(kAppRemotingAuthScopeValues, use_plus).c_str(),
-      net::EscapeUrlEncodedData(google_apis::GetOAuth2ClientID(
-          google_apis::CLIENT_REMOTING), use_plus).c_str());
+      net::EscapeUrlEncodedData(
+          google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING),
+          use_plus).c_str());
 }
 
 void PrintUsage() {
@@ -69,6 +71,10 @@
   printf("  %s: Specifies the service api to use (dev|test) [default: dev]\n",
          switches::kServiceEnvironmentSwitchName);
   printf(
+      "  %s: Retrieves and displays the connection status for all known "
+      "hosts, no tests will be run\n",
+      switches::kShowHostAvailabilitySwitchName);
+  printf(
       "  %s: Specifies the optional logging level of the tool (0-3)."
       " [default: off]\n",
       switches::kLoggingLevelSwitchName);
@@ -90,23 +96,24 @@
   printf("\n      has been revoked or expired.\n");
   printf("      Passing in the same auth code twice will result in an error\n");
 
-  printf("\nFollow these steps to produce an auth code:\n"
-         " - Open the Authorization URL link shown below in your browser\n"
-         " - Approve the requested permissions for the tool\n"
-         " - Copy the 'code' value in the redirected URL\n"
-         " - Run the tool and pass in copied auth code as a parameter\n");
+  printf(
+      "\nFollow these steps to produce an auth code:\n"
+      " - Open the Authorization URL link shown below in your browser\n"
+      " - Approve the requested permissions for the tool\n"
+      " - Copy the 'code' value in the redirected URL\n"
+      " - Run the tool and pass in copied auth code as a parameter\n");
 
   printf("\nAuthorization URL:\n");
   printf("%s\n", GetAuthorizationCodeUri().c_str());
 
   printf("\nRedirected URL Example:\n");
-  printf("https://chromoting-oauth.talkgadget.google.com/talkgadget/oauth/"
-         "chrome-remote-desktop/dev?code=4/AKtf...\n");
+  printf(
+      "https://chromoting-oauth.talkgadget.google.com/talkgadget/oauth/"
+      "chrome-remote-desktop/dev?code=4/AKtf...\n");
 
   printf("\nTool usage example with the newly created auth code:\n");
   printf("ar_test_driver --%s=example@gmail.com --%s=4/AKtf...\n\n",
-         switches::kUserNameSwitchName,
-         switches::kAuthCodeSwitchName);
+         switches::kUserNameSwitchName, switches::kAuthCodeSwitchName);
 }
 
 }  // namespace
@@ -139,8 +146,7 @@
     PrintUsage();
     PrintAuthCodeInfo();
     return base::LaunchUnitTestsSerially(
-        argc,
-        argv,
+        argc, argv,
         base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
   }
 
@@ -182,7 +188,7 @@
   // Update the logging verbosity level is user specified one.
   std::string verbosity_level;
   verbosity_level =
-        command_line->GetSwitchValueASCII(switches::kLoggingLevelSwitchName);
+      command_line->GetSwitchValueASCII(switches::kLoggingLevelSwitchName);
   if (!verbosity_level.empty()) {
     // Turn on logging for the test_driver and remoting components.
     // This switch is parsed during logging::InitLogging.
@@ -206,6 +212,13 @@
     return -1;
   }
 
+  if (command_line->HasSwitch(switches::kShowHostAvailabilitySwitchName)) {
+    // When this flag is specified, we will retrieve connection information
+    // for all known applications and report the status.  No tests will be run.
+    shared_data->ShowHostAvailability();
+    return 0;
+  }
+
   // Since we've successfully set up our shared_data object, we'll assign the
   // value to our global* and transfer ownership to the framework.
   remoting::test::AppRemotingSharedData = shared_data.release();
@@ -214,7 +227,6 @@
   // Because many tests may access the same remoting host(s), we need to run
   // the tests sequentially so they do not interfere with each other.
   return base::LaunchUnitTestsSerially(
-      argc,
-      argv,
+      argc, argv,
       base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
 }
diff --git a/remoting/test/app_remoting_test_driver_environment.cc b/remoting/test/app_remoting_test_driver_environment.cc
index 874e46a..9013c9a 100644
--- a/remoting/test/app_remoting_test_driver_environment.cc
+++ b/remoting/test/app_remoting_test_driver_environment.cc
@@ -4,11 +4,16 @@
 
 #include "remoting/test/app_remoting_test_driver_environment.h"
 
+#include <map>
+#include <string>
+#include <vector>
+
 #include "base/bind.h"
 #include "base/callback_forward.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
 #include "remoting/test/access_token_fetcher.h"
 #include "remoting/test/refresh_token_store.h"
 #include "remoting/test/remote_host_info.h"
@@ -28,6 +33,9 @@
       test_remote_host_info_fetcher_(nullptr) {
   DCHECK(!user_name_.empty());
   DCHECK(service_environment < kUnknownEnvironment);
+
+  PopulateApplicationNames();
+  PopulateApplicationDetailsMap();
 }
 
 AppRemotingTestDriverEnvironment::~AppRemotingTestDriverEnvironment() {
@@ -99,11 +107,9 @@
 
   base::RunLoop run_loop;
 
-  RemoteHostInfoCallback remote_host_info_fetch_callback =
-      base::Bind(&AppRemotingTestDriverEnvironment::OnRemoteHostInfoRetrieved,
-                 base::Unretained(this),
-                 run_loop.QuitClosure(),
-                 remote_host_info);
+  RemoteHostInfoCallback remote_host_info_fetch_callback = base::Bind(
+      &AppRemotingTestDriverEnvironment::OnRemoteHostInfoRetrieved,
+      base::Unretained(this), run_loop.QuitClosure(), remote_host_info);
 
   // If a unit test has set |test_remote_host_info_fetcher_| then we should use
   // it below.  Note that we do not want to destroy the test object at the end
@@ -117,9 +123,7 @@
   }
 
   remote_host_info_fetcher->RetrieveRemoteHostInfo(
-      application_id,
-      access_token_,
-      service_environment_,
+      application_id, access_token_, service_environment_,
       remote_host_info_fetch_callback);
 
   run_loop.Run();
@@ -127,6 +131,50 @@
   return remote_host_info->IsReadyForConnection();
 }
 
+void AppRemotingTestDriverEnvironment::ShowHostAvailability() {
+  const char kHostAvailabilityFormatString[] = "%-25s%-35s%-10s";
+  std::vector<std::string>::const_iterator it = application_names_.begin();
+
+  LOG(INFO) << base::StringPrintf(kHostAvailabilityFormatString,
+                                  "Application Name", "Application ID",
+                                  "Status");
+
+  while (it != application_names_.end()) {
+    std::string application_name = *it;
+
+    const RemoteApplicationDetails& application_details =
+        GetDetailsFromAppName(application_name);
+
+    RemoteHostInfo remote_host_info;
+    GetRemoteHostInfoForApplicationId(application_details.application_id,
+                                      &remote_host_info);
+
+    std::string status;
+    RemoteHostStatus remote_host_status = remote_host_info.remote_host_status;
+    if (remote_host_status == kRemoteHostStatusReady) {
+      status = "Ready :)";
+    } else if (remote_host_status == kRemoteHostStatusPending) {
+      status = "Pending :|";
+    } else {
+      status = "Unknown :(";
+    }
+
+    LOG(INFO) << base::StringPrintf(
+        kHostAvailabilityFormatString, application_name.c_str(),
+        application_details.application_id.c_str(), status.c_str());
+
+    ++it;
+  }
+}
+
+const RemoteApplicationDetails&
+AppRemotingTestDriverEnvironment::GetDetailsFromAppName(
+    const std::string& application_name) {
+  DCHECK_GT(application_details_map_.count(application_name), 0UL);
+
+  return application_details_map_.at(application_name);
+}
+
 void AppRemotingTestDriverEnvironment::SetAccessTokenFetcherForTest(
     AccessTokenFetcher* access_token_fetcher) {
   DCHECK(access_token_fetcher);
@@ -164,8 +212,7 @@
 
   AccessTokenCallback access_token_callback =
       base::Bind(&AppRemotingTestDriverEnvironment::OnAccessTokenRetrieved,
-                 base::Unretained(this),
-                 run_loop.QuitClosure());
+                 base::Unretained(this), run_loop.QuitClosure());
 
   // If a unit test has set |test_access_token_fetcher_| then we should use it
   // below.  Note that we do not want to destroy the test object at the end of
@@ -180,15 +227,13 @@
   if (!auth_code.empty()) {
     // If the user passed in an authcode, then use it to retrieve an
     // updated access/refresh token.
-    access_token_fetcher->GetAccessTokenFromAuthCode(
-        auth_code,
-        access_token_callback);
+    access_token_fetcher->GetAccessTokenFromAuthCode(auth_code,
+                                                     access_token_callback);
   } else {
     DCHECK(!refresh_token_.empty());
 
-    access_token_fetcher->GetAccessTokenFromRefreshToken(
-        refresh_token_,
-        access_token_callback);
+    access_token_fetcher->GetAccessTokenFromRefreshToken(refresh_token_,
+                                                         access_token_callback);
   }
 
   run_loop.Run();
@@ -243,9 +288,7 @@
     const RemoteHostInfo& retrieved_remote_host_info) {
   DCHECK(remote_host_info);
 
-  if (retrieved_remote_host_info.IsReadyForConnection()) {
-    *remote_host_info = retrieved_remote_host_info;
-  }
+  *remote_host_info = retrieved_remote_host_info;
 
   done_closure.Run();
 }
diff --git a/remoting/test/app_remoting_test_driver_environment.h b/remoting/test/app_remoting_test_driver_environment.h
index c814f0b..eb18a86 100644
--- a/remoting/test/app_remoting_test_driver_environment.h
+++ b/remoting/test/app_remoting_test_driver_environment.h
@@ -5,9 +5,12 @@
 #ifndef REMOTING_TEST_APP_REMOTING_TEST_DRIVER_ENVIRONMENT_H_
 #define REMOTING_TEST_APP_REMOTING_TEST_DRIVER_ENVIRONMENT_H_
 
+#include <map>
 #include <string>
+#include <vector>
 
 #include "base/memory/scoped_ptr.h"
+#include "remoting/test/remote_application_details.h"
 #include "remoting/test/remote_host_info_fetcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -23,9 +26,8 @@
 // access tokens and retrieving remote host connection information.
 class AppRemotingTestDriverEnvironment : public testing::Environment {
  public:
-  AppRemotingTestDriverEnvironment(
-      const std::string& user_name,
-      ServiceEnvironment service_environment);
+  AppRemotingTestDriverEnvironment(const std::string& user_name,
+                                   ServiceEnvironment service_environment);
   ~AppRemotingTestDriverEnvironment() override;
 
   // Returns false if a valid access token cannot be retrieved.
@@ -37,9 +39,16 @@
 
   // Synchronously request remote host information for |application_id|.
   // Returns true if the request was successful and |remote_host_info| is valid.
-  bool GetRemoteHostInfoForApplicationId(
-      const std::string& application_id,
-      RemoteHostInfo* remote_host_info);
+  bool GetRemoteHostInfoForApplicationId(const std::string& application_id,
+                                         RemoteHostInfo* remote_host_info);
+
+  // Retrieves connection information for all known applications and displays
+  // their availability to STDOUT.
+  void ShowHostAvailability();
+
+  // Provides the RemoteApplicationDetails for the specified |application_name|.
+  const RemoteApplicationDetails& GetDetailsFromAppName(
+      const std::string& application_name);
 
   // Used to set fake/mock objects for AppRemotingTestDriverEnvironment tests.
   void SetAccessTokenFetcherForTest(AccessTokenFetcher* access_token_fetcher);
@@ -59,18 +68,25 @@
 
   // Called after the access token fetcher completes.
   // The tokens will be empty on failure.
-  void OnAccessTokenRetrieved(
-      base::Closure done_closure,
-      const std::string& access_token,
-      const std::string& refresh_token);
+  void OnAccessTokenRetrieved(base::Closure done_closure,
+                              const std::string& access_token,
+                              const std::string& refresh_token);
 
   // Called after the remote host info fetcher completes.
-  // |remote_host_info| is not modified on failure.
+  // |remote_host_info| is modified on failure.
   void OnRemoteHostInfoRetrieved(
       base::Closure done_closure,
       RemoteHostInfo* remote_host_info,
       const RemoteHostInfo& retrieved_remote_host_info);
 
+  // Populates |application_names_| with the names of the supported remote
+  // applications.
+  void PopulateApplicationNames();
+
+  // Populates |application_details_map_| with the RemoteApplicationDetails for
+  // all supported remote applications.
+  void PopulateApplicationDetailsMap();
+
   // Used for authenticating with the app remoting service API.
   std::string access_token_;
 
@@ -92,6 +108,12 @@
   // RemoteHostInfoFetcher used by TestDriverEnvironment tests.
   remoting::test::RemoteHostInfoFetcher* test_remote_host_info_fetcher_;
 
+  // Contains the names of all supported remote applications.
+  std::vector<std::string> application_names_;
+
+  // Contains RemoteApplicationDetails for all supported remote applications.
+  std::map<std::string, RemoteApplicationDetails> application_details_map_;
+
   DISALLOW_COPY_AND_ASSIGN(AppRemotingTestDriverEnvironment);
 };
 
diff --git a/remoting/test/app_remoting_test_driver_environment_app_details.cc b/remoting/test/app_remoting_test_driver_environment_app_details.cc
new file mode 100644
index 0000000..58f12f7
--- /dev/null
+++ b/remoting/test/app_remoting_test_driver_environment_app_details.cc
@@ -0,0 +1,21 @@
+// 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 "remoting/test/app_remoting_test_driver_environment.h"
+
+namespace remoting {
+namespace test {
+
+void AppRemotingTestDriverEnvironment::PopulateApplicationNames() {
+  // The public version of this method is stubbed out as there isn't a publicly
+  // available test application.
+}
+
+void AppRemotingTestDriverEnvironment::PopulateApplicationDetailsMap() {
+  // The public version of this method is stubbed out as there isn't a publicly
+  // available test application.
+}
+
+}  // namespace test
+}  // namespace remoting
diff --git a/remoting/test/app_remoting_test_driver_environment_unittest.cc b/remoting/test/app_remoting_test_driver_environment_unittest.cc
index 97638bf..8fd9d49 100644
--- a/remoting/test/app_remoting_test_driver_environment_unittest.cc
+++ b/remoting/test/app_remoting_test_driver_environment_unittest.cc
@@ -26,15 +26,13 @@
 // file system dependencies when testing the TestDriverEnvironment.
 class FakeRefreshTokenStore : public RefreshTokenStore {
  public:
-  FakeRefreshTokenStore() :
-      refresh_token_value(kRefreshTokenValue),
-      refresh_token_write_succeeded(true),
-      refresh_token_write_attempted(false) {}
+  FakeRefreshTokenStore()
+      : refresh_token_value(kRefreshTokenValue),
+        refresh_token_write_succeeded(true),
+        refresh_token_write_attempted(false) {}
   ~FakeRefreshTokenStore() override {}
 
-  std::string FetchRefreshToken() override {
-    return refresh_token_value;
-  };
+  std::string FetchRefreshToken() override { return refresh_token_value; };
 
   bool StoreRefreshToken(const std::string& refresh_token) override {
     // Record the information passed to us to write.
@@ -70,9 +68,8 @@
   EXPECT_CALL(mock_access_token_fetcher, GetAccessTokenFromRefreshToken(_, _))
       .Times(0);
 
-  AppRemotingTestDriverEnvironment environment_object(
-      kUserNameValue,
-      kDeveloperEnvironment);
+  AppRemotingTestDriverEnvironment environment_object(kUserNameValue,
+                                                      kDeveloperEnvironment);
 
   environment_object.SetAccessTokenFetcherForTest(&mock_access_token_fetcher);
 
@@ -83,10 +80,12 @@
   EXPECT_TRUE(init_result);
   EXPECT_TRUE(fake_token_store.refresh_token_write_attempted);
   EXPECT_EQ(fake_token_store.refresh_token_value_written.compare(
-      kFakeAccessTokenFetcherRefreshTokenValue), 0);
+                kFakeAccessTokenFetcherRefreshTokenValue),
+            0);
   EXPECT_EQ(environment_object.user_name().compare(kUserNameValue), 0);
   EXPECT_EQ(environment_object.access_token().compare(
-      kFakeAccessTokenFetcherAccessTokenValue), 0);
+                kFakeAccessTokenFetcherAccessTokenValue),
+            0);
 
   // Attempt to init again, we should not see any additional calls or errors.
   init_result = environment_object.Initialize(kAuthCodeValue);
@@ -112,9 +111,8 @@
   EXPECT_CALL(mock_access_token_fetcher, GetAccessTokenFromRefreshToken(_, _))
       .Times(0);
 
-  AppRemotingTestDriverEnvironment environment_object(
-      kUserNameValue,
-      kDeveloperEnvironment);
+  AppRemotingTestDriverEnvironment environment_object(kUserNameValue,
+                                                      kDeveloperEnvironment);
 
   environment_object.SetAccessTokenFetcherForTest(&mock_access_token_fetcher);
 
@@ -140,9 +138,8 @@
   EXPECT_CALL(mock_access_token_fetcher, GetAccessTokenFromAuthCode(_, _))
       .Times(0);
 
-  AppRemotingTestDriverEnvironment environment_object(
-      kUserNameValue,
-      kDeveloperEnvironment);
+  AppRemotingTestDriverEnvironment environment_object(kUserNameValue,
+                                                      kDeveloperEnvironment);
 
   environment_object.SetAccessTokenFetcherForTest(&mock_access_token_fetcher);
 
@@ -160,7 +157,8 @@
   // Verify the object was initialized correctly.
   EXPECT_EQ(environment_object.user_name().compare(kUserNameValue), 0);
   EXPECT_EQ(environment_object.access_token().compare(
-      kFakeAccessTokenFetcherAccessTokenValue), 0);
+                kFakeAccessTokenFetcherAccessTokenValue),
+            0);
 
   // Attempt to init again, we should not see any additional calls or errors.
   init_result = environment_object.Initialize(std::string());
@@ -187,9 +185,8 @@
   EXPECT_CALL(mock_access_token_fetcher, GetAccessTokenFromAuthCode(_, _))
       .Times(0);
 
-  AppRemotingTestDriverEnvironment environment_object(
-      kUserNameValue,
-      kDeveloperEnvironment);
+  AppRemotingTestDriverEnvironment environment_object(kUserNameValue,
+                                                      kDeveloperEnvironment);
 
   environment_object.SetAccessTokenFetcherForTest(&mock_access_token_fetcher);
 
@@ -218,9 +215,8 @@
   EXPECT_CALL(mock_access_token_fetcher, GetAccessTokenFromRefreshToken(_, _))
       .Times(0);
 
-  AppRemotingTestDriverEnvironment environment_object(
-      kUserNameValue,
-      kDeveloperEnvironment);
+  AppRemotingTestDriverEnvironment environment_object(kUserNameValue,
+                                                      kDeveloperEnvironment);
 
   environment_object.SetAccessTokenFetcherForTest(&mock_access_token_fetcher);
 
@@ -252,9 +248,8 @@
   EXPECT_CALL(mock_access_token_fetcher, GetAccessTokenFromRefreshToken(_, _))
       .Times(0);
 
-  AppRemotingTestDriverEnvironment environment_object(
-      kUserNameValue,
-      kDeveloperEnvironment);
+  AppRemotingTestDriverEnvironment environment_object(kUserNameValue,
+                                                      kDeveloperEnvironment);
 
   environment_object.SetAccessTokenFetcherForTest(&mock_access_token_fetcher);
 
@@ -284,13 +279,12 @@
     EXPECT_CALL(mock_access_token_fetcher, GetAccessTokenFromAuthCode(_, _))
         .Times(1);
 
-    EXPECT_CALL(mock_access_token_fetcher,
-                GetAccessTokenFromRefreshToken(_, _)).Times(1);
+    EXPECT_CALL(mock_access_token_fetcher, GetAccessTokenFromRefreshToken(_, _))
+        .Times(1);
   }
 
-  AppRemotingTestDriverEnvironment environment_object(
-      kUserNameValue,
-      kDeveloperEnvironment);
+  AppRemotingTestDriverEnvironment environment_object(kUserNameValue,
+                                                      kDeveloperEnvironment);
 
   environment_object.SetAccessTokenFetcherForTest(&mock_access_token_fetcher);
 
@@ -301,10 +295,12 @@
   EXPECT_TRUE(init_result);
   EXPECT_TRUE(fake_token_store.refresh_token_write_attempted);
   EXPECT_EQ(fake_token_store.refresh_token_value_written.compare(
-      kFakeAccessTokenFetcherRefreshTokenValue), 0);
+                kFakeAccessTokenFetcherRefreshTokenValue),
+            0);
   EXPECT_EQ(environment_object.user_name().compare(kUserNameValue), 0);
   EXPECT_EQ(environment_object.access_token().compare(
-      kFakeAccessTokenFetcherAccessTokenValue), 0);
+                kFakeAccessTokenFetcherAccessTokenValue),
+            0);
 
   // Attempt to init again, we should not see any additional calls or errors.
   bool refresh_result = environment_object.RefreshAccessToken();
@@ -331,13 +327,12 @@
         .Times(1);
 
     // Mock is set up for this call to fail.
-    EXPECT_CALL(mock_access_token_fetcher,
-                GetAccessTokenFromRefreshToken(_, _)).Times(1);
+    EXPECT_CALL(mock_access_token_fetcher, GetAccessTokenFromRefreshToken(_, _))
+        .Times(1);
   }
 
-  AppRemotingTestDriverEnvironment environment_object(
-      kUserNameValue,
-      kDeveloperEnvironment);
+  AppRemotingTestDriverEnvironment environment_object(kUserNameValue,
+                                                      kDeveloperEnvironment);
 
   environment_object.SetAccessTokenFetcherForTest(&mock_access_token_fetcher);
 
@@ -348,10 +343,12 @@
   EXPECT_TRUE(init_result);
   EXPECT_TRUE(fake_token_store.refresh_token_write_attempted);
   EXPECT_EQ(fake_token_store.refresh_token_value_written.compare(
-      kFakeAccessTokenFetcherRefreshTokenValue), 0);
+                kFakeAccessTokenFetcherRefreshTokenValue),
+            0);
   EXPECT_EQ(environment_object.user_name().compare(kUserNameValue), 0);
   EXPECT_EQ(environment_object.access_token().compare(
-      kFakeAccessTokenFetcherAccessTokenValue), 0);
+                kFakeAccessTokenFetcherAccessTokenValue),
+            0);
 
   fake_access_token_fetcher->set_fail_access_token_from_refresh_token(true);
 
diff --git a/remoting/test/fake_access_token_fetcher.cc b/remoting/test/fake_access_token_fetcher.cc
index fb479de..237569ef 100644
--- a/remoting/test/fake_access_token_fetcher.cc
+++ b/remoting/test/fake_access_token_fetcher.cc
@@ -7,12 +7,13 @@
 namespace remoting {
 namespace test {
 
-FakeAccessTokenFetcher::FakeAccessTokenFetcher() :
-    fail_access_token_from_auth_code_(false),
-    fail_access_token_from_refresh_token_(false) {}
+FakeAccessTokenFetcher::FakeAccessTokenFetcher()
+    : fail_access_token_from_auth_code_(false),
+      fail_access_token_from_refresh_token_(false) {
+}
 
-FakeAccessTokenFetcher::~FakeAccessTokenFetcher() {}
-
+FakeAccessTokenFetcher::~FakeAccessTokenFetcher() {
+}
 
 void FakeAccessTokenFetcher::GetAccessTokenFromAuthCode(
     const std::string& auth_code,
diff --git a/remoting/test/fake_access_token_fetcher.h b/remoting/test/fake_access_token_fetcher.h
index 173469b..b2d74de 100644
--- a/remoting/test/fake_access_token_fetcher.h
+++ b/remoting/test/fake_access_token_fetcher.h
@@ -24,9 +24,8 @@
   ~FakeAccessTokenFetcher() override;
 
   // AccessTokenFetcher interface.
-  void GetAccessTokenFromAuthCode(
-    const std::string& auth_code,
-    const AccessTokenCallback& callback) override;
+  void GetAccessTokenFromAuthCode(const std::string& auth_code,
+                                  const AccessTokenCallback& callback) override;
   void GetAccessTokenFromRefreshToken(
       const std::string& refresh_token,
       const AccessTokenCallback& callback) override;
diff --git a/remoting/test/fake_remote_host_info_fetcher.cc b/remoting/test/fake_remote_host_info_fetcher.cc
index 8270984..c97f2e4 100644
--- a/remoting/test/fake_remote_host_info_fetcher.cc
+++ b/remoting/test/fake_remote_host_info_fetcher.cc
@@ -7,10 +7,12 @@
 namespace remoting {
 namespace test {
 
-FakeRemoteHostInfoFetcher::FakeRemoteHostInfoFetcher() :
-    fail_retrieve_remote_host_info_(false) {}
+FakeRemoteHostInfoFetcher::FakeRemoteHostInfoFetcher()
+    : fail_retrieve_remote_host_info_(false) {
+}
 
-FakeRemoteHostInfoFetcher::~FakeRemoteHostInfoFetcher() {}
+FakeRemoteHostInfoFetcher::~FakeRemoteHostInfoFetcher() {
+}
 
 bool FakeRemoteHostInfoFetcher::RetrieveRemoteHostInfo(
     const std::string& application_id,
diff --git a/remoting/test/fake_remote_host_info_fetcher.h b/remoting/test/fake_remote_host_info_fetcher.h
index d82860f9..af8b0b4 100644
--- a/remoting/test/fake_remote_host_info_fetcher.h
+++ b/remoting/test/fake_remote_host_info_fetcher.h
@@ -21,11 +21,10 @@
   ~FakeRemoteHostInfoFetcher() override;
 
   // RemoteHostInfoFetcher interface.
-  bool RetrieveRemoteHostInfo(
-      const std::string& application_id,
-      const std::string& access_token,
-      ServiceEnvironment service_environment,
-      const RemoteHostInfoCallback& callback) override;
+  bool RetrieveRemoteHostInfo(const std::string& application_id,
+                              const std::string& access_token,
+                              ServiceEnvironment service_environment,
+                              const RemoteHostInfoCallback& callback) override;
 
   void set_fail_retrieve_remote_host_info(bool fail) {
     fail_retrieve_remote_host_info_ = fail;
diff --git a/remoting/test/mock_access_token_fetcher.cc b/remoting/test/mock_access_token_fetcher.cc
index 694e0923..d5d52019 100644
--- a/remoting/test/mock_access_token_fetcher.cc
+++ b/remoting/test/mock_access_token_fetcher.cc
@@ -20,12 +20,13 @@
     scoped_ptr<AccessTokenFetcher> fetcher) {
   internal_access_token_fetcher_ = fetcher.Pass();
 
-  ON_CALL(*this, GetAccessTokenFromAuthCode(_, _)).WillByDefault(
-      Invoke(internal_access_token_fetcher_.get(),
-             &AccessTokenFetcher::GetAccessTokenFromAuthCode));
-  ON_CALL(*this, GetAccessTokenFromRefreshToken(_, _)).WillByDefault(
-      Invoke(internal_access_token_fetcher_.get(),
-             &AccessTokenFetcher::GetAccessTokenFromRefreshToken));
+  ON_CALL(*this, GetAccessTokenFromAuthCode(_, _))
+      .WillByDefault(Invoke(internal_access_token_fetcher_.get(),
+                            &AccessTokenFetcher::GetAccessTokenFromAuthCode));
+  ON_CALL(*this, GetAccessTokenFromRefreshToken(_, _))
+      .WillByDefault(
+          Invoke(internal_access_token_fetcher_.get(),
+                 &AccessTokenFetcher::GetAccessTokenFromRefreshToken));
 }
 
 }  // namespace test
diff --git a/remoting/test/refresh_token_store.cc b/remoting/test/refresh_token_store.cc
index dfbf6dde..e191f5b 100644
--- a/remoting/test/refresh_token_store.cc
+++ b/remoting/test/refresh_token_store.cc
@@ -24,8 +24,8 @@
   }
 
   refresh_token_dir_path = refresh_token_dir_path.Append(kRemotingFolder);
-  refresh_token_dir_path = refresh_token_dir_path.Append(
-      kRefreshTokenStoreFolder);
+  refresh_token_dir_path =
+      refresh_token_dir_path.Append(kRefreshTokenStoreFolder);
 
   // We call AppendASCII here our user_name is a std::string but wide strings
   // are used on WIN platforms.  ApendASCII will convert our std::string into
@@ -58,10 +58,12 @@
   DISALLOW_COPY_AND_ASSIGN(RefreshTokenStoreOnDisk);
 };
 
-RefreshTokenStoreOnDisk::RefreshTokenStoreOnDisk(const std::string user_name) :
-    user_name_(user_name) {}
+RefreshTokenStoreOnDisk::RefreshTokenStoreOnDisk(const std::string user_name)
+    : user_name_(user_name) {
+}
 
-RefreshTokenStoreOnDisk::~RefreshTokenStoreOnDisk() {}
+RefreshTokenStoreOnDisk::~RefreshTokenStoreOnDisk() {
+}
 
 std::string RefreshTokenStoreOnDisk::FetchRefreshToken() {
   base::FilePath token_dir_path(GetRefreshTokenDirPath(user_name_));
@@ -131,8 +133,8 @@
 
 scoped_ptr<RefreshTokenStore> RefreshTokenStore::OnDisk(
     const std::string& user_name) {
-  return make_scoped_ptr<RefreshTokenStore>(new RefreshTokenStoreOnDisk(
-      user_name));
+  return make_scoped_ptr<RefreshTokenStore>(
+      new RefreshTokenStoreOnDisk(user_name));
 }
 
 }  // namespace test
diff --git a/remoting/test/remote_application_details.h b/remoting/test/remote_application_details.h
new file mode 100644
index 0000000..711b210
--- /dev/null
+++ b/remoting/test/remote_application_details.h
@@ -0,0 +1,26 @@
+// 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 REMOTING_TEST_REMOTE_APPLICATION_DETAILS_H_
+#define REMOTING_TEST_REMOTE_APPLICATION_DETAILS_H_
+
+#include <string>
+
+namespace remoting {
+namespace test {
+
+// Container for application details used for testing.
+struct RemoteApplicationDetails {
+  RemoteApplicationDetails(const std::string& app_id,
+                           const std::string& window_title)
+      : application_id(app_id), main_window_title(window_title) {}
+
+  std::string application_id;
+  std::string main_window_title;
+};
+
+}  // namespace test
+}  // namespace remoting
+
+#endif  // REMOTING_TEST_REMOTE_APPLICATION_DETAILS_H_
diff --git a/remoting/test/remote_host_info.cc b/remoting/test/remote_host_info.cc
index 038374559..6288390 100644
--- a/remoting/test/remote_host_info.cc
+++ b/remoting/test/remote_host_info.cc
@@ -9,10 +9,12 @@
 namespace remoting {
 namespace test {
 
-RemoteHostInfo::RemoteHostInfo() :
-    remote_host_status(kRemoteHostStatusUnknown) {}
+RemoteHostInfo::RemoteHostInfo()
+    : remote_host_status(kRemoteHostStatusUnknown) {
+}
 
-RemoteHostInfo::~RemoteHostInfo() {}
+RemoteHostInfo::~RemoteHostInfo() {
+}
 
 bool RemoteHostInfo::IsReadyForConnection() const {
   return (remote_host_status == kRemoteHostStatusReady);
diff --git a/remoting/test/remote_host_info.h b/remoting/test/remote_host_info.h
index fe9aa5cf..d74269f0 100644
--- a/remoting/test/remote_host_info.h
+++ b/remoting/test/remote_host_info.h
@@ -11,9 +11,9 @@
 namespace test {
 
 enum RemoteHostStatus {
-    kRemoteHostStatusReady,
-    kRemoteHostStatusPending,
-    kRemoteHostStatusUnknown
+  kRemoteHostStatusReady,
+  kRemoteHostStatusPending,
+  kRemoteHostStatusUnknown
 };
 
 // Holds the information needed to establish a connection with a remote host.
diff --git a/remoting/test/remote_host_info_fetcher.cc b/remoting/test/remote_host_info_fetcher.cc
index e7dc0958..f2b0e8fd 100644
--- a/remoting/test/remote_host_info_fetcher.cc
+++ b/remoting/test/remote_host_info_fetcher.cc
@@ -25,9 +25,11 @@
 namespace remoting {
 namespace test {
 
-RemoteHostInfoFetcher::RemoteHostInfoFetcher() {}
+RemoteHostInfoFetcher::RemoteHostInfoFetcher() {
+}
 
-RemoteHostInfoFetcher::~RemoteHostInfoFetcher() {}
+RemoteHostInfoFetcher::~RemoteHostInfoFetcher() {
+}
 
 bool RemoteHostInfoFetcher::RetrieveRemoteHostInfo(
     const std::string& application_id,
@@ -62,14 +64,13 @@
 
   remote_host_info_callback_ = callback;
 
-  scoped_refptr<remoting::URLRequestContextGetter> request_context_getter;
-  request_context_getter = new remoting::URLRequestContextGetter(
+  request_context_getter_ = new remoting::URLRequestContextGetter(
       base::ThreadTaskRunnerHandle::Get(),   // network_runner
       base::ThreadTaskRunnerHandle::Get());  // file_runner
 
   request_.reset(
       net::URLFetcher::Create(GURL(service_url), net::URLFetcher::POST, this));
-  request_->SetRequestContext(request_context_getter.get());
+  request_->SetRequestContext(request_context_getter_.get());
   request_->AddExtraRequestHeader("Authorization: OAuth " + access_token);
   request_->AddExtraRequestHeader(kRequestTestOrigin);
   request_->SetUploadData("application/json; charset=UTF-8", "{}");
diff --git a/remoting/test/remote_host_info_fetcher.h b/remoting/test/remote_host_info_fetcher.h
index a9fd742..83e8b320 100644
--- a/remoting/test/remote_host_info_fetcher.h
+++ b/remoting/test/remote_host_info_fetcher.h
@@ -36,9 +36,9 @@
 // Note: When adding new environments, add them before kUnknownEnvironment as
 //       the last entry is used for bounds checking.
 enum ServiceEnvironment {
-    kDeveloperEnvironment,
-    kTestingEnvironment,
-    kUnknownEnvironment
+  kDeveloperEnvironment,
+  kTestingEnvironment,
+  kUnknownEnvironment
 };
 
 // Supplied by the client for each remote host info request and returns a valid,
@@ -58,11 +58,10 @@
 
   // Makes a service call to retrieve the details for a remote host.  The
   // callback will be called once the HTTP request has completed.
-  virtual bool RetrieveRemoteHostInfo(
-      const std::string& application_id,
-      const std::string& access_token,
-      ServiceEnvironment service_environment,
-      const RemoteHostInfoCallback& callback);
+  virtual bool RetrieveRemoteHostInfo(const std::string& application_id,
+                                      const std::string& access_token,
+                                      ServiceEnvironment service_environment,
+                                      const RemoteHostInfoCallback& callback);
 
  private:
   // net::URLFetcherDelegate interface.
@@ -71,6 +70,9 @@
   // Holds the URLFetcher for the RemoteHostInfo request.
   scoped_ptr<net::URLFetcher> request_;
 
+  // Provides application-specific context for the network request.
+  scoped_refptr<remoting::URLRequestContextGetter> request_context_getter_;
+
   // Caller-supplied callback used to return remote host info on success.
   RemoteHostInfoCallback remote_host_info_callback_;
 
diff --git a/remoting/test/remote_host_info_fetcher_unittest.cc b/remoting/test/remote_host_info_fetcher_unittest.cc
index e04f2e3..048f246 100644
--- a/remoting/test/remote_host_info_fetcher_unittest.cc
+++ b/remoting/test/remote_host_info_fetcher_unittest.cc
@@ -43,25 +43,23 @@
 // necessary, for use by the RemoteHostInfoFetcher.
 class RemoteHostInfoFetcherTest : public ::testing::Test {
  public:
-  RemoteHostInfoFetcherTest() :
-    url_fetcher_factory_(nullptr) {}
+  RemoteHostInfoFetcherTest() : url_fetcher_factory_(nullptr) {}
   ~RemoteHostInfoFetcherTest() override {}
 
   // Used as a RemoteHostInfoCallback for testing.
   void OnRemoteHostInfoRetrieved(
-    base::Closure done_closure,
-    const RemoteHostInfo& retrieved_remote_host_info);
+      base::Closure done_closure,
+      const RemoteHostInfo& retrieved_remote_host_info);
 
  protected:
   // testing::Test interface.
   void SetUp() override;
 
   // Sets the HTTP status and data returned for a specified URL.
-  void SetFakeResponse(
-      const GURL& url,
-      const std::string& data,
-      net::HttpStatusCode code,
-      net::URLRequestStatus::Status status);
+  void SetFakeResponse(const GURL& url,
+                       const std::string& data,
+                       net::HttpStatusCode code,
+                       net::URLRequestStatus::Status status);
 
   // Used for result verification.
   RemoteHostInfo remote_host_info_;
@@ -92,13 +90,11 @@
     message_loop_.reset(new base::MessageLoopForIO);
   }
 
-  dev_service_environment_url_ = base::StringPrintf(
-      kDevServiceEnvironmentUrlFormat,
-      kTestApplicationId);
+  dev_service_environment_url_ =
+      base::StringPrintf(kDevServiceEnvironmentUrlFormat, kTestApplicationId);
 
-  test_service_environment_url_ = base::StringPrintf(
-      kTestServiceEnvironmentUrlFormat,
-      kTestApplicationId);
+  test_service_environment_url_ =
+      base::StringPrintf(kTestServiceEnvironmentUrlFormat, kTestApplicationId);
 }
 
 void RemoteHostInfoFetcherTest::SetFakeResponse(
@@ -110,29 +106,22 @@
 }
 
 TEST_F(RemoteHostInfoFetcherTest, RetrieveRemoteHostInfoFromDev) {
-  SetFakeResponse(
-      GURL(dev_service_environment_url_),
-      kRemoteHostInfoReadyResponse,
-      net::HTTP_OK,
-      net::URLRequestStatus::SUCCESS);
+  SetFakeResponse(GURL(dev_service_environment_url_),
+                  kRemoteHostInfoReadyResponse, net::HTTP_OK,
+                  net::URLRequestStatus::SUCCESS);
 
-  SetFakeResponse(
-      GURL(test_service_environment_url_),
-      kRemoteHostInfoEmptyResponse,
-      net::HTTP_NOT_FOUND,
-      net::URLRequestStatus::FAILED);
+  SetFakeResponse(GURL(test_service_environment_url_),
+                  kRemoteHostInfoEmptyResponse, net::HTTP_NOT_FOUND,
+                  net::URLRequestStatus::FAILED);
 
   base::RunLoop run_loop;
   RemoteHostInfoCallback remote_host_info_callback =
       base::Bind(&RemoteHostInfoFetcherTest::OnRemoteHostInfoRetrieved,
-                 base::Unretained(this),
-                 run_loop.QuitClosure());
+                 base::Unretained(this), run_loop.QuitClosure());
 
   RemoteHostInfoFetcher remote_host_info_fetcher;
   bool request_started = remote_host_info_fetcher.RetrieveRemoteHostInfo(
-      kTestApplicationId,
-      kAccessTokenValue,
-      kDeveloperEnvironment,
+      kTestApplicationId, kAccessTokenValue, kDeveloperEnvironment,
       remote_host_info_callback);
 
   run_loop.Run();
@@ -147,29 +136,22 @@
 }
 
 TEST_F(RemoteHostInfoFetcherTest, RetrieveRemoteHostInfoFromTest) {
-  SetFakeResponse(
-      GURL(test_service_environment_url_),
-      kRemoteHostInfoReadyResponse,
-      net::HTTP_OK,
-      net::URLRequestStatus::SUCCESS);
+  SetFakeResponse(GURL(test_service_environment_url_),
+                  kRemoteHostInfoReadyResponse, net::HTTP_OK,
+                  net::URLRequestStatus::SUCCESS);
 
-  SetFakeResponse(
-      GURL(dev_service_environment_url_),
-      kRemoteHostInfoEmptyResponse,
-      net::HTTP_NOT_FOUND,
-      net::URLRequestStatus::FAILED);
+  SetFakeResponse(GURL(dev_service_environment_url_),
+                  kRemoteHostInfoEmptyResponse, net::HTTP_NOT_FOUND,
+                  net::URLRequestStatus::FAILED);
 
   base::RunLoop run_loop;
   RemoteHostInfoCallback remote_host_info_callback =
       base::Bind(&RemoteHostInfoFetcherTest::OnRemoteHostInfoRetrieved,
-                 base::Unretained(this),
-                 run_loop.QuitClosure());
+                 base::Unretained(this), run_loop.QuitClosure());
 
   RemoteHostInfoFetcher remote_host_info_fetcher;
   bool request_started = remote_host_info_fetcher.RetrieveRemoteHostInfo(
-      kTestApplicationId,
-      kAccessTokenValue,
-      kTestingEnvironment,
+      kTestApplicationId, kAccessTokenValue, kTestingEnvironment,
       remote_host_info_callback);
 
   run_loop.Run();
@@ -187,37 +169,29 @@
   base::RunLoop run_loop;
   RemoteHostInfoCallback remote_host_info_callback =
       base::Bind(&RemoteHostInfoFetcherTest::OnRemoteHostInfoRetrieved,
-                 base::Unretained(this),
-                 run_loop.QuitClosure());
+                 base::Unretained(this), run_loop.QuitClosure());
 
   RemoteHostInfoFetcher remote_host_info_fetcher;
   bool request_started = remote_host_info_fetcher.RetrieveRemoteHostInfo(
-      kTestApplicationId,
-      kAccessTokenValue,
-      kUnknownEnvironment,
+      kTestApplicationId, kAccessTokenValue, kUnknownEnvironment,
       remote_host_info_callback);
 
   EXPECT_FALSE(request_started);
 }
 
 TEST_F(RemoteHostInfoFetcherTest, RetrieveRemoteHostInfoNetworkError) {
-  SetFakeResponse(
-      GURL(dev_service_environment_url_),
-      kRemoteHostInfoReadyResponse,
-      net::HTTP_NOT_FOUND,
-      net::URLRequestStatus::FAILED);
+  SetFakeResponse(GURL(dev_service_environment_url_),
+                  kRemoteHostInfoReadyResponse, net::HTTP_NOT_FOUND,
+                  net::URLRequestStatus::FAILED);
 
   base::RunLoop run_loop;
   RemoteHostInfoCallback remote_host_info_callback =
       base::Bind(&RemoteHostInfoFetcherTest::OnRemoteHostInfoRetrieved,
-                 base::Unretained(this),
-                 run_loop.QuitClosure());
+                 base::Unretained(this), run_loop.QuitClosure());
 
   RemoteHostInfoFetcher remote_host_info_fetcher;
   bool request_started = remote_host_info_fetcher.RetrieveRemoteHostInfo(
-      kTestApplicationId,
-      kAccessTokenValue,
-      kDeveloperEnvironment,
+      kTestApplicationId, kAccessTokenValue, kDeveloperEnvironment,
       remote_host_info_callback);
 
   run_loop.Run();
@@ -235,23 +209,18 @@
 }
 
 TEST_F(RemoteHostInfoFetcherTest, RetrieveRemoteHostInfoPendingResponse) {
-  SetFakeResponse(
-      GURL(dev_service_environment_url_),
-      kRemoteHostInfoPendingResponse,
-      net::HTTP_OK,
-      net::URLRequestStatus::SUCCESS);
+  SetFakeResponse(GURL(dev_service_environment_url_),
+                  kRemoteHostInfoPendingResponse, net::HTTP_OK,
+                  net::URLRequestStatus::SUCCESS);
 
   base::RunLoop run_loop;
   RemoteHostInfoCallback remote_host_info_callback =
       base::Bind(&RemoteHostInfoFetcherTest::OnRemoteHostInfoRetrieved,
-                 base::Unretained(this),
-                 run_loop.QuitClosure());
+                 base::Unretained(this), run_loop.QuitClosure());
 
   RemoteHostInfoFetcher remote_host_info_fetcher;
   bool request_started = remote_host_info_fetcher.RetrieveRemoteHostInfo(
-      kTestApplicationId,
-      kAccessTokenValue,
-      kDeveloperEnvironment,
+      kTestApplicationId, kAccessTokenValue, kDeveloperEnvironment,
       remote_host_info_callback);
 
   run_loop.Run();
@@ -269,23 +238,18 @@
 }
 
 TEST_F(RemoteHostInfoFetcherTest, RetrieveRemoteHostInfoEmptyResponse) {
-  SetFakeResponse(
-      GURL(dev_service_environment_url_),
-      kRemoteHostInfoEmptyResponse,
-      net::HTTP_OK,
-      net::URLRequestStatus::SUCCESS);
+  SetFakeResponse(GURL(dev_service_environment_url_),
+                  kRemoteHostInfoEmptyResponse, net::HTTP_OK,
+                  net::URLRequestStatus::SUCCESS);
 
   base::RunLoop run_loop;
   RemoteHostInfoCallback remote_host_info_callback =
       base::Bind(&RemoteHostInfoFetcherTest::OnRemoteHostInfoRetrieved,
-                 base::Unretained(this),
-                 run_loop.QuitClosure());
+                 base::Unretained(this), run_loop.QuitClosure());
 
   RemoteHostInfoFetcher remote_host_info_fetcher;
   bool request_started = remote_host_info_fetcher.RetrieveRemoteHostInfo(
-      kTestApplicationId,
-      kAccessTokenValue,
-      kDeveloperEnvironment,
+      kTestApplicationId, kAccessTokenValue, kDeveloperEnvironment,
       remote_host_info_callback);
 
   run_loop.Run();
@@ -304,29 +268,22 @@
 
 TEST_F(RemoteHostInfoFetcherTest, MultipleRetrieveRemoteHostInfoRequests) {
   // First, we will fetch info from the development service environment.
-  SetFakeResponse(
-      GURL(dev_service_environment_url_),
-      kRemoteHostInfoReadyResponse,
-      net::HTTP_OK,
-      net::URLRequestStatus::SUCCESS);
+  SetFakeResponse(GURL(dev_service_environment_url_),
+                  kRemoteHostInfoReadyResponse, net::HTTP_OK,
+                  net::URLRequestStatus::SUCCESS);
 
-  SetFakeResponse(
-      GURL(test_service_environment_url_),
-      kRemoteHostInfoEmptyResponse,
-      net::HTTP_NOT_FOUND,
-      net::URLRequestStatus::FAILED);
+  SetFakeResponse(GURL(test_service_environment_url_),
+                  kRemoteHostInfoEmptyResponse, net::HTTP_NOT_FOUND,
+                  net::URLRequestStatus::FAILED);
 
   base::RunLoop dev_run_loop;
   RemoteHostInfoCallback dev_remote_host_info_callback =
       base::Bind(&RemoteHostInfoFetcherTest::OnRemoteHostInfoRetrieved,
-                 base::Unretained(this),
-                 dev_run_loop.QuitClosure());
+                 base::Unretained(this), dev_run_loop.QuitClosure());
 
   RemoteHostInfoFetcher remote_host_info_fetcher;
   bool dev_request_started = remote_host_info_fetcher.RetrieveRemoteHostInfo(
-      kTestApplicationId,
-      kAccessTokenValue,
-      kDeveloperEnvironment,
+      kTestApplicationId, kAccessTokenValue, kDeveloperEnvironment,
       dev_remote_host_info_callback);
 
   dev_run_loop.Run();
@@ -340,32 +297,25 @@
   EXPECT_TRUE(!remote_host_info_.shared_secret.empty());
 
   // Next, we will fetch info from the test service environment.
-  SetFakeResponse(
-      GURL(test_service_environment_url_),
-      kRemoteHostInfoReadyResponse,
-      net::HTTP_OK,
-      net::URLRequestStatus::SUCCESS);
+  SetFakeResponse(GURL(test_service_environment_url_),
+                  kRemoteHostInfoReadyResponse, net::HTTP_OK,
+                  net::URLRequestStatus::SUCCESS);
 
-  SetFakeResponse(
-      GURL(dev_service_environment_url_),
-      kRemoteHostInfoEmptyResponse,
-      net::HTTP_NOT_FOUND,
-      net::URLRequestStatus::FAILED);
+  SetFakeResponse(GURL(dev_service_environment_url_),
+                  kRemoteHostInfoEmptyResponse, net::HTTP_NOT_FOUND,
+                  net::URLRequestStatus::FAILED);
 
   base::RunLoop test_run_loop;
   RemoteHostInfoCallback test_remote_host_info_callback =
       base::Bind(&RemoteHostInfoFetcherTest::OnRemoteHostInfoRetrieved,
-                 base::Unretained(this),
-                 test_run_loop.QuitClosure());
+                 base::Unretained(this), test_run_loop.QuitClosure());
 
   // Reset the state of our internal |remote_host_info_| object.
   remote_host_info_ = RemoteHostInfo();
   EXPECT_FALSE(remote_host_info_.IsReadyForConnection());
 
   bool test_request_started = remote_host_info_fetcher.RetrieveRemoteHostInfo(
-      kTestApplicationId,
-      kAccessTokenValue,
-      kTestingEnvironment,
+      kTestApplicationId, kAccessTokenValue, kTestingEnvironment,
       test_remote_host_info_callback);
 
   test_run_loop.Run();
diff --git a/remoting/test/test_chromoting_client.cc b/remoting/test/test_chromoting_client.cc
index 925a4fb..adf1093 100644
--- a/remoting/test/test_chromoting_client.cc
+++ b/remoting/test/test_chromoting_client.cc
@@ -31,6 +31,9 @@
 
 namespace {
 
+const char kAppRemotingCapabilities[] =
+    "rateLimitResizeRequests desktopShape sendInitialResolution googleDrive";
+
 const char kXmppHostName[] = "talk.google.com";
 const int kXmppPortNumber = 5222;
 
@@ -160,8 +163,7 @@
   chromoting_client_.reset(new ChromotingClient(client_context_.get(),
                                                 this,  // client_user_interface.
                                                 video_renderer_.get(),
-                                                nullptr  // audio_player
-                                                ));
+                                                nullptr));  // audio_player
 
   if (test_connection_to_host_) {
     chromoting_client_->SetConnectionToHostForTests(
@@ -212,8 +214,7 @@
 
   chromoting_client_->Start(signal_strategy_.get(), authenticator.Pass(),
                             transport_factory.Pass(), remote_host_info.host_jid,
-                            std::string()  // capabilities
-                            );
+                            kAppRemotingCapabilities);
 }
 
 void TestChromotingClient::EndConnection() {
diff --git a/remoting/test/test_chromoting_client.h b/remoting/test/test_chromoting_client.h
index 6899df0..69f3967 100644
--- a/remoting/test/test_chromoting_client.h
+++ b/remoting/test/test_chromoting_client.h
@@ -10,6 +10,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
+#include "remoting/client/chromoting_client.h"
 #include "remoting/client/client_user_interface.h"
 #include "remoting/protocol/clipboard_filter.h"
 #include "remoting/protocol/cursor_shape_stub.h"
@@ -17,13 +18,17 @@
 #include "remoting/test/remote_host_info.h"
 
 namespace remoting {
-class ChromotingClient;
+
 class ClientContext;
 class XmppSignalStrategy;
 class VideoRenderer;
+
+namespace protocol {
+class ClipboardStub;
+class HostStub;
+class InputStub;
 }
 
-namespace remoting {
 namespace test {
 
 // Manages a chromoting connection to a remote host.  Destroying a
@@ -46,6 +51,13 @@
   // Ends the current remote connection and updates the connection state.
   void EndConnection();
 
+  // Stubs used to send messages to the remote host.
+  protocol::ClipboardStub* clipboard_forwarder() {
+    return chromoting_client_->clipboard_forwarder();
+  }
+  protocol::HostStub* host_stub() { return chromoting_client_->host_stub(); }
+  protocol::InputStub* input_stub() { return chromoting_client_->input_stub(); }
+
   // Registers an observer which will be notfied when remote connection events
   // occur.  Registered Observers must not tear-down this object on receipt of
   // these callbacks. The callbacks should be used for informational purposes.
diff --git a/remoting/webapp/app_remoting/js/app_remoting.js b/remoting/webapp/app_remoting/js/app_remoting.js
index e8f3bbf7..fa067037 100644
--- a/remoting/webapp/app_remoting/js/app_remoting.js
+++ b/remoting/webapp/app_remoting/js/app_remoting.js
@@ -14,13 +14,14 @@
 var remoting = remoting || {};
 
 /**
- * @param {remoting.Application} app The main app that owns this delegate.
+ * @param {Array<string>} appCapabilities Array of application capabilities.
  * @constructor
- * @implements {remoting.Application.Delegate}
+ * @implements {remoting.ApplicationInterface}
  * @implements {remoting.ProtocolExtension}
+ * @extends {remoting.Application}
  */
-remoting.AppRemoting = function(app) {
-  app.setDelegate(this);
+remoting.AppRemoting = function(appCapabilities) {
+  base.inherits(this, remoting.Application, appCapabilities);
 
   /** @private {remoting.ApplicationContextMenu} */
   this.contextMenu_ = null;
@@ -60,11 +61,26 @@
 };
 
 /**
- * Initialize the application. This is called before an OAuth token is requested
- * and should be used for tasks such as initializing the DOM, registering event
- * handlers, etc.
+ * @return {string} Application product name to be used in UI.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.AppRemoting.prototype.init = function() {
+remoting.AppRemoting.prototype.getApplicationName = function() {
+  var manifest = chrome.runtime.getManifest();
+  return manifest.name;
+};
+
+/**
+ * @param {!remoting.Error} error The failure reason.
+ * @override {remoting.ApplicationInterface}
+ */
+remoting.AppRemoting.prototype.signInFailed_ = function(error) {
+  this.onError_(error);
+};
+
+/**
+ * @override {remoting.ApplicationInterface}
+ */
+remoting.AppRemoting.prototype.initApplication_ = function() {
   // TODO(jamiewalch): Remove ClientSession's dependency on remoting.fullscreen
   // so that this is no longer required.
   remoting.fullscreen = new remoting.FullscreenAppsV2();
@@ -91,15 +107,10 @@
 };
 
 /**
- * Start the application. Once start() is called, the delegate can assume that
- * the user has consented to all permissions specified in the manifest.
- *
- * @param {remoting.SessionConnector} connector
- * @param {string} token An OAuth access token. The delegate should not cache
- *     this token, but can assume that it will remain valid during application
- *     start-up.
+ * @param {string} token An OAuth access token.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.AppRemoting.prototype.start = function(connector, token) {
+remoting.AppRemoting.prototype.startApplication_ = function(token) {
   remoting.LoadingWindow.show();
 
   /** @type {remoting.AppRemoting} */
@@ -147,9 +158,9 @@
                                    host['sharedSecret']);
         };
 
-        connector.connectMe2App(host, fetchThirdPartyToken);
+        that.sessionConnector_.connectMe2App(host, fetchThirdPartyToken);
       } else if (response && response.status == 'pending') {
-        that.handleError(new remoting.Error(
+        that.onError_(new remoting.Error(
             remoting.Error.Tag.SERVICE_UNAVAILABLE));
       }
     } else {
@@ -158,61 +169,45 @@
       // been updated to properly report 'unknown' errors (rather than
       // reporting them as AUTHENTICATION_FAILED).
       if (xhrResponse.status == 0) {
-        that.handleError(new remoting.Error(
+        that.onError_(new remoting.Error(
             remoting.Error.Tag.NETWORK_FAILURE));
       } else if (xhrResponse.status == 401) {
-        that.handleError(new remoting.Error(
+        that.onError_(new remoting.Error(
             remoting.Error.Tag.AUTHENTICATION_FAILED));
       } else if (xhrResponse.status == 403) {
-        that.handleError(new remoting.Error(
+        that.onError_(new remoting.Error(
             remoting.Error.Tag.APP_NOT_AUTHORIZED));
       } else if (xhrResponse.status == 502 || xhrResponse.status == 503) {
-        that.handleError(new remoting.Error(
+        that.onError_(new remoting.Error(
             remoting.Error.Tag.SERVICE_UNAVAILABLE));
       } else {
-        that.handleError(remoting.Error.unexpected());
+        that.onError_(remoting.Error.unexpected());
       }
     }
   };
 
   new remoting.Xhr({
     method: 'POST',
-    url: that.runApplicationUrl(),
+    url: that.runApplicationUrl_(),
     oauthToken: token
   }).start().then(parseAppHostResponse);
 };
 
 /**
- * Report an authentication error to the user. This is called in lieu of start()
- * if the user cannot be authenticated or if they decline the app permissions.
- *
- * @param {!remoting.Error} error The failure reason.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.AppRemoting.prototype.signInFailed = function(error) {
-  this.handleError(error);
+remoting.AppRemoting.prototype.exitApplication_ = function() {
+  remoting.LoadingWindow.close();
+  this.closeMainWindow_();
 };
 
 /**
- * @return {string} Application product name to be used in UI.
- */
-remoting.AppRemoting.prototype.getApplicationName = function() {
-  var manifest = chrome.runtime.getManifest();
-  return manifest.name;
-};
-
-/** @return {string} */
-remoting.AppRemoting.prototype.runApplicationUrl = function() {
-  return remoting.settings.APP_REMOTING_API_BASE_URL + '/applications/' +
-      remoting.settings.getAppRemotingApplicationId() + '/run';
-};
-
-/**
- * Called when a new session has been connected.
- *
  * @param {remoting.ConnectionInfo} connectionInfo
- * @return {void} Nothing.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.AppRemoting.prototype.handleConnected = function(connectionInfo) {
+remoting.AppRemoting.prototype.onConnected_ = function(connectionInfo) {
+  this.initSession_(connectionInfo);
+
   remoting.identity.getUserInfo().then(
       function(userInfo) {
         remoting.clientSession.sendClientMessage(
@@ -220,7 +215,7 @@
             JSON.stringify({fullName: userInfo.name}));
       });
 
-  remoting.app.getSessionConnector().registerProtocolExtension(this);
+  this.sessionConnector_.registerProtocolExtension(this);
 
   this.connectedView_ = new remoting.AppConnectedView(
       document.getElementById('client-container'), connectionInfo);
@@ -233,11 +228,9 @@
 };
 
 /**
- * Called when the current session has been disconnected.
- *
- * @return {void} Nothing.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.AppRemoting.prototype.handleDisconnected = function() {
+remoting.AppRemoting.prototype.onDisconnected_ = function() {
   base.dispose(this.connectedView_);
   this.connectedView_ = null;
 
@@ -245,18 +238,30 @@
 };
 
 /**
- * Called when the current session's connection has failed.
- *
- * @param {remoting.SessionConnector} connector
  * @param {!remoting.Error} error
- * @return {void} Nothing.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.AppRemoting.prototype.handleConnectionFailed = function(
-    connector, error) {
-  this.handleError(error);
+remoting.AppRemoting.prototype.onConnectionFailed_ = function(error) {
+  this.onError_(error);
 };
 
-/** @return {Array<string>} */
+/**
+ * @param {!remoting.Error} error The error to be localized and displayed.
+ * @override {remoting.ApplicationInterface}
+ */
+remoting.AppRemoting.prototype.onError_ = function(error) {
+  console.error('Connection failed: ' + error.toString());
+  remoting.LoadingWindow.close();
+  remoting.MessageWindow.showErrorMessage(
+      chrome.i18n.getMessage(/*i18n-content*/'CONNECTION_FAILED'),
+      chrome.i18n.getMessage(error.getTag()));
+};
+
+
+/**
+ * @return {Array<string>}
+ * @override {remoting.ProtocolExtension}
+ */
 remoting.AppRemoting.prototype.getExtensionTypes = function() {
   return ['openURL', 'onWindowRemoved', 'onWindowAdded',
           'onAllWindowsMinimized', 'setKeyboardLayouts', 'pingResponse'];
@@ -265,6 +270,7 @@
 /**
  * @param {function(string,string)} sendMessageToHost Callback to send a message
  *     to the host.
+ * @override {remoting.ProtocolExtension}
  */
 remoting.AppRemoting.prototype.startExtension = function(sendMessageToHost) {
 };
@@ -272,6 +278,7 @@
 /**
  * @param {string} type The message type.
  * @param {Object} message The parsed extension message data.
+ * @override {remoting.ProtocolExtension}
  */
 remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) {
   switch (type) {
@@ -321,22 +328,10 @@
 };
 
 /**
- * Called when an error needs to be displayed to the user.
- *
- * @param {!remoting.Error} error The error to be localized and displayed.
- * @return {void} Nothing.
+ * @return {string}
+ * @private
  */
-remoting.AppRemoting.prototype.handleError = function(error) {
-  console.error('Connection failed: ' + error.toString());
-  remoting.LoadingWindow.close();
-  remoting.MessageWindow.showErrorMessage(
-      chrome.i18n.getMessage(/*i18n-content*/'CONNECTION_FAILED'),
-      chrome.i18n.getMessage(error.getTag()));
-};
-
-/**
- * Close the loading window before exiting.
- */
-remoting.AppRemoting.prototype.handleExit = function() {
-  remoting.LoadingWindow.close();
+remoting.AppRemoting.prototype.runApplicationUrl_ = function() {
+  return remoting.settings.APP_REMOTING_API_BASE_URL + '/applications/' +
+      remoting.settings.getAppRemotingApplicationId() + '/run';
 };
diff --git a/remoting/webapp/app_remoting/js/ar_main.js b/remoting/webapp/app_remoting/js/ar_main.js
index 269b5ff9..2efe129e 100644
--- a/remoting/webapp/app_remoting/js/ar_main.js
+++ b/remoting/webapp/app_remoting/js/ar_main.js
@@ -11,8 +11,7 @@
  * Entry point ('load' handler) for App Remoting webapp.
  */
 remoting.startAppRemoting = function() {
-  remoting.app = new remoting.Application(remoting.app_capabilities());
-  var app_remoting = new remoting.AppRemoting(remoting.app);
+  remoting.app = new remoting.AppRemoting(remoting.app_capabilities());
   remoting.app.start();
 };
 
diff --git a/remoting/webapp/base/js/application.js b/remoting/webapp/base/js/application.js
index bcd6cf12..1171ae9 100644
--- a/remoting/webapp/base/js/application.js
+++ b/remoting/webapp/base/js/application.js
@@ -15,10 +15,13 @@
 /**
  * @param {Array<string>} appCapabilities Array of application capabilities.
  * @constructor
+ * @implements {remoting.ApplicationInterface}
  */
 remoting.Application = function(appCapabilities) {
-  /** @private {remoting.Application.Delegate} */
-  this.delegate_ = null;
+  // Create global factories.
+  remoting.ClientPlugin.factory = new remoting.DefaultClientPluginFactory();
+  remoting.SessionConnector.factory =
+      new remoting.DefaultSessionConnectorFactory();
 
   /** @private {Array<string>} */
   this.appCapabilities_ = [
@@ -29,26 +32,23 @@
   // Append the app-specific capabilities.
   this.appCapabilities_.push.apply(this.appCapabilities_, appCapabilities);
 
-  /** @private {remoting.SessionConnector} */
-  this.sessionConnector_ = null;
+  /** @protected {remoting.SessionConnector} */
+  this.sessionConnector_ = remoting.SessionConnector.factory.createConnector(
+      document.getElementById('client-container'),
+      this.onConnected_.bind(this),
+      this.onError_.bind(this),
+      this.onConnectionFailed_.bind(this),
+      this.appCapabilities_);
 
   /** @private {base.Disposable} */
   this.sessionConnectedHooks_ = null;
 };
 
 /**
- * @param {remoting.Application.Delegate} appDelegate The delegate that
- *    contains the app-specific functionality.
+ * @return {remoting.SessionConnector} The session connector.
  */
-remoting.Application.prototype.setDelegate = function(appDelegate) {
-  this.delegate_ = appDelegate;
-};
-
-/**
- * @return {string} Application product name to be used in UI.
- */
-remoting.Application.prototype.getApplicationName = function() {
-  return this.delegate_.getApplicationName();
+remoting.Application.prototype.getSessionConnector = function() {
+  return this.sessionConnector_;
 };
 
 /**
@@ -60,51 +60,7 @@
   return capabilities.indexOf(capability) != -1;
 };
 
-/**
- * Initialize the application and register all event handlers. After this
- * is called, the app is running and waiting for user events.
- *
- * @return {void} Nothing.
- */
-remoting.Application.prototype.start = function() {
-  // Create global objects.
-  remoting.ClientPlugin.factory = new remoting.DefaultClientPluginFactory();
-  remoting.SessionConnector.factory =
-      new remoting.DefaultSessionConnectorFactory();
-
-  // TODO(garykac): This should be owned properly rather than living in the
-  // global 'remoting' namespace.
-  remoting.settings = new remoting.Settings();
-
-  remoting.initGlobalObjects();
-  remoting.initIdentity();
-
-  this.delegate_.init();
-
-  var that = this;
-  remoting.identity.getToken().then(
-      this.delegate_.start.bind(this.delegate_, this.getSessionConnector())
-  ).catch(remoting.Error.handler(
-      function(/** !remoting.Error */ error) {
-        if (error.hasTag(remoting.Error.Tag.CANCELLED)) {
-          that.exit();
-        } else {
-          that.delegate_.signInFailed(error);
-        }
-      }
-    )
-  );
-};
-
-/**
- * Quit the application.
- */
-remoting.Application.prototype.exit = function() {
-  this.delegate_.handleExit();
-  chrome.app.window.current().close();
-};
-
-/** Disconnect the remoting client. */
+/* Disconnect the remoting client. */
 remoting.Application.prototype.disconnect = function() {
   if (remoting.clientSession) {
     remoting.clientSession.disconnect(remoting.Error.none());
@@ -112,67 +68,63 @@
   }
 };
 
+/* Public method to exit the application. */
+remoting.Application.prototype.quit = function() {
+  this.exitApplication_();
+};
+
+/**
+ * Close the main window when quitting the application. This should be called
+ * by exitApplication() in the subclass.
+ * @protected
+ */
+remoting.Application.prototype.closeMainWindow_ = function() {
+  chrome.app.window.current().close();
+};
+
+/**
+ * Initialize the application and register all event handlers. After this
+ * is called, the app is running and waiting for user events.
+ */
+remoting.Application.prototype.start = function() {
+  // TODO(garykac): This should be owned properly rather than living in the
+  // global 'remoting' namespace.
+  remoting.settings = new remoting.Settings();
+
+  remoting.initGlobalObjects();
+  remoting.initIdentity();
+
+  this.initApplication_();
+
+  var that = this;
+  remoting.identity.getToken().
+    then(this.startApplication_.bind(this)).
+    catch(remoting.Error.handler(
+      function(/** !remoting.Error */ error) {
+        if (error.hasTag(remoting.Error.Tag.CANCELLED)) {
+          that.exitApplication_();
+        } else {
+          that.signInFailed_(error);
+        }
+      }
+    )
+  );
+};
+
 /**
  * Called when a new session has been connected.
  *
  * @param {remoting.ConnectionInfo} connectionInfo
  * @return {void} Nothing.
+ * @protected
  */
-remoting.Application.prototype.onConnected = function(connectionInfo) {
+remoting.Application.prototype.initSession_ = function(connectionInfo) {
   this.sessionConnectedHooks_ = new base.Disposables(
     new base.EventHook(connectionInfo.session(), 'stateChanged',
                        this.onSessionFinished_.bind(this)),
     new base.RepeatingTimer(this.updateStatistics_.bind(this), 1000)
   );
   remoting.clipboard.startSession();
-
-  this.delegate_.handleConnected(connectionInfo);
-};
-
-/**
- * Called when the current session has been disconnected.
- *
- * @return {void} Nothing.
- */
-remoting.Application.prototype.onDisconnected = function() {
-  this.delegate_.handleDisconnected();
-};
-
-/**
- * Called when the current session's connection has failed.
- *
- * @param {!remoting.Error} error
- * @return {void} Nothing.
- */
-remoting.Application.prototype.onConnectionFailed = function(error) {
-  this.delegate_.handleConnectionFailed(this.sessionConnector_, error);
-};
-
-/**
- * Called when an error needs to be displayed to the user.
- *
- * @param {!remoting.Error} errorTag The error to be localized and displayed.
- * @return {void} Nothing.
- */
-remoting.Application.prototype.onError = function(errorTag) {
-  this.delegate_.handleError(errorTag);
-};
-
-/**
- * @return {remoting.SessionConnector} A session connector, creating a new one
- *     if necessary.
- */
-remoting.Application.prototype.getSessionConnector = function() {
-  // TODO(garykac): Check if this can be initialized in the ctor.
-  if (!this.sessionConnector_) {
-    this.sessionConnector_ = remoting.SessionConnector.factory.createConnector(
-        document.getElementById('client-container'),
-        this.onConnected.bind(this),
-        this.onError.bind(this),
-        this.onConnectionFailed.bind(this),
-        this.appCapabilities_);
-  }
-  return this.sessionConnector_;
 };
 
 /**
@@ -186,7 +138,7 @@
   switch (state.current) {
     case remoting.ClientSession.State.CLOSED:
       console.log('Connection closed by host');
-      this.onDisconnected();
+      this.onDisconnected_();
       break;
     case remoting.ClientSession.State.FAILED:
       var error = remoting.clientSession.getError();
@@ -195,14 +147,14 @@
       if (error === null) {
         error = remoting.Error.unexpected();
       }
-      this.onError(error);
+      this.onError_(error);
       break;
 
     default:
       console.error('Unexpected client plugin state: ' + state.current);
       // This should only happen if the web-app and client plugin get out of
       // sync, so MISSING_PLUGIN is a suitable error.
-      this.onError(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN));
+      this.onError_(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN));
       break;
   }
 
@@ -219,83 +171,144 @@
 };
 
 
+/*
+ * remoting.ApplicationInterface
+ * These functions must be overridden in the subclass.
+ */
+
+/** @return {string} */
+remoting.Application.prototype.getApplicationName = function() {
+  base.debug.assert(false, "Subclass must override");
+};
+
 /**
+ * @param {!remoting.Error} error
+ * @protected
+ */
+remoting.Application.prototype.signInFailed_ = function(error) {
+  base.debug.assert(false, "Subclass must override");
+};
+
+/** @protected */
+remoting.Application.prototype.initApplication_ = function() {
+  base.debug.assert(false, "Subclass must override");
+};
+
+/**
+ * @param {string} token
+ * @protected
+ */
+remoting.Application.prototype.startApplication_ = function(token) {
+  base.debug.assert(false, "Subclass must override");
+};
+
+/** @protected */
+remoting.Application.prototype.exitApplication_ = function() {
+  base.debug.assert(false, "Subclass must override");
+};
+
+/**
+ * @param {remoting.ConnectionInfo} connectionInfo
+ * @protected
+ */
+remoting.Application.prototype.onConnected_ = function(connectionInfo) {
+  base.debug.assert(false, "Subclass must override");
+};
+
+/** @protected */
+remoting.Application.prototype.onDisconnected_ = function() {
+  base.debug.assert(false, "Subclass must override");
+};
+
+/**
+ * @param {!remoting.Error} error
+ * @protected
+ */
+remoting.Application.prototype.onConnectionFailed_ = function(error) {
+  base.debug.assert(false, "Subclass must override");
+};
+
+/**
+ * @param {!remoting.Error} error The error to be localized and displayed.
+ * @protected
+ */
+remoting.Application.prototype.onError_ = function(error) {
+  base.debug.assert(false, "Subclass must override");
+};
+
+
+/**
+ * The interface specifies the methods that a subclass of remoting.Application
+ * is required implement to override the default behavior.
+ *
  * @interface
  */
-remoting.Application.Delegate = function() {};
-
-/**
- * Initialize the application. This is called before an OAuth token is requested
- * and should be used for tasks such as initializing the DOM, registering event
- * handlers, etc.
- */
-remoting.Application.Delegate.prototype.init = function() {};
-
-/**
- * Start the application. Once start() is called, the delegate can assume that
- * the user has consented to all permissions specified in the manifest.
- *
- * @param {remoting.SessionConnector} connector
- * @param {string} token An OAuth access token. The delegate should not cache
- *     this token, but can assume that it will remain valid during application
- *     start-up.
- */
-remoting.Application.Delegate.prototype.start = function(connector, token) {};
-
-/**
- * Report an authentication error to the user. This is called in lieu of start()
- * if the user cannot be authenticated.
- *
- * @param {!remoting.Error} error The failure reason.
- */
-remoting.Application.Delegate.prototype.signInFailed = function(error) {};
+remoting.ApplicationInterface = function() {};
 
 /**
  * @return {string} Application product name to be used in UI.
  */
-remoting.Application.Delegate.prototype.getApplicationName = function() {};
+remoting.ApplicationInterface.prototype.getApplicationName = function() {};
+
+/**
+ * Report an authentication error to the user. This is called in lieu of
+ * startApplication() if the user cannot be authenticated or if they decline
+ * the app permissions.
+ *
+ * @param {!remoting.Error} error The failure reason.
+ */
+remoting.ApplicationInterface.prototype.signInFailed_ = function(error) {};
+
+/**
+ * Initialize the application. This is called before an OAuth token is requested
+ * and should be used for tasks such as initializing the DOM, registering event
+ * handlers, etc. After this is called, the app is running and waiting for
+ * user events.
+ */
+remoting.ApplicationInterface.prototype.initApplication_ = function() {};
+
+/**
+ * Start the application. Once startApplication() is called, we can assume that
+ * the user has consented to all permissions specified in the manifest.
+ *
+ * @param {string} token An OAuth access token. The app should not cache
+ *     this token, but can assume that it will remain valid during application
+ *     start-up.
+ */
+remoting.ApplicationInterface.prototype.startApplication_ = function(token) {};
+
+/**
+ * Close down the application before exiting.
+ */
+remoting.ApplicationInterface.prototype.exitApplication_ = function() {};
 
 /**
  * Called when a new session has been connected.
  *
  * @param {remoting.ConnectionInfo} connectionInfo
- * @return {void} Nothing.
  */
-remoting.Application.Delegate.prototype.handleConnected = function(
-    connectionInfo) {};
+remoting.ApplicationInterface.prototype.onConnected_ =
+    function(connectionInfo) {};
 
 /**
  * Called when the current session has been disconnected.
- *
- * @return {void} Nothing.
  */
-remoting.Application.Delegate.prototype.handleDisconnected = function() {};
+remoting.ApplicationInterface.prototype.onDisconnected_ = function() {};
 
 /**
  * Called when the current session's connection has failed.
  *
- * @param {remoting.SessionConnector} connector
  * @param {!remoting.Error} error
- * @return {void} Nothing.
  */
-remoting.Application.Delegate.prototype.handleConnectionFailed =
-    function(connector, error) {};
+remoting.ApplicationInterface.prototype.onConnectionFailed_ =
+    function(error) {};
 
 /**
  * Called when an error needs to be displayed to the user.
  *
  * @param {!remoting.Error} errorTag The error to be localized and displayed.
- * @return {void} Nothing.
  */
-remoting.Application.Delegate.prototype.handleError = function(errorTag) {};
-
-/**
- * Perform any application-specific cleanup before exiting. This is called in
- * lieu of start() if the user declines the app permissions, and will usually
- * be called immediately prior to exiting, although delegates should not rely
- * on this.
- */
-remoting.Application.Delegate.prototype.handleExit = function() {};
+remoting.ApplicationInterface.prototype.onError_ = function(errorTag) {};
 
 
 /** @type {remoting.Application} */
diff --git a/remoting/webapp/base/js/base_inherits_unittest.js b/remoting/webapp/base/js/base_inherits_unittest.js
index bda316b..baa5b6f7 100644
--- a/remoting/webapp/base/js/base_inherits_unittest.js
+++ b/remoting/webapp/base/js/base_inherits_unittest.js
@@ -17,9 +17,12 @@
   this.name = 'grandChild';
 }
 
-/** @return {string} */
-GrandChildClass.prototype.overrideMethod = function() {
-  return 'overrideMethod - grandChild';
+/**
+ * @param {string} arg
+ * @return {string}
+ */
+GrandChildClass.prototype.overrideMethod = function(arg) {
+  return 'overrideMethod - grandChild - ' + arg;
 }
 
 /**
@@ -32,9 +35,12 @@
   this.childOnly = 'childOnly';
 }
 
-/** @return {string} */
-ChildClass.prototype.overrideMethod = function() {
-  return 'overrideMethod - child';
+/**
+ * @param {string} arg
+ * @return {string}
+ */
+ChildClass.prototype.overrideMethod = function(arg) {
+  return 'overrideMethod - child - ' + arg;
 }
 
 /** @return {string} */
@@ -43,8 +49,8 @@
 }
 
 /**
- * @constructor
  * @param {string} arg
+ * @constructor
  */
 var ParentClass = function(arg) {
   /** @type  {string} */
@@ -60,9 +66,12 @@
   return 'parentMethod';
 }
 
-/** @return  {string} */
-ParentClass.prototype.overrideMethod = function() {
-  return 'overrideMethod - parent';
+/**
+ * @param {string} arg
+ * @return {string}
+ */
+ParentClass.prototype.overrideMethod = function(arg) {
+  return 'overrideMethod - parent - ' + arg;
 }
 
 QUnit.test('should invoke parent constructor with the correct arguments',
@@ -90,19 +99,33 @@
 QUnit.test('should override parent property and method', function(assert) {
   var child = new ChildClass();
   assert.equal(child.name, 'child');
-  assert.equal(child.overrideMethod(), 'overrideMethod - child');
+  assert.equal(child.overrideMethod('123'), 'overrideMethod - child - 123');
   assert.equal(child.childOnly, 'childOnly');
   assert.equal(child.childMethod(), 'childMethod');
 });
 
-QUnit.test('should works on an inheritance chain', function(assert) {
+QUnit.test('should work on an inheritance chain', function(assert) {
   var grandChild = new GrandChildClass();
   assert.equal(grandChild.name, 'grandChild');
-  assert.equal(grandChild.overrideMethod(), 'overrideMethod - grandChild');
+  assert.equal(grandChild.overrideMethod('246'),
+               'overrideMethod - grandChild - 246');
   assert.equal(grandChild.childOnly, 'childOnly');
   assert.equal(grandChild.childMethod(), 'childMethod');
   assert.equal(grandChild.parentOnly, 'parentOnly');
   assert.equal(grandChild.parentMethod(), 'parentMethod');
 });
 
-})();
\ No newline at end of file
+QUnit.test('should be able to access parent class methods', function(assert) {
+  var grandChild = new GrandChildClass();
+
+  assert.equal(grandChild.overrideMethod('789'),
+               'overrideMethod - grandChild - 789');
+
+  var childMethod = ChildClass.prototype.overrideMethod.call(grandChild, '81');
+  assert.equal(childMethod, 'overrideMethod - child - 81');
+
+  var parentMethod = ParentClass.prototype.overrideMethod.call(grandChild, '4');
+  assert.equal(parentMethod, 'overrideMethod - parent - 4');
+});
+
+})();
diff --git a/remoting/webapp/browser_test/bump_scroll_browser_test.js b/remoting/webapp/browser_test/bump_scroll_browser_test.js
index 734e6adf..a90d5b1 100644
--- a/remoting/webapp/browser_test/bump_scroll_browser_test.js
+++ b/remoting/webapp/browser_test/bump_scroll_browser_test.js
@@ -58,7 +58,8 @@
 
 /** @return {remoting.DesktopViewport} */
 function getViewportForTesting() {
-  var view = remoting.desktopDelegateForTesting.getConnectedViewForTesting();
+  var desktopApp = /** @type {remoting.DesktopRemoting} */ (remoting.app);
+  var view = desktopApp.getConnectedViewForTesting();
   if (view) {
     return view.getViewportForTesting();
   }
@@ -343,4 +344,4 @@
     Promise.resolve(true);
   }
   return this.verifyScroll(undefined, undefined, desktopViewport);
-};
\ No newline at end of file
+};
diff --git a/remoting/webapp/build-webapp.py b/remoting/webapp/build-webapp.py
index a5e69ef..d49bd07c 100755
--- a/remoting/webapp/build-webapp.py
+++ b/remoting/webapp/build-webapp.py
@@ -14,6 +14,7 @@
 # Python 2.5 compatibility
 from __future__ import with_statement
 
+import argparse
 import io
 import os
 import platform
@@ -62,6 +63,36 @@
                  "'" + placeholder + "'", "'" + value + "'")
 
 
+def replaceBool(destination, placeholder, value):
+  # Look for a "!!" in the source code so the expession we're
+  # replacing looks like a boolean to the compiler.  A single "!"
+  # would satisfy the compiler but might confused human readers.
+  findAndReplace(os.path.join(destination, 'plugin_settings.js'),
+                 "!!'" + placeholder + "'", 'true' if value else 'false')
+
+
+def parseBool(boolStr):
+  """Tries to parse a string as a boolean value.
+
+  Returns a bool on success; raises ValueError on failure.
+  """
+  lower = boolStr.tolower()
+  if lower in ['0', 'false']: return False
+  if lower in ['1', 'true']: return True
+  raise ValueError('not a boolean string {!r}'.format(boolStr))
+
+
+def getenvBool(name, defaultValue):
+  """Gets an environment value as a boolean."""
+  rawValue = os.environ.get(name)
+  if rawValue is None:
+    return defaultValue
+  try:
+    return parseBool(rawValue)
+  except ValueError:
+    raise Exception('Value of ${} must be boolean!'.format(name))
+
+
 def processJinjaTemplate(input_file, include_paths, output_file, context):
   jinja2_path = os.path.normpath(
       os.path.join(os.path.abspath(__file__),
@@ -76,9 +107,9 @@
   io.open(output_file, 'w', encoding='utf-8').write(rendered)
 
 def buildWebApp(buildtype, version, destination, zip_path,
-                manifest_template, webapp_type, app_id, app_name,
-                app_description, app_capabilities, files, locales, jinja_paths,
-                service_environment):
+                manifest_template, webapp_type, appid, app_name,
+                app_description, app_capabilities, files, locales_listfile,
+                jinja_paths, service_environment, use_gcd):
   """Does the main work of building the webapp directory and zipfile.
 
   Args:
@@ -89,7 +120,7 @@
              contents of |destination|.
     manifest_template: jinja2 template file for manifest.
     webapp_type: webapp type ("v1", "v2", "v2_pnacl" or "app_remoting").
-    app_id: A string with the Remoting Application Id (only used for app
+    appid: A string with the Remoting Application Id (only used for app
              remoting webapps). If supplied, it defaults to using the
              test API server.
     app_name: A string with the name of the application.
@@ -98,13 +129,24 @@
                       enabled for this application.
     files: An array of strings listing the paths for resources to include
            in this webapp.
-    locales: An array of strings listing locales, which are copied, along
-             with their directory structure from the _locales directory down.
+    locales_listfile: The name of a file containing a list of locales, one per
+             line, which are copied, along with their directory structure, from
+             the _locales directory down.
     jinja_paths: An array of paths to search for {%include} directives in
                  addition to the directory containing the manifest template.
     service_environment: Used to point the webApp to one of the
                          dev/test/staging/prod environments
+    use_gcd: True if GCD support should be enabled.
   """
+
+  # Load the locales files from the locales_listfile.
+  if not locales_listfile:
+    raise Exception('You must specify a locales_listfile')
+  locales = []
+  with open(locales_listfile) as input:
+    for s in input:
+      locales.append(s.rstrip())
+
   # Ensure a fresh directory.
   try:
     shutil.rmtree(destination)
@@ -190,8 +232,8 @@
                         + buildtype + ': ' + service_environment)
       if 'out/Release' not in destination and 'out\Release' not in destination:
         raise Exception('Prod builds must be placed in the out/Release folder')
-      if app_id != None:
-        raise Exception('Cannot pass in an app_id for '
+      if appid != None:
+        raise Exception('Cannot pass in an appid for '
                         + buildtype + ' builds: ' + service_environment)
       if appRemotingApiHost != None:
         raise Exception('Cannot set APP_REMOTING_API_HOST env var for '
@@ -202,7 +244,7 @@
 
     # If an Application ID was set (either from service_environment variable or
     # from a command line argument), hardcode it, otherwise get it at runtime.
-    effectiveAppId = appRemotingApplicationId or app_id
+    effectiveAppId = appRemotingApplicationId or appid
     if effectiveAppId:
       appRemotingApplicationId = "'" + effectiveAppId + "'"
     else:
@@ -236,6 +278,7 @@
   else:
     appRemotingApiBaseUrl = ''
 
+  replaceBool(destination, 'USE_GCD', use_gcd)
   replaceString(destination, 'OAUTH2_BASE_URL', oauth2BaseUrl)
   replaceString(destination, 'OAUTH2_API_BASE_URL', oauth2ApiBaseUrl)
   replaceString(destination, 'DIRECTORY_API_BASE_URL', directoryApiBaseUrl)
@@ -284,10 +327,9 @@
                  "'OAUTH2_REDIRECT_URL'", oauth2RedirectUrlJs)
 
   # Configure xmpp server and directory bot settings in the plugin.
-  findAndReplace(os.path.join(destination, 'plugin_settings.js'),
-                 "Boolean('XMPP_SERVER_USE_TLS')",
-                  os.environ.get('XMPP_SERVER_USE_TLS', 'true'))
-
+  replaceBool(
+      destination, 'XMPP_SERVER_USE_TLS',
+      getenvBool('XMPP_SERVER_USE_TLS', True))
   xmppServer = os.environ.get('XMPP_SERVER',
                                'talk.google.com:443')
   replaceString(destination, 'XMPP_SERVER', xmppServer)
@@ -339,6 +381,7 @@
         'APP_NAME': app_name,
         'APP_DESCRIPTION': app_description,
         'OAUTH_GDRIVE_SCOPE': '',
+        'USE_GCD': use_gcd,
         'XMPP_SERVER': xmppServer,
     }
     if 'GOOGLE_DRIVE' in app_capabilities:
@@ -356,72 +399,28 @@
 
 
 def main():
-  if len(sys.argv) < 6:
-    print ('Usage: build-webapp.py '
-           '<build-type> <version> <dst> <zip-path> <manifest_template> '
-           '<webapp_type> <other files...> '
-           '--app_name <name> '
-           '--app_description <description> '
-           '--app_capabilities <capabilities...> '
-           '[--appid <appid>] '
-           '[--locales_listfile <locales-listfile-name>] '
-           '[--jinja_paths <paths...>] '
-           '[--service_environment <service_environment>]')
-    return 1
+  parser = argparse.ArgumentParser()
+  parser.add_argument('buildtype')
+  parser.add_argument('version')
+  parser.add_argument('destination')
+  parser.add_argument('zip_path')
+  parser.add_argument('manifest_template')
+  parser.add_argument('webapp_type')
+  parser.add_argument('files', nargs='*', metavar='file', default=[])
+  parser.add_argument('--app_name', metavar='NAME')
+  parser.add_argument('--app_description', metavar='TEXT')
+  parser.add_argument('--app_capabilities',
+                      nargs='*', default=[], metavar='CAPABILITY')
+  parser.add_argument('--appid')
+  parser.add_argument('--locales_listfile', default='', metavar='PATH')
+  parser.add_argument('--jinja_paths', nargs='*', default=[], metavar='PATH')
+  parser.add_argument('--service_environment', default='', metavar='ENV')
+  parser.add_argument('--use_gcd', choices=['0', '1'], default='0')
 
-  arg_type = ''
-  files = []
-  locales_listfile = ''
-  jinja_paths = []
-  app_id = None
-  app_name = None
-  app_description = None
-  app_capabilities = set([])
-  service_environment = ''
-
-  for arg in sys.argv[7:]:
-    if arg in ['--locales_listfile',
-               '--jinja_paths',
-               '--appid',
-               '--app_name',
-               '--app_description',
-               '--app_capabilities',
-               '--service_environment']:
-      arg_type = arg
-    elif arg_type == '--locales_listfile':
-      locales_listfile = arg
-      arg_type = ''
-    elif arg_type == '--jinja_paths':
-      jinja_paths.append(arg)
-    elif arg_type == '--appid':
-      app_id = arg
-      arg_type = ''
-    elif arg_type == '--app_name':
-      app_name = arg
-      arg_type = ''
-    elif arg_type == '--app_description':
-      app_description = arg
-      arg_type = ''
-    elif arg_type == '--app_capabilities':
-      app_capabilities.add(arg)
-    elif arg_type == '--service_environment':
-      service_environment = arg
-      arg_type = ''
-    else:
-      files.append(arg)
-
-  # Load the locales files from the locales_listfile.
-  if not locales_listfile:
-    raise Exception('You must specify a locales_listfile')
-  locales = []
-  with open(locales_listfile) as input:
-    for s in input:
-      locales.append(s.rstrip())
-
-  return buildWebApp(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4],
-                     sys.argv[5], sys.argv[6], app_id, app_name,
-                     app_description, app_capabilities, files, locales,
-                     jinja_paths, service_environment)
+  args = parser.parse_args()
+  args.use_gcd = (args.use_gcd != '0')
+  args.app_capabilities = set(args.app_capabilities)
+  return buildWebApp(**vars(args))
 
 
 if __name__ == '__main__':
diff --git a/remoting/webapp/crd/js/client_session.js b/remoting/webapp/crd/js/client_session.js
index 0bb20cb6..fa6dbfc 100644
--- a/remoting/webapp/crd/js/client_session.js
+++ b/remoting/webapp/crd/js/client_session.js
@@ -50,7 +50,7 @@
   base.inherits(this, base.EventSourceImpl);
 
   /** @private */
-  this.state_ = remoting.ClientSession.State.CREATED;
+  this.state_ = remoting.ClientSession.State.INITIALIZING;
 
   /** @private {!remoting.Error} */
   this.error_ = remoting.Error.none();
@@ -98,20 +98,27 @@
 };
 
 // Note that the positive values in both of these enums are copied directly
-// from chromoting_scriptable_object.h and must be kept in sync. The negative
-// values represent state transitions that occur within the web-app that have
-// no corresponding plugin state transition.
+// from connection_to_host.h and must be kept in sync. Code in
+// chromoting_instance.cc converts the C++ enums into strings that must match
+// the names given here.
+// The negative values represent state transitions that occur within the
+// web-app that have no corresponding plugin state transition.
 /** @enum {number} */
 remoting.ClientSession.State = {
   CONNECTION_CANCELED: -3,  // Connection closed (gracefully) before connecting.
   CONNECTION_DROPPED: -2,  // Succeeded, but subsequently closed with an error.
   CREATED: -1,
   UNKNOWN: 0,
-  CONNECTING: 1,
-  INITIALIZING: 2,
-  CONNECTED: 3,
-  CLOSED: 4,
-  FAILED: 5
+  INITIALIZING: 1,
+  CONNECTING: 2,
+  // We don't currently receive AUTHENTICATED from the host - it comes through
+  // as 'CONNECTING' instead.
+  // TODO(garykac) Update chromoting_instance.cc to send this once we've
+  // shipped a webapp release with support for AUTHENTICATED.
+  AUTHENTICATED: 3,
+  CONNECTED: 4,
+  CLOSED: 5,
+  FAILED: 6
 };
 
 /**
@@ -484,7 +491,8 @@
   var oldState = this.state_;
   this.state_ = newState;
   var state = this.state_;
-  if (oldState == remoting.ClientSession.State.CONNECTING) {
+  if (oldState == remoting.ClientSession.State.CONNECTING ||
+      oldState == remoting.ClientSession.State.AUTHENTICATED) {
     if (this.state_ == remoting.ClientSession.State.CLOSED) {
       state = remoting.ClientSession.State.CONNECTION_CANCELED;
     } else if (this.state_ == remoting.ClientSession.State.FAILED &&
diff --git a/remoting/webapp/crd/js/crd_main.js b/remoting/webapp/crd/js/crd_main.js
index 20ae60aa..e2dc1c5 100644
--- a/remoting/webapp/crd/js/crd_main.js
+++ b/remoting/webapp/crd/js/crd_main.js
@@ -200,8 +200,7 @@
 
 
 remoting.startDesktopRemoting = function() {
-  remoting.app = new remoting.Application(remoting.app_capabilities());
-  var desktop_remoting = new remoting.DesktopRemoting(remoting.app);
+  remoting.app = new remoting.DesktopRemoting(remoting.app_capabilities());
   remoting.app.start();
 };
 
diff --git a/remoting/webapp/crd/js/desktop_remoting.js b/remoting/webapp/crd/js/desktop_remoting.js
index fff4452..6fd674f 100644
--- a/remoting/webapp/crd/js/desktop_remoting.js
+++ b/remoting/webapp/crd/js/desktop_remoting.js
@@ -14,20 +14,13 @@
 var remoting = remoting || {};
 
 /**
- * @param {remoting.Application} app The main app that owns this delegate.
+ * @param {Array<string>} appCapabilities Array of application capabilities.
  * @constructor
- * @implements {remoting.Application.Delegate}
+ * @implements {remoting.ApplicationInterface}
+ * @extends {remoting.Application}
  */
-remoting.DesktopRemoting = function(app) {
-  /**
-   * TODO(garykac): Remove this reference to the Application. It's only
-   * needed to get the current mode when reporting errors. So we should be
-   * able to refactor and remove this reference cycle.
-   *
-   * @private {remoting.Application}
-   */
-  this.app_ = app;
-  app.setDelegate(this);
+remoting.DesktopRemoting = function(appCapabilities) {
+  base.inherits(this, remoting.Application, appCapabilities);
 
   /**
    * Whether to refresh the JID and retry the connection if the current JID
@@ -39,17 +32,28 @@
 
   /** @private {remoting.DesktopConnectedView} */
   this.connectedView_ = null;
-
-  remoting.desktopDelegateForTesting = this;
 };
 
 /**
- * Initialize the application and register all event handlers. After this
- * is called, the app is running and waiting for user events.
- *
- * @return {void} Nothing.
+ * @return {string} Application product name to be used in UI.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.DesktopRemoting.prototype.init = function() {
+remoting.DesktopRemoting.prototype.getApplicationName = function() {
+  return chrome.i18n.getMessage(/*i18n-content*/'PRODUCT_NAME');
+};
+
+/**
+ * @param {!remoting.Error} error The failure reason.
+ * @override {remoting.ApplicationInterface}
+ */
+remoting.DesktopRemoting.prototype.signInFailed_ = function(error) {
+  remoting.showErrorMessage(error);
+};
+
+/**
+ * @override {remoting.ApplicationInterface}
+ */
+remoting.DesktopRemoting.prototype.initApplication_ = function() {
   remoting.initElementEventHandlers();
 
   if (base.isAppsV2()) {
@@ -122,15 +126,10 @@
 };
 
 /**
- * Start the application. Once start() is called, the delegate can assume that
- * the user has consented to all permissions specified in the manifest.
- *
- * @param {remoting.SessionConnector} connector
- * @param {string} token An OAuth access token. The delegate should not cache
- *     this token, but can assume that it will remain valid during application
- *     start-up.
+ * @param {string} token An OAuth access token.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.DesktopRemoting.prototype.start = function(connector, token) {
+remoting.DesktopRemoting.prototype.startApplication_ = function(token) {
   remoting.identity.getEmail().then(
       function(/** string */ email) {
         document.getElementById('current-email').innerText = email;
@@ -139,35 +138,23 @@
       });
 };
 
-/**
- * Report an authentication error to the user. This is called in lieu of start()
- * if the user cannot be authenticated or if they decline the app permissions.
- *
- * @param {!remoting.Error} error The failure reason.
- */
-remoting.DesktopRemoting.prototype.signInFailed = function(error) {
-  remoting.showErrorMessage(error);
+/** @override {remoting.ApplicationInterface} */
+remoting.DesktopRemoting.prototype.exitApplication_ = function() {
+  this.closeMainWindow_();
 };
 
 /**
- * @return {string} Application product name to be used in UI.
- */
-remoting.DesktopRemoting.prototype.getApplicationName = function() {
-  return chrome.i18n.getMessage(/*i18n-content*/'PRODUCT_NAME');
-};
-
-/**
- * Called when a new session has been connected.
- *
  * @param {remoting.ConnectionInfo} connectionInfo
- * @return {void} Nothing.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.DesktopRemoting.prototype.handleConnected = function(connectionInfo) {
+remoting.DesktopRemoting.prototype.onConnected_ = function(connectionInfo) {
+  this.initSession_(connectionInfo);
+
   // Set the text on the buttons shown under the error message so that they are
   // easy to understand in the case where a successful connection failed, as
   // opposed to the case where a connection never succeeded.
   // TODO(garykac): Investigate to see if these need to be reverted to their
-  // original values in the onDisconnected method.
+  // original values in the onDisconnected_ method.
   var button1 = document.getElementById('client-reconnect-button');
   l10n.localizeElementFromTag(button1, /*i18n-content*/'RECONNECT');
   button1.removeAttribute('autofocus');
@@ -197,10 +184,10 @@
   var sessionConnector = remoting.app.getSessionConnector();
   if (connectionInfo.mode() === remoting.DesktopConnectedView.Mode.ME2ME) {
     if (remoting.app.hasCapability(remoting.ClientSession.Capability.CAST)) {
-      sessionConnector.registerProtocolExtension(
+      this.sessionConnector_.registerProtocolExtension(
           new remoting.CastExtensionHandler());
     }
-    sessionConnector.registerProtocolExtension(
+    this.sessionConnector_.registerProtocolExtension(
         new remoting.GnubbyAuthHandler());
   }
   if (connectionInfo.session().hasCapability(
@@ -211,12 +198,13 @@
   }
 
   if (remoting.pairingRequested) {
+    var that = this;
     /**
      * @param {string} clientId
      * @param {string} sharedSecret
      */
     var onPairingComplete = function(clientId, sharedSecret) {
-      var connector = remoting.app.getSessionConnector();
+      var connector = that.sessionConnector_;
       var host = remoting.hostList.getHostForId(connector.getHostId());
       host.options.pairingInfo.clientId = clientId;
       host.options.pairingInfo.sharedSecret = sharedSecret;
@@ -244,11 +232,9 @@
 };
 
 /**
- * Called when the current session has been disconnected.
- *
- * @return {void} Nothing.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.DesktopRemoting.prototype.handleDisconnected = function() {
+remoting.DesktopRemoting.prototype.onDisconnected_ = function() {
   var mode = this.connectedView_.getMode();
   if (mode === remoting.DesktopConnectedView.Mode.IT2ME) {
     remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_IT2ME);
@@ -260,27 +246,24 @@
 };
 
 /**
- * Called when the current session's connection has failed.
- *
- * @param {remoting.SessionConnector} connector
  * @param {!remoting.Error} error
- * @return {void} Nothing.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.DesktopRemoting.prototype.handleConnectionFailed = function(
-    connector, error) {
+remoting.DesktopRemoting.prototype.onConnectionFailed_ = function(error) {
   var that = this;
   var onHostListRefresh = function(/** boolean */ success) {
     if (success) {
+      var connector = that.sessionConnector_;
       var host = remoting.hostList.getHostForId(connector.getHostId());
       if (host) {
         connector.retryConnectMe2Me(host);
         return;
       }
     }
-    that.handleError(error);
+    that.onError_(error);
   };
 
-  var mode = this.app_.getSessionConnector().getConnectionMode();
+  var mode = this.sessionConnector_.getConnectionMode();
   if (error.hasTag(remoting.Error.Tag.HOST_IS_OFFLINE) &&
       mode === remoting.DesktopConnectedView.Mode.ME2ME &&
       this.refreshHostJidIfOffline_) {
@@ -289,20 +272,18 @@
     // The plugin will be re-created when the host finished refreshing
     remoting.hostList.refresh(onHostListRefresh);
   } else {
-    this.handleError(error);
+    this.onError_(error);
   }
 };
 
 /**
- * Called when an error needs to be displayed to the user.
- *
  * @param {!remoting.Error} error The error to be localized and displayed.
- * @return {void} Nothing.
+ * @override {remoting.ApplicationInterface}
  */
-remoting.DesktopRemoting.prototype.handleError = function(error) {
+remoting.DesktopRemoting.prototype.onError_ = function(error) {
   console.error('Connection failed: ' + error.toString());
   var mode = this.connectedView_ ? this.connectedView_.getMode()
-      : this.app_.getSessionConnector().getConnectionMode();
+      : this.sessionConnector_.getConnectionMode();
   base.dispose(this.connectedView_);
   this.connectedView_ = null;
 
@@ -326,12 +307,6 @@
 };
 
 /**
- * No cleanup required for desktop remoting.
- */
-remoting.DesktopRemoting.prototype.handleExit = function() {
-};
-
-/**
  * Determine whether or not the app is running in a window.
  * @param {function(boolean):void} callback Callback to receive whether or not
  *     the current tab is running in windowed mode.
@@ -364,7 +339,7 @@
  * @private
  */
 remoting.DesktopRemoting.prototype.promptClose_ = function() {
-  var sessionConnector = remoting.app.getSessionConnector();
+  var sessionConnector = this.sessionConnector_;
   if (sessionConnector &&
       sessionConnector.getConnectionMode() ==
           remoting.DesktopConnectedView.Mode.IT2ME) {
@@ -385,9 +360,3 @@
 remoting.DesktopRemoting.prototype.getConnectedViewForTesting = function() {
   return this.connectedView_;
 };
-
-/**
- * Global instance of remoting.DesktopRemoting used for testing.
- * @type {remoting.DesktopRemoting}
- */
-remoting.desktopDelegateForTesting = null;
diff --git a/remoting/webapp/crd/js/dns_blackhole_checker.js b/remoting/webapp/crd/js/dns_blackhole_checker.js
index 95b342c..77f588c 100644
--- a/remoting/webapp/crd/js/dns_blackhole_checker.js
+++ b/remoting/webapp/crd/js/dns_blackhole_checker.js
@@ -110,10 +110,7 @@
 };
 
 remoting.DnsBlackholeChecker.prototype.dispose = function() {
-  if (this.xhr_) {
-    this.xhr_.abort();
-    this.xhr_ = null;
-  }
+  this.xhr_ = null;
   base.dispose(this.signalStrategy_);
   this.setState_(remoting.SignalStrategy.State.CLOSED);
 };
@@ -157,6 +154,12 @@
  * @private
  */
 remoting.DnsBlackholeChecker.prototype.onHttpRequestDone_ = function(response) {
+  if (this.xhr_ == null) {
+    // This happens when the dispose() method is called while a
+    // request is pending.
+    return;
+  }
+
   this.xhr_ = null;
   if (response.status >= 200 && response.status <= 299) {
     console.log("DNS blackhole check succeeded.");
diff --git a/remoting/webapp/crd/js/host_list_api_impl.js b/remoting/webapp/crd/js/host_list_api_impl.js
index 52f9377e..757c439 100644
--- a/remoting/webapp/crd/js/host_list_api_impl.js
+++ b/remoting/webapp/crd/js/host_list_api_impl.js
@@ -53,23 +53,18 @@
  */
 remoting.HostListApiImpl.prototype.put =
     function(hostId, hostName, hostPublicKey, onDone, onError) {
-  /** @param {string} token */
-  var onToken = function(token) {
-    var newHostDetails = {
+  new remoting.Xhr({
+    method: 'PUT',
+    url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts/' + hostId,
+    jsonContent: {
       'data': {
         'hostId': hostId,
         'hostName': hostName,
         'publicKey': hostPublicKey
       }
-    };
-    new remoting.Xhr({
-      method: 'PUT',
-      url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts/' + hostId,
-      jsonContent: newHostDetails,
-      oauthToken: token
-    }).start().then(remoting.Xhr.defaultResponse(onDone, onError));
-  };
-  remoting.identity.getToken().then(onToken, remoting.Error.handler(onError));
+    },
+    useIdentity: true
+  }).start().then(remoting.HostListApiImpl.defaultResponse_(onDone, onError));
 };
 
 /**
@@ -80,16 +75,12 @@
  * @param {string} hostId
  */
 remoting.HostListApiImpl.prototype.remove = function(hostId, onDone, onError) {
-  /** @param {string} token */
-  var onToken = function(token) {
-    new remoting.Xhr({
-      method: 'DELETE',
-      url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts/' + hostId,
-      oauthToken: token
-    }).start().then(remoting.Xhr.defaultResponse(
-        onDone, onError, [remoting.Error.Tag.NOT_FOUND]));
-  };
-  remoting.identity.getToken().then(onToken, remoting.Error.handler(onError));
+  new remoting.Xhr({
+    method: 'DELETE',
+    url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts/' + hostId,
+    useIdentity: true
+  }).start().then(remoting.HostListApiImpl.defaultResponse_(
+      onDone, onError, [remoting.Error.Tag.NOT_FOUND]));
 };
 
 /**
@@ -135,6 +126,35 @@
   }
 };
 
+/**
+ * Generic success/failure response proxy.
+ *
+ * @param {function():void} onDone
+ * @param {function(!remoting.Error):void} onError
+ * @param {Array<remoting.Error.Tag>=} opt_ignoreErrors
+ * @return {function(!remoting.Xhr.Response):void}
+ * @private
+ */
+remoting.HostListApiImpl.defaultResponse_ = function(
+    onDone, onError, opt_ignoreErrors) {
+  /** @param {!remoting.Xhr.Response} response */
+  var result = function(response) {
+    var error = remoting.Error.fromHttpStatus(response.status);
+    if (error.isNone()) {
+      onDone();
+      return;
+    }
+
+    if (opt_ignoreErrors && error.hasTag.apply(error, opt_ignoreErrors)) {
+      onDone();
+      return;
+    }
+
+    onError(error);
+  };
+  return result;
+};
+
 /** @type {remoting.HostListApi} */
 remoting.hostListApi = new remoting.HostListApiImpl();
 
diff --git a/remoting/webapp/crd/js/log_to_server.js b/remoting/webapp/crd/js/log_to_server.js
index 20c9b1ce..dfe8cea 100644
--- a/remoting/webapp/crd/js/log_to_server.js
+++ b/remoting/webapp/crd/js/log_to_server.js
@@ -109,6 +109,7 @@
 remoting.LogToServer.isStartOfSession_ = function(state) {
   return ((state == remoting.ClientSession.State.CONNECTING) ||
       (state == remoting.ClientSession.State.INITIALIZING) ||
+      (state == remoting.ClientSession.State.AUTHENTICATED) ||
       (state == remoting.ClientSession.State.CONNECTED));
 };
 
diff --git a/remoting/webapp/crd/js/plugin_settings.js b/remoting/webapp/crd/js/plugin_settings.js
index 2822ab0..bbc4df8 100644
--- a/remoting/webapp/crd/js/plugin_settings.js
+++ b/remoting/webapp/crd/js/plugin_settings.js
@@ -41,7 +41,7 @@
  */
 remoting.Settings.prototype.OAUTH2_REDIRECT_URL = function() {
   return 'OAUTH2_REDIRECT_URL';
-}
+};
 
 /** @type {string} Base URL for the App Remoting API. */
 remoting.Settings.prototype.APP_REMOTING_API_BASE_URL =
@@ -67,7 +67,7 @@
 remoting.Settings.prototype.XMPP_SERVER = 'XMPP_SERVER';
 /** @type {boolean} Whether to use TLS on connections to the XMPP server. */
 remoting.Settings.prototype.XMPP_SERVER_USE_TLS =
-    Boolean('XMPP_SERVER_USE_TLS');
+    !!'XMPP_SERVER_USE_TLS';
 
 // Third party authentication settings.
 /** @type {string} The third party auth redirect URI. */
@@ -76,3 +76,6 @@
 
 // 'native', 'nacl' or 'pnacl'.
 remoting.Settings.prototype.CLIENT_PLUGIN_TYPE = 'CLIENT_PLUGIN_TYPE';
+
+/** @const {boolean} If true, use GCD instead of Chromoting registry. */
+remoting.Settings.prototype.USE_GCD = !!'USE_GCD';
diff --git a/remoting/webapp/crd/js/server_log_entry.js b/remoting/webapp/crd/js/server_log_entry.js
index 3f1cc8b7..5a33e80 100644
--- a/remoting/webapp/crd/js/server_log_entry.js
+++ b/remoting/webapp/crd/js/server_log_entry.js
@@ -59,12 +59,12 @@
   switch(state) {
     case remoting.ClientSession.State.UNKNOWN:
       return 'unknown';
-    case remoting.ClientSession.State.CREATED:
-      return 'created';
-    case remoting.ClientSession.State.CONNECTING:
-      return 'connecting';
     case remoting.ClientSession.State.INITIALIZING:
       return 'initializing';
+    case remoting.ClientSession.State.CONNECTING:
+      return 'connecting';
+    case remoting.ClientSession.State.AUTHENTICATED:
+      return 'authenticated';
     case remoting.ClientSession.State.CONNECTED:
       return 'connected';
     case remoting.ClientSession.State.CLOSED:
diff --git a/remoting/webapp/crd/js/session_connector_impl.js b/remoting/webapp/crd/js/session_connector_impl.js
index 0d9ea5b8..bbb89937 100644
--- a/remoting/webapp/crd/js/session_connector_impl.js
+++ b/remoting/webapp/crd/js/session_connector_impl.js
@@ -491,10 +491,6 @@
       this.initProtocolExtensions_();
       break;
 
-    case remoting.ClientSession.State.CREATED:
-      console.log('Created plugin');
-      break;
-
     case remoting.ClientSession.State.CONNECTING:
       remoting.identity.getEmail().then(
           function(/** string */ email) {
@@ -502,6 +498,10 @@
           });
       break;
 
+    case remoting.ClientSession.State.AUTHENTICATED:
+      console.log('Connection authenticated');
+      break;
+
     case remoting.ClientSession.State.INITIALIZING:
       console.log('Initializing connection');
       break;
diff --git a/remoting/webapp/crd/js/window_frame.js b/remoting/webapp/crd/js/window_frame.js
index 2b54ce6d..28eb2576 100644
--- a/remoting/webapp/crd/js/window_frame.js
+++ b/remoting/webapp/crd/js/window_frame.js
@@ -53,7 +53,7 @@
     { cls: 'window-maximize-restore',
       fn: this.maximizeOrRestoreWindow_.bind(this) },
     { cls: 'window-minimize', fn: this.minimizeWindow_.bind(this) },
-    { cls: 'window-close', fn: remoting.app.exit.bind(remoting.app) },
+    { cls: 'window-close', fn: remoting.app.quit.bind(remoting.app) },
     { cls: 'window-controls-stub', fn: this.toggleWindowControls_.bind(this) }
   ];
   for (var i = 0; i < handlers.length; ++i) {
diff --git a/remoting/webapp/crd/js/xhr.js b/remoting/webapp/crd/js/xhr.js
index cdefb0aa..cc50b277 100644
--- a/remoting/webapp/crd/js/xhr.js
+++ b/remoting/webapp/crd/js/xhr.js
@@ -17,13 +17,7 @@
  * @param {remoting.Xhr.Params} params
  */
 remoting.Xhr = function(params) {
-  /** @private @const {!XMLHttpRequest} */
-  this.nativeXhr_ = new XMLHttpRequest();
-  this.nativeXhr_.onreadystatechange = this.onReadyStateChange_.bind(this);
-  this.nativeXhr_.withCredentials = params.withCredentials || false;
-
-  /** @private @const */
-  this.responseType_ = params.responseType || remoting.Xhr.ResponseType.TEXT;
+  remoting.Xhr.checkParams_(params);
 
   // Apply URL parameters.
   var url = params.url;
@@ -35,92 +29,83 @@
         remoting.Xhr.removeNullFields_(params.urlParams));
   }
   if (parameterString) {
-    base.debug.assert(url.indexOf('?') == -1);
     url += '?' + parameterString;
   }
 
-  // Check that the content spec is consistent.
-  if ((Number(params.textContent !== undefined) +
-       Number(params.formContent !== undefined) +
-       Number(params.jsonContent !== undefined)) > 1) {
-    throw new Error(
-        'may only specify one of textContent, formContent, and jsonContent');
-  }
-
   // Prepare the build modified headers.
-  var headers = remoting.Xhr.removeNullFields_(params.headers);
+  /** @const */
+  this.headers_ = remoting.Xhr.removeNullFields_(params.headers);
 
   // Convert the content fields to a single text content variable.
   /** @private {?string} */
   this.content_ = null;
   if (params.textContent !== undefined) {
+    this.maybeSetContentType_('text/plain');
     this.content_ = params.textContent;
   } else if (params.formContent !== undefined) {
-    if (!('Content-type' in headers)) {
-      headers['Content-type'] = 'application/x-www-form-urlencoded';
-    }
+    this.maybeSetContentType_('application/x-www-form-urlencoded');
     this.content_ = remoting.Xhr.urlencodeParamHash(params.formContent);
   } else if (params.jsonContent !== undefined) {
-    if (!('Content-type' in headers)) {
-      headers['Content-type'] = 'application/json; charset=UTF-8';
-    }
+    this.maybeSetContentType_('application/json');
     this.content_ = JSON.stringify(params.jsonContent);
   }
 
   // Apply the oauthToken field.
   if (params.oauthToken !== undefined) {
-    base.debug.assert(!('Authorization' in headers));
-    headers['Authorization'] = 'Bearer ' + params.oauthToken;
+    this.setAuthToken_(params.oauthToken);
   }
 
-  this.nativeXhr_.open(params.method, url, true);
-  for (var key in headers) {
-    this.nativeXhr_.setRequestHeader(key, headers[key]);
+  /** @private @const {boolean} */
+  this.acceptJson_ = params.acceptJson || false;
+  if (this.acceptJson_) {
+    this.maybeSetHeader_('Accept', 'application/json');
   }
 
+  // Apply useIdentity field.
+  /** @const {boolean} */
+  this.useIdentity_ = params.useIdentity || false;
+
+  /** @private @const {!XMLHttpRequest} */
+  this.nativeXhr_ = new XMLHttpRequest();
+  this.nativeXhr_.onreadystatechange = this.onReadyStateChange_.bind(this);
+  this.nativeXhr_.withCredentials = params.withCredentials || false;
+  this.nativeXhr_.open(params.method, url, true);
+
   /** @private {base.Deferred<!remoting.Xhr.Response>} */
   this.deferred_ = null;
 };
 
 /**
- * @enum {string}
- */
-remoting.Xhr.ResponseType = {
-  TEXT: 'TEXT',  // Request a plain text response (default).
-  JSON: 'JSON',  // Request a JSON response.
-  NONE: 'NONE'   // Don't request any response.
-};
-
-/**
- * Parameters for the 'start' function.
+ * Parameters for the 'start' function.  Unless otherwise noted, all
+ * parameters are optional.
  *
- * method: The HTTP method to use.
+ * method: (required) The HTTP method to use.
  *
- * url: The URL to request.
+ * url: (required) The URL to request.
  *
- * urlParams: (optional) Parameters to be appended to the URL.
- *     Null-valued parameters are omitted.
+ * urlParams: Parameters to be appended to the URL.  Null-valued
+ *     parameters are omitted.
  *
- * textContent: (optional) Text to be sent as the request body.
+ * textContent: Text to be sent as the request body.
  *
- * formContent: (optional) Data to be URL-encoded and sent as the
- *     request body.  Causes Content-type header to be set
- *     appropriately.
+ * formContent: Data to be URL-encoded and sent as the request body.
+ *     Causes Content-type header to be set appropriately.
  *
- * jsonContent: (optional) Data to be JSON-encoded and sent as the
- *     request body.  Causes Content-type header to be set
- *     appropriately.
+ * jsonContent: Data to be JSON-encoded and sent as the request body.
+ *     Causes Content-type header to be set appropriately.
  *
- * headers: (optional) Additional request headers to be sent.
- *     Null-valued headers are omitted.
+ * headers: Additional request headers to be sent.  Null-valued
+ *     headers are omitted.
  *
- * withCredentials: (optional) Value of the XHR's withCredentials field.
+ * withCredentials: Value of the XHR's withCredentials field.
  *
- * oauthToken: (optional) An OAuth2 token used to construct an
- *     Authentication header.
+ * oauthToken: An OAuth2 token used to construct an Authentication
+ *     header.
  *
- * responseType: (optional) Request a response of a specific
- *    type. Default: TEXT.
+ * useIdentity: Use identity API to get an OAuth2 token.
+ *
+ * acceptJson: If true, send an Accept header indicating that a JSON
+ *     response is expected.
  *
  * @typedef {{
  *   method: string,
@@ -132,25 +117,18 @@
  *   headers:(Object<string,?string>|undefined),
  *   withCredentials:(boolean|undefined),
  *   oauthToken:(string|undefined),
- *   responseType:(remoting.Xhr.ResponseType|undefined)
+ *   useIdentity:(boolean|undefined),
+ *   acceptJson:(boolean|undefined)
  * }}
  */
 remoting.Xhr.Params;
 
 /**
- * Aborts the HTTP request.  Does nothing is the request has finished
- * already.
- */
-remoting.Xhr.prototype.abort = function() {
-  this.nativeXhr_.abort();
-};
-
-/**
  * Starts and HTTP request and gets a promise that is resolved when
  * the request completes.
  *
- * Any error that prevents receiving an HTTP status
- * code causes this promise to be rejected.
+ * Any error that prevents sending the request causes the promise to
+ * be rejected.
  *
  * NOTE: Calling this method more than once will return the same
  * promise and not start a new request, despite what the name
@@ -160,22 +138,117 @@
  */
 remoting.Xhr.prototype.start = function() {
   if (this.deferred_ == null) {
-    var xhr = this.nativeXhr_;
-    xhr.send(this.content_);
-    this.content_ = null;  // for gc
     this.deferred_ = new base.Deferred();
+
+    // Send the XHR, possibly after getting an OAuth token.
+    var that = this;
+    if (this.useIdentity_) {
+      remoting.identity.getToken().then(function(token) {
+        base.debug.assert(that.nativeXhr_.readyState == 1);
+        that.setAuthToken_(token);
+        that.sendXhr_();
+      }).catch(function(error) {
+        that.deferred_.reject(error);
+      });
+    } else {
+      this.sendXhr_();
+    }
   }
   return this.deferred_.promise();
 };
 
 /**
+ * @param {remoting.Xhr.Params} params
+ * @throws {Error} if params are invalid
+ */
+remoting.Xhr.checkParams_ = function(params) {
+  if (params.urlParams) {
+    if (params.url.indexOf('?') != -1) {
+      throw new Error('URL may not contain "?" when urlParams is set');
+    }
+    if (params.url.indexOf('#') != -1) {
+      throw new Error('URL may not contain "#" when urlParams is set');
+    }
+  }
+
+  if ((Number(params.textContent !== undefined) +
+       Number(params.formContent !== undefined) +
+       Number(params.jsonContent !== undefined)) > 1) {
+    throw new Error(
+        'may only specify one of textContent, formContent, and jsonContent');
+  }
+
+  if (params.useIdentity && params.oauthToken !== undefined) {
+    throw new Error('may not specify both useIdentity and oauthToken');
+  }
+
+  if ((params.useIdentity || params.oauthToken !== undefined) &&
+      params.headers &&
+      params.headers['Authorization'] != null) {
+    throw new Error(
+        'may not specify useIdentity or oauthToken ' +
+        'with an Authorization header');
+  }
+};
+
+/**
+ * @param {string} token
+ * @private
+ */
+remoting.Xhr.prototype.setAuthToken_ = function(token) {
+  this.setHeader_('Authorization', 'Bearer ' + token);
+};
+
+/**
+ * @param {string} type
+ * @private
+ */
+remoting.Xhr.prototype.maybeSetContentType_ = function(type) {
+  this.maybeSetHeader_('Content-type', type + '; charset=UTF-8');
+};
+
+/**
+ * @param {string} key
+ * @param {string} value
+ * @private
+ */
+remoting.Xhr.prototype.setHeader_ = function(key, value) {
+  var wasSet = this.maybeSetHeader_(key, value);
+  base.debug.assert(wasSet);
+};
+
+/**
+ * @param {string} key
+ * @param {string} value
+ * @return {boolean}
+ * @private
+ */
+remoting.Xhr.prototype.maybeSetHeader_ = function(key, value) {
+  if (!(key in this.headers_)) {
+    this.headers_[key] = value;
+    return true;
+  }
+  return false;
+};
+
+/** @private */
+remoting.Xhr.prototype.sendXhr_ = function() {
+  for (var key in this.headers_) {
+    this.nativeXhr_.setRequestHeader(key, this.headers_[key]);
+  }
+  this.nativeXhr_.send(this.content_);
+  this.content_ = null;  // for gc
+};
+
+/**
  * @private
  */
 remoting.Xhr.prototype.onReadyStateChange_ = function() {
   var xhr = this.nativeXhr_;
   if (xhr.readyState == 4) {
     // See comments at remoting.Xhr.Response.
-    this.deferred_.resolve(new remoting.Xhr.Response(xhr, this.responseType_));
+    this.deferred_.resolve(new remoting.Xhr.Response(
+        xhr, this.acceptJson_));
   }
 };
 
@@ -189,11 +262,11 @@
  *
  * @constructor
  * @param {!XMLHttpRequest} xhr
- * @param {remoting.Xhr.ResponseType} type
+ * @param {boolean} allowJson
  */
-remoting.Xhr.Response = function(xhr, type) {
+remoting.Xhr.Response = function(xhr, allowJson) {
   /** @private @const */
-  this.type_ = type;
+  this.allowJson_ = allowJson;
 
   /**
    * The HTTP status code.
@@ -215,6 +288,9 @@
 
   /** @private {string} */
   this.text_ = xhr.responseText || '';
+
+  /** @private {*|undefined}  */
+  this.json_ = undefined;
 };
 
 /**
@@ -225,11 +301,16 @@
 };
 
 /**
+ * Get the JSON content of the response.  Requires acceptJson to have
+ * been true in the request.
  * @return {*} The parsed JSON content of the response.
  */
 remoting.Xhr.Response.prototype.getJson = function() {
-  base.debug.assert(this.type_ == remoting.Xhr.ResponseType.JSON);
-  return JSON.parse(this.text_);
+  base.debug.assert(this.allowJson_);
+  if (this.json_ === undefined) {
+    this.json_ = JSON.parse(this.text_);
+  }
+  return this.json_;
 };
 
 /**
@@ -271,33 +352,3 @@
   }
   return '';
 };
-
-/**
- * Generic success/failure response proxy.
- *
- * TODO(jrw): Stop using this and move default error handling directly
- * into Xhr class.
- *
- * @param {function():void} onDone
- * @param {function(!remoting.Error):void} onError
- * @param {Array<remoting.Error.Tag>=} opt_ignoreErrors
- * @return {function(!remoting.Xhr.Response):void}
- */
-remoting.Xhr.defaultResponse = function(onDone, onError, opt_ignoreErrors) {
-  /** @param {!remoting.Xhr.Response} response */
-  var result = function(response) {
-    var error = remoting.Error.fromHttpStatus(response.status);
-    if (error.isNone()) {
-      onDone();
-      return;
-    }
-
-    if (opt_ignoreErrors && error.hasTag.apply(error, opt_ignoreErrors)) {
-      onDone();
-      return;
-    }
-
-    onError(error);
-  };
-  return result;
-};
diff --git a/remoting/webapp/crd/js/xhr_unittest.js b/remoting/webapp/crd/js/xhr_unittest.js
index b02987e..c8e96e0f 100644
--- a/remoting/webapp/crd/js/xhr_unittest.js
+++ b/remoting/webapp/crd/js/xhr_unittest.js
@@ -10,16 +10,27 @@
 
 'use strict';
 
+/** @type {sinon.FakeXhrCtrl} */
+var fakeXhrCtrl;
+
 /** @type {sinon.FakeXhr} */
 var fakeXhr;
 
 QUnit.module('xhr', {
   beforeEach: function() {
     fakeXhr = null;
-    sinon.useFakeXMLHttpRequest().onCreate =
+    fakeXhrCtrl = sinon.useFakeXMLHttpRequest();
+    fakeXhrCtrl.onCreate =
         function(/** sinon.FakeXhr */ xhr) {
           fakeXhr = xhr;
         };
+    remoting.identity = new remoting.Identity();
+    chromeMocks.activate(['identity']);
+    chromeMocks.identity.mock$setToken('my_token');
+  },
+  afterEach: function() {
+    chromeMocks.restore();
+    remoting.identity = null;
   }
 });
 
@@ -38,14 +49,140 @@
       'k1=v1&k2=v2');
 });
 
-QUnit.test('basic GET', function(assert) {
+//
+// Test for what happens when the parameters are specified
+// incorrectly.
+//
+
+QUnit.test('invalid *content parameters', function(assert) {
+  assert.throws(function() {
+    new remoting.Xhr({
+      method: 'POST',
+      url: 'http://foo.com',
+      jsonContent: {},
+      formContent: {}
+    });
+  });
+
+  assert.throws(function() {
+    new remoting.Xhr({
+      method: 'POST',
+      url: 'http://foo.com',
+      textContent: '',
+      formContent: {}
+    });
+  });
+
+  assert.throws(function() {
+    new remoting.Xhr({
+      method: 'POST',
+      url: 'http://foo.com',
+      textContent: '',
+      jsonContent: {}
+    });
+  });
+});
+
+
+QUnit.test('invalid URL parameters', function(assert) {
+  assert.throws(function() {
+    new remoting.Xhr({
+      method: 'POST',
+      url: 'http://foo.com?',
+      urlParams: {}
+    });
+  });
+
+  assert.throws(function() {
+    new remoting.Xhr({
+      method: 'POST',
+      url: 'http://foo.com#',
+      urlParams: {}
+    });
+  });
+});
+
+QUnit.test('invalid auth parameters', function(assert) {
+  assert.throws(function() {
+    new remoting.Xhr({
+      method: 'POST',
+      url: 'http://foo.com',
+      useIdentity: false,
+      oauthToken: '',
+      headers: {
+        'Authorization': ''
+      }
+    });
+  });
+
+  assert.throws(function() {
+    new remoting.Xhr({
+      method: 'POST',
+      url: 'http://foo.com',
+      useIdentity: true,
+      headers: {
+        'Authorization': ''
+      }
+    });
+  });
+
+  assert.throws(function() {
+    new remoting.Xhr({
+      method: 'POST',
+      url: 'http://foo.com',
+      useIdentity: true,
+      oauthToken: '',
+      headers: {}
+    });
+  });
+});
+
+QUnit.test('invalid auth parameters', function(assert) {
+  assert.throws(function() {
+    new remoting.Xhr({
+      method: 'POST',
+      url: 'http://foo.com',
+      useIdentity: false,
+      oauthToken: '',
+      headers: {
+        'Authorization': ''
+      }
+    });
+  });
+
+  assert.throws(function() {
+    new remoting.Xhr({
+      method: 'POST',
+      url: 'http://foo.com',
+      useIdentity: true,
+      headers: {
+        'Authorization': ''
+      }
+    });
+  });
+
+  assert.throws(function() {
+    new remoting.Xhr({
+      method: 'POST',
+      url: 'http://foo.com',
+      useIdentity: true,
+      oauthToken: '',
+      headers: {}
+    });
+  });
+});
+
+//
+// The typical case.
+//
+
+QUnit.test('successful GET', function(assert) {
   var promise = new remoting.Xhr({
     method: 'GET',
-    url: 'http://foo.com',
-    responseType: remoting.Xhr.ResponseType.TEXT
+    url: 'http://foo.com'
   }).start().then(function(response) {
-      assert.equal(response.status, 200);
-      assert.equal(response.getText(), 'body');
+    assert.equal(response.status, 200);
+    assert.equal(response.getText(), 'body');
   });
   assert.equal(fakeXhr.method, 'GET');
   assert.equal(fakeXhr.url, 'http://foo.com');
@@ -56,96 +193,105 @@
   return promise;
 });
 
-QUnit.test('GET with param string', function(assert) {
+//
+// Tests for the effect of acceptJson.
+//
+
+QUnit.test('acceptJson required', function(assert) {
+  var promise = new remoting.Xhr({
+    method: 'GET',
+    url: 'http://foo.com'
+  }).start().then(function(response) {
+    assert.throws(response.getJson);
+  });
+  fakeXhr.respond(200, {}, '{}');
+  return promise;
+});
+
+QUnit.test('JSON response', function(assert) {
+  var responseJson = {
+      'myJsonData': [true]
+  };
+  var responseText = JSON.stringify(responseJson);
   var promise = new remoting.Xhr({
     method: 'GET',
     url: 'http://foo.com',
-    responseType: remoting.Xhr.ResponseType.TEXT,
-    urlParams: 'the_param_string'
+    acceptJson: true
   }).start().then(function(response) {
-    assert.equal(response.status, 200);
-    assert.equal(response.getText(), 'body');
+    // Calling getText is still OK even when a JSON response is
+    // requested.
+    assert.equal(
+        response.getText(),
+        responseText);
+    // Check that getJson works as advertised.
+    assert.deepEqual(
+        response.getJson(),
+        responseJson);
+    // Calling getJson multiple times doesn't re-parse the response.
+    assert.strictEqual(
+        response.getJson(),
+        response.getJson());
   });
-  assert.equal(fakeXhr.method, 'GET');
-  assert.equal(fakeXhr.url, 'http://foo.com?the_param_string');
-  assert.equal(fakeXhr.withCredentials, false);
-  assert.equal(fakeXhr.requestBody, null);
-  assert.ok(!('Content-type' in fakeXhr.requestHeaders));
-  fakeXhr.respond(200, {}, 'body');
+  fakeXhr.respond(200, {}, responseText);
   return promise;
 });
 
+//
+// Tests for various parameters that modify the HTTP request.
+//
+
+QUnit.test('GET with param string', function(assert) {
+  new remoting.Xhr({
+    method: 'GET',
+    url: 'http://foo.com',
+    urlParams: 'the_param_string'
+  }).start();
+  assert.equal(fakeXhr.url, 'http://foo.com?the_param_string');
+});
+
 QUnit.test('GET with param object', function(assert) {
-  var promise = new remoting.Xhr({
+  new remoting.Xhr({
     method: 'GET',
     url: 'http://foo.com',
-    responseType: remoting.Xhr.ResponseType.TEXT,
     urlParams: {'a': 'b', 'c': 'd'}
-  }).start().then(function(response) {
-    assert.equal(response.status, 200);
-    assert.equal(response.getText(), 'body');
-  });
-  assert.equal(fakeXhr.method, 'GET');
+  }).start();
   assert.equal(fakeXhr.url, 'http://foo.com?a=b&c=d');
-  assert.equal(fakeXhr.withCredentials, false);
-  assert.equal(fakeXhr.requestBody, null);
-  assert.ok(!('Content-type' in fakeXhr.requestHeaders));
-  fakeXhr.respond(200, {}, 'body');
-  return promise;
 });
 
 QUnit.test('GET with headers', function(assert) {
-  var promise = new remoting.Xhr({
+  new remoting.Xhr({
     method: 'GET',
     url: 'http://foo.com',
-    responseType: remoting.Xhr.ResponseType.TEXT,
     headers: {'Header1': 'headerValue1', 'Header2': 'headerValue2'}
-  }).start().then(function(response) {
-    assert.equal(response.status, 200);
-    assert.equal(response.getText(), 'body');
-  });
-  assert.equal(fakeXhr.method, 'GET');
-  assert.equal(fakeXhr.url, 'http://foo.com');
-  assert.equal(fakeXhr.withCredentials, false);
-  assert.equal(fakeXhr.requestBody, null);
+  }).start();
   assert.equal(
       fakeXhr.requestHeaders['Header1'],
       'headerValue1');
   assert.equal(
       fakeXhr.requestHeaders['Header2'],
       'headerValue2');
-  assert.ok(!('Content-type' in fakeXhr.requestHeaders));
-  fakeXhr.respond(200, {}, 'body');
-  return promise;
 });
 
 
 QUnit.test('GET with credentials', function(assert) {
-  var promise = new remoting.Xhr({
+  new remoting.Xhr({
     method: 'GET',
     url: 'http://foo.com',
-    responseType: remoting.Xhr.ResponseType.TEXT,
     withCredentials: true
-  }).start().then(function(response) {
-    assert.equal(response.status, 200);
-    assert.equal(response.getText(), 'body');
-  });
-  assert.equal(fakeXhr.method, 'GET');
-  assert.equal(fakeXhr.url, 'http://foo.com');
+  }).start();
   assert.equal(fakeXhr.withCredentials, true);
-  assert.equal(fakeXhr.requestBody, null);
-  assert.ok(!('Content-type' in fakeXhr.requestHeaders));
-  fakeXhr.respond(200, {}, 'body');
-  return promise;
 });
 
+//
+// Checking that typical POST requests work.
+//
+
 QUnit.test('POST with text content', function(assert) {
   var done = assert.async();
 
   var promise = new remoting.Xhr({
     method: 'POST',
     url: 'http://foo.com',
-    responseType: remoting.Xhr.ResponseType.TEXT,
     textContent: 'the_content_string'
   }).start().then(function(response) {
     assert.equal(response.status, 200);
@@ -156,71 +302,53 @@
   assert.equal(fakeXhr.url, 'http://foo.com');
   assert.equal(fakeXhr.withCredentials, false);
   assert.equal(fakeXhr.requestBody, 'the_content_string');
-  assert.ok(!('Content-type' in fakeXhr.requestHeaders));
+  assert.equal(fakeXhr.requestHeaders['Content-type'],
+               'text/plain; charset=UTF-8');
   fakeXhr.respond(200, {}, 'body');
   return promise;
 });
 
 QUnit.test('POST with form content', function(assert) {
-  var promise = new remoting.Xhr({
+  new remoting.Xhr({
     method: 'POST',
     url: 'http://foo.com',
-    responseType: remoting.Xhr.ResponseType.TEXT,
     formContent: {'a': 'b', 'c': 'd'}
-  }).start().then(function(response) {
-    assert.equal(response.status, 200);
-    assert.equal(response.getText(), 'body');
-  });
-  assert.equal(fakeXhr.method, 'POST');
-  assert.equal(fakeXhr.url, 'http://foo.com');
-  assert.equal(fakeXhr.withCredentials, false);
+  }).start();
   assert.equal(fakeXhr.requestBody, 'a=b&c=d');
   assert.equal(
       fakeXhr.requestHeaders['Content-type'],
-      'application/x-www-form-urlencoded');
-  fakeXhr.respond(200, {}, 'body');
-  return promise;
+      'application/x-www-form-urlencoded; charset=UTF-8');
 });
 
-QUnit.test('defaultResponse 200', function(assert) {
-  var done = assert.async();
+//
+// Tests for authentication-related options.
+//
 
-  var onDone = function() {
-    assert.ok(true);
-    done();
-  };
-
-  var onError = function(error) {
-    assert.ok(false);
-    done();
-  };
-
+QUnit.test('GET with auth token', function(assert) {
   new remoting.Xhr({
-    method: 'POST',
-    url: 'http://foo.com'
-  }).start().then(remoting.Xhr.defaultResponse(onDone, onError));
-  fakeXhr.respond(200, {}, '');
+    method: 'GET',
+    url: 'http://foo.com',
+    oauthToken: 'my_token'
+  }).start();
+  assert.equal(fakeXhr.requestHeaders['Authorization'],
+              'Bearer my_token');
 });
 
+QUnit.test('GET with useIdentity', function(assert) {
+  var xhr = new remoting.Xhr({
+    method: 'GET',
+    url: 'http://foo.com',
+    useIdentity: true
+  });
 
-QUnit.test('defaultResponse 404', function(assert) {
+  xhr.start();
+
   var done = assert.async();
-
-  var onDone = function() {
-    assert.ok(false);
+  fakeXhr.addEventListener('loadstart', function() {
+    assert.equal(fakeXhr.requestHeaders['Authorization'],
+                 'Bearer my_token');
     done();
-  };
-
-  var onError = function(error) {
-    assert.ok(true);
-    done();
-  };
-
-  new remoting.Xhr({
-    method: 'POST',
-    url: 'http://foo.com'
-  }).start().then(remoting.Xhr.defaultResponse(onDone, onError));
-  fakeXhr.respond(404, {}, '');
+  });
 });
 
 })();
diff --git a/remoting/webapp/crd/manifest.json.jinja2 b/remoting/webapp/crd/manifest.json.jinja2
index 8f0f59cf..29096ea 100644
--- a/remoting/webapp/crd/manifest.json.jinja2
+++ b/remoting/webapp/crd/manifest.json.jinja2
@@ -53,7 +53,12 @@
   "oauth2": {
     "client_id": "{{ REMOTING_IDENTITY_API_CLIENT_ID }}",
     "scopes": [
-      "https://www.googleapis.com/auth/chromoting https://www.googleapis.com/auth/googletalk https://www.googleapis.com/auth/userinfo#email"
+      {% if USE_GCD %}
+      "https://www.googleapis.com/auth/clouddevices",
+      {% endif %}
+      "https://www.googleapis.com/auth/chromoting",
+      "https://www.googleapis.com/auth/googletalk",
+      "https://www.googleapis.com/auth/userinfo#email"
     ]
   },
   "sandbox": {
diff --git a/remoting/webapp/js_proto/sinon_proto.js b/remoting/webapp/js_proto/sinon_proto.js
index 9e223dc..a34d76a 100644
--- a/remoting/webapp/js_proto/sinon_proto.js
+++ b/remoting/webapp/js_proto/sinon_proto.js
@@ -127,12 +127,23 @@
 /** @returns {Object}  */
 sinon.createStubInstance = function (/** * */ constructor) {};
 
-/** @return {sinon.FakeXhr} */
+/** @interface */
+sinon.FakeXhrCtrl = function() {};
+
+/**
+ * @type {?function(!sinon.FakeXhr)}
+ */
+sinon.FakeXhrCtrl.prototype.onCreate;
+
+/** @return {sinon.FakeXhrCtrl} */
 sinon.useFakeXMLHttpRequest = function() {};
 
 /** @interface */
 sinon.FakeXhr = function() {};
 
+/** @type {number} */
+sinon.FakeXhr.prototype.readyState;
+
 /** @type {string} */
 sinon.FakeXhr.prototype.method;
 
@@ -156,6 +167,7 @@
 sinon.FakeXhr.prototype.respond;
 
 /**
- * @type {?function(!sinon.FakeXhr)}
+ * @param {string} event
+ * @param {Function} handler
  */
-sinon.FakeXhr.prototype.onCreate;
\ No newline at end of file
+sinon.FakeXhr.prototype.addEventListener;
diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc
index d88e5eb..f23d431 100644
--- a/sandbox/win/src/broker_services.cc
+++ b/sandbox/win/src/broker_services.cc
@@ -487,8 +487,10 @@
   DWORD win_result = target->Create(exe_path, command_line, inherit_handles,
                                     policy_base->GetLowBoxSid() ? true : false,
                                     startup_info, &process_info);
-  if (ERROR_SUCCESS != win_result)
-    return SpawnCleanup(target, win_result);
+  if (ERROR_SUCCESS != win_result) {
+    SpawnCleanup(target, win_result);
+    return SBOX_ERROR_CREATE_PROCESS;
+  }
 
   // Now the policy is the owner of the target.
   if (!policy_base->AddTarget(target)) {
diff --git a/sandbox/win/src/sandbox_types.h b/sandbox/win/src/sandbox_types.h
index 22840ce..3e531be4 100644
--- a/sandbox/win/src/sandbox_types.h
+++ b/sandbox/win/src/sandbox_types.h
@@ -45,6 +45,8 @@
   SBOX_ERROR_CANNOT_INIT_APPCONTAINER = 16,
   // Initializing or updating ProcThreadAttributes failed.
   SBOX_ERROR_PROC_THREAD_ATTRIBUTES = 17,
+  // Error in creating process.
+  SBOX_ERROR_CREATE_PROCESS = 18,
   // Placeholder for last item of the enum.
   SBOX_ERROR_LAST
 };
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index e9990e6..100cdfc 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -273,6 +273,10 @@
 #   define SK_SUPPORT_LEGACY_INT_COLORMATRIX
 #endif
 
+#ifndef    SK_SUPPORT_LEGACY_PATHOP_ENUMS
+#   define SK_SUPPORT_LEGACY_PATHOP_ENUMS
+#endif
+
 #ifndef    SK_LEGACY_STROKE_CURVES
 #   define SK_LEGACY_STROKE_CURVES
 #endif
diff --git a/storage/browser/fileapi/sandbox_directory_database.cc b/storage/browser/fileapi/sandbox_directory_database.cc
index 8343fea8..976a3fa2 100644
--- a/storage/browser/fileapi/sandbox_directory_database.cc
+++ b/storage/browser/fileapi/sandbox_directory_database.cc
@@ -18,6 +18,7 @@
 #include "base/strings/string_util.h"
 #include "storage/browser/fileapi/file_system_usage_cache.h"
 #include "storage/common/fileapi/file_system_util.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
 
@@ -727,6 +728,7 @@
   leveldb::Options options;
   options.max_open_files = 0;  // Use minimum.
   options.create_if_missing = true;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   if (env_override_)
     options.env = env_override_;
   leveldb::DB* db;
diff --git a/storage/browser/fileapi/sandbox_origin_database.cc b/storage/browser/fileapi/sandbox_origin_database.cc
index fa0521f..1f149e3 100644
--- a/storage/browser/fileapi/sandbox_origin_database.cc
+++ b/storage/browser/fileapi/sandbox_origin_database.cc
@@ -17,6 +17,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "storage/common/fileapi/file_system_util.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
 
@@ -80,6 +81,7 @@
   leveldb::Options options;
   options.max_open_files = 0;  // Use minimum.
   options.create_if_missing = true;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   if (env_override_)
     options.env = env_override_;
   leveldb::DB* db;
diff --git a/storage/browser/quota/special_storage_policy.h b/storage/browser/quota/special_storage_policy.h
index e6c37b5..276d6acd 100644
--- a/storage/browser/quota/special_storage_policy.h
+++ b/storage/browser/quota/special_storage_policy.h
@@ -52,10 +52,6 @@
   // disk capacity.
   virtual bool CanQueryDiskSize(const GURL& origin) = 0;
 
-  // Checks if extension identified with |extension_id| is registered as
-  // file handler.
-  virtual bool IsFileHandler(const std::string& extension_id) = 0;
-
   // Checks if the origin contains per-site isolated storage.
   virtual bool HasIsolatedStorage(const GURL& origin) = 0;
 
diff --git a/sync/api/attachments/attachment_id.h b/sync/api/attachments/attachment_id.h
index 1c53355..039d38684 100644
--- a/sync/api/attachments/attachment_id.h
+++ b/sync/api/attachments/attachment_id.h
@@ -75,6 +75,8 @@
   AttachmentId(sync_pb::AttachmentIdProto* proto);
 };
 
+// All public interfaces use AttachmentIdList. AttachmentIdSet is used in
+// implementations of algorithms where set properties are needed.
 typedef std::vector<AttachmentId> AttachmentIdList;
 typedef std::set<AttachmentId> AttachmentIdSet;
 
diff --git a/sync/api/attachments/attachment_store.cc b/sync/api/attachments/attachment_store.cc
index 7cf774e..b945c9f 100644
--- a/sync/api/attachments/attachment_store.cc
+++ b/sync/api/attachments/attachment_store.cc
@@ -16,10 +16,16 @@
 
 namespace syncer {
 
+namespace {
+
+void NoOpDropCallback(const AttachmentStore::Result& result) {
+}
+}
+
 AttachmentStore::AttachmentStore(
     const scoped_refptr<AttachmentStoreFrontend>& frontend,
-    AttachmentReferrer referrer)
-    : frontend_(frontend), referrer_(referrer) {
+    Component component)
+    : frontend_(frontend), component_(component) {
 }
 
 AttachmentStore::~AttachmentStore() {
@@ -32,12 +38,12 @@
 
 void AttachmentStore::Write(const AttachmentList& attachments,
                             const WriteCallback& callback) {
-  frontend_->Write(referrer_, attachments, callback);
+  frontend_->Write(component_, attachments, callback);
 }
 
 void AttachmentStore::Drop(const AttachmentIdList& ids,
                            const DropCallback& callback) {
-  frontend_->Drop(referrer_, ids, callback);
+  frontend_->DropReference(component_, ids, callback);
 }
 
 void AttachmentStore::ReadMetadata(const AttachmentIdList& ids,
@@ -46,14 +52,14 @@
 }
 
 void AttachmentStore::ReadAllMetadata(const ReadMetadataCallback& callback) {
-  frontend_->ReadAllMetadata(referrer_, callback);
+  frontend_->ReadAllMetadata(component_, callback);
 }
 
-scoped_ptr<AttachmentStore> AttachmentStore::CreateAttachmentStoreForSync()
-    const {
-  scoped_ptr<AttachmentStore> attachment_store(
-      new AttachmentStore(frontend_, SYNC));
-  return attachment_store.Pass();
+scoped_ptr<AttachmentStoreForSync>
+AttachmentStore::CreateAttachmentStoreForSync() const {
+  scoped_ptr<AttachmentStoreForSync> attachment_store_for_sync(
+      new AttachmentStoreForSync(frontend_, component_, SYNC));
+  return attachment_store_for_sync.Pass();
 }
 
 scoped_ptr<AttachmentStore> AttachmentStore::CreateInMemoryStore() {
@@ -103,4 +109,21 @@
   return attachment_store.Pass();
 }
 
+AttachmentStoreForSync::AttachmentStoreForSync(
+    const scoped_refptr<AttachmentStoreFrontend>& frontend,
+    Component consumer_component,
+    Component sync_component)
+    : AttachmentStore(frontend, consumer_component),
+      sync_component_(sync_component) {
+}
+
+void AttachmentStoreForSync::SetSyncReference(const AttachmentIdList& ids) {
+  frontend()->SetReference(sync_component_, ids);
+}
+
+void AttachmentStoreForSync::DropSyncReference(const AttachmentIdList& ids) {
+  frontend()->DropReference(sync_component_, ids,
+                            base::Bind(&NoOpDropCallback));
+}
+
 }  // namespace syncer
diff --git a/sync/api/attachments/attachment_store.h b/sync/api/attachments/attachment_store.h
index e8d8f5c7..cc83619 100644
--- a/sync/api/attachments/attachment_store.h
+++ b/sync/api/attachments/attachment_store.h
@@ -20,8 +20,9 @@
 
 namespace syncer {
 
-class AttachmentStoreFrontend;
 class AttachmentStoreBackend;
+class AttachmentStoreForSync;
+class AttachmentStoreFrontend;
 
 // AttachmentStore is a place to locally store and access Attachments.
 //
@@ -50,7 +51,7 @@
   // Each attachment can have references from sync or model type. Tracking these
   // references is needed for lifetime management of attachment, it can only be
   // deleted from the store when it doesn't have references.
-  enum AttachmentReferrer {
+  enum Component {
     MODEL_TYPE,
     SYNC,
   };
@@ -121,7 +122,7 @@
   // Given current AttachmentStore (this) creates separate AttachmentStore that
   // will be used by sync components (AttachmentService). Resulting
   // AttachmentStore is backed by the same frontend/backend.
-  scoped_ptr<AttachmentStore> CreateAttachmentStoreForSync() const;
+  scoped_ptr<AttachmentStoreForSync> CreateAttachmentStoreForSync() const;
 
   // Creates an AttachmentStore backed by in-memory implementation of attachment
   // store. For now frontend lives on the same thread as backend.
@@ -145,18 +146,47 @@
   static scoped_ptr<AttachmentStore> CreateMockStoreForTest(
       scoped_ptr<AttachmentStoreBackend> backend);
 
- private:
+ protected:
   AttachmentStore(const scoped_refptr<AttachmentStoreFrontend>& frontend,
-                  AttachmentReferrer referrer);
+                  Component component);
 
+  const scoped_refptr<AttachmentStoreFrontend>& frontend() { return frontend_; }
+
+ private:
   scoped_refptr<AttachmentStoreFrontend> frontend_;
   // Modification operations with attachment store will be performed on behalf
-  // of |referrer_|.
-  const AttachmentReferrer referrer_;
+  // of |component_|.
+  const Component component_;
 
   DISALLOW_COPY_AND_ASSIGN(AttachmentStore);
 };
 
+// AttachmentStoreForSync extends AttachmentStore and provides additional
+// functions necessary for AttachmentService. These are needed when
+// AttachmentService writes attachment on behalf of model type after download
+// and takes reference on attachment for the duration of upload.
+// Model type implementation shouldn't use this interface.
+class AttachmentStoreForSync : public AttachmentStore {
+ public:
+  // Asynchronously adds reference from sync to attachments.
+  void SetSyncReference(const AttachmentIdList& ids);
+
+  // Asynchronously drops sync reference from attachments.
+  void DropSyncReference(const AttachmentIdList& ids);
+
+ private:
+  friend class AttachmentStore;
+  AttachmentStoreForSync(const scoped_refptr<AttachmentStoreFrontend>& frontend,
+                         Component consumer_component,
+                         Component sync_component);
+
+  // |sync_component_| is passed to frontend when sync related operations are
+  // perfromed.
+  const Component sync_component_;
+
+  DISALLOW_COPY_AND_ASSIGN(AttachmentStoreForSync);
+};
+
 }  // namespace syncer
 
 #endif  // SYNC_API_ATTACHMENTS_ATTACHMENT_STORE_H_
diff --git a/sync/api/attachments/attachment_store_backend.h b/sync/api/attachments/attachment_store_backend.h
index 74859fe..73bc0fb 100644
--- a/sync/api/attachments/attachment_store_backend.h
+++ b/sync/api/attachments/attachment_store_backend.h
@@ -36,17 +36,19 @@
   virtual void Init(const AttachmentStore::InitCallback& callback) = 0;
   virtual void Read(const AttachmentIdList& ids,
                     const AttachmentStore::ReadCallback& callback) = 0;
-  virtual void Write(AttachmentStore::AttachmentReferrer referrer,
+  virtual void Write(AttachmentStore::Component component,
                      const AttachmentList& attachments,
                      const AttachmentStore::WriteCallback& callback) = 0;
-  virtual void Drop(AttachmentStore::AttachmentReferrer referrer,
-                    const AttachmentIdList& ids,
-                    const AttachmentStore::DropCallback& callback) = 0;
+  virtual void SetReference(AttachmentStore::Component component,
+                            const AttachmentIdList& ids) = 0;
+  virtual void DropReference(AttachmentStore::Component component,
+                             const AttachmentIdList& ids,
+                             const AttachmentStore::DropCallback& callback) = 0;
   virtual void ReadMetadata(
       const AttachmentIdList& ids,
       const AttachmentStore::ReadMetadataCallback& callback) = 0;
   virtual void ReadAllMetadata(
-      AttachmentStore::AttachmentReferrer referrer,
+      AttachmentStore::Component component,
       const AttachmentStore::ReadMetadataCallback& callback) = 0;
 
  protected:
diff --git a/sync/api/fake_syncable_service.cc b/sync/api/fake_syncable_service.cc
index d2177e9a..342dd2db 100644
--- a/sync/api/fake_syncable_service.cc
+++ b/sync/api/fake_syncable_service.cc
@@ -70,9 +70,10 @@
   return process_sync_changes_error_;
 }
 
-scoped_ptr<AttachmentStore> FakeSyncableService::GetAttachmentStoreForSync() {
+scoped_ptr<AttachmentStoreForSync>
+FakeSyncableService::GetAttachmentStoreForSync() {
   return attachment_store_ ? attachment_store_->CreateAttachmentStoreForSync()
-                           : scoped_ptr<AttachmentStore>();
+                           : scoped_ptr<AttachmentStoreForSync>();
 }
 
 void FakeSyncableService::SetAttachmentService(
diff --git a/sync/api/fake_syncable_service.h b/sync/api/fake_syncable_service.h
index 9b75647..4fd0c557 100644
--- a/sync/api/fake_syncable_service.h
+++ b/sync/api/fake_syncable_service.h
@@ -43,7 +43,7 @@
   SyncDataList GetAllSyncData(ModelType type) const override;
   SyncError ProcessSyncChanges(const tracked_objects::Location& from_here,
                                const SyncChangeList& change_list) override;
-  scoped_ptr<AttachmentStore> GetAttachmentStoreForSync() override;
+  scoped_ptr<AttachmentStoreForSync> GetAttachmentStoreForSync() override;
   void SetAttachmentService(
       scoped_ptr<AttachmentService> attachment_service) override;
 
diff --git a/sync/api/syncable_service.cc b/sync/api/syncable_service.cc
index f0cfe67..a5d67c4 100644
--- a/sync/api/syncable_service.cc
+++ b/sync/api/syncable_service.cc
@@ -8,8 +8,9 @@
 
 SyncableService::~SyncableService() {}
 
-scoped_ptr<AttachmentStore> SyncableService::GetAttachmentStoreForSync() {
-  return scoped_ptr<AttachmentStore>();
+scoped_ptr<AttachmentStoreForSync>
+SyncableService::GetAttachmentStoreForSync() {
+  return scoped_ptr<AttachmentStoreForSync>();
 }
 
 void SyncableService::SetAttachmentService(
diff --git a/sync/api/syncable_service.h b/sync/api/syncable_service.h
index 0d5aae4c..a6f8b81 100644
--- a/sync/api/syncable_service.h
+++ b/sync/api/syncable_service.h
@@ -68,14 +68,15 @@
 
   // Returns AttachmentStore for use by sync when uploading or downloading
   // attachments.
-  // GetAttachmentStore is called right before MergeDataAndStartSyncing. If at
-  // that time GetAttachmentStore returns NULL then datatype is considered not
-  // using attachments and all attempts to upload/download attachments will
-  // fail. Default implementation returns NULL. Datatype that uses sync
-  // attachments should create attachment store, implement GetAttachmentStore
-  // to return result of AttachmentStore::CreateAttachmentStoreForSync() from
-  // attachment store object.
-  virtual scoped_ptr<AttachmentStore> GetAttachmentStoreForSync();
+  // GetAttachmentStoreForSync is called right before MergeDataAndStartSyncing.
+  // If at that time GetAttachmentStoreForSync returns NULL then datatype is
+  // considered not using attachments and all attempts to upload/download
+  // attachments will fail. Default implementation returns NULL. Datatype that
+  // uses sync attachments should create attachment store, implement
+  // GetAttachmentStoreForSync to return result of
+  // AttachmentStore::CreateAttachmentStoreForSync() from attachment store
+  // object.
+  virtual scoped_ptr<AttachmentStoreForSync> GetAttachmentStoreForSync();
 
   // Called by sync to provide AttachmentService to be used to download
   // attachments.
diff --git a/sync/internal_api/attachments/attachment_service_impl.cc b/sync/internal_api/attachments/attachment_service_impl.cc
index e761515..fb65172 100644
--- a/sync/internal_api/attachments/attachment_service_impl.cc
+++ b/sync/internal_api/attachments/attachment_service_impl.cc
@@ -110,7 +110,7 @@
 }
 
 AttachmentServiceImpl::AttachmentServiceImpl(
-    scoped_ptr<AttachmentStore> attachment_store,
+    scoped_ptr<AttachmentStoreForSync> attachment_store,
     scoped_ptr<AttachmentUploader> attachment_uploader,
     scoped_ptr<AttachmentDownloader> attachment_downloader,
     Delegate* delegate,
@@ -151,12 +151,10 @@
   scoped_ptr<AttachmentDownloader> attachment_downloader(
       new FakeAttachmentDownloader());
   scoped_ptr<syncer::AttachmentService> attachment_service(
-      new syncer::AttachmentServiceImpl(attachment_store.Pass(),
-                                        attachment_uploader.Pass(),
-                                        attachment_downloader.Pass(),
-                                        NULL,
-                                        base::TimeDelta(),
-                                        base::TimeDelta()));
+      new syncer::AttachmentServiceImpl(
+          attachment_store->CreateAttachmentStoreForSync(),
+          attachment_uploader.Pass(), attachment_downloader.Pass(), NULL,
+          base::TimeDelta(), base::TimeDelta()));
   return attachment_service.Pass();
 }
 
@@ -223,8 +221,11 @@
     const AttachmentUploader::UploadResult& result,
     const AttachmentId& attachment_id) {
   DCHECK(CalledOnValidThread());
+  AttachmentIdList ids;
+  ids.push_back(attachment_id);
   switch (result) {
     case AttachmentUploader::UPLOAD_SUCCESS:
+      attachment_store_->DropSyncReference(ids);
       upload_task_queue_->MarkAsSucceeded(attachment_id);
       if (delegate_) {
         delegate_->OnAttachmentUploaded(attachment_id);
@@ -236,6 +237,7 @@
       break;
     case AttachmentUploader::UPLOAD_UNSPECIFIED_ERROR:
       // TODO(pavely): crbug/372622: Deal with UploadAttachment failures.
+      attachment_store_->DropSyncReference(ids);
       upload_task_queue_->MarkAsFailed(attachment_id);
       break;
   }
@@ -273,14 +275,15 @@
 }
 
 void AttachmentServiceImpl::UploadAttachments(
-    const AttachmentIdSet& attachment_ids) {
+    const AttachmentIdList& attachment_ids) {
   DCHECK(CalledOnValidThread());
   if (!attachment_uploader_.get()) {
     return;
   }
-  AttachmentIdSet::const_iterator iter = attachment_ids.begin();
-  AttachmentIdSet::const_iterator end = attachment_ids.end();
-  for (; iter != end; ++iter) {
+  attachment_store_->SetSyncReference(attachment_ids);
+
+  for (auto iter = attachment_ids.begin(); iter != attachment_ids.end();
+       ++iter) {
     upload_task_queue_->AddToQueue(*iter);
   }
 }
@@ -305,6 +308,7 @@
     for (; iter != end; ++iter) {
       upload_task_queue_->Cancel(*iter);
     }
+    attachment_store_->DropSyncReference(*unavailable_attachment_ids);
   }
 
   AttachmentMap::const_iterator iter = attachments->begin();
diff --git a/sync/internal_api/attachments/attachment_service_impl_unittest.cc b/sync/internal_api/attachments/attachment_service_impl_unittest.cc
index db83699..f619b75 100644
--- a/sync/internal_api/attachments/attachment_service_impl_unittest.cc
+++ b/sync/internal_api/attachments/attachment_service_impl_unittest.cc
@@ -39,17 +39,23 @@
     read_callbacks.push_back(callback);
   }
 
-  void Write(AttachmentStore::AttachmentReferrer referrer,
+  void Write(AttachmentStore::Component component,
              const AttachmentList& attachments,
              const AttachmentStore::WriteCallback& callback) override {
     write_attachments.push_back(attachments);
     write_callbacks.push_back(callback);
   }
 
-  void Drop(AttachmentStore::AttachmentReferrer referrer,
-            const AttachmentIdList& ids,
-            const AttachmentStore::DropCallback& callback) override {
-    NOTREACHED();
+  void SetReference(AttachmentStore::Component component,
+                    const AttachmentIdList& ids) override {
+    set_reference_ids.push_back(ids);
+  }
+
+  void DropReference(AttachmentStore::Component component,
+                     const AttachmentIdList& ids,
+                     const AttachmentStore::DropCallback& callback) override {
+    ASSERT_EQ(AttachmentStore::SYNC, component);
+    drop_ids.push_back(ids);
   }
 
   void ReadMetadata(
@@ -59,7 +65,7 @@
   }
 
   void ReadAllMetadata(
-      AttachmentStore::AttachmentReferrer referrer,
+      AttachmentStore::Component component,
       const AttachmentStore::ReadMetadataCallback& callback) override {
     NOTREACHED();
   }
@@ -100,7 +106,6 @@
   // Respond to Write request with |result|.
   void RespondToWrite(const AttachmentStore::Result& result) {
     AttachmentStore::WriteCallback callback = write_callbacks.back();
-    AttachmentList attachments = write_attachments.back();
     write_callbacks.pop_back();
     write_attachments.pop_back();
     base::MessageLoop::current()->PostTask(FROM_HERE,
@@ -111,6 +116,8 @@
   std::vector<AttachmentStore::ReadCallback> read_callbacks;
   std::vector<AttachmentList> write_attachments;
   std::vector<AttachmentStore::WriteCallback> write_callbacks;
+  std::vector<AttachmentIdList> set_reference_ids;
+  std::vector<AttachmentIdList> drop_ids;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockAttachmentStoreBackend);
@@ -223,8 +230,9 @@
       attachment_downloader_ = downloader->AsWeakPtr();
     }
     attachment_service_.reset(new AttachmentServiceImpl(
-        attachment_store.Pass(), uploader.Pass(), downloader.Pass(), delegate,
-        base::TimeDelta::FromMinutes(1), base::TimeDelta::FromMinutes(8)));
+        attachment_store->CreateAttachmentStoreForSync(), uploader.Pass(),
+        downloader.Pass(), delegate, base::TimeDelta::FromMinutes(1),
+        base::TimeDelta::FromMinutes(8)));
 
     scoped_ptr<base::MockTimer> timer_to_pass(
         new base::MockTimer(false, false));
@@ -260,6 +268,14 @@
     }
   }
 
+  static AttachmentIdSet AttachmentIdSetFromList(
+      const AttachmentIdList& id_list) {
+    AttachmentIdSet id_set;
+    std::copy(id_list.begin(), id_list.end(),
+              std::inserter(id_set, id_set.end()));
+    return id_set;
+  }
+
   const std::vector<AttachmentService::GetOrDownloadResult>&
   download_results() const {
     return download_results_;
@@ -420,19 +436,20 @@
 }
 
 TEST_F(AttachmentServiceImplTest, UploadAttachments_Success) {
-  AttachmentIdSet attachment_ids;
+  AttachmentIdList attachment_ids;
   const unsigned num_attachments = 3;
   for (unsigned i = 0; i < num_attachments; ++i) {
-    attachment_ids.insert(AttachmentId::Create(0, 0));
+    attachment_ids.push_back(AttachmentId::Create(0, 0));
   }
   attachment_service()->UploadAttachments(attachment_ids);
-
+  RunLoop();
+  EXPECT_FALSE(store()->set_reference_ids.empty());
   for (unsigned i = 0; i < num_attachments; ++i) {
     RunLoopAndFireTimer();
     // See that the service has issued a read for at least one of the
     // attachments.
     ASSERT_GE(store()->read_ids.size(), 1U);
-    store()->RespondToRead(attachment_ids);
+    store()->RespondToRead(AttachmentIdSetFromList(attachment_ids));
     RunLoop();
     ASSERT_GE(uploader()->upload_requests.size(), 1U);
     uploader()->RespondToUpload(uploader()->upload_requests.begin()->first,
@@ -444,11 +461,11 @@
 
   // See that all the attachments were uploaded.
   ASSERT_EQ(attachment_ids.size(), on_attachment_uploaded_list().size());
-  AttachmentIdSet::const_iterator iter = attachment_ids.begin();
-  const AttachmentIdSet::const_iterator end = attachment_ids.end();
-  for (iter = attachment_ids.begin(); iter != end; ++iter) {
+  for (auto iter = attachment_ids.begin(); iter != attachment_ids.end();
+       ++iter) {
     EXPECT_THAT(on_attachment_uploaded_list(), testing::Contains(*iter));
   }
+  EXPECT_EQ(num_attachments, store()->drop_ids.size());
 }
 
 TEST_F(AttachmentServiceImplTest, UploadAttachments_Success_NoDelegate) {
@@ -456,13 +473,13 @@
                               make_scoped_ptr(new MockAttachmentDownloader()),
                               NULL);  // No delegate.
 
-  AttachmentIdSet attachment_ids;
-  attachment_ids.insert(AttachmentId::Create(0, 0));
+  AttachmentIdList attachment_ids;
+  attachment_ids.push_back(AttachmentId::Create(0, 0));
   attachment_service()->UploadAttachments(attachment_ids);
   RunLoopAndFireTimer();
   ASSERT_EQ(1U, store()->read_ids.size());
   ASSERT_EQ(0U, uploader()->upload_requests.size());
-  store()->RespondToRead(attachment_ids);
+  store()->RespondToRead(AttachmentIdSetFromList(attachment_ids));
   RunLoop();
   ASSERT_EQ(0U, store()->read_ids.size());
   ASSERT_EQ(1U, uploader()->upload_requests.size());
@@ -473,15 +490,15 @@
 }
 
 TEST_F(AttachmentServiceImplTest, UploadAttachments_SomeMissingFromStore) {
-  AttachmentIdSet attachment_ids;
-  attachment_ids.insert(AttachmentId::Create(0, 0));
-  attachment_ids.insert(AttachmentId::Create(0, 0));
+  AttachmentIdList attachment_ids;
+  attachment_ids.push_back(AttachmentId::Create(0, 0));
+  attachment_ids.push_back(AttachmentId::Create(0, 0));
   attachment_service()->UploadAttachments(attachment_ids);
   RunLoopAndFireTimer();
   ASSERT_GE(store()->read_ids.size(), 1U);
 
   ASSERT_EQ(0U, uploader()->upload_requests.size());
-  store()->RespondToRead(attachment_ids);
+  store()->RespondToRead(AttachmentIdSetFromList(attachment_ids));
   RunLoop();
   ASSERT_EQ(1U, uploader()->upload_requests.size());
 
@@ -495,13 +512,14 @@
   RunLoop();
   // No upload requests since the read failed.
   ASSERT_EQ(0U, uploader()->upload_requests.size());
+  EXPECT_EQ(attachment_ids.size(), store()->drop_ids.size());
 }
 
 TEST_F(AttachmentServiceImplTest, UploadAttachments_AllMissingFromStore) {
-  AttachmentIdSet attachment_ids;
+  AttachmentIdList attachment_ids;
   const unsigned num_attachments = 2;
   for (unsigned i = 0; i < num_attachments; ++i) {
-    attachment_ids.insert(AttachmentId::Create(0, 0));
+    attachment_ids.push_back(AttachmentId::Create(0, 0));
   }
   attachment_service()->UploadAttachments(attachment_ids);
 
@@ -517,6 +535,7 @@
   EXPECT_EQ(0U, uploader()->upload_requests.size());
   // See that the delegate was never called.
   ASSERT_EQ(0U, on_attachment_uploaded_list().size());
+  EXPECT_EQ(num_attachments, store()->drop_ids.size());
 }
 
 TEST_F(AttachmentServiceImplTest, UploadAttachments_NoUploader) {
@@ -524,27 +543,28 @@
                               make_scoped_ptr(new MockAttachmentDownloader()),
                               this);
 
-  AttachmentIdSet attachment_ids;
-  attachment_ids.insert(AttachmentId::Create(0, 0));
+  AttachmentIdList attachment_ids;
+  attachment_ids.push_back(AttachmentId::Create(0, 0));
   attachment_service()->UploadAttachments(attachment_ids);
   RunLoop();
   EXPECT_EQ(0U, store()->read_ids.size());
   ASSERT_EQ(0U, on_attachment_uploaded_list().size());
+  EXPECT_EQ(0U, store()->drop_ids.size());
 }
 
 // Upload three attachments.  For one of them, server responds with error.
 TEST_F(AttachmentServiceImplTest, UploadAttachments_OneUploadFails) {
-  AttachmentIdSet attachment_ids;
+  AttachmentIdList attachment_ids;
   const unsigned num_attachments = 3;
   for (unsigned i = 0; i < num_attachments; ++i) {
-    attachment_ids.insert(AttachmentId::Create(0, 0));
+    attachment_ids.push_back(AttachmentId::Create(0, 0));
   }
   attachment_service()->UploadAttachments(attachment_ids);
 
   for (unsigned i = 0; i < 3; ++i) {
     RunLoopAndFireTimer();
     ASSERT_GE(store()->read_ids.size(), 1U);
-    store()->RespondToRead(attachment_ids);
+    store()->RespondToRead(AttachmentIdSetFromList(attachment_ids));
     RunLoop();
     ASSERT_EQ(1U, uploader()->upload_requests.size());
     AttachmentUploader::UploadResult result =
@@ -560,19 +580,20 @@
     RunLoop();
   }
   ASSERT_EQ(2U, on_attachment_uploaded_list().size());
+  EXPECT_EQ(num_attachments, store()->drop_ids.size());
 }
 
 // Attempt an upload, respond with transient error to trigger backoff, issue
 // network disconnect/connect events and see that backoff is cleared.
 TEST_F(AttachmentServiceImplTest,
        UploadAttachments_ResetBackoffAfterNetworkChange) {
-  AttachmentIdSet attachment_ids;
-  attachment_ids.insert(AttachmentId::Create(0, 0));
+  AttachmentIdList attachment_ids;
+  attachment_ids.push_back(AttachmentId::Create(0, 0));
   attachment_service()->UploadAttachments(attachment_ids);
 
   RunLoopAndFireTimer();
   ASSERT_EQ(1U, store()->read_ids.size());
-  store()->RespondToRead(attachment_ids);
+  store()->RespondToRead(AttachmentIdSetFromList(attachment_ids));
   RunLoop();
   ASSERT_EQ(1U, uploader()->upload_requests.size());
 
diff --git a/sync/internal_api/attachments/attachment_service_proxy.cc b/sync/internal_api/attachments/attachment_service_proxy.cc
index 50b7a649..91a627db 100644
--- a/sync/internal_api/attachments/attachment_service_proxy.cc
+++ b/sync/internal_api/attachments/attachment_service_proxy.cc
@@ -66,7 +66,7 @@
 }
 
 void AttachmentServiceProxy::UploadAttachments(
-    const AttachmentIdSet& attachment_ids) {
+    const AttachmentIdList& attachment_ids) {
   DCHECK(wrapped_task_runner_.get());
   wrapped_task_runner_->PostTask(
       FROM_HERE,
@@ -91,7 +91,7 @@
 }
 
 void AttachmentServiceProxy::Core::UploadAttachments(
-    const AttachmentIdSet& attachment_ids) {
+    const AttachmentIdList& attachment_ids) {
   if (!wrapped_) {
     return;
   }
diff --git a/sync/internal_api/attachments/attachment_service_proxy_unittest.cc b/sync/internal_api/attachments/attachment_service_proxy_unittest.cc
index a66b39f4..fe52f469 100644
--- a/sync/internal_api/attachments/attachment_service_proxy_unittest.cc
+++ b/sync/internal_api/attachments/attachment_service_proxy_unittest.cc
@@ -46,7 +46,7 @@
                    base::Passed(&attachments)));
   }
 
-  void UploadAttachments(const AttachmentIdSet& attachments_ids) override {
+  void UploadAttachments(const AttachmentIdList& attachments_ids) override {
     CalledOnValidThread();
     Increment();
   }
@@ -135,7 +135,7 @@
 // thread.
 TEST_F(AttachmentServiceProxyTest, MethodsAreProxied) {
   proxy->GetOrDownloadAttachments(AttachmentIdList(), callback_get_or_download);
-  proxy->UploadAttachments(AttachmentIdSet());
+  proxy->UploadAttachments(AttachmentIdList());
   // Wait for the posted calls to execute in the stub thread.
   WaitForStubThread();
   EXPECT_EQ(2, stub->GetCallCount());
diff --git a/sync/internal_api/attachments/attachment_store_frontend.cc b/sync/internal_api/attachments/attachment_store_frontend.cc
index 97547de..b9d69950 100644
--- a/sync/internal_api/attachments/attachment_store_frontend.cc
+++ b/sync/internal_api/attachments/attachment_store_frontend.cc
@@ -56,25 +56,33 @@
 }
 
 void AttachmentStoreFrontend::Write(
-    AttachmentStore::AttachmentReferrer referrer,
+    AttachmentStore::Component component,
     const AttachmentList& attachments,
     const AttachmentStore::WriteCallback& callback) {
   DCHECK(CalledOnValidThread());
   backend_task_runner_->PostTask(
       FROM_HERE, base::Bind(&AttachmentStoreBackend::Write,
-                            base::Unretained(backend_.get()), referrer,
+                            base::Unretained(backend_.get()), component,
                             attachments, callback));
 }
 
-void AttachmentStoreFrontend::Drop(
-    AttachmentStore::AttachmentReferrer referrer,
+void AttachmentStoreFrontend::SetReference(AttachmentStore::Component component,
+                                           const AttachmentIdList& ids) {
+  DCHECK(CalledOnValidThread());
+  backend_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&AttachmentStoreBackend::SetReference,
+                            base::Unretained(backend_.get()), component, ids));
+}
+
+void AttachmentStoreFrontend::DropReference(
+    AttachmentStore::Component component,
     const AttachmentIdList& ids,
     const AttachmentStore::DropCallback& callback) {
   DCHECK(CalledOnValidThread());
   backend_task_runner_->PostTask(
       FROM_HERE,
-      base::Bind(&AttachmentStoreBackend::Drop,
-                 base::Unretained(backend_.get()), referrer, ids, callback));
+      base::Bind(&AttachmentStoreBackend::DropReference,
+                 base::Unretained(backend_.get()), component, ids, callback));
 }
 
 void AttachmentStoreFrontend::ReadMetadata(
@@ -87,13 +95,13 @@
 }
 
 void AttachmentStoreFrontend::ReadAllMetadata(
-    AttachmentStore::AttachmentReferrer referrer,
+    AttachmentStore::Component component,
     const AttachmentStore::ReadMetadataCallback& callback) {
   DCHECK(CalledOnValidThread());
   backend_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&AttachmentStoreBackend::ReadAllMetadata,
-                 base::Unretained(backend_.get()), referrer, callback));
+                 base::Unretained(backend_.get()), component, callback));
 }
 
 }  // namespace syncer
diff --git a/sync/internal_api/attachments/attachment_store_frontend_unittest.cc b/sync/internal_api/attachments/attachment_store_frontend_unittest.cc
index 696b43a..a1e8740 100644
--- a/sync/internal_api/attachments/attachment_store_frontend_unittest.cc
+++ b/sync/internal_api/attachments/attachment_store_frontend_unittest.cc
@@ -25,7 +25,8 @@
   MockAttachmentStore(const base::Closure& init_called,
                       const base::Closure& read_called,
                       const base::Closure& write_called,
-                      const base::Closure& drop_called,
+                      const base::Closure& set_reference_called,
+                      const base::Closure& drop_reference_called,
                       const base::Closure& read_metadata_called,
                       const base::Closure& read_all_metadata_called,
                       const base::Closure& dtor_called)
@@ -33,7 +34,8 @@
         init_called_(init_called),
         read_called_(read_called),
         write_called_(write_called),
-        drop_called_(drop_called),
+        set_reference_called_(set_reference_called),
+        drop_reference_called_(drop_reference_called),
         read_metadata_called_(read_metadata_called),
         read_all_metadata_called_(read_all_metadata_called),
         dtor_called_(dtor_called) {}
@@ -49,16 +51,21 @@
     read_called_.Run();
   }
 
-  void Write(AttachmentStore::AttachmentReferrer referrer,
+  void Write(AttachmentStore::Component component,
              const AttachmentList& attachments,
              const AttachmentStore::WriteCallback& callback) override {
     write_called_.Run();
   }
 
-  void Drop(AttachmentStore::AttachmentReferrer referrer,
-            const AttachmentIdList& ids,
-            const AttachmentStore::DropCallback& callback) override {
-    drop_called_.Run();
+  void SetReference(AttachmentStore::Component component,
+                    const AttachmentIdList& ids) override {
+    set_reference_called_.Run();
+  }
+
+  void DropReference(AttachmentStore::Component component,
+                     const AttachmentIdList& ids,
+                     const AttachmentStore::DropCallback& callback) override {
+    drop_reference_called_.Run();
   }
 
   void ReadMetadata(
@@ -68,7 +75,7 @@
   }
 
   void ReadAllMetadata(
-      AttachmentStore::AttachmentReferrer referrer,
+      AttachmentStore::Component component,
       const AttachmentStore::ReadMetadataCallback& callback) override {
     read_all_metadata_called_.Run();
   }
@@ -76,7 +83,8 @@
   base::Closure init_called_;
   base::Closure read_called_;
   base::Closure write_called_;
-  base::Closure drop_called_;
+  base::Closure set_reference_called_;
+  base::Closure drop_reference_called_;
   base::Closure read_metadata_called_;
   base::Closure read_all_metadata_called_;
   base::Closure dtor_called_;
@@ -90,6 +98,7 @@
       : init_call_count_(0),
         read_call_count_(0),
         write_call_count_(0),
+        add_component_call_count_(0),
         drop_call_count_(0),
         read_metadata_call_count_(0),
         read_all_metadata_call_count_(0),
@@ -103,7 +112,9 @@
                    base::Unretained(this)),
         base::Bind(&AttachmentStoreFrontendTest::WriteCalled,
                    base::Unretained(this)),
-        base::Bind(&AttachmentStoreFrontendTest::DropCalled,
+        base::Bind(&AttachmentStoreFrontendTest::SetReferenceCalled,
+                   base::Unretained(this)),
+        base::Bind(&AttachmentStoreFrontendTest::DropReferenceCalled,
                    base::Unretained(this)),
         base::Bind(&AttachmentStoreFrontendTest::ReadMetadataCalled,
                    base::Unretained(this)),
@@ -136,7 +147,9 @@
 
   void WriteCalled() { ++write_call_count_; }
 
-  void DropCalled() { ++drop_call_count_; }
+  void SetReferenceCalled() { ++add_component_call_count_; }
+
+  void DropReferenceCalled() { ++drop_call_count_; }
 
   void ReadMetadataCalled() { ++read_metadata_call_count_; }
 
@@ -154,6 +167,7 @@
   int init_call_count_;
   int read_call_count_;
   int write_call_count_;
+  int add_component_call_count_;
   int drop_call_count_;
   int read_metadata_call_count_;
   int read_all_metadata_call_count_;
@@ -162,7 +176,7 @@
 
 // Test that method calls are forwarded to backend loop
 TEST_F(AttachmentStoreFrontendTest, MethodsCalled) {
-  AttachmentIdList ids;
+  AttachmentIdList id_list;
   AttachmentList attachments;
 
   attachment_store_frontend_->Init(
@@ -172,7 +186,7 @@
   EXPECT_EQ(init_call_count_, 1);
 
   attachment_store_frontend_->Read(
-      ids, base::Bind(&AttachmentStoreFrontendTest::ReadDone));
+      id_list, base::Bind(&AttachmentStoreFrontendTest::ReadDone));
   EXPECT_EQ(read_call_count_, 0);
   RunMessageLoop();
   EXPECT_EQ(read_call_count_, 1);
@@ -184,15 +198,17 @@
   RunMessageLoop();
   EXPECT_EQ(write_call_count_, 1);
 
-  attachment_store_frontend_->Drop(
-      AttachmentStore::SYNC, ids,
+  attachment_store_frontend_->SetReference(AttachmentStore::SYNC, id_list);
+
+  attachment_store_frontend_->DropReference(
+      AttachmentStore::SYNC, id_list,
       base::Bind(&AttachmentStoreFrontendTest::DoneWithResult));
   EXPECT_EQ(drop_call_count_, 0);
   RunMessageLoop();
   EXPECT_EQ(drop_call_count_, 1);
 
   attachment_store_frontend_->ReadMetadata(
-      ids, base::Bind(&AttachmentStoreFrontendTest::ReadMetadataDone));
+      id_list, base::Bind(&AttachmentStoreFrontendTest::ReadMetadataDone));
   EXPECT_EQ(read_metadata_call_count_, 0);
   RunMessageLoop();
   EXPECT_EQ(read_metadata_call_count_, 1);
diff --git a/sync/internal_api/attachments/attachment_store_test_template.h b/sync/internal_api/attachments/attachment_store_test_template.h
index 6292b01..80208a3 100644
--- a/sync/internal_api/attachments/attachment_store_test_template.h
+++ b/sync/internal_api/attachments/attachment_store_test_template.h
@@ -128,6 +128,13 @@
 
 TYPED_TEST_CASE_P(AttachmentStoreTest);
 
+// Verify that CreateAttachmentStoreForSync() creates valid object.
+TYPED_TEST_P(AttachmentStoreTest, CreateAttachmentStoreForSync) {
+  scoped_ptr<AttachmentStoreForSync> attachment_store_for_sync =
+      this->store->CreateAttachmentStoreForSync();
+  EXPECT_NE(nullptr, attachment_store_for_sync);
+}
+
 // Verify that we do not overwrite existing attachments and that we do not treat
 // it as an error.
 TYPED_TEST_P(AttachmentStoreTest, Write_NoOverwriteNoError) {
@@ -386,6 +393,7 @@
 }
 
 REGISTER_TYPED_TEST_CASE_P(AttachmentStoreTest,
+                           CreateAttachmentStoreForSync,
                            Write_NoOverwriteNoError,
                            Write_RoundTrip,
                            Read_OneNotFound,
diff --git a/sync/internal_api/attachments/in_memory_attachment_store.cc b/sync/internal_api/attachments/in_memory_attachment_store.cc
index ba21426..b0fb846 100644
--- a/sync/internal_api/attachments/in_memory_attachment_store.cc
+++ b/sync/internal_api/attachments/in_memory_attachment_store.cc
@@ -66,7 +66,7 @@
 }
 
 void InMemoryAttachmentStore::Write(
-    AttachmentStore::AttachmentReferrer referrer,
+    AttachmentStore::Component component,
     const AttachmentList& attachments,
     const AttachmentStore::WriteCallback& callback) {
   DCHECK(CalledOnValidThread());
@@ -78,12 +78,25 @@
   PostCallback(base::Bind(callback, AttachmentStore::SUCCESS));
 }
 
-void InMemoryAttachmentStore::Drop(
-    AttachmentStore::AttachmentReferrer referrer,
+void InMemoryAttachmentStore::SetReference(AttachmentStore::Component component,
+                                           const AttachmentIdList& ids) {
+  DCHECK(CalledOnValidThread());
+  DCHECK_EQ(AttachmentStore::SYNC, component);
+}
+
+void InMemoryAttachmentStore::DropReference(
+    AttachmentStore::Component component,
     const AttachmentIdList& ids,
     const AttachmentStore::DropCallback& callback) {
   DCHECK(CalledOnValidThread());
   AttachmentStore::Result result = AttachmentStore::SUCCESS;
+  if (component == AttachmentStore::SYNC) {
+    // TODO(pavely): There is no reference handling implementation yet. All
+    // calls to AddReferrer are ignored. Calls to Drop coming from sync should
+    // be ignored too.
+    PostCallback(base::Bind(callback, AttachmentStore::SUCCESS));
+    return;
+  }
   AttachmentIdList::const_iterator ids_iter = ids.begin();
   AttachmentIdList::const_iterator ids_end = ids.end();
   for (; ids_iter != ids_end; ++ids_iter) {
@@ -117,7 +130,7 @@
 }
 
 void InMemoryAttachmentStore::ReadAllMetadata(
-    AttachmentStore::AttachmentReferrer referrer,
+    AttachmentStore::Component component,
     const AttachmentStore::ReadMetadataCallback& callback) {
   DCHECK(CalledOnValidThread());
   AttachmentStore::Result result_code = AttachmentStore::SUCCESS;
diff --git a/sync/internal_api/attachments/on_disk_attachment_store.cc b/sync/internal_api/attachments/on_disk_attachment_store.cc
index 699e1f5..a2128a5f 100644
--- a/sync/internal_api/attachments/on_disk_attachment_store.cc
+++ b/sync/internal_api/attachments/on_disk_attachment_store.cc
@@ -13,6 +13,7 @@
 #include "sync/internal_api/attachments/proto/attachment_store.pb.h"
 #include "sync/internal_api/public/attachments/attachment_util.h"
 #include "sync/protocol/attachments.pb.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/options.h"
 #include "third_party/leveldatabase/src/include/leveldb/slice.h"
@@ -134,7 +135,7 @@
 }
 
 void OnDiskAttachmentStore::Write(
-    AttachmentStore::AttachmentReferrer referrer,
+    AttachmentStore::Component component,
     const AttachmentList& attachments,
     const AttachmentStore::WriteCallback& callback) {
   DCHECK(CalledOnValidThread());
@@ -153,11 +154,24 @@
   PostCallback(base::Bind(callback, result_code));
 }
 
-void OnDiskAttachmentStore::Drop(
-    AttachmentStore::AttachmentReferrer referrer,
+void OnDiskAttachmentStore::SetReference(AttachmentStore::Component component,
+                                         const AttachmentIdList& ids) {
+  DCHECK(CalledOnValidThread());
+  DCHECK_EQ(AttachmentStore::SYNC, component);
+}
+
+void OnDiskAttachmentStore::DropReference(
+    AttachmentStore::Component component,
     const AttachmentIdList& ids,
     const AttachmentStore::DropCallback& callback) {
   DCHECK(CalledOnValidThread());
+  if (component == AttachmentStore::SYNC) {
+    // TODO(pavely): There is no reference handling implementation yet. All
+    // calls to AddReferrer are ignored. Calls to Drop coming from sync should
+    // be ignored too.
+    PostCallback(base::Bind(callback, AttachmentStore::SUCCESS));
+    return;
+  }
   AttachmentStore::Result result_code =
       AttachmentStore::STORE_INITIALIZATION_FAILED;
   if (db_) {
@@ -208,7 +222,7 @@
 }
 
 void OnDiskAttachmentStore::ReadAllMetadata(
-    AttachmentStore::AttachmentReferrer referrer,
+    AttachmentStore::Component component,
     const AttachmentStore::ReadMetadataCallback& callback) {
   DCHECK(CalledOnValidThread());
   AttachmentStore::Result result_code =
@@ -261,6 +275,7 @@
   scoped_ptr<leveldb::DB> db;
   leveldb::Options options;
   options.create_if_missing = true;
+  options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
   // TODO(pavely): crbug/424287 Consider adding info_log, block_cache and
   // filter_policy to options.
   leveldb::Status status =
diff --git a/sync/internal_api/public/attachments/attachment_service.h b/sync/internal_api/public/attachments/attachment_service.h
index da4b597..6d061b6 100644
--- a/sync/internal_api/public/attachments/attachment_service.h
+++ b/sync/internal_api/public/attachments/attachment_service.h
@@ -68,7 +68,7 @@
   // A request to upload attachments does not persist across restarts of Chrome.
   //
   // Invokes OnAttachmentUploaded on the Delegate (if provided).
-  virtual void UploadAttachments(const AttachmentIdSet& attachment_ids) = 0;
+  virtual void UploadAttachments(const AttachmentIdList& attachment_ids) = 0;
 };
 
 }  // namespace syncer
diff --git a/sync/internal_api/public/attachments/attachment_service_impl.h b/sync/internal_api/public/attachments/attachment_service_impl.h
index c3275bf..04b4121 100644
--- a/sync/internal_api/public/attachments/attachment_service_impl.h
+++ b/sync/internal_api/public/attachments/attachment_service_impl.h
@@ -50,7 +50,7 @@
   //
   // |max_backoff_delay| the maxmium delay between upload attempts when backed
   // off.
-  AttachmentServiceImpl(scoped_ptr<AttachmentStore> attachment_store,
+  AttachmentServiceImpl(scoped_ptr<AttachmentStoreForSync> attachment_store,
                         scoped_ptr<AttachmentUploader> attachment_uploader,
                         scoped_ptr<AttachmentDownloader> attachment_downloader,
                         Delegate* delegate,
@@ -64,7 +64,7 @@
   // AttachmentService implementation.
   void GetOrDownloadAttachments(const AttachmentIdList& attachment_ids,
                                 const GetOrDownloadCallback& callback) override;
-  void UploadAttachments(const AttachmentIdSet& attachment_ids) override;
+  void UploadAttachments(const AttachmentIdList& attachment_ids) override;
 
   // NetworkChangeObserver implementation.
   void OnNetworkChanged(
@@ -97,7 +97,7 @@
       scoped_ptr<AttachmentMap> attachments,
       scoped_ptr<AttachmentIdList> unavailable_attachment_ids);
 
-  scoped_ptr<AttachmentStore> attachment_store_;
+  scoped_ptr<AttachmentStoreForSync> attachment_store_;
 
   // May be null.
   const scoped_ptr<AttachmentUploader> attachment_uploader_;
diff --git a/sync/internal_api/public/attachments/attachment_service_proxy.h b/sync/internal_api/public/attachments/attachment_service_proxy.h
index ef6e287..c62a206b 100644
--- a/sync/internal_api/public/attachments/attachment_service_proxy.h
+++ b/sync/internal_api/public/attachments/attachment_service_proxy.h
@@ -52,7 +52,7 @@
 
   void GetOrDownloadAttachments(const AttachmentIdList& attachment_ids,
                                 const GetOrDownloadCallback& callback) override;
-  void UploadAttachments(const AttachmentIdSet& attachment_ids) override;
+  void UploadAttachments(const AttachmentIdList& attachment_ids) override;
 
  protected:
   // Core does the work of proxying calls to AttachmentService methods from one
@@ -78,7 +78,7 @@
     void GetOrDownloadAttachments(
         const AttachmentIdList& attachment_ids,
         const GetOrDownloadCallback& callback) override;
-    void UploadAttachments(const AttachmentIdSet& attachment_ids) override;
+    void UploadAttachments(const AttachmentIdList& attachment_ids) override;
 
    protected:
     friend class base::RefCountedThreadSafe<Core>;
diff --git a/sync/internal_api/public/attachments/attachment_store_frontend.h b/sync/internal_api/public/attachments/attachment_store_frontend.h
index ec4a33f..682cb4c 100644
--- a/sync/internal_api/public/attachments/attachment_store_frontend.h
+++ b/sync/internal_api/public/attachments/attachment_store_frontend.h
@@ -40,18 +40,19 @@
   void Read(const AttachmentIdList& ids,
             const AttachmentStore::ReadCallback& callback);
 
-  void Write(AttachmentStore::AttachmentReferrer referrer,
+  void Write(AttachmentStore::Component component,
              const AttachmentList& attachments,
              const AttachmentStore::WriteCallback& callback);
-
-  void Drop(AttachmentStore::AttachmentReferrer referrer,
-            const AttachmentIdList& ids,
-            const AttachmentStore::DropCallback& callback);
+  void SetReference(AttachmentStore::Component component,
+                    const AttachmentIdList& ids);
+  void DropReference(AttachmentStore::Component component,
+                     const AttachmentIdList& ids,
+                     const AttachmentStore::DropCallback& callback);
 
   void ReadMetadata(const AttachmentIdList& ids,
                     const AttachmentStore::ReadMetadataCallback& callback);
 
-  void ReadAllMetadata(AttachmentStore::AttachmentReferrer referrer,
+  void ReadAllMetadata(AttachmentStore::Component component,
                        const AttachmentStore::ReadMetadataCallback& callback);
 
  private:
diff --git a/sync/internal_api/public/attachments/in_memory_attachment_store.h b/sync/internal_api/public/attachments/in_memory_attachment_store.h
index 5f85992..e5114741 100644
--- a/sync/internal_api/public/attachments/in_memory_attachment_store.h
+++ b/sync/internal_api/public/attachments/in_memory_attachment_store.h
@@ -33,17 +33,19 @@
   void Init(const AttachmentStore::InitCallback& callback) override;
   void Read(const AttachmentIdList& ids,
             const AttachmentStore::ReadCallback& callback) override;
-  void Write(AttachmentStore::AttachmentReferrer referrer,
+  void Write(AttachmentStore::Component component,
              const AttachmentList& attachments,
              const AttachmentStore::WriteCallback& callback) override;
-  void Drop(AttachmentStore::AttachmentReferrer referrer,
-            const AttachmentIdList& ids,
-            const AttachmentStore::DropCallback& callback) override;
+  void SetReference(AttachmentStore::Component component,
+                    const AttachmentIdList& ids) override;
+  void DropReference(AttachmentStore::Component component,
+                     const AttachmentIdList& ids,
+                     const AttachmentStore::DropCallback& callback) override;
   void ReadMetadata(
       const AttachmentIdList& ids,
       const AttachmentStore::ReadMetadataCallback& callback) override;
   void ReadAllMetadata(
-      AttachmentStore::AttachmentReferrer referrer,
+      AttachmentStore::Component component,
       const AttachmentStore::ReadMetadataCallback& callback) override;
 
  private:
diff --git a/sync/internal_api/public/attachments/on_disk_attachment_store.h b/sync/internal_api/public/attachments/on_disk_attachment_store.h
index 62cec5b..bde55c8 100644
--- a/sync/internal_api/public/attachments/on_disk_attachment_store.h
+++ b/sync/internal_api/public/attachments/on_disk_attachment_store.h
@@ -43,17 +43,19 @@
   void Init(const AttachmentStore::InitCallback& callback) override;
   void Read(const AttachmentIdList& ids,
             const AttachmentStore::ReadCallback& callback) override;
-  void Write(AttachmentStore::AttachmentReferrer referrer,
+  void Write(AttachmentStore::Component component,
              const AttachmentList& attachments,
              const AttachmentStore::WriteCallback& callback) override;
-  void Drop(AttachmentStore::AttachmentReferrer referrer,
-            const AttachmentIdList& ids,
-            const AttachmentStore::DropCallback& callback) override;
+  void SetReference(AttachmentStore::Component component,
+                    const AttachmentIdList& ids) override;
+  void DropReference(AttachmentStore::Component component,
+                     const AttachmentIdList& ids,
+                     const AttachmentStore::DropCallback& callback) override;
   void ReadMetadata(
       const AttachmentIdList& ids,
       const AttachmentStore::ReadMetadataCallback& callback) override;
   void ReadAllMetadata(
-      AttachmentStore::AttachmentReferrer referrer,
+      AttachmentStore::Component component,
       const AttachmentStore::ReadMetadataCallback& callback) override;
 
  private:
diff --git a/sync/internal_api/public/read_transaction.h b/sync/internal_api/public/read_transaction.h
index c75d16a..bdb92cb 100644
--- a/sync/internal_api/public/read_transaction.h
+++ b/sync/internal_api/public/read_transaction.h
@@ -46,9 +46,9 @@
   void GetDataTypeContext(ModelType type,
                           sync_pb::DataTypeContext* context) const;
 
-  // Clear |id_set| and fill it with the ids of attachments that need to be
+  // Clear |ids| and fill it with the ids of attachments that need to be
   // uploaded to the sync server.
-  void GetAttachmentIdsToUpload(ModelType type, AttachmentIdSet* id_set) const;
+  void GetAttachmentIdsToUpload(ModelType type, AttachmentIdList* ids) const;
 
   // Return the current (opaque) store birthday.
   std::string GetStoreBirthday() const;
diff --git a/sync/internal_api/read_transaction.cc b/sync/internal_api/read_transaction.cc
index 872818f..9d81b0e 100644
--- a/sync/internal_api/read_transaction.cc
+++ b/sync/internal_api/read_transaction.cc
@@ -48,10 +48,9 @@
 }
 
 void ReadTransaction::GetAttachmentIdsToUpload(ModelType type,
-                                               AttachmentIdSet* id_set) const {
-  DCHECK(id_set);
-  transaction_->directory()->GetAttachmentIdsToUpload(
-      transaction_, type, id_set);
+                                               AttachmentIdList* ids) const {
+  DCHECK(ids);
+  transaction_->directory()->GetAttachmentIdsToUpload(transaction_, type, ids);
 }
 
 std::string ReadTransaction::GetStoreBirthday() const {
diff --git a/sync/protocol/sync.proto b/sync/protocol/sync.proto
index d5a75cb..4d6cf63 100644
--- a/sync/protocol/sync.proto
+++ b/sync/protocol/sync.proto
@@ -714,7 +714,7 @@
 
 message ClientToServerMessage {
   required string share = 1;
-  optional int32 protocol_version = 2 [default = 42];
+  optional int32 protocol_version = 2 [default = 43];
   enum Contents {
     COMMIT = 1;
     GET_UPDATES = 2;
diff --git a/sync/syncable/directory.cc b/sync/syncable/directory.cc
index e330de23..e603e4e 100644
--- a/sync/syncable/directory.cc
+++ b/sync/syncable/directory.cc
@@ -1498,13 +1498,13 @@
 
 void Directory::GetAttachmentIdsToUpload(BaseTransaction* trans,
                                          ModelType type,
-                                         AttachmentIdSet* id_set) {
+                                         AttachmentIdList* ids) {
   // TODO(maniscalco): Maintain an index by ModelType and rewrite this method to
   // use it.  The approach below is likely very expensive because it iterates
   // all entries (bug 415199).
   DCHECK(trans);
-  DCHECK(id_set);
-  id_set->clear();
+  DCHECK(ids);
+  ids->clear();
   AttachmentIdSet on_server_id_set;
   AttachmentIdSet not_on_server_id_set;
   std::vector<int64> metahandles;
@@ -1543,11 +1543,9 @@
   // return.
   //
   // TODO(maniscalco): Eliminate redundant metadata storage (bug 415203).
-  std::set_difference(not_on_server_id_set.begin(),
-                      not_on_server_id_set.end(),
-                      on_server_id_set.begin(),
-                      on_server_id_set.end(),
-                      std::inserter(*id_set, id_set->end()));
+  std::set_difference(not_on_server_id_set.begin(), not_on_server_id_set.end(),
+                      on_server_id_set.begin(), on_server_id_set.end(),
+                      std::back_inserter(*ids));
 }
 
 }  // namespace syncable
diff --git a/sync/syncable/directory.h b/sync/syncable/directory.h
index 89eec170..b2561170 100644
--- a/sync/syncable/directory.h
+++ b/sync/syncable/directory.h
@@ -417,11 +417,11 @@
   // preserve sync preferences in DB on disk.
   void UnmarkDirtyEntry(WriteTransaction* trans, Entry* entry);
 
-  // Clears |id_set| and fills it with the ids of attachments that need to be
+  // Clears |ids| and fills it with the ids of attachments that need to be
   // uploaded to the sync server.
   void GetAttachmentIdsToUpload(BaseTransaction* trans,
                                 ModelType type,
-                                AttachmentIdSet* id_set);
+                                AttachmentIdList* ids);
 
  private:
   struct Kernel {
diff --git a/sync/syncable/directory_unittest.cc b/sync/syncable/directory_unittest.cc
index ee48637..1d04c3a 100644
--- a/sync/syncable/directory_unittest.cc
+++ b/sync/syncable/directory_unittest.cc
@@ -1865,21 +1865,21 @@
       PREFERENCES, "some other entry", id2, attachment_metadata);
 
   // See that Directory reports that this attachment is not on the server.
-  AttachmentIdSet id_set;
+  AttachmentIdList ids;
   {
     ReadTransaction trans(FROM_HERE, dir().get());
-    dir()->GetAttachmentIdsToUpload(&trans, PREFERENCES, &id_set);
+    dir()->GetAttachmentIdsToUpload(&trans, PREFERENCES, &ids);
   }
-  ASSERT_EQ(1U, id_set.size());
-  ASSERT_EQ(attachment_id, *id_set.begin());
+  ASSERT_EQ(1U, ids.size());
+  ASSERT_EQ(attachment_id, *ids.begin());
 
   // Call again, but this time with a ModelType for which there are no entries.
   // See that Directory correctly reports that there are none.
   {
     ReadTransaction trans(FROM_HERE, dir().get());
-    dir()->GetAttachmentIdsToUpload(&trans, PASSWORDS, &id_set);
+    dir()->GetAttachmentIdsToUpload(&trans, PASSWORDS, &ids);
   }
-  ASSERT_TRUE(id_set.empty());
+  ASSERT_TRUE(ids.empty());
 
   // Now, mark the attachment as "on the server" via entry_1.
   {
@@ -1892,9 +1892,9 @@
   // server.
   {
     ReadTransaction trans(FROM_HERE, dir().get());
-    dir()->GetAttachmentIdsToUpload(&trans, PREFERENCES, &id_set);
+    dir()->GetAttachmentIdsToUpload(&trans, PREFERENCES, &ids);
   }
-  ASSERT_TRUE(id_set.empty());
+  ASSERT_TRUE(ids.empty());
 }
 
 // Verify that the directory accepts entries with unset parent ID.
diff --git a/sync/test/fake_server/unique_client_entity.cc b/sync/test/fake_server/unique_client_entity.cc
index 3d08c61..994c9d0 100644
--- a/sync/test/fake_server/unique_client_entity.cc
+++ b/sync/test/fake_server/unique_client_entity.cc
@@ -74,7 +74,7 @@
     const string& name,
     const sync_pb::EntitySpecifics& entity_specifics) {
   string client_defined_unique_tag = GenerateSyncableHash(model_type, name);
-  string id = FakeServerEntity::CreateId(model_type, base::GenerateGUID());
+  string id = FakeServerEntity::CreateId(model_type, client_defined_unique_tag);
   return scoped_ptr<FakeServerEntity>(
       new UniqueClientEntity(id,
                              model_type,
diff --git a/testing/android/junit/BUILD.gn b/testing/android/junit/BUILD.gn
index f80d0b57..a338fca3 100644
--- a/testing/android/junit/BUILD.gn
+++ b/testing/android/junit/BUILD.gn
@@ -8,6 +8,7 @@
 
 # GYP: //testing/android/junit_test.gyp:junit_test_support
 java_library("junit_test_support") {
+  testonly = true
   DEPRECATED_java_in_dir = "java/src"
   deps = [
     "//third_party/junit",
@@ -18,6 +19,7 @@
 
 # GYP: //testing/android/junit_test.gyp:junit_unit_tests
 java_binary("junit_unittests") {
+  testonly = true
   deps = [
     ":junit_test_support",
     "//third_party/junit",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 64f2cd5..f7f561d 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -65,6 +65,7 @@
         "test": "sandbox_linux_unittests",
         "args": ["--test-launcher-print-test-stdio=always"]
       },
+      "gfx_unittests",
       "ui_base_unittests",
       "ui_chromeos_unittests",
       "ui_touch_selection_unittests",
@@ -171,6 +172,7 @@
       "skia_unittests",
       "sql_unittests",
       "sync_unit_tests",
+      "gfx_unittests",
       "ui_base_unittests",
       "ui_chromeos_unittests",
       "ui_touch_selection_unittests",
@@ -225,6 +227,7 @@
         "test": "sandbox_linux_unittests",
         "args": ["--test-launcher-print-test-stdio=always"]
       },
+      "gfx_unittests",
       "ui_base_unittests",
       "ui_chromeos_unittests",
       "ui_touch_selection_unittests",
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 48ed324..7d74bc4 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -1,6 +1,7 @@
 {
   "Linux Tests": {
     "gtest_tests": [
+      "gn_unittests",
       {
         "test": "accessibility_unittests",
         "swarming": {
@@ -195,6 +196,7 @@
   },
   "Linux Tests (dbg)(1)(32)": {
     "gtest_tests": [
+      "gn_unittests",
       {
         "test": "accessibility_unittests",
         "swarming": {
@@ -363,6 +365,7 @@
   },
   "Linux Tests (dbg)(1)": {
     "gtest_tests": [
+      "gn_unittests",
       {
         "test": "accessibility_unittests",
         "swarming": {
@@ -543,6 +546,7 @@
   },
   "Linux Clang (dbg)": {
     "gtest_tests": [
+      "gn_unittests",
       "accessibility_unittests",
       "app_shell_unittests",
       "base_unittests",
@@ -573,7 +577,7 @@
   "Android Tests (dbg)": {
     "scripts": [
       {
-        "name": "check_licenses",
+        "name": "webview_licenses",
         "script": "webview_licenses.py"
       }
     ]
@@ -581,7 +585,7 @@
   "Android Tests": {
     "scripts": [
       {
-        "name": "check_licenses",
+        "name": "webview_licenses",
         "script": "webview_licenses.py"
       }
     ]
diff --git a/testing/chromoting/chromoting_integration_tests.isolate b/testing/chromoting/chromoting_integration_tests.isolate
index 8dcbe5d..bd6c841 100644
--- a/testing/chromoting/chromoting_integration_tests.isolate
+++ b/testing/chromoting/chromoting_integration_tests.isolate
@@ -31,6 +31,9 @@
           '<(PRODUCT_DIR)/remoting/com.google.chrome.remote_desktop.json',
           '<(PRODUCT_DIR)/remoting/com.google.chrome.remote_assistance.json',
           '<(PRODUCT_DIR)/remoting-me2me-host.deb',
+          '<(PRODUCT_DIR)/nacl_helper',
+          '<(PRODUCT_DIR)/nacl_helper_bootstrap',
+          '<(PRODUCT_DIR)/pnacl/',
         ],
       },
     }],
diff --git a/testing/commit_queue/config.json b/testing/commit_queue/config.json
index af3b7bb6..16edb179 100644
--- a/testing/commit_queue/config.json
+++ b/testing/commit_queue/config.json
@@ -6,9 +6,6 @@
     "git_repo_url": "https://chromium.googlesource.com/chromium/src",
     "hide_ref_in_committed_msg": true,
     "max_commit_burst": 2,
-    "project_bases": [
-        ".*"
-    ],
     "project_bases_legacy": [
         "^svn\\:\\/\\/svn\\.chromium\\.org\\/chrome/trunk/src(|/.*)$",
         "^svn\\:\\/\\/chrome\\-svn\\/chrome/trunk/src(|/.*)$",
@@ -39,10 +36,10 @@
                             "android_amp_rel_tests_recipe": [
                                 "defaulttests"
                             ],
-                            "cast_shell": [
+                            "cast_shell_android": [
                                 "defaulttests"
                             ],
-                            "cast_shell_apk": [
+                            "cast_shell_linux": [
                                 "defaulttests"
                             ],
                             "linux_arm_compile": [
diff --git a/third_party/analytics/README.chromium b/third_party/analytics/README.chromium
index a63989c..27806bc 100644
--- a/third_party/analytics/README.chromium
+++ b/third_party/analytics/README.chromium
@@ -1,7 +1,7 @@
 Name: Chrome Platform Analytics
 URL: https://github.com/GoogleChrome/chrome-platform-analytics
-Version: 1.6.0
-Date: 3/9/2015
+Version: 1.6.0c
+Date: 3/24/2015
 License: Apache 2.0
 License File: NOT_SHIPPED
 Security Critical: yes
@@ -15,7 +15,7 @@
 traditional JS apps (non-closure-compiled) can employ the library.
 
 The SHA1 hash for this version of GoogleChrome/chrome-platform-analytics is
-c2a12c437b170da922ea54b79933174d2f5fcb05.
+472ed678a2322e9a18b10eadb5adba2cde68529a.
 
 Local Modifications:
 Added externs.js allowing apps that do use closure compiler to compile code
diff --git a/third_party/analytics/google-analytics-bundle.js b/third_party/analytics/google-analytics-bundle.js
index 12d6d24..dd825ac 100644
--- a/third_party/analytics/google-analytics-bundle.js
+++ b/third_party/analytics/google-analytics-bundle.js
@@ -3,8 +3,8 @@
 function(a,b,c){return a.call.apply(a.bind,arguments)},ga=function(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}},t=function(a,b,c){t=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?fa:ga;return t.apply(null,arguments)},ha=function(a,b){var c=Array.prototype.slice.call(arguments,
 1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},u=Date.now||function(){return+new Date},v=function(a,b){var c=a.split("."),d=k;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)!c.length&&m(b)?d[e]=b:d=d[e]?d[e]:d[e]={}},w=function(a,b){function c(){}c.prototype=b.prototype;a.P=b.prototype;a.prototype=new c;a.ie=function(a,c,f){for(var g=Array(arguments.length-2),l=2;l<arguments.length;l++)g[l-2]=arguments[l];return b.prototype[c].apply(a,
 g)}};Function.prototype.bind=Function.prototype.bind||function(a,b){if(1<arguments.length){var c=Array.prototype.slice.call(arguments,1);c.unshift(this,a);return t.apply(null,c)}return t(this,a)};var x=function(a){if(Error.captureStackTrace)Error.captureStackTrace(this,x);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))};w(x,Error);x.prototype.name="CustomError";var ia=String.prototype.trim?function(a){return a.trim()}:function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},ja=function(a,b){return a<b?-1:a>b?1:0};var y=Array.prototype,ka=y.indexOf?function(a,b,c){return y.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(p(a))return p(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},la=y.forEach?function(a,b,c){y.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=p(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},ma=y.some?function(a,b,c){return y.some.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=p(a)?a.split(""):
-a,f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return!0;return!1},na=y.every?function(a,b,c){return y.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=p(a)?a.split(""):a,f=0;f<d;f++)if(f in e&&!b.call(c,e[f],f,a))return!1;return!0},pa=function(a){var b;t:{b=oa;for(var c=a.length,d=p(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break t}b=-1}return 0>b?null:p(a)?a.charAt(b):a[b]},qa=function(a,b){var c=ka(a,b),d;(d=0<=c)&&y.splice.call(a,c,1);return d},ra=function(a){return y.concat.apply(y,
-arguments)},sa=function(a,b,c){return 2>=arguments.length?y.slice.call(a,b):y.slice.call(a,b,c)};var ta="StopIteration"in k?k.StopIteration:Error("StopIteration"),ua=function(){};ua.prototype.next=function(){throw ta;};ua.prototype.Sb=function(){return this};var va=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)},wa=function(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b},xa=function(a){var b=[],c=0,d;for(d in a)b[c++]=d;return b},ya=function(a,b){var c;t:{for(c in a)if(b.call(void 0,a[c],c,a))break t;c=void 0}return c&&a[c]},za="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Aa=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<za.length;f++)c=
+a,f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return!0;return!1},na=y.every?function(a,b,c){return y.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=p(a)?a.split(""):a,f=0;f<d;f++)if(f in e&&!b.call(c,e[f],f,a))return!1;return!0},pa=function(a){var b;a:{b=oa;for(var c=a.length,d=p(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break a}b=-1}return 0>b?null:p(a)?a.charAt(b):a[b]},qa=function(a,b){var c=ka(a,b),d;(d=0<=c)&&y.splice.call(a,c,1);return d},ra=function(a){return y.concat.apply(y,
+arguments)},sa=function(a,b,c){return 2>=arguments.length?y.slice.call(a,b):y.slice.call(a,b,c)};var ta="StopIteration"in k?k.StopIteration:Error("StopIteration"),ua=function(){};ua.prototype.next=function(){throw ta;};ua.prototype.Sb=function(){return this};var va=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)},wa=function(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b},xa=function(a){var b=[],c=0,d;for(d in a)b[c++]=d;return b},ya=function(a,b){var c;a:{for(c in a)if(b.call(void 0,a[c],c,a))break a;c=void 0}return c&&a[c]},za="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Aa=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<za.length;f++)c=
 za[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}},Ba=function(a){var b=arguments.length;if(1==b&&n(arguments[0]))return Ba.apply(null,arguments[0]);for(var c={},d=0;d<b;d++)c[arguments[d]]=!0;return c};var z=function(a,b){this.p={};this.b=[];this.Ja=this.j=0;var c=arguments.length;if(1<c){if(c%2)throw Error("Uneven number of arguments");for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else a&&this.ha(a)};z.prototype.t=function(){Ca(this);for(var a=[],b=0;b<this.b.length;b++)a.push(this.p[this.b[b]]);return a};z.prototype.H=function(){Ca(this);return this.b.concat()};z.prototype.T=function(a){return A(this.p,a)};
 z.prototype.remove=function(a){return A(this.p,a)?(delete this.p[a],this.j--,this.Ja++,this.b.length>2*this.j&&Ca(this),!0):!1};var Ca=function(a){if(a.j!=a.b.length){for(var b=0,c=0;b<a.b.length;){var d=a.b[b];A(a.p,d)&&(a.b[c++]=d);b++}a.b.length=c}if(a.j!=a.b.length){for(var e={},c=b=0;b<a.b.length;)d=a.b[b],A(e,d)||(a.b[c++]=d,e[d]=1),b++;a.b.length=c}};h=z.prototype;h.get=function(a,b){return A(this.p,a)?this.p[a]:b};
 h.set=function(a,b){A(this.p,a)||(this.j++,this.b.push(a),this.Ja++);this.p[a]=b};h.ha=function(a){var b;a instanceof z?(b=a.H(),a=a.t()):(b=xa(a),a=wa(a));for(var c=0;c<b.length;c++)this.set(b[c],a[c])};h.forEach=function(a,b){for(var c=this.H(),d=0;d<c.length;d++){var e=c[d],f=this.get(e);a.call(b,f,e,this)}};h.clone=function(){return new z(this)};h.Nb=function(){Ca(this);for(var a={},b=0;b<this.b.length;b++){var c=this.b[b];a[c]=this.p[c]}return a};
@@ -20,26 +20,26 @@
 a)throw Error("Expected metric index range 1-200, but was : "+a);return{id:"metric"+a,name:"cm"+a,valueType:"integer",maxLength:void 0,defaultValue:void 0}};var Pa=function(a){if(1>a)return"0";if(3>a)return"1-2";a=Math.floor(Math.log(a-1)/Math.log(2));return Math.pow(2,a)+1+"-"+Math.pow(2,a+1)},Qa=function(a,b){for(var c=0,d=a.length-1,e=0;c<=d;){var f=Math.floor((c+d)/2),e=a[f];if(b<=e){d=0==f?0:a[f-1];if(b>d)return(d+1).toString()+"-"+e.toString();d=f-1}else if(b>e){if(f>=a.length-1)return(a[a.length-1]+1).toString()+"+";c=f+1}}return"<= 0"};var B=function(){this.gb=[]},Ra=function(){return new B};h=B.prototype;h.when=function(a){this.gb.push(a);return this};h.Rb=function(a){var b=arguments;this.when(function(a){return 0<=ka(b,a.ub())});return this};h.Yc=function(a,b){var c=sa(arguments,1);this.when(function(b){b=b.V().get(a);return 0<=ka(c,b)});return this};h.mb=function(a,b){if(r(this.e))throw Error("Filter has already been set.");this.e=r(b)?t(a,b):a;return this};
 h.ia=function(){if(0==this.gb.length)throw Error("Must specify at least one predicate using #when or a helper method.");if(!r(this.e))throw Error("Must specify a delegate filter using #applyFilter.");return t(function(a){na(this.gb,function(b){return b(a)})&&this.e(a)},this)};var C=function(){this.lb=!1;this.zb="";this.Kb=!1;this.ya=null};C.prototype.Wb=function(a){this.lb=!0;this.zb=a||" - ";return this};C.prototype.Sc=function(){this.Kb=!0;return this};C.prototype.Dc=function(){return Sa(this,Pa)};C.prototype.Ec=function(a){return Sa(this,ha(Qa,a))};
 var Sa=function(a,b){if(null!=a.ya)throw Error("LabelerBuilder: Only one labeling strategy may be used.");a.ya=t(function(a){var d=a.V().get(La),e=a.V().get(Ka);ea(d)&&(d=b(d),null!=e&&this.lb&&(d=e+this.zb+d),a.V().set(Ka,d))},a);return a};C.prototype.ia=function(){if(null==this.ya)throw Error("LabelerBuilder: a labeling strategy must be specified prior to calling build().");return Ra().Rb("event").mb(t(function(a){this.ya(a);this.Kb&&a.V().remove(La)},this)).ia()};var Ua=function(a,b){var c=Array.prototype.slice.call(arguments),d=c.shift();if("undefined"==typeof d)throw Error("[goog.string.format] Template required");return d.replace(/%([0\-\ \+]*)(\d+)?(\.(\d+))?([%sfdiu])/g,function(a,b,d,l,D,N,Y,Z){if("%"==N)return"%";var Nb=c.shift();if("undefined"==typeof Nb)throw Error("[goog.string.format] Not enough arguments");arguments[0]=Nb;return Ta[N].apply(null,arguments)})},Ta={s:function(a,b,c){return isNaN(c)||""==c||a.length>=c?a:a=-1<b.indexOf("-",0)?a+Array(c-
-a.length+1).join(" "):Array(c-a.length+1).join(" ")+a},f:function(a,b,c,d,e){d=a.toString();isNaN(e)||""==e||(d=a.toFixed(e));var f;f=0>a?"-":0<=b.indexOf("+")?"+":0<=b.indexOf(" ")?" ":"";0<=a&&(d=f+d);if(isNaN(c)||d.length>=c)return d;d=isNaN(e)?Math.abs(a).toString():Math.abs(a).toFixed(e);a=c-d.length-f.length;return d=0<=b.indexOf("-",0)?f+d+Array(a+1).join(" "):f+Array(a+1).join(0<=b.indexOf("0",0)?"0":" ")+d},d:function(a,b,c,d,e,f,g,l){return Ta.f(parseInt(a,10),b,c,d,0,f,g,l)}};Ta.i=Ta.d;
-Ta.u=Ta.d;var Va=function(a){if("function"==typeof a.t)return a.t();if(p(a))return a.split("");if(da(a)){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}return wa(a)},Wa=function(a,b){if("function"==typeof a.forEach)a.forEach(b,void 0);else if(da(a)||p(a))la(a,b,void 0);else{var c;if("function"==typeof a.H)c=a.H();else if("function"!=typeof a.t)if(da(a)||p(a)){c=[];for(var d=a.length,e=0;e<d;e++)c.push(e)}else c=xa(a);else c=void 0;for(var d=Va(a),e=d.length,f=0;f<e;f++)b.call(void 0,d[f],c&&c[f],
+a.length+1).join(" "):Array(c-a.length+1).join(" ")+a},f:function(a,b,c,d,e){d=a.toString();isNaN(e)||""==e||(d=parseFloat(a).toFixed(e));var f;f=0>a?"-":0<=b.indexOf("+")?"+":0<=b.indexOf(" ")?" ":"";0<=a&&(d=f+d);if(isNaN(c)||d.length>=c)return d;d=isNaN(e)?Math.abs(a).toString():Math.abs(a).toFixed(e);a=c-d.length-f.length;return d=0<=b.indexOf("-",0)?f+d+Array(a+1).join(" "):f+Array(a+1).join(0<=b.indexOf("0",0)?"0":" ")+d},d:function(a,b,c,d,e,f,g,l){return Ta.f(parseInt(a,10),b,c,d,0,f,g,l)}};
+Ta.i=Ta.d;Ta.u=Ta.d;var Va=function(a){if("function"==typeof a.t)return a.t();if(p(a))return a.split("");if(da(a)){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}return wa(a)},Wa=function(a,b){if("function"==typeof a.forEach)a.forEach(b,void 0);else if(da(a)||p(a))la(a,b,void 0);else{var c;if("function"==typeof a.H)c=a.H();else if("function"!=typeof a.t)if(da(a)||p(a)){c=[];for(var d=a.length,e=0;e<d;e++)c.push(e)}else c=xa(a);else c=void 0;for(var d=Va(a),e=d.length,f=0;f<e;f++)b.call(void 0,d[f],c&&c[f],
 a)}};var E=function(a){this.w=new z;if(0<arguments.length%2)throw Error("Uneven number of arguments to ParameterMap constructor.");for(var b=arguments,c=0;c<b.length;c+=2)this.set(b[c],b[c+1])};E.prototype.set=function(a,b){if(null==b)throw Error("undefined-or-null value for key: "+a.name);this.w.set(a.name,{key:a,value:b})};E.prototype.remove=function(a){this.w.remove(a.name)};E.prototype.get=function(a){a=this.w.get(a.name,null);return null===a?null:a.value};E.prototype.ha=function(a){this.w.ha(a.w)};
 var Xa=function(a,b){la(a.w.t(),function(a){b(a.key,a.value)})};E.prototype.Nb=function(){var a={};Xa(this,function(b,c){a[b.id]=c});return a};E.prototype.clone=function(){var a=new E;a.w=this.w.clone();return a};E.prototype.toString=function(){var a={};Xa(this,function(b,c){a[b.id]=c});return JSON.stringify(a)};var F=function(a){this.e=a};h=F.prototype;h.Yb=function(a){var b=new F(t(this.M,this));b.I=Ia;b.Q=a;return b};h.action=function(a){var b=new F(t(this.M,this));b.I=Ja;b.Q=a;return b};h.label=function(a){var b=new F(t(this.M,this));b.I=Ka;b.Q=a;return b};h.value=function(a){var b=new F(t(this.M,this));b.I=La;b.Q=a;return b};h.fc=function(a){var b=new F(t(this.M,this));b.I=Na(a.index);b.Q=a.value;return b};h.vc=function(a){var b=new F(t(this.M,this));b.I=Oa(a.index);b.Q=a.value;return b};
-h.send=function(a){var b=new E;this.M(b);return a.send("event",b)};h.M=function(a){null!=this.I&&null!=this.Q&&!a.w.T(this.I.name)&&a.set(this.I,this.Q);r(this.e)&&this.e(a)};var Ya=new F(ba);var G=function(){this.aa=this.aa;this.Ca=this.Ca};G.prototype.aa=!1;G.prototype.ma=function(){this.aa||(this.aa=!0,this.o())};G.prototype.o=function(){if(this.Ca)for(;this.Ca.length;)this.Ca.shift()()};var Za=function(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.X=!1;this.Hb=!0};Za.prototype.preventDefault=function(){this.defaultPrevented=!0;this.Hb=!1};var $a=function(a){$a[" "](a);return a};$a[" "]=ba;var H;t:{var ab=k.navigator;if(ab){var bb=ab.userAgent;if(bb){H=bb;break t}}H=""};var cb=function(){return-1!=H.indexOf("Edge")||-1!=H.indexOf("Trident")||-1!=H.indexOf("MSIE")};var I=function(){return-1!=H.indexOf("Edge")};var db=-1!=H.indexOf("Opera")||-1!=H.indexOf("OPR"),J=cb(),eb=-1!=H.indexOf("Gecko")&&!(-1!=H.toLowerCase().indexOf("webkit")&&!I())&&!(-1!=H.indexOf("Trident")||-1!=H.indexOf("MSIE"))&&!I(),fb=-1!=H.toLowerCase().indexOf("webkit")&&!I(),gb=function(){var a=H;if(eb)return/rv\:([^\);]+)(\)|;)/.exec(a);if(J&&I())return/Edge\/([\d\.]+)/.exec(a);if(J)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(fb)return/WebKit\/(\S+)/.exec(a)},hb=function(){var a=k.document;return a?a.documentMode:void 0},ib=
+h.send=function(a){var b=new E;this.M(b);return a.send("event",b)};h.M=function(a){null!=this.I&&null!=this.Q&&!a.w.T(this.I.name)&&a.set(this.I,this.Q);r(this.e)&&this.e(a)};var Ya=new F(ba);var G=function(){this.aa=this.aa;this.Ca=this.Ca};G.prototype.aa=!1;G.prototype.ma=function(){this.aa||(this.aa=!0,this.o())};G.prototype.o=function(){if(this.Ca)for(;this.Ca.length;)this.Ca.shift()()};var Za=function(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.X=!1;this.Hb=!0};Za.prototype.preventDefault=function(){this.defaultPrevented=!0;this.Hb=!1};var $a=function(a){$a[" "](a);return a};$a[" "]=ba;var H;a:{var ab=k.navigator;if(ab){var bb=ab.userAgent;if(bb){H=bb;break a}}H=""};var cb=function(){return-1!=H.indexOf("Edge")||-1!=H.indexOf("Trident")||-1!=H.indexOf("MSIE")};var I=function(){return-1!=H.indexOf("Edge")};var db=-1!=H.indexOf("Opera")||-1!=H.indexOf("OPR"),J=cb(),eb=-1!=H.indexOf("Gecko")&&!(-1!=H.toLowerCase().indexOf("webkit")&&!I())&&!(-1!=H.indexOf("Trident")||-1!=H.indexOf("MSIE"))&&!I(),fb=-1!=H.toLowerCase().indexOf("webkit")&&!I(),gb=function(){var a=H;if(eb)return/rv\:([^\);]+)(\)|;)/.exec(a);if(J&&I())return/Edge\/([\d\.]+)/.exec(a);if(J)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(fb)return/WebKit\/(\S+)/.exec(a)},hb=function(){var a=k.document;return a?a.documentMode:void 0},ib=
 function(){if(db&&k.opera){var a=k.opera.version;return q(a)?a():a}var a="",b=gb();b&&(a=b?b[1]:"");return J&&!I()&&(b=hb(),b>parseFloat(a))?String(b):a}(),jb={},K=function(a){var b;if(!(b=jb[a])){b=0;for(var c=ia(String(ib)).split("."),d=ia(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var g=c[f]||"",l=d[f]||"",D=/(\d*)(\D*)/g,N=/(\d*)(\D*)/g;do{var Y=D.exec(g)||["","",""],Z=N.exec(l)||["","",""];if(0==Y[0].length&&0==Z[0].length)break;b=ja(0==Y[1].length?0:parseInt(Y[1],
-10),0==Z[1].length?0:parseInt(Z[1],10))||ja(0==Y[2].length,0==Z[2].length)||ja(Y[2],Z[2])}while(0==b)}b=jb[a]=0<=b}return b},kb=k.document,lb=hb(),mb=!kb||!J||!lb&&I()?void 0:lb||("CSS1Compat"==kb.compatMode?parseInt(ib,10):5);var nb=!J||J&&(I()||9<=mb),ob=J&&!K("9"),pb=!fb||K("528"),qb=eb&&K("1.9b")||J&&K("8")||db&&K("9.5")||fb&&K("528"),rb=eb&&!K("8")||J&&!K("9");var sb=function(a,b){Za.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.sb=this.state=null;if(a){var c=this.type=a.type;this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(d){if(eb){var e;t:{try{$a(d.nodeName);e=!0;break t}catch(f){}e=!1}e||(d=null)}}else"mouseover"==
+10),0==Z[1].length?0:parseInt(Z[1],10))||ja(0==Y[2].length,0==Z[2].length)||ja(Y[2],Z[2])}while(0==b)}b=jb[a]=0<=b}return b},kb=k.document,lb=hb(),mb=!kb||!J||!lb&&I()?void 0:lb||("CSS1Compat"==kb.compatMode?parseInt(ib,10):5);var nb=!J||J&&(I()||9<=mb),ob=J&&!K("9"),pb=!fb||K("528"),qb=eb&&K("1.9b")||J&&K("8")||db&&K("9.5")||fb&&K("528"),rb=eb&&!K("8")||J&&!K("9");var sb=function(a,b){Za.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.sb=this.state=null;if(a){var c=this.type=a.type;this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(d){if(eb){var e;a:{try{$a(d.nodeName);e=!0;break a}catch(f){}e=!1}e||(d=null)}}else"mouseover"==
 c?d=a.fromElement:"mouseout"==c&&(d=a.toElement);this.relatedTarget=d;this.offsetX=fb||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=fb||void 0!==a.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;
 this.metaKey=a.metaKey;this.state=a.state;this.sb=a;a.defaultPrevented&&this.preventDefault()}};w(sb,Za);sb.prototype.preventDefault=function(){sb.P.preventDefault.call(this);var a=this.sb;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,ob)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var tb="closure_listenable_"+(1E6*Math.random()|0),ub=function(a){return!(!a||!a[tb])},vb=0;var wb=function(a,b,c,d,e){this.O=a;this.proxy=null;this.src=b;this.type=c;this.ka=!!d;this.sa=e;this.key=++vb;this.removed=this.ja=!1},xb=function(a){a.removed=!0;a.O=null;a.proxy=null;a.src=null;a.sa=null};var L=function(a){this.src=a;this.k={};this.fa=0};L.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.k[f];a||(a=this.k[f]=[],this.fa++);var g=yb(a,b,d,e);-1<g?(b=a[g],c||(b.ja=!1)):(b=new wb(b,this.src,f,!!d,e),b.ja=c,a.push(b));return b};L.prototype.remove=function(a,b,c,d){a=a.toString();if(!(a in this.k))return!1;var e=this.k[a];b=yb(e,b,c,d);return-1<b?(xb(e[b]),y.splice.call(e,b,1),0==e.length&&(delete this.k[a],this.fa--),!0):!1};
 var zb=function(a,b){var c=b.type;if(!(c in a.k))return!1;var d=qa(a.k[c],b);d&&(xb(b),0==a.k[c].length&&(delete a.k[c],a.fa--));return d};L.prototype.removeAll=function(a){a=a&&a.toString();var b=0,c;for(c in this.k)if(!a||c==a){for(var d=this.k[c],e=0;e<d.length;e++)++b,xb(d[e]);delete this.k[c];this.fa--}return b};L.prototype.ba=function(a,b,c,d){a=this.k[a.toString()];var e=-1;a&&(e=yb(a,b,c,d));return-1<e?a[e]:null};
 var yb=function(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.removed&&f.O==b&&f.ka==!!c&&f.sa==d)return e}return-1};var Ab="closure_lm_"+(1E6*Math.random()|0),Bb={},Cb=0,Db=function(a,b,c,d,e){if(n(b)){for(var f=0;f<b.length;f++)Db(a,b[f],c,d,e);return null}c=Eb(c);return ub(a)?a.listen(b,c,d,e):Fb(a,b,c,!1,d,e)},Fb=function(a,b,c,d,e,f){if(!b)throw Error("Invalid event type");var g=!!e,l=Gb(a);l||(a[Ab]=l=new L(a));c=l.add(b,c,d,e,f);if(c.proxy)return c;d=Hb();c.proxy=d;d.src=a;d.O=c;a.addEventListener?a.addEventListener(b.toString(),d,g):a.attachEvent(Ib(b.toString()),d);Cb++;return c},Hb=function(){var a=Jb,
 b=nb?function(c){return a.call(b.src,b.O,c)}:function(c){c=a.call(b.src,b.O,c);if(!c)return c};return b},Kb=function(a,b,c,d,e){if(n(b)){for(var f=0;f<b.length;f++)Kb(a,b[f],c,d,e);return null}c=Eb(c);return ub(a)?a.cb(b,c,d,e):Fb(a,b,c,!0,d,e)},Lb=function(a,b,c,d,e){if(n(b))for(var f=0;f<b.length;f++)Lb(a,b[f],c,d,e);else c=Eb(c),ub(a)?a.jb(b,c,d,e):a&&(a=Gb(a))&&(b=a.ba(b,c,!!d,e))&&Mb(b)},Mb=function(a){if(ea(a)||!a||a.removed)return!1;var b=a.src;if(ub(b))return zb(b.B,a);var c=a.type,d=a.proxy;
 b.removeEventListener?b.removeEventListener(c,d,a.ka):b.detachEvent&&b.detachEvent(Ib(c),d);Cb--;(c=Gb(b))?(zb(c,a),0==c.fa&&(c.src=null,b[Ab]=null)):xb(a);return!0},Ib=function(a){return a in Bb?Bb[a]:Bb[a]="on"+a},Pb=function(a,b,c,d){var e=!0;if(a=Gb(a))if(b=a.k[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.ka==c&&!f.removed&&(f=Ob(f,d),e=e&&!1!==f)}return e},Ob=function(a,b){var c=a.O,d=a.sa||a.src;a.ja&&Mb(a);return c.call(d,b)},Jb=function(a,b){if(a.removed)return!0;if(!nb){var c;
-if(!(c=b))t:{c=["window","event"];for(var d=k,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break t}c=d}e=c;c=new sb(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){t:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break t}catch(g){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,l=e.length-1;!c.X&&0<=l;l--){c.currentTarget=e[l];var D=Pb(e[l],f,!0,c),d=d&&D}for(l=0;!c.X&&l<e.length;l++)c.currentTarget=e[l],D=Pb(e[l],f,!1,
+if(!(c=b))a:{c=["window","event"];for(var d=k,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break a}c=d}e=c;c=new sb(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(g){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,l=e.length-1;!c.X&&0<=l;l--){c.currentTarget=e[l];var D=Pb(e[l],f,!0,c),d=d&&D}for(l=0;!c.X&&l<e.length;l++)c.currentTarget=e[l],D=Pb(e[l],f,!1,
 c),d=d&&D}return d}return Ob(a,new sb(b,this))},Gb=function(a){a=a[Ab];return a instanceof L?a:null},Qb="__closure_events_fn_"+(1E9*Math.random()>>>0),Eb=function(a){if(q(a))return a;a[Qb]||(a[Qb]=function(b){return a.handleEvent(b)});return a[Qb]};var M=function(){G.call(this);this.B=new L(this);this.Tb=this;this.fb=null};w(M,G);M.prototype[tb]=!0;h=M.prototype;h.addEventListener=function(a,b,c,d){Db(this,a,b,c,d)};h.removeEventListener=function(a,b,c,d){Lb(this,a,b,c,d)};
 h.dispatchEvent=function(a){var b,c=this.fb;if(c){b=[];for(var d=1;c;c=c.fb)b.push(c),++d}c=this.Tb;d=a.type||a;if(p(a))a=new Za(a,c);else if(a instanceof Za)a.target=a.target||c;else{var e=a;a=new Za(d,c);Aa(a,e)}var e=!0,f;if(b)for(var g=b.length-1;!a.X&&0<=g;g--)f=a.currentTarget=b[g],e=Rb(f,d,!0,a)&&e;a.X||(f=a.currentTarget=c,e=Rb(f,d,!0,a)&&e,a.X||(e=Rb(f,d,!1,a)&&e));if(b)for(g=0;!a.X&&g<b.length;g++)f=a.currentTarget=b[g],e=Rb(f,d,!1,a)&&e;return e};
 h.o=function(){M.P.o.call(this);this.B&&this.B.removeAll(void 0);this.fb=null};h.listen=function(a,b,c,d){return this.B.add(String(a),b,!1,c,d)};h.cb=function(a,b,c,d){return this.B.add(String(a),b,!0,c,d)};h.jb=function(a,b,c,d){return this.B.remove(String(a),b,c,d)};var Rb=function(a,b,c,d){b=a.B.k[String(b)];if(!b)return!0;b=b.concat();for(var e=!0,f=0;f<b.length;++f){var g=b[f];if(g&&!g.removed&&g.ka==c){var l=g.O,D=g.sa||g.src;g.ja&&zb(a.B,g);e=!1!==l.call(D,d)&&e}}return e&&0!=d.Hb};
-M.prototype.ba=function(a,b,c,d){return this.B.ba(String(a),b,c,d)};var Sb=function(a,b,c){this.tc=c;this.dc=a;this.Gc=b;this.Ba=0;this.ta=null};Sb.prototype.get=function(){var a;0<this.Ba?(this.Ba--,a=this.ta,this.ta=a.next,a.next=null):a=this.dc();return a};Sb.prototype.put=function(a){this.Gc(a);this.Ba<this.tc&&(this.Ba++,a.next=this.ta,this.ta=a)};var Tb=function(a){k.setTimeout(function(){throw a;},0)},Ub,Vb=function(){var a=k.MessageChannel;"undefined"===typeof a&&"undefined"!==typeof window&&window.postMessage&&window.addEventListener&&-1==H.indexOf("Presto")&&(a=function(){var a=document.createElement("iframe");a.style.display="none";a.src="";document.documentElement.appendChild(a);var b=a.contentWindow,a=b.document;a.open();a.write("");a.close();var c="callImmediate"+Math.random(),d="file:"==b.location.protocol?"*":b.location.protocol+
-"//"+b.location.host,a=t(function(a){if(("*"==d||a.origin==d)&&a.data==c)this.port1.onmessage()},this);b.addEventListener("message",a,!1);this.port1={};this.port2={postMessage:function(){b.postMessage(c,d)}}});if("undefined"!==typeof a&&!cb()){var b=new a,c={},d=c;b.port1.onmessage=function(){if(m(c.next)){c=c.next;var a=c.ob;c.ob=null;a()}};return function(a){d.next={ob:a};d=d.next;b.port2.postMessage(0)}}return"undefined"!==typeof document&&"onreadystatechange"in document.createElement("script")?
-function(a){var b=document.createElement("script");b.onreadystatechange=function(){b.onreadystatechange=null;b.parentNode.removeChild(b);b=null;a();a=null};document.documentElement.appendChild(b)}:function(a){k.setTimeout(a,0)}};var Wb=function(){this.Ka=this.Z=null},Yb=new Sb(function(){return new Xb},function(a){a.reset()},100);Wb.prototype.add=function(a,b){var c=Yb.get();c.set(a,b);this.Ka?this.Ka.next=c:this.Z=c;this.Ka=c};Wb.prototype.remove=function(){var a=null;this.Z&&(a=this.Z,this.Z=this.Z.next,this.Z||(this.Ka=null),a.next=null);return a};var Xb=function(){this.next=this.scope=this.Wa=null};Xb.prototype.set=function(a,b){this.Wa=a;this.scope=b;this.next=null};
+M.prototype.ba=function(a,b,c,d){return this.B.ba(String(a),b,c,d)};var Sb=function(a,b,c){this.tc=c;this.dc=a;this.Gc=b;this.Ba=0;this.ta=null};Sb.prototype.get=function(){var a;0<this.Ba?(this.Ba--,a=this.ta,this.ta=a.next,a.next=null):a=this.dc();return a};Sb.prototype.put=function(a){this.Gc(a);this.Ba<this.tc&&(this.Ba++,a.next=this.ta,this.ta=a)};var Tb=function(a){k.setTimeout(function(){throw a;},0)},Ub,Vb=function(){var a=k.MessageChannel;"undefined"===typeof a&&"undefined"!==typeof window&&window.postMessage&&window.addEventListener&&-1==H.indexOf("Presto")&&(a=function(){var a=document.createElement("IFRAME");a.style.display="none";a.src="";document.documentElement.appendChild(a);var b=a.contentWindow,a=b.document;a.open();a.write("");a.close();var c="callImmediate"+Math.random(),d="file:"==b.location.protocol?"*":b.location.protocol+
+"//"+b.location.host,a=t(function(a){if(("*"==d||a.origin==d)&&a.data==c)this.port1.onmessage()},this);b.addEventListener("message",a,!1);this.port1={};this.port2={postMessage:function(){b.postMessage(c,d)}}});if("undefined"!==typeof a&&!cb()){var b=new a,c={},d=c;b.port1.onmessage=function(){if(m(c.next)){c=c.next;var a=c.ob;c.ob=null;a()}};return function(a){d.next={ob:a};d=d.next;b.port2.postMessage(0)}}return"undefined"!==typeof document&&"onreadystatechange"in document.createElement("SCRIPT")?
+function(a){var b=document.createElement("SCRIPT");b.onreadystatechange=function(){b.onreadystatechange=null;b.parentNode.removeChild(b);b=null;a();a=null};document.documentElement.appendChild(b)}:function(a){k.setTimeout(a,0)}};var Wb=function(){this.Ka=this.Z=null},Yb=new Sb(function(){return new Xb},function(a){a.reset()},100);Wb.prototype.add=function(a,b){var c=Yb.get();c.set(a,b);this.Ka?this.Ka.next=c:this.Z=c;this.Ka=c};Wb.prototype.remove=function(){var a=null;this.Z&&(a=this.Z,this.Z=this.Z.next,this.Z||(this.Ka=null),a.next=null);return a};var Xb=function(){this.next=this.scope=this.Wa=null};Xb.prototype.set=function(a,b){this.Wa=a;this.scope=b;this.next=null};
 Xb.prototype.reset=function(){this.next=this.scope=this.Wa=null};var cc=function(a,b){Zb||$b();ac||(Zb(),ac=!0);bc.add(a,b)},Zb,$b=function(){if(k.Promise&&k.Promise.resolve){var a=k.Promise.resolve();Zb=function(){a.then(dc)}}else Zb=function(){var a=dc;!q(k.setImmediate)||k.Window&&k.Window.prototype&&k.Window.prototype.setImmediate==k.setImmediate?(Ub||(Ub=Vb()),Ub(a)):k.setImmediate(a)}},ac=!1,bc=new Wb,dc=function(){for(var a=null;a=bc.remove();){try{a.Wa.call(a.scope)}catch(b){Tb(b)}Yb.put(a)}ac=!1};var ec=function(a){a.prototype.then=a.prototype.then;a.prototype.$goog_Thenable=!0},fc=function(a){if(!a)return!1;try{return!!a.$goog_Thenable}catch(b){return!1}};var P=function(a,b){this.m=0;this.D=void 0;this.S=this.G=this.l=null;this.ra=this.Va=!1;if(a==gc)O(this,2,b);else try{var c=this;a.call(b,function(a){O(c,2,a)},function(a){O(c,3,a)})}catch(d){O(this,3,d)}},hc=function(){this.next=this.context=this.da=this.Da=this.L=null;this.Na=!1};hc.prototype.reset=function(){this.context=this.da=this.Da=this.L=null;this.Na=!1};
 var ic=new Sb(function(){return new hc},function(a){a.reset()},100),jc=function(a,b,c){var d=ic.get();d.Da=a;d.da=b;d.context=c;return d},gc=function(){};P.prototype.then=function(a,b,c){return kc(this,q(a)?a:null,q(b)?b:null,c)};ec(P);P.prototype.cancel=function(a){0==this.m&&cc(function(){var b=new lc(a);mc(this,b)},this)};
 var mc=function(a,b){if(0==a.m)if(a.l){var c=a.l;if(c.G){for(var d=0,e=null,f=null,g=c.G;g&&(g.Na||(d++,g.L==a&&(e=g),!(e&&1<d)));g=g.next)e||(f=g);e&&(0==c.m&&1==d?mc(c,b):(f?(d=f,d.next==c.S&&(c.S=d),d.next=d.next.next):nc(c),oc(c,e,3,b)))}a.l=null}else O(a,3,b)},qc=function(a,b){a.G||2!=a.m&&3!=a.m||pc(a);a.S?a.S.next=b:a.G=b;a.S=b},kc=function(a,b,c,d){var e=jc(null,null,null);e.L=new P(function(a,g){e.Da=b?function(c){try{var e=b.call(d,c);a(e)}catch(N){g(N)}}:a;e.da=c?function(b){try{var e=
@@ -55,7 +55,7 @@
 var Ac=function(a){var b=new Q;zc(a,b.v,b.A,b);return b},Bc=function(a){return ma(a.Fa,function(a){return q(a[1])})},wc=function(a){if(a.Ia&&a.C&&Bc(a)){var b=a.Ia,c=Cc[b];c&&(k.clearTimeout(c.ua),delete Cc[b]);a.Ia=0}a.l&&(a.l.Qa--,delete a.l);for(var b=a.D,d=c=!1;a.Fa.length&&!a.Oa;){var e=a.Fa.shift(),f=e[0],g=e[1],e=e[2];if(f=a.ca?g:f)try{var l=f.call(e||a.rb,b);m(l)&&(a.ca=a.ca&&(l==b||l instanceof Error),a.D=b=l);fc(b)&&(d=!0,a.Oa=!0)}catch(D){b=D,a.ca=!0,Bc(a)||(c=!0)}}a.D=b;d&&(l=t(a.qb,a,
 !0),d=t(a.qb,a,!1),b instanceof Q?(zc(b,l,d),b.Xb=!0):b.then(l,d));c&&(b=new Dc(b),Cc[b.ua]=b,a.Ia=b.ua)},Ec=function(a){var b=new Q;b.v(a);return b},Gc=function(){var a=Fc,b=new Q;b.A(a);return b},xc=function(){x.call(this)};w(xc,x);xc.prototype.message="Deferred has already fired";xc.prototype.name="AlreadyCalledError";var uc=function(){x.call(this)};w(uc,x);uc.prototype.message="Deferred was canceled";uc.prototype.name="CanceledError";
 var Dc=function(a){this.ua=k.setTimeout(t(this.Tc,this),0);this.na=a};Dc.prototype.Tc=function(){delete Cc[this.ua];throw this.na;};var Cc={};var Hc=function(a){this.qa=[];this.e=a};Hc.prototype.R=function(a){if(!q(a))throw Error("Invalid filter. Must be a function.");this.qa.push(a)};Hc.prototype.send=function(a,b){if(0==this.qa.length)return this.e.send(a,b);var c=new R(a,b);return Ic(this,0,c).n(function(){if(!c.Sa)return this.e.send(a,b)},this)};var Ic=function(a,b,c){return Ec().n(function(){return this.qa[b](c)},a).n(function(){if(++b<this.qa.length&&!c.Sa)return Ic(this,b,c)},a)},R=function(a,b){this.Wc=a;this.Cc=b;this.Sa=!1};
-R.prototype.ub=function(){return this.Wc};R.prototype.V=function(){return this.Cc};R.prototype.cancel=function(){this.Sa=!0};Ba("area base br col command embed hr img input keygen link meta param source track wbr".split(" "));Ba("action","cite","data","formaction","href","manifest","poster","src");Ba("embed","iframe","link","object","script","style","template");var Jc=function(a,b){this.width=a;this.height=b};Jc.prototype.clone=function(){return new Jc(this.width,this.height)};Jc.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};!eb&&!J||J&&J&&(I()||9<=mb)||eb&&K("1.9.1");J&&K("9");var Kc={id:"apiVersion",name:"v",valueType:"text",maxLength:void 0,defaultValue:void 0},Lc={id:"appName",name:"an",valueType:"text",maxLength:100,defaultValue:void 0},Mc={id:"appVersion",name:"av",valueType:"text",maxLength:100,defaultValue:void 0},Nc={id:"clientId",name:"cid",valueType:"text",maxLength:void 0,defaultValue:void 0},Oc={id:"language",name:"ul",valueType:"text",maxLength:20,defaultValue:void 0},Pc={id:"libVersion",name:"_v",valueType:"text",maxLength:void 0,defaultValue:void 0},Qc={id:"sampleRateOverride",
+R.prototype.ub=function(){return this.Wc};R.prototype.V=function(){return this.Cc};R.prototype.cancel=function(){this.Sa=!0};Ba("area base br col command embed hr img input keygen link meta param source track wbr".split(" "));var Jc=function(a,b){this.width=a;this.height=b};Jc.prototype.clone=function(){return new Jc(this.width,this.height)};Jc.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};!eb&&!J||J&&J&&(I()||9<=mb)||eb&&K("1.9.1");J&&K("9");var Kc={id:"apiVersion",name:"v",valueType:"text",maxLength:void 0,defaultValue:void 0},Lc={id:"appName",name:"an",valueType:"text",maxLength:100,defaultValue:void 0},Mc={id:"appVersion",name:"av",valueType:"text",maxLength:100,defaultValue:void 0},Nc={id:"clientId",name:"cid",valueType:"text",maxLength:void 0,defaultValue:void 0},Oc={id:"language",name:"ul",valueType:"text",maxLength:20,defaultValue:void 0},Pc={id:"libVersion",name:"_v",valueType:"text",maxLength:void 0,defaultValue:void 0},Qc={id:"sampleRateOverride",
 name:"usro",valueType:"integer",maxLength:void 0,defaultValue:void 0},Rc={id:"screenColors",name:"sd",valueType:"text",maxLength:20,defaultValue:void 0},Sc={id:"screenResolution",name:"sr",valueType:"text",maxLength:20,defaultValue:void 0},Tc={id:"trackingId",name:"tid",valueType:"text",maxLength:void 0,defaultValue:void 0},Uc={id:"viewportSize",name:"vp",valueType:"text",maxLength:20,defaultValue:void 0},Vc={ad:Kc,dd:Lc,ed:Mc,nd:Nc,Fd:Oc,Gd:Pc,Md:Qc,Nd:Rc,Od:Sc,ae:Tc,he:Uc},Xc=function(a){if(!p(a))return a;
 var b=Wc(a,Ma);if(r(b))return b;b=Wc(a,Vc);if(r(b))return b;b=/^dimension(\d+)$/.exec(a);if(null!==b)return Na(parseInt(b[1],10));b=/^metric(\d+)$/.exec(a);if(null!==b)return Oa(parseInt(b[1],10));throw Error(a+" is not a valid parameter name.");},Wc=function(a,b){var c=ya(b,function(b){return b.id==a&&"metric"!=a&&"dimension"!=a});return r(c)?c:null};var S=function(a,b){this.ac=b;this.q=b.Xa();this.Fb=new E;this.ib=!1};h=S.prototype;h.set=function(a,b){if(null==b)throw Error("Value must be defined and not null. Parameter="+a.id);var c=Xc(a);this.Fb.set(c,b)};h.R=function(a){this.ac.R(a)};h.send=function(a,b){if(a instanceof F)return a.send(this);var c=this.Fb.clone();b instanceof E?c.ha(b):r(b)&&va(b,function(a,b){null!=a&&c.set(Xc(b),a)},this);this.ib&&(this.ib=!1,c.set(Ga,"start"));return this.q.send(a,c)};
 h.Hc=function(a){var b={description:a};this.set(Ha,a);return this.send("appview",b)};h.Ic=function(a,b,c,d){return this.send("event",{eventCategory:a,eventAction:b,eventLabel:c,eventValue:d})};h.Kc=function(a,b,c){return this.send("social",{socialNetwork:a,socialAction:b,socialTarget:c})};h.Jc=function(a,b){return this.send("exception",{exDescription:a,exFatal:b})};h.Ib=function(a,b,c,d,e){return this.send("timing",{timingCategory:a,timingVar:b,timingLabel:d,timingValue:c,sampleRateOverride:e})};
@@ -74,12 +74,12 @@
 k.FormData;!(0<=ka(Sd,b))||d||c||f.set("Content-Type","application/x-www-form-urlencoded;charset=utf-8");f.forEach(function(a,b){this.a.setRequestHeader(b,a)},this);this.Gb&&(this.a.responseType=this.Gb);"withCredentials"in this.a&&(this.a.withCredentials=this.Zc);try{Vd(this),0<this.Ha&&((this.kb=Wd(this.a))?(this.a.timeout=this.Ha,this.a.ontimeout=t(this.Mb,this)):this.Ga=td(this.Mb,this.Ha,this)),this.va=!0,this.a.send(a),this.va=!1}catch(g){this.na(5,g)}};
 var Wd=function(a){return J&&K(9)&&ea(a.timeout)&&m(a.ontimeout)},oa=function(a){return"content-type"==a.toLowerCase()};X.prototype.Mb=function(){"undefined"!=typeof aa&&this.a&&(this.za="Timed out after "+this.Ha+"ms, aborting",this.dispatchEvent("timeout"),this.abort(8))};X.prototype.na=function(a,b){this.F=!1;this.a&&(this.N=!0,this.a.abort(),this.N=!1);this.za=b;Xd(this);Yd(this)};var Xd=function(a){a.Ua||(a.Ua=!0,a.dispatchEvent("complete"),a.dispatchEvent("error"))};
 X.prototype.abort=function(){this.a&&this.F&&(this.F=!1,this.N=!0,this.a.abort(),this.N=!1,this.dispatchEvent("complete"),this.dispatchEvent("abort"),Yd(this))};X.prototype.o=function(){this.a&&(this.F&&(this.F=!1,this.N=!0,this.a.abort(),this.N=!1),Yd(this,!0));X.P.o.call(this)};X.prototype.Db=function(){this.aa||(this.Za||this.va||this.N?Zd(this):this.wc())};X.prototype.wc=function(){Zd(this)};
-var Zd=function(a){if(a.F&&"undefined"!=typeof aa&&(!a.La[1]||4!=$d(a)||2!=ae(a)))if(a.va&&4==$d(a))td(a.Db,0,a);else if(a.dispatchEvent("readystatechange"),4==$d(a)){a.F=!1;try{var b=ae(a),c;t:switch(b){case 200:case 201:case 202:case 204:case 206:case 304:case 1223:c=!0;break t;default:c=!1}var d;if(!(d=c)){var e;if(e=0===b){var f=Jd(String(a.$a))[1]||null;if(!f&&self.location)var g=self.location.protocol,f=g.substr(0,g.length-1);e=!Rd.test(f?f.toLowerCase():"")}d=e}if(d)a.dispatchEvent("complete"),
+var Zd=function(a){if(a.F&&"undefined"!=typeof aa&&(!a.La[1]||4!=$d(a)||2!=ae(a)))if(a.va&&4==$d(a))td(a.Db,0,a);else if(a.dispatchEvent("readystatechange"),4==$d(a)){a.F=!1;try{var b=ae(a),c;a:switch(b){case 200:case 201:case 202:case 204:case 206:case 304:case 1223:c=!0;break a;default:c=!1}var d;if(!(d=c)){var e;if(e=0===b){var f=Jd(String(a.$a))[1]||null;if(!f&&self.location)var g=self.location.protocol,f=g.substr(0,g.length-1);e=!Rd.test(f?f.toLowerCase():"")}d=e}if(d)a.dispatchEvent("complete"),
 a.dispatchEvent("success");else{var l;try{l=2<$d(a)?a.a.statusText:""}catch(D){l=""}a.za=l+" ["+ae(a)+"]";Xd(a)}}finally{Yd(a)}}},Yd=function(a,b){if(a.a){Vd(a);var c=a.a,d=a.La[0]?ba:null;a.a=null;a.La=null;b||a.dispatchEvent("ready");try{c.onreadystatechange=d}catch(e){}}},Vd=function(a){a.a&&a.kb&&(a.a.ontimeout=null);ea(a.Ga)&&(k.clearTimeout(a.Ga),a.Ga=null)},$d=function(a){return a.a?a.a.readyState:0},ae=function(a){try{return 2<$d(a)?a.a.status:-1}catch(b){return-1}};var be=function(a,b,c){this.r=a||null;this.qc=!!c},ce=function(a){a.c||(a.c=new z,a.j=0,a.r&&Kd(a.r,function(b,c){a.add(decodeURIComponent(b.replace(/\+/g," ")),c)}))};h=be.prototype;h.c=null;h.j=null;h.add=function(a,b){ce(this);this.r=null;a=de(this,a);var c=this.c.get(a);c||this.c.set(a,c=[]);c.push(b);this.j++;return this};h.remove=function(a){ce(this);a=de(this,a);return this.c.T(a)?(this.r=null,this.j-=this.c.get(a).length,this.c.remove(a)):!1};h.T=function(a){ce(this);a=de(this,a);return this.c.T(a)};
 h.H=function(){ce(this);for(var a=this.c.t(),b=this.c.H(),c=[],d=0;d<b.length;d++)for(var e=a[d],f=0;f<e.length;f++)c.push(b[d]);return c};h.t=function(a){ce(this);var b=[];if(p(a))this.T(a)&&(b=ra(b,this.c.get(de(this,a))));else{a=this.c.t();for(var c=0;c<a.length;c++)b=ra(b,a[c])}return b};h.set=function(a,b){ce(this);this.r=null;a=de(this,a);this.T(a)&&(this.j-=this.c.get(a).length);this.c.set(a,[b]);this.j++;return this};
 h.get=function(a,b){var c=a?this.t(a):[];return 0<c.length?String(c[0]):b};h.toString=function(){if(this.r)return this.r;if(!this.c)return"";for(var a=[],b=this.c.H(),c=0;c<b.length;c++)for(var d=b[c],e=encodeURIComponent(String(d)),d=this.t(d),f=0;f<d.length;f++){var g=e;""!==d[f]&&(g+="="+encodeURIComponent(String(d[f])));a.push(g)}return this.r=a.join("&")};h.clone=function(){var a=new be;a.r=this.r;this.c&&(a.c=this.c.clone(),a.j=this.j);return a};
 var de=function(a,b){var c=String(b);a.qc&&(c=c.toLowerCase());return c};var ee=function(a,b){this.Mc=a;this.Aa=b};ee.prototype.send=function(a,b){if(pb&&!navigator.onLine)return Gc();var c=new Q,d=fe(a,b);d.length>this.Aa?c.A({status:"payload-too-big",la:Ua("Encoded hit length == %s, but should be <= %s.",d.length,this.Aa)}):Ud(this.Mc,function(){c.v(Dd)},d);return c};var fe=function(a,b){var c=new be;c.add(Fa.name,a);Xa(b,function(a,b){c.add(a.name,b.toString())});return c.toString()};var ge=function(a,b,c){this.g=a;this.Lc=b;this.Aa=c};ge.prototype.Xa=function(){if(!this.q){if(!Ac(this.g.ea).C)throw Error("Cannot construct shared channel prior to settings being ready.");new wd;var a=new yd(new ee(this.Lc,this.Aa)),b=new Fd;this.q=new xd(this.g,new Gd(this.g,new Ed(b,a)))}return this.q};var he=new z,ie=function(){Da||(Da=new T(new ld));return Da};v("goog.async.Deferred",Q);v("goog.async.Deferred.prototype.addCallback",Q.prototype.n);v("goog.async.Deferred.prototype.callback",Q.prototype.v);v("goog.async.Deferred.prototype.then",Q.prototype.then);v("goog.events.EventTarget",M);v("goog.events.EventTarget.prototype.listen",M.prototype.listen);
-v("analytics.getService",function(a){var b=he.get(a,null);if(null===b){var b=chrome.runtime.getManifest().version,c=ie();if(!Ea){var d=ie();Ea=new rd(d,new ge(d,"https://www.google-analytics.com/collect",8192))}b=new Zc("ca1.6.0",a,b,c,Ea);he.set(a,b)}return b});v("analytics.internal.GoogleAnalyticsService",Zc);v("analytics.internal.GoogleAnalyticsService.prototype.getTracker",Zc.prototype.mc);v("analytics.internal.GoogleAnalyticsService.prototype.getConfig",Zc.prototype.kc);
+v("analytics.getService",function(a,b){var c=he.get(a,null),d=b||chrome.runtime.getManifest().version;if(null===c){c=ie();if(!Ea){var e=ie();Ea=new rd(e,new ge(e,"https://www.google-analytics.com/collect",8192))}c=new Zc("ca1.6.0",a,d,c,Ea);he.set(a,c)}return c});v("analytics.internal.GoogleAnalyticsService",Zc);v("analytics.internal.GoogleAnalyticsService.prototype.getTracker",Zc.prototype.mc);v("analytics.internal.GoogleAnalyticsService.prototype.getConfig",Zc.prototype.kc);
 v("analytics.internal.ServiceSettings",T);v("analytics.internal.ServiceSettings.prototype.setTrackingPermitted",T.prototype.Oc);v("analytics.internal.ServiceSettings.prototype.isTrackingPermitted",T.prototype.xa);v("analytics.internal.ServiceSettings.prototype.setSampleRate",T.prototype.Nc);v("analytics.internal.ServiceSettings.prototype.resetUserId",T.prototype.Fc);v("analytics.internal.ServiceTracker",S);v("analytics.internal.ServiceTracker.prototype.send",S.prototype.send);
 v("analytics.internal.ServiceTracker.prototype.sendAppView",S.prototype.Hc);v("analytics.internal.ServiceTracker.prototype.sendEvent",S.prototype.Ic);v("analytics.internal.ServiceTracker.prototype.sendSocial",S.prototype.Kc);v("analytics.internal.ServiceTracker.prototype.sendException",S.prototype.Jc);v("analytics.internal.ServiceTracker.prototype.sendTiming",S.prototype.Ib);v("analytics.internal.ServiceTracker.prototype.startTiming",S.prototype.Rc);v("analytics.internal.ServiceTracker.Timing",Yc);
 v("analytics.internal.ServiceTracker.Timing.prototype.send",Yc.prototype.send);v("analytics.internal.ServiceTracker.prototype.forceSessionStart",S.prototype.jc);v("analytics.internal.ServiceTracker.prototype.addFilter",S.prototype.R);v("analytics.internal.FilterChannel.Hit",R);v("analytics.internal.FilterChannel.Hit.prototype.getHitType",R.prototype.ub);v("analytics.internal.FilterChannel.Hit.prototype.getParameters",R.prototype.V);v("analytics.internal.FilterChannel.Hit.prototype.cancel",R.prototype.cancel);
diff --git a/third_party/android_media/BUILD.gn b/third_party/android_media/BUILD.gn
new file mode 100644
index 0000000..96bba5a9
--- /dev/null
+++ b/third_party/android_media/BUILD.gn
@@ -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.
+
+import("//build/config/android/rules.gni")
+
+assert(is_android)
+
+android_resources("android_media_resources") {
+  custom_package = "org.chromium.third_party.android.media"
+  resource_dirs = [ "java/res" ]
+  deps = [
+    "//third_party/android_tools:android_support_v7_appcompat_resources",
+    "//third_party/android_tools:android_support_v7_mediarouter_resources",
+  ]
+}
+
+android_library("android_media_java") {
+  DEPRECATED_java_in_dir = "java/src"
+  deps = [
+    ":android_media_resources",
+    "//third_party/android_tools:android_support_v7_mediarouter_java",
+    "//third_party/android_tools:android_support_v7_mediarouter_resources",
+    "//third_party/android_tools:android_support_v7_appcompat_java",
+    "//third_party/android_tools:android_support_v13_java",
+  ]
+}
diff --git a/third_party/android_media/LICENSE b/third_party/android_media/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/third_party/android_media/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/android_media/OWNERS b/third_party/android_media/OWNERS
new file mode 100644
index 0000000..d88be63
--- /dev/null
+++ b/third_party/android_media/OWNERS
@@ -0,0 +1,3 @@
+aberent@chromium.org
+dgn@chromium.org
+avayvod@chromium.org
\ No newline at end of file
diff --git a/third_party/android_media/README.chromium b/third_party/android_media/README.chromium
new file mode 100644
index 0000000..0354ea6
--- /dev/null
+++ b/third_party/android_media/README.chromium
@@ -0,0 +1,19 @@
+Name: MediaController android sample.
+URL: https://android.googlesource.com/platform/development/+/master/samples/Support4Demos/src/com/example/android/supportv4/media/MediaController.java
+Version: 74c5bcff29959931c8a4b15e4afa613975a47f4c
+License: Apache 2.0
+Security Critical: no
+
+Description:
+This contains a modified copy of MediaController.java and the associated
+resources.
+
+MediaController.java is based on a public supportv4 android sample available
+as part of the Android support libraries installation (see
+http://developer.android.com/tools/support-library/index.html). It is also
+available from:
+https://android.googlesource.com/platform/development/+/master/samples/Support4Demos/src/com/example/android/supportv4/media/MediaController.java
+
+
+Local Modifications:
+The only changes are some minor bug fixes.
\ No newline at end of file
diff --git a/third_party/android_media/android_media.gyp b/third_party/android_media/android_media.gyp
new file mode 100644
index 0000000..f6a1e25
--- /dev/null
+++ b/third_party/android_media/android_media.gyp
@@ -0,0 +1,25 @@
+# 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.
+
+{
+  'variables': {
+    'chromium_code': 1,
+  },
+  'targets': [
+    {
+      'target_name': 'android_media_java',
+      'type': 'none',
+          'dependencies': [
+            '../android_tools/android_tools.gyp:android_support_v13_javalib',
+          ],
+      'variables': {
+        'java_in_dir': 'java',
+        'has_java_resources': 1,
+        'R_package': 'org.chromium.third_party.android.media',
+        'R_package_relpath': 'org/chromium/third_party/android/media',
+      },
+      'includes': [ '../../build/java.gypi' ],
+    },
+  ],
+}
diff --git a/third_party/android_media/java/res/layout/media_controller.xml b/third_party/android_media/java/res/layout/media_controller.xml
new file mode 100644
index 0000000..73cba27
--- /dev/null
+++ b/third_party/android_media/java/res/layout/media_controller.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<!-- This file is taken from the Android codebase.
+     samples/Support4Demos/res/layout/media_controller.xml
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:paddingTop="4dip"
+        android:orientation="horizontal">
+
+        <ImageButton
+            android:id="@+id/prev"
+            android:contentDescription="@null"
+            style="@android:style/MediaButton.Previous" />
+        <ImageButton
+            android:id="@+id/rew"
+            android:contentDescription="@null"
+            style="@android:style/MediaButton.Rew" />
+        <ImageButton
+            android:id="@+id/pause"
+            android:contentDescription="@null"
+            style="@android:style/MediaButton.Play" />
+        <ImageButton
+            android:id="@+id/ffwd"
+            android:contentDescription="@null"
+            style="@android:style/MediaButton.Ffwd" />
+        <ImageButton
+            android:id="@+id/next"
+            android:contentDescription="@null"
+            style="@android:style/MediaButton.Next" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/mediacontroller_progress_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <TextView android:id="@+id/time_current"
+            android:textSize="14sp"
+            android:textStyle="bold"
+            android:paddingTop="4dip"
+            android:paddingStart="4dip"
+            android:layout_gravity="center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingEnd="4dip"
+            android:textColor="@color/cast_media_controller_text" />
+
+        <SeekBar
+            android:id="@+id/mediacontroller_progress_bar"
+            style="?android:attr/progressBarStyleHorizontal"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="32dip" />
+
+        <TextView android:id="@+id/time"
+            android:textSize="14sp"
+            android:textStyle="bold"
+            android:paddingTop="4dip"
+            android:paddingEnd="4dip"
+            android:layout_gravity="center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dip"
+            android:textColor="@color/cast_media_controller_text" />
+    </LinearLayout>
+
+</LinearLayout>
+
diff --git a/third_party/android_media/java/res/values/colors.xml b/third_party/android_media/java/res/values/colors.xml
new file mode 100644
index 0000000..d6c6e3c
--- /dev/null
+++ b/third_party/android_media/java/res/values/colors.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources>
+    <!-- Cast colors -->
+    <!-- This is the same as the internal Android color "dim_foreground_dark"
+         which is used in MediaController. -->
+    <color name="cast_media_controller_text">#bebebe</color>
+
+</resources>
diff --git a/third_party/android_media/java/src/org/chromium/third_party/android/media/MediaController.java b/third_party/android_media/java/src/org/chromium/third_party/android/media/MediaController.java
new file mode 100644
index 0000000..1e6b231
--- /dev/null
+++ b/third_party/android_media/java/src/org/chromium/third_party/android/media/MediaController.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.chromium.third_party.android.media;
+
+import android.content.Context;
+import android.support.v4.media.TransportController;
+import android.support.v4.media.TransportMediator;
+import android.support.v4.media.TransportStateListener;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import org.chromium.third_party.android.media.R;
+
+import java.util.Formatter;
+import java.util.Locale;
+
+/**
+ * Helper for implementing media controls in an application.
+ * We use this custom version instead of {@link android.widget.MediaController} so that we can
+ * customize the look as we want. This file is taken directly from the public Android sample for
+ * supportv4, with tiny bug fixes.
+ */
+public class MediaController extends FrameLayout {
+
+    private TransportController mController;
+    private Context mContext;
+    private ViewGroup mProgressGroup;
+    private SeekBar mProgressBar;
+    private TextView mEndTime, mCurrentTime;
+    private boolean mDragging;
+    private boolean mUseFastForward;
+    private boolean mListenersSet;
+    private boolean mShowNext, mShowPrev;
+    private View.OnClickListener mNextListener, mPrevListener;
+    private StringBuilder mFormatBuilder;
+    private Formatter mFormatter;
+    private ImageButton mPauseButton;
+    private ImageButton mFfwdButton;
+    private ImageButton mRewButton;
+    private ImageButton mNextButton;
+    private ImageButton mPrevButton;
+
+    private TransportStateListener mStateListener = new TransportStateListener() {
+        @Override
+        public void onPlayingChanged(TransportController controller) {
+            updatePausePlay();
+        }
+        @Override
+        public void onTransportControlsChanged(TransportController controller) {
+            updateButtons();
+        }
+    };
+
+    public MediaController(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        mUseFastForward = true;
+        LayoutInflater inflate = (LayoutInflater)
+                mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        inflate.inflate(R.layout.media_controller, this, true);
+        initControllerView();
+    }
+
+    public MediaController(Context context, boolean useFastForward) {
+        super(context);
+        mContext = context;
+        mUseFastForward = useFastForward;
+    }
+
+    public MediaController(Context context) {
+        this(context, true);
+    }
+
+    public void setMediaPlayer(TransportController controller) {
+        if (getWindowToken() != null) {
+            if (mController != null) {
+                mController.unregisterStateListener(mStateListener);
+            }
+            if (controller != null) {
+                controller.registerStateListener(mStateListener);
+            }
+        }
+        mController = controller;
+        updatePausePlay();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mController != null) {
+            mController.registerStateListener(mStateListener);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mController != null) {
+            mController.unregisterStateListener(mStateListener);
+        }
+    }
+
+    private void initControllerView() {
+        mPauseButton = (ImageButton) findViewById(R.id.pause);
+        if (mPauseButton != null) {
+            mPauseButton.requestFocus();
+            mPauseButton.setOnClickListener(mPauseListener);
+        }
+
+        mFfwdButton = (ImageButton) findViewById(R.id.ffwd);
+        if (mFfwdButton != null) {
+            mFfwdButton.setOnClickListener(mFfwdListener);
+            mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
+        }
+
+        mRewButton = (ImageButton) findViewById(R.id.rew);
+        if (mRewButton != null) {
+            mRewButton.setOnClickListener(mRewListener);
+            mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
+        }
+
+        // By default these are hidden. They will be enabled when setPrevNextListeners() is called
+        mNextButton = (ImageButton) findViewById(R.id.next);
+        if (mNextButton != null && !mListenersSet) {
+            mNextButton.setVisibility(View.GONE);
+        }
+        mPrevButton = (ImageButton) findViewById(R.id.prev);
+        if (mPrevButton != null && !mListenersSet) {
+            mPrevButton.setVisibility(View.GONE);
+        }
+
+        mProgressGroup = (ViewGroup) findViewById(R.id.mediacontroller_progress_container);
+
+        if (mProgressGroup != null) {
+            mProgressBar = (SeekBar) mProgressGroup.findViewById(R.id.mediacontroller_progress_bar);
+            if (mProgressBar != null) {
+                mProgressBar.setOnSeekBarChangeListener(mSeekListener);
+                mProgressBar.setMax(1000);
+            }
+        }
+
+        mEndTime = (TextView) findViewById(R.id.time);
+        mCurrentTime = (TextView) findViewById(R.id.time_current);
+        mFormatBuilder = new StringBuilder();
+        mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
+
+        installPrevNextListeners();
+    }
+
+    /**
+     * Disable pause or seek buttons if the stream cannot be paused or seeked.
+     * This requires the control interface to be a MediaPlayerControlExt
+     */
+    void updateButtons() {
+        int flags = mController.getTransportControlFlags();
+        boolean enabled = isEnabled();
+        if (mPauseButton != null) {
+            boolean needPlayPauseButton = (flags & TransportMediator.FLAG_KEY_MEDIA_PAUSE) != 0 ||
+                                          (flags & TransportMediator.FLAG_KEY_MEDIA_PLAY) != 0;
+            mPauseButton.setEnabled(enabled && needPlayPauseButton);
+        }
+        if (mRewButton != null) {
+            mRewButton.setEnabled(enabled &&
+                                  (flags & TransportMediator.FLAG_KEY_MEDIA_REWIND) != 0);
+        }
+        if (mFfwdButton != null) {
+            mFfwdButton.setEnabled(enabled &&
+                    (flags & TransportMediator.FLAG_KEY_MEDIA_FAST_FORWARD) != 0);
+        }
+        if (mPrevButton != null) {
+            mShowPrev = (flags & TransportMediator.FLAG_KEY_MEDIA_PREVIOUS) != 0
+                    || mPrevListener != null;
+            mPrevButton.setEnabled(enabled && mShowPrev);
+        }
+        if (mNextButton != null) {
+            mShowNext = (flags & TransportMediator.FLAG_KEY_MEDIA_NEXT) != 0
+                    || mNextListener != null;
+            mNextButton.setEnabled(enabled && mShowNext);
+        }
+    }
+
+    public void refresh() {
+        updateProgress();
+        updateButtons();
+        updatePausePlay();
+    }
+
+    private String stringForTime(int timeMs) {
+        int totalSeconds = timeMs / 1000;
+
+        int seconds = totalSeconds % 60;
+        int minutes = (totalSeconds / 60) % 60;
+        int hours   = totalSeconds / 3600;
+
+        mFormatBuilder.setLength(0);
+        if (hours > 0) {
+            return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
+        } else {
+            return mFormatter.format("%02d:%02d", minutes, seconds).toString();
+        }
+    }
+
+    public long updateProgress() {
+        if (mController == null || mDragging) {
+            return 0;
+        }
+        long position = mController.getCurrentPosition();
+        long duration = mController.getDuration();
+        if (duration <= 0) {
+            // If there is no valid duration, hide the progress bar and time indicators.
+            if (mProgressGroup != null) mProgressGroup.setVisibility(View.INVISIBLE);
+        } else if (mProgressBar != null) {
+            if (mProgressGroup != null) mProgressGroup.setVisibility(View.VISIBLE);
+            // use long to avoid overflow
+            long pos = 1000L * position / duration;
+            mProgressBar.setProgress( (int) pos);
+
+            int percent = mController.getBufferPercentage();
+            mProgressBar.setSecondaryProgress(percent * 10);
+        }
+
+        if (mEndTime != null)
+            mEndTime.setText(stringForTime((int)duration));
+        if (mCurrentTime != null)
+            mCurrentTime.setText(stringForTime((int)position));
+
+        return position;
+    }
+
+    private View.OnClickListener mPauseListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            doPauseResume();
+        }
+    };
+
+    private void updatePausePlay() {
+        if (mPauseButton == null)
+            return;
+
+        if (mController.isPlaying()) {
+            mPauseButton.setImageResource(android.R.drawable.ic_media_pause);
+        } else {
+            mPauseButton.setImageResource(android.R.drawable.ic_media_play);
+        }
+    }
+
+    private void doPauseResume() {
+        if (mController.isPlaying()) {
+            mController.pausePlaying();
+        } else {
+            mController.startPlaying();
+        }
+        updatePausePlay();
+    }
+
+    // There are two scenarios that can trigger the seekbar listener to trigger:
+    //
+    // The first is the user using the touchpad to adjust the posititon of the
+    // seekbar's thumb. In this case onStartTrackingTouch is called followed by
+    // a number of onProgressChanged notifications, concluded by onStopTrackingTouch.
+    // We're setting the field "mDragging" to true for the duration of the dragging
+    // session to avoid jumps in the position in case of ongoing playback.
+    //
+    // The second scenario involves the user operating the scroll ball, in this
+    // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications,
+    // we will simply apply the updated position without suspending regular updates.
+    private SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() {
+        @Override
+        public void onStartTrackingTouch(SeekBar bar) {
+            mDragging = true;
+        }
+
+        @Override
+        public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
+            if (!fromuser) {
+                // We're not interested in programmatically generated changes to
+                // the progress bar's position.
+                return;
+            }
+
+            long duration = mController.getDuration();
+            long newposition = (duration * progress) / 1000L;
+            mController.seekTo((int) newposition);
+            if (mCurrentTime != null)
+                mCurrentTime.setText(stringForTime( (int) newposition));
+        }
+
+        @Override
+        public void onStopTrackingTouch(SeekBar bar) {
+            mDragging = false;
+            updateProgress();
+            updatePausePlay();
+        }
+    };
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        updateButtons();
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setClassName(MediaController.class.getName());
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setClassName(MediaController.class.getName());
+    }
+
+    private View.OnClickListener mRewListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            long pos = mController.getCurrentPosition();
+            pos -= 5000; // milliseconds
+            mController.seekTo(pos);
+            updateProgress();
+        }
+    };
+
+    private View.OnClickListener mFfwdListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            long pos = mController.getCurrentPosition();
+            pos += 15000; // milliseconds
+            mController.seekTo(pos);
+            updateProgress();
+        }
+    };
+
+    private void installPrevNextListeners() {
+        if (mNextButton != null) {
+            mNextButton.setOnClickListener(mNextListener);
+            mNextButton.setEnabled(mShowNext);
+        }
+
+        if (mPrevButton != null) {
+            mPrevButton.setOnClickListener(mPrevListener);
+            mPrevButton.setEnabled(mShowPrev);
+        }
+    }
+
+    public void setPrevNextListeners(View.OnClickListener next, View.OnClickListener prev) {
+        mNextListener = next;
+        mPrevListener = prev;
+        mListenersSet = true;
+
+        installPrevNextListeners();
+
+        if (mNextButton != null) {
+            mNextButton.setVisibility(View.VISIBLE);
+            mShowNext = true;
+        }
+        if (mPrevButton != null) {
+            mPrevButton.setVisibility(View.VISIBLE);
+            mShowPrev = true;
+        }
+    }
+}
+
diff --git a/third_party/android_platform/BUILD.gn b/third_party/android_platform/BUILD.gn
new file mode 100644
index 0000000..6cc6651
--- /dev/null
+++ b/third_party/android_platform/BUILD.gn
@@ -0,0 +1,31 @@
+# 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.
+
+import("config.gni")
+
+gypi_values = exec_script("//build/gypi_to_gn.py",
+                          [ rebase_path("relocation_packer.gyp") ],
+                          "scope",
+                          [ "relocation_packer.gyp" ])
+
+if (current_toolchain == host_toolchain) {
+  # GYP: //third_party/android_platform/relocation_packer.gyp:android_lib_relocation_packer
+  source_set("android_lib_relocation_packer") {
+    deps = [
+      "//third_party/elfutils:libelf",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    sources = gypi_values.relocation_packer_sources
+  }
+
+  # GYP: //third_party/android_platform/relocation_packer.gyp:android_relocation_packer
+  executable("android_relocation_packer") {
+    deps = [
+      ":android_lib_relocation_packer",
+      "//third_party/elfutils:libelf",
+    ]
+    sources = gypi_values.relocation_packer_main_source
+  }
+}
diff --git a/third_party/android_platform/README.chromium b/third_party/android_platform/README.chromium
index f6645b6..53f24299 100644
--- a/third_party/android_platform/README.chromium
+++ b/third_party/android_platform/README.chromium
@@ -15,6 +15,9 @@
 after which it is named, and the jar can be built by invoking make on the
 android_system_stubs target.
 
+Also includes a ported copy of the Android relocation packing tool source,
+along with the files required to build it in the chromium tree.
+
 Local Modifications:
 Only picked the few scripts needed by chrome.
 Updated output directories to use environment variable.
@@ -24,3 +27,10 @@
 Added support for finding symbols when library is loaded directly from the APK.
 Changed the toolchain to remove references to 4.6 toolchains.
 Added support for arch=x64 as an alias to arch=x86_64
+
+Android relocation packing tool details:
+    Copy sources from AOSP bionic/tools/relocation_packer
+    Remove scripts that regenerate golden test data (not relevant here)
+    Create a nativehelper/ScopedFd.h to satisfy inclusion from main.cc
+    Create gyp build
+    Create gn build (currently packer only; no unit tests)
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/Android.mk b/third_party/android_platform/bionic/tools/relocation_packer/Android.mk
new file mode 100644
index 0000000..99a39c0
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/Android.mk
@@ -0,0 +1,96 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+common_cppflags := -Wall -Wextra -Wunused -Werror -Wold-style-cast
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+LOCAL_SRC_FILES := \
+  src/debug.cc \
+  src/delta_encoder.cc \
+  src/elf_file.cc \
+  src/leb128.cc \
+  src/packer.cc \
+  src/sleb128.cc \
+
+LOCAL_STATIC_LIBRARIES := libelf
+LOCAL_C_INCLUDES := external/elfutils/src/libelf
+LOCAL_MODULE := lib_relocation_packer
+
+LOCAL_CPPFLAGS := $(common_cppflags)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+LOCAL_SRC_FILES := src/main.cc
+LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
+LOCAL_C_INCLUDES := external/elfutils/src/libelf libnativehelper/include
+
+LOCAL_MODULE := relocation_packer
+
+LOCAL_CPPFLAGS := $(common_cppflags)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+LOCAL_SRC_FILES := \
+  src/debug_unittest.cc \
+  src/delta_encoder_unittest.cc \
+  src/elf_file_unittest.cc \
+  src/leb128_unittest.cc \
+  src/sleb128_unittest.cc \
+  src/packer_unittest.cc \
+
+LOCAL_STATIC_LIBRARIES := lib_relocation_packer libelf
+LOCAL_C_INCLUDES := external/elfutils/src/libelf
+
+LOCAL_CPPFLAGS := $(common_cppflags)
+
+LOCAL_MODULE := relocation_packer_unit_tests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+# $(1) library name
+define copy-test-library
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE := $(1)
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE_PATH := $(HOST_OUT_EXECUTABLES)
+LOCAL_STRIP_MODULE := false
+LOCAL_SRC_FILES := test_data/$(1)
+include $(BUILD_PREBUILT)
+endef
+
+$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_arm32_packed.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64.so))
+$(eval $(call copy-test-library,elf_file_unittest_relocs_arm64_packed.so))
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/LICENSE b/third_party/android_platform/bionic/tools/relocation_packer/LICENSE
new file mode 100644
index 0000000..972bb2e
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/debug.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/debug.cc
new file mode 100644
index 0000000..29d7ab06
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/debug.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "debug.h"
+
+#include <stdlib.h>
+#include <iostream>
+#include <string>
+
+namespace relocation_packer {
+
+// Construct a new message logger.  Prints if level is less than or equal to
+// the level set with SetVerbose() and predicate is true.
+Logger::Logger(Severity severity, int level, bool predicate) {
+  severity_ = severity;
+  level_ = level;
+  predicate_ = predicate;
+}
+
+// On destruction, flush and print the strings accumulated.  Abort if FATAL.
+Logger::~Logger() {
+  if (predicate_) {
+    if (level_ <= max_level_) {
+      std::ostream* log = severity_ == INFO ? info_stream_ : error_stream_;
+      std::string tag;
+      switch (severity_) {
+        case INFO: tag = "INFO"; break;
+        case WARNING: tag = "WARNING"; break;
+        case ERROR: tag = "ERROR"; break;
+        case FATAL: tag = "FATAL"; break;
+      }
+      stream_.flush();
+      *log << tag << ": " << stream_.str() << std::endl;
+    }
+    if (severity_ == FATAL)
+      abort();
+  }
+}
+
+// Reset to initial state.
+void Logger::Reset() {
+  max_level_ = -1;
+  info_stream_ = &std::cout;
+  error_stream_ = &std::cerr;
+}
+
+// Verbosity.  Not thread-safe.
+int Logger::max_level_ = -1;
+
+// Logging streams.  Not thread-safe.
+std::ostream* Logger::info_stream_ = &std::cout;
+std::ostream* Logger::error_stream_ = &std::cerr;
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/debug.h b/third_party/android_platform/bionic/tools/relocation_packer/src/debug.h
new file mode 100644
index 0000000..48be6c1
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/debug.h
@@ -0,0 +1,115 @@
+// 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.
+
+// Logging and checks.  Avoids a dependency on base.
+//
+// LOG(tag) prints messages.  Tags are INFO, WARNING, ERROR and FATAL.
+// INFO prints to stdout, the others to stderr.  FATAL aborts after printing.
+//
+// LOG_IF(tag, predicate) logs if predicate evaluates to true, else silent.
+//
+// VLOG(level) logs INFO messages where level is less than or equal to the
+// verbosity level set with SetVerbose().
+//
+// VLOG_IF(level, predicate) logs INFO if predicate evaluates to true,
+// else silent.
+//
+// CHECK(predicate) logs a FATAL error if predicate is false.
+// NOTREACHED() always aborts.
+// Log streams can be changed with SetStreams().  Logging is not thread-safe.
+//
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
+#define TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
+
+#include <limits.h>
+#include <ostream>
+#include <sstream>
+
+namespace relocation_packer {
+
+class Logger {
+ public:
+  enum Severity {INFO = 0, WARNING, ERROR, FATAL};
+
+  // Construct a new message logger.  Prints if level is less than or
+  // equal to the level set with SetVerbose() and predicate is true.
+  // |severity| is an enumerated severity.
+  // |level| is the verbosity level.
+  // |predicate| controls if the logger prints or is silent.
+  Logger(Severity severity, int level, bool predicate);
+
+  // On destruction, flush and print the strings accumulated in stream_.
+  ~Logger();
+
+  // Return the stream for this logger.
+  std::ostream& GetStream() { return stream_; }
+
+  // Set verbosity level.  Messages with a level less than or equal to
+  // this level are printed, others are discarded.  Static, not thread-safe.
+  static void SetVerbose(int level) { max_level_ = level; }
+
+  // Set info and error logging streams.  Static, not thread-safe.
+  static void SetStreams(std::ostream* info_stream,
+                         std::ostream* error_stream) {
+    info_stream_ = info_stream;
+    error_stream_ = error_stream;
+  }
+
+  // Reset to initial state.
+  static void Reset();
+
+ private:
+  // Message severity, verbosity level, and predicate.
+  Severity severity_;
+  int level_;
+  bool predicate_;
+
+  // String stream, accumulates message text.
+  std::ostringstream stream_;
+
+  // Verbosity for INFO messages.  Not thread-safe.
+  static int max_level_;
+
+  // Logging streams.  Not thread-safe.
+  static std::ostream* info_stream_;
+  static std::ostream* error_stream_;
+};
+
+}  // namespace relocation_packer
+
+// Make logging severities visible globally.
+typedef relocation_packer::Logger::Severity LogSeverity;
+using LogSeverity::INFO;
+using LogSeverity::WARNING;
+using LogSeverity::ERROR;
+using LogSeverity::FATAL;
+
+// LOG(severity) prints a message with the given severity, and aborts if
+// severity is FATAL.  LOG_IF(severity, predicate) does the same but only if
+// predicate is true.  INT_MIN is guaranteed to be less than or equal to
+// any verbosity level.
+#define LOG(severity) \
+    (relocation_packer::Logger(severity, INT_MIN, true).GetStream())
+#define LOG_IF(severity, predicate) \
+    (relocation_packer::Logger(severity, INT_MIN, (predicate)).GetStream())
+
+// VLOG(level) prints its message as INFO if level is less than or equal to
+// the current verbosity level.
+#define VLOG(level) \
+    (relocation_packer::Logger(INFO, (level), true).GetStream())
+#define VLOG_IF(level, predicate) \
+    (relocation_packer::Logger(INFO, (level), (predicate)).GetStream())
+
+// CHECK(predicate) fails with a FATAL log message if predicate is false.
+#define CHECK(predicate) (LOG_IF(FATAL, !(predicate)) \
+    << __FILE__ << ":" << __LINE__ << ": " \
+    << __FUNCTION__ << ": CHECK '" #predicate "' failed")
+
+// NOTREACHED() always fails with a FATAL log message.
+#define NOTREACHED(_) (LOG(FATAL) \
+    << __FILE__ << ":" << __LINE__ << ": " \
+    << __FUNCTION__ << ": NOTREACHED() hit")
+
+#endif  // TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/debug_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/debug_unittest.cc
new file mode 100644
index 0000000..b31e2ae
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/debug_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "debug.h"
+
+#include <sstream>
+#include "gtest/gtest.h"
+
+namespace relocation_packer {
+
+TEST(Debug, Log) {
+  Logger::Reset();
+  std::ostringstream info;
+  std::ostringstream error;
+  Logger::SetStreams(&info, &error);
+
+  LOG(INFO) << "INFO log message";
+  LOG(WARNING) << "WARNING log message";
+  LOG(ERROR) << "ERROR log message";
+
+  EXPECT_EQ("INFO: INFO log message\n", info.str());
+  EXPECT_EQ("WARNING: WARNING log message\n"
+            "ERROR: ERROR log message\n", error.str());
+  Logger::Reset();
+}
+
+TEST(Debug, LogIf) {
+  Logger::Reset();
+  std::ostringstream info;
+  std::ostringstream error;
+  Logger::SetStreams(&info, &error);
+
+  LOG_IF(INFO, true) << "INFO log message";
+  LOG_IF(INFO, false) << "INFO log message, SHOULD NOT PRINT";
+  LOG_IF(WARNING, true) << "WARNING log message";
+  LOG_IF(WARNING, false) << "WARNING log message, SHOULD NOT PRINT";
+  LOG_IF(ERROR, true) << "ERROR log message";
+  LOG_IF(ERROR, false) << "ERROR log message, SHOULD NOT PRINT";
+  LOG_IF(FATAL, false) << "FATAL log message, SHOULD NOT PRINT";
+
+  EXPECT_EQ("INFO: INFO log message\n", info.str());
+  EXPECT_EQ("WARNING: WARNING log message\n"
+            "ERROR: ERROR log message\n", error.str());
+  Logger::Reset();
+}
+
+TEST(Debug, Vlog) {
+  Logger::Reset();
+  std::ostringstream info;
+  std::ostringstream error;
+  Logger::SetStreams(&info, &error);
+
+  VLOG(0) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
+  VLOG(1) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
+  VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+  EXPECT_EQ("", info.str());
+  EXPECT_EQ("", error.str());
+
+  Logger::SetVerbose(1);
+
+  VLOG(0) << "VLOG 0 INFO log message";
+  VLOG(1) << "VLOG 1 INFO log message";
+  VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+  EXPECT_EQ("INFO: VLOG 0 INFO log message\n"
+            "INFO: VLOG 1 INFO log message\n", info.str());
+  EXPECT_EQ("", error.str());
+  Logger::Reset();
+}
+
+TEST(Debug, VlogIf) {
+  Logger::Reset();
+  std::ostringstream info;
+  std::ostringstream error;
+  Logger::SetStreams(&info, &error);
+
+  VLOG_IF(0, true) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
+  VLOG_IF(1, true) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
+  VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+  EXPECT_EQ("", info.str());
+  EXPECT_EQ("", error.str());
+
+  Logger::SetVerbose(1);
+
+  VLOG_IF(0, true) << "VLOG 0 INFO log message";
+  VLOG_IF(0, false) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
+  VLOG_IF(1, true) << "VLOG 1 INFO log message";
+  VLOG_IF(1, false) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
+  VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+  VLOG_IF(2, false) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+  EXPECT_EQ("INFO: VLOG 0 INFO log message\n"
+            "INFO: VLOG 1 INFO log message\n", info.str());
+  EXPECT_EQ("", error.str());
+  Logger::Reset();
+}
+
+TEST(DebugDeathTest, Fatal) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  Logger::Reset();
+  EXPECT_DEATH(LOG(FATAL) << "FATAL log message", "FATAL: FATAL log message");
+  EXPECT_DEATH(
+      LOG_IF(FATAL, true) << "FATAL log message", "FATAL: FATAL log message");
+}
+
+TEST(DebugDeathTest, Check) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  Logger::Reset();
+  CHECK(0 == 0);
+  EXPECT_DEATH(CHECK(0 == 1), "FATAL: .*:.*: .*: CHECK '0 == 1' failed");
+}
+
+TEST(DebugDeathTest, NotReached) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  Logger::Reset();
+  EXPECT_DEATH(NOTREACHED(), "FATAL: .*:.*: .*: NOTREACHED\\(\\) hit");
+}
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.cc
new file mode 100644
index 0000000..8349d7c
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.cc
@@ -0,0 +1,307 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "delta_encoder.h"
+
+#include <vector>
+
+#include "debug.h"
+
+static constexpr uint32_t RELOCATION_GROUPED_BY_INFO_FLAG = 1;
+static constexpr uint32_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2;
+static constexpr uint32_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4;
+static constexpr uint32_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8;
+
+static bool is_relocation_grouped_by_info(uint64_t flags) {
+  return (flags & RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
+}
+
+static bool is_relocation_grouped_by_offset_delta(uint64_t flags) {
+  return (flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0;
+}
+
+static bool is_relocation_grouped_by_addend(uint64_t flags) {
+  return (flags & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0;
+}
+
+static bool is_relocation_group_has_addend(uint64_t flags) {
+  return (flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
+}
+
+namespace relocation_packer {
+
+// Encode relocations into a delta encoded (packed) representation.
+template <typename ELF>
+void RelocationDeltaCodec<ELF>::Encode(const std::vector<ElfRela>& relocations,
+                                       std::vector<ElfAddr>* packed) {
+  if (relocations.size() == 0)
+    return;
+
+  // Start with the relocation count, then append groups
+  // TODO(dimitry): we might want to move it to DT_ANDROID_RELCOUNT section
+  packed->push_back(static_cast<ElfAddr>(relocations.size()));
+
+  // lets write starting offset (offset of the first reloc - first delta)
+  ElfAddr start_offset = relocations.size() > 1 ?
+      relocations[0].r_offset - (relocations[1].r_offset - relocations[0].r_offset) :
+      relocations[0].r_offset;
+
+  packed->push_back(start_offset);
+
+  // this one is used to calculate delta
+  ElfAddr previous_addend = 0;
+  ElfAddr previous_offset = start_offset;
+
+  for (size_t group_start = 0; group_start < relocations.size(); ) {
+    ElfAddr group_flags = 0;
+    ElfAddr group_offset_delta = 0;
+    ElfAddr group_addend = 0;
+    ElfAddr group_info = 0;
+
+    ElfAddr group_size = 0;
+
+    DetectGroup(relocations, group_start, previous_offset, &group_size, &group_flags,
+        &group_offset_delta, &group_info, &group_addend);
+
+    // write the group header
+    packed->push_back(group_size);
+    packed->push_back(group_flags);
+
+    if (is_relocation_grouped_by_offset_delta(group_flags)) {
+      packed->push_back(group_offset_delta);
+    }
+
+    if (is_relocation_grouped_by_info(group_flags)) {
+      packed->push_back(group_info);
+    }
+
+    if (is_relocation_group_has_addend(group_flags) &&
+        is_relocation_grouped_by_addend(group_flags)) {
+      packed->push_back(group_addend - previous_addend);
+      previous_addend = group_addend;
+    }
+
+    for (size_t i = 0; i < static_cast<size_t>(group_size); ++i) {
+      CHECK((group_start + i) < relocations.size());
+      const ElfRela* relocation = &relocations[group_start + i];
+
+      if (!is_relocation_grouped_by_offset_delta(group_flags)) {
+        packed->push_back(relocation->r_offset - previous_offset);
+      }
+      previous_offset = relocation->r_offset;
+
+      if (!is_relocation_grouped_by_info(group_flags)) {
+        packed->push_back(relocation->r_info);
+      }
+
+      if (is_relocation_group_has_addend(group_flags) &&
+          !is_relocation_grouped_by_addend(group_flags)) {
+        packed->push_back(relocation->r_addend - previous_addend);
+        previous_addend = relocation->r_addend;
+      }
+    }
+
+    // If the relocation group does not have an addend - reset it to 0
+    // to simplify addend computation for the group following this one.
+    if (!is_relocation_group_has_addend(group_flags)) {
+      previous_addend = 0;
+    }
+
+    group_start += group_size;
+  }
+}
+
+// Decode relocations from a delta encoded (packed) representation.
+template <typename ELF>
+void RelocationDeltaCodec<ELF>::Decode(const std::vector<ElfAddr>& packed,
+                                       std::vector<ElfRela>* relocations) {
+  if (packed.size() < 5) {
+    return;
+  }
+
+  size_t ndx = 0;
+  ElfAddr current_count = 0;
+  ElfAddr total_count = packed[ndx++];
+
+  ElfAddr offset = packed[ndx++];
+  ElfAddr info = 0;
+  ElfAddr addend = 0;
+
+  while(current_count < total_count) {
+    // read group
+    ElfAddr group_size = packed[ndx++];
+    ElfAddr group_flags = packed[ndx++];
+    ElfAddr group_offset_delta = 0;
+
+    if (is_relocation_grouped_by_offset_delta(group_flags)) {
+      group_offset_delta = packed[ndx++];
+    }
+
+    if (is_relocation_grouped_by_info(group_flags)) {
+      info = packed[ndx++];
+    }
+
+    if (is_relocation_group_has_addend(group_flags) &&
+        is_relocation_grouped_by_addend(group_flags)) {
+      addend += packed[ndx++];
+    }
+
+    // now read not grouped info
+    for (ElfAddr i = 0; i<group_size; ++i) {
+      if (is_relocation_grouped_by_offset_delta(group_flags)) {
+        offset += group_offset_delta;
+      } else {
+        offset += packed[ndx++];
+      }
+
+      if (!is_relocation_grouped_by_info(group_flags)) {
+        info = packed[ndx++];
+      }
+
+      if (is_relocation_group_has_addend(group_flags) &&
+          !is_relocation_grouped_by_addend(group_flags)) {
+        addend += packed[ndx++];
+      }
+
+      ElfRela reloc;
+      reloc.r_offset = offset;
+      reloc.r_info = info;
+      reloc.r_addend = is_relocation_group_has_addend(group_flags) ? addend : 0;
+      relocations->push_back(reloc);
+    }
+
+    if (!is_relocation_group_has_addend(group_flags)) {
+      addend = 0;
+    }
+
+    current_count += group_size;
+  }
+}
+
+// This function detects a way to group reloc_one and reloc_two, sets up group_flags
+// and initializes values for corresponding group_ fields. For example if relocations
+// can be grouped by r_info the function will set group_info variable.
+template <typename ELF>
+void RelocationDeltaCodec<ELF>::DetectGroupFields(const ElfRela& reloc_one,
+                                                  const ElfRela& reloc_two,
+                                                  ElfAddr current_offset_delta,
+                                                  ElfAddr* group_flags,
+                                                  ElfAddr* group_offset_delta,
+                                                  ElfAddr* group_info,
+                                                  ElfAddr* group_addend) {
+  *group_flags = 0;
+
+  const ElfAddr offset_delta = static_cast<ElfAddr>(reloc_two.r_offset) -
+      static_cast<ElfAddr>(reloc_one.r_offset);
+
+  if (offset_delta == current_offset_delta) {
+    *group_flags |= RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG;
+    if (group_offset_delta != nullptr) {
+      *group_offset_delta = current_offset_delta;
+    }
+  }
+
+  if (reloc_one.r_info == reloc_two.r_info) {
+    *group_flags |= RELOCATION_GROUPED_BY_INFO_FLAG;
+    if (group_info != nullptr) {
+      *group_info = reloc_one.r_info;
+    }
+  }
+
+  if (reloc_one.r_addend != 0 || reloc_two.r_addend != 0) {
+    *group_flags |= RELOCATION_GROUP_HAS_ADDEND_FLAG;
+    if (reloc_one.r_addend == reloc_two.r_addend) {
+      *group_flags |= RELOCATION_GROUPED_BY_ADDEND_FLAG;
+      if (group_addend != nullptr) {
+        *group_addend = reloc_one.r_addend;
+      }
+    }
+  }
+}
+
+// This function is used to detect if there is better group available
+// during RelocationDeltaCodec<ELF>::DetectGroup processing.
+// Current implementation prefers having groups without addend (== zero addend)
+// to any other groups field with the ratio 3:1. This is because addend tends
+// to be more unevenly distributed than other fields.
+static uint32_t group_weight(uint64_t flags) {
+  uint32_t weight = 0;
+  if (!is_relocation_group_has_addend(flags)) {
+    weight += 3;
+  } else if (is_relocation_grouped_by_addend(flags)) {
+    weight += 1;
+  }
+
+  if (is_relocation_grouped_by_offset_delta(flags)) {
+    weight += 1;
+  }
+
+  if (is_relocation_grouped_by_info(flags)) {
+    weight += 1;
+  }
+
+  return weight;
+}
+
+template <typename ELF>
+void RelocationDeltaCodec<ELF>::DetectGroup(const std::vector<ElfRela>& relocations,
+                                          size_t group_starts_with, ElfAddr previous_offset,
+                                          ElfAddr* group_size, ElfAddr* group_flags,
+                                          ElfAddr* group_offset_delta, ElfAddr* group_info,
+                                          ElfAddr* group_addend) {
+  CHECK(group_starts_with < relocations.size());
+  CHECK(group_flags != nullptr);
+
+  const ElfRela& reloc_one = relocations[group_starts_with++];
+  if (group_starts_with == relocations.size()) {
+    *group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG;
+    *group_size = 1;
+    return;
+  }
+
+  const ElfAddr offset_delta = reloc_one.r_offset - previous_offset;
+
+  // detect group_flags
+  DetectGroupFields(reloc_one, relocations[group_starts_with], offset_delta, group_flags,
+      group_offset_delta, group_info, group_addend);
+
+  if (group_starts_with + 1 == relocations.size()) {
+    *group_size = 2;
+    return;
+  }
+
+  ElfAddr cnt = 1;
+  for (size_t i = group_starts_with; i < relocations.size() - 1; ++i) {
+    ElfAddr candidate_flags;
+    // check if next group (reloc_current; reloc_next) has better grouped_by flags
+    DetectGroupFields(relocations[i], relocations[i+1], offset_delta, &candidate_flags,
+        nullptr, nullptr, nullptr);
+
+    if (group_weight(*group_flags) < group_weight(candidate_flags)) {
+      break;
+    }
+    cnt++;
+
+    if (candidate_flags != *group_flags) {
+      break;
+    }
+
+    if (i + 1 == relocations.size() - 1) { // last one
+      cnt++;
+    }
+  }
+
+  // if as a result of checking candidates we ended up with cnt == 1
+  // reset flags to the default state
+  if (cnt == 1) {
+    *group_flags = reloc_one.r_addend == 0 ? 0 : RELOCATION_GROUP_HAS_ADDEND_FLAG;
+  }
+
+  *group_size = cnt;
+}
+
+template class RelocationDeltaCodec<ELF32_traits>;
+template class RelocationDeltaCodec<ELF64_traits>;
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.h b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.h
new file mode 100644
index 0000000..46c324c
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder.h
@@ -0,0 +1,132 @@
+// 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.
+
+// Delta encode and decode REL/RELA section of elf file.
+//
+// The encoded data format is sequence of elements of ElfAddr type (unsigned long):
+//
+// [00] relocation_count - the total count of relocations
+// [01] initial r_offset - this is initial r_offset for the
+//                         relocation table.
+// followed by group structures:
+// [02] group
+// ...
+// [nn] group
+
+// the generalized format of the group is (! - always present ? - depends on group_flags):
+// --------------
+// ! group_size
+// ! group_flags
+// ? group_r_offset_delta when RELOCATION_GROUPED_BY_OFFSET_DELTA flag is set
+// ? group_r_info when RELOCATION_GROUPED_BY_INFO flag is set
+// ? group_r_addend_group_delta when RELOCATION_GROUP_HAS_ADDEND and RELOCATION_GROUPED_BY_ADDEND
+//   flag is set
+//
+// The group description is followed by individual relocations.
+// please note that there is a case when individual relocation
+// section could be empty - that is if every field ends up grouped.
+//
+// The format for individual relocations section is:
+// ? r_offset_delta - when RELOCATION_GROUPED_BY_OFFSET_DELTA is not set
+// ? r_info - when RELOCATION_GROUPED_BY_INFO flag is not set
+// ? r_addend_delta - RELOCATION_GROUP_HAS_ADDEND is set and RELOCATION_GROUPED_BY_ADDEND is not set
+//
+// For example lets pack the following relocations:
+//
+// Relocation section '.rela.dyn' at offset 0xbf58 contains 939 entries:
+//     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+//     00000000000a2178  0000000000000403 R_AARCH64_RELATIVE                        177a8
+//     00000000000a2180  0000000000000403 R_AARCH64_RELATIVE                        177cc
+//     00000000000a2188  0000000000000403 R_AARCH64_RELATIVE                        177e0
+//     00000000000a2190  0000000000000403 R_AARCH64_RELATIVE                        177f4
+//     00000000000a2198  0000000000000403 R_AARCH64_RELATIVE                        17804
+//     00000000000a21a0  0000000000000403 R_AARCH64_RELATIVE                        17818
+//     00000000000a21a8  0000000000000403 R_AARCH64_RELATIVE                        1782c
+//     00000000000a21b0  0000000000000403 R_AARCH64_RELATIVE                        17840
+//     00000000000a21b8  0000000000000403 R_AARCH64_RELATIVE                        17854
+//     00000000000a21c0  0000000000000403 R_AARCH64_RELATIVE                        17868
+//     00000000000a21c8  0000000000000403 R_AARCH64_RELATIVE                        1787c
+//     00000000000a21d0  0000000000000403 R_AARCH64_RELATIVE                        17890
+//     00000000000a21d8  0000000000000403 R_AARCH64_RELATIVE                        178a4
+//     00000000000a21e8  0000000000000403 R_AARCH64_RELATIVE                        178b8
+//
+// The header is going to be
+// [00] 14                 <- count
+// [01] 0x00000000000a2170 <- initial relocation (first relocation - delta,
+//                            the delta is 8 in this case)
+// -- starting the first and only group
+// [03] 14                 <- group size
+// [03] 0xb                <- flags RELOCATION_GROUP_HAS_ADDEND | RELOCATION_GROUPED_BY_OFFSET_DELTA
+//                            | RELOCATION_GROUPED_BY_INFO
+// [04] 8                  <- offset delta
+// [05] 0x403              <- r_info
+// -- end of group definition, starting list of r_addend deltas
+// [06] 0x177a8
+// [07] 0x24               = 177cc - 177a8
+// [08] 0x14               = 177e0 - 177cc
+// [09] 0x14               = 177f4 - 177e0
+// [10] 0x10               = 17804 - 177f4
+// [11] 0x14               = 17818 - 17804
+// [12] 0x14               = 1782c - 17818
+// [13] 0x14               = 17840 - 1782c
+// [14] 0x14               = 17854 - 17840
+// [15] 0x14               = 17868 - 17854
+// [16] 0x14               = 1787c - 17868
+// [17] 0x14               = 17890 - 1787c
+// [18] 0x14               = 178a4 - 17890
+// [19] 0x14               = 178b8 - 178a4
+// -- the end.
+
+// TODO (dimitry): consider using r_addend_group_delta in the way we use group offset delta, it can
+//                 save us more bytes...
+
+// The input ends when sum(group_size) == relocation_count
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
+#define TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
+
+#include <vector>
+
+#include "elf.h"
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// A RelocationDeltaCodec packs vectors of relative relocations with
+// addends into more compact forms, and unpacks them to reproduce the
+// pre-packed data.
+template <typename ELF>
+class RelocationDeltaCodec {
+ public:
+  typedef typename ELF::Addr ElfAddr;
+  typedef typename ELF::Rela ElfRela;
+
+  // Encode relocations with addends into a more compact form.
+  // |relocations| is a vector of relative relocation with addend structs.
+  // |packed| is the vector of packed words into which relocations are packed.
+  static void Encode(const std::vector<ElfRela>& relocations,
+                     std::vector<ElfAddr>* packed);
+
+  // Decode relative relocations with addends from their more compact form.
+  // |packed| is the vector of packed relocations.
+  // |relocations| is a vector of unpacked relative relocations.
+  static void Decode(const std::vector<ElfAddr>& packed,
+                     std::vector<ElfRela>* relocations);
+
+ private:
+  static void DetectGroup(const std::vector<ElfRela>& relocations,
+                          size_t group_starts_with, ElfAddr previous_offset,
+                          ElfAddr* group_size, ElfAddr* group_flags,
+                          ElfAddr* group_offset_delta, ElfAddr* group_info,
+                          ElfAddr* group_addend);
+
+  static void DetectGroupFields(const ElfRela& reloc_one, const ElfRela& reloc_two,
+                                ElfAddr current_offset_delta, ElfAddr* group_flags,
+                                ElfAddr* group_offset_delta, ElfAddr* group_info,
+                                ElfAddr* group_addend);
+};
+
+}  // namespace relocation_packer
+
+#endif  // TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder_unittest.cc
new file mode 100644
index 0000000..06d9c96
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/delta_encoder_unittest.cc
@@ -0,0 +1,223 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "delta_encoder.h"
+
+#include <vector>
+#include "elf.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+template <typename T>
+void AddRelocation(uint32_t addr,
+                   uint32_t info,
+                   int32_t addend,
+                   std::vector<T>* relocations) {
+  T relocation;
+  relocation.r_offset = addr;
+  relocation.r_info = info;
+  relocation.r_addend = addend;
+  relocations->push_back(relocation);
+}
+
+template <typename T>
+bool CheckRelocation(uint32_t addr,
+                     uint32_t info,
+                     int32_t addend,
+                     const T& relocation) {
+  return relocation.r_offset == addr &&
+      relocation.r_info == info &&
+      relocation.r_addend == addend;
+}
+
+}  // namespace
+
+namespace relocation_packer {
+
+template <typename ELF>
+static void encode() {
+  std::vector<typename ELF::Rela> relocations;
+  std::vector<typename ELF::Addr> packed;
+
+  RelocationDeltaCodec<ELF> codec;
+
+  codec.Encode(relocations, &packed);
+
+  ASSERT_EQ(0U, packed.size());
+
+  // Initial relocation.
+  AddRelocation(0xf00d0000, 11U, 10000, &relocations);
+
+  codec.Encode(relocations, &packed);
+
+  // size of reloc table, size of group, flags, 3 fields, zero
+  EXPECT_EQ(7U, packed.size());
+  // One pair present.
+  size_t ndx = 0;
+  EXPECT_EQ(1U, packed[ndx++]);
+  EXPECT_EQ(0xf00d0000, packed[ndx++]);
+  EXPECT_EQ(1U, packed[ndx++]); // group_size
+  EXPECT_EQ(8U, packed[ndx++]); // flags
+  // Delta from the neutral element is zero
+  EXPECT_EQ(0U, packed[ndx++]); // offset_delta
+  EXPECT_EQ(11U, packed[ndx++]); // info
+  EXPECT_EQ(10000U, packed[ndx++]); // addend_delta
+
+  // Add a second relocation, 4 byte offset delta, 12 byte addend delta.
+  // same info
+  AddRelocation(0xf00d0004, 11U, 10012, &relocations);
+
+  packed.clear();
+  codec.Encode(relocations, &packed);
+
+  ndx = 0;
+  EXPECT_EQ(8U, packed.size());
+
+  EXPECT_EQ(2U, packed[ndx++]); // relocs count
+  EXPECT_EQ(0xf00cfffc, packed[ndx++]); // initial offset
+  EXPECT_EQ(2U, packed[ndx++]); // group count
+  EXPECT_EQ(11U, packed[ndx++]); // flags
+  EXPECT_EQ(4U, packed[ndx++]); // group offset delta
+  EXPECT_EQ(11U, packed[ndx++]); // info
+
+  EXPECT_EQ(10000U, packed[ndx++]); // addend delta
+  EXPECT_EQ(12U, packed[ndx++]); // addend delta
+
+  // Add a third relocation, 4 byte offset delta, 12 byte addend delta.
+  // different info
+  AddRelocation(0xf00d0008, 41U, 10024, &relocations);
+
+  // Add three more relocations, 8 byte offset deltas, -24 byte addend deltas.
+  AddRelocation(0xf00d0010, 42U, 10000, &relocations);
+  AddRelocation(0xf00d0018, 42U, 9976, &relocations);
+  AddRelocation(0xf00d0020, 42U, 9952, &relocations);
+
+  AddRelocation(0xf00d2028, 1042U, 0, &relocations);
+  AddRelocation(0xf00d2030, 3442U, 0, &relocations);
+
+  packed.clear();
+  codec.Encode(relocations, &packed);
+
+  ndx = 0;
+  EXPECT_EQ(26U, packed.size());
+  // Total number of relocs
+  EXPECT_EQ(8U, packed[ndx++]);
+  EXPECT_EQ(0xf00cfffc, packed[ndx++]);
+  // 2 in first group
+  EXPECT_EQ(2U, packed[ndx++]);
+  EXPECT_EQ(11U, packed[ndx++]); //flags
+  EXPECT_EQ(4U, packed[ndx++]); // group offset delta
+  EXPECT_EQ(11U, packed[ndx++]); // info
+
+  // Initial relocation.
+  EXPECT_EQ(10000U, packed[ndx++]); // addend delta
+  // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
+  EXPECT_EQ(12U, packed[ndx++]); // addend delta
+
+  // second group has only one reloc
+  EXPECT_EQ(1U, packed[ndx++]); // count
+  EXPECT_EQ(8U, packed[ndx++]); // flags
+
+  EXPECT_EQ(4U, packed[ndx++]); // offset delta
+  EXPECT_EQ(41U, packed[ndx++]); // info
+  EXPECT_EQ(12U, packed[ndx++]); // addend delta
+
+  // next - 3 relocs grouped by info
+  EXPECT_EQ(3U, packed[ndx++]); // count
+  EXPECT_EQ(11U, packed[ndx++]); // flags
+  EXPECT_EQ(8U, packed[ndx++]); // group offset delta
+  EXPECT_EQ(42U, packed[ndx++]); // info
+  // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
+  EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
+  EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
+  EXPECT_EQ(static_cast<typename ELF::Addr>(-24), packed[ndx++]);
+
+  // and last - 2 relocations without addend
+  EXPECT_EQ(2U, packed[ndx++]);
+  EXPECT_EQ(0U, packed[ndx++]); // flags
+  // offset_deltas and r_infos for next 2 relocations
+  EXPECT_EQ(0x2008U, packed[ndx++]); // offset delta
+  EXPECT_EQ(1042U, packed[ndx++]); // r_info
+  EXPECT_EQ(0x8U, packed[ndx++]); // offset delta
+  EXPECT_EQ(3442U, packed[ndx++]); // r_info
+
+  EXPECT_EQ(packed.size(), ndx);
+}
+
+TEST(Delta, Encode32) {
+  encode<ELF32_traits>();
+}
+
+TEST(Delta, Encode64) {
+  encode<ELF64_traits>();
+}
+
+template <typename ELF>
+static void decode() {
+  std::vector<typename ELF::Addr> packed;
+  std::vector<typename ELF::Rela> relocations;
+
+  RelocationDeltaCodec<ELF> codec;
+  codec.Decode(packed, &relocations);
+
+  EXPECT_EQ(0U, relocations.size());
+
+  // Six pairs.
+  packed.push_back(6U); // count
+  packed.push_back(0xc0ddfffc); // base offset
+  packed.push_back(3U); // group count
+  packed.push_back(11U); // flags
+  packed.push_back(4U); // offset delta
+  packed.push_back(11U); // info
+  // Initial relocation.
+  packed.push_back(10000U);
+  // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
+  packed.push_back(12U); // addend
+  packed.push_back(12U); // addend
+
+  // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
+  packed.push_back(1U); // group count
+  packed.push_back(9U); // flags
+  packed.push_back(11U); // info
+
+  packed.push_back(8U);
+  packed.push_back(static_cast<typename ELF::Addr>(-24));
+  // next group with 2 relocs
+  packed.push_back(2U); // group count
+  packed.push_back(11U); // flags
+  packed.push_back(8U); // offset
+  packed.push_back(42U); // info
+
+  packed.push_back(static_cast<typename ELF::Addr>(-24)); // addend
+  packed.push_back(static_cast<typename ELF::Addr>(-24)); // addend
+
+  relocations.clear();
+  codec.Decode(packed, &relocations);
+
+  EXPECT_EQ(6U, relocations.size());
+  // Initial relocation.
+  EXPECT_TRUE(CheckRelocation(0xc0de0000, 11U, 10000, relocations[0]));
+  // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
+  EXPECT_TRUE(CheckRelocation(0xc0de0004, 11U, 10012, relocations[1]));
+  EXPECT_TRUE(CheckRelocation(0xc0de0008, 11U, 10024, relocations[2]));
+  // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
+  EXPECT_TRUE(CheckRelocation(0xc0de0010, 11U, 10000, relocations[3]));
+  EXPECT_TRUE(CheckRelocation(0xc0de0018, 42U, 9976, relocations[4]));
+  EXPECT_TRUE(CheckRelocation(0xc0de0020, 42U, 9952, relocations[5]));
+}
+
+TEST(Delta, Decode32) {
+  decode<ELF32_traits>();
+}
+
+TEST(Delta, Decode64) {
+  decode<ELF64_traits>();
+}
+
+// TODO (dimitry): add more tests (fix by 19 January 2038 03:14:07 UTC)
+// TODO (dimtiry): 1. Incorrect packed array for decode
+// TODO (dimtiry): 2. Try to catch situation where it is likely to get series of groups with size 1
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.cc
new file mode 100644
index 0000000..20b25ef
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.cc
@@ -0,0 +1,882 @@
+// 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.
+
+// Implementation notes:
+//
+// We need to remove a piece from the ELF shared library.  However, we also
+// want to avoid fixing DWARF cfi data and relative relocation addresses.
+// So after packing we shift offets and starting address of the RX segment
+// while preserving code/data vaddrs location.
+// This requires some fixups for symtab/hash/gnu_hash dynamic section addresses.
+
+#include "elf_file.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "debug.h"
+#include "elf_traits.h"
+#include "libelf.h"
+#include "packer.h"
+
+namespace relocation_packer {
+
+// Out-of-band dynamic tags used to indicate the offset and size of the
+// android packed relocations section.
+static constexpr int32_t DT_ANDROID_REL = DT_LOOS + 2;
+static constexpr int32_t DT_ANDROID_RELSZ = DT_LOOS + 3;
+
+static constexpr int32_t DT_ANDROID_RELA = DT_LOOS + 4;
+static constexpr int32_t DT_ANDROID_RELASZ = DT_LOOS + 5;
+
+static constexpr uint32_t SHT_ANDROID_REL = SHT_LOOS + 1;
+static constexpr uint32_t SHT_ANDROID_RELA = SHT_LOOS + 2;
+
+// Alignment to preserve, in bytes.  This must be at least as large as the
+// largest d_align and sh_addralign values found in the loaded file.
+// Out of caution for RELRO page alignment, we preserve to a complete target
+// page.  See http://www.airs.com/blog/archives/189.
+static constexpr size_t kPreserveAlignment = 4096;
+
+// Get section data.  Checks that the section has exactly one data entry,
+// so that the section size and the data size are the same.  True in
+// practice for all sections we resize when packing or unpacking.  Done
+// by ensuring that a call to elf_getdata(section, data) returns NULL as
+// the next data entry.
+static Elf_Data* GetSectionData(Elf_Scn* section) {
+  Elf_Data* data = elf_getdata(section, NULL);
+  CHECK(data && elf_getdata(section, data) == NULL);
+  return data;
+}
+
+// Rewrite section data.  Allocates new data and makes it the data element's
+// buffer.  Relies on program exit to free allocated data.
+static void RewriteSectionData(Elf_Scn* section,
+                               const void* section_data,
+                               size_t size) {
+  Elf_Data* data = GetSectionData(section);
+  CHECK(size == data->d_size);
+  uint8_t* area = new uint8_t[size];
+  memcpy(area, section_data, size);
+  data->d_buf = area;
+}
+
+// Verbose ELF header logging.
+template <typename Ehdr>
+static void VerboseLogElfHeader(const Ehdr* elf_header) {
+  VLOG(1) << "e_phoff = " << elf_header->e_phoff;
+  VLOG(1) << "e_shoff = " << elf_header->e_shoff;
+  VLOG(1) << "e_ehsize = " << elf_header->e_ehsize;
+  VLOG(1) << "e_phentsize = " << elf_header->e_phentsize;
+  VLOG(1) << "e_phnum = " << elf_header->e_phnum;
+  VLOG(1) << "e_shnum = " << elf_header->e_shnum;
+  VLOG(1) << "e_shstrndx = " << elf_header->e_shstrndx;
+}
+
+// Verbose ELF program header logging.
+template <typename Phdr>
+static void VerboseLogProgramHeader(size_t program_header_index,
+                             const Phdr* program_header) {
+  std::string type;
+  switch (program_header->p_type) {
+    case PT_NULL: type = "NULL"; break;
+    case PT_LOAD: type = "LOAD"; break;
+    case PT_DYNAMIC: type = "DYNAMIC"; break;
+    case PT_INTERP: type = "INTERP"; break;
+    case PT_PHDR: type = "PHDR"; break;
+    case PT_GNU_RELRO: type = "GNU_RELRO"; break;
+    case PT_GNU_STACK: type = "GNU_STACK"; break;
+    case PT_ARM_EXIDX: type = "EXIDX"; break;
+    default: type = "(OTHER)"; break;
+  }
+  VLOG(1) << "phdr[" << program_header_index << "] : " << type;
+  VLOG(1) << "  p_offset = " << program_header->p_offset;
+  VLOG(1) << "  p_vaddr = " << program_header->p_vaddr;
+  VLOG(1) << "  p_paddr = " << program_header->p_paddr;
+  VLOG(1) << "  p_filesz = " << program_header->p_filesz;
+  VLOG(1) << "  p_memsz = " << program_header->p_memsz;
+  VLOG(1) << "  p_flags = " << program_header->p_flags;
+  VLOG(1) << "  p_align = " << program_header->p_align;
+}
+
+// Verbose ELF section header logging.
+template <typename Shdr>
+static void VerboseLogSectionHeader(const std::string& section_name,
+                             const Shdr* section_header) {
+  VLOG(1) << "section " << section_name;
+  VLOG(1) << "  sh_addr = " << section_header->sh_addr;
+  VLOG(1) << "  sh_offset = " << section_header->sh_offset;
+  VLOG(1) << "  sh_size = " << section_header->sh_size;
+  VLOG(1) << "  sh_entsize = " << section_header->sh_entsize;
+  VLOG(1) << "  sh_addralign = " << section_header->sh_addralign;
+}
+
+// Verbose ELF section data logging.
+static void VerboseLogSectionData(const Elf_Data* data) {
+  VLOG(1) << "  data";
+  VLOG(1) << "    d_buf = " << data->d_buf;
+  VLOG(1) << "    d_off = " << data->d_off;
+  VLOG(1) << "    d_size = " << data->d_size;
+  VLOG(1) << "    d_align = " << data->d_align;
+}
+
+// Load the complete ELF file into a memory image in libelf, and identify
+// the .rel.dyn or .rela.dyn, .dynamic, and .android.rel.dyn or
+// .android.rela.dyn sections.  No-op if the ELF file has already been loaded.
+template <typename ELF>
+bool ElfFile<ELF>::Load() {
+  if (elf_)
+    return true;
+
+  Elf* elf = elf_begin(fd_, ELF_C_RDWR, NULL);
+  CHECK(elf);
+
+  if (elf_kind(elf) != ELF_K_ELF) {
+    LOG(ERROR) << "File not in ELF format";
+    return false;
+  }
+
+  auto elf_header = ELF::getehdr(elf);
+  if (!elf_header) {
+    LOG(ERROR) << "Failed to load ELF header: " << elf_errmsg(elf_errno());
+    return false;
+  }
+
+  if (elf_header->e_type != ET_DYN) {
+    LOG(ERROR) << "ELF file is not a shared object";
+    return false;
+  }
+
+  // Require that our endianness matches that of the target, and that both
+  // are little-endian.  Safe for all current build/target combinations.
+  const int endian = elf_header->e_ident[EI_DATA];
+  CHECK(endian == ELFDATA2LSB);
+  CHECK(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
+
+  const int file_class = elf_header->e_ident[EI_CLASS];
+  VLOG(1) << "endian = " << endian << ", file class = " << file_class;
+  VerboseLogElfHeader(elf_header);
+
+  auto elf_program_header = ELF::getphdr(elf);
+  CHECK(elf_program_header != nullptr);
+
+  const typename ELF::Phdr* dynamic_program_header = NULL;
+  for (size_t i = 0; i < elf_header->e_phnum; ++i) {
+    auto program_header = &elf_program_header[i];
+    VerboseLogProgramHeader(i, program_header);
+
+    if (program_header->p_type == PT_DYNAMIC) {
+      CHECK(dynamic_program_header == NULL);
+      dynamic_program_header = program_header;
+    }
+  }
+  CHECK(dynamic_program_header != nullptr);
+
+  size_t string_index;
+  elf_getshdrstrndx(elf, &string_index);
+
+  // Notes of the dynamic relocations, packed relocations, and .dynamic
+  // sections.  Found while iterating sections, and later stored in class
+  // attributes.
+  Elf_Scn* found_relocations_section = nullptr;
+  Elf_Scn* found_dynamic_section = nullptr;
+
+  // Notes of relocation section types seen.  We require one or the other of
+  // these; both is unsupported.
+  bool has_rel_relocations = false;
+  bool has_rela_relocations = false;
+
+  Elf_Scn* section = NULL;
+  while ((section = elf_nextscn(elf, section)) != nullptr) {
+    auto section_header = ELF::getshdr(section);
+    std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+    VerboseLogSectionHeader(name, section_header);
+
+    // Note relocation section types.
+    if (section_header->sh_type == SHT_REL || section_header->sh_type == SHT_ANDROID_REL) {
+      has_rel_relocations = true;
+    }
+    if (section_header->sh_type == SHT_RELA || section_header->sh_type == SHT_ANDROID_RELA) {
+      has_rela_relocations = true;
+    }
+
+    // Note special sections as we encounter them.
+    if ((name == ".rel.dyn" || name == ".rela.dyn") &&
+        section_header->sh_size > 0) {
+      found_relocations_section = section;
+    }
+
+    if (section_header->sh_offset == dynamic_program_header->p_offset) {
+      found_dynamic_section = section;
+    }
+
+    // Ensure we preserve alignment, repeated later for the data block(s).
+    CHECK(section_header->sh_addralign <= kPreserveAlignment);
+
+    Elf_Data* data = NULL;
+    while ((data = elf_getdata(section, data)) != NULL) {
+      CHECK(data->d_align <= kPreserveAlignment);
+      VerboseLogSectionData(data);
+    }
+  }
+
+  // Loading failed if we did not find the required special sections.
+  if (!found_relocations_section) {
+    LOG(ERROR) << "Missing or empty .rel.dyn or .rela.dyn section";
+    return false;
+  }
+  if (!found_dynamic_section) {
+    LOG(ERROR) << "Missing .dynamic section";
+    return false;
+  }
+
+  // Loading failed if we could not identify the relocations type.
+  if (!has_rel_relocations && !has_rela_relocations) {
+    LOG(ERROR) << "No relocations sections found";
+    return false;
+  }
+  if (has_rel_relocations && has_rela_relocations) {
+    LOG(ERROR) << "Multiple relocations sections with different types found, "
+               << "not currently supported";
+    return false;
+  }
+
+  elf_ = elf;
+  relocations_section_ = found_relocations_section;
+  dynamic_section_ = found_dynamic_section;
+  relocations_type_ = has_rel_relocations ? REL : RELA;
+  return true;
+}
+
+// Helper for ResizeSection().  Adjust the main ELF header for the hole.
+template <typename ELF>
+static void AdjustElfHeaderForHole(typename ELF::Ehdr* elf_header,
+                                   typename ELF::Off hole_start,
+                                   ssize_t hole_size) {
+  if (elf_header->e_phoff > hole_start) {
+    elf_header->e_phoff += hole_size;
+    VLOG(1) << "e_phoff adjusted to " << elf_header->e_phoff;
+  }
+  if (elf_header->e_shoff > hole_start) {
+    elf_header->e_shoff += hole_size;
+    VLOG(1) << "e_shoff adjusted to " << elf_header->e_shoff;
+  }
+}
+
+// Helper for ResizeSection().  Adjust all section headers for the hole.
+template <typename ELF>
+static void AdjustSectionHeadersForHole(Elf* elf,
+                                        typename ELF::Off hole_start,
+                                        ssize_t hole_size) {
+  size_t string_index;
+  elf_getshdrstrndx(elf, &string_index);
+
+  Elf_Scn* section = NULL;
+  while ((section = elf_nextscn(elf, section)) != NULL) {
+    auto section_header = ELF::getshdr(section);
+    std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+
+    if (section_header->sh_offset > hole_start) {
+      section_header->sh_offset += hole_size;
+      VLOG(1) << "section " << name
+              << " sh_offset adjusted to " << section_header->sh_offset;
+    } else {
+      section_header->sh_addr -= hole_size;
+      VLOG(1) << "section " << name
+              << " sh_addr adjusted to " << section_header->sh_addr;
+    }
+  }
+}
+
+// Helper for ResizeSection().  Adjust the offsets of any program headers
+// that have offsets currently beyond the hole start.
+template <typename ELF>
+static void AdjustProgramHeaderOffsets(typename ELF::Phdr* program_headers,
+                                       size_t count,
+                                       typename ELF::Off hole_start,
+                                       ssize_t hole_size) {
+  for (size_t i = 0; i < count; ++i) {
+    typename ELF::Phdr* program_header = &program_headers[i];
+
+    if (program_header->p_offset > hole_start) {
+      // The hole start is past this segment, so adjust offset.
+      program_header->p_offset += hole_size;
+      VLOG(1) << "phdr[" << i
+              << "] p_offset adjusted to "<< program_header->p_offset;
+    } else {
+      program_header->p_vaddr -= hole_size;
+      program_header->p_paddr -= hole_size;
+      VLOG(1) << "phdr[" << i
+              << "] p_vaddr adjusted to "<< program_header->p_vaddr
+              << "; p_paddr adjusted to "<< program_header->p_paddr;
+    }
+  }
+}
+
+// Helper for ResizeSection().  Find the first loadable segment in the
+// file.  We expect it to map from file offset zero.
+template <typename ELF>
+static typename ELF::Phdr* FindLoadSegmentForHole(typename ELF::Phdr* program_headers,
+                                                  size_t count,
+                                                  typename ELF::Off hole_start) {
+  for (size_t i = 0; i < count; ++i) {
+    typename ELF::Phdr* program_header = &program_headers[i];
+
+    if (program_header->p_type == PT_LOAD &&
+        program_header->p_offset <= hole_start &&
+        (program_header->p_offset + program_header->p_filesz) >= hole_start ) {
+      return program_header;
+    }
+  }
+  LOG(FATAL) << "Cannot locate a LOAD segment with hole_start=0x" << std::hex << hole_start;
+  NOTREACHED();
+
+  return nullptr;
+}
+
+// Helper for ResizeSection().  Rewrite program headers.
+template <typename ELF>
+static void RewriteProgramHeadersForHole(Elf* elf,
+                                         typename ELF::Off hole_start,
+                                         ssize_t hole_size) {
+  const typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
+  CHECK(elf_header);
+
+  typename ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+  CHECK(elf_program_header);
+
+  const size_t program_header_count = elf_header->e_phnum;
+
+  // Locate the segment that we can overwrite to form the new LOAD entry,
+  // and the segment that we are going to split into two parts.
+  typename ELF::Phdr* target_load_header =
+      FindLoadSegmentForHole<ELF>(elf_program_header, program_header_count, hole_start);
+
+  VLOG(1) << "phdr[" << target_load_header - elf_program_header << "] adjust";
+  // Adjust PT_LOAD program header memsz and filesz
+  target_load_header->p_filesz += hole_size;
+  target_load_header->p_memsz += hole_size;
+
+  // Adjust the offsets and p_vaddrs
+  AdjustProgramHeaderOffsets<ELF>(elf_program_header,
+                                  program_header_count,
+                                  hole_start,
+                                  hole_size);
+}
+
+// Helper for ResizeSection().  Locate and return the dynamic section.
+template <typename ELF>
+static Elf_Scn* GetDynamicSection(Elf* elf) {
+  const typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
+  CHECK(elf_header);
+
+  const typename ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+  CHECK(elf_program_header);
+
+  // Find the program header that describes the dynamic section.
+  const typename ELF::Phdr* dynamic_program_header = NULL;
+  for (size_t i = 0; i < elf_header->e_phnum; ++i) {
+    const typename ELF::Phdr* program_header = &elf_program_header[i];
+
+    if (program_header->p_type == PT_DYNAMIC) {
+      dynamic_program_header = program_header;
+    }
+  }
+  CHECK(dynamic_program_header);
+
+  // Now find the section with the same offset as this program header.
+  Elf_Scn* dynamic_section = NULL;
+  Elf_Scn* section = NULL;
+  while ((section = elf_nextscn(elf, section)) != NULL) {
+    typename ELF::Shdr* section_header = ELF::getshdr(section);
+
+    if (section_header->sh_offset == dynamic_program_header->p_offset) {
+      dynamic_section = section;
+    }
+  }
+  CHECK(dynamic_section != NULL);
+
+  return dynamic_section;
+}
+
+// Helper for ResizeSection().  Adjust the .dynamic section for the hole.
+template <typename ELF>
+void ElfFile<ELF>::AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
+                                               typename ELF::Off hole_start,
+                                               ssize_t hole_size,
+                                               relocations_type_t relocations_type) {
+  CHECK(relocations_type != NONE);
+  Elf_Data* data = GetSectionData(dynamic_section);
+
+  auto dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+  std::vector<typename ELF::Dyn> dynamics(
+      dynamic_base,
+      dynamic_base + data->d_size / sizeof(dynamics[0]));
+
+  if (hole_size > 0) { // expanding
+    hole_start += hole_size;
+  }
+
+  for (size_t i = 0; i < dynamics.size(); ++i) {
+    typename ELF::Dyn* dynamic = &dynamics[i];
+    const typename ELF::Sword tag = dynamic->d_tag;
+
+    // Any tags that hold offsets are adjustment candidates.
+    const bool is_adjustable = (tag == DT_PLTGOT ||
+                                tag == DT_HASH ||
+                                tag == DT_GNU_HASH ||
+                                tag == DT_STRTAB ||
+                                tag == DT_SYMTAB ||
+                                tag == DT_RELA ||
+                                tag == DT_INIT ||
+                                tag == DT_FINI ||
+                                tag == DT_REL ||
+                                tag == DT_JMPREL ||
+                                tag == DT_INIT_ARRAY ||
+                                tag == DT_FINI_ARRAY ||
+                                tag == DT_ANDROID_REL||
+                                tag == DT_ANDROID_RELA);
+
+    if (is_adjustable && dynamic->d_un.d_ptr <= hole_start) {
+      dynamic->d_un.d_ptr -= hole_size;
+      VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
+              << " d_ptr adjusted to " << dynamic->d_un.d_ptr;
+    }
+
+    // DT_RELSZ or DT_RELASZ indicate the overall size of relocations.
+    // Only one will be present.  Adjust by hole size.
+    if (tag == DT_RELSZ || tag == DT_RELASZ || tag == DT_ANDROID_RELSZ || tag == DT_ANDROID_RELASZ) {
+      dynamic->d_un.d_val += hole_size;
+      VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
+              << " d_val adjusted to " << dynamic->d_un.d_val;
+    }
+
+    // Ignore DT_RELCOUNT and DT_RELACOUNT: (1) nobody uses them and
+    // technically (2) the relative relocation count is not changed.
+
+    // DT_RELENT and DT_RELAENT don't change, ignore them as well.
+  }
+
+  void* section_data = &dynamics[0];
+  size_t bytes = dynamics.size() * sizeof(dynamics[0]);
+  RewriteSectionData(dynamic_section, section_data, bytes);
+}
+
+// Resize a section.  If the new size is larger than the current size, open
+// up a hole by increasing file offsets that come after the hole.  If smaller
+// than the current size, remove the hole by decreasing those offsets.
+template <typename ELF>
+void ElfFile<ELF>::ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size,
+                                 typename ELF::Word new_sh_type,
+                                 relocations_type_t relocations_type) {
+
+  size_t string_index;
+  elf_getshdrstrndx(elf, &string_index);
+  auto section_header = ELF::getshdr(section);
+  std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+
+  if (section_header->sh_size == new_size) {
+    return;
+  }
+
+  // Require that the section size and the data size are the same.  True
+  // in practice for all sections we resize when packing or unpacking.
+  Elf_Data* data = GetSectionData(section);
+  CHECK(data->d_off == 0 && data->d_size == section_header->sh_size);
+
+  // Require that the section is not zero-length (that is, has allocated
+  // data that we can validly expand).
+  CHECK(data->d_size && data->d_buf);
+
+  const auto hole_start = section_header->sh_offset;
+  const ssize_t hole_size = new_size - data->d_size;
+
+  VLOG_IF(1, (hole_size > 0)) << "expand section (" << name << ") size: " <<
+      data->d_size << " -> " << (data->d_size + hole_size);
+  VLOG_IF(1, (hole_size < 0)) << "shrink section (" << name << ") size: " <<
+      data->d_size << " -> " << (data->d_size + hole_size);
+
+  // libelf overrides sh_entsize for known sh_types, so it does not matter what we set
+  // for SHT_REL/SHT_RELA.
+  typename ELF::Xword new_entsize =
+      (new_sh_type == SHT_ANDROID_REL || new_sh_type == SHT_ANDROID_RELA) ? 1 : 0;
+
+  VLOG(1) << "Update section (" << name << ") entry size: " <<
+      section_header->sh_entsize << " -> " << new_entsize;
+
+  // Resize the data and the section header.
+  data->d_size += hole_size;
+  section_header->sh_size += hole_size;
+  section_header->sh_entsize = new_entsize;
+  section_header->sh_type = new_sh_type;
+
+  // Add the hole size to all offsets in the ELF file that are after the
+  // start of the hole.  If the hole size is positive we are expanding the
+  // section to create a new hole; if negative, we are closing up a hole.
+
+  // Start with the main ELF header.
+  typename ELF::Ehdr* elf_header = ELF::getehdr(elf);
+  AdjustElfHeaderForHole<ELF>(elf_header, hole_start, hole_size);
+
+  // Adjust all section headers.
+  AdjustSectionHeadersForHole<ELF>(elf, hole_start, hole_size);
+
+  // Rewrite the program headers to either split or coalesce segments,
+  // and adjust dynamic entries to match.
+  RewriteProgramHeadersForHole<ELF>(elf, hole_start, hole_size);
+
+  Elf_Scn* dynamic_section = GetDynamicSection<ELF>(elf);
+  AdjustDynamicSectionForHole(dynamic_section, hole_start, hole_size, relocations_type);
+}
+
+// Find the first slot in a dynamics array with the given tag.  The array
+// always ends with a free (unused) element, and which we exclude from the
+// search.  Returns dynamics->size() if not found.
+template <typename ELF>
+static size_t FindDynamicEntry(typename ELF::Sword tag,
+                               std::vector<typename ELF::Dyn>* dynamics) {
+  // Loop until the penultimate entry.  We exclude the end sentinel.
+  for (size_t i = 0; i < dynamics->size() - 1; ++i) {
+    if (dynamics->at(i).d_tag == tag) {
+      return i;
+    }
+  }
+
+  // The tag was not found.
+  return dynamics->size();
+}
+
+// Replace dynamic entry.
+template <typename ELF>
+static void ReplaceDynamicEntry(typename ELF::Sword tag,
+                                const typename ELF::Dyn& dyn,
+                                std::vector<typename ELF::Dyn>* dynamics) {
+  const size_t slot = FindDynamicEntry<ELF>(tag, dynamics);
+  if (slot == dynamics->size()) {
+    LOG(FATAL) << "Dynamic slot is not found for tag=" << tag;
+  }
+
+  // Replace this entry with the one supplied.
+  dynamics->at(slot) = dyn;
+  VLOG(1) << "dynamic[" << slot << "] overwritten with " << dyn.d_tag;
+}
+
+// Remove relative entries from dynamic relocations and write as packed
+// data into android packed relocations.
+template <typename ELF>
+bool ElfFile<ELF>::PackRelocations() {
+  // Load the ELF file into libelf.
+  if (!Load()) {
+    LOG(ERROR) << "Failed to load as ELF";
+    return false;
+  }
+
+  // Retrieve the current dynamic relocations section data.
+  Elf_Data* data = GetSectionData(relocations_section_);
+  // we always pack rela, because packed format is pretty much the same
+  std::vector<typename ELF::Rela> relocations;
+
+  if (relocations_type_ == REL) {
+    // Convert data to a vector of relocations.
+    const typename ELF::Rel* relocations_base = reinterpret_cast<typename ELF::Rel*>(data->d_buf);
+    ConvertRelArrayToRelaVector(relocations_base,
+        data->d_size / sizeof(typename ELF::Rel), &relocations);
+    LOG(INFO) << "Relocations   : REL";
+  } else if (relocations_type_ == RELA) {
+    // Convert data to a vector of relocations with addends.
+    const typename ELF::Rela* relocations_base = reinterpret_cast<typename ELF::Rela*>(data->d_buf);
+    relocations = std::vector<typename ELF::Rela>(
+        relocations_base,
+        relocations_base + data->d_size / sizeof(relocations[0]));
+
+    LOG(INFO) << "Relocations   : RELA";
+  } else {
+    NOTREACHED();
+  }
+
+  return PackTypedRelocations(&relocations);
+}
+
+// Helper for PackRelocations().  Rel type is one of ELF::Rel or ELF::Rela.
+template <typename ELF>
+bool ElfFile<ELF>::PackTypedRelocations(std::vector<typename ELF::Rela>* relocations) {
+  typedef typename ELF::Rela Rela;
+
+  // If no relocations then we have nothing packable.  Perhaps
+  // the shared object has already been packed?
+  if (relocations->empty()) {
+    LOG(ERROR) << "No relocations found (already packed?)";
+    return false;
+  }
+
+  const size_t rel_size =
+      relocations_type_ == RELA ? sizeof(typename ELF::Rela) : sizeof(typename ELF::Rel);
+  const size_t initial_bytes = relocations->size() * rel_size;
+
+  LOG(INFO) << "Unpacked                   : " << initial_bytes << " bytes";
+  std::vector<uint8_t> packed;
+  RelocationPacker<ELF> packer;
+
+  // Pack relocations: dry run to estimate memory savings.
+  packer.PackRelocations(*relocations, &packed);
+  const size_t packed_bytes_estimate = packed.size() * sizeof(packed[0]);
+  LOG(INFO) << "Packed         (no padding): " << packed_bytes_estimate << " bytes";
+
+  if (packed.empty()) {
+    LOG(INFO) << "Too few relocations to pack";
+    return false;
+  }
+
+  // Pre-calculate the size of the hole we will close up when we rewrite
+  // dynamic relocations.  We have to adjust relocation addresses to
+  // account for this.
+  typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
+  ssize_t hole_size = initial_bytes - packed_bytes_estimate;
+
+  // hole_size needs to be page_aligned.
+  hole_size -= hole_size % kPreserveAlignment;
+
+  LOG(INFO) << "Compaction                 : " << hole_size << " bytes";
+
+  // Adjusting for alignment may have removed any packing benefit.
+  if (hole_size == 0) {
+    LOG(INFO) << "Too few relocations to pack after alignment";
+    return false;
+  }
+
+  if (hole_size <= 0) {
+    LOG(INFO) << "Packing relocations saves no space";
+    return false;
+  }
+
+  size_t data_padding_bytes = is_padding_relocations_ ?
+      initial_bytes - packed_bytes_estimate :
+      initial_bytes - hole_size - packed_bytes_estimate;
+
+  // pad data
+  std::vector<uint8_t> padding(data_padding_bytes, 0);
+  packed.insert(packed.end(), padding.begin(), padding.end());
+
+  const void* packed_data = &packed[0];
+
+  // Run a loopback self-test as a check that packing is lossless.
+  std::vector<Rela> unpacked;
+  packer.UnpackRelocations(packed, &unpacked);
+  CHECK(unpacked.size() == relocations->size());
+  CHECK(!memcmp(&unpacked[0],
+                &relocations->at(0),
+                unpacked.size() * sizeof(unpacked[0])));
+
+  // Rewrite the current dynamic relocations section with packed one then shrink it to size.
+  const size_t bytes = packed.size() * sizeof(packed[0]);
+  ResizeSection(elf_, relocations_section_, bytes,
+      relocations_type_ == REL ? SHT_ANDROID_REL : SHT_ANDROID_RELA, relocations_type_);
+  RewriteSectionData(relocations_section_, packed_data, bytes);
+
+  // TODO (dimitry): fix string table and replace .rel.dyn/plt with .android.rel.dyn/plt
+
+  // Rewrite .dynamic and rename relocation tags describing the packed android
+  // relocations.
+  Elf_Data* data = GetSectionData(dynamic_section_);
+  const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+  std::vector<typename ELF::Dyn> dynamics(
+      dynamic_base,
+      dynamic_base + data->d_size / sizeof(dynamics[0]));
+  section_header = ELF::getshdr(relocations_section_);
+  {
+    typename ELF::Dyn dyn;
+    dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA;
+    dyn.d_un.d_ptr = section_header->sh_addr;
+    ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_REL : DT_RELA, dyn, &dynamics);
+  }
+  {
+    typename ELF::Dyn dyn;
+    dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ;
+    dyn.d_un.d_val = section_header->sh_size;
+    ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_RELSZ : DT_RELASZ, dyn, &dynamics);
+  }
+
+  const void* dynamics_data = &dynamics[0];
+  const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
+  RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes);
+
+  Flush();
+  return true;
+}
+
+// Find packed relative relocations in the packed android relocations
+// section, unpack them, and rewrite the dynamic relocations section to
+// contain unpacked data.
+template <typename ELF>
+bool ElfFile<ELF>::UnpackRelocations() {
+  // Load the ELF file into libelf.
+  if (!Load()) {
+    LOG(ERROR) << "Failed to load as ELF";
+    return false;
+  }
+
+  typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
+  // Retrieve the current packed android relocations section data.
+  Elf_Data* data = GetSectionData(relocations_section_);
+
+  // Convert data to a vector of bytes.
+  const uint8_t* packed_base = reinterpret_cast<uint8_t*>(data->d_buf);
+  std::vector<uint8_t> packed(
+      packed_base,
+      packed_base + data->d_size / sizeof(packed[0]));
+
+  if ((section_header->sh_type == SHT_ANDROID_RELA || section_header->sh_type == SHT_ANDROID_REL) &&
+      packed.size() > 3 &&
+      packed[0] == 'A' &&
+      packed[1] == 'P' &&
+      (packed[2] == 'U' || packed[2] == 'S') &&
+      packed[3] == '2') {
+    LOG(INFO) << "Relocations   : " << (relocations_type_ == REL ? "REL" : "RELA");
+  } else {
+    LOG(ERROR) << "Packed relocations not found (not packed?)";
+    return false;
+  }
+
+  return UnpackTypedRelocations(packed);
+}
+
+// Helper for UnpackRelocations().  Rel type is one of ELF::Rel or ELF::Rela.
+template <typename ELF>
+bool ElfFile<ELF>::UnpackTypedRelocations(const std::vector<uint8_t>& packed) {
+  // Unpack the data to re-materialize the relative relocations.
+  const size_t packed_bytes = packed.size() * sizeof(packed[0]);
+  LOG(INFO) << "Packed           : " << packed_bytes << " bytes";
+  std::vector<typename ELF::Rela> unpacked_relocations;
+  RelocationPacker<ELF> packer;
+  packer.UnpackRelocations(packed, &unpacked_relocations);
+
+  const size_t relocation_entry_size =
+      relocations_type_ == REL ? sizeof(typename ELF::Rel) : sizeof(typename ELF::Rela);
+  const size_t unpacked_bytes = unpacked_relocations.size() * relocation_entry_size;
+  LOG(INFO) << "Unpacked         : " << unpacked_bytes << " bytes";
+
+  // Retrieve the current dynamic relocations section data.
+  Elf_Data* data = GetSectionData(relocations_section_);
+
+  LOG(INFO) << "Relocations      : " << unpacked_relocations.size() << " entries";
+
+  // If we found the same number of null relocation entries in the dynamic
+  // relocations section as we hold as unpacked relative relocations, then
+  // this is a padded file.
+
+  const bool is_padded = packed_bytes == unpacked_bytes;
+
+  // Unless padded, pre-apply relative relocations to account for the
+  // hole, and pre-adjust all relocation offsets accordingly.
+  typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_);
+
+  if (!is_padded) {
+    LOG(INFO) << "Expansion     : " << unpacked_bytes - packed_bytes << " bytes";
+  }
+
+  // Rewrite the current dynamic relocations section with unpacked version of
+  // relocations.
+  const void* section_data = nullptr;
+  std::vector<typename ELF::Rel> unpacked_rel_relocations;
+  if (relocations_type_ == RELA) {
+    section_data = &unpacked_relocations[0];
+  } else if (relocations_type_ == REL) {
+    ConvertRelaVectorToRelVector(unpacked_relocations, &unpacked_rel_relocations);
+    section_data = &unpacked_rel_relocations[0];
+  } else {
+    NOTREACHED();
+  }
+
+  ResizeSection(elf_, relocations_section_, unpacked_bytes,
+      relocations_type_ == REL ? SHT_REL : SHT_RELA, relocations_type_);
+  RewriteSectionData(relocations_section_, section_data, unpacked_bytes);
+
+  // Rewrite .dynamic to remove two tags describing packed android relocations.
+  data = GetSectionData(dynamic_section_);
+  const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf);
+  std::vector<typename ELF::Dyn> dynamics(
+      dynamic_base,
+      dynamic_base + data->d_size / sizeof(dynamics[0]));
+  {
+    typename ELF::Dyn dyn;
+    dyn.d_tag = relocations_type_ == REL ? DT_REL : DT_RELA;
+    dyn.d_un.d_ptr = section_header->sh_addr;
+    ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA,
+        dyn, &dynamics);
+  }
+
+  {
+    typename ELF::Dyn dyn;
+    dyn.d_tag = relocations_type_ == REL ? DT_RELSZ : DT_RELASZ;
+    dyn.d_un.d_val = section_header->sh_size;
+    ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ,
+        dyn, &dynamics);
+  }
+
+  const void* dynamics_data = &dynamics[0];
+  const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
+  RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes);
+
+  Flush();
+  return true;
+}
+
+// Flush rewritten shared object file data.
+template <typename ELF>
+void ElfFile<ELF>::Flush() {
+  // Flag all ELF data held in memory as needing to be written back to the
+  // file, and tell libelf that we have controlled the file layout.
+  elf_flagelf(elf_, ELF_C_SET, ELF_F_DIRTY);
+  elf_flagelf(elf_, ELF_C_SET, ELF_F_LAYOUT);
+
+  // Write ELF data back to disk.
+  const off_t file_bytes = elf_update(elf_, ELF_C_WRITE);
+  if (file_bytes == -1) {
+    LOG(ERROR) << "elf_update failed: " << elf_errmsg(elf_errno());
+  }
+
+  CHECK(file_bytes > 0);
+  VLOG(1) << "elf_update returned: " << file_bytes;
+
+  // Clean up libelf, and truncate the output file to the number of bytes
+  // written by elf_update().
+  elf_end(elf_);
+  elf_ = NULL;
+  const int truncate = ftruncate(fd_, file_bytes);
+  CHECK(truncate == 0);
+}
+
+template <typename ELF>
+void ElfFile<ELF>::ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array,
+                                               size_t rel_array_size,
+                                               std::vector<typename ELF::Rela>* rela_vector) {
+  for (size_t i = 0; i<rel_array_size; ++i) {
+    typename ELF::Rela rela;
+    rela.r_offset = rel_array[i].r_offset;
+    rela.r_info = rel_array[i].r_info;
+    rela.r_addend = 0;
+    rela_vector->push_back(rela);
+  }
+}
+
+template <typename ELF>
+void ElfFile<ELF>::ConvertRelaVectorToRelVector(const std::vector<typename ELF::Rela>& rela_vector,
+                                                std::vector<typename ELF::Rel>* rel_vector) {
+  for (auto rela : rela_vector) {
+    typename ELF::Rel rel;
+    rel.r_offset = rela.r_offset;
+    rel.r_info = rela.r_info;
+    CHECK(rela.r_addend == 0);
+    rel_vector->push_back(rel);
+  }
+}
+
+template class ElfFile<ELF32_traits>;
+template class ElfFile<ELF64_traits>;
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.h b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.h
new file mode 100644
index 0000000..73c31923
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file.h
@@ -0,0 +1,155 @@
+// 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.
+
+// ELF shared object file updates handler.
+//
+// Provides functions to remove relative relocations from the .rel.dyn
+// or .rela.dyn sections and pack into .android.rel.dyn or .android.rela.dyn,
+// and unpack to return the file to its pre-packed state.
+//
+// Files to be packed or unpacked must include an existing .android.rel.dyn
+// or android.rela.dyn section.  A standard libchrome.<version>.so will not
+// contain this section, so the following can be used to add one:
+//
+//   echo -n 'NULL' >/tmp/small
+//   if file libchrome.<version>.so | grep -q 'ELF 32'; then
+//     arm-linux-androideabi-objcopy
+//         --add-section .android.rel.dyn=/tmp/small
+//         libchrome.<version>.so libchrome.<version>.so.packed
+//   else
+//     aarch64-linux-android-objcopy
+//         --add-section .android.rela.dyn=/tmp/small
+//         libchrome.<version>.so libchrome.<version>.so.packed
+//   fi
+//   rm /tmp/small
+//
+// To use, open the file and pass the file descriptor to the constructor,
+// then pack or unpack as desired.  Packing or unpacking will flush the file
+// descriptor on success.  Example:
+//
+//   int fd = open(..., O_RDWR);
+//   ElfFile elf_file(fd);
+//   bool status;
+//   if (is_packing)
+//     status = elf_file.PackRelocations();
+//   else
+//     status = elf_file.UnpackRelocations();
+//   close(fd);
+//
+// SetPadding() causes PackRelocations() to pad .rel.dyn or .rela.dyn with
+// NONE-type entries rather than cutting a hole out of the shared object
+// file.  This keeps all load addresses and offsets constant, and enables
+// easier debugging and testing.
+//
+// A packed shared object file has all of its relative relocations
+// removed from .rel.dyn or .rela.dyn, and replaced as packed data in
+// .android.rel.dyn or .android.rela.dyn respectively.  The resulting file
+// is shorter than its non-packed original.
+//
+// Unpacking a packed file restores the file to its non-packed state, by
+// expanding the packed data in .android.rel.dyn or .android.rela.dyn,
+// combining the relative relocations with the data already in .rel.dyn
+// or .rela.dyn, and then writing back the now expanded section.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_
+#define TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_
+
+#include <string.h>
+#include <vector>
+
+#include "elf.h"
+#include "libelf.h"
+#include "packer.h"
+
+namespace relocation_packer {
+
+// An ElfFile reads shared objects, and shuttles relative relocations
+// between .rel.dyn or .rela.dyn and .android.rel.dyn or .android.rela.dyn
+// sections.
+template <typename ELF>
+class ElfFile {
+ public:
+  explicit ElfFile(int fd)
+      : fd_(fd), is_padding_relocations_(false), elf_(NULL),
+        relocations_section_(NULL), dynamic_section_(NULL),
+        relocations_type_(NONE) {}
+  ~ElfFile() {}
+
+  // Set padding mode.  When padding, PackRelocations() will not shrink
+  // the .rel.dyn or .rela.dyn section, but instead replace relative with
+  // NONE-type entries.
+  // |flag| is true to pad .rel.dyn or .rela.dyn, false to shrink it.
+  inline void SetPadding(bool flag) { is_padding_relocations_ = flag; }
+
+  // Transfer relative relocations from .rel.dyn or .rela.dyn to a packed
+  // representation in .android.rel.dyn or .android.rela.dyn.  Returns true
+  // on success.
+  bool PackRelocations();
+
+  // Transfer relative relocations from a packed representation in
+  // .android.rel.dyn or .android.rela.dyn to .rel.dyn or .rela.dyn.  Returns
+  // true on success.
+  bool UnpackRelocations();
+
+ private:
+  enum relocations_type_t {
+    NONE = 0, REL, RELA
+  };
+
+  // Load a new ElfFile from a filedescriptor.  If flushing, the file must
+  // be open for read/write.  Returns true on successful ELF file load.
+  // |fd| is an open file descriptor for the shared object.
+  bool Load();
+
+  // Templated packer, helper for PackRelocations().  Rel type is one of
+  // ELF::Rel or ELF::Rela.
+  bool PackTypedRelocations(std::vector<typename ELF::Rela>* relocations);
+
+  // Templated unpacker, helper for UnpackRelocations().  Rel type is one of
+  // ELF::Rel or ELF::Rela.
+  bool UnpackTypedRelocations(const std::vector<uint8_t>& packed);
+
+  // Write ELF file changes.
+  void Flush();
+
+  void AdjustRelativeRelocationTargets(typename ELF::Off hole_start,
+                                       ssize_t hole_size,
+                                       std::vector<typename ELF::Rela>* relocations);
+
+  static void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size,
+                            typename ELF::Word new_sh_type, relocations_type_t relocations_type);
+
+  static void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
+                                          typename ELF::Off hole_start,
+                                          ssize_t hole_size,
+                                          relocations_type_t relocations_type);
+
+  static void ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array, size_t rel_array_size,
+                                          std::vector<typename ELF::Rela>* rela_vector);
+
+  static void ConvertRelaVectorToRelVector(const std::vector<typename ELF::Rela>& rela_vector,
+                                           std::vector<typename ELF::Rel>* rel_vector);
+
+
+  // File descriptor opened on the shared object.
+  int fd_;
+
+  // If set, pad rather than shrink .rel.dyn or .rela.dyn.  Primarily for
+  // debugging, allows packing to be checked without affecting load addresses.
+  bool is_padding_relocations_;
+
+  // Libelf handle, assigned by Load().
+  Elf* elf_;
+
+  // Sections that we manipulate, assigned by Load().
+  Elf_Scn* relocations_section_;
+  Elf_Scn* dynamic_section_;
+
+  // Relocation type found, assigned by Load().
+  relocations_type_t relocations_type_;
+};
+
+}  // namespace relocation_packer
+
+#endif  // TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file_unittest.cc
new file mode 100644
index 0000000..434f1010
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_file_unittest.cc
@@ -0,0 +1,188 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "elf_file.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+#include "debug.h"
+#include "elf_traits.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+void GetDataFilePath(const char* name, std::string* path) {
+  std::string data_dir;
+
+  const char* bindir = getenv("bindir");
+  if (bindir) {
+    data_dir = std::string(bindir);
+  } else {
+    char path[PATH_MAX];
+    memset(path, 0, sizeof(path));
+    ASSERT_NE(-1, readlink("/proc/self/exe", path, sizeof(path) - 1));
+
+    data_dir = std::string(path);
+    size_t pos = data_dir.rfind('/');
+    ASSERT_NE(std::string::npos, pos);
+
+    data_dir.erase(pos);
+  }
+
+  *path = data_dir + "/" + name;
+}
+
+void OpenRelocsTestFile(const char* name, FILE** stream) {
+  std::string path;
+  GetDataFilePath(name, &path);
+
+  FILE* testfile = fopen(path.c_str(), "rb");
+  ASSERT_FALSE(testfile == NULL) << "Error opening '" << path << "'";
+
+  FILE* temporary = tmpfile();
+  ASSERT_FALSE(temporary == NULL);
+
+  static const size_t buffer_size = 4096;
+  unsigned char buffer[buffer_size];
+
+  size_t bytes;
+  do {
+    bytes = fread(buffer, 1, sizeof(buffer), testfile);
+    ASSERT_EQ(bytes, fwrite(buffer, 1, bytes, temporary));
+  } while (bytes > 0);
+
+  ASSERT_EQ(0, fclose(testfile));
+  ASSERT_EQ(0, fseek(temporary, 0, SEEK_SET));
+  ASSERT_EQ(0, lseek(fileno(temporary), 0, SEEK_SET));
+
+  *stream = temporary;
+}
+
+void OpenRelocsTestFiles(const std::string& arch, FILE** relocs_so, FILE** packed_relocs_so) {
+  const std::string base = std::string("elf_file_unittest_relocs_") + arch;
+  const std::string relocs = base + ".so";
+  const std::string packed_relocs = base + "_packed.so";
+
+  OpenRelocsTestFile(relocs.c_str(), relocs_so);
+  OpenRelocsTestFile(packed_relocs.c_str(), packed_relocs_so);
+}
+
+void CloseRelocsTestFile(FILE* temporary) {
+  fclose(temporary);
+}
+
+void CloseRelocsTestFiles(FILE* relocs_so, FILE* packed_relocs_so) {
+  CloseRelocsTestFile(relocs_so);
+  CloseRelocsTestFile(packed_relocs_so);
+}
+
+void CheckFileContentsEqual(FILE* first, FILE* second) {
+  ASSERT_EQ(0, fseek(first, 0, SEEK_SET));
+  ASSERT_EQ(0, fseek(second, 0, SEEK_SET));
+
+  static const size_t buffer_size = 4096;
+  unsigned char first_buffer[buffer_size];
+  unsigned char second_buffer[buffer_size];
+
+  do {
+    size_t first_read = fread(first_buffer, 1, sizeof(first_buffer), first);
+    size_t second_read = fread(second_buffer, 1, sizeof(second_buffer), second);
+
+    EXPECT_EQ(first_read, second_read);
+    EXPECT_EQ(0, memcmp(first_buffer, second_buffer, first_read));
+  } while (!feof(first) && !feof(second));
+
+  EXPECT_TRUE(feof(first) && feof(second));
+}
+
+template <typename ELF>
+static void ProcessUnpack(FILE* relocs_so, FILE* packed_relocs_so) {
+  relocation_packer::ElfFile<ELF> elf_file(fileno(packed_relocs_so));
+
+  // Ensure packing fails (already packed).
+  EXPECT_FALSE(elf_file.PackRelocations());
+
+  // Unpack golden relocations, and check files are now identical.
+  EXPECT_TRUE(elf_file.UnpackRelocations());
+  CheckFileContentsEqual(packed_relocs_so, relocs_so);
+
+  CloseRelocsTestFiles(relocs_so, packed_relocs_so);
+}
+
+static void RunUnpackRelocationsTestFor(const std::string& arch) {
+  ASSERT_NE(static_cast<uint32_t>(EV_NONE), elf_version(EV_CURRENT));
+
+  FILE* relocs_so = NULL;
+  FILE* packed_relocs_so = NULL;
+  OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so);
+
+  if (relocs_so != NULL && packed_relocs_so != NULL) {
+    // lets detect elf class
+    ASSERT_EQ(0, fseek(relocs_so, EI_CLASS, SEEK_SET))
+        << "Invalid file length: " << strerror(errno);
+    uint8_t elf_class = 0;
+    ASSERT_EQ(1U, fread(&elf_class, 1, 1, relocs_so));
+    ASSERT_EQ(0, fseek(relocs_so, 0, SEEK_SET));
+    if (elf_class == ELFCLASS32) {
+      ProcessUnpack<ELF32_traits>(relocs_so, packed_relocs_so);
+    } else {
+      ProcessUnpack<ELF64_traits>(relocs_so, packed_relocs_so);
+    }
+  }
+}
+
+template <typename ELF>
+static void ProcessPack(FILE* relocs_so, FILE* packed_relocs_so) {
+  relocation_packer::ElfFile<ELF> elf_file(fileno(relocs_so));
+
+  // Ensure unpacking fails (not packed).
+  EXPECT_FALSE(elf_file.UnpackRelocations());
+
+  // Pack relocations, and check files are now identical.
+  EXPECT_TRUE(elf_file.PackRelocations());
+  CheckFileContentsEqual(relocs_so, packed_relocs_so);
+
+  CloseRelocsTestFiles(relocs_so, packed_relocs_so);
+}
+
+static void RunPackRelocationsTestFor(const std::string& arch) {
+  ASSERT_NE(static_cast<uint32_t>(EV_NONE), elf_version(EV_CURRENT));
+
+  FILE* relocs_so = NULL;
+  FILE* packed_relocs_so = NULL;
+  OpenRelocsTestFiles(arch, &relocs_so, &packed_relocs_so);
+
+  if (relocs_so != NULL && packed_relocs_so != NULL) {
+    // lets detect elf class
+    ASSERT_EQ(0, fseek(packed_relocs_so, EI_CLASS, SEEK_SET))
+        << "Invalid file length: " << strerror(errno);
+    uint8_t elf_class = 0;
+    ASSERT_EQ(1U, fread(&elf_class, 1, 1, packed_relocs_so));
+    fseek(packed_relocs_so, 0, SEEK_SET);
+    if (elf_class == ELFCLASS32) {
+      ProcessPack<ELF32_traits>(relocs_so, packed_relocs_so);
+    } else {
+      ProcessPack<ELF64_traits>(relocs_so, packed_relocs_so);
+    }
+  }
+}
+
+}  // namespace
+
+namespace relocation_packer {
+
+TEST(ElfFile, PackRelocations) {
+  RunPackRelocationsTestFor("arm32");
+  RunPackRelocationsTestFor("arm64");
+}
+
+TEST(ElfFile, UnpackRelocations) {
+  RunUnpackRelocationsTestFor("arm32");
+  RunUnpackRelocationsTestFor("arm64");
+}
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/elf_traits.h b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_traits.h
new file mode 100644
index 0000000..41b06c85
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/elf_traits.h
@@ -0,0 +1,64 @@
+// 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.
+
+// Target-specific ELF type traits.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_
+#define TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_
+
+#include "elf.h"
+#include "libelf.h"
+
+// ELF is a traits structure used to provide convenient aliases for
+// 32/64 bit Elf types and functions, depending on the target file.
+
+struct ELF32_traits {
+  typedef Elf32_Addr Addr;
+  typedef Elf32_Dyn Dyn;
+  typedef Elf32_Ehdr Ehdr;
+  typedef Elf32_Off Off;
+  typedef Elf32_Phdr Phdr;
+  typedef Elf32_Rel Rel;
+  typedef Elf32_Rela Rela;
+  typedef Elf32_Shdr Shdr;
+  typedef Elf32_Sword Sword;
+  typedef Elf32_Sxword Sxword;
+  typedef Elf32_Sym Sym;
+  typedef Elf32_Word Word;
+  typedef Elf32_Xword Xword;
+  typedef Elf32_Half Half;
+
+  static inline Ehdr* getehdr(Elf* elf) { return elf32_getehdr(elf); }
+  static inline Phdr* getphdr(Elf* elf) { return elf32_getphdr(elf); }
+  static inline Shdr* getshdr(Elf_Scn* scn) { return elf32_getshdr(scn); }
+  static inline Word elf_r_type(Word info) { return ELF32_R_TYPE(info); }
+  static inline int elf_st_type(uint8_t info) { return ELF32_ST_TYPE(info); }
+  static inline Word elf_r_sym(Word info) { return ELF32_R_SYM(info); }
+};
+
+struct ELF64_traits {
+  typedef Elf64_Addr Addr;
+  typedef Elf64_Dyn Dyn;
+  typedef Elf64_Ehdr Ehdr;
+  typedef Elf64_Off Off;
+  typedef Elf64_Phdr Phdr;
+  typedef Elf64_Rel Rel;
+  typedef Elf64_Rela Rela;
+  typedef Elf64_Shdr Shdr;
+  typedef Elf64_Sword Sword;
+  typedef Elf64_Sxword Sxword;
+  typedef Elf64_Sym Sym;
+  typedef Elf64_Word Word;
+  typedef Elf64_Xword Xword;
+  typedef Elf64_Half Half;
+
+  static inline Ehdr* getehdr(Elf* elf) { return elf64_getehdr(elf); }
+  static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); }
+  static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); }
+  static inline Xword elf_r_type(Xword info) { return ELF64_R_TYPE(info); }
+  static inline int elf_st_type(uint8_t info) { return ELF64_ST_TYPE(info); }
+  static inline Word elf_r_sym(Xword info) { return ELF64_R_SYM(info); }
+};
+
+#endif  // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.cc
new file mode 100644
index 0000000..101c557
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.cc
@@ -0,0 +1,87 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "leb128.h"
+
+#include <stdint.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// Empty constructor and destructor to silence chromium-style.
+template <typename uint_t>
+Leb128Encoder<uint_t>::Leb128Encoder() { }
+
+template <typename uint_t>
+Leb128Encoder<uint_t>::~Leb128Encoder() { }
+
+// Add a single value to the encoding.  Values are encoded with variable
+// length.  The least significant 7 bits of each byte hold 7 bits of data,
+// and the most significant bit is set on each byte except the last.
+template <typename uint_t>
+void Leb128Encoder<uint_t>::Enqueue(uint_t value) {
+  uint_t uvalue = static_cast<uint_t>(value);
+  do {
+    const uint8_t byte = uvalue & 127;
+    uvalue >>= 7;
+    encoding_.push_back((uvalue ? 128 : 0) | byte);
+  } while (uvalue);
+}
+
+// Add a vector of values to the encoding.
+template <typename uint_t>
+void Leb128Encoder<uint_t>::EnqueueAll(const std::vector<uint_t>& values) {
+  for (size_t i = 0; i < values.size(); ++i) {
+    Enqueue(values[i]);
+  }
+}
+
+// Create a new decoder for the given encoded stream.
+template <typename uint_t>
+Leb128Decoder<uint_t>::Leb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with) {
+  encoding_ = encoding;
+  cursor_ = start_with;
+}
+
+// Empty destructor to silence chromium-style.
+template <typename uint_t>
+Leb128Decoder<uint_t>::~Leb128Decoder() { }
+
+// Decode and retrieve a single value from the encoding.  Read forwards until
+// a byte without its most significant bit is found, then read the 7 bit
+// fields of the bytes spanned to re-form the value.
+template <typename uint_t>
+uint_t Leb128Decoder<uint_t>::Dequeue() {
+  uint_t value = 0;
+
+  size_t shift = 0;
+  uint8_t byte;
+
+  // Loop until we reach a byte with its high order bit clear.
+  do {
+    byte = encoding_[cursor_++];
+    value |= static_cast<uint_t>(byte & 127) << shift;
+    shift += 7;
+  } while (byte & 128);
+
+  return value;
+}
+
+// Decode and retrieve all remaining values from the encoding.
+template <typename uint_t>
+void Leb128Decoder<uint_t>::DequeueAll(std::vector<uint_t>* values) {
+  while (cursor_ < encoding_.size()) {
+    values->push_back(Dequeue());
+  }
+}
+
+template class Leb128Encoder<uint32_t>;
+template class Leb128Encoder<uint64_t>;
+
+template class Leb128Decoder<uint32_t>;
+template class Leb128Decoder<uint64_t>;
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.h b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.h
new file mode 100644
index 0000000..2c5b5d07
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128.h
@@ -0,0 +1,76 @@
+// 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.
+
+// LEB128 encoder and decoder for packed relative relocations.
+//
+// Run-length encoded relative relocations consist of a large number
+// of pairs of relatively small positive integer values.  Encoding these as
+// LEB128 saves space.
+//
+// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_LEB128_H_
+#define TOOLS_RELOCATION_PACKER_SRC_LEB128_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// Encode packed words as a LEB128 byte stream.
+template <typename uint_t>
+class Leb128Encoder {
+ public:
+  // Explicit (but empty) constructor and destructor, for chromium-style.
+  Leb128Encoder();
+  ~Leb128Encoder();
+
+  // Add a value to the encoding stream.
+  // |value| is the unsigned int to add.
+  void Enqueue(uint_t value);
+
+  // Add a vector of values to the encoding stream.
+  // |values| is the vector of unsigned ints to add.
+  void EnqueueAll(const std::vector<uint_t>& values);
+
+  // Retrieve the encoded representation of the values.
+  // |encoding| is the returned vector of encoded data.
+  void GetEncoding(std::vector<uint8_t>* encoding) { *encoding = encoding_; }
+
+ private:
+  // Growable vector holding the encoded LEB128 stream.
+  std::vector<uint8_t> encoding_;
+};
+
+// Decode a LEB128 byte stream to produce packed words.
+template <typename uint_t>
+class Leb128Decoder {
+ public:
+  // Create a new decoder for the given encoded stream.
+  // |encoding| is the vector of encoded data.
+  explicit Leb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with);
+
+  // Explicit (but empty) destructor, for chromium-style.
+  ~Leb128Decoder();
+
+  // Retrieve the next value from the encoded stream.
+  uint_t Dequeue();
+
+  // Retrieve all remaining values from the encoded stream.
+  // |values| is the vector of decoded data.
+  void DequeueAll(std::vector<uint_t>* values);
+
+ private:
+  // Encoded LEB128 stream.
+  std::vector<uint8_t> encoding_;
+
+  // Cursor indicating the current stream retrieval point.
+  size_t cursor_;
+};
+
+}  // namespace relocation_packer
+
+#endif  // TOOLS_RELOCATION_PACKER_SRC_LEB128_H_
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/leb128_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128_unittest.cc
new file mode 100644
index 0000000..8a7028c
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/leb128_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "leb128.h"
+
+#include <vector>
+#include "gtest/gtest.h"
+
+namespace relocation_packer {
+
+TEST(Leb128, Encoder64) {
+  std::vector<uint64_t> values;
+  values.push_back(624485);
+  values.push_back(0);
+  values.push_back(1);
+  values.push_back(127);
+  values.push_back(128);
+
+  Leb128Encoder<uint64_t> encoder;
+  encoder.EnqueueAll(values);
+
+  encoder.Enqueue(4294967295);
+  encoder.Enqueue(18446744073709551615ul);
+
+  std::vector<uint8_t> encoding;
+  encoder.GetEncoding(&encoding);
+
+  EXPECT_EQ(23U, encoding.size());
+  // 624485
+  EXPECT_EQ(0xe5, encoding[0]);
+  EXPECT_EQ(0x8e, encoding[1]);
+  EXPECT_EQ(0x26, encoding[2]);
+  // 0
+  EXPECT_EQ(0x00, encoding[3]);
+  // 1
+  EXPECT_EQ(0x01, encoding[4]);
+  // 127
+  EXPECT_EQ(0x7f, encoding[5]);
+  // 128
+  EXPECT_EQ(0x80, encoding[6]);
+  EXPECT_EQ(0x01, encoding[7]);
+  // 4294967295
+  EXPECT_EQ(0xff, encoding[8]);
+  EXPECT_EQ(0xff, encoding[9]);
+  EXPECT_EQ(0xff, encoding[10]);
+  EXPECT_EQ(0xff, encoding[11]);
+  EXPECT_EQ(0x0f, encoding[12]);
+  // 18446744073709551615
+  EXPECT_EQ(0xff, encoding[13]);
+  EXPECT_EQ(0xff, encoding[14]);
+  EXPECT_EQ(0xff, encoding[15]);
+  EXPECT_EQ(0xff, encoding[16]);
+  EXPECT_EQ(0xff, encoding[17]);
+  EXPECT_EQ(0xff, encoding[18]);
+  EXPECT_EQ(0xff, encoding[19]);
+  EXPECT_EQ(0xff, encoding[20]);
+  EXPECT_EQ(0xff, encoding[21]);
+  EXPECT_EQ(0x01, encoding[22]);
+}
+
+TEST(Leb128, Decoder64) {
+  std::vector<uint8_t> encoding;
+  // 624485
+  encoding.push_back(0xe5);
+  encoding.push_back(0x8e);
+  encoding.push_back(0x26);
+  // 0
+  encoding.push_back(0x00);
+  // 1
+  encoding.push_back(0x01);
+  // 127
+  encoding.push_back(0x7f);
+  // 128
+  encoding.push_back(0x80);
+  encoding.push_back(0x01);
+  // 4294967295
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0x0f);
+  // 18446744073709551615
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0x01);
+
+  Leb128Decoder<uint64_t> decoder(encoding, 0);
+
+  EXPECT_EQ(624485U, decoder.Dequeue());
+
+  std::vector<uint64_t> dequeued;
+  decoder.DequeueAll(&dequeued);
+
+  EXPECT_EQ(6U, dequeued.size());
+  EXPECT_EQ(0U, dequeued[0]);
+  EXPECT_EQ(1U, dequeued[1]);
+  EXPECT_EQ(127U, dequeued[2]);
+  EXPECT_EQ(128U, dequeued[3]);
+  EXPECT_EQ(4294967295U, dequeued[4]);
+  EXPECT_EQ(18446744073709551615UL, dequeued[5]);
+}
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/main.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/main.cc
new file mode 100644
index 0000000..3f784e4f
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/main.cc
@@ -0,0 +1,153 @@
+// 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.
+
+// Tool to pack and unpack relative relocations in a shared library.
+//
+// Packing removes relative relocations from .rel.dyn and writes them
+// in a more compact form to .android.rel.dyn.  Unpacking does the reverse.
+//
+// Invoke with -v to trace actions taken when packing or unpacking.
+// Invoke with -p to pad removed relocations with R_*_NONE.  Suppresses
+// shrinking of .rel.dyn.
+// See PrintUsage() below for full usage details.
+//
+// NOTE: Breaks with libelf 0.152, which is buggy.  libelf 0.158 works.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string>
+
+#include "debug.h"
+#include "elf_file.h"
+#include "elf_traits.h"
+#include "libelf.h"
+
+#include "nativehelper/ScopedFd.h"
+
+static void PrintUsage(const char* argv0) {
+  std::string temporary = argv0;
+  const size_t last_slash = temporary.find_last_of("/");
+  if (last_slash != temporary.npos) {
+    temporary.erase(0, last_slash + 1);
+  }
+  const char* basename = temporary.c_str();
+
+  printf(
+      "Usage: %s [-u] [-v] [-p] file\n\n"
+      "Pack or unpack relative relocations in a shared library.\n\n"
+      "  -u, --unpack   unpack previously packed relative relocations\n"
+      "  -v, --verbose  trace object file modifications (for debugging)\n"
+      "  -p, --pad      do not shrink relocations, but pad (for debugging)\n\n",
+      basename);
+
+  printf(
+      "Debug sections are not handled, so packing should not be used on\n"
+      "shared libraries compiled for debugging or otherwise unstripped.\n");
+}
+
+int main(int argc, char* argv[]) {
+  bool is_unpacking = false;
+  bool is_verbose = false;
+  bool is_padding = false;
+
+  static const option options[] = {
+    {"unpack", 0, 0, 'u'}, {"verbose", 0, 0, 'v'}, {"pad", 0, 0, 'p'},
+    {"help", 0, 0, 'h'}, {NULL, 0, 0, 0}
+  };
+  bool has_options = true;
+  while (has_options) {
+    int c = getopt_long(argc, argv, "uvph", options, NULL);
+    switch (c) {
+      case 'u':
+        is_unpacking = true;
+        break;
+      case 'v':
+        is_verbose = true;
+        break;
+      case 'p':
+        is_padding = true;
+        break;
+      case 'h':
+        PrintUsage(argv[0]);
+        return 0;
+      case '?':
+        LOG(INFO) << "Try '" << argv[0] << " --help' for more information.";
+        return 1;
+      case -1:
+        has_options = false;
+        break;
+      default:
+        NOTREACHED();
+        return 1;
+    }
+  }
+  if (optind != argc - 1) {
+    LOG(INFO) << "Try '" << argv[0] << " --help' for more information.";
+    return 1;
+  }
+
+  if (elf_version(EV_CURRENT) == EV_NONE) {
+    LOG(WARNING) << "Elf Library is out of date!";
+  }
+
+  const char* file = argv[argc - 1];
+  ScopedFd fd(open(file, O_RDWR));
+  if (fd.get() == -1) {
+    LOG(ERROR) << file << ": " << strerror(errno);
+    return 1;
+  }
+
+  if (is_verbose)
+    relocation_packer::Logger::SetVerbose(1);
+
+  // We need to detect elf class in order to create
+  // correct implementation
+  uint8_t e_ident[EI_NIDENT];
+  if (TEMP_FAILURE_RETRY(read(fd.get(), e_ident, EI_NIDENT) != EI_NIDENT)) {
+    LOG(ERROR) << file << ": failed to read elf header:" << strerror(errno);
+    return 1;
+  }
+
+  if (TEMP_FAILURE_RETRY(lseek(fd.get(), 0, SEEK_SET)) != 0) {
+    LOG(ERROR) << file << ": lseek to 0 failed:" << strerror(errno);
+    return 1;
+  }
+
+  bool status = false;
+
+  if (e_ident[EI_CLASS] == ELFCLASS32) {
+    relocation_packer::ElfFile<ELF32_traits> elf_file(fd.get());
+    elf_file.SetPadding(is_padding);
+
+    if (is_unpacking) {
+      status = elf_file.UnpackRelocations();
+    } else {
+      status = elf_file.PackRelocations();
+    }
+  } else if (e_ident[EI_CLASS] == ELFCLASS64) {
+    relocation_packer::ElfFile<ELF64_traits> elf_file(fd.get());
+    elf_file.SetPadding(is_padding);
+
+    if (is_unpacking) {
+      status = elf_file.UnpackRelocations();
+    } else {
+      status = elf_file.PackRelocations();
+    }
+  } else {
+    LOG(ERROR) << file << ": unknown ELFCLASS: " << e_ident[EI_CLASS];
+    return 1;
+  }
+
+  if (!status) {
+    LOG(ERROR) << file << ": failed to pack/unpack file";
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/nativehelper/ScopedFd.h b/third_party/android_platform/bionic/tools/relocation_packer/src/nativehelper/ScopedFd.h
new file mode 100644
index 0000000..5a22652
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/nativehelper/ScopedFd.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SCOPED_FD_H_included
+#define SCOPED_FD_H_included
+
+#include <unistd.h>
+
+// Local definition of DISALLOW_COPY_AND_ASSIGN, avoids depending on base.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&);               \
+  void operator=(const TypeName&)
+
+// A smart pointer that closes the given fd on going out of scope.
+// Use this when the fd is incidental to the purpose of your function,
+// but needs to be cleaned up on exit.
+class ScopedFd {
+public:
+    explicit ScopedFd(int fd) : fd_(fd) {
+    }
+
+    ~ScopedFd() {
+      reset();
+    }
+
+    int get() const {
+        return fd_;
+    }
+
+    int release() __attribute__((warn_unused_result)) {
+        int localFd = fd_;
+        fd_ = -1;
+        return localFd;
+    }
+
+    void reset(int new_fd = -1) {
+      if (fd_ != -1) {
+          TEMP_FAILURE_RETRY(close(fd_));
+      }
+      fd_ = new_fd;
+    }
+
+private:
+    int fd_;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedFd);
+};
+
+#endif  // SCOPED_FD_H_included
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/packer.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/packer.cc
new file mode 100644
index 0000000..8e30612
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/packer.cc
@@ -0,0 +1,88 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "packer.h"
+
+#include <vector>
+
+#include "debug.h"
+#include "delta_encoder.h"
+#include "elf_traits.h"
+#include "leb128.h"
+#include "sleb128.h"
+
+namespace relocation_packer {
+
+// Pack relocations into a group encoded packed representation.
+template <typename ELF>
+void RelocationPacker<ELF>::PackRelocations(const std::vector<typename ELF::Rela>& relocations,
+                                            std::vector<uint8_t>* packed) {
+  // Run-length encode.
+  std::vector<typename ELF::Addr> packed_words;
+  RelocationDeltaCodec<ELF> codec;
+  codec.Encode(relocations, &packed_words);
+
+  // If insufficient data do nothing.
+  if (packed_words.empty())
+    return;
+
+  Sleb128Encoder<typename ELF::Addr> sleb128_encoder;
+  Leb128Encoder<typename ELF::Addr> leb128_encoder;
+
+  std::vector<uint8_t> leb128_packed;
+  std::vector<uint8_t> sleb128_packed;
+
+  leb128_encoder.EnqueueAll(packed_words);
+  leb128_encoder.GetEncoding(&leb128_packed);
+
+  sleb128_encoder.EnqueueAll(packed_words);
+  sleb128_encoder.GetEncoding(&sleb128_packed);
+
+  // TODO (simonb): Estimate savings on current android system image and consider using
+  // one encoder for all packed relocations to reduce complexity.
+  if (leb128_packed.size() <= sleb128_packed.size()) {
+    packed->push_back('A');
+    packed->push_back('P');
+    packed->push_back('U');
+    packed->push_back('2');
+    packed->insert(packed->end(), leb128_packed.begin(), leb128_packed.end());
+  } else {
+    packed->push_back('A');
+    packed->push_back('P');
+    packed->push_back('S');
+    packed->push_back('2');
+    packed->insert(packed->end(), sleb128_packed.begin(), sleb128_packed.end());
+  }
+}
+
+// Unpack relative relocations from a run-length encoded packed
+// representation.
+template <typename ELF>
+void RelocationPacker<ELF>::UnpackRelocations(
+    const std::vector<uint8_t>& packed,
+    std::vector<typename ELF::Rela>* relocations) {
+
+  std::vector<typename ELF::Addr> packed_words;
+  CHECK(packed.size() > 4 &&
+        packed[0] == 'A' &&
+        packed[1] == 'P' &&
+        (packed[2] == 'U' || packed[2] == 'S') &&
+        packed[3] == '2');
+
+  if (packed[2] == 'U') {
+    Leb128Decoder<typename ELF::Addr> decoder(packed, 4);
+    decoder.DequeueAll(&packed_words);
+  } else {
+    Sleb128Decoder<typename ELF::Addr> decoder(packed, 4);
+    decoder.DequeueAll(&packed_words);
+  }
+
+  RelocationDeltaCodec<ELF> codec;
+  codec.Decode(packed_words, relocations);
+}
+
+template class RelocationPacker<ELF32_traits>;
+template class RelocationPacker<ELF64_traits>;
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/packer.h b/third_party/android_platform/bionic/tools/relocation_packer/src/packer.h
new file mode 100644
index 0000000..8a57e623
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/packer.h
@@ -0,0 +1,74 @@
+// 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.
+
+// Pack relative relocations into a more compact form.
+//
+//
+// For relative relocations without addends (32 bit platforms)
+// -----------------------------------------------------------
+//
+// Applies two packing strategies.  The first is run-length encoding, which
+// turns a large set of relative relocations into a much smaller set
+// of delta-count pairs, prefixed with a two-word header comprising the
+// count of pairs and the initial relocation offset.  The second is LEB128
+// encoding, which compresses the result of run-length encoding.
+//
+// Once packed, data is prefixed by an identifier that allows for any later
+// versioning of packing strategies.
+//
+// A complete packed stream of relocations without addends might look
+// something like:
+//
+//   "APR1"   pairs  init_offset count1 delta1 count2 delta2 ...
+//   41505231 f2b003 b08ac716    e001   04     01     10     ...
+//
+//
+// For relative relocations with addends (64 bit platforms)
+// --------------------------------------------------------
+//
+// Applies two packing strategies.  The first is delta encoding, which
+// turns a large set of relative relocations into a smaller set
+// of offset and addend delta pairs, prefixed with a header indicating the
+// count of pairs.  The second is signed LEB128 encoding, which compacts
+// the result of delta encoding.
+//
+// Once packed, data is prefixed by an identifier that allows for any later
+// versioning of packing strategies.
+//
+// A complete packed stream might look something like:
+//
+//   "APA1"   pairs  offset_d1 addend_d1 offset_d2 addend_d2 ...
+//   41505232 f2b018 04        28        08        9f01      ...
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_PACKER_H_
+#define TOOLS_RELOCATION_PACKER_SRC_PACKER_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "elf.h"
+
+namespace relocation_packer {
+
+// A RelocationPacker packs vectors of relocations into more
+// compact forms, and unpacks them to reproduce the pre-packed data.
+template <typename ELF>
+class RelocationPacker {
+ public:
+  // Pack relocations into a more compact form.
+  // |relocations| is a vector of relocation structs.
+  // |packed| is the vector of packed bytes into which relocations are packed.
+  static void PackRelocations(const std::vector<typename ELF::Rela>& relocations,
+                              std::vector<uint8_t>* packed);
+
+  // Unpack relocations from their more compact form.
+  // |packed| is the vector of packed relocations.
+  // |relocations| is a vector of unpacked relocation structs.
+  static void UnpackRelocations(const std::vector<uint8_t>& packed,
+                                std::vector<typename ELF::Rela>* relocations);
+};
+
+}  // namespace relocation_packer
+
+#endif  // TOOLS_RELOCATION_PACKER_SRC_PACKER_H_
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/packer_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/packer_unittest.cc
new file mode 100644
index 0000000..8dddd8b
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/packer_unittest.cc
@@ -0,0 +1,292 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "packer.h"
+
+#include <vector>
+#include "elf.h"
+#include "elf_traits.h"
+#include "gtest/gtest.h"
+
+
+template <typename ELF>
+static void AddRelocation(typename ELF::Addr addr,
+                   typename ELF::Xword info,
+                   typename ELF::Sxword addend,
+                   std::vector<typename ELF::Rela>* relocations) {
+  typename ELF::Rela relocation;
+  relocation.r_offset = addr;
+  relocation.r_info = info;
+  relocation.r_addend = addend;
+
+  relocations->push_back(relocation);
+}
+
+template <typename ELF>
+static bool CheckRelocation(typename ELF::Addr addr,
+                     typename ELF::Xword info,
+                     typename ELF::Sxword addend,
+                     const typename ELF::Rela& relocation) {
+  return relocation.r_offset == addr &&
+      relocation.r_info == info &&
+      relocation.r_addend == addend;
+}
+
+namespace relocation_packer {
+
+template <typename ELF>
+static void DoPackNoAddend() {
+  std::vector<typename ELF::Rela> relocations;
+  std::vector<uint8_t> packed;
+  // Initial relocation.
+  AddRelocation<ELF>(0xd1ce0000, 0x11, 0, &relocations);
+  // Two more relocations, 4 byte deltas.
+  AddRelocation<ELF>(0xd1ce0004, 0x11, 0, &relocations);
+  AddRelocation<ELF>(0xd1ce0008, 0x11, 0, &relocations);
+  // Three more relocations, 8 byte deltas.
+  AddRelocation<ELF>(0xd1ce0010, 0x11, 0, &relocations);
+  AddRelocation<ELF>(0xd1ce0018, 0x11, 0, &relocations);
+  AddRelocation<ELF>(0xd1ce0020, 0x11, 0, &relocations);
+
+  RelocationPacker<ELF> packer;
+
+  packed.clear();
+  packer.PackRelocations(relocations, &packed);
+
+  ASSERT_EQ(18U, packed.size());
+  // Identifier.
+  size_t ndx = 0;
+  EXPECT_EQ('A', packed[ndx++]);
+  EXPECT_EQ('P', packed[ndx++]);
+  EXPECT_EQ('U', packed[ndx++]);
+  EXPECT_EQ('2', packed[ndx++]);
+  // relocation count
+  EXPECT_EQ(6, packed[ndx++]);
+  // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
+  EXPECT_EQ(0xfc, packed[ndx++]);
+  EXPECT_EQ(0xff, packed[ndx++]);
+  EXPECT_EQ(0xb7, packed[ndx++]);
+  EXPECT_EQ(0x8e, packed[ndx++]);
+  EXPECT_EQ(0x0d, packed[ndx++]);
+  // first group
+  EXPECT_EQ(3, packed[ndx++]);  // size
+  EXPECT_EQ(3, packed[ndx++]); // flags
+  EXPECT_EQ(4, packed[ndx++]); // r_offset_delta
+  EXPECT_EQ(0x11, packed[ndx++]); // r_info
+  // second group
+  EXPECT_EQ(3, packed[ndx++]);  // size
+  EXPECT_EQ(3, packed[ndx++]); // flags
+  EXPECT_EQ(8, packed[ndx++]); // r_offset_delta
+  EXPECT_EQ(0x11, packed[ndx++]); // r_info
+
+  EXPECT_EQ(ndx, packed.size());
+}
+
+TEST(Packer, PackNoAddend) {
+  DoPackNoAddend<ELF32_traits>();
+  DoPackNoAddend<ELF64_traits>();
+}
+
+template <typename ELF>
+static void DoUnpackNoAddend() {
+  std::vector<typename ELF::Rela> relocations;
+  std::vector<uint8_t> packed;
+  packed.push_back('A');
+  packed.push_back('P');
+  packed.push_back('U');
+  packed.push_back('2');
+  // relocation count
+  packed.push_back(6);
+  // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
+  packed.push_back(0xfc);
+  packed.push_back(0xff);
+  packed.push_back(0xb7);
+  packed.push_back(0x8e);
+  packed.push_back(0x0d);
+  // first group
+  packed.push_back(3);  // size
+  packed.push_back(3); // flags
+  packed.push_back(4); // r_offset_delta
+  packed.push_back(0x11); // r_info
+  // second group
+  packed.push_back(3);  // size
+  packed.push_back(3); // flags
+  packed.push_back(8); // r_offset_delta
+  packed.push_back(0x11); // r_info
+
+  RelocationPacker<ELF> packer;
+  packer.UnpackRelocations(packed, &relocations);
+
+  size_t ndx = 0;
+  EXPECT_EQ(6U, relocations.size());
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x11, 0, relocations[ndx++]));
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x11, 0, relocations[ndx++]));
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x11, 0, relocations[ndx++]));
+
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0010, 0x11, 0, relocations[ndx++]));
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0018, 0x11, 0, relocations[ndx++]));
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0020, 0x11, 0, relocations[ndx++]));
+
+  EXPECT_EQ(ndx, relocations.size());
+}
+
+TEST(Packer, UnpackNoAddend) {
+  DoUnpackNoAddend<ELF32_traits>();
+  DoUnpackNoAddend<ELF64_traits>();
+}
+
+template <typename ELF>
+static void DoPackWithAddend() {
+  std::vector<typename ELF::Rela> relocations;
+
+  // Initial relocation.
+  AddRelocation<ELF>(0xd1ce0000, 0x01, 10024, &relocations);
+  // Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
+  AddRelocation<ELF>(0xd1ce0004, 0x01, 10012, &relocations);
+  AddRelocation<ELF>(0xd1ce0008, 0x01, 10024, &relocations);
+  // Three more relocations, 8 byte deltas, -24 byte addend deltas.
+  AddRelocation<ELF>(0xd1ce0010, 0x01, 10000, &relocations);
+  AddRelocation<ELF>(0xd1ce0018, 0x01, 9976, &relocations);
+  AddRelocation<ELF>(0xd1ce0020, 0x01, 9952, &relocations);
+
+  std::vector<uint8_t> packed;
+
+  RelocationPacker<ELF> packer;
+
+  packed.clear();
+  packer.PackRelocations(relocations, &packed);
+
+  EXPECT_EQ(26U, packed.size());
+  size_t ndx = 0;
+  // Identifier.
+  EXPECT_EQ('A', packed[ndx++]);
+  EXPECT_EQ('P', packed[ndx++]);
+  EXPECT_EQ('S', packed[ndx++]);
+  EXPECT_EQ('2', packed[ndx++]);
+  // Relocation count
+  EXPECT_EQ(6U, packed[ndx++]);
+  // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d/7d (depending on ELF::Addr)
+  EXPECT_EQ(0xfc, packed[ndx++]);
+  EXPECT_EQ(0xff, packed[ndx++]);
+  EXPECT_EQ(0xb7, packed[ndx++]);
+  EXPECT_EQ(0x8e, packed[ndx++]);
+  if (sizeof(typename ELF::Addr) == 8) {
+    // positive for uint64_t
+    EXPECT_EQ(0x0d, packed[ndx++]);
+  } else {
+    // negative for uint32_t
+    EXPECT_EQ(0x7d, packed[ndx++]);
+  }
+  // group 1
+  EXPECT_EQ(0x03, packed[ndx++]); // size
+  EXPECT_EQ(0x0b, packed[ndx++]); // flags
+  EXPECT_EQ(0x04, packed[ndx++]); // r_offset_delta
+  EXPECT_EQ(0x01, packed[ndx++]); // r_info
+  // group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80
+  EXPECT_EQ(0xa8, packed[ndx++]);
+  EXPECT_EQ(0xce, packed[ndx++]);
+  EXPECT_EQ(0x00, packed[ndx++]);
+  // group 1 - addend 2: -12 = 0x74
+  EXPECT_EQ(0x74, packed[ndx++]);
+  // group 1 - addend 3: +12 = 0x0c
+  EXPECT_EQ(0x0c, packed[ndx++]);
+
+  // group 2
+  EXPECT_EQ(0x03, packed[ndx++]); // size
+  EXPECT_EQ(0x0b, packed[ndx++]); // flags
+  EXPECT_EQ(0x08, packed[ndx++]); // r_offset_delta
+  EXPECT_EQ(0x01, packed[ndx++]); // r_info
+
+  // group 2 - addend 1: -24 = 0x68
+  EXPECT_EQ(0x68, packed[ndx++]);
+  // group 2 - addend 2: -24 = 0x68
+  EXPECT_EQ(0x68, packed[ndx++]);
+  // group 2 - addend 3: -24 = 0x68
+  EXPECT_EQ(0x68, packed[ndx++]);
+
+  EXPECT_EQ(ndx, packed.size());
+}
+
+TEST(Packer, PackWithAddend) {
+  DoPackWithAddend<ELF32_traits>();
+  DoPackWithAddend<ELF64_traits>();
+}
+
+template <typename ELF>
+static void DoUnpackWithAddend() {
+  std::vector<uint8_t> packed;
+  // Identifier.
+  packed.push_back('A');
+  packed.push_back('P');
+  packed.push_back('S');
+  packed.push_back('2');
+  // Relocation count
+  packed.push_back(6U);
+  // base relocation = 0xd1cdfffc -> fc, ff, b7, 8e, 0d
+  packed.push_back(0xfc);
+  packed.push_back(0xff);
+  packed.push_back(0xb7);
+  packed.push_back(0x8e);
+  if (sizeof(typename ELF::Addr) == 8) {
+    // positive for uint64_t
+    packed.push_back(0x0d);
+  } else {
+    // negative for uint32_t
+    packed.push_back(0x7d);
+  }
+  // group 1
+  packed.push_back(0x03); // size
+  packed.push_back(0x0b); // flags
+  packed.push_back(0x04); // r_offset_delta
+  packed.push_back(0x01); // r_info
+  // group 1 - addend 1: 10024 = 0xa8, 0xce, 0x80
+  packed.push_back(0xa8);
+  packed.push_back(0xce);
+  packed.push_back(0x00);
+  // group 1 - addend 2: -12 = 0x74
+  packed.push_back(0x74);
+  // group 1 - addend 3: +12 = 0x0c
+  packed.push_back(0x0c);
+
+  // group 2
+  packed.push_back(0x03); // size
+  packed.push_back(0x0b); // flags
+  packed.push_back(0x08); // r_offset_delta
+  packed.push_back(0x01); // r_info
+
+  // group 2 - addend 1: -24 = 0x68
+  packed.push_back(0x68);
+  // group 2 - addend 2: -24 = 0x68
+  packed.push_back(0x68);
+  // group 2 - addend 3: -24 = 0x68
+  packed.push_back(0x68);
+
+  std::vector<typename ELF::Rela> relocations;
+
+  RelocationPacker<ELF> packer;
+
+  relocations.clear();
+  packer.UnpackRelocations(packed, &relocations);
+
+  EXPECT_EQ(6U, relocations.size());
+  size_t ndx = 0;
+  // Initial relocation.
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0000, 0x01, 10024, relocations[ndx++]));
+  // Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0004, 0x01, 10012, relocations[ndx++]));
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0008, 0x01, 10024, relocations[ndx++]));
+  // Three more relocations, 8 byte offset deltas, -24 byte addend deltas.
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0010, 0x01, 10000, relocations[ndx++]));
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0018, 0x01, 9976, relocations[ndx++]));
+  EXPECT_TRUE(CheckRelocation<ELF>(0xd1ce0020, 0x01, 9952, relocations[ndx++]));
+
+  EXPECT_EQ(ndx, relocations.size());
+}
+
+TEST(Packer, UnpackWithAddend) {
+  DoUnpackWithAddend<ELF32_traits>();
+  DoUnpackWithAddend<ELF64_traits>();
+}
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/run_all_unittests.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/run_all_unittests.cc
new file mode 100644
index 0000000..4122be18
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/run_all_unittests.cc
@@ -0,0 +1,10 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.cc
new file mode 100644
index 0000000..12c21e3
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.cc
@@ -0,0 +1,131 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sleb128.h"
+
+#include <limits.h>
+#include <stdint.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace {
+
+template <typename T>
+class uint_traits {};
+
+template <>
+class uint_traits<uint64_t> {
+ public:
+  typedef int64_t int_t;
+};
+
+template <>
+class uint_traits<uint32_t> {
+ public:
+  typedef int32_t int_t;
+};
+
+}
+
+namespace relocation_packer {
+
+// Empty constructor and destructor to silence chromium-style.
+template <typename uint_t>
+Sleb128Encoder<uint_t>::Sleb128Encoder() { }
+
+template <typename uint_t>
+Sleb128Encoder<uint_t>::~Sleb128Encoder() { }
+
+// Add a single value to the encoding.  Values are encoded with variable
+// length.  The least significant 7 bits of each byte hold 7 bits of data,
+// and the most significant bit is set on each byte except the last.  The
+// value is sign extended up to a multiple of 7 bits (ensuring that the
+// most significant bit is zero for a positive number and one for a
+// negative number).
+template <typename uint_t>
+void Sleb128Encoder<uint_t>::Enqueue(uint_t value) {
+  typedef typename uint_traits<uint_t>::int_t int_t;
+  static const size_t size = CHAR_BIT * sizeof(value);
+
+  bool more = true;
+  const bool negative = static_cast<int_t>(value) < 0;
+
+  while (more) {
+    uint8_t byte = value & 127;
+    value >>= 7;
+
+    // Sign extend if encoding a -ve value.
+    if (negative)
+      value |= -(static_cast<uint_t>(1) << (size - 7));
+
+    // The sign bit of byte is second high order bit.
+    const bool sign_bit = byte & 64;
+    if ((value == 0 && !sign_bit) || (value == static_cast<uint_t>(-1) && sign_bit))
+      more = false;
+    else
+      byte |= 128;
+    encoding_.push_back(byte);
+  }
+}
+
+// Add a vector of values to the encoding.
+template <typename uint_t>
+void Sleb128Encoder<uint_t>::EnqueueAll(const std::vector<uint_t>& values) {
+  for (size_t i = 0; i < values.size(); ++i) {
+    Enqueue(values[i]);
+  }
+}
+
+// Create a new decoder for the given encoded stream.
+template <typename uint_t>
+Sleb128Decoder<uint_t>::Sleb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with) {
+  encoding_ = encoding;
+  cursor_ = start_with;
+}
+
+// Empty destructor to silence chromium-style.
+template <typename uint_t>
+Sleb128Decoder<uint_t>::~Sleb128Decoder() { }
+
+// Decode and retrieve a single value from the encoding.  Consume bytes
+// until one without its most significant bit is found, and re-form the
+// value from the 7 bit fields of the bytes consumed.
+template <typename uint_t>
+uint_t Sleb128Decoder<uint_t>::Dequeue() {
+  uint_t value = 0;
+  static const size_t size = CHAR_BIT * sizeof(value);
+
+  size_t shift = 0;
+  uint8_t byte;
+
+  // Loop until we reach a byte with its high order bit clear.
+  do {
+    byte = encoding_[cursor_++];
+    value |= (static_cast<uint_t>(byte & 127) << shift);
+    shift += 7;
+  } while (byte & 128);
+
+  // The sign bit is second high order bit of the final byte decoded.
+  // Sign extend if value is -ve and we did not shift all of it.
+  if (shift < size && (byte & 64))
+    value |= -(static_cast<uint_t>(1) << shift);
+
+  return static_cast<uint_t>(value);
+}
+
+// Decode and retrieve all remaining values from the encoding.
+template <typename uint_t>
+void Sleb128Decoder<uint_t>::DequeueAll(std::vector<uint_t>* values) {
+  while (cursor_ < encoding_.size()) {
+    values->push_back(Dequeue());
+  }
+}
+
+template class Sleb128Encoder<uint32_t>;
+template class Sleb128Encoder<uint64_t>;
+template class Sleb128Decoder<uint32_t>;
+template class Sleb128Decoder<uint64_t>;
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.h b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.h
new file mode 100644
index 0000000..fa0a2461
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128.h
@@ -0,0 +1,77 @@
+// 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.
+
+// SLEB128 encoder and decoder for packed relative relocations.
+//
+// Delta encoded relative relocations consist of a large number
+// of pairs signed integer values, many with small values.  Encoding these
+// as signed LEB128 saves space.
+//
+// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_
+#define TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_
+
+#include <stdint.h>
+#include <unistd.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// Encode packed words as a signed LEB128 byte stream.
+template<typename int_t>
+class Sleb128Encoder {
+ public:
+  // Explicit (but empty) constructor and destructor, for chromium-style.
+  Sleb128Encoder();
+  ~Sleb128Encoder();
+
+  // Add a value to the encoding stream.
+  // |value| is the signed int to add.
+  void Enqueue(int_t value);
+
+  // Add a vector of values to the encoding stream.
+  // |values| is the vector of signed ints to add.
+  void EnqueueAll(const std::vector<int_t>& values);
+
+  // Retrieve the encoded representation of the values.
+  // |encoding| is the returned vector of encoded data.
+  void GetEncoding(std::vector<uint8_t>* encoding) { *encoding = encoding_; }
+
+ private:
+  // Growable vector holding the encoded LEB128 stream.
+  std::vector<uint8_t> encoding_;
+};
+
+// Decode a LEB128 byte stream to produce packed words.
+template <typename int_t>
+class Sleb128Decoder {
+ public:
+  // Create a new decoder for the given encoded stream.
+  // |encoding| is the vector of encoded data.
+  explicit Sleb128Decoder(const std::vector<uint8_t>& encoding, size_t start_with);
+
+  // Explicit (but empty) destructor, for chromium-style.
+  ~Sleb128Decoder();
+
+  // Retrieve the next value from the encoded stream.
+  int_t Dequeue();
+
+  // Retrieve all remaining values from the encoded stream.
+  // |values| is the vector of decoded data.
+  void DequeueAll(std::vector<int_t>* values);
+
+ private:
+  // Encoded LEB128 stream.
+  std::vector<uint8_t> encoding_;
+
+  // Cursor indicating the current stream retrieval point.
+  size_t cursor_;
+};
+
+}  // namespace relocation_packer
+
+#endif  // TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128_unittest.cc b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128_unittest.cc
new file mode 100644
index 0000000..49a553c2
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/src/sleb128_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sleb128.h"
+
+#include <vector>
+#include "elf_traits.h"
+#include "gtest/gtest.h"
+
+namespace relocation_packer {
+
+TEST(Sleb128, Encoder64) {
+  std::vector<uint64_t> values;
+  values.push_back(624485U);
+  values.push_back(0U);
+  values.push_back(1U);
+  values.push_back(63U);
+  values.push_back(64U);
+  values.push_back(static_cast<uint64_t>(-1));
+  values.push_back(static_cast<uint64_t>(-624485));
+
+  Sleb128Encoder<uint64_t> encoder;
+  encoder.EnqueueAll(values);
+
+  encoder.Enqueue(2147483647U);
+  encoder.Enqueue(static_cast<uint64_t>(-2147483648));
+  encoder.Enqueue(9223372036854775807ULL);
+  encoder.Enqueue(static_cast<uint64_t>(-9223372036854775807LL - 1));
+
+  std::vector<uint8_t> encoding;
+  encoder.GetEncoding(&encoding);
+
+  EXPECT_EQ(42u, encoding.size());
+  // 624485
+  EXPECT_EQ(0xe5, encoding[0]);
+  EXPECT_EQ(0x8e, encoding[1]);
+  EXPECT_EQ(0x26, encoding[2]);
+  // 0
+  EXPECT_EQ(0x00, encoding[3]);
+  // 1
+  EXPECT_EQ(0x01, encoding[4]);
+  // 63
+  EXPECT_EQ(0x3f, encoding[5]);
+  // 64
+  EXPECT_EQ(0xc0, encoding[6]);
+  EXPECT_EQ(0x00, encoding[7]);
+  // -1
+  EXPECT_EQ(0x7f, encoding[8]);
+  // -624485
+  EXPECT_EQ(0x9b, encoding[9]);
+  EXPECT_EQ(0xf1, encoding[10]);
+  EXPECT_EQ(0x59, encoding[11]);
+  // 2147483647
+  EXPECT_EQ(0xff, encoding[12]);
+  EXPECT_EQ(0xff, encoding[13]);
+  EXPECT_EQ(0xff, encoding[14]);
+  EXPECT_EQ(0xff, encoding[15]);
+  EXPECT_EQ(0x07, encoding[16]);
+  // -2147483648
+  EXPECT_EQ(0x80, encoding[17]);
+  EXPECT_EQ(0x80, encoding[18]);
+  EXPECT_EQ(0x80, encoding[19]);
+  EXPECT_EQ(0x80, encoding[20]);
+  EXPECT_EQ(0x78, encoding[21]);
+  // 9223372036854775807
+  EXPECT_EQ(0xff, encoding[22]);
+  EXPECT_EQ(0xff, encoding[23]);
+  EXPECT_EQ(0xff, encoding[24]);
+  EXPECT_EQ(0xff, encoding[25]);
+  EXPECT_EQ(0xff, encoding[26]);
+  EXPECT_EQ(0xff, encoding[27]);
+  EXPECT_EQ(0xff, encoding[28]);
+  EXPECT_EQ(0xff, encoding[29]);
+  EXPECT_EQ(0xff, encoding[30]);
+  EXPECT_EQ(0x00, encoding[31]);
+  // -9223372036854775808
+  EXPECT_EQ(0x80, encoding[32]);
+  EXPECT_EQ(0x80, encoding[33]);
+  EXPECT_EQ(0x80, encoding[34]);
+  EXPECT_EQ(0x80, encoding[35]);
+  EXPECT_EQ(0x80, encoding[36]);
+  EXPECT_EQ(0x80, encoding[37]);
+  EXPECT_EQ(0x80, encoding[38]);
+  EXPECT_EQ(0x80, encoding[39]);
+  EXPECT_EQ(0x80, encoding[40]);
+  EXPECT_EQ(0x7f, encoding[41]);
+}
+
+TEST(Sleb128, Decoder) {
+  std::vector<uint8_t> encoding;
+  // 624485
+  encoding.push_back(0xe5);
+  encoding.push_back(0x8e);
+  encoding.push_back(0x26);
+  // 0
+  encoding.push_back(0x00);
+  // 1
+  encoding.push_back(0x01);
+  // 63
+  encoding.push_back(0x3f);
+  // 64
+  encoding.push_back(0xc0);
+  encoding.push_back(0x00);
+  // -1
+  encoding.push_back(0x7f);
+  // -624485
+  encoding.push_back(0x9b);
+  encoding.push_back(0xf1);
+  encoding.push_back(0x59);
+  // 2147483647
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0x07);
+  // -2147483648
+  encoding.push_back(0x80);
+  encoding.push_back(0x80);
+  encoding.push_back(0x80);
+  encoding.push_back(0x80);
+  encoding.push_back(0x78);
+  // 9223372036854775807
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0xff);
+  encoding.push_back(0x00);
+  // -9223372036854775808
+  encoding.push_back(0x80);
+  encoding.push_back(0x80);
+  encoding.push_back(0x80);
+  encoding.push_back(0x80);
+  encoding.push_back(0x80);
+  encoding.push_back(0x80);
+  encoding.push_back(0x80);
+  encoding.push_back(0x80);
+  encoding.push_back(0x80);
+  encoding.push_back(0x7f);
+
+  Sleb128Decoder<uint64_t> decoder(encoding, 0);
+
+  EXPECT_EQ(624485U, decoder.Dequeue());
+
+  std::vector<uint64_t> dequeued;
+  decoder.DequeueAll(&dequeued);
+
+  EXPECT_EQ(10U, dequeued.size());
+  EXPECT_EQ(0U, dequeued[0]);
+  EXPECT_EQ(1U, dequeued[1]);
+  EXPECT_EQ(63U, dequeued[2]);
+  EXPECT_EQ(64U, dequeued[3]);
+  EXPECT_EQ(static_cast<uint64_t>(-1), dequeued[4]);
+  EXPECT_EQ(static_cast<uint64_t>(-624485), dequeued[5]);
+  EXPECT_EQ(2147483647U, dequeued[6]);
+  EXPECT_EQ(static_cast<uint64_t>(-2147483648), dequeued[7]);
+  EXPECT_EQ(9223372036854775807ULL, dequeued[8]);
+  EXPECT_EQ(static_cast<uint64_t>(-9223372036854775807LL - 1), dequeued[9]);
+}
+
+}  // namespace relocation_packer
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc
new file mode 100644
index 0000000..5e1fa74
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc
@@ -0,0 +1,1014 @@
+// 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.
+
+// Test data for packing/unpacking.  When compiled, creates a run of
+// relative relocations.
+//
+// See generate_elf_file_unittest_relocs.sh for instructions on how to build
+// unit test data from this source file.
+
+const int i = 0;
+
+// Generator:
+// python -c 'for i in xrange(0,1000):print"const void* pointer_%d = &i;"%i'
+const void* pointer_0 = &i;
+const void* pointer_1 = &i;
+const void* pointer_2 = &i;
+const void* pointer_3 = &i;
+const void* pointer_4 = &i;
+const void* pointer_5 = &i;
+const void* pointer_6 = &i;
+const void* pointer_7 = &i;
+const void* pointer_8 = &i;
+const void* pointer_9 = &i;
+const void* pointer_10 = &i;
+const void* pointer_11 = &i;
+const void* pointer_12 = &i;
+const void* pointer_13 = &i;
+const void* pointer_14 = &i;
+const void* pointer_15 = &i;
+const void* pointer_16 = &i;
+const void* pointer_17 = &i;
+const void* pointer_18 = &i;
+const void* pointer_19 = &i;
+const void* pointer_20 = &i;
+const void* pointer_21 = &i;
+const void* pointer_22 = &i;
+const void* pointer_23 = &i;
+const void* pointer_24 = &i;
+const void* pointer_25 = &i;
+const void* pointer_26 = &i;
+const void* pointer_27 = &i;
+const void* pointer_28 = &i;
+const void* pointer_29 = &i;
+const void* pointer_30 = &i;
+const void* pointer_31 = &i;
+const void* pointer_32 = &i;
+const void* pointer_33 = &i;
+const void* pointer_34 = &i;
+const void* pointer_35 = &i;
+const void* pointer_36 = &i;
+const void* pointer_37 = &i;
+const void* pointer_38 = &i;
+const void* pointer_39 = &i;
+const void* pointer_40 = &i;
+const void* pointer_41 = &i;
+const void* pointer_42 = &i;
+const void* pointer_43 = &i;
+const void* pointer_44 = &i;
+const void* pointer_45 = &i;
+const void* pointer_46 = &i;
+const void* pointer_47 = &i;
+const void* pointer_48 = &i;
+const void* pointer_49 = &i;
+const void* pointer_50 = &i;
+const void* pointer_51 = &i;
+const void* pointer_52 = &i;
+const void* pointer_53 = &i;
+const void* pointer_54 = &i;
+const void* pointer_55 = &i;
+const void* pointer_56 = &i;
+const void* pointer_57 = &i;
+const void* pointer_58 = &i;
+const void* pointer_59 = &i;
+const void* pointer_60 = &i;
+const void* pointer_61 = &i;
+const void* pointer_62 = &i;
+const void* pointer_63 = &i;
+const void* pointer_64 = &i;
+const void* pointer_65 = &i;
+const void* pointer_66 = &i;
+const void* pointer_67 = &i;
+const void* pointer_68 = &i;
+const void* pointer_69 = &i;
+const void* pointer_70 = &i;
+const void* pointer_71 = &i;
+const void* pointer_72 = &i;
+const void* pointer_73 = &i;
+const void* pointer_74 = &i;
+const void* pointer_75 = &i;
+const void* pointer_76 = &i;
+const void* pointer_77 = &i;
+const void* pointer_78 = &i;
+const void* pointer_79 = &i;
+const void* pointer_80 = &i;
+const void* pointer_81 = &i;
+const void* pointer_82 = &i;
+const void* pointer_83 = &i;
+const void* pointer_84 = &i;
+const void* pointer_85 = &i;
+const void* pointer_86 = &i;
+const void* pointer_87 = &i;
+const void* pointer_88 = &i;
+const void* pointer_89 = &i;
+const void* pointer_90 = &i;
+const void* pointer_91 = &i;
+const void* pointer_92 = &i;
+const void* pointer_93 = &i;
+const void* pointer_94 = &i;
+const void* pointer_95 = &i;
+const void* pointer_96 = &i;
+const void* pointer_97 = &i;
+const void* pointer_98 = &i;
+const void* pointer_99 = &i;
+const void* pointer_100 = &i;
+const void* pointer_101 = &i;
+const void* pointer_102 = &i;
+const void* pointer_103 = &i;
+const void* pointer_104 = &i;
+const void* pointer_105 = &i;
+const void* pointer_106 = &i;
+const void* pointer_107 = &i;
+const void* pointer_108 = &i;
+const void* pointer_109 = &i;
+const void* pointer_110 = &i;
+const void* pointer_111 = &i;
+const void* pointer_112 = &i;
+const void* pointer_113 = &i;
+const void* pointer_114 = &i;
+const void* pointer_115 = &i;
+const void* pointer_116 = &i;
+const void* pointer_117 = &i;
+const void* pointer_118 = &i;
+const void* pointer_119 = &i;
+const void* pointer_120 = &i;
+const void* pointer_121 = &i;
+const void* pointer_122 = &i;
+const void* pointer_123 = &i;
+const void* pointer_124 = &i;
+const void* pointer_125 = &i;
+const void* pointer_126 = &i;
+const void* pointer_127 = &i;
+const void* pointer_128 = &i;
+const void* pointer_129 = &i;
+const void* pointer_130 = &i;
+const void* pointer_131 = &i;
+const void* pointer_132 = &i;
+const void* pointer_133 = &i;
+const void* pointer_134 = &i;
+const void* pointer_135 = &i;
+const void* pointer_136 = &i;
+const void* pointer_137 = &i;
+const void* pointer_138 = &i;
+const void* pointer_139 = &i;
+const void* pointer_140 = &i;
+const void* pointer_141 = &i;
+const void* pointer_142 = &i;
+const void* pointer_143 = &i;
+const void* pointer_144 = &i;
+const void* pointer_145 = &i;
+const void* pointer_146 = &i;
+const void* pointer_147 = &i;
+const void* pointer_148 = &i;
+const void* pointer_149 = &i;
+const void* pointer_150 = &i;
+const void* pointer_151 = &i;
+const void* pointer_152 = &i;
+const void* pointer_153 = &i;
+const void* pointer_154 = &i;
+const void* pointer_155 = &i;
+const void* pointer_156 = &i;
+const void* pointer_157 = &i;
+const void* pointer_158 = &i;
+const void* pointer_159 = &i;
+const void* pointer_160 = &i;
+const void* pointer_161 = &i;
+const void* pointer_162 = &i;
+const void* pointer_163 = &i;
+const void* pointer_164 = &i;
+const void* pointer_165 = &i;
+const void* pointer_166 = &i;
+const void* pointer_167 = &i;
+const void* pointer_168 = &i;
+const void* pointer_169 = &i;
+const void* pointer_170 = &i;
+const void* pointer_171 = &i;
+const void* pointer_172 = &i;
+const void* pointer_173 = &i;
+const void* pointer_174 = &i;
+const void* pointer_175 = &i;
+const void* pointer_176 = &i;
+const void* pointer_177 = &i;
+const void* pointer_178 = &i;
+const void* pointer_179 = &i;
+const void* pointer_180 = &i;
+const void* pointer_181 = &i;
+const void* pointer_182 = &i;
+const void* pointer_183 = &i;
+const void* pointer_184 = &i;
+const void* pointer_185 = &i;
+const void* pointer_186 = &i;
+const void* pointer_187 = &i;
+const void* pointer_188 = &i;
+const void* pointer_189 = &i;
+const void* pointer_190 = &i;
+const void* pointer_191 = &i;
+const void* pointer_192 = &i;
+const void* pointer_193 = &i;
+const void* pointer_194 = &i;
+const void* pointer_195 = &i;
+const void* pointer_196 = &i;
+const void* pointer_197 = &i;
+const void* pointer_198 = &i;
+const void* pointer_199 = &i;
+const void* pointer_200 = &i;
+const void* pointer_201 = &i;
+const void* pointer_202 = &i;
+const void* pointer_203 = &i;
+const void* pointer_204 = &i;
+const void* pointer_205 = &i;
+const void* pointer_206 = &i;
+const void* pointer_207 = &i;
+const void* pointer_208 = &i;
+const void* pointer_209 = &i;
+const void* pointer_210 = &i;
+const void* pointer_211 = &i;
+const void* pointer_212 = &i;
+const void* pointer_213 = &i;
+const void* pointer_214 = &i;
+const void* pointer_215 = &i;
+const void* pointer_216 = &i;
+const void* pointer_217 = &i;
+const void* pointer_218 = &i;
+const void* pointer_219 = &i;
+const void* pointer_220 = &i;
+const void* pointer_221 = &i;
+const void* pointer_222 = &i;
+const void* pointer_223 = &i;
+const void* pointer_224 = &i;
+const void* pointer_225 = &i;
+const void* pointer_226 = &i;
+const void* pointer_227 = &i;
+const void* pointer_228 = &i;
+const void* pointer_229 = &i;
+const void* pointer_230 = &i;
+const void* pointer_231 = &i;
+const void* pointer_232 = &i;
+const void* pointer_233 = &i;
+const void* pointer_234 = &i;
+const void* pointer_235 = &i;
+const void* pointer_236 = &i;
+const void* pointer_237 = &i;
+const void* pointer_238 = &i;
+const void* pointer_239 = &i;
+const void* pointer_240 = &i;
+const void* pointer_241 = &i;
+const void* pointer_242 = &i;
+const void* pointer_243 = &i;
+const void* pointer_244 = &i;
+const void* pointer_245 = &i;
+const void* pointer_246 = &i;
+const void* pointer_247 = &i;
+const void* pointer_248 = &i;
+const void* pointer_249 = &i;
+const void* pointer_250 = &i;
+const void* pointer_251 = &i;
+const void* pointer_252 = &i;
+const void* pointer_253 = &i;
+const void* pointer_254 = &i;
+const void* pointer_255 = &i;
+const void* pointer_256 = &i;
+const void* pointer_257 = &i;
+const void* pointer_258 = &i;
+const void* pointer_259 = &i;
+const void* pointer_260 = &i;
+const void* pointer_261 = &i;
+const void* pointer_262 = &i;
+const void* pointer_263 = &i;
+const void* pointer_264 = &i;
+const void* pointer_265 = &i;
+const void* pointer_266 = &i;
+const void* pointer_267 = &i;
+const void* pointer_268 = &i;
+const void* pointer_269 = &i;
+const void* pointer_270 = &i;
+const void* pointer_271 = &i;
+const void* pointer_272 = &i;
+const void* pointer_273 = &i;
+const void* pointer_274 = &i;
+const void* pointer_275 = &i;
+const void* pointer_276 = &i;
+const void* pointer_277 = &i;
+const void* pointer_278 = &i;
+const void* pointer_279 = &i;
+const void* pointer_280 = &i;
+const void* pointer_281 = &i;
+const void* pointer_282 = &i;
+const void* pointer_283 = &i;
+const void* pointer_284 = &i;
+const void* pointer_285 = &i;
+const void* pointer_286 = &i;
+const void* pointer_287 = &i;
+const void* pointer_288 = &i;
+const void* pointer_289 = &i;
+const void* pointer_290 = &i;
+const void* pointer_291 = &i;
+const void* pointer_292 = &i;
+const void* pointer_293 = &i;
+const void* pointer_294 = &i;
+const void* pointer_295 = &i;
+const void* pointer_296 = &i;
+const void* pointer_297 = &i;
+const void* pointer_298 = &i;
+const void* pointer_299 = &i;
+const void* pointer_300 = &i;
+const void* pointer_301 = &i;
+const void* pointer_302 = &i;
+const void* pointer_303 = &i;
+const void* pointer_304 = &i;
+const void* pointer_305 = &i;
+const void* pointer_306 = &i;
+const void* pointer_307 = &i;
+const void* pointer_308 = &i;
+const void* pointer_309 = &i;
+const void* pointer_310 = &i;
+const void* pointer_311 = &i;
+const void* pointer_312 = &i;
+const void* pointer_313 = &i;
+const void* pointer_314 = &i;
+const void* pointer_315 = &i;
+const void* pointer_316 = &i;
+const void* pointer_317 = &i;
+const void* pointer_318 = &i;
+const void* pointer_319 = &i;
+const void* pointer_320 = &i;
+const void* pointer_321 = &i;
+const void* pointer_322 = &i;
+const void* pointer_323 = &i;
+const void* pointer_324 = &i;
+const void* pointer_325 = &i;
+const void* pointer_326 = &i;
+const void* pointer_327 = &i;
+const void* pointer_328 = &i;
+const void* pointer_329 = &i;
+const void* pointer_330 = &i;
+const void* pointer_331 = &i;
+const void* pointer_332 = &i;
+const void* pointer_333 = &i;
+const void* pointer_334 = &i;
+const void* pointer_335 = &i;
+const void* pointer_336 = &i;
+const void* pointer_337 = &i;
+const void* pointer_338 = &i;
+const void* pointer_339 = &i;
+const void* pointer_340 = &i;
+const void* pointer_341 = &i;
+const void* pointer_342 = &i;
+const void* pointer_343 = &i;
+const void* pointer_344 = &i;
+const void* pointer_345 = &i;
+const void* pointer_346 = &i;
+const void* pointer_347 = &i;
+const void* pointer_348 = &i;
+const void* pointer_349 = &i;
+const void* pointer_350 = &i;
+const void* pointer_351 = &i;
+const void* pointer_352 = &i;
+const void* pointer_353 = &i;
+const void* pointer_354 = &i;
+const void* pointer_355 = &i;
+const void* pointer_356 = &i;
+const void* pointer_357 = &i;
+const void* pointer_358 = &i;
+const void* pointer_359 = &i;
+const void* pointer_360 = &i;
+const void* pointer_361 = &i;
+const void* pointer_362 = &i;
+const void* pointer_363 = &i;
+const void* pointer_364 = &i;
+const void* pointer_365 = &i;
+const void* pointer_366 = &i;
+const void* pointer_367 = &i;
+const void* pointer_368 = &i;
+const void* pointer_369 = &i;
+const void* pointer_370 = &i;
+const void* pointer_371 = &i;
+const void* pointer_372 = &i;
+const void* pointer_373 = &i;
+const void* pointer_374 = &i;
+const void* pointer_375 = &i;
+const void* pointer_376 = &i;
+const void* pointer_377 = &i;
+const void* pointer_378 = &i;
+const void* pointer_379 = &i;
+const void* pointer_380 = &i;
+const void* pointer_381 = &i;
+const void* pointer_382 = &i;
+const void* pointer_383 = &i;
+const void* pointer_384 = &i;
+const void* pointer_385 = &i;
+const void* pointer_386 = &i;
+const void* pointer_387 = &i;
+const void* pointer_388 = &i;
+const void* pointer_389 = &i;
+const void* pointer_390 = &i;
+const void* pointer_391 = &i;
+const void* pointer_392 = &i;
+const void* pointer_393 = &i;
+const void* pointer_394 = &i;
+const void* pointer_395 = &i;
+const void* pointer_396 = &i;
+const void* pointer_397 = &i;
+const void* pointer_398 = &i;
+const void* pointer_399 = &i;
+const void* pointer_400 = &i;
+const void* pointer_401 = &i;
+const void* pointer_402 = &i;
+const void* pointer_403 = &i;
+const void* pointer_404 = &i;
+const void* pointer_405 = &i;
+const void* pointer_406 = &i;
+const void* pointer_407 = &i;
+const void* pointer_408 = &i;
+const void* pointer_409 = &i;
+const void* pointer_410 = &i;
+const void* pointer_411 = &i;
+const void* pointer_412 = &i;
+const void* pointer_413 = &i;
+const void* pointer_414 = &i;
+const void* pointer_415 = &i;
+const void* pointer_416 = &i;
+const void* pointer_417 = &i;
+const void* pointer_418 = &i;
+const void* pointer_419 = &i;
+const void* pointer_420 = &i;
+const void* pointer_421 = &i;
+const void* pointer_422 = &i;
+const void* pointer_423 = &i;
+const void* pointer_424 = &i;
+const void* pointer_425 = &i;
+const void* pointer_426 = &i;
+const void* pointer_427 = &i;
+const void* pointer_428 = &i;
+const void* pointer_429 = &i;
+const void* pointer_430 = &i;
+const void* pointer_431 = &i;
+const void* pointer_432 = &i;
+const void* pointer_433 = &i;
+const void* pointer_434 = &i;
+const void* pointer_435 = &i;
+const void* pointer_436 = &i;
+const void* pointer_437 = &i;
+const void* pointer_438 = &i;
+const void* pointer_439 = &i;
+const void* pointer_440 = &i;
+const void* pointer_441 = &i;
+const void* pointer_442 = &i;
+const void* pointer_443 = &i;
+const void* pointer_444 = &i;
+const void* pointer_445 = &i;
+const void* pointer_446 = &i;
+const void* pointer_447 = &i;
+const void* pointer_448 = &i;
+const void* pointer_449 = &i;
+const void* pointer_450 = &i;
+const void* pointer_451 = &i;
+const void* pointer_452 = &i;
+const void* pointer_453 = &i;
+const void* pointer_454 = &i;
+const void* pointer_455 = &i;
+const void* pointer_456 = &i;
+const void* pointer_457 = &i;
+const void* pointer_458 = &i;
+const void* pointer_459 = &i;
+const void* pointer_460 = &i;
+const void* pointer_461 = &i;
+const void* pointer_462 = &i;
+const void* pointer_463 = &i;
+const void* pointer_464 = &i;
+const void* pointer_465 = &i;
+const void* pointer_466 = &i;
+const void* pointer_467 = &i;
+const void* pointer_468 = &i;
+const void* pointer_469 = &i;
+const void* pointer_470 = &i;
+const void* pointer_471 = &i;
+const void* pointer_472 = &i;
+const void* pointer_473 = &i;
+const void* pointer_474 = &i;
+const void* pointer_475 = &i;
+const void* pointer_476 = &i;
+const void* pointer_477 = &i;
+const void* pointer_478 = &i;
+const void* pointer_479 = &i;
+const void* pointer_480 = &i;
+const void* pointer_481 = &i;
+const void* pointer_482 = &i;
+const void* pointer_483 = &i;
+const void* pointer_484 = &i;
+const void* pointer_485 = &i;
+const void* pointer_486 = &i;
+const void* pointer_487 = &i;
+const void* pointer_488 = &i;
+const void* pointer_489 = &i;
+const void* pointer_490 = &i;
+const void* pointer_491 = &i;
+const void* pointer_492 = &i;
+const void* pointer_493 = &i;
+const void* pointer_494 = &i;
+const void* pointer_495 = &i;
+const void* pointer_496 = &i;
+const void* pointer_497 = &i;
+const void* pointer_498 = &i;
+const void* pointer_499 = &i;
+const void* pointer_500 = &i;
+const void* pointer_501 = &i;
+const void* pointer_502 = &i;
+const void* pointer_503 = &i;
+const void* pointer_504 = &i;
+const void* pointer_505 = &i;
+const void* pointer_506 = &i;
+const void* pointer_507 = &i;
+const void* pointer_508 = &i;
+const void* pointer_509 = &i;
+const void* pointer_510 = &i;
+const void* pointer_511 = &i;
+const void* pointer_512 = &i;
+const void* pointer_513 = &i;
+const void* pointer_514 = &i;
+const void* pointer_515 = &i;
+const void* pointer_516 = &i;
+const void* pointer_517 = &i;
+const void* pointer_518 = &i;
+const void* pointer_519 = &i;
+const void* pointer_520 = &i;
+const void* pointer_521 = &i;
+const void* pointer_522 = &i;
+const void* pointer_523 = &i;
+const void* pointer_524 = &i;
+const void* pointer_525 = &i;
+const void* pointer_526 = &i;
+const void* pointer_527 = &i;
+const void* pointer_528 = &i;
+const void* pointer_529 = &i;
+const void* pointer_530 = &i;
+const void* pointer_531 = &i;
+const void* pointer_532 = &i;
+const void* pointer_533 = &i;
+const void* pointer_534 = &i;
+const void* pointer_535 = &i;
+const void* pointer_536 = &i;
+const void* pointer_537 = &i;
+const void* pointer_538 = &i;
+const void* pointer_539 = &i;
+const void* pointer_540 = &i;
+const void* pointer_541 = &i;
+const void* pointer_542 = &i;
+const void* pointer_543 = &i;
+const void* pointer_544 = &i;
+const void* pointer_545 = &i;
+const void* pointer_546 = &i;
+const void* pointer_547 = &i;
+const void* pointer_548 = &i;
+const void* pointer_549 = &i;
+const void* pointer_550 = &i;
+const void* pointer_551 = &i;
+const void* pointer_552 = &i;
+const void* pointer_553 = &i;
+const void* pointer_554 = &i;
+const void* pointer_555 = &i;
+const void* pointer_556 = &i;
+const void* pointer_557 = &i;
+const void* pointer_558 = &i;
+const void* pointer_559 = &i;
+const void* pointer_560 = &i;
+const void* pointer_561 = &i;
+const void* pointer_562 = &i;
+const void* pointer_563 = &i;
+const void* pointer_564 = &i;
+const void* pointer_565 = &i;
+const void* pointer_566 = &i;
+const void* pointer_567 = &i;
+const void* pointer_568 = &i;
+const void* pointer_569 = &i;
+const void* pointer_570 = &i;
+const void* pointer_571 = &i;
+const void* pointer_572 = &i;
+const void* pointer_573 = &i;
+const void* pointer_574 = &i;
+const void* pointer_575 = &i;
+const void* pointer_576 = &i;
+const void* pointer_577 = &i;
+const void* pointer_578 = &i;
+const void* pointer_579 = &i;
+const void* pointer_580 = &i;
+const void* pointer_581 = &i;
+const void* pointer_582 = &i;
+const void* pointer_583 = &i;
+const void* pointer_584 = &i;
+const void* pointer_585 = &i;
+const void* pointer_586 = &i;
+const void* pointer_587 = &i;
+const void* pointer_588 = &i;
+const void* pointer_589 = &i;
+const void* pointer_590 = &i;
+const void* pointer_591 = &i;
+const void* pointer_592 = &i;
+const void* pointer_593 = &i;
+const void* pointer_594 = &i;
+const void* pointer_595 = &i;
+const void* pointer_596 = &i;
+const void* pointer_597 = &i;
+const void* pointer_598 = &i;
+const void* pointer_599 = &i;
+const void* pointer_600 = &i;
+const void* pointer_601 = &i;
+const void* pointer_602 = &i;
+const void* pointer_603 = &i;
+const void* pointer_604 = &i;
+const void* pointer_605 = &i;
+const void* pointer_606 = &i;
+const void* pointer_607 = &i;
+const void* pointer_608 = &i;
+const void* pointer_609 = &i;
+const void* pointer_610 = &i;
+const void* pointer_611 = &i;
+const void* pointer_612 = &i;
+const void* pointer_613 = &i;
+const void* pointer_614 = &i;
+const void* pointer_615 = &i;
+const void* pointer_616 = &i;
+const void* pointer_617 = &i;
+const void* pointer_618 = &i;
+const void* pointer_619 = &i;
+const void* pointer_620 = &i;
+const void* pointer_621 = &i;
+const void* pointer_622 = &i;
+const void* pointer_623 = &i;
+const void* pointer_624 = &i;
+const void* pointer_625 = &i;
+const void* pointer_626 = &i;
+const void* pointer_627 = &i;
+const void* pointer_628 = &i;
+const void* pointer_629 = &i;
+const void* pointer_630 = &i;
+const void* pointer_631 = &i;
+const void* pointer_632 = &i;
+const void* pointer_633 = &i;
+const void* pointer_634 = &i;
+const void* pointer_635 = &i;
+const void* pointer_636 = &i;
+const void* pointer_637 = &i;
+const void* pointer_638 = &i;
+const void* pointer_639 = &i;
+const void* pointer_640 = &i;
+const void* pointer_641 = &i;
+const void* pointer_642 = &i;
+const void* pointer_643 = &i;
+const void* pointer_644 = &i;
+const void* pointer_645 = &i;
+const void* pointer_646 = &i;
+const void* pointer_647 = &i;
+const void* pointer_648 = &i;
+const void* pointer_649 = &i;
+const void* pointer_650 = &i;
+const void* pointer_651 = &i;
+const void* pointer_652 = &i;
+const void* pointer_653 = &i;
+const void* pointer_654 = &i;
+const void* pointer_655 = &i;
+const void* pointer_656 = &i;
+const void* pointer_657 = &i;
+const void* pointer_658 = &i;
+const void* pointer_659 = &i;
+const void* pointer_660 = &i;
+const void* pointer_661 = &i;
+const void* pointer_662 = &i;
+const void* pointer_663 = &i;
+const void* pointer_664 = &i;
+const void* pointer_665 = &i;
+const void* pointer_666 = &i;
+const void* pointer_667 = &i;
+const void* pointer_668 = &i;
+const void* pointer_669 = &i;
+const void* pointer_670 = &i;
+const void* pointer_671 = &i;
+const void* pointer_672 = &i;
+const void* pointer_673 = &i;
+const void* pointer_674 = &i;
+const void* pointer_675 = &i;
+const void* pointer_676 = &i;
+const void* pointer_677 = &i;
+const void* pointer_678 = &i;
+const void* pointer_679 = &i;
+const void* pointer_680 = &i;
+const void* pointer_681 = &i;
+const void* pointer_682 = &i;
+const void* pointer_683 = &i;
+const void* pointer_684 = &i;
+const void* pointer_685 = &i;
+const void* pointer_686 = &i;
+const void* pointer_687 = &i;
+const void* pointer_688 = &i;
+const void* pointer_689 = &i;
+const void* pointer_690 = &i;
+const void* pointer_691 = &i;
+const void* pointer_692 = &i;
+const void* pointer_693 = &i;
+const void* pointer_694 = &i;
+const void* pointer_695 = &i;
+const void* pointer_696 = &i;
+const void* pointer_697 = &i;
+const void* pointer_698 = &i;
+const void* pointer_699 = &i;
+const void* pointer_700 = &i;
+const void* pointer_701 = &i;
+const void* pointer_702 = &i;
+const void* pointer_703 = &i;
+const void* pointer_704 = &i;
+const void* pointer_705 = &i;
+const void* pointer_706 = &i;
+const void* pointer_707 = &i;
+const void* pointer_708 = &i;
+const void* pointer_709 = &i;
+const void* pointer_710 = &i;
+const void* pointer_711 = &i;
+const void* pointer_712 = &i;
+const void* pointer_713 = &i;
+const void* pointer_714 = &i;
+const void* pointer_715 = &i;
+const void* pointer_716 = &i;
+const void* pointer_717 = &i;
+const void* pointer_718 = &i;
+const void* pointer_719 = &i;
+const void* pointer_720 = &i;
+const void* pointer_721 = &i;
+const void* pointer_722 = &i;
+const void* pointer_723 = &i;
+const void* pointer_724 = &i;
+const void* pointer_725 = &i;
+const void* pointer_726 = &i;
+const void* pointer_727 = &i;
+const void* pointer_728 = &i;
+const void* pointer_729 = &i;
+const void* pointer_730 = &i;
+const void* pointer_731 = &i;
+const void* pointer_732 = &i;
+const void* pointer_733 = &i;
+const void* pointer_734 = &i;
+const void* pointer_735 = &i;
+const void* pointer_736 = &i;
+const void* pointer_737 = &i;
+const void* pointer_738 = &i;
+const void* pointer_739 = &i;
+const void* pointer_740 = &i;
+const void* pointer_741 = &i;
+const void* pointer_742 = &i;
+const void* pointer_743 = &i;
+const void* pointer_744 = &i;
+const void* pointer_745 = &i;
+const void* pointer_746 = &i;
+const void* pointer_747 = &i;
+const void* pointer_748 = &i;
+const void* pointer_749 = &i;
+const void* pointer_750 = &i;
+const void* pointer_751 = &i;
+const void* pointer_752 = &i;
+const void* pointer_753 = &i;
+const void* pointer_754 = &i;
+const void* pointer_755 = &i;
+const void* pointer_756 = &i;
+const void* pointer_757 = &i;
+const void* pointer_758 = &i;
+const void* pointer_759 = &i;
+const void* pointer_760 = &i;
+const void* pointer_761 = &i;
+const void* pointer_762 = &i;
+const void* pointer_763 = &i;
+const void* pointer_764 = &i;
+const void* pointer_765 = &i;
+const void* pointer_766 = &i;
+const void* pointer_767 = &i;
+const void* pointer_768 = &i;
+const void* pointer_769 = &i;
+const void* pointer_770 = &i;
+const void* pointer_771 = &i;
+const void* pointer_772 = &i;
+const void* pointer_773 = &i;
+const void* pointer_774 = &i;
+const void* pointer_775 = &i;
+const void* pointer_776 = &i;
+const void* pointer_777 = &i;
+const void* pointer_778 = &i;
+const void* pointer_779 = &i;
+const void* pointer_780 = &i;
+const void* pointer_781 = &i;
+const void* pointer_782 = &i;
+const void* pointer_783 = &i;
+const void* pointer_784 = &i;
+const void* pointer_785 = &i;
+const void* pointer_786 = &i;
+const void* pointer_787 = &i;
+const void* pointer_788 = &i;
+const void* pointer_789 = &i;
+const void* pointer_790 = &i;
+const void* pointer_791 = &i;
+const void* pointer_792 = &i;
+const void* pointer_793 = &i;
+const void* pointer_794 = &i;
+const void* pointer_795 = &i;
+const void* pointer_796 = &i;
+const void* pointer_797 = &i;
+const void* pointer_798 = &i;
+const void* pointer_799 = &i;
+const void* pointer_800 = &i;
+const void* pointer_801 = &i;
+const void* pointer_802 = &i;
+const void* pointer_803 = &i;
+const void* pointer_804 = &i;
+const void* pointer_805 = &i;
+const void* pointer_806 = &i;
+const void* pointer_807 = &i;
+const void* pointer_808 = &i;
+const void* pointer_809 = &i;
+const void* pointer_810 = &i;
+const void* pointer_811 = &i;
+const void* pointer_812 = &i;
+const void* pointer_813 = &i;
+const void* pointer_814 = &i;
+const void* pointer_815 = &i;
+const void* pointer_816 = &i;
+const void* pointer_817 = &i;
+const void* pointer_818 = &i;
+const void* pointer_819 = &i;
+const void* pointer_820 = &i;
+const void* pointer_821 = &i;
+const void* pointer_822 = &i;
+const void* pointer_823 = &i;
+const void* pointer_824 = &i;
+const void* pointer_825 = &i;
+const void* pointer_826 = &i;
+const void* pointer_827 = &i;
+const void* pointer_828 = &i;
+const void* pointer_829 = &i;
+const void* pointer_830 = &i;
+const void* pointer_831 = &i;
+const void* pointer_832 = &i;
+const void* pointer_833 = &i;
+const void* pointer_834 = &i;
+const void* pointer_835 = &i;
+const void* pointer_836 = &i;
+const void* pointer_837 = &i;
+const void* pointer_838 = &i;
+const void* pointer_839 = &i;
+const void* pointer_840 = &i;
+const void* pointer_841 = &i;
+const void* pointer_842 = &i;
+const void* pointer_843 = &i;
+const void* pointer_844 = &i;
+const void* pointer_845 = &i;
+const void* pointer_846 = &i;
+const void* pointer_847 = &i;
+const void* pointer_848 = &i;
+const void* pointer_849 = &i;
+const void* pointer_850 = &i;
+const void* pointer_851 = &i;
+const void* pointer_852 = &i;
+const void* pointer_853 = &i;
+const void* pointer_854 = &i;
+const void* pointer_855 = &i;
+const void* pointer_856 = &i;
+const void* pointer_857 = &i;
+const void* pointer_858 = &i;
+const void* pointer_859 = &i;
+const void* pointer_860 = &i;
+const void* pointer_861 = &i;
+const void* pointer_862 = &i;
+const void* pointer_863 = &i;
+const void* pointer_864 = &i;
+const void* pointer_865 = &i;
+const void* pointer_866 = &i;
+const void* pointer_867 = &i;
+const void* pointer_868 = &i;
+const void* pointer_869 = &i;
+const void* pointer_870 = &i;
+const void* pointer_871 = &i;
+const void* pointer_872 = &i;
+const void* pointer_873 = &i;
+const void* pointer_874 = &i;
+const void* pointer_875 = &i;
+const void* pointer_876 = &i;
+const void* pointer_877 = &i;
+const void* pointer_878 = &i;
+const void* pointer_879 = &i;
+const void* pointer_880 = &i;
+const void* pointer_881 = &i;
+const void* pointer_882 = &i;
+const void* pointer_883 = &i;
+const void* pointer_884 = &i;
+const void* pointer_885 = &i;
+const void* pointer_886 = &i;
+const void* pointer_887 = &i;
+const void* pointer_888 = &i;
+const void* pointer_889 = &i;
+const void* pointer_890 = &i;
+const void* pointer_891 = &i;
+const void* pointer_892 = &i;
+const void* pointer_893 = &i;
+const void* pointer_894 = &i;
+const void* pointer_895 = &i;
+const void* pointer_896 = &i;
+const void* pointer_897 = &i;
+const void* pointer_898 = &i;
+const void* pointer_899 = &i;
+const void* pointer_900 = &i;
+const void* pointer_901 = &i;
+const void* pointer_902 = &i;
+const void* pointer_903 = &i;
+const void* pointer_904 = &i;
+const void* pointer_905 = &i;
+const void* pointer_906 = &i;
+const void* pointer_907 = &i;
+const void* pointer_908 = &i;
+const void* pointer_909 = &i;
+const void* pointer_910 = &i;
+const void* pointer_911 = &i;
+const void* pointer_912 = &i;
+const void* pointer_913 = &i;
+const void* pointer_914 = &i;
+const void* pointer_915 = &i;
+const void* pointer_916 = &i;
+const void* pointer_917 = &i;
+const void* pointer_918 = &i;
+const void* pointer_919 = &i;
+const void* pointer_920 = &i;
+const void* pointer_921 = &i;
+const void* pointer_922 = &i;
+const void* pointer_923 = &i;
+const void* pointer_924 = &i;
+const void* pointer_925 = &i;
+const void* pointer_926 = &i;
+const void* pointer_927 = &i;
+const void* pointer_928 = &i;
+const void* pointer_929 = &i;
+const void* pointer_930 = &i;
+const void* pointer_931 = &i;
+const void* pointer_932 = &i;
+const void* pointer_933 = &i;
+const void* pointer_934 = &i;
+const void* pointer_935 = &i;
+const void* pointer_936 = &i;
+const void* pointer_937 = &i;
+const void* pointer_938 = &i;
+const void* pointer_939 = &i;
+const void* pointer_940 = &i;
+const void* pointer_941 = &i;
+const void* pointer_942 = &i;
+const void* pointer_943 = &i;
+const void* pointer_944 = &i;
+const void* pointer_945 = &i;
+const void* pointer_946 = &i;
+const void* pointer_947 = &i;
+const void* pointer_948 = &i;
+const void* pointer_949 = &i;
+const void* pointer_950 = &i;
+const void* pointer_951 = &i;
+const void* pointer_952 = &i;
+const void* pointer_953 = &i;
+const void* pointer_954 = &i;
+const void* pointer_955 = &i;
+const void* pointer_956 = &i;
+const void* pointer_957 = &i;
+const void* pointer_958 = &i;
+const void* pointer_959 = &i;
+const void* pointer_960 = &i;
+const void* pointer_961 = &i;
+const void* pointer_962 = &i;
+const void* pointer_963 = &i;
+const void* pointer_964 = &i;
+const void* pointer_965 = &i;
+const void* pointer_966 = &i;
+const void* pointer_967 = &i;
+const void* pointer_968 = &i;
+const void* pointer_969 = &i;
+const void* pointer_970 = &i;
+const void* pointer_971 = &i;
+const void* pointer_972 = &i;
+const void* pointer_973 = &i;
+const void* pointer_974 = &i;
+const void* pointer_975 = &i;
+const void* pointer_976 = &i;
+const void* pointer_977 = &i;
+const void* pointer_978 = &i;
+const void* pointer_979 = &i;
+const void* pointer_980 = &i;
+const void* pointer_981 = &i;
+const void* pointer_982 = &i;
+const void* pointer_983 = &i;
+const void* pointer_984 = &i;
+const void* pointer_985 = &i;
+const void* pointer_986 = &i;
+const void* pointer_987 = &i;
+const void* pointer_988 = &i;
+const void* pointer_989 = &i;
+const void* pointer_990 = &i;
+const void* pointer_991 = &i;
+const void* pointer_992 = &i;
+const void* pointer_993 = &i;
+const void* pointer_994 = &i;
+const void* pointer_995 = &i;
+const void* pointer_996 = &i;
+const void* pointer_997 = &i;
+const void* pointer_998 = &i;
+const void* pointer_999 = &i;
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
new file mode 100755
index 0000000..6ce6d0cd
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
Binary files differ
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
new file mode 100755
index 0000000..d97ef82
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
Binary files differ
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
new file mode 100755
index 0000000..945b450e
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
Binary files differ
diff --git a/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
new file mode 100755
index 0000000..e44e4597
--- /dev/null
+++ b/third_party/android_platform/bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
Binary files differ
diff --git a/third_party/android_platform/config.gni b/third_party/android_platform/config.gni
new file mode 100644
index 0000000..88f6362
--- /dev/null
+++ b/third_party/android_platform/config.gni
@@ -0,0 +1,9 @@
+# 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.
+
+relocation_packer_target =
+    "//third_party/android_platform/relocation_packer($host_toolchain)"
+relocation_packer_dir =
+    get_label_info("$relocation_packer_target", "root_out_dir")
+relocation_packer_exe = "${relocation_packer_dir}/android_relocation_packer"
diff --git a/third_party/android_platform/relocation_packer.gyp b/third_party/android_platform/relocation_packer.gyp
new file mode 100644
index 0000000..69136f5
--- /dev/null
+++ b/third_party/android_platform/relocation_packer.gyp
@@ -0,0 +1,83 @@
+# 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.
+
+{
+  'variables': {
+    # These files lists are shared with the GN build.
+    'relocation_packer_sources': [
+      'bionic/tools/relocation_packer/src/debug.cc',
+      'bionic/tools/relocation_packer/src/delta_encoder.cc',
+      'bionic/tools/relocation_packer/src/elf_file.cc',
+      'bionic/tools/relocation_packer/src/leb128.cc',
+      'bionic/tools/relocation_packer/src/packer.cc',
+      'bionic/tools/relocation_packer/src/sleb128.cc',
+    ],
+    'relocation_packer_main_source': [
+      'bionic/tools/relocation_packer/src/main.cc',
+    ],
+    'relocation_packer_test_sources': [
+      'bionic/tools/relocation_packer/src/debug_unittest.cc',
+      'bionic/tools/relocation_packer/src/delta_encoder_unittest.cc',
+      'bionic/tools/relocation_packer/src/elf_file_unittest.cc',
+      'bionic/tools/relocation_packer/src/leb128_unittest.cc',
+      'bionic/tools/relocation_packer/src/packer_unittest.cc',
+      'bionic/tools/relocation_packer/src/sleb128_unittest.cc',
+      'bionic/tools/relocation_packer/src/run_all_unittests.cc',
+    ],
+  },
+  'targets': [
+    {
+      # GN: //third_party/android_platform:android_lib_relocation_packer
+      'target_name': 'android_lib_relocation_packer',
+      'toolsets': ['host'],
+      'type': 'static_library',
+      'dependencies': [
+        '../../third_party/elfutils/elfutils.gyp:libelf',
+      ],
+      'sources': [
+        '<@(relocation_packer_sources)'
+      ],
+    },
+    {
+      # GN: //third_party/android_platform:android_relocation_packer
+      'target_name': 'android_relocation_packer',
+      'toolsets': ['host'],
+      'type': 'executable',
+      'dependencies': [
+        '../../third_party/elfutils/elfutils.gyp:libelf',
+        'android_lib_relocation_packer',
+      ],
+      'sources': [
+        '<@(relocation_packer_main_source)'
+      ],
+    },
+    {
+      # TODO(GN)
+      'target_name': 'android_relocation_packer_unittests',
+      'toolsets': ['host'],
+      'type': 'executable',
+      'dependencies': [
+        '../../testing/gtest.gyp:gtest',
+        'android_lib_relocation_packer',
+      ],
+      'include_dirs': [
+        '../..',
+      ],
+      'sources': [
+        '<@(relocation_packer_test_sources)'
+      ],
+      'copies': [
+        {
+          'destination': '<(PRODUCT_DIR)',
+          'files': [
+            'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so',
+            'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so',
+            'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so',
+            'bionic/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so',
+          ],
+        },
+      ],
+    },
+  ],
+}
diff --git a/third_party/closure_compiler/bump_compiler_version b/third_party/closure_compiler/bump_compiler_version
index 15e802b7..c481b06 100755
--- a/third_party/closure_compiler/bump_compiler_version
+++ b/third_party/closure_compiler/bump_compiler_version
@@ -32,7 +32,24 @@
 
 echo "Copying compiler.jar and chrome_extensions.js"
 cp build/compiler.jar "${SCRIPT_DIR}/compiler/"
-cp contrib/externs/chrome_extensions.js "${SCRIPT_DIR}/externs/"
+(cat <<EOT && cat contrib/externs/chrome_extensions.js) > "${SCRIPT_DIR}/externs/chrome_extensions.js"
+//    SSSSSSSSSSSSSSS TTTTTTTTTTTTTTTTTTTTTTT     OOOOOOOOO     PPPPPPPPPPPPPPPPP
+//  SS:::::::::::::::ST:::::::::::::::::::::T   OO:::::::::OO   P::::::::::::::::P
+// S:::::SSSSSS::::::ST:::::::::::::::::::::T OO:::::::::::::OO P::::::PPPPPP:::::P
+// S:::::S     SSSSSSST:::::TT:::::::TT:::::TO:::::::OOO:::::::OPP:::::P     P:::::P
+// S:::::S            TTTTTT  T:::::T  TTTTTTO::::::O   O::::::O  P::::P     P:::::P
+// S:::::S                    T:::::T        O:::::O     O:::::O  P::::P     P:::::P
+//  S::::SSSS                                                     P::::PPPPPP:::::P
+//   SS::::::SSSSS       This file is generated. To update it,    P:::::::::::::PP
+//     SSS::::::::SS          run bump_compiler_version.          P::::PPPPPPPPP
+//        SSSSSS::::S                                             P::::P
+//             S:::::S        T:::::T        O:::::O     O:::::O  P::::P
+//             S:::::S        T:::::T        O::::::O   O::::::O  P::::P
+// SSSSSSS     S:::::S      TT:::::::TT      O:::::::OOO:::::::OPP::::::PP
+// S::::::SSSSSS:::::S      T:::::::::T       OO:::::::::::::OO P::::::::P
+// S:::::::::::::::SS       T:::::::::T         OO:::::::::OO   P::::::::P
+//  SSSSSSSSSSSSSSS         TTTTTTTTTTT           OOOOOOOOO     PPPPPPPPPP
+EOT
 
 echo "Rebuilding runner.jar"
 "${SCRIPT_DIR}/runner/build_runner_jar.py"
diff --git a/third_party/closure_compiler/checker.py b/third_party/closure_compiler/checker.py
index 5d372b2..38473e9c 100755
--- a/third_party/closure_compiler/checker.py
+++ b/third_party/closure_compiler/checker.py
@@ -18,8 +18,7 @@
 
 
 class Checker(object):
-  """Runs the Closure compiler on a given source file and returns the
-  success/errors."""
+  """Runs the Closure compiler on a given source file to typecheck it."""
 
   _COMMON_CLOSURE_ARGS = [
     "--accept_const_keyword",
@@ -48,7 +47,7 @@
     "--source_map_format=V3",
   ]
 
-  # These are the extra flags used when compiling in 'strict' mode.
+  # These are the extra flags used when compiling in strict mode.
   # Flags that are normally disabled are turned on for strict mode.
   _STRICT_CLOSURE_ARGS = [
     "--jscomp_error=reportUnknownTypes",
@@ -72,9 +71,12 @@
     "-XX:+TieredCompilation"
   ]
 
-  _found_java = False
-
   def __init__(self, verbose=False, strict=False):
+    """
+    Args:
+      verbose: Whether this class should output diagnostic messages.
+      strict: Whether the Closure Compiler should be invoked more strictly.
+    """
     current_dir = os.path.join(os.path.dirname(__file__))
     self._runner_jar = os.path.join(current_dir, "runner", "runner.jar")
     self._temp_files = []
@@ -83,21 +85,31 @@
     self._error_filter = error_filter.PromiseErrorFilter()
 
   def _clean_up(self):
+    """Deletes any temp files this class knows about."""
     if not self._temp_files:
       return
 
-    self._debug("Deleting temporary files: %s" % ", ".join(self._temp_files))
+    self._log_debug("Deleting temp files: %s" % ", ".join(self._temp_files))
     for f in self._temp_files:
       os.remove(f)
     self._temp_files = []
 
-  def _debug(self, msg, error=False):
+  def _log_debug(self, msg, error=False):
+    """Logs |msg| to stdout if --verbose/-v is passed when invoking this script.
+
+    Args:
+      msg: A debug message to log.
+    """
     if self._verbose:
       print "(INFO) %s" % msg
 
-  def _error(self, msg):
+  def _log_error(self, msg):
+    """Logs |msg| to stderr regardless of --flags.
+
+    Args:
+      msg: An error message to log.
+    """
     print >> sys.stderr, "(ERROR) %s" % msg
-    self._clean_up()
 
   def _common_args(self):
     """Returns an array of the common closure compiler args."""
@@ -105,84 +117,128 @@
       return self._COMMON_CLOSURE_ARGS + self._STRICT_CLOSURE_ARGS
     return self._COMMON_CLOSURE_ARGS + self._DISABLED_CLOSURE_ARGS
 
-  def _run_command(self, cmd):
-    """Runs a shell command.
+  def _run_jar(self, jar, args):
+    """Runs a .jar from the command line with arguments.
 
     Args:
-        cmd: A list of tokens to be joined into a shell command.
+      jar: A file path to a .jar file
+      args: A list of command line arguments to be passed when running the .jar.
 
     Return:
-        True if the exit code was 0, else False.
+      (exit_code, stderr) The exit code of the command (e.g. 0 for success) and
+          the stderr collected while running |jar| (as a string).
     """
-    cmd_str = " ".join(cmd)
-    self._debug("Running command: %s" % cmd_str)
+    shell_command = " ".join(self._JAR_COMMAND + [jar] + args)
+    self._log_debug("Running jar: %s" % shell_command)
 
     devnull = open(os.devnull, "w")
-    return subprocess.Popen(
-        cmd_str, stdout=devnull, stderr=subprocess.PIPE, shell=True)
+    kwargs = {"stdout": devnull, "stderr": subprocess.PIPE, "shell": True}
+    process = subprocess.Popen(shell_command, **kwargs)
+    _, stderr = process.communicate()
+    return process.returncode, stderr
 
-  def _check_java_path(self):
-    """Checks that `java` is on the system path."""
-    if not self._found_java:
-      proc = self._run_command(["which", "java"])
-      proc.communicate()
-      if proc.returncode == 0:
-        self._found_java = True
-      else:
-        self._error("Cannot find java (`which java` => %s)" % proc.returncode)
+  def _get_line_number(self, match):
+    """When chrome is built, it preprocesses its JavaScript from:
 
-    return self._found_java
+      <include src="blah.js">
+      alert(1);
 
-  def _run_jar(self, jar, args=None):
-    args = args or []
-    self._check_java_path()
-    return self._run_command(self._JAR_COMMAND + [jar] + args)
+    to:
 
-  def _fix_line_number(self, match):
-    """Changes a line number from /tmp/file:300 to /orig/file:100.
+      /* contents of blah.js inlined */
+      alert(1);
+
+    Because Closure Compiler requires this inlining already be done (as
+    <include> isn't valid JavaScript), this script creates temporary files to
+    expand all the <include>s.
+
+    When type errors are hit in temporary files, a developer doesn't know the
+    original source location to fix. This method maps from /tmp/file:300 back to
+    /original/source/file:100 so fixing errors is faster for developers.
 
     Args:
-        match: A re.MatchObject from matching against a line number regex.
+      match: A re.MatchObject from matching against a line number regex.
 
     Returns:
-        The fixed up /file and :line number.
+      The fixed up /file and :line number.
     """
     real_file = self._processor.get_file_from_line(match.group(1))
     return "%s:%d" % (os.path.abspath(real_file.file), real_file.line_number)
 
-  def _fix_up_error(self, error):
-    """Filter out irrelevant errors or fix line numbers.
+  def _filter_errors(self, errors):
+    """Removes some extraneous errors. For example, we ignore:
+
+      Variable x first declared in /tmp/expanded/file
+
+    Because it's just a duplicated error (it'll only ever show up 2+ times).
+    We also ignore Promose-based errors:
+
+      found   : function (VolumeInfo): (Promise<(DirectoryEntry|null)>|null)
+      required: (function (Promise<VolumeInfo>): ?|null|undefined)
+
+    as templates don't work with Promises in all cases yet. See
+    https://github.com/google/closure-compiler/issues/715 for details.
 
     Args:
-        error: A Closure compiler error (2 line string with error and source).
+      errors: A list of string errors extracted from Closure Compiler output.
 
     Return:
-        The fixed up error string (blank if it should be ignored).
+      A slimmer, sleeker list of relevant errors (strings).
     """
-    if " first declared in " in error:
-      # Ignore "Variable x first declared in /same/file".
-      return ""
+    first_declared_in = lambda e: " first declared in " not in e
+    return self._error_filter.filter(filter(first_declared_in, errors))
 
+  def _fix_up_error(self, error):
+    """Reverse the effects that funky <include> preprocessing steps have on
+    errors messages.
+
+    Args:
+      error: A Closure compiler error (2 line string with error and source).
+
+    Return:
+      The fixed up error string.
+    """
     expanded_file = self._expanded_file
-    fixed = re.sub("%s:(\d+)" % expanded_file, self._fix_line_number, error)
+    fixed = re.sub("%s:(\d+)" % expanded_file, self._get_line_number, error)
     return fixed.replace(expanded_file, os.path.abspath(self._file_arg))
 
   def _format_errors(self, errors):
-    """Formats Closure compiler errors to easily spot compiler output."""
-    errors = filter(None, errors)
+    """Formats Closure compiler errors to easily spot compiler output.
+
+    Args:
+      errors: A list of strings extracted from the Closure compiler's output.
+
+    Returns:
+      A formatted output string.
+    """
     contents = "\n## ".join("\n\n".join(errors).splitlines())
     return "## %s" % contents if contents else ""
 
   def _create_temp_file(self, contents):
+    """Creates an owned temporary file with |contents|.
+
+    Args:
+      content: A string of the file contens to write to a temporary file.
+
+    Return:
+      The filepath of the newly created, written, and closed temporary file.
+    """
     with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file:
       self._temp_files.append(tmp_file.name)
       tmp_file.write(contents)
     return tmp_file.name
 
   def _run_js_check(self, sources, out_file=None, externs=None):
-    if not self._check_java_path():
-      return 1, ""
+    """Check |sources| for type errors.
 
+    Args:
+      sources: Files to check.
+      externs: @extern files that inform the compiler about custom globals.
+
+    Returns:
+      (errors, stderr) A parsed list of errors (strings) found by the compiler
+          and the raw stderr (as a string).
+    """
     args = ["--js=%s" % s for s in sources]
 
     if out_file:
@@ -191,93 +247,93 @@
 
     if externs:
       args += ["--externs=%s" % e for e in externs]
+
     args_file_content = " %s" % " ".join(self._common_args() + args)
-    self._debug("Args: %s" % args_file_content.strip())
+    self._log_debug("Args: %s" % args_file_content.strip())
 
     args_file = self._create_temp_file(args_file_content)
-    self._debug("Args file: %s" % args_file)
+    self._log_debug("Args file: %s" % args_file)
 
     runner_args = ["--compiler-args-file=%s" % args_file]
-    runner_cmd = self._run_jar(self._runner_jar, args=runner_args)
-    _, stderr = runner_cmd.communicate()
+    _, stderr = self._run_jar(self._runner_jar, runner_args)
 
     errors = stderr.strip().split("\n\n")
-    self._debug("Summary: %s" % errors.pop())
+    maybe_summary = errors.pop()
 
-    self._clean_up()
+    if re.search(".*error.*warning.*typed", maybe_summary):
+      self._log_debug("Summary: %s" % maybe_summary)
+    else:
+      # Not a summary. Running the jar failed. Bail.
+      self._log_error(stderr)
+      self._clean_up()
+      sys.exit(1)
 
     return errors, stderr
 
   def check(self, source_file, out_file=None, depends=None, externs=None):
-    """Closure compile a file and check for errors.
+    """Closure compiler |source_file| while checking for errors.
 
     Args:
-        source_file: A file to check.
-        out_file: A file where the compiled output is written to.
-        depends: Other files that would be included with a <script> earlier in
-            the page.
-        externs: @extern files that inform the compiler about custom globals.
+      source_file: A file to check.
+      out_file: A file where the compiled output is written to.
+      depends: Files that |source_file| requires to run (e.g. earlier <script>).
+      externs: @extern files that inform the compiler about custom globals.
 
     Returns:
-        (has_errors, output) A boolean indicating if there were errors and the
-            Closure compiler output (as a string).
+      (found_errors, stderr) A boolean indicating whether errors were found and
+          the raw Closure compiler stderr (as a string).
     """
-    depends = depends or []
-    externs = externs or set()
-
-    if not self._check_java_path():
-      return 1, ""
-
-    self._debug("FILE: %s" % source_file)
+    self._log_debug("FILE: %s" % source_file)
 
     if source_file.endswith("_externs.js"):
-      self._debug("Skipping externs: %s" % source_file)
+      self._log_debug("Skipping externs: %s" % source_file)
       return
 
     self._file_arg = source_file
 
-    tmp_dir = tempfile.gettempdir()
-    rel_path = lambda f: os.path.join(os.path.relpath(os.getcwd(), tmp_dir), f)
+    cwd, tmp_dir = os.getcwd(), tempfile.gettempdir()
+    rel_path = lambda f: os.path.join(os.path.relpath(cwd, tmp_dir), f)
 
+    depends = depends or []
     includes = [rel_path(f) for f in depends + [source_file]]
     contents = ['<include src="%s">' % i for i in includes]
     meta_file = self._create_temp_file("\n".join(contents))
-    self._debug("Meta file: %s" % meta_file)
+    self._log_debug("Meta file: %s" % meta_file)
 
     self._processor = processor.Processor(meta_file)
     self._expanded_file = self._create_temp_file(self._processor.contents)
-    self._debug("Expanded file: %s" % self._expanded_file)
+    self._log_debug("Expanded file: %s" % self._expanded_file)
 
     errors, stderr = self._run_js_check([self._expanded_file],
                                         out_file=out_file, externs=externs)
+    filtered_errors = self._filter_errors(errors)
+    fixed_errors = map(self._fix_up_error, filtered_errors)
+    output = self._format_errors(fixed_errors)
 
-    # Filter out false-positive promise chain errors.
-    # See https://github.com/google/closure-compiler/issues/715 for details.
-    errors = self._error_filter.filter(errors);
-
-    output = self._format_errors(map(self._fix_up_error, errors))
-    if errors:
+    if fixed_errors:
       prefix = "\n" if output else ""
-      self._error("Error in: %s%s%s" % (source_file, prefix, output))
+      self._log_error("Error in: %s%s%s" % (source_file, prefix, output))
     elif output:
-      self._debug("Output: %s" % output)
+      self._log_debug("Output: %s" % output)
 
-    return bool(errors), output
+    self._clean_up()
+    return bool(fixed_errors), stderr
 
   def check_multiple(self, sources):
     """Closure compile a set of files and check for errors.
 
     Args:
-        sources: An array of files to check.
+      sources: An array of files to check.
 
     Returns:
-        (has_errors, output) A boolean indicating if there were errors and the
-            Closure compiler output (as a string).
+      (found_errors, stderr) A boolean indicating whether errors were found and
+          the raw Closure Compiler stderr (as a string).
     """
-
-    errors, stderr = self._run_js_check(sources)
+    errors, stderr = self._run_js_check(sources, [])
+    self._clean_up()
     return bool(errors), stderr
 
+
 if __name__ == "__main__":
   parser = argparse.ArgumentParser(
       description="Typecheck JavaScript using Closure compiler")
@@ -316,20 +372,17 @@
   if opts.single_file:
     for source in opts.sources:
       depends, externs = build.inputs.resolve_recursive_dependencies(
-          source,
-          depends,
-          externs)
-      has_errors, _ = checker.check(source, out_file=opts.out_file,
-                                    depends=depends, externs=externs)
-      if has_errors:
+          source, depends, externs)
+      found_errors, _ = checker.check(source, out_file=opts.out_file,
+                                      depends=depends, externs=externs)
+      if found_errors:
         sys.exit(1)
-
   else:
-    has_errors, errors = checker.check_multiple(opts.sources)
-    if has_errors:
-      print errors
+    found_errors, stderr = checker.check_multiple(opts.sources)
+    if found_errors:
+      print stderr
       sys.exit(1)
 
   if opts.success_stamp:
-    with open(opts.success_stamp, 'w'):
+    with open(opts.success_stamp, "w"):
       os.utime(opts.success_stamp, None)
diff --git a/third_party/closure_compiler/compile_js.gypi b/third_party/closure_compiler/compile_js.gypi
index 6b19152..81b86a87 100644
--- a/third_party/closure_compiler/compile_js.gypi
+++ b/third_party/closure_compiler/compile_js.gypi
@@ -19,6 +19,7 @@
         'depends%': [],
       },
       'inputs': [
+        'compile_js.gypi',
         '<(CLOSURE_DIR)/checker.py',
         '<(CLOSURE_DIR)/processor.py',
         '<(CLOSURE_DIR)/build/inputs.py',
@@ -37,6 +38,7 @@
         '--depends', '<@(depends)',
         '--externs', '<@(externs)',
         '--out_file', '<(out_file)',
+        # Add '--verbose', for glorious log spam.
       ],
       'message': 'Compiling <(source_file)',
     }
diff --git a/third_party/closure_compiler/compiled_resources.gyp b/third_party/closure_compiler/compiled_resources.gyp
index 6f58aa7e..96c1619 100644
--- a/third_party/closure_compiler/compiled_resources.gyp
+++ b/third_party/closure_compiler/compiled_resources.gyp
@@ -35,7 +35,10 @@
         '../../ui/webui/resources/js/chromeos/compiled_resources.gyp:*',
         '../../ui/webui/resources/js/compiled_resources.gyp:*',
         '../../ui/webui/resources/js/cr/ui/compiled_resources.gyp:*',
-      ]
+      ],
+      'includes': [
+        '../../remoting/remoting_webapp_compile.gypi',
+      ],
     },
   ]
 }
diff --git a/third_party/closure_compiler/compiler_customization_test.py b/third_party/closure_compiler/compiler_customization_test.py
index 950059c3..ead5b2e 100755
--- a/third_party/closure_compiler/compiler_customization_test.py
+++ b/third_party/closure_compiler/compiler_customization_test.py
@@ -36,18 +36,17 @@
     return self._checker.check(file_path)
 
   def _runCheckerTestExpectError(self, source_code, expected_error):
-    _, output = self._runChecker(source_code)
+    _, stderr = self._runChecker(source_code)
 
-    self.assertTrue(expected_error in output,
+    self.assertTrue(expected_error in stderr,
         msg="Expected chunk: \n%s\n\nOutput:\n%s\n" % (
-            expected_error, output))
+            expected_error, stderr))
 
   def _runCheckerTestExpectSuccess(self, source_code):
-    return_code, output = self._runChecker(source_code)
+    found_errors, stderr = self._runChecker(source_code)
 
-    self.assertTrue(return_code == 0,
-        msg="Expected success, got return code %d\n\nOutput:\n%s\n" % (
-            return_code, output))
+    self.assertFalse(found_errors,
+        msg="Expected success, but got failure\n\nOutput:\n%s\n" % stderr)
 
   def testGetInstance(self):
     self._runCheckerTestExpectError("""
diff --git a/third_party/closure_compiler/externs/chrome_extensions.js b/third_party/closure_compiler/externs/chrome_extensions.js
index 0dc3411..5026048 100644
--- a/third_party/closure_compiler/externs/chrome_extensions.js
+++ b/third_party/closure_compiler/externs/chrome_extensions.js
@@ -1,3 +1,19 @@
+//    SSSSSSSSSSSSSSS TTTTTTTTTTTTTTTTTTTTTTT     OOOOOOOOO     PPPPPPPPPPPPPPPPP
+//  SS:::::::::::::::ST:::::::::::::::::::::T   OO:::::::::OO   P::::::::::::::::P
+// S:::::SSSSSS::::::ST:::::::::::::::::::::T OO:::::::::::::OO P::::::PPPPPP:::::P
+// S:::::S     SSSSSSST:::::TT:::::::TT:::::TO:::::::OOO:::::::OPP:::::P     P:::::P
+// S:::::S            TTTTTT  T:::::T  TTTTTTO::::::O   O::::::O  P::::P     P:::::P
+// S:::::S                    T:::::T        O:::::O     O:::::O  P::::P     P:::::P
+//  S::::SSSS                                                     P::::PPPPPP:::::P
+//   SS::::::SSSSS       This file is generated. To update it,    P:::::::::::::PP
+//     SSS::::::::SS          run bump_compiler_version.          P::::PPPPPPPPP
+//        SSSSSS::::S                                             P::::P
+//             S:::::S        T:::::T        O:::::O     O:::::O  P::::P
+//             S:::::S        T:::::T        O::::::O   O::::::O  P::::P
+// SSSSSSS     S:::::S      TT:::::::TT      O:::::::OOO:::::::OPP::::::PP
+// S::::::SSSSSS:::::S      T:::::::::T       OO:::::::::::::OO P::::::::P
+// S:::::::::::::::SS       T:::::::::T         OO:::::::::OO   P::::::::P
+//  SSSSSSSSSSSSSSS         TTTTTTTTTTT           OOOOOOOOO     PPPPPPPPPP
 /*
  * Copyright 2009 The Closure Compiler Authors
  *
@@ -558,6 +574,115 @@
 
 
 /**
+ * Private API.
+ *
+ * @const
+ * @see https://code.google.com/p/chromium/codesearch#chromium/src/chrome/common/extensions/api/audio_modem.idl
+ * @see go/chrome-modem
+ */
+chrome.audioModem = {};
+
+
+/**
+ * @typedef {?{
+ *   tokenLength: number,
+ *   crc: (boolean|undefined),
+ *   parity: (boolean|undefined)
+ * }}
+ */
+chrome.audioModem.TokenEncoding;
+
+
+/**
+ * @typedef {?{
+ *   timeoutMillis: number,
+ *   band: string,
+ *   encoding: !chrome.audioModem.TokenEncoding
+ * }}
+ */
+chrome.audioModem.RequestParams;
+
+
+/** @constructor */
+chrome.audioModem.ReceivedToken = function() {};
+
+
+/** @type {!ArrayBuffer} */
+chrome.audioModem.ReceivedToken.prototype.token;
+
+
+/** @type {string} */
+chrome.audioModem.ReceivedToken.prototype.band;
+
+
+/**
+ * @param {!chrome.audioModem.RequestParams} params
+ * @param {!ArrayBuffer} token
+ * @param {function(string)} callback
+ */
+chrome.audioModem.transmit = function(params, token, callback) {};
+
+
+/**
+ * @param {string} band
+ * @param {function(string)} callback
+ */
+chrome.audioModem.stopTransmit = function(band, callback) {};
+
+
+/**
+ * @param {!chrome.audioModem.RequestParams} params
+ * @param {function(string)} callback
+ */
+chrome.audioModem.receive = function(params, callback) {};
+
+
+/**
+ * @param {string} band
+ * @param {function(string)} callback
+ */
+chrome.audioModem.stopReceive = function(band, callback) {};
+
+
+/** @constructor */
+chrome.audioModem.ReceivedEvent = function() {};
+
+
+/**
+ * @param {function(!Array<!chrome.audioModem.ReceivedToken>)} callback
+ */
+chrome.audioModem.ReceivedEvent.prototype.addListener = function(callback) {};
+
+
+/**
+ * @param {function(!Array<!chrome.audioModem.ReceivedToken>)} callback
+ */
+chrome.audioModem.ReceivedEvent.prototype.removeListener =
+    function(callback) {};
+
+
+/**
+ * @param {function(!Array<!chrome.audioModem.ReceivedToken>)} callback
+ * @return {boolean}
+ */
+chrome.audioModem.ReceivedEvent.prototype.hasListener = function(callback) {};
+
+
+/**
+ * @return {boolean}
+ */
+chrome.audioModem.ReceivedEvent.prototype.hasListeners = function() {};
+
+
+/** @type {!chrome.audioModem.ReceivedEvent} */
+chrome.audioModem.onReceived;
+
+
+/** @type {!ChromeStringEvent} */
+chrome.audioModem.onTransmitFail;
+
+
+/**
  * @const
  * @see https://developer.chrome.com/apps/bluetooth
  */
@@ -1199,7 +1324,7 @@
  * @constructor
  * @see https://developer.chrome.com/apps/bluetoothLowEnergy#type-Characteristic
  */
-chrome.bluetoothLowEnergy.Characteristic;
+chrome.bluetoothLowEnergy.Characteristic = function() {};
 
 
 /** @type {string} */
@@ -1226,7 +1351,7 @@
  * @constructor
  * @see https://developer.chrome.com/apps/bluetoothLowEnergy#type-Descriptor
  */
-chrome.bluetoothLowEnergy.Descriptor;
+chrome.bluetoothLowEnergy.Descriptor = function() {};
 
 /** @type {string} */
 chrome.bluetoothLowEnergy.Descriptor.prototype.uuid;
@@ -1403,17 +1528,17 @@
 chrome.bluetoothLowEnergy.ServiceEvent = function() {};
 
 
-/** @param {function(!chrome.bluetoothLowEnergy.ServiceEvent): void} callback */
+/** @param {function(!chrome.bluetoothLowEnergy.Service): void} callback */
 chrome.bluetoothLowEnergy.ServiceEvent.prototype.addListener =
     function(callback) {};
 
 
-/** @param {function(!chrome.bluetoothLowEnergy.ServiceEvent): void} callback */
+/** @param {function(!chrome.bluetoothLowEnergy.Service): void} callback */
 chrome.bluetoothLowEnergy.ServiceEvent.prototype.removeListener =
     function(callback) {};
 
 /**
- * @param {function(!chrome.bluetoothLowEnergy.ServiceEvent): void} callback
+ * @param {function(!chrome.bluetoothLowEnergy.Service): void} callback
  * @return {boolean}
  */
 chrome.bluetoothLowEnergy.ServiceEvent.prototype.hasListener =
@@ -1453,7 +1578,7 @@
 
 
 /**
- * @param {function(!chrome.bluetoothLowEnergy.CharacteristicEvent): void}
+ * @param {function(!chrome.bluetoothLowEnergy.Characteristic): void}
  *     callback
  */
 chrome.bluetoothLowEnergy.CharacteristicEvent.prototype.addListener =
@@ -1461,7 +1586,7 @@
 
 
 /**
- * @param {function(!chrome.bluetoothLowEnergy.CharacteristicEvent): void}
+ * @param {function(!chrome.bluetoothLowEnergy.Characteristic): void}
  *     callback
  */
 chrome.bluetoothLowEnergy.CharacteristicEvent.prototype.removeListener =
@@ -1469,7 +1594,7 @@
 
 
 /**
- * @param {function(!chrome.bluetoothLowEnergy.CharacteristicEvent): void}
+ * @param {function(!chrome.bluetoothLowEnergy.Characteristic): void}
  *     callback
  * @return {boolean}
  */
@@ -1497,7 +1622,7 @@
 
 
 /**
- * @param {function(!chrome.bluetoothLowEnergy.DescriptorEvent): void}
+ * @param {function(!chrome.bluetoothLowEnergy.Descriptor): void}
  *     callback
  */
 chrome.bluetoothLowEnergy.DescriptorEvent.prototype.addListener =
@@ -1505,7 +1630,7 @@
 
 
 /**
- * @param {function(!chrome.bluetoothLowEnergy.DescriptorEvent): void}
+ * @param {function(!chrome.bluetoothLowEnergy.Descriptor): void}
  *     callback
  */
 chrome.bluetoothLowEnergy.DescriptorEvent.prototype.removeListener =
@@ -1513,7 +1638,7 @@
 
 
 /**
- * @param {function(!chrome.bluetoothLowEnergy.DescriptorEvent): void} callback
+ * @param {function(!chrome.bluetoothLowEnergy.Descriptor): void} callback
  * @return {boolean}
  */
 chrome.bluetoothLowEnergy.DescriptorEvent.prototype.hasListener =
@@ -2491,6 +2616,38 @@
 
 
 /**
+ * @see https://developer.chrome.com/extensions/topSites
+ * @const
+ */
+chrome.topSites = {};
+
+
+
+/**
+ * @constructor
+ * @see https://developer.chrome.com/extensions/topSites#type-MostVisitedURL
+ */
+chrome.topSites.MostVisitedURL = function() {};
+
+
+/** @type {string} */
+chrome.topSites.MostVisitedURL.prototype.url;
+
+
+/** @type {string} */
+chrome.topSites.MostVisitedURL.prototype.title;
+
+
+/**
+ * Gets a list of top sites.
+ * @param {function(!Array<!chrome.topSites.MostVisitedURL>)} callback Invoked
+ *     with a list of most visited URLs.
+ * @see https://developer.chrome.com/extensions/topSites#method-get
+ */
+chrome.topSites.get = function(callback) {};
+
+
+/**
  * @const
  * @see https://developer.chrome.com/extensions/windows.html
  */
@@ -6875,7 +7032,7 @@
 /**
  * @param {!chrome.fileSystem.RequestFileSystemOptions} options Options for the
  *     request.
- * @param {!function(FileSystem)} callback A completion callback.
+ * @param {function(!FileSystem=)} callback A completion callback.
  * @see http://developer.chrome.com/apps/fileSystem.html#method-requestFileSystem
  */
 chrome.fileSystem.requestFileSystem = function(options, callback) {};
@@ -7057,16 +7214,20 @@
 /**
  * Clears the alarm with the given name.
  * @param {string=} opt_name
+ * @param {function(boolean)=} opt_callback A callback that will be called with
+ *     a boolean for whether the alarm was cleared.
  * @see http://developer.chrome.com/extensions/alarms.html#method-clear
  */
-chrome.alarms.clear = function(opt_name) {};
+chrome.alarms.clear = function(opt_name, opt_callback) {};
 
 
 /**
  * Clears all alarms.
+ * @param {function(boolean)=} opt_callback A callback that will be called with
+ *     a boolean for whether the alarms were cleared.
  * @see http://developer.chrome.com/extensions/alarms.html#method-clearAll
  */
-chrome.alarms.clearAll = function() {};
+chrome.alarms.clearAll = function(opt_callback) {};
 
 
 /**
@@ -8328,6 +8489,13 @@
 
 
 /**
+ * @param {string} guid
+ * @param {function()=} opt_callback
+ */
+chrome.networkingPrivate.forgetNetwork = function(guid, opt_callback) {};
+
+
+/**
  * @param {!chrome.networkingPrivate.NetworkFilter} filter
  * @param {function(!Array.<!Object>)=} opt_callback
  */
@@ -8376,11 +8544,11 @@
 
 /**
  * @param {string} guid
- * @param {string} opt_carrier
+ * @param {(string|function())=} opt_carrierOrCallback
  * @param {function()=} opt_callback
  */
 chrome.networkingPrivate.startActivate =
-  function(guid, opt_carrier, opt_callback) {};
+  function(guid, opt_carrierOrCallback, opt_callback) {};
 
 
 /**
@@ -8569,6 +8737,7 @@
 
 /**
  * Establish the session.
+ * TODO(user): Deprecated. Remove after app updated to use createSession.
  * @param {string} ipAddress
  * @param {number} port
  * @param {function(number, string, !Array.<string>): void}
@@ -8581,6 +8750,18 @@
 
 
 /**
+ * Create new pairing session.
+ * @param {string} serviceName The mDNS service name of the device.
+ * @param {function(number, string, !Array.<string>): void}
+ *     callback Called when the session is established or on error. 1st param,
+ *     |sessionId|, is the session ID (identifies the session for future calls).
+ *     2nd param, |status|, is the status (success or type of error). 3rd param,
+ *     |pairingTypes|, is a list of pairing types supported by this device.
+ */
+chrome.gcdPrivate.createSession = function(serviceName, callback) {};
+
+
+/**
  * Start pairing with the selected method.
  * @param {number} sessionId
  * @param {string} pairingType
diff --git a/third_party/closure_compiler/externs/settings_private.js b/third_party/closure_compiler/externs/settings_private.js
new file mode 100644
index 0000000..a9efae8
--- /dev/null
+++ b/third_party/closure_compiler/externs/settings_private.js
@@ -0,0 +1,86 @@
+// 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 Externs generated from namespace: settingsPrivate
+ * @externs
+ */
+
+/**
+ * @const
+ */
+chrome.settingsPrivate = {};
+
+/**
+ * @typedef {{
+ *   key: string,
+ *   type: chrome.settingsPrivate.PrefType,
+ *   value: *,
+ *   source: chrome.settingsPrivate.PrefSource
+ * }}
+ */
+chrome.settingsPrivate.PrefObject;
+
+/**
+ * @typedef {function(success)}
+ */
+chrome.settingsPrivate.OnPrefSetCallback;
+
+/**
+ * @typedef {function(!Array<chrome.settingsPrivate.PrefObject>)}
+ */
+chrome.settingsPrivate.GetAllPrefsCallback;
+
+/**
+ * @typedef {function(chrome.settingsPrivate.PrefObject)}
+ */
+chrome.settingsPrivate.GetPrefCallback;
+
+/**
+ * Sets a boolean settings value.
+ * @param {string} name
+ * @param {boolean} value
+ * @param {chrome.settingsPrivate.OnPrefSetCallback} callback
+ */
+chrome.settingsPrivate.setBooleanPref = function(name, value, callback) {};
+
+/**
+ * Sets a number settings value.
+ * @param {string} name
+ * @param {number} value
+ * @param {chrome.settingsPrivate.OnPrefSetCallback} callback
+ */
+chrome.settingsPrivate.setNumericPref = function(name, value, callback) {};
+
+/**
+ * Sets a string settings value.
+ * @param {string} name
+ * @param {string} value
+ * @param {chrome.settingsPrivate.OnPrefSetCallback} callback
+ */
+chrome.settingsPrivate.setStringPref = function(name, value, callback) {};
+
+/**
+ * Sets a URL settings value.
+ * @param {string} name
+ * @param {string} value
+ * @param {chrome.settingsPrivate.OnPrefSetCallback} callback
+ */
+chrome.settingsPrivate.setURLPref = function(name, value, callback) {};
+
+/**
+ * Gets all the prefs.
+ * @param {chrome.settingsPrivate.GetAllPrefsCallback} callback
+ */
+chrome.settingsPrivate.getAllPrefs = function(callback) {};
+
+/**
+ * Gets the value of a specific pref.
+ * @param {string} name
+ * @param {chrome.settingsPrivate.GetPrefCallback} callback
+ */
+chrome.settingsPrivate.getPref = function(name, callback) {};
+
+/** @type {!ChromeEvent} */
+chrome.settingsPrivate.onPrefsChanged;
diff --git a/third_party/closure_compiler/processor.py b/third_party/closure_compiler/processor.py
index af88862..d7163e48 100644
--- a/third_party/closure_compiler/processor.py
+++ b/third_party/closure_compiler/processor.py
@@ -10,13 +10,13 @@
 
 
 class LineNumber(object):
-  """A simple wrapper to hold line information (e.g. file.js:32).
-  
-  Args:
-    source_file: A file path.
-    line_number: The line in |file|.
-  """
+  """A simple wrapper to hold line information (e.g. file.js:32)."""
   def __init__(self, source_file, line_number):
+    """
+    Args:
+      source_file: A file path (as a string).
+      line_number: The line in |file| (as an integer).
+    """
     self.file = source_file
     self.line_number = int(line_number)
 
@@ -25,7 +25,7 @@
   """An in-memory cache to speed up reading the same files over and over.
   
   Usage:
-      FileCache.read(path_to_file)
+    FileCache.read(path_to_file)
   """
 
   _cache = defaultdict(str)
@@ -35,10 +35,10 @@
     """Read a file and return it as a string.
 
     Args:
-        source_file: a file to read and return the contents of.
+      source_file: a file path (as a string) to read and return the contents.
 
     Returns:
-        |file| as a string.
+      The contents of |source_file| (as a string).
     """
     abs_file = os.path.abspath(source_file)
     self._cache[abs_file] = self._cache[abs_file] or open(abs_file, "r").read()
@@ -51,42 +51,40 @@
 
   For example
 
-      1: /* blah.js */
-      2: <if expr="is_win">
-      3: <include src="win.js">
-      4: </if>
+    1: /* blah.js */
+    2: <if expr="is_win">
+    3: <include src="win.js">
+    4: </if>
 
   would be turned into:
 
-      1: /* blah.js */
-      2:
-      3: /* win.js */
-      4: alert('Ew; Windows.');
-      5:
-
-  Args:
-      source_file: A file to process.
-
-  Attributes:
-      contents: Expanded contents after inlining <include>s and stripping <if>s.
-      included_files: A list of files that were inlined via <include>.
+    1: /* blah.js */
+    2:
+    3: /* win.js */
+    4: alert('Ew; Windows.');
+    5:
   """
 
   _IF_TAGS_REG = "</?if[^>]*?>"
   _INCLUDE_REG = "<include[^>]+src=['\"]([^>]*)['\"]>"
 
   def __init__(self, source_file):
-    self._included_files = set()
+    """
+    Args:
+      source_file: A file path to process (as a string).
+    """
+    self.included_files = set()
     self._index = 0
     self._lines = self._get_file(source_file)
 
+    # Can't enumerate(self._lines) here because some lines are re-processed.
     while self._index < len(self._lines):
       current_line = self._lines[self._index]
       match = re.search(self._INCLUDE_REG, current_line[2])
       if match:
         file_dir = os.path.dirname(current_line[0])
         file_name = os.path.abspath(os.path.join(file_dir, match.group(1)))
-        if file_name not in self._included_files:
+        if file_name not in self.included_files:
           self._include_file(file_name)
           continue  # Stay on the same line.
         else:
@@ -106,7 +104,7 @@
     return [(source_file, lnum + 1, line) for lnum, line in enumerate(lines)]
 
   def _include_file(self, source_file):
-    self._included_files.add(source_file)
+    self.included_files.add(source_file)
     f = self._get_file(source_file)
     self._lines = self._lines[:self._index] + f + self._lines[self._index + 1:]
 
@@ -114,12 +112,7 @@
     """Get the original file and line number for an expanded file's line number.
 
     Args:
-        line_number: A processed file's line number.
+      line_number: A processed file's line number (as an integer or string).
     """
     line_number = int(line_number) - 1
     return LineNumber(self._lines[line_number][0], self._lines[line_number][1])
-
-  @property
-  def included_files(self):
-    """A list of files that were inlined via <include>."""
-    return self._included_files
diff --git a/third_party/dom_distiller_js/README.chromium b/third_party/dom_distiller_js/README.chromium
index 4aa5381..c63d398 100644
--- a/third_party/dom_distiller_js/README.chromium
+++ b/third_party/dom_distiller_js/README.chromium
@@ -1,6 +1,6 @@
 Name: dom-distiller-js
 URL: https://github.com/chromium/dom-distiller
-Version: 8c015c13ad
+Version: cc268ee730
 License: BSD
 Security Critical: yes
 
diff --git a/third_party/google_input_tools/inputview.gypi b/third_party/google_input_tools/inputview.gypi
index 77b8687..110cd4d 100644
--- a/third_party/google_input_tools/inputview.gypi
+++ b/third_party/google_input_tools/inputview.gypi
@@ -86,6 +86,12 @@
       'src/chrome/os/inputview/inputtoolcode.js',
       'src/chrome/os/inputview/keyboardcontainer.js',
       'src/chrome/os/inputview/layouts/compactspacerow.js',
+      'src/chrome/os/inputview/layouts/material/compactspacerow.js',
+      'src/chrome/os/inputview/layouts/material/rowsof101.js',
+      'src/chrome/os/inputview/layouts/material/rowsof102.js',
+      'src/chrome/os/inputview/layouts/material/rowsofcompact.js',
+      'src/chrome/os/inputview/layouts/material/spacerow.js',
+      'src/chrome/os/inputview/layouts/material/util.js',
       'src/chrome/os/inputview/layouts/rowsof101.js',
       'src/chrome/os/inputview/layouts/rowsof102.js',
       'src/chrome/os/inputview/layouts/rowsofcompact.js',
diff --git a/third_party/google_input_tools/update.py b/third_party/google_input_tools/update.py
index dc0800fb..652e658 100755
--- a/third_party/google_input_tools/update.py
+++ b/third_party/google_input_tools/update.py
@@ -45,6 +45,14 @@
     'i18n.input.chrome.inputview.layouts.RowsOfNumberpad',
     'i18n.input.chrome.inputview.layouts.SpaceRow',
     'i18n.input.chrome.inputview.layouts.util',
+    'i18n.input.chrome.inputview.layouts.material.CompactSpaceRow',
+    'i18n.input.chrome.inputview.layouts.material.RowsOf101',
+    'i18n.input.chrome.inputview.layouts.material.RowsOf102',
+    'i18n.input.chrome.inputview.layouts.material.RowsOfCompact',
+    'i18n.input.chrome.inputview.layouts.material.RowsOfJP',
+    'i18n.input.chrome.inputview.layouts.material.RowsOfNumberpad',
+    'i18n.input.chrome.inputview.layouts.material.SpaceRow',
+    'i18n.input.chrome.inputview.layouts.material.util',
     'i18n.input.hwt.util'
 ]
 
diff --git a/third_party/instrumented_libraries/binaries/msan-no-origins-precise.tgz.sha1 b/third_party/instrumented_libraries/binaries/msan-no-origins-precise.tgz.sha1
new file mode 100644
index 0000000..9f17c0d
--- /dev/null
+++ b/third_party/instrumented_libraries/binaries/msan-no-origins-precise.tgz.sha1
@@ -0,0 +1 @@
+2a66a901c3b0acd3e5c5cbf49ddfa442f77bb38e
\ No newline at end of file
diff --git a/third_party/instrumented_libraries/binaries/msan-no-origins-trusty.tgz.sha1 b/third_party/instrumented_libraries/binaries/msan-no-origins-trusty.tgz.sha1
new file mode 100644
index 0000000..bbe66571
--- /dev/null
+++ b/third_party/instrumented_libraries/binaries/msan-no-origins-trusty.tgz.sha1
@@ -0,0 +1 @@
+098097aa3f25d94b73014c9249a41bfe37f3242b
\ No newline at end of file
diff --git a/third_party/instrumented_libraries/instrumented_libraries.gyp b/third_party/instrumented_libraries/instrumented_libraries.gyp
index 2d61a3b..4ac7e42e 100644
--- a/third_party/instrumented_libraries/instrumented_libraries.gyp
+++ b/third_party/instrumented_libraries/instrumented_libraries.gyp
@@ -6,6 +6,8 @@
   'variables': {
     'verbose_libraries_build%': 0,
     'instrumented_libraries_jobs%': 1,
+    'instrumented_libraries_cc%': '<!(cd <(DEPTH) && pwd -P)/<(make_clang_dir)/bin/clang',
+    'instrumented_libraries_cxx%': '<!(cd <(DEPTH) && pwd -P)/<(make_clang_dir)/bin/clang++',
   },
 
   'libdir': 'lib',
@@ -22,11 +24,11 @@
       'sanitizer_type': 'tsan',
     }],
     ['use_goma==1', {
-      'cc': '<(gomadir)/gomacc <!(cd <(DEPTH) && pwd -P)/<(make_clang_dir)/bin/clang',
-      'cxx': '<(gomadir)/gomacc <!(cd <(DEPTH) && pwd -P)/<(make_clang_dir)/bin/clang++',
+      'cc': '<(gomadir)/gomacc <(instrumented_libraries_cc)',
+      'cxx': '<(gomadir)/gomacc <(instrumented_libraries_cxx)',
     }, {
-      'cc': '<!(cd <(DEPTH) && pwd -P)/<(make_clang_dir)/bin/clang',
-      'cxx': '<!(cd <(DEPTH) && pwd -P)/<(make_clang_dir)/bin/clang++',
+      'cc': '<(instrumented_libraries_cc)',
+      'cxx': '<(instrumented_libraries_cxx)',
     }],
   ],
 
@@ -94,8 +96,13 @@
               ['msan_track_origins==2', {
                 'archive_name': 'msan-chained-origins-<(_ubuntu_release)',
               }, {
-                'archive_name': 'UNSUPPORTED_CONFIGURATION'
-              }],
+                'conditions': [
+                  ['msan_track_origins==0', {
+                    'archive_name': 'msan-no-origins-<(_ubuntu_release)',
+                  }, {
+                    'archive_name': 'UNSUPPORTED_CONFIGURATION'
+                  }],
+              ]}],
           ]}, {
               'archive_name': 'UNSUPPORTED_CONFIGURATION'
           }],
diff --git a/third_party/instrumented_libraries/scripts/build_and_package.sh b/third_party/instrumented_libraries/scripts/build_and_package.sh
new file mode 100755
index 0000000..0de0c91
--- /dev/null
+++ b/third_party/instrumented_libraries/scripts/build_and_package.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+# 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.
+
+set -eu
+
+supported_build_types="msan-no-origins msan-chained-origins"
+supported_releases="precise trusty"
+ubuntu_release=$(lsb_release -cs)
+
+function show_help {
+  echo "Usage: build_and_package.sh <build_type>"
+  echo "Supported build types: all ${supported_build_types}"
+}
+
+function build_libraries {
+  local build_type=$1
+  case ${build_type} in
+    "msan-chained-origins")
+      local gyp_defines="msan=1 msan_track_origins=2"
+      ;;
+    "msan-no-origins")
+      local gyp_defines="msan=1 msan_track_origins=0"
+      ;;
+    *)
+      show_help
+      exit 1
+      ;;
+  esac
+  
+  local archive_name=${build_type}-${ubuntu_release}
+  local out_dir=out-${archive_name}
+
+  echo "Building instrumented libraries in ${out_dir}..."
+
+  rm -rf $out_dir
+  mkdir $out_dir
+
+  GYP_DEFINES="${gyp_defines} \
+               use_instrumented_libraries=1 instrumented_libraries_jobs=8" \
+  GYP_GENERATOR_FLAGS="output_dir=${out_dir}" \
+  gclient runhooks
+
+  ninja -j4 -C ${out_dir}/Release instrumented_libraries
+
+  echo "Creating archive ${archive_name}.tgz..."
+
+  files=$(ls -1 ${out_dir}/Release/instrumented_libraries)
+
+  tar zcf ${archive_name}.tgz -C ${out_dir}/Release/instrumented_libraries \
+      --exclude="?san/*.txt" ${files}
+
+  echo To upload, run:
+  echo upload_to_google_storage.py -b \
+       chromium-instrumented-libraries ${archive_name}.tgz
+  echo You should then commit the resulting .sha1 file.
+}
+
+if ! [[ "${supported_releases}" =~ ${ubuntu_release} ]]
+then
+  echo "Unsupported Ubuntu release: ${ubuntu_release}"
+  echo "Supported releases: ${supported_releases}"
+  exit 1
+fi
+
+if [ -z "${1-}" ]
+then
+  show_help
+  exit 0
+fi
+
+if ! [[ "all ${supported_build_types}" =~ $1 ]]
+then
+  show_help
+  exit 1
+fi
+if [ "$1" == "all" ]
+then
+  for build_type in ${supported_build_types}
+  do
+    build_libraries ${build_type}
+  done
+else
+  build_libraries $1
+fi
+
diff --git a/third_party/instrumented_libraries/scripts/download_binaries.py b/third_party/instrumented_libraries/scripts/download_binaries.py
index 62ef9fc..83ec7b6 100755
--- a/third_party/instrumented_libraries/scripts/download_binaries.py
+++ b/third_party/instrumented_libraries/scripts/download_binaries.py
@@ -20,6 +20,8 @@
 
 def get_configuration(gyp_defines):
   if re.search(r'\b(msan)=1', gyp_defines):
+    if 'msan_track_origins=0' in gyp_defines:
+      return 'msan-no-origins'
     if 'msan_track_origins=2' in gyp_defines:
       return 'msan-chained-origins'
     if 'msan_track_origins=' not in gyp_defines:
diff --git a/third_party/junit/BUILD.gn b/third_party/junit/BUILD.gn
index 1b02acfe..babe0f2 100644
--- a/third_party/junit/BUILD.gn
+++ b/third_party/junit/BUILD.gn
@@ -11,6 +11,8 @@
 
 # GYP: //third_party/junit.gyp:junit_jar
 java_library("junit") {
+  chromium_code = false
+  testonly = true
   deps = [
     ":hamcrest",
   ]
diff --git a/third_party/leveldatabase/env_chromium.cc b/third_party/leveldatabase/env_chromium.cc
index 7febd75..732068b 100644
--- a/third_party/leveldatabase/env_chromium.cc
+++ b/third_party/leveldatabase/env_chromium.cc
@@ -15,6 +15,7 @@
 #include "base/metrics/histogram.h"
 #include "base/process/process_metrics.h"
 #include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "third_party/leveldatabase/chromium_logger.h"
@@ -319,6 +320,7 @@
  public:
   IDBEnv() : ChromiumEnv() {
     name_ = "LevelDBEnv.IDB";
+    uma_ioerror_base_name_ = name_ + ".IOError.BFE";
     make_backup_ = true;
   }
 };
@@ -389,7 +391,7 @@
                    base::File::Error error) {
   DCHECK_LT(error, 0);
   char buf[512];
-  snprintf(buf, sizeof(buf), "%s (ChromeMethodPFE: %d::%s::%d)",
+  snprintf(buf, sizeof(buf), "%s (ChromeMethodBFE: %d::%s::%d)",
            message.c_str(), method, MethodIDToString(method), -error);
   return Status::IOError(filename, buf);
 }
@@ -415,13 +417,13 @@
   }
   int parsed_error;
   if (RE2::PartialMatch(status_string.c_str(),
-                        "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method,
+                        "ChromeMethodBFE: (\\d+)::.*::(\\d+)", &method,
                         &parsed_error)) {
     *method_param = static_cast<MethodID>(method);
     *error = static_cast<base::File::Error>(-parsed_error);
     DCHECK_LT(*error, base::File::FILE_OK);
     DCHECK_GT(*error, base::File::FILE_ERROR_MAX);
-    return METHOD_AND_PFE;
+    return METHOD_AND_BFE;
   }
   return NONE;
 }
@@ -500,7 +502,7 @@
   base::File::Error error = base::File::FILE_OK;
   leveldb_env::ErrorParsingResult result =
       leveldb_env::ParseMethodAndError(status, &method, &error);
-  return (result == leveldb_env::METHOD_AND_PFE &&
+  return (result == leveldb_env::METHOD_AND_BFE &&
           static_cast<base::File::Error>(error) ==
               base::File::FILE_ERROR_NO_SPACE);
 }
@@ -518,6 +520,7 @@
       bgsignal_(&mu_),
       started_bgthread_(false),
       kMaxRetryTimeMillis(1000) {
+  uma_ioerror_base_name_ = name_ + ".IOError.BFE";
 }
 
 ChromiumEnv::~ChromiumEnv() {
@@ -934,9 +937,9 @@
 
 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
                                                       int limit) const {
-  std::string uma_name(name_);
-  // TODO(dgrogan): This is probably not the best way to concatenate strings.
-  uma_name.append(".IOError.").append(MethodIDToString(method));
+  std::string uma_name;
+  base::StringAppendF(&uma_name, "%s.%s", uma_ioerror_base_name_.c_str(),
+                      MethodIDToString(method));
   return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
       base::Histogram::kUmaTargetedHistogramFlag);
 }
diff --git a/third_party/leveldatabase/env_chromium.h b/third_party/leveldatabase/env_chromium.h
index a6064828..5372ac8 100644
--- a/third_party/leveldatabase/env_chromium.h
+++ b/third_party/leveldatabase/env_chromium.h
@@ -47,6 +47,11 @@
   kNumEntries
 };
 
+// The default value for leveldb::Options::reuse_logs. Currently log reuse is an
+// experimental feature in leveldb. More info at:
+// https://github.com/google/leveldb/commit/251ebf5dc70129ad3
+const bool kDefaultLogReuseOptionValue = true;
+
 const char* MethodIDToString(MethodID method);
 
 leveldb::Status MakeIOError(leveldb::Slice filename,
@@ -59,7 +64,7 @@
 
 enum ErrorParsingResult {
   METHOD_ONLY,
-  METHOD_AND_PFE,
+  METHOD_AND_BFE,
   NONE,
 };
 
@@ -129,6 +134,7 @@
 
  protected:
   std::string name_;
+  std::string uma_ioerror_base_name_;
   bool make_backup_;
 
  private:
diff --git a/third_party/leveldatabase/env_chromium_unittest.cc b/third_party/leveldatabase/env_chromium_unittest.cc
index 6de36c21..d0107b2 100644
--- a/third_party/leveldatabase/env_chromium_unittest.cc
+++ b/third_party/leveldatabase/env_chromium_unittest.cc
@@ -43,7 +43,7 @@
   const Status s = MakeIOError("Somefile.txt", "message", in_method, fe);
   MethodID method;
   base::File::Error error;
-  EXPECT_EQ(leveldb_env::METHOD_AND_PFE,
+  EXPECT_EQ(leveldb_env::METHOD_AND_BFE,
             ParseMethodAndError(s, &method, &error));
   EXPECT_EQ(in_method, method);
   EXPECT_EQ(fe, error);
diff --git a/third_party/libjingle/README.chromium b/third_party/libjingle/README.chromium
index 4d5d3b5..9db0f14 100644
--- a/third_party/libjingle/README.chromium
+++ b/third_party/libjingle/README.chromium
@@ -1,7 +1,7 @@
 Name: libjingle
 URL: http://code.google.com/p/webrtc/
 Version: unknown
-Revision: 8836
+Revision: 8869
 License: BSD
 License File: source/talk/COPYING
 Security Critical: yes
diff --git a/third_party/libjingle/libjingle.gyp b/third_party/libjingle/libjingle.gyp
index a35931d..b9f9c6b 100644
--- a/third_party/libjingle/libjingle.gyp
+++ b/third_party/libjingle/libjingle.gyp
@@ -11,7 +11,6 @@
     'libjingle_additional_deps%': [],
     'libjingle_peerconnection_additional_deps%': [],
     'libjingle_source%': "source",
-    'libpeer_target_type%': 'static_library',
     'webrtc_p2p': "../webrtc/p2p",
     'webrtc_xmpp': "../webrtc/libjingle/xmpp",
   },
@@ -20,18 +19,19 @@
   # :jingle_all_dependent_configs in the GN build.
   'target_defaults': {
     'defines': [
+      'ENABLE_EXTERNAL_AUTH',
       'EXPAT_RELATIVE_PATH',
       'FEATURE_ENABLE_SSL',
       'GTEST_RELATIVE_PATH',
       'HAVE_SRTP',
       'HAVE_WEBRTC_VIDEO',
       'HAVE_WEBRTC_VOICE',
+      'LIBPEERCONNECTION_LIB=1',
       'LOGGING_INSIDE_WEBRTC',
       'NO_MAIN_THREAD_WRAPPING',
       'NO_SOUND_SYSTEM',
       'SRTP_RELATIVE_PATH',
       'USE_WEBRTC_DEV_BRANCH',
-      'ENABLE_EXTERNAL_AUTH',
       'WEBRTC_CHROMIUM_BUILD',
     ],
     'configurations': {
@@ -174,9 +174,6 @@
       ],
     },
     'conditions': [
-      ['"<(libpeer_target_type)"=="static_library"', {
-        'defines': [ 'LIBPEERCONNECTION_LIB=1' ],
-      }],
       ['use_openssl==1', {
         'defines': [
           'SSL_USE_OPENSSL',
@@ -331,11 +328,7 @@
           'target_name': 'libjingle_webrtc_common',
           'type': 'static_library',
           'all_dependent_settings': {
-            'conditions': [
-              ['"<(libpeer_target_type)"=="static_library"', {
-                'defines': [ 'LIBPEERCONNECTION_LIB=1' ],
-              }],
-            ],
+            'defines': [ 'LIBPEERCONNECTION_LIB=1' ],
           },
           'sources': [
             'overrides/talk/media/webrtc/webrtcexport.h',
@@ -559,20 +552,14 @@
             'overrides/init_webrtc.h',
           ],
           'dependencies': [
+            '<(DEPTH)/third_party/webrtc/modules/modules.gyp:audio_processing',
             'libjingle_webrtc_common',
           ],
-          'conditions': [
-            ['libpeer_target_type=="static_library"', {
-              'dependencies': [
-                '<(DEPTH)/third_party/webrtc/modules/modules.gyp:audio_processing',
-              ],
-            }],
-          ],
         },
         {
           # GN version: //third_party/libjingle:libpeerconnection
           'target_name': 'libpeerconnection',
-          'type': '<(libpeer_target_type)',
+          'type': 'static_library',
           'sources': [
             # Note: sources list duplicated in GN build.
             '<(libjingle_source)/talk/media/webrtc/simulcast.cc',
@@ -593,52 +580,9 @@
             'libjingle_webrtc_common',
           ],
           'conditions': [
-            ['libpeer_target_type!="static_library"', {
-              'sources': [
-                'overrides/initialize_module.cc',
-              ],
-              'conditions': [
-                ['OS!="mac" and OS!="android"', {
-                  'sources': [
-                    'overrides/allocator_shim/allocator_proxy.cc',
-                  ],
-                }],
-              ],
-            }],
-            ['"<(libpeer_target_type)"!="static_library"', {
-              # Used to control symbol export/import.
-              'defines': [ 'LIBPEERCONNECTION_IMPLEMENTATION=1' ],
-            }],
-            ['OS=="win" and "<(libpeer_target_type)"!="static_library"', {
-              'link_settings': {
-                'libraries': [
-                  '-lsecur32.lib',
-                  '-lcrypt32.lib',
-                  '-liphlpapi.lib',
-                ],
-              },
-            }],
-            ['OS!="win" and "<(libpeer_target_type)"!="static_library"', {
-              'cflags': [
-                # For compatibility with how we export symbols from this
-                # target on Windows.  This also prevents the linker from
-                # picking up symbols from this target that should be linked
-                # in from other libjingle libs.
-                '-fvisibility=hidden',
-              ],
-            }],
-            ['OS=="mac" and libpeer_target_type!="static_library"', {
-              'product_name': 'libpeerconnection',
-            }],
-            ['OS=="android" and "<(libpeer_target_type)"=="static_library"', {
+            ['OS=="android"', {
               'standalone_static_library': 1,
             }],
-            ['OS=="linux" and libpeer_target_type!="static_library"', {
-              # The installer and various tools depend on finding the .so
-              # in this directory and not lib.target as will otherwise be
-              # the case with make builds.
-              'product_dir': '<(PRODUCT_DIR)/lib',
-            }],
           ],
         },  # target libpeerconnection
       ],
diff --git a/third_party/mojo/src/mojo/edk/embedder/platform_channel_utils_posix.h b/third_party/mojo/src/mojo/edk/embedder/platform_channel_utils_posix.h
index 34efcad..3073632 100644
--- a/third_party/mojo/src/mojo/edk/embedder/platform_channel_utils_posix.h
+++ b/third_party/mojo/src/mojo/edk/embedder/platform_channel_utils_posix.h
@@ -21,9 +21,9 @@
 
 // The maximum number of handles that can be sent "at once" using
 // |PlatformChannelSendmsgWithHandles()|.
-// TODO(vtl): This number is taken from ipc/file_descriptor_set_posix.h:
-// |FileDescriptorSet::kMaxDescriptorsPerMessage|. Where does it come from?
-const size_t kPlatformChannelMaxNumHandles = 7;
+// TODO(vtl): This number is taken from ipc/ipc_message_attachment_set.h:
+// |IPC::MessageAttachmentSet::kMaxDescriptorsPerMessage|.
+const size_t kPlatformChannelMaxNumHandles = 128;
 
 // Use these to write to a socket created using |PlatformChannelPair| (or
 // equivalent). These are like |write()| and |writev()|, but handle |EINTR| and
diff --git a/third_party/mojo/src/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/third_party/mojo/src/mojo/edk/system/multiprocess_message_pipe_unittest.cc
index af3b3bb..dcdc500 100644
--- a/third_party/mojo/src/mojo/edk/system/multiprocess_message_pipe_unittest.cc
+++ b/third_party/mojo/src/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -409,7 +409,7 @@
   std::string read_buffer(100, '\0');
   uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
   DispatcherVector dispatchers;
-  uint32_t num_dispatchers = 30;  // Maximum number to receive.
+  uint32_t num_dispatchers = 255;  // Maximum number to receive.
   CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]),
                            MakeUserPointer(&num_bytes), &dispatchers,
                            &num_dispatchers, MOJO_READ_MESSAGE_FLAG_NONE),
@@ -512,7 +512,7 @@
 #if defined(OS_POSIX) && !defined(OS_ANDROID)
 INSTANTIATE_TEST_CASE_P(PipeCount,
                         MultiprocessMessagePipeTestWithPipeCount,
-                        testing::Values(1u, 10u, 25u));
+                        testing::Values(1u, 128u, 255u));
 #endif
 
 }  // namespace
diff --git a/third_party/mojo/src/mojo/public/VERSION b/third_party/mojo/src/mojo/public/VERSION
index 627bf07..6979599 100644
--- a/third_party/mojo/src/mojo/public/VERSION
+++ b/third_party/mojo/src/mojo/public/VERSION
@@ -1 +1 @@
-7214b7ec7d27563b2666afad86cf1c5895c56c18
\ No newline at end of file
+912f52f69dadbe1e3cf9576f26731863770bcfc3
\ No newline at end of file
diff --git a/third_party/mojo/src/mojo/public/c/gles2/chromium_miscellaneous.h b/third_party/mojo/src/mojo/public/c/gles2/chromium_miscellaneous.h
new file mode 100644
index 0000000..e7c5aeea
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/c/gles2/chromium_miscellaneous.h
@@ -0,0 +1,30 @@
+// 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 MOJO_PUBLIC_C_GLES2_CHROMIUM_MISCELLANEOUS_H_
+#define MOJO_PUBLIC_C_GLES2_CHROMIUM_MISCELLANEOUS_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+#include <GLES2/gl2.h>
+
+#include "mojo/public/c/gles2/gles2_export.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_miscellaneous_autogen.h"
+#undef VISIT_GL_CALL
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_GLES2_CHROMIUM_MISCELLANEOUS_H_
diff --git a/third_party/mojo/src/mojo/public/c/gles2/chromium_sub_image.h b/third_party/mojo/src/mojo/public/c/gles2/chromium_sub_image.h
new file mode 100644
index 0000000..8d9ef8a1
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/c/gles2/chromium_sub_image.h
@@ -0,0 +1,30 @@
+// 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 MOJO_PUBLIC_C_GLES2_CHROMIUM_SUB_IMAGE_H_
+#define MOJO_PUBLIC_C_GLES2_CHROMIUM_SUB_IMAGE_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+#include <GLES2/gl2.h>
+
+#include "mojo/public/c/gles2/gles2_export.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sub_image_autogen.h"
+#undef VISIT_GL_CALL
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_GLES2_CHROMIUM_SUBIMAGE_H_
diff --git a/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_chromium_miscellaneous_autogen.h b/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_chromium_miscellaneous_autogen.h
new file mode 100644
index 0000000..ff7dabe
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_chromium_miscellaneous_autogen.h
@@ -0,0 +1,11 @@
+// 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.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+//    clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+VISIT_GL_CALL(ShallowFlushCHROMIUM, void, (), ())
diff --git a/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_chromium_sub_image_autogen.h b/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_chromium_sub_image_autogen.h
new file mode 100644
index 0000000..c10f29f
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_chromium_sub_image_autogen.h
@@ -0,0 +1,24 @@
+// 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.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+//    clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+VISIT_GL_CALL(
+    MapTexSubImage2DCHROMIUM,
+    void*,
+    (GLenum target,
+     GLint level,
+     GLint xoffset,
+     GLint yoffset,
+     GLsizei width,
+     GLsizei height,
+     GLenum format,
+     GLenum type,
+     GLenum access),
+    (target, level, xoffset, yoffset, width, height, format, type, access))
+VISIT_GL_CALL(UnmapTexSubImage2DCHROMIUM, void, (const void* mem), (mem))
diff --git a/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_occlusion_query_ext_autogen.h b/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_occlusion_query_ext_autogen.h
new file mode 100644
index 0000000..b1ccc42
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/c/gles2/gles2_call_visitor_occlusion_query_ext_autogen.h
@@ -0,0 +1,26 @@
+// 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.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+//    clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+VISIT_GL_CALL(GenQueriesEXT, void, (GLsizei n, GLuint* queries), (n, queries))
+VISIT_GL_CALL(DeleteQueriesEXT,
+              void,
+              (GLsizei n, const GLuint* queries),
+              (n, queries))
+VISIT_GL_CALL(IsQueryEXT, GLboolean, (GLuint id), (id))
+VISIT_GL_CALL(BeginQueryEXT, void, (GLenum target, GLuint id), (target, id))
+VISIT_GL_CALL(EndQueryEXT, void, (GLenum target), (target))
+VISIT_GL_CALL(GetQueryivEXT,
+              void,
+              (GLenum target, GLenum pname, GLint* params),
+              (target, pname, params))
+VISIT_GL_CALL(GetQueryObjectuivEXT,
+              void,
+              (GLuint id, GLenum pname, GLuint* params),
+              (id, pname, params))
diff --git a/third_party/mojo/src/mojo/public/c/gles2/occlusion_query_ext.h b/third_party/mojo/src/mojo/public/c/gles2/occlusion_query_ext.h
new file mode 100644
index 0000000..5922077
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/c/gles2/occlusion_query_ext.h
@@ -0,0 +1,30 @@
+// 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 MOJO_PUBLIC_C_GLES2_OCCLUSION_QUERY_EXT_H_
+#define MOJO_PUBLIC_C_GLES2_OCCLUSION_QUERY_EXT_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+#include <GLES2/gl2.h>
+
+#include "mojo/public/c/gles2/gles2_export.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_occlusion_query_ext_autogen.h"
+#undef VISIT_GL_CALL
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_GLES2_OCCLUSION_QUERY_EXT_H_
diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/binding.h b/third_party/mojo/src/mojo/public/cpp/bindings/binding.h
index a982bdbd..05a162e 100644
--- a/third_party/mojo/src/mojo/public/cpp/bindings/binding.h
+++ b/third_party/mojo/src/mojo/public/cpp/bindings/binding.h
@@ -50,11 +50,11 @@
 // waiting for calls to arrive. Normally it is fine to use the default waiter.
 // However, the caller may provide their own implementation if needed. The
 // |Binding| will not take ownership of the waiter, and the waiter must outlive
-// the |Binding|.
-//
-// TODO(ggowan): Find out under what circumstances the caller may need to
-// provide their own implementation of MojoAsyncWaiter, and then describe those
-// circumstances.
+// the |Binding|. The provided waiter must be able to signal the implementation
+// which generally means it needs to be able to schedule work on the thread the
+// implementation runs on. If writing library code that has to work on different
+// types of threads callers may need to provide different waiter
+// implementations.
 template <typename Interface>
 class Binding : public ErrorHandler {
  public:
@@ -106,7 +106,7 @@
   }
 
   // Completes a binding that was constructed with only an interface
-  // implementation.  Takes ownership of |handle| and binds it to the previously
+  // implementation. Takes ownership of |handle| and binds it to the previously
   // specified implementation. See class comment for definition of |waiter|.
   void Bind(
       ScopedMessagePipeHandle handle,
diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/BUILD.gn b/third_party/mojo/src/mojo/public/cpp/bindings/tests/BUILD.gn
index efd0b38..6d09151 100644
--- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/BUILD.gn
+++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -9,6 +9,7 @@
 
   sources = [
     "array_unittest.cc",
+    "binding_unittest.cc",
     "bounds_checker_unittest.cc",
     "buffer_unittest.cc",
     "callback_unittest.cc",
diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/binding_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/binding_unittest.cc
new file mode 100644
index 0000000..2e38f13
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/binding_unittest.cc
@@ -0,0 +1,116 @@
+// 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 "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class ServiceImpl : public sample::Service {
+ public:
+  ServiceImpl() {}
+  ~ServiceImpl() override {}
+
+ private:
+  // sample::Service implementation
+  void Frobinate(sample::FooPtr foo,
+                 BazOptions options,
+                 sample::PortPtr port,
+                 const FrobinateCallback& callback) override {
+    callback.Run(1);
+  }
+  void GetPort(InterfaceRequest<sample::Port> port) override {}
+};
+
+class RecordingErrorHandler : public ErrorHandler {
+ public:
+  RecordingErrorHandler() : error_(false) {}
+  ~RecordingErrorHandler() override {}
+
+  bool encountered_error() const { return error_; }
+
+ private:
+  // ErrorHandler implementation.
+  void OnConnectionError() override { error_ = true; }
+
+  bool error_;
+};
+
+class BindingTest : public testing::Test {
+ public:
+  BindingTest() {}
+  ~BindingTest() override {}
+
+ protected:
+  RecordingErrorHandler handler_;
+  ServiceImpl impl_;
+  Environment env_;
+  RunLoop loop_;
+};
+
+// Tests that destroying a mojo::Binding closes the bound message pipe handle.
+TEST_F(BindingTest, DestroyClosesMessagePipe) {
+  sample::ServicePtr ptr;
+  auto request = GetProxy(&ptr);
+  ptr.set_error_handler(&handler_);
+  bool called = false;
+  auto called_cb = [&called](int32_t result) { called = true; };
+  {
+    Binding<sample::Service> binding(&impl_, request.Pass());
+    ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+                   called_cb);
+    loop_.RunUntilIdle();
+    EXPECT_TRUE(called);
+    EXPECT_FALSE(handler_.encountered_error());
+  }
+  // Now that the Binding is out of scope we should detect an error on the other
+  // end of the pipe.
+  loop_.RunUntilIdle();
+  EXPECT_TRUE(handler_.encountered_error());
+
+  // And calls should fail.
+  called = false;
+  ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+                 called_cb);
+  loop_.RunUntilIdle();
+  EXPECT_FALSE(called);
+}
+
+// Tests that explicitly calling Unbind followed by rebinding works.
+TEST_F(BindingTest, Unbind) {
+  sample::ServicePtr ptr;
+  Binding<sample::Service> binding(&impl_, GetProxy(&ptr));
+
+  bool called = false;
+  auto called_cb = [&called](int32_t result) { called = true; };
+  ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+                 called_cb);
+  loop_.RunUntilIdle();
+  EXPECT_TRUE(called);
+
+  called = false;
+  auto request = binding.Unbind();
+  EXPECT_FALSE(binding.is_bound());
+  // All calls should fail when not bound...
+  ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+                 called_cb);
+  loop_.RunUntilIdle();
+  EXPECT_FALSE(called);
+
+  called = false;
+  binding.Bind(request.Pass());
+  EXPECT_TRUE(binding.is_bound());
+  // ...and should succeed again when the rebound.
+  ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+                 called_cb);
+  loop_.RunUntilIdle();
+  EXPECT_TRUE(called);
+}
+
+}  // namespace
+}  // mojo
diff --git a/third_party/mojo/src/mojo/public/dart/BUILD.gn b/third_party/mojo/src/mojo/public/dart/BUILD.gn
new file mode 100644
index 0000000..711eee3
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/dart/BUILD.gn
@@ -0,0 +1,33 @@
+import("../mojo_sdk.gni")
+import("rules.gni")
+
+dart_mojo_sdk_sources = [
+  "application.dart",
+  "bindings.dart",
+  "core.dart",
+  "src/application_connection.dart",
+  "src/application.dart",
+  "src/buffer.dart",
+  "src/codec.dart",
+  "src/data_pipe.dart",
+  "src/drain_data.dart",
+  "src/event_stream.dart",
+  "src/handle.dart",
+  "src/message.dart",
+  "src/message_pipe.dart",
+  "src/proxy.dart",
+  "src/struct.dart",
+  "src/stub.dart",
+  "src/types.dart",
+]
+
+dart_package("dart") {
+  # This base dir ensures that Dart's Mojo SDK can be imported with, e.g.,
+  # import 'package:mojo/public/dart/core.dart' even when the Mojo SDK lives
+  # somewhere else in the source tree.
+  base_dir = rebase_path("../../$mojo_root", ".", ".")
+  sources = dart_mojo_sdk_sources
+  deps = [
+    "../interfaces/application",
+  ]
+}
diff --git a/third_party/mojo/src/mojo/public/dart/application.dart b/third_party/mojo/src/mojo/public/dart/application.dart
index ed3bf24..13f7c0b 100644
--- a/third_party/mojo/src/mojo/public/dart/application.dart
+++ b/third_party/mojo/src/mojo/public/dart/application.dart
@@ -7,12 +7,14 @@
 import 'dart:async';
 import 'dart:convert';
 import 'dart:typed_data';
-import 'dart:mojo.bindings' as bindings;
-import 'dart:mojo.core' as core;
 
-import 'package:mojo/public/interfaces/application/application.mojom.dart' as application_mojom;
+import 'package:mojo/public/dart/bindings.dart' as bindings;
+import 'package:mojo/public/dart/core.dart' as core;
+import 'package:mojo/public/interfaces/application/application.mojom.dart'
+    as application_mojom;
 import 'package:mojo/public/interfaces/application/service_provider.mojom.dart';
-import 'package:mojo/public/interfaces/application/shell.mojom.dart' as shell_mojom;
+import 'package:mojo/public/interfaces/application/shell.mojom.dart'
+    as shell_mojom;
 
 part 'src/application.dart';
 part 'src/application_connection.dart';
diff --git a/third_party/mojo/src/mojo/public/dart/bindings.dart b/third_party/mojo/src/mojo/public/dart/bindings.dart
index 642744f..e68afec 100644
--- a/third_party/mojo/src/mojo/public/dart/bindings.dart
+++ b/third_party/mojo/src/mojo/public/dart/bindings.dart
@@ -7,7 +7,8 @@
 import 'dart:async';
 import 'dart:convert';
 import 'dart:typed_data';
-import 'dart:mojo.core' as core;
+
+import 'package:mojo/public/dart/core.dart' as core;
 
 part 'src/codec.dart';
 part 'src/message.dart';
diff --git a/third_party/mojo/src/mojo/public/dart/core.dart b/third_party/mojo/src/mojo/public/dart/core.dart
index 6cdbe26..e72da3f 100644
--- a/third_party/mojo/src/mojo/public/dart/core.dart
+++ b/third_party/mojo/src/mojo/public/dart/core.dart
@@ -7,6 +7,7 @@
 import 'dart:async';
 import 'dart:collection';
 import 'dart:isolate';
+import 'dart:mojo.internal';
 import 'dart:typed_data';
 
 part 'src/buffer.dart';
@@ -14,7 +15,5 @@
 part 'src/drain_data.dart';
 part 'src/event_stream.dart';
 part 'src/handle.dart';
-part 'src/handle_watcher.dart';
 part 'src/message_pipe.dart';
-part 'src/timer_queue.dart';
 part 'src/types.dart';
diff --git a/third_party/mojo/src/mojo/public/dart/internal.dart b/third_party/mojo/src/mojo/public/dart/internal.dart
new file mode 100644
index 0000000..4239df2
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/dart/internal.dart
@@ -0,0 +1,14 @@
+// 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.
+
+library internal;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:isolate';
+import 'dart:typed_data';
+
+part 'src/handle_watcher.dart';
+part 'src/natives.dart';
+part 'src/timer_queue.dart';
diff --git a/third_party/mojo/src/mojo/public/dart/rules.gni b/third_party/mojo/src/mojo/public/dart/rules.gni
index 56c502a..120a738 100644
--- a/third_party/mojo/src/mojo/public/dart/rules.gni
+++ b/third_party/mojo/src/mojo/public/dart/rules.gni
@@ -35,6 +35,9 @@
 
     rebase_base_dir =
         rebase_path(get_label_info(":$target_name", "dir"), root_build_dir)
+    if (defined(invoker.base_dir)) {
+      rebase_base_dir = invoker.base_dir
+    }
     rebase_inputs = rebase_path(inputs, root_build_dir)
     rebase_zip_inputs = rebase_path(zip_inputs, root_build_dir)
     rebase_output = rebase_path(output, root_build_dir)
@@ -74,50 +77,9 @@
       rebase_path(package_output),
       rebase_path("$target_gen_dir/${target_name}_analyze.stamp"),
       "--no-hints",
-
-      # The dart bindings refer to the autogenerated application interface in
-      # their source code; since that code is then packaged into the image, we
-      # need to manually resolve these package urls to the autogenerated code.
-      "--url-mapping=package:mojo/public/interfaces/application/application.mojom.dart,/" + rebase_path(
-              "mojo/public/interfaces/application/application.mojom.dart",
-              "/",
-              root_gen_dir + mojo_root),
-      "--url-mapping=package:mojo/public/interfaces/application/service_provider.mojom.dart,/" + rebase_path(
-              "mojo/public/interfaces/application/service_provider.mojom.dart",
-              "/",
-              root_gen_dir + mojo_root),
-      "--url-mapping=package:mojo/public/interfaces/application/shell.mojom.dart,/" + rebase_path(
-              "mojo/public/interfaces/application/shell.mojom.dart",
-              "/",
-              root_gen_dir + mojo_root),
-
-      # If you are trying to use the mojo sdk in another repository, you'll
-      # probably also need these url mapping overrides. Users are currently
-      # referring to these files with these third_party/mojo/ URLs.
-      #
-      # (This means that we're referring to the same files through two different
-      # URLs, depending on which part of the system we're referring to them from.
-      # This is not a good idea.)
-      "--url-mapping=package:third_party/mojo/src/mojo/public/interfaces/application/application.mojom.dart,/" + rebase_path(
-              "mojo/public/interfaces/application/application.mojom.dart",
-              "/",
-              root_gen_dir + mojo_root),
-      "--url-mapping=package:third_party/mojo/src/mojo/public/interfaces/application/service_provider.mojom.dart,/" + rebase_path(
-              "mojo/public/interfaces/application/service_provider.mojom.dart",
-              "/",
-              root_gen_dir + mojo_root),
-      "--url-mapping=package:third_party/mojo/src/mojo/public/interfaces/application/shell.mojom.dart,/" + rebase_path(
-              "mojo/public/interfaces/application/shell.mojom.dart",
-              "/",
-              root_gen_dir + mojo_root),
     ]
 
-    # Because the standard 'mojo:*' packages depend on the application mojom
-    # interface, we must depend on their existence, even though we don't depend
-    # directly on them.
-    deps = [
-      rebase_path("mojo/public/interfaces/application", ".", mojo_root),
-    ]
+    deps = []
     if (defined(invoker.deps)) {
       deps += invoker.deps
     }
diff --git a/third_party/mojo/src/mojo/public/dart/src/buffer.dart b/third_party/mojo/src/mojo/public/dart/src/buffer.dart
index 4467315..5e153be 100644
--- a/third_party/mojo/src/mojo/public/dart/src/buffer.dart
+++ b/third_party/mojo/src/mojo/public/dart/src/buffer.dart
@@ -4,25 +4,6 @@
 
 part of core;
 
-class _MojoSharedBufferNatives {
-  static List Create(int numBytes, int flags)
-      native "MojoSharedBuffer_Create";
-
-  static List Duplicate(int bufferHandle, int flags)
-      native "MojoSharedBuffer_Duplicate";
-
-  static List Map(MojoSharedBuffer buffer,
-                  int bufferHandle,
-                  int offset,
-                  int numBytes,
-                  int flags)
-      native "MojoSharedBuffer_Map";
-
-  static int Unmap(ByteData buffer)
-      native "MojoSharedBuffer_Unmap";
-}
-
-
 class MojoSharedBuffer {
   static const int CREATE_FLAG_NONE = 0;
   static const int DUPLICATE_FLAG_NONE = 0;
@@ -32,11 +13,11 @@
   MojoResult status;
   ByteData mapping;
 
-  MojoSharedBuffer(
-      this.handle, [this.status = MojoResult.OK, this.mapping = null]);
+  MojoSharedBuffer(this.handle,
+      [this.status = MojoResult.OK, this.mapping = null]);
 
   factory MojoSharedBuffer.create(int numBytes, [int flags = 0]) {
-    List result = _MojoSharedBufferNatives.Create(numBytes, flags);
+    List result = MojoSharedBufferNatives.Create(numBytes, flags);
     if (result == null) {
       return null;
     }
@@ -52,13 +33,13 @@
   }
 
   factory MojoSharedBuffer.duplicate(MojoSharedBuffer msb, [int flags = 0]) {
-    List result = _MojoSharedBufferNatives.Duplicate(msb.handle.h, flags);
+    List result = MojoSharedBufferNatives.Duplicate(msb.handle.h, flags);
     if (result == null) {
       return null;
     }
     assert((result is List) && (result.length == 2));
     var r = new MojoResult(result[0]);
-    if(!r.isOk) {
+    if (!r.isOk) {
       return null;
     }
 
@@ -83,8 +64,8 @@
       status = MojoResult.INVALID_ARGUMENT;
       return status;
     }
-    List result = _MojoSharedBufferNatives.Map(
-        this, handle.h, offset, numBytes, flags);
+    List result =
+        MojoSharedBufferNatives.Map(this, handle.h, offset, numBytes, flags);
     if (result == null) {
       status = MojoResult.INVALID_ARGUMENT;
       return status;
@@ -96,7 +77,7 @@
   }
 
   MojoResult unmap() {
-    int r = _MojoSharedBufferNatives.Unmap(mapping);
+    int r = MojoSharedBufferNatives.Unmap(mapping);
     status = new MojoResult(r);
     mapping = null;
     return status;
diff --git a/third_party/mojo/src/mojo/public/dart/src/data_pipe.dart b/third_party/mojo/src/mojo/public/dart/src/data_pipe.dart
index 7f1666d..884bbcb 100644
--- a/third_party/mojo/src/mojo/public/dart/src/data_pipe.dart
+++ b/third_party/mojo/src/mojo/public/dart/src/data_pipe.dart
@@ -4,29 +4,6 @@
 
 part of core;
 
-class _MojoDataPipeNatives {
-  static List MojoCreateDataPipe(int elementBytes, int capacityBytes,
-      int flags) native "MojoDataPipe_Create";
-
-  static List MojoWriteData(int handle, ByteData data, int numBytes,
-      int flags) native "MojoDataPipe_WriteData";
-
-  static List MojoBeginWriteData(int handle, int bufferBytes,
-      int flags) native "MojoDataPipe_BeginWriteData";
-
-  static int MojoEndWriteData(
-      int handle, int bytesWritten) native "MojoDataPipe_EndWriteData";
-
-  static List MojoReadData(int handle, ByteData data, int numBytes,
-      int flags) native "MojoDataPipe_ReadData";
-
-  static List MojoBeginReadData(int handle, int bufferBytes,
-      int flags) native "MojoDataPipe_BeginReadData";
-
-  static int MojoEndReadData(
-      int handle, int bytesRead) native "MojoDataPipe_EndReadData";
-}
-
 class MojoDataPipeProducer {
   static const int FLAG_NONE = 0;
   static const int FLAG_ALL_OR_NONE = 1 << 0;
@@ -45,8 +22,8 @@
     }
 
     int data_numBytes = (numBytes == -1) ? data.lengthInBytes : numBytes;
-    List result = _MojoDataPipeNatives.MojoWriteData(
-        handle.h, data, data_numBytes, flags);
+    List result =
+        MojoDataPipeNatives.MojoWriteData(handle.h, data, data_numBytes, flags);
     if (result == null) {
       status = MojoResult.INVALID_ARGUMENT;
       return 0;
@@ -64,7 +41,7 @@
     }
 
     List result =
-        _MojoDataPipeNatives.MojoBeginWriteData(handle.h, bufferBytes, flags);
+        MojoDataPipeNatives.MojoBeginWriteData(handle.h, bufferBytes, flags);
     if (result == null) {
       status = MojoResult.INVALID_ARGUMENT;
       return null;
@@ -80,7 +57,7 @@
       status = MojoResult.INVALID_ARGUMENT;
       return status;
     }
-    int result = _MojoDataPipeNatives.MojoEndWriteData(handle.h, bytesWritten);
+    int result = MojoDataPipeNatives.MojoEndWriteData(handle.h, bytesWritten);
     status = new MojoResult(result);
     return status;
   }
@@ -110,7 +87,7 @@
 
     int data_numBytes = (numBytes == -1) ? data.lengthInBytes : numBytes;
     List result =
-        _MojoDataPipeNatives.MojoReadData(handle.h, data, data_numBytes, flags);
+        MojoDataPipeNatives.MojoReadData(handle.h, data, data_numBytes, flags);
     if (result == null) {
       status = MojoResult.INVALID_ARGUMENT;
       return 0;
@@ -127,7 +104,7 @@
     }
 
     List result =
-        _MojoDataPipeNatives.MojoBeginReadData(handle.h, bufferBytes, flags);
+        MojoDataPipeNatives.MojoBeginReadData(handle.h, bufferBytes, flags);
     if (result == null) {
       status = MojoResult.INVALID_ARGUMENT;
       return null;
@@ -143,7 +120,7 @@
       status = MojoResult.INVALID_ARGUMENT;
       return status;
     }
-    int result = _MojoDataPipeNatives.MojoEndReadData(handle.h, bytesRead);
+    int result = MojoDataPipeNatives.MojoEndReadData(handle.h, bytesRead);
     status = new MojoResult(result);
     return status;
   }
@@ -171,7 +148,7 @@
 
   factory MojoDataPipe([int elementBytes = DEFAULT_ELEMENT_SIZE,
       int capacityBytes = DEFAULT_CAPACITY, int flags = FLAG_NONE]) {
-    List result = _MojoDataPipeNatives.MojoCreateDataPipe(
+    List result = MojoDataPipeNatives.MojoCreateDataPipe(
         elementBytes, capacityBytes, flags);
     if (result == null) {
       return null;
diff --git a/third_party/mojo/src/mojo/public/dart/src/event_stream.dart b/third_party/mojo/src/mojo/public/dart/src/event_stream.dart
index 645334e..712d0259 100644
--- a/third_party/mojo/src/mojo/public/dart/src/event_stream.dart
+++ b/third_party/mojo/src/mojo/public/dart/src/event_stream.dart
@@ -64,7 +64,8 @@
     _controller.addStream(_receivePort).whenComplete(_controller.close);
 
     if (_signals != MojoHandleSignals.NONE) {
-      var res = MojoHandleWatcher.add(_handle, _sendPort, _signals.value);
+      var res = new MojoResult(
+          MojoHandleWatcher.add(_handle.h, _sendPort, _signals.value));
       if (!res.isOk) {
         throw "MojoHandleWatcher add failed: $res";
       }
@@ -78,7 +79,8 @@
   void enableSignals(MojoHandleSignals signals) {
     _signals = signals;
     if (_isListening) {
-      var res = MojoHandleWatcher.add(_handle, _sendPort, signals.value);
+      var res = new MojoResult(
+          MojoHandleWatcher.add(_handle.h, _sendPort, signals.value));
       if (!res.isOk) {
         throw "MojoHandleWatcher add failed: $res";
       }
@@ -92,11 +94,13 @@
 
   Future _handleWatcherClose() {
     assert(_handle != null);
-    return MojoHandleWatcher.close(_handle, wait: true).then((_) {
+    assert(MojoHandle._removeUnclosedHandle(_handle));
+    return MojoHandleWatcher.close(_handle.h, wait: true).then((r) {
       if (_receivePort != null) {
         _receivePort.close();
         _receivePort = null;
       }
+      return new MojoResult(r);
     });
   }
 
@@ -118,12 +122,13 @@
 
   void _onPauseStateChange() {
     if (_controller.isPaused) {
-      var res = MojoHandleWatcher.remove(_handle);
+      var res = new MojoResult(MojoHandleWatcher.remove(_handle.h));
       if (!res.isOk) {
         throw "MojoHandleWatcher add failed: $res";
       }
     } else {
-      var res = MojoHandleWatcher.add(_handle, _sendPort, _signals.value);
+      var res = new MojoResult(
+          MojoHandleWatcher.add(_handle.h, _sendPort, _signals.value));
       if (!res.isOk) {
         throw "MojoHandleWatcher add failed: $res";
       }
@@ -200,21 +205,24 @@
         assert(_eventStream.readyWrite);
         handleWrite();
       }
-      if (_isOpen) {
+      if (!signalsReceived.isPeerClosed) {
         _eventStream.enableSignals(signalsWatched);
       }
       _isInHandler = false;
       if (signalsReceived.isPeerClosed) {
-        if (onError != null) {
-          onError();
-        }
-        close();
+        // nodefer is true here because there is no need to wait to close until
+        // outstanding messages are sent. The other side is gone.
+        close(nodefer: true).then((_) {
+          if (onError != null) {
+            onError();
+          }
+        });
       }
     }, onDone: close);
     return subscription;
   }
 
-  Future close() {
+  Future close({bool nodefer: false}) {
     var result;
     _isOpen = false;
     _endpoint = null;
diff --git a/third_party/mojo/src/mojo/public/dart/src/handle.dart b/third_party/mojo/src/mojo/public/dart/src/handle.dart
index 6b99ed8..e7ac3eb 100644
--- a/third_party/mojo/src/mojo/public/dart/src/handle.dart
+++ b/third_party/mojo/src/mojo/public/dart/src/handle.dart
@@ -4,15 +4,6 @@
 
 part of core;
 
-class _MojoHandleNatives {
-  static int register(MojoEventStream eventStream) native "MojoHandle_Register";
-  static int close(int handle) native "MojoHandle_Close";
-  static List wait(
-      int handle, int signals, int deadline) native "MojoHandle_Wait";
-  static List waitMany(List<int> handles, List<int> signals,
-      int deadline) native "MojoHandle_WaitMany";
-}
-
 class _HandleCreationRecord {
   final MojoHandle handle;
   final StackTrace stack;
@@ -36,7 +27,7 @@
 
   MojoResult close() {
     assert(_removeUnclosedHandle(this));
-    int result = _MojoHandleNatives.close(_h);
+    int result = MojoHandleNatives.close(_h);
     _h = INVALID;
     return new MojoResult(result);
   }
@@ -47,8 +38,11 @@
   }
 
   MojoWaitResult wait(int signals, int deadline) {
-    List result = _MojoHandleNatives.wait(h, signals, deadline);
-    return new MojoWaitResult(new MojoResult(result[0]), result[1]);
+    List result = MojoHandleNatives.wait(h, signals, deadline);
+    var state = result[1] != null
+        ? new MojoHandleSignalsState(result[1][0], result[1][1])
+        : null;
+    return new MojoWaitResult(new MojoResult(result[0]), state);
   }
 
   bool _ready(MojoHandleSignals signal) {
@@ -89,13 +83,16 @@
 
   static MojoWaitManyResult waitMany(
       List<int> handles, List<int> signals, int deadline) {
-    List result = _MojoHandleNatives.waitMany(handles, signals, deadline);
-    return new MojoWaitManyResult(
-        new MojoResult(result[0]), result[1], result[2]);
+    List result = MojoHandleNatives.waitMany(handles, signals, deadline);
+    List states = result[2] != null
+        ? result[2].map((l) => new MojoHandleSignalsState(l[0], l[1])).toList()
+        : null;
+    return new MojoWaitManyResult(new MojoResult(result[0]), result[1], states);
   }
 
   static MojoResult register(MojoEventStream eventStream) {
-    return new MojoResult(_MojoHandleNatives.register(eventStream));
+    return new MojoResult(
+        MojoHandleNatives.register(eventStream, eventStream._handle.h));
   }
 
   static HashMap<int, _HandleCreationRecord> _unclosedHandles = new HashMap();
diff --git a/third_party/mojo/src/mojo/public/dart/src/handle_watcher.dart b/third_party/mojo/src/mojo/public/dart/src/handle_watcher.dart
index 198a9f6..70cc297 100644
--- a/third_party/mojo/src/mojo/public/dart/src/handle_watcher.dart
+++ b/third_party/mojo/src/mojo/public/dart/src/handle_watcher.dart
@@ -2,17 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-part of core;
-
-class _MojoHandleWatcherNatives {
-  static int sendControlData(int controlHandle, int mojoHandle, SendPort port,
-      int data) native "MojoHandleWatcher_SendControlData";
-  static List recvControlData(
-      int controlHandle) native "MojoHandleWatcher_RecvControlData";
-  static int setControlHandle(
-      int controlHandle) native "MojoHandleWatcher_SetControlHandle";
-  static int getControlHandle() native "MojoHandleWatcher_GetControlHandle";
-}
+part of internal;
 
 // The MojoHandleWatcher sends a stream of events to application isolates that
 // register Mojo handles with it. Application isolates make the following calls:
@@ -36,14 +26,27 @@
   static const int TIMER = 3;
   static const int SHUTDOWN = 4;
 
+  static const int kMojoHandleInvalid = 0;
+  static const int kDeadlineIndefinite = -1;
+
+  static const int kMojoResultOk = 0;
+  static const int kMojoResultDeadlineExceeded = -4;
+  static const int kMojoResultFailedPrecondition = -9;
+
+  static const int kMojoSignalsReadable = (1 << 0);
+  static const int kMojoSignalsWritable = (1 << 1);
+  static const int kMojoSignalsPeerClosed = (1 << 2);
+  static const int kMojoSignalsAll =
+      kMojoSignalsReadable | kMojoSignalsWritable | kMojoSignalsPeerClosed;
+
   static int _encodeCommand(int cmd, [int signals = 0]) =>
-      (cmd << 3) | (signals & MojoHandleSignals.kAll);
+      (cmd << 3) | (signals & kMojoSignalsAll);
   static int _decodeCommand(int cmd) {
-    assert(MojoHandleSignals.kAll < 1 << 3);
+    assert(kMojoSignalsAll < 1 << 3);
     return cmd >> 3;
   }
-  static MojoHandleSignals _decodeSignals(int cmd) {
-    return new MojoHandleSignals(cmd & MojoHandleSignals.kAll);
+  static int _decodeSignals(int cmd) {
+    return cmd & kMojoSignalsAll;
   }
 
   // The Mojo handle over which control messages are sent.
@@ -66,10 +69,6 @@
   // A mapping from Mojo handles to their indices in _handles.
   Map<int, int> _handleIndices;
 
-  // Since we are not storing wrapped handles, a dummy handle for when we need
-  // a MojoHandle.
-  MojoHandle _tempHandle;
-
   // Priority queue of timers registered with the watcher.
   TimerQueue _timerQueue;
 
@@ -80,12 +79,11 @@
         _signals = new List<int>(),
         _handleIndices = new Map<int, int>(),
         _handleCount = 1,
-        _tempHandle = new MojoHandle(MojoHandle.INVALID),
         _timerQueue = new TimerQueue() {
     // Setup control handle.
     _handles.add(_controlHandle);
     _ports.add(null); // There is no port for the control handle.
-    _signals.add(MojoHandleSignals.kReadable);
+    _signals.add(kMojoSignalsReadable);
     _handleIndices[_controlHandle] = 0;
   }
 
@@ -93,22 +91,22 @@
     MojoHandleWatcher watcher = new MojoHandleWatcher(consumerHandle);
     while (!watcher._shutdown) {
       int deadline = watcher._processTimerDeadlines();
-      MojoWaitManyResult mwmr =
-          MojoHandle.waitMany(watcher._handles, watcher._signals, deadline);
-      if (mwmr.result.isOk && mwmr.index == 0) {
+      // mwmr[0]: result, mwmr[1]: index, mwmr[2]: list of signal states.
+      List mwmr = MojoHandleNatives.waitMany(
+          watcher._handles, watcher._signals, deadline);
+      if ((mwmr[0] == kMojoResultOk) && (mwmr[1] == 0)) {
         watcher._handleControlMessage();
-      } else if (mwmr.result.isOk && (mwmr.index > 0)) {
-        int handle = watcher._handles[mwmr.index];
+      } else if ((mwmr[0] == kMojoResultOk) && (mwmr[1] > 0)) {
+        int handle = watcher._handles[mwmr[1]];
 
         // Route event.
-        watcher._routeEvent(
-            mwmr.states[mwmr.index].satisfied_signals, mwmr.index);
+        watcher._routeEvent(mwmr[2][mwmr[1]][0], mwmr[1]);
         // Remove the handle from the list.
         watcher._removeHandle(handle);
-      } else if (!mwmr.result.isDeadlineExceeded) {
+      } else if (mwmr[0] != kMojoResultDeadlineExceeded) {
         // Some handle was closed, but not by us.
         // Find it and close it on our side.
-        watcher._pruneClosedHandles(mwmr.states);
+        watcher._pruneClosedHandles(mwmr[2]);
       }
     }
   }
@@ -118,7 +116,7 @@
   }
 
   void _handleControlMessage() {
-    List result = _MojoHandleWatcherNatives.recvControlData(_controlHandle);
+    List result = MojoHandleWatcherNatives.recvControlData(_controlHandle);
     // result[0] = mojo handle if any, or a timer deadline in milliseconds.
     // result[1] = SendPort if any.
     // result[2] = command << 2 | WRITABLE | READABLE
@@ -147,18 +145,18 @@
     }
   }
 
-  void _addHandle(int mojoHandle, SendPort port, MojoHandleSignals signals) {
+  void _addHandle(int mojoHandle, SendPort port, int signals) {
     int idx = _handleIndices[mojoHandle];
     if (idx == null) {
       _handles.add(mojoHandle);
       _ports.add(port);
-      _signals.add(signals.value);
+      _signals.add(signals);
       _handleIndices[mojoHandle] = _handleCount;
       _handleCount++;
     } else {
       assert(_ports[idx] == port);
       assert(_handles[idx] == mojoHandle);
-      _signals[idx] |= signals.value;
+      _signals[idx] |= signals;
     }
   }
 
@@ -200,21 +198,19 @@
       // has already been pruned. This happens when the app isolate has not yet
       // received the PEER_CLOSED event. The app isolate will not close the
       // handle, so we must do so here.
-      _tempHandle._set(mojoHandle);
-      _tempHandle.close();
+      MojoHandleNatives.close(mojoHandle);
       if (port != null) port.send(null); // Notify that close is done.
       return;
     }
     if (idx == 0) {
       throw "The control handle (idx = 0) cannot be closed.";
     }
-    _tempHandle._set(_handles[idx]);
-    _tempHandle.close();
+    MojoHandleNatives.close(_handles[idx]);
     if (port != null) port.send(null); // Notify that close is done.
     if (pruning) {
       // If this handle is being pruned, notify the application isolate
       // by sending MojoHandleSignals.PEER_CLOSED.
-      _ports[idx].send([_signals[idx], MojoHandleSignals.kPeerClosed]);
+      _ports[idx].send([_signals[idx], kMojoSignalsPeerClosed]);
     }
     _removeHandle(mojoHandle);
   }
@@ -229,28 +225,27 @@
     }
     return _timerQueue.hasTimer
         ? (_timerQueue.currentTimeout - now) * 1000
-        : MojoHandle.DEADLINE_INDEFINITE;
+        : kDeadlineIndefinite;
   }
 
   void _timer(SendPort port, int deadline) {
     _timerQueue.updateTimer(port, deadline);
   }
 
-  void _pruneClosedHandles(List<MojoHandleSignalsState> states) {
+  void _pruneClosedHandles(List<List<int>> states) {
     List<int> closed = new List();
     for (var i = 0; i < _handles.length; i++) {
       if (states != null) {
-        var signals = new MojoHandleSignals(states[i].satisfied_signals);
-        if (signals.isPeerClosed) {
+        int signals = states[i][0];
+        if ((signals & kMojoSignalsPeerClosed) != 0) {
           closed.add(_handles[i]);
         }
       } else {
-        _tempHandle._set(_handles[i]);
-        MojoWaitResult mwr = _tempHandle.wait(MojoHandleSignals.kAll, 0);
-        if ((!mwr.result.isOk) && (!mwr.result.isDeadlineExceeded)) {
+        List mwr = MojoHandleNatives.wait(_handles[i], kMojoSignalsAll, 0);
+        if ((mwr[0] != kMojoResultOk) &&
+            (mwr[0] != kMojoResultDeadlineExceeded)) {
           closed.add(_handles[i]);
         }
-        _tempHandle._set(MojoHandle.INVALID);
       }
     }
     for (var h in closed) {
@@ -262,38 +257,36 @@
 
   void _shutdownHandleWatcher(SendPort shutdownSendPort) {
     _shutdown = true;
-    _tempHandle._set(_controlHandle);
-    _tempHandle.close();
+    MojoHandleNatives.close(_controlHandle);
     shutdownSendPort.send(null);
   }
 
-  static MojoResult _sendControlData(
-      MojoHandle mojoHandle, SendPort port, int data) {
-    int controlHandle = _MojoHandleWatcherNatives.getControlHandle();
-    if (controlHandle == MojoHandle.INVALID) {
-      return MojoResult.FAILED_PRECONDITION;
+  static int _sendControlData(int rawHandle, SendPort port, int data) {
+    int controlHandle = MojoHandleWatcherNatives.getControlHandle();
+    if (controlHandle == kMojoHandleInvalid) {
+      return kMojoResultFailedPrecondition;
     }
 
-    int rawHandle = MojoHandle.INVALID;
-    if (mojoHandle != null) {
-      rawHandle = mojoHandle.h;
-    }
-    var result = _MojoHandleWatcherNatives.sendControlData(
+    var result = MojoHandleWatcherNatives.sendControlData(
         controlHandle, rawHandle, port, data);
-    return new MojoResult(result);
+    return result;
   }
 
   // Starts up the MojoHandleWatcher isolate. Should be called only once
   // per VM process.
   static Future<Isolate> _start() {
     // Make a control message pipe,
-    MojoMessagePipe pipe = new MojoMessagePipe();
-    int consumerHandle = pipe.endpoints[0].handle.h;
-    int producerHandle = pipe.endpoints[1].handle.h;
+    List pipeEndpoints = MojoMessagePipeNatives.MojoCreateMessagePipe(0);
+    assert(pipeEndpoints != null);
+    assert((pipeEndpoints is List) && (pipeEndpoints.length == 3));
+    assert(pipeEndpoints[0] == kMojoResultOk);
+
+    int consumerHandle = pipeEndpoints[1];
+    int producerHandle = pipeEndpoints[2];
 
     // Call setControlHandle with the other end.
-    assert(producerHandle != MojoHandle.INVALID);
-    _MojoHandleWatcherNatives.setControlHandle(producerHandle);
+    assert(producerHandle != kMojoHandleInvalid);
+    MojoHandleWatcherNatives.setControlHandle(producerHandle);
 
     // Spawn the handle watcher isolate with the MojoHandleWatcher,
     return Isolate.spawn(_handleWatcherIsolate, consumerHandle);
@@ -307,15 +300,15 @@
     var shutdownSendPort = shutdownReceivePort.sendPort;
 
     // Send the shutdown command.
-    _sendControlData(null, shutdownSendPort, _encodeCommand(SHUTDOWN));
+    _sendControlData(
+        kMojoHandleInvalid, shutdownSendPort, _encodeCommand(SHUTDOWN));
 
     // Close the control handle.
-    int controlHandle = _MojoHandleWatcherNatives.getControlHandle();
-    var handle = new MojoHandle(controlHandle);
-    handle.close();
+    int controlHandle = MojoHandleWatcherNatives.getControlHandle();
+    MojoHandleNatives.close(controlHandle);
 
     // Invalidate the control handle.
-    _MojoHandleWatcherNatives.setControlHandle(MojoHandle.INVALID);
+    MojoHandleWatcherNatives.setControlHandle(kMojoHandleInvalid);
 
     // Wait for the handle watcher isolate to exit.
     shutdownReceivePort.first.then((_) {
@@ -326,13 +319,13 @@
   // If wait is true, returns a future that resolves only after the handle
   // has actually been closed by the handle watcher. Otherwise, returns a
   // future that resolves immediately.
-  static Future<MojoResult> close(MojoHandle mojoHandle, {bool wait: false}) {
-    assert(MojoHandle._removeUnclosedHandle(mojoHandle));
+  static Future<int> close(int mojoHandle, {bool wait: false}) {
+    //assert(MojoHandle._removeUnclosedHandle(mojoHandle));
     if (!wait) {
       return new Future.value(
           _sendControlData(mojoHandle, null, _encodeCommand(CLOSE)));
     }
-    MojoResult result;
+    int result;
     var completer = new Completer();
     var rawPort = new RawReceivePort((_) {
       completer.complete(result);
@@ -345,17 +338,16 @@
     });
   }
 
-  static MojoResult add(MojoHandle mojoHandle, SendPort port, int signals) {
+  static int add(int mojoHandle, SendPort port, int signals) {
     return _sendControlData(mojoHandle, port, _encodeCommand(ADD, signals));
   }
 
-  static MojoResult remove(MojoHandle mojoHandle) {
+  static int remove(int mojoHandle) {
     return _sendControlData(mojoHandle, null, _encodeCommand(REMOVE));
   }
 
-  static MojoResult timer(Object ignored, SendPort port, int deadline) {
+  static int timer(Object ignored, SendPort port, int deadline) {
     // The deadline will be unwrapped before sending to the handle watcher.
-    return _sendControlData(
-        new MojoHandle._internal(deadline), port, _encodeCommand(TIMER));
+    return _sendControlData(deadline, port, _encodeCommand(TIMER));
   }
 }
diff --git a/third_party/mojo/src/mojo/public/dart/src/message_pipe.dart b/third_party/mojo/src/mojo/public/dart/src/message_pipe.dart
index 5021c78..29ae078b 100644
--- a/third_party/mojo/src/mojo/public/dart/src/message_pipe.dart
+++ b/third_party/mojo/src/mojo/public/dart/src/message_pipe.dart
@@ -4,16 +4,6 @@
 
 part of core;
 
-class _MojoMessagePipeNatives {
-  static List MojoCreateMessagePipe(int flags) native "MojoMessagePipe_Create";
-
-  static int MojoWriteMessage(int handle, ByteData data, int numBytes,
-      List<int> handles, int flags) native "MojoMessagePipe_Write";
-
-  static List MojoReadMessage(int handle, ByteData data, int numBytes,
-      List<int> handles, int flags) native "MojoMessagePipe_Read";
-}
-
 class MojoMessagePipeReadResult {
   final MojoResult status;
   final int bytesRead;
@@ -60,7 +50,7 @@
         (handles != null) ? handles.map((h) => h.h).toList() : null;
 
     // Do the call.
-    int result = _MojoMessagePipeNatives.MojoWriteMessage(
+    int result = MojoMessagePipeNatives.MojoWriteMessage(
         handle.h, data, dataNumBytes, mojoHandles, flags);
 
     status = new MojoResult(result);
@@ -95,7 +85,7 @@
     }
 
     // Do the call.
-    List result = _MojoMessagePipeNatives.MojoReadMessage(
+    List result = MojoMessagePipeNatives.MojoReadMessage(
         handle.h, data, dataNumBytes, mojoHandles, flags);
 
     if (result == null) {
@@ -140,7 +130,7 @@
   }
 
   factory MojoMessagePipe([int flags = FLAG_NONE]) {
-    List result = _MojoMessagePipeNatives.MojoCreateMessagePipe(flags);
+    List result = MojoMessagePipeNatives.MojoCreateMessagePipe(flags);
     if (result == null) {
       return null;
     }
diff --git a/third_party/mojo/src/mojo/public/dart/src/natives.dart b/third_party/mojo/src/mojo/public/dart/src/natives.dart
new file mode 100644
index 0000000..a46ddd6
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/dart/src/natives.dart
@@ -0,0 +1,70 @@
+// 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.
+
+part of internal;
+
+class MojoHandleNatives {
+  static int register(
+      Object eventStream, int handle) native "MojoHandle_Register";
+  static int close(int handle) native "MojoHandle_Close";
+  static List wait(
+      int handle, int signals, int deadline) native "MojoHandle_Wait";
+  static List waitMany(List<int> handles, List<int> signals,
+      int deadline) native "MojoHandle_WaitMany";
+}
+
+class MojoHandleWatcherNatives {
+  static int sendControlData(int controlHandle, int mojoHandle, SendPort port,
+      int data) native "MojoHandleWatcher_SendControlData";
+  static List recvControlData(
+      int controlHandle) native "MojoHandleWatcher_RecvControlData";
+  static int setControlHandle(
+      int controlHandle) native "MojoHandleWatcher_SetControlHandle";
+  static int getControlHandle() native "MojoHandleWatcher_GetControlHandle";
+}
+
+class MojoMessagePipeNatives {
+  static List MojoCreateMessagePipe(int flags) native "MojoMessagePipe_Create";
+
+  static int MojoWriteMessage(int handle, ByteData data, int numBytes,
+      List<int> handles, int flags) native "MojoMessagePipe_Write";
+
+  static List MojoReadMessage(int handle, ByteData data, int numBytes,
+      List<int> handles, int flags) native "MojoMessagePipe_Read";
+}
+
+class MojoDataPipeNatives {
+  static List MojoCreateDataPipe(int elementBytes, int capacityBytes,
+      int flags) native "MojoDataPipe_Create";
+
+  static List MojoWriteData(int handle, ByteData data, int numBytes,
+      int flags) native "MojoDataPipe_WriteData";
+
+  static List MojoBeginWriteData(int handle, int bufferBytes,
+      int flags) native "MojoDataPipe_BeginWriteData";
+
+  static int MojoEndWriteData(
+      int handle, int bytesWritten) native "MojoDataPipe_EndWriteData";
+
+  static List MojoReadData(int handle, ByteData data, int numBytes,
+      int flags) native "MojoDataPipe_ReadData";
+
+  static List MojoBeginReadData(int handle, int bufferBytes,
+      int flags) native "MojoDataPipe_BeginReadData";
+
+  static int MojoEndReadData(
+      int handle, int bytesRead) native "MojoDataPipe_EndReadData";
+}
+
+class MojoSharedBufferNatives {
+  static List Create(int numBytes, int flags) native "MojoSharedBuffer_Create";
+
+  static List Duplicate(
+      int bufferHandle, int flags) native "MojoSharedBuffer_Duplicate";
+
+  static List Map(Object buffer, int bufferHandle, int offset, int numBytes,
+      int flags) native "MojoSharedBuffer_Map";
+
+  static int Unmap(ByteData buffer) native "MojoSharedBuffer_Unmap";
+}
diff --git a/third_party/mojo/src/mojo/public/dart/src/proxy.dart b/third_party/mojo/src/mojo/public/dart/src/proxy.dart
index 5317e56..a84ca3e 100644
--- a/third_party/mojo/src/mojo/public/dart/src/proxy.dart
+++ b/third_party/mojo/src/mojo/public/dart/src/proxy.dart
@@ -4,6 +4,12 @@
 
 part of bindings;
 
+class ProxyCloseException {
+  final String message;
+  ProxyCloseException(this.message);
+  String toString() => message;
+}
+
 abstract class Proxy extends core.MojoEventStreamListener {
   Map<int, Completer> _completerMap;
   int _nextId = 0;
@@ -40,6 +46,15 @@
     throw 'Unexpected write signal in proxy.';
   }
 
+  @override
+  Future close({bool nodefer: false}) {
+    for (var completer in _completerMap.values) {
+      completer.completeError(new ProxyCloseException('Proxy closed'));
+    }
+    _completerMap.clear();
+    return super.close(nodefer: nodefer);
+  }
+
   void sendMessage(Struct message, int name) {
     if (!isOpen) {
       listen();
diff --git a/third_party/mojo/src/mojo/public/dart/src/stub.dart b/third_party/mojo/src/mojo/public/dart/src/stub.dart
index 6695f07..9008f55e 100644
--- a/third_party/mojo/src/mojo/public/dart/src/stub.dart
+++ b/third_party/mojo/src/mojo/public/dart/src/stub.dart
@@ -44,26 +44,29 @@
         if (isOpen) {
           endpoint.write(
               response.buffer, response.buffer.lengthInBytes, response.handles);
-          if (!endpoint.status.isOk) {
-            throw 'message pipe write failed: ${endpoint.status}';
-          }
+          // FailedPrecondition is only used to indicate that the other end of
+          // the pipe has been closed. We can ignore the close here and wait for
+          // the PeerClosed signal on the event stream.
+          assert(endpoint.status.isOk || endpoint.status.isFailedPrecondition);
           if (_isClosing && (_outstandingResponseFutures == 0)) {
             // This was the final response future for which we needed to send
             // a response. It is safe to close.
-            super.close();
-            _isClosing = false;
-            _closeCompleter.complete(null);
-            _closeCompleter = null;
+            super.close().then((_) {
+              _isClosing = false;
+              _closeCompleter.complete(null);
+              _closeCompleter = null;
+            });
           }
         }
       });
     } else if (_isClosing && (_outstandingResponseFutures == 0)) {
       // We are closing, there is no response to send for this message, and
       // there are no outstanding response futures. Do the close now.
-      super.close();
-      _isClosing = false;
-      _closeCompleter.complete(null);
-      _closeCompleter = null;
+      super.close().then((_) {
+        _isClosing = false;
+        _closeCompleter.complete(null);
+        _closeCompleter = null;
+      });
     }
   }
 
@@ -73,7 +76,8 @@
 
   // NB: |nodefer| should only be true when calling close() while handling an
   // exception thrown from handleRead(), e.g. when we receive a malformed
-  // message.
+  // message, or when we have received the PEER_CLOSED event.
+  @override
   Future close({bool nodefer: false}) {
     if (isOpen &&
         !nodefer &&
@@ -86,7 +90,13 @@
       _closeCompleter = new Completer();
       return _closeCompleter.future;
     } else {
-      return super.close();
+      return super.close(nodefer: nodefer).then((_) {
+        if (_isClosing) {
+          _isClosing = false;
+          _closeCompleter.complete(null);
+          _closeCompleter = null;
+        }
+      });
     }
   }
 
diff --git a/third_party/mojo/src/mojo/public/dart/src/timer_queue.dart b/third_party/mojo/src/mojo/public/dart/src/timer_queue.dart
index 8f9936b..01c2493 100644
--- a/third_party/mojo/src/mojo/public/dart/src/timer_queue.dart
+++ b/third_party/mojo/src/mojo/public/dart/src/timer_queue.dart
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-part of core;
+part of internal;
 
 class Timeout implements Comparable<Timeout> {
-  int deadline;  // milliseconds since the Unix epoch.
+  int deadline; // milliseconds since the Unix epoch.
   SendPort port;
 
   Timeout(this.port, this.deadline);
 
-  int compareTo(Timeout other)  => other.deadline - deadline;
+  int compareTo(Timeout other) => deadline - other.deadline;
 }
 
 class TimerQueue {
diff --git a/third_party/mojo/src/mojo/public/interfaces/application/BUILD.gn b/third_party/mojo/src/mojo/public/interfaces/application/BUILD.gn
index 463df558..5b15344c 100644
--- a/third_party/mojo/src/mojo/public/interfaces/application/BUILD.gn
+++ b/third_party/mojo/src/mojo/public/interfaces/application/BUILD.gn
@@ -11,4 +11,9 @@
     "service_provider.mojom",
     "shell.mojom",
   ]
+
+  # This base dir ensures that Dart's Mojo SDK can be imported with, e.g.,
+  # import 'package:mojo/public/dart/core.dart' even when the Mojo SDK lives
+  # somewhere else in the source tree.
+  base_dir = mojo_root
 }
diff --git a/third_party/mojo/src/mojo/public/mojo.gni b/third_party/mojo/src/mojo/public/mojo.gni
index b8676e6..f2631a0b 100644
--- a/third_party/mojo/src/mojo/public/mojo.gni
+++ b/third_party/mojo/src/mojo/public/mojo.gni
@@ -6,23 +6,25 @@
 
 # If using the prebuilt shell, gate its usage by the platforms for which it is
 # published.
-use_prebuilt_mojo_shell = false
-if (!defined(build_mojo_shell_from_source) || !build_mojo_shell_from_source) {
-  use_prebuilt_mojo_shell = is_linux || is_android
+mojo_use_prebuilt_mojo_shell = false
+if (!defined(mojo_build_mojo_shell_from_source) ||
+    !mojo_build_mojo_shell_from_source) {
+  mojo_use_prebuilt_mojo_shell = is_linux || is_android
 }
 
 # If using the prebuilt network service, gate its usage by the platforms for
 # which it is published.
-use_prebuilt_network_service = false
-if (!defined(build_network_service_from_source) ||
-    !build_network_service_from_source) {
-  use_prebuilt_network_service = is_linux || is_android
+mojo_use_prebuilt_network_service = false
+if (!defined(mojo_build_network_service_from_source) ||
+    !mojo_build_network_service_from_source) {
+  mojo_use_prebuilt_network_service = is_linux || is_android
 }
 
 # Enable Dart apptest framework by default.
-use_dart_apptest_framework = true
-if (defined(disable_dart_apptest_framework) && disable_dart_apptest_framework) {
-  use_dart_apptest_framework = false
+mojo_use_dart_apptest_framework = true
+if (defined(mojo_disable_dart_apptest_framework) &&
+    mojo_disable_dart_apptest_framework) {
+  mojo_use_dart_apptest_framework = false
 }
 
 # The absolute path to the directory containing the mojo public SDK (i.e., the
diff --git a/third_party/mojo/src/mojo/public/mojo_application.gni b/third_party/mojo/src/mojo/public/mojo_application.gni
index ce8cf9e..2c7ee4a 100644
--- a/third_party/mojo/src/mojo/public/mojo_application.gni
+++ b/third_party/mojo/src/mojo/public/mojo_application.gni
@@ -77,11 +77,11 @@
       }
 
       # Copy any necessary prebuilt artifacts.
-      if (use_prebuilt_mojo_shell) {
+      if (mojo_use_prebuilt_mojo_shell) {
         data_deps +=
             [ rebase_path("mojo/public/tools:copy_mojo_shell", ".", mojo_root) ]
       }
-      if (use_prebuilt_network_service) {
+      if (mojo_use_prebuilt_network_service) {
         data_deps += [ rebase_path("mojo/public/tools:copy_network_service",
                                    ".",
                                    mojo_root) ]
@@ -197,11 +197,11 @@
       }
 
       # Copy any necessary prebuilt artifacts.
-      if (use_prebuilt_mojo_shell) {
+      if (mojo_use_prebuilt_mojo_shell) {
         data_deps +=
             [ rebase_path("mojo/public/tools:copy_mojo_shell", ".", mojo_root) ]
       }
-      if (use_prebuilt_network_service) {
+      if (mojo_use_prebuilt_network_service) {
         data_deps += [ rebase_path("mojo/public/tools:copy_network_service",
                                    ".",
                                    mojo_root) ]
diff --git a/third_party/mojo/src/mojo/public/platform/native/BUILD.gn b/third_party/mojo/src/mojo/public/platform/native/BUILD.gn
index fa23fb1..0e099f99 100644
--- a/third_party/mojo/src/mojo/public/platform/native/BUILD.gn
+++ b/third_party/mojo/src/mojo/public/platform/native/BUILD.gn
@@ -26,10 +26,16 @@
 
 mojo_sdk_source_set("gles2") {
   sources = [
+    "gles2_impl_chromium_miscellaneous_thunks.cc",
+    "gles2_impl_chromium_miscellaneous_thunks.h",
+    "gles2_impl_chromium_sub_image_thunks.cc",
+    "gles2_impl_chromium_sub_image_thunks.h",
     "gles2_impl_chromium_sync_point_thunks.cc",
     "gles2_impl_chromium_sync_point_thunks.h",
     "gles2_impl_chromium_texture_mailbox_thunks.cc",
     "gles2_impl_chromium_texture_mailbox_thunks.h",
+    "gles2_impl_occlusion_query_ext_thunks.cc",
+    "gles2_impl_occlusion_query_ext_thunks.h",
     "gles2_impl_thunks.cc",
     "gles2_impl_thunks.h",
     "gles2_thunks.cc",
diff --git a/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_miscellaneous_thunks.cc b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_miscellaneous_thunks.cc
new file mode 100644
index 0000000..6e6b67d
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_miscellaneous_thunks.cc
@@ -0,0 +1,33 @@
+// 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 "mojo/public/platform/native/gles2_impl_chromium_miscellaneous_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+static MojoGLES2ImplChromiumMiscellaneousThunks
+    g_impl_chromium_miscellaneous_thunks = {0};
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS)  \
+  ReturnType gl##Function PARAMETERS {                              \
+    assert(g_impl_chromium_miscellaneous_thunks.Function);          \
+    return g_impl_chromium_miscellaneous_thunks.Function ARGUMENTS; \
+  }
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_miscellaneous_autogen.h"
+#undef VISIT_GL_CALL
+
+extern "C" THUNK_EXPORT size_t MojoSetGLES2ImplChromiumMiscellaneousThunks(
+    const MojoGLES2ImplChromiumMiscellaneousThunks*
+        gles2_impl_chromium_miscellaneous_thunks) {
+  if (gles2_impl_chromium_miscellaneous_thunks->size >=
+      sizeof(g_impl_chromium_miscellaneous_thunks))
+    g_impl_chromium_miscellaneous_thunks =
+        *gles2_impl_chromium_miscellaneous_thunks;
+  return sizeof(g_impl_chromium_miscellaneous_thunks);
+}
+
+}  // extern "C"
diff --git a/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_miscellaneous_thunks.h b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_miscellaneous_thunks.h
new file mode 100644
index 0000000..d71b3d22
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_miscellaneous_thunks.h
@@ -0,0 +1,45 @@
+// 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 MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_MISCELLANEOUS_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_MISCELLANEOUS_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/chromium_miscellaneous.h"
+
+// Specifies the frozen API for the GLES2 CHROMIUM_miscellaneous extension.
+#pragma pack(push, 8)
+struct MojoGLES2ImplChromiumMiscellaneousThunks {
+  size_t size;  // Should be set to sizeof(*this).
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  ReturnType(*Function) PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_miscellaneous_autogen.h"
+#undef VISIT_GL_CALL
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder to get the embedder's implementation
+// of GLES2.
+inline MojoGLES2ImplChromiumMiscellaneousThunks
+MojoMakeGLES2ImplChromiumMiscellaneousThunks() {
+  MojoGLES2ImplChromiumMiscellaneousThunks
+      gles2_impl_chromium_miscellaneous_thunks = {
+          sizeof(MojoGLES2ImplChromiumMiscellaneousThunks),
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function,
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_miscellaneous_autogen.h"
+#undef VISIT_GL_CALL
+      };
+
+  return gles2_impl_chromium_miscellaneous_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system.
+// The contents of |gles2_impl_chromium_miscellaneous_thunks| are copied.
+typedef size_t (*MojoSetGLES2ImplChromiumMiscellaneousThunksFn)(
+    const MojoGLES2ImplChromiumMiscellaneousThunks* thunks);
+
+#endif  // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_MISCELLANEOUS_THUNKS_H_
diff --git a/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_sub_image_thunks.cc b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_sub_image_thunks.cc
new file mode 100644
index 0000000..eae2467
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_sub_image_thunks.cc
@@ -0,0 +1,32 @@
+// 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 "mojo/public/platform/native/gles2_impl_chromium_sub_image_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+static MojoGLES2ImplChromiumSubImageThunks g_impl_chromium_sub_image_thunks = {
+    0};
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  ReturnType gl##Function PARAMETERS {                             \
+    assert(g_impl_chromium_sub_image_thunks.Function);             \
+    return g_impl_chromium_sub_image_thunks.Function ARGUMENTS;    \
+  }
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sub_image_autogen.h"
+#undef VISIT_GL_CALL
+
+extern "C" THUNK_EXPORT size_t MojoSetGLES2ImplChromiumSubImageThunks(
+    const MojoGLES2ImplChromiumSubImageThunks*
+        gles2_impl_chromium_sub_image_thunks) {
+  if (gles2_impl_chromium_sub_image_thunks->size >=
+      sizeof(g_impl_chromium_sub_image_thunks))
+    g_impl_chromium_sub_image_thunks = *gles2_impl_chromium_sub_image_thunks;
+  return sizeof(g_impl_chromium_sub_image_thunks);
+}
+
+}  // extern "C"
diff --git a/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_sub_image_thunks.h b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_sub_image_thunks.h
new file mode 100644
index 0000000..af37a91c
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_chromium_sub_image_thunks.h
@@ -0,0 +1,44 @@
+// 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 MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SUB_IMAGE_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SUB_IMAGE_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/chromium_sub_image.h"
+
+// Specifies the frozen API for the GLES2 CHROMIUM_sub_image extension.
+#pragma pack(push, 8)
+struct MojoGLES2ImplChromiumSubImageThunks {
+  size_t size;  // Should be set to sizeof(*this).
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  ReturnType(*Function) PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sub_image_autogen.h"
+#undef VISIT_GL_CALL
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder to get the embedder's implementation
+// of GLES2.
+inline MojoGLES2ImplChromiumSubImageThunks
+MojoMakeGLES2ImplChromiumSubImageThunks() {
+  MojoGLES2ImplChromiumSubImageThunks gles2_impl_chromium_sub_image_thunks = {
+      sizeof(MojoGLES2ImplChromiumSubImageThunks),
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function,
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sub_image_autogen.h"
+#undef VISIT_GL_CALL
+  };
+
+  return gles2_impl_chromium_sub_image_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system.
+// The contents of |gles2_impl_chromium_sub_image_thunks| are copied.
+typedef size_t (*MojoSetGLES2ImplChromiumSubImageThunksFn)(
+    const MojoGLES2ImplChromiumSubImageThunks* thunks);
+
+#endif  // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SUB_IMAGE_THUNKS_H_
diff --git a/third_party/mojo/src/mojo/public/platform/native/gles2_impl_occlusion_query_ext_thunks.cc b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_occlusion_query_ext_thunks.cc
new file mode 100644
index 0000000..a5f7b06
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_occlusion_query_ext_thunks.cc
@@ -0,0 +1,32 @@
+// 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 "mojo/public/platform/native/gles2_impl_occlusion_query_ext_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+static MojoGLES2ImplOcclusionQueryExtThunks g_impl_occlusion_query_ext_thunks =
+    {0};
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  ReturnType gl##Function PARAMETERS {                             \
+    assert(g_impl_occlusion_query_ext_thunks.Function);            \
+    return g_impl_occlusion_query_ext_thunks.Function ARGUMENTS;   \
+  }
+#include "mojo/public/c/gles2/gles2_call_visitor_occlusion_query_ext_autogen.h"
+#undef VISIT_GL_CALL
+
+extern "C" THUNK_EXPORT size_t MojoSetGLES2ImplOcclusionQueryExtThunks(
+    const MojoGLES2ImplOcclusionQueryExtThunks*
+        gles2_impl_occlusion_query_ext_thunks) {
+  if (gles2_impl_occlusion_query_ext_thunks->size >=
+      sizeof(g_impl_occlusion_query_ext_thunks))
+    g_impl_occlusion_query_ext_thunks = *gles2_impl_occlusion_query_ext_thunks;
+  return sizeof(g_impl_occlusion_query_ext_thunks);
+}
+
+}  // extern "C"
diff --git a/third_party/mojo/src/mojo/public/platform/native/gles2_impl_occlusion_query_ext_thunks.h b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_occlusion_query_ext_thunks.h
new file mode 100644
index 0000000..57f1c61a
--- /dev/null
+++ b/third_party/mojo/src/mojo/public/platform/native/gles2_impl_occlusion_query_ext_thunks.h
@@ -0,0 +1,44 @@
+// 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 MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_OCCLUSION_QUERY_EXT_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_OCCLUSION_QUERY_EXT_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/occlusion_query_ext.h"
+
+// Specifies the frozen API for the Occlusion Query Extension.
+#pragma pack(push, 8)
+struct MojoGLES2ImplOcclusionQueryExtThunks {
+  size_t size;  // Should be set to sizeof(*this).
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+  ReturnType(*Function) PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_occlusion_query_ext_autogen.h"
+#undef VISIT_GL_CALL
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder to get the embedder's implementation
+// of GLES2.
+inline MojoGLES2ImplOcclusionQueryExtThunks
+MojoMakeGLES2ImplOcclusionQueryExtThunks() {
+  MojoGLES2ImplOcclusionQueryExtThunks gles2_impl_occlusion_query_ext_thunks = {
+      sizeof(MojoGLES2ImplOcclusionQueryExtThunks),
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function,
+#include "mojo/public/c/gles2/gles2_call_visitor_occlusion_query_ext_autogen.h"
+#undef VISIT_GL_CALL
+  };
+
+  return gles2_impl_occlusion_query_ext_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system.
+// The contents of |gles2_impl_occlusion_query_ext_thunks| are copied.
+typedef size_t (*MojoSetGLES2ImplOcclusionQueryExtThunksFn)(
+    const MojoGLES2ImplOcclusionQueryExtThunks* thunks);
+
+#endif  // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SYNC_POINT_THUNKS_H_
diff --git a/third_party/mojo/src/mojo/public/tools/BUILD.gn b/third_party/mojo/src/mojo/public/tools/BUILD.gn
index b5cfcd4..9344cf31 100644
--- a/third_party/mojo/src/mojo/public/tools/BUILD.gn
+++ b/third_party/mojo/src/mojo/public/tools/BUILD.gn
@@ -5,7 +5,7 @@
 import("//build/module_args/mojo.gni")
 import("../mojo.gni")
 
-if (use_prebuilt_mojo_shell) {
+if (mojo_use_prebuilt_mojo_shell) {
   copy("copy_mojo_shell") {
     filename = "mojo_shell"
     if (is_android) {
@@ -28,13 +28,13 @@
   }
 }
 
-if (use_prebuilt_network_service) {
+if (mojo_use_prebuilt_network_service) {
   copy("copy_network_service") {
     filename = "network_service.mojo"
-    if (defined(prebuilt_network_service_location) &&
-        prebuilt_network_service_location != "") {
+    if (defined(mojo_prebuilt_network_service_location) &&
+        mojo_prebuilt_network_service_location != "") {
       sources = [
-        "$prebuilt_network_service_location",
+        "$mojo_prebuilt_network_service_location",
       ]
     } else {
       if (is_android) {
@@ -78,7 +78,7 @@
 # loaded from Google Storage and then puts it in a rule which the
 # "dart_package" template in mojo/public/dart/rules.gni can introspect on,
 # accessing the 'label' and 'target_out_dir' variables.
-if (use_dart_apptest_framework) {
+if (mojo_use_dart_apptest_framework) {
   copy("dart_apptest_framework") {
     sources = [
       "prebuilt/frameworks/apptest.dartzip",
diff --git a/third_party/mojo/src/mojo/public/tools/NETWORK_SERVICE_VERSION b/third_party/mojo/src/mojo/public/tools/NETWORK_SERVICE_VERSION
index fd7a713..611dc8b 100644
--- a/third_party/mojo/src/mojo/public/tools/NETWORK_SERVICE_VERSION
+++ b/third_party/mojo/src/mojo/public/tools/NETWORK_SERVICE_VERSION
@@ -1 +1 @@
-custom_build_0cf4c5c1ad031c67b48d76cb599122be6f7e0709_issue1013183003_patchset1
\ No newline at end of file
+fed9cc4ee48bf831f52127f56c2d442ddedff4a1
\ No newline at end of file
diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl
index cca521d..30463b2 100644
--- a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl
+++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl
@@ -63,7 +63,11 @@
           throw 'Expected a message with a valid request Id.';
         }
         Completer c = completerMap[message.header.requestId];
-        completerMap[message.header.requestId] = null;
+        if (c == null) {
+          throw 'Message had unknown request Id: ${message.header.requestId}';
+        }
+        completerMap.remove(message.header.requestId);
+        assert(!c.isCompleted);
         c.complete(r);
         break;
 {%- endif %}
@@ -154,7 +158,7 @@
       core.MojoMessagePipeEndpoint endpoint) =>
       new {{interface|name}}Proxy.fromEndpoint(endpoint);
 
-  Future close() => impl.close();
+  Future close({bool nodefer: false}) => impl.close(nodefer: nodefer);
 
   String toString() {
     return "{{interface|name}}Proxy($impl)";
diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/module.lib.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/module.lib.tmpl
index c3b0558..21b643ec 100644
--- a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/module.lib.tmpl
+++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/module.lib.tmpl
@@ -5,8 +5,9 @@
 library {{module.name}};
 
 import 'dart:async';
-import 'dart:mojo.bindings' as bindings;
-import 'dart:mojo.core' as core;
+
+import 'package:mojo/public/dart/bindings.dart' as bindings;
+import 'package:mojo/public/dart/core.dart' as core;
 
 {%- for import in imports %}
 import 'package:{{import.module.path}}.dart' as {{import.unique_name}};
diff --git a/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni b/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni
index 5cfd5cd..dd68501 100644
--- a/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni
+++ b/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni
@@ -334,7 +334,14 @@
       output,
     ]
 
-    rebase_base_dir = rebase_path("$root_build_dir/gen/", root_build_dir)
+    invoker_base_dir = ""
+    if (defined(invoker.base_dir)) {
+      invoker_base_dir =
+          rebase_path(invoker.base_dir, "$root_build_dir/../../", ".")
+    }
+
+    rebase_base_dir =
+        rebase_path("$root_build_dir/gen/$invoker_base_dir", root_build_dir)
     if (defined(invoker.sources)) {
       rebase_inputs = rebase_path(inputs, root_build_dir)
     }
diff --git a/third_party/mojo_services/src/accessibility/public/interfaces/BUILD.gn b/third_party/mojo_services/src/accessibility/public/interfaces/BUILD.gn
index 7997908c..2b29c1a 100644
--- a/third_party/mojo_services/src/accessibility/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/accessibility/public/interfaces/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
diff --git a/third_party/mojo_services/src/clipboard/public/interfaces/BUILD.gn b/third_party/mojo_services/src/clipboard/public/interfaces/BUILD.gn
index 7ac21d2..16fe33b 100644
--- a/third_party/mojo_services/src/clipboard/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/clipboard/public/interfaces/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
diff --git a/third_party/mojo_services/src/content_handler/public/interfaces/BUILD.gn b/third_party/mojo_services/src/content_handler/public/interfaces/BUILD.gn
index 5016c65..c58507a 100644
--- a/third_party/mojo_services/src/content_handler/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/content_handler/public/interfaces/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
@@ -12,14 +12,14 @@
 
   import_dirs = [ get_path_info("../../../", "abspath") ]
 
-  if (defined(network_service_root)) {
-    import_dirs += [ network_service_root ]
+  if (defined(mojo_network_service_root)) {
+    import_dirs += [ mojo_network_service_root ]
   } else {
-    network_service_root = "../../.."
+    mojo_network_service_root = "../../.."
   }
 
   deps = [
-    "$network_service_root/network/public/interfaces",
+    "$mojo_network_service_root/network/public/interfaces",
   ]
 
   mojo_sdk_deps = [ "mojo/public/interfaces/application" ]
diff --git a/third_party/mojo_services/src/geometry/public/cpp/BUILD.gn b/third_party/mojo_services/src/geometry/public/cpp/BUILD.gn
index c05b8d36..c527c93a 100644
--- a/third_party/mojo_services/src/geometry/public/cpp/BUILD.gn
+++ b/third_party/mojo_services/src/geometry/public/cpp/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/mojo_sdk.gni")
 
 mojo_sdk_source_set("cpp") {
diff --git a/third_party/mojo_services/src/geometry/public/interfaces/BUILD.gn b/third_party/mojo_services/src/geometry/public/interfaces/BUILD.gn
index 4896addc..9047ee2 100644
--- a/third_party/mojo_services/src/geometry/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/geometry/public/interfaces/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
diff --git a/third_party/mojo_services/src/gpu/public/interfaces/BUILD.gn b/third_party/mojo_services/src/gpu/public/interfaces/BUILD.gn
index 865f73e5..ec061df 100644
--- a/third_party/mojo_services/src/gpu/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/gpu/public/interfaces/BUILD.gn
@@ -2,13 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
   sources = [
-    "context_provider.mojom",
     "command_buffer.mojom",
+    "context_provider.mojom",
     "gpu.mojom",
     "gpu_capabilities.mojom",
     "viewport_parameter_listener.mojom",
diff --git a/third_party/mojo_services/src/http_server/public/cpp/BUILD.gn b/third_party/mojo_services/src/http_server/public/cpp/BUILD.gn
index a24f006..4fca6c7 100644
--- a/third_party/mojo_services/src/http_server/public/cpp/BUILD.gn
+++ b/third_party/mojo_services/src/http_server/public/cpp/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/mojo_sdk.gni")
 
 mojo_sdk_source_set("cpp") {
diff --git a/third_party/mojo_services/src/http_server/public/interfaces/BUILD.gn b/third_party/mojo_services/src/http_server/public/interfaces/BUILD.gn
index 05eee62a..79aa6a6 100644
--- a/third_party/mojo_services/src/http_server/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/http_server/public/interfaces/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
@@ -15,13 +15,13 @@
 
   import_dirs = [ get_path_info("../../../", "abspath") ]
 
-  if (defined(network_service_root)) {
-    import_dirs += [ network_service_root ]
+  if (defined(mojo_network_service_root)) {
+    import_dirs += [ mojo_network_service_root ]
   } else {
-    network_service_root = "../../.."
+    mojo_network_service_root = "../../.."
   }
 
   deps = [
-    "$network_service_root/network/public/interfaces",
+    "$mojo_network_service_root/network/public/interfaces",
   ]
 }
diff --git a/third_party/mojo_services/src/input_events/public/interfaces/BUILD.gn b/third_party/mojo_services/src/input_events/public/interfaces/BUILD.gn
index e7a75f2..5ef09eb 100644
--- a/third_party/mojo_services/src/input_events/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/input_events/public/interfaces/BUILD.gn
@@ -2,13 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
   sources = [
-    "input_events.mojom",
     "input_event_constants.mojom",
+    "input_events.mojom",
     "input_key_codes.mojom",
   ]
 
diff --git a/third_party/mojo_services/src/input_events/public/interfaces/input_event_constants.mojom b/third_party/mojo_services/src/input_events/public/interfaces/input_event_constants.mojom
index f137fb3..b3e0ea1f6 100644
--- a/third_party/mojo_services/src/input_events/public/interfaces/input_event_constants.mojom
+++ b/third_party/mojo_services/src/input_events/public/interfaces/input_event_constants.mojom
@@ -4,50 +4,14 @@
 
 module mojo;
 
-// This mirrors ui::EventType
 enum EventType {
-  UNKNOWN ,
-  MOUSE_PRESSED,
-  MOUSE_DRAGGED,
-  MOUSE_RELEASED,
-  MOUSE_MOVED,
-  MOUSE_ENTERED,
-  MOUSE_EXITED,
+  UNKNOWN,
   KEY_PRESSED,
   KEY_RELEASED,
-  MOUSEWHEEL,
-  MOUSE_CAPTURE_CHANGED,
-  TOUCH_RELEASED,
-  TOUCH_PRESSED,
-  TOUCH_MOVED,
-  TOUCH_CANCELLED,
-  DROP_TARGET_EVENT,
-  TRANSLATED_KEY_PRESS,
-  TRANSLATED_KEY_RELEASE,
-  GESTURE_SCROLL_BEGIN,
-  GESTURE_SCROLL_END,
-  GESTURE_SCROLL_UPDATE,
-  GESTURE_TAP,
-  GESTURE_TAP_DOWN,
-  GESTURE_TAP_CANCEL,
-  GESTURE_TAP_UNCONFIRMED,
-  GESTURE_DOUBLE_TAP,
-  GESTURE_BEGIN,
-  GESTURE_END,
-  GESTURE_TWO_FINGER_TAP,
-  GESTURE_PINCH_BEGIN,
-  GESTURE_PINCH_END,
-  GESTURE_PINCH_UPDATE,
-  GESTURE_LONG_PRESS,
-  GESTURE_LONG_TAP,
-  GESTURE_SWIPE,
-  GESTURE_SHOW_PRESS,
-  GESTURE_WIN8_EDGE_SWIPE,
-  SCROLL,
-  SCROLL_FLING_START,
-  SCROLL_FLING_CANCEL,
-  CANCEL_MODE,
-  UMA_DATA
+  POINTER_CANCEL,
+  POINTER_DOWN,
+  POINTER_MOVE,
+  POINTER_UP,
 };
 
 // This mirrors ui::EventFlags
@@ -75,3 +39,8 @@
 
   // TODO(erg): Move accessibility flags and maybe synthetic touch events here.
 };
+
+enum PointerKind {
+  TOUCH,
+  MOUSE,
+};
diff --git a/third_party/mojo_services/src/input_events/public/interfaces/input_events.mojom b/third_party/mojo_services/src/input_events/public/interfaces/input_events.mojom
index 1cab579..0086265 100644
--- a/third_party/mojo_services/src/input_events/public/interfaces/input_events.mojom
+++ b/third_party/mojo_services/src/input_events/public/interfaces/input_events.mojom
@@ -8,11 +8,6 @@
 import "input_events/public/interfaces/input_event_constants.mojom";
 import "input_events/public/interfaces/input_key_codes.mojom";
 
-struct LocationData {
-  Point? in_view_location;
-  Point? screen_location;
-};
-
 struct KeyData {
   // The chromium event key code; these values are from the ui/ KeyCode enum,
   // which has the fun property of being neither consistently the Windows key
@@ -52,47 +47,30 @@
   uint16 unmodified_text;
 };
 
-struct TouchData {
+struct PointerData {
   int32 pointer_id;
-};
-
-struct GestureData {
-  // A bounding box for all the input events that contributed to this gesture.
-  RectF? bounding_box;
-
-  // GESTURE_SCROLL_UPDATE
-  float scroll_x;
-  float scroll_y;
-
-  // SCROLL_FLING_START
-  float velocity_x;
-  float velocity_y;
-
-  // GESTURE_PINCH_UPDATE
-  float scale;
-
-  // GESTURE_SWIPE
-  bool swipe_left;
-  bool swipe_right;
-  bool swipe_up;
-  bool swipe_down;
-
-  // GESTURE_TAP and GESTURE_TAP_UNCONFIRMED and GESTURE_DOUBLE_TAP
-  int32 tap_count;
-};
-
-struct MouseWheelData {
-  int32 x_offset;
-  int32 y_offset;
+  PointerKind kind;
+  // |x| and |y| are in the coordinate system of the View.
+  float x;
+  float y;
+  // |screen_x| and |screen_y| are in screen coordinates.
+  float screen_x;
+  float screen_y;
+  float pressure;
+  float radius_major;
+  float radius_minor;
+  float orientation;
+  // Used for devices that support wheels. Ranges from -1 to 1.
+  float horizontal_wheel;
+  float vertical_wheel;
 };
 
 struct Event {
+  // TODO(sky): rename to type.
   EventType action;
+  // TODO(sky): parts of this should move to PointerData.
   EventFlags flags;
   int64 time_stamp;
-  LocationData? location_data;
   KeyData? key_data;
-  TouchData? touch_data;
-  GestureData? gesture_data;
-  MouseWheelData? wheel_data;
+  PointerData? pointer_data;
 };
diff --git a/third_party/mojo_services/src/native_viewport/public/cpp/BUILD.gn b/third_party/mojo_services/src/native_viewport/public/cpp/BUILD.gn
index 035ddfa6..855de712 100644
--- a/third_party/mojo_services/src/native_viewport/public/cpp/BUILD.gn
+++ b/third_party/mojo_services/src/native_viewport/public/cpp/BUILD.gn
@@ -2,13 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/mojo_sdk.gni")
 
 mojo_sdk_source_set("args") {
   public_configs = [ "../../../public/build/config:mojo_services" ]
   sources = [
-    "lib/args.cc",
     "args.h",
+    "lib/args.cc",
   ]
 }
diff --git a/third_party/mojo_services/src/native_viewport/public/interfaces/BUILD.gn b/third_party/mojo_services/src/native_viewport/public/interfaces/BUILD.gn
index 03115640..86ff870 100644
--- a/third_party/mojo_services/src/native_viewport/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/native_viewport/public/interfaces/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
diff --git a/third_party/mojo_services/src/navigation/public/interfaces/BUILD.gn b/third_party/mojo_services/src/navigation/public/interfaces/BUILD.gn
index da9fc70..19b9f8a 100644
--- a/third_party/mojo_services/src/navigation/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/navigation/public/interfaces/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
@@ -12,13 +12,13 @@
 
   import_dirs = [ get_path_info("../../../", "abspath") ]
 
-  if (defined(network_service_root)) {
-    import_dirs += [ network_service_root ]
+  if (defined(mojo_network_service_root)) {
+    import_dirs += [ mojo_network_service_root ]
   } else {
-    network_service_root = "../../.."
+    mojo_network_service_root = "../../.."
   }
 
   deps = [
-    "$network_service_root/network/public/interfaces",
+    "$mojo_network_service_root/network/public/interfaces",
   ]
 }
diff --git a/third_party/mojo_services/src/surfaces/public/cpp/BUILD.gn b/third_party/mojo_services/src/surfaces/public/cpp/BUILD.gn
index ddac4f3..9cf48b33 100644
--- a/third_party/mojo_services/src/surfaces/public/cpp/BUILD.gn
+++ b/third_party/mojo_services/src/surfaces/public/cpp/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/mojo_sdk.gni")
 
 mojo_sdk_source_set("cpp") {
diff --git a/third_party/mojo_services/src/surfaces/public/interfaces/BUILD.gn b/third_party/mojo_services/src/surfaces/public/interfaces/BUILD.gn
index 8350df8..7902a7a 100644
--- a/third_party/mojo_services/src/surfaces/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/surfaces/public/interfaces/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
diff --git a/third_party/mojo_services/src/view_manager/public/cpp/BUILD.gn b/third_party/mojo_services/src/view_manager/public/cpp/BUILD.gn
index d121021..189e8273 100644
--- a/third_party/mojo_services/src/view_manager/public/cpp/BUILD.gn
+++ b/third_party/mojo_services/src/view_manager/public/cpp/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/mojo_sdk.gni")
 
 mojo_sdk_source_set("cpp") {
diff --git a/third_party/mojo_services/src/view_manager/public/interfaces/BUILD.gn b/third_party/mojo_services/src/view_manager/public/interfaces/BUILD.gn
index 69a454b1..177066b8 100644
--- a/third_party/mojo_services/src/view_manager/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/view_manager/public/interfaces/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
diff --git a/third_party/mojo_services/src/window_manager/public/interfaces/BUILD.gn b/third_party/mojo_services/src/window_manager/public/interfaces/BUILD.gn
index 14b7d2c..b97f069 100644
--- a/third_party/mojo_services/src/window_manager/public/interfaces/BUILD.gn
+++ b/third_party/mojo_services/src/window_manager/public/interfaces/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("../../../mojo_sdk_root.gni")
+import("//build/module_args/mojo.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
diff --git a/third_party/robolectric/BUILD.gn b/third_party/robolectric/BUILD.gn
index 07613acb..211c3d6 100644
--- a/third_party/robolectric/BUILD.gn
+++ b/third_party/robolectric/BUILD.gn
@@ -6,19 +6,16 @@
 
 # GYP: //third_party/robolectric/robolectric.gyp:android-all-4.3_r2-robolectric-0
 java_prebuilt("android-all-4.3_r2-robolectric-0") {
-  visibility = [ ":*" ]
   jar_path = "lib/android-all-4.3_r2-robolectric-0.jar"
 }
 
 # GYP: //third_party/robolectric/robolectric.gyp:tagsoup-1.2
 java_prebuilt("tagsoup-1.2") {
-  visibility = [ ":*" ]
   jar_path = "lib/tagsoup-1.2.jar"
 }
 
 # GYP: //third_party/robolectric/robolectric.gyp:json-20080701
 java_prebuilt("json-20080701") {
-  visibility = [ ":*" ]
   jar_path = "lib/json-20080701.jar"
 }
 
diff --git a/third_party/sqlite/amalgamation/sqlite3.c b/third_party/sqlite/amalgamation/sqlite3.c
index 7d748a5..82dc7803 100644
--- a/third_party/sqlite/amalgamation/sqlite3.c
+++ b/third_party/sqlite/amalgamation/sqlite3.c
@@ -12996,7 +12996,7 @@
 SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int);
 SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName);
 SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
-SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*);
+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int);
 SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*);
 SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*);
 SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *);
@@ -80848,10 +80848,11 @@
 SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(
   Parse *pParse,           /* Parsing context */
   Expr *pExpr,             /* Add the "COLLATE" clause to this expression */
-  const Token *pCollName   /* Name of collating sequence */
+  const Token *pCollName,  /* Name of collating sequence */
+  int dequote              /* True to dequote pCollName */
 ){
   if( pCollName->n>0 ){
-    Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, 1);
+    Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote);
     if( pNew ){
       pNew->pLeft = pExpr;
       pNew->flags |= EP_Collate|EP_Skip;
@@ -80865,7 +80866,7 @@
   assert( zC!=0 );
   s.z = zC;
   s.n = sqlite3Strlen30(s.z);
-  return sqlite3ExprAddCollateToken(pParse, pExpr, &s);
+  return sqlite3ExprAddCollateToken(pParse, pExpr, &s, 0);
 }
 
 /*
@@ -114998,7 +114999,7 @@
     Expr *pNewExpr2;
     int idxNew1;
     int idxNew2;
-    Token sCollSeqName;  /* Name of collating sequence */
+    const char *zCollSeqName;     /* Name of collating sequence */
 
     pLeft = pExpr->x.pList->a[1].pExpr;
     pStr2 = sqlite3ExprDup(db, pStr1, 0);
@@ -115018,11 +115019,10 @@
       }
       *pC = c + 1;
     }
-    sCollSeqName.z = noCase ? "NOCASE" : "BINARY";
-    sCollSeqName.n = 6;
+    zCollSeqName = noCase ? "NOCASE" : "BINARY";
     pNewExpr1 = sqlite3ExprDup(db, pLeft, 0);
     pNewExpr1 = sqlite3PExpr(pParse, TK_GE, 
-           sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName),
+           sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName),
            pStr1, 0);
     transferJoinMarkings(pNewExpr1, pExpr);
     idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC);
@@ -115030,7 +115030,7 @@
     exprAnalyze(pSrc, pWC, idxNew1);
     pNewExpr2 = sqlite3ExprDup(db, pLeft, 0);
     pNewExpr2 = sqlite3PExpr(pParse, TK_LT,
-           sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName),
+           sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName),
            pStr2, 0);
     transferJoinMarkings(pNewExpr2, pExpr);
     idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC);
@@ -123107,7 +123107,7 @@
         break;
       case 193: /* expr ::= expr COLLATE ID|STRING */
 {
-  yygotominor.yy346.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy346.pExpr, &yymsp[0].minor.yy0);
+  yygotominor.yy346.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy346.pExpr, &yymsp[0].minor.yy0, 1);
   yygotominor.yy346.zStart = yymsp[-2].minor.yy346.zStart;
   yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
 }
@@ -123387,7 +123387,7 @@
         break;
       case 244: /* idxlist ::= idxlist COMMA nm collate sortorder */
 {
-  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0);
+  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0, 1);
   yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, p);
   sqlite3ExprListSetName(pParse,yygotominor.yy14,&yymsp[-2].minor.yy0,1);
   sqlite3ExprListCheckLength(pParse, yygotominor.yy14, "index");
@@ -123396,7 +123396,7 @@
         break;
       case 245: /* idxlist ::= nm collate sortorder */
 {
-  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0);
+  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0, 1);
   yygotominor.yy14 = sqlite3ExprListAppend(pParse,0, p);
   sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[-2].minor.yy0, 1);
   sqlite3ExprListCheckLength(pParse, yygotominor.yy14, "index");
diff --git a/third_party/sqlite/patches/0018-backport-Fix-collation-dequoting.patch b/third_party/sqlite/patches/0018-backport-Fix-collation-dequoting.patch
new file mode 100644
index 0000000..f8f1597
--- /dev/null
+++ b/third_party/sqlite/patches/0018-backport-Fix-collation-dequoting.patch
@@ -0,0 +1,205 @@
+From f4b79cfaefb87fa2c37a860c5a64f320a5265f99 Mon Sep 17 00:00:00 2001
+From: Scott Hess <shess@chromium.org>
+Date: Mon, 23 Mar 2015 11:24:11 -0700
+Subject: [PATCH] [backport] Fix collation dequoting.
+
+Backport https://www.sqlite.org/src/info/eddc05e7bb31fae7
+"Fix a problem causing collation sequence names to be dequoted
+multiple times under some circumstances."
+
+BUG=469082
+---
+ third_party/sqlite/src/src/expr.c         |  7 ++--
+ third_party/sqlite/src/src/parse.y        |  6 ++--
+ third_party/sqlite/src/src/sqliteInt.h    |  2 +-
+ third_party/sqlite/src/src/where.c        |  9 +++--
+ third_party/sqlite/src/test/collate1.test | 58 +++++++++++++++++++++++++++++--
+ 5 files changed, 68 insertions(+), 14 deletions(-)
+
+diff --git a/third_party/sqlite/src/src/expr.c b/third_party/sqlite/src/src/expr.c
+index 65f211e..2d96c8d 100644
+--- a/third_party/sqlite/src/src/expr.c
++++ b/third_party/sqlite/src/src/expr.c
+@@ -69,10 +69,11 @@ char sqlite3ExprAffinity(Expr *pExpr){
+ Expr *sqlite3ExprAddCollateToken(
+   Parse *pParse,           /* Parsing context */
+   Expr *pExpr,             /* Add the "COLLATE" clause to this expression */
+-  const Token *pCollName   /* Name of collating sequence */
++  const Token *pCollName,  /* Name of collating sequence */
++  int dequote              /* True to dequote pCollName */
+ ){
+   if( pCollName->n>0 ){
+-    Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, 1);
++    Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote);
+     if( pNew ){
+       pNew->pLeft = pExpr;
+       pNew->flags |= EP_Collate|EP_Skip;
+@@ -86,7 +87,7 @@ Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){
+   assert( zC!=0 );
+   s.z = zC;
+   s.n = sqlite3Strlen30(s.z);
+-  return sqlite3ExprAddCollateToken(pParse, pExpr, &s);
++  return sqlite3ExprAddCollateToken(pParse, pExpr, &s, 0);
+ }
+ 
+ /*
+diff --git a/third_party/sqlite/src/src/parse.y b/third_party/sqlite/src/src/parse.y
+index 877827e..d888cff 100644
+--- a/third_party/sqlite/src/src/parse.y
++++ b/third_party/sqlite/src/src/parse.y
+@@ -854,7 +854,7 @@ expr(A) ::= VARIABLE(X).     {
+   spanSet(&A, &X, &X);
+ }
+ expr(A) ::= expr(E) COLLATE ids(C). {
+-  A.pExpr = sqlite3ExprAddCollateToken(pParse, E.pExpr, &C);
++  A.pExpr = sqlite3ExprAddCollateToken(pParse, E.pExpr, &C, 1);
+   A.zStart = E.zStart;
+   A.zEnd = &C.z[C.n];
+ }
+@@ -1200,14 +1200,14 @@ uniqueflag(A) ::= .        {A = OE_None;}
+ idxlist_opt(A) ::= .                         {A = 0;}
+ idxlist_opt(A) ::= LP idxlist(X) RP.         {A = X;}
+ idxlist(A) ::= idxlist(X) COMMA nm(Y) collate(C) sortorder(Z).  {
+-  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &C);
++  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &C, 1);
+   A = sqlite3ExprListAppend(pParse,X, p);
+   sqlite3ExprListSetName(pParse,A,&Y,1);
+   sqlite3ExprListCheckLength(pParse, A, "index");
+   if( A ) A->a[A->nExpr-1].sortOrder = (u8)Z;
+ }
+ idxlist(A) ::= nm(Y) collate(C) sortorder(Z). {
+-  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &C);
++  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &C, 1);
+   A = sqlite3ExprListAppend(pParse,0, p);
+   sqlite3ExprListSetName(pParse, A, &Y, 1);
+   sqlite3ExprListCheckLength(pParse, A, "index");
+diff --git a/third_party/sqlite/src/src/sqliteInt.h b/third_party/sqlite/src/src/sqliteInt.h
+index 9d6a7d8..264f4fe 100644
+--- a/third_party/sqlite/src/src/sqliteInt.h
++++ b/third_party/sqlite/src/src/sqliteInt.h
+@@ -3462,7 +3462,7 @@ int sqlite3ReadSchema(Parse *pParse);
+ CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int);
+ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName);
+ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
+-Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*);
++Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int);
+ Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*);
+ Expr *sqlite3ExprSkipCollate(Expr*);
+ int sqlite3CheckCollSeq(Parse *, CollSeq *);
+diff --git a/third_party/sqlite/src/src/where.c b/third_party/sqlite/src/src/where.c
+index bc01107..793b01d 100644
+--- a/third_party/sqlite/src/src/where.c
++++ b/third_party/sqlite/src/src/where.c
+@@ -1252,7 +1252,7 @@ static void exprAnalyze(
+     Expr *pNewExpr2;
+     int idxNew1;
+     int idxNew2;
+-    Token sCollSeqName;  /* Name of collating sequence */
++    const char *zCollSeqName;     /* Name of collating sequence */
+ 
+     pLeft = pExpr->x.pList->a[1].pExpr;
+     pStr2 = sqlite3ExprDup(db, pStr1, 0);
+@@ -1272,11 +1272,10 @@ static void exprAnalyze(
+       }
+       *pC = c + 1;
+     }
+-    sCollSeqName.z = noCase ? "NOCASE" : "BINARY";
+-    sCollSeqName.n = 6;
++    zCollSeqName = noCase ? "NOCASE" : "BINARY";
+     pNewExpr1 = sqlite3ExprDup(db, pLeft, 0);
+     pNewExpr1 = sqlite3PExpr(pParse, TK_GE, 
+-           sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName),
++           sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName),
+            pStr1, 0);
+     transferJoinMarkings(pNewExpr1, pExpr);
+     idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC);
+@@ -1284,7 +1283,7 @@ static void exprAnalyze(
+     exprAnalyze(pSrc, pWC, idxNew1);
+     pNewExpr2 = sqlite3ExprDup(db, pLeft, 0);
+     pNewExpr2 = sqlite3PExpr(pParse, TK_LT,
+-           sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName),
++           sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName),
+            pStr2, 0);
+     transferJoinMarkings(pNewExpr2, pExpr);
+     idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC);
+diff --git a/third_party/sqlite/src/test/collate1.test b/third_party/sqlite/src/test/collate1.test
+index 2085415..0716ac7 100644
+--- a/third_party/sqlite/src/test/collate1.test
++++ b/third_party/sqlite/src/test/collate1.test
+@@ -10,12 +10,12 @@
+ #
+ #***********************************************************************
+ # This file implements regression tests for SQLite library.  The
+-# focus of this script is page cache subsystem.
++# focus of this script is testing collation sequences.
+ #
+-# $Id: collate1.test,v 1.5 2007/02/01 23:02:46 drh Exp $
+ 
+ set testdir [file dirname $argv0]
+ source $testdir/tester.tcl
++set testprefix collate1
+ 
+ #
+ # Tests are roughly organised as follows:
+@@ -333,4 +333,58 @@ do_test collate1-5.3 {
+   }
+ } {1 2}
+ 
++
++
++#-------------------------------------------------------------------------
++# Fix problems with handling collation sequences named '"""'.
++#
++do_execsql_test 6.1 {
++  SELECT """""""";
++} {\"\"\"}
++
++do_catchsql_test 6.2 {
++  CREATE TABLE x1(a);
++  SELECT a FROM x1 ORDER BY a COLLATE """""""";
++} {1 {no such collation sequence: """}}
++
++do_catchsql_test 6.3 {
++  SELECT a FROM x1 ORDER BY 1 COLLATE """""""";
++} {1 {no such collation sequence: """}}
++
++do_catchsql_test 6.4 {
++  SELECT 0 UNION SELECT 0 ORDER BY 1 COLLATE """""""";
++} {1 {no such collation sequence: """}}
++
++db collate {"""} [list string compare -nocase]
++
++do_execsql_test 6.5 {
++  PRAGMA foreign_keys = ON;
++  CREATE TABLE p1(a PRIMARY KEY COLLATE '"""');
++  CREATE TABLE c1(x, y REFERENCES p1);
++} {}
++
++do_execsql_test 6.6 { 
++  INSERT INTO p1 VALUES('abc'); 
++  INSERT INTO c1 VALUES(1, 'ABC'); 
++}
++
++ifcapable foreignkey {
++  do_catchsql_test 6.7 { 
++    DELETE FROM p1 WHERE rowid = 1 
++  } {1 {FOREIGN KEY constraint failed}}
++}
++
++do_execsql_test 6.8 { 
++  INSERT INTO p1 VALUES('abb');
++  INSERT INTO p1 VALUES('wxz');
++  INSERT INTO p1 VALUES('wxy');
++
++  INSERT INTO c1 VALUES(2, 'abb');
++  INSERT INTO c1 VALUES(3, 'wxz');
++  INSERT INTO c1 VALUES(4, 'WXY');
++  SELECT x, y FROM c1 ORDER BY y COLLATE """""""";
++} {2 abb 1 ABC 4 WXY 3 wxz}
++
+ finish_test
++
++
+-- 
+2.2.1
+
diff --git a/third_party/sqlite/src/src/expr.c b/third_party/sqlite/src/src/expr.c
index 65f211e3..2d96c8d 100644
--- a/third_party/sqlite/src/src/expr.c
+++ b/third_party/sqlite/src/src/expr.c
@@ -69,10 +69,11 @@
 Expr *sqlite3ExprAddCollateToken(
   Parse *pParse,           /* Parsing context */
   Expr *pExpr,             /* Add the "COLLATE" clause to this expression */
-  const Token *pCollName   /* Name of collating sequence */
+  const Token *pCollName,  /* Name of collating sequence */
+  int dequote              /* True to dequote pCollName */
 ){
   if( pCollName->n>0 ){
-    Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, 1);
+    Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote);
     if( pNew ){
       pNew->pLeft = pExpr;
       pNew->flags |= EP_Collate|EP_Skip;
@@ -86,7 +87,7 @@
   assert( zC!=0 );
   s.z = zC;
   s.n = sqlite3Strlen30(s.z);
-  return sqlite3ExprAddCollateToken(pParse, pExpr, &s);
+  return sqlite3ExprAddCollateToken(pParse, pExpr, &s, 0);
 }
 
 /*
diff --git a/third_party/sqlite/src/src/parse.y b/third_party/sqlite/src/src/parse.y
index 877827e6..d888cff 100644
--- a/third_party/sqlite/src/src/parse.y
+++ b/third_party/sqlite/src/src/parse.y
@@ -854,7 +854,7 @@
   spanSet(&A, &X, &X);
 }
 expr(A) ::= expr(E) COLLATE ids(C). {
-  A.pExpr = sqlite3ExprAddCollateToken(pParse, E.pExpr, &C);
+  A.pExpr = sqlite3ExprAddCollateToken(pParse, E.pExpr, &C, 1);
   A.zStart = E.zStart;
   A.zEnd = &C.z[C.n];
 }
@@ -1200,14 +1200,14 @@
 idxlist_opt(A) ::= .                         {A = 0;}
 idxlist_opt(A) ::= LP idxlist(X) RP.         {A = X;}
 idxlist(A) ::= idxlist(X) COMMA nm(Y) collate(C) sortorder(Z).  {
-  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &C);
+  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &C, 1);
   A = sqlite3ExprListAppend(pParse,X, p);
   sqlite3ExprListSetName(pParse,A,&Y,1);
   sqlite3ExprListCheckLength(pParse, A, "index");
   if( A ) A->a[A->nExpr-1].sortOrder = (u8)Z;
 }
 idxlist(A) ::= nm(Y) collate(C) sortorder(Z). {
-  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &C);
+  Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &C, 1);
   A = sqlite3ExprListAppend(pParse,0, p);
   sqlite3ExprListSetName(pParse, A, &Y, 1);
   sqlite3ExprListCheckLength(pParse, A, "index");
diff --git a/third_party/sqlite/src/src/sqliteInt.h b/third_party/sqlite/src/src/sqliteInt.h
index 9d6a7d8..264f4fe 100644
--- a/third_party/sqlite/src/src/sqliteInt.h
+++ b/third_party/sqlite/src/src/sqliteInt.h
@@ -3462,7 +3462,7 @@
 CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int);
 CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName);
 CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
-Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*);
+Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int);
 Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*);
 Expr *sqlite3ExprSkipCollate(Expr*);
 int sqlite3CheckCollSeq(Parse *, CollSeq *);
diff --git a/third_party/sqlite/src/src/where.c b/third_party/sqlite/src/src/where.c
index bc01107..793b01d 100644
--- a/third_party/sqlite/src/src/where.c
+++ b/third_party/sqlite/src/src/where.c
@@ -1252,7 +1252,7 @@
     Expr *pNewExpr2;
     int idxNew1;
     int idxNew2;
-    Token sCollSeqName;  /* Name of collating sequence */
+    const char *zCollSeqName;     /* Name of collating sequence */
 
     pLeft = pExpr->x.pList->a[1].pExpr;
     pStr2 = sqlite3ExprDup(db, pStr1, 0);
@@ -1272,11 +1272,10 @@
       }
       *pC = c + 1;
     }
-    sCollSeqName.z = noCase ? "NOCASE" : "BINARY";
-    sCollSeqName.n = 6;
+    zCollSeqName = noCase ? "NOCASE" : "BINARY";
     pNewExpr1 = sqlite3ExprDup(db, pLeft, 0);
     pNewExpr1 = sqlite3PExpr(pParse, TK_GE, 
-           sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName),
+           sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName),
            pStr1, 0);
     transferJoinMarkings(pNewExpr1, pExpr);
     idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC);
@@ -1284,7 +1283,7 @@
     exprAnalyze(pSrc, pWC, idxNew1);
     pNewExpr2 = sqlite3ExprDup(db, pLeft, 0);
     pNewExpr2 = sqlite3PExpr(pParse, TK_LT,
-           sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName),
+           sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName),
            pStr2, 0);
     transferJoinMarkings(pNewExpr2, pExpr);
     idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC);
diff --git a/third_party/sqlite/src/test/collate1.test b/third_party/sqlite/src/test/collate1.test
index 2085415..0716ac74 100644
--- a/third_party/sqlite/src/test/collate1.test
+++ b/third_party/sqlite/src/test/collate1.test
@@ -10,12 +10,12 @@
 #
 #***********************************************************************
 # This file implements regression tests for SQLite library.  The
-# focus of this script is page cache subsystem.
+# focus of this script is testing collation sequences.
 #
-# $Id: collate1.test,v 1.5 2007/02/01 23:02:46 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
+set testprefix collate1
 
 #
 # Tests are roughly organised as follows:
@@ -333,4 +333,58 @@
   }
 } {1 2}
 
+
+
+#-------------------------------------------------------------------------
+# Fix problems with handling collation sequences named '"""'.
+#
+do_execsql_test 6.1 {
+  SELECT """""""";
+} {\"\"\"}
+
+do_catchsql_test 6.2 {
+  CREATE TABLE x1(a);
+  SELECT a FROM x1 ORDER BY a COLLATE """""""";
+} {1 {no such collation sequence: """}}
+
+do_catchsql_test 6.3 {
+  SELECT a FROM x1 ORDER BY 1 COLLATE """""""";
+} {1 {no such collation sequence: """}}
+
+do_catchsql_test 6.4 {
+  SELECT 0 UNION SELECT 0 ORDER BY 1 COLLATE """""""";
+} {1 {no such collation sequence: """}}
+
+db collate {"""} [list string compare -nocase]
+
+do_execsql_test 6.5 {
+  PRAGMA foreign_keys = ON;
+  CREATE TABLE p1(a PRIMARY KEY COLLATE '"""');
+  CREATE TABLE c1(x, y REFERENCES p1);
+} {}
+
+do_execsql_test 6.6 { 
+  INSERT INTO p1 VALUES('abc'); 
+  INSERT INTO c1 VALUES(1, 'ABC'); 
+}
+
+ifcapable foreignkey {
+  do_catchsql_test 6.7 { 
+    DELETE FROM p1 WHERE rowid = 1 
+  } {1 {FOREIGN KEY constraint failed}}
+}
+
+do_execsql_test 6.8 { 
+  INSERT INTO p1 VALUES('abb');
+  INSERT INTO p1 VALUES('wxz');
+  INSERT INTO p1 VALUES('wxy');
+
+  INSERT INTO c1 VALUES(2, 'abb');
+  INSERT INTO c1 VALUES(3, 'wxz');
+  INSERT INTO c1 VALUES(4, 'WXY');
+  SELECT x, y FROM c1 ORDER BY y COLLATE """""""";
+} {2 abb 1 ABC 4 WXY 3 wxz}
+
 finish_test
+
+
diff --git a/third_party/woff2/.gitignore b/third_party/woff2/.gitignore
new file mode 100644
index 0000000..dc6b1e2
--- /dev/null
+++ b/third_party/woff2/.gitignore
@@ -0,0 +1,3 @@
+*.o
+/woff2_compress
+/woff2_decompress
diff --git a/third_party/woff2/BUILD.gn b/third_party/woff2/BUILD.gn
new file mode 100644
index 0000000..c1495be7
--- /dev/null
+++ b/third_party/woff2/BUILD.gn
@@ -0,0 +1,33 @@
+# 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.
+
+source_set("woff2_dec") {
+  sources = [
+    "src/buffer.h",
+    "src/round.h",
+    "src/store_bytes.h",
+    "src/table_tags.cc",
+    "src/table_tags.h",
+    "src/woff2_common.h",
+    "src/woff2_dec.cc",
+    "src/woff2_dec.h",
+  ]
+
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [ "//build/config/compiler:no_chromium_code" ]
+
+  deps = [
+    "//third_party/brotli",
+  ]
+
+  include_dirs = [
+    "src",
+    "//third_party/brotli/dec",
+  ]
+
+  # TODO(ksakamoto): http://crbug.com/167187
+  if (is_win) {
+    cflags = [ "/wd4267" ]  # Conversion from size_t to 'type'.
+  }
+}
diff --git a/third_party/woff2/LICENSE b/third_party/woff2/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/third_party/woff2/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/woff2/Makefile b/third_party/woff2/Makefile
new file mode 100644
index 0000000..fcb9525
--- /dev/null
+++ b/third_party/woff2/Makefile
@@ -0,0 +1,42 @@
+OS := $(shell uname)
+
+IDIRS=-I./brotli/dec/ -I./brotli/enc/ -I./src
+
+CXX = g++
+LFLAGS =
+GFLAGS=-no-canonical-prefixes -fno-omit-frame-pointer -m64
+CXXFLAGS = -c $(IDIRS) -std=c++0x $(GFLAGS)
+
+ifeq ($(OS), Darwin)
+  CXXFLAGS += -DOS_MACOSX
+else
+  CXXFLAGS += -fno-tree-vrp
+endif
+
+SRCDIR = src
+
+OUROBJ = font.o glyph.o normalize.o table_tags.o transform.o \
+         woff2_dec.o woff2_enc.o
+
+BROTLI = brotli
+ENCOBJ = $(BROTLI)/enc/*.o
+DECOBJ = $(BROTLI)/dec/*.o
+
+OBJS = $(patsubst %, $(SRCDIR)/%, $(OUROBJ))
+EXECUTABLES=woff2_compress woff2_decompress
+
+EXE_OBJS=$(patsubst %, $(SRCDIR)/%.o, $(EXECUTABLES))
+
+all : $(OBJS) $(EXECUTABLES)
+
+$(EXECUTABLES) : $(EXE_OBJS) deps
+	$(CXX) $(LFLAGS) $(OBJS) $(ENCOBJ) $(DECOBJ) $(SRCDIR)/$@.o -o $@
+
+deps :
+	make -C $(BROTLI)/dec
+	make -C $(BROTLI)/enc
+
+clean :
+	rm -f $(OBJS) $(EXE_OBJS) $(EXECUTABLES)
+	make -C $(BROTLI)/dec clean
+	make -C $(BROTLI)/enc clean
diff --git a/third_party/woff2/OWNERS b/third_party/woff2/OWNERS
new file mode 100644
index 0000000..458697e6
--- /dev/null
+++ b/third_party/woff2/OWNERS
@@ -0,0 +1,2 @@
+ksakamoto@chromium.org
+bashi@chromium.org
diff --git a/third_party/woff2/README.chromium b/third_party/woff2/README.chromium
new file mode 100644
index 0000000..4b6c27ab
--- /dev/null
+++ b/third_party/woff2/README.chromium
@@ -0,0 +1,16 @@
+Name: woff2
+URL: https://github.com/google/woff2
+Version: 445f541996fe8376f3976d35692fd2b9a6eedf2d
+License: Apache 2.0
+License File: LICENSE
+Security Critical: yes
+
+Description:
+The reference implementation of the WOFF 2.0 web font compression
+format (http://www.w3.org/TR/WOFF2/).
+
+Local Modifications:
+
+- BUILD.gn: Added.
+- woff2.gyp: Added.
+- Cherry-picked https://github.com/google/woff2/commit/0c1ec8895bce7c7b2da66ebc6e9887ef2e0c1a55.
diff --git a/third_party/woff2/README.md b/third_party/woff2/README.md
new file mode 100644
index 0000000..ea55a36
--- /dev/null
+++ b/third_party/woff2/README.md
@@ -0,0 +1,46 @@
+This is a README for the font compression reference code. There are several
+compression related modules in this repository.
+
+brotli/ contains reference code for the Brotli byte-level compression
+algorithm. Note that it is licensed under an Apache 2 license.
+
+src/ contains the C++ code for compressing and decompressing fonts.
+
+# Build & Run
+
+This document documents how to run the compression reference code. At this
+writing, the code, while it is intended to produce a bytestream that can be
+reconstructed into a working font, the reference decompression code is not
+done, and the exact format of that bytestream is subject to change.
+
+## Build
+
+On a standard Unix-style environment:
+
+```
+git clone https://github.com/google/woff2.git
+cd woff2
+git submodule init
+git submodule update
+make clean all
+```
+
+## Run
+
+```
+woff2_compress myfont.ttf
+woff2_decompress myfont.woff2
+```
+
+# References
+
+http://www.w3.org/TR/WOFF2/
+http://www.w3.org/Submission/MTX/
+
+Also please refer to documents (currently Google Docs):
+
+WOFF Ultra Condensed file format: proposals and discussion of wire format
+issues (PDF is in docs/ directory)
+
+WIFF Ultra Condensed: more discussion of results and compression techniques.
+This tool was used to prepare the data in that document.
diff --git a/third_party/woff2/src/buffer.h b/third_party/woff2/src/buffer.h
new file mode 100644
index 0000000..3595e84
--- /dev/null
+++ b/third_party/woff2/src/buffer.h
@@ -0,0 +1,172 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// The parts of ots.h & opentype-sanitiser.h that we need, taken from the
+// https://code.google.com/p/ots/ project.
+
+#ifndef WOFF2_BUFFER_H_
+#define WOFF2_BUFFER_H_
+
+#if defined(_WIN32)
+#include <stdlib.h>
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#define ntohl(x) _byteswap_ulong (x)
+#define ntohs(x) _byteswap_ushort (x)
+#define htonl(x) _byteswap_ulong (x)
+#define htons(x) _byteswap_ushort (x)
+#else
+#include <arpa/inet.h>
+#include <stdint.h>
+#endif
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+
+namespace woff2 {
+
+#if defined(_MSC_VER) || !defined(FONT_COMPRESSION_DEBUG)
+#define FONT_COMPRESSION_FAILURE() false
+#else
+#define FONT_COMPRESSION_FAILURE() \
+  util::compression::font::Failure(__FILE__, __LINE__, __PRETTY_FUNCTION__)
+inline bool Failure(const char *f, int l, const char *fn) {
+  fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn);
+  fflush(stderr);
+  return false;
+}
+#endif
+
+// -----------------------------------------------------------------------------
+// Buffer helper class
+//
+// This class perform some trival buffer operations while checking for
+// out-of-bounds errors. As a family they return false if anything is amiss,
+// updating the current offset otherwise.
+// -----------------------------------------------------------------------------
+class Buffer {
+ public:
+  Buffer(const uint8_t *buffer, size_t len)
+      : buffer_(buffer),
+        length_(len),
+        offset_(0) { }
+
+  bool Skip(size_t n_bytes) {
+    return Read(NULL, n_bytes);
+  }
+
+  bool Read(uint8_t *buffer, size_t n_bytes) {
+    if (n_bytes > 1024 * 1024 * 1024) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    if ((offset_ + n_bytes > length_) ||
+        (offset_ > length_ - n_bytes)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    if (buffer) {
+      std::memcpy(buffer, buffer_ + offset_, n_bytes);
+    }
+    offset_ += n_bytes;
+    return true;
+  }
+
+  inline bool ReadU8(uint8_t *value) {
+    if (offset_ + 1 > length_) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    *value = buffer_[offset_];
+    ++offset_;
+    return true;
+  }
+
+  bool ReadU16(uint16_t *value) {
+    if (offset_ + 2 > length_) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
+    *value = ntohs(*value);
+    offset_ += 2;
+    return true;
+  }
+
+  bool ReadS16(int16_t *value) {
+    return ReadU16(reinterpret_cast<uint16_t*>(value));
+  }
+
+  bool ReadU24(uint32_t *value) {
+    if (offset_ + 3 > length_) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    *value = static_cast<uint32_t>(buffer_[offset_]) << 16 |
+        static_cast<uint32_t>(buffer_[offset_ + 1]) << 8 |
+        static_cast<uint32_t>(buffer_[offset_ + 2]);
+    offset_ += 3;
+    return true;
+  }
+
+  bool ReadU32(uint32_t *value) {
+    if (offset_ + 4 > length_) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
+    *value = ntohl(*value);
+    offset_ += 4;
+    return true;
+  }
+
+  bool ReadS32(int32_t *value) {
+    return ReadU32(reinterpret_cast<uint32_t*>(value));
+  }
+
+  bool ReadTag(uint32_t *value) {
+    if (offset_ + 4 > length_) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
+    offset_ += 4;
+    return true;
+  }
+
+  bool ReadR64(uint64_t *value) {
+    if (offset_ + 8 > length_) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint64_t));
+    offset_ += 8;
+    return true;
+  }
+
+  const uint8_t *buffer() const { return buffer_; }
+  size_t offset() const { return offset_; }
+  size_t length() const { return length_; }
+
+  void set_offset(size_t newoffset) { offset_ = newoffset; }
+
+ private:
+  const uint8_t * const buffer_;
+  const size_t length_;
+  size_t offset_;
+};
+
+} // namespace woff2
+
+#endif  // WOFF2_BUFFER_H_
diff --git a/third_party/woff2/src/file.h b/third_party/woff2/src/file.h
new file mode 100644
index 0000000..69a92f8
--- /dev/null
+++ b/third_party/woff2/src/file.h
@@ -0,0 +1,40 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// File IO helpers
+
+#ifndef WOFF2_FILE_H_
+#define WOFF2_FILE_H_
+
+#include <fstream>
+#include <iterator>
+
+namespace woff2 {
+
+inline std::string GetFileContent(std::string filename) {
+  std::ifstream ifs(filename.c_str(), std::ios::binary);
+  return std::string(
+    std::istreambuf_iterator<char>(ifs.rdbuf()),
+    std::istreambuf_iterator<char>());
+}
+
+inline void SetFileContents(std::string filename, std::string content) {
+  std::ofstream ofs(filename.c_str(), std::ios::binary);
+  std::copy(content.begin(),
+            content.end(),
+            std::ostream_iterator<char>(ofs));
+}
+
+} // namespace woff2
+#endif  // WOFF2_FILE_H_
diff --git a/third_party/woff2/src/font.cc b/third_party/woff2/src/font.cc
new file mode 100644
index 0000000..a7e5607
--- /dev/null
+++ b/third_party/woff2/src/font.cc
@@ -0,0 +1,198 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Font management utilities
+
+#include "./font.h"
+
+#include <algorithm>
+
+#include "./buffer.h"
+#include "./port.h"
+#include "./store_bytes.h"
+#include "./table_tags.h"
+
+namespace woff2 {
+
+Font::Table* Font::FindTable(uint32_t tag) {
+  std::map<uint32_t, Font::Table>::iterator it = tables.find(tag);
+  return it == tables.end() ? 0 : &it->second;
+}
+
+const Font::Table* Font::FindTable(uint32_t tag) const {
+  std::map<uint32_t, Font::Table>::const_iterator it = tables.find(tag);
+  return it == tables.end() ? 0 : &it->second;
+}
+
+bool ReadFont(const uint8_t* data, size_t len, Font* font) {
+  Buffer file(data, len);
+
+  // We don't care about the search_range, entry_selector and range_shift
+  // fields, they will always be computed upon writing the font.
+  if (!file.ReadU32(&font->flavor) ||
+      !file.ReadU16(&font->num_tables) ||
+      !file.Skip(6)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+
+  std::map<uint32_t, uint32_t> intervals;
+  for (uint16_t i = 0; i < font->num_tables; ++i) {
+    Font::Table table;
+    if (!file.ReadU32(&table.tag) ||
+        !file.ReadU32(&table.checksum) ||
+        !file.ReadU32(&table.offset) ||
+        !file.ReadU32(&table.length)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    if ((table.offset & 3) != 0 ||
+        table.length > len ||
+        len - table.length < table.offset) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    intervals[table.offset] = table.length;
+    table.data = data + table.offset;
+    if (font->tables.find(table.tag) != font->tables.end()) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    font->tables[table.tag] = table;
+  }
+
+  // Check that tables are non-overlapping.
+  uint32_t last_offset = 12UL + 16UL * font->num_tables;
+  for (const auto& i : intervals) {
+    if (i.first < last_offset || i.first + i.second < i.first) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    last_offset = i.first + i.second;
+  }
+  return true;
+}
+
+size_t FontFileSize(const Font& font) {
+  size_t max_offset = 12ULL + 16ULL * font.num_tables;
+  for (const auto& i : font.tables) {
+    const Font::Table& table = i.second;
+    size_t padding_size = (4 - (table.length & 3)) & 3;
+    size_t end_offset = (padding_size + table.offset) + table.length;
+    max_offset = std::max(max_offset, end_offset);
+  }
+  return max_offset;
+}
+
+bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size) {
+  if (dst_size < 12ULL + 16ULL * font.num_tables) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  size_t offset = 0;
+  StoreU32(font.flavor, &offset, dst);
+  Store16(font.num_tables, &offset, dst);
+  uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0;
+  uint16_t search_range = max_pow2 ? 1 << (max_pow2 + 4) : 0;
+  uint16_t range_shift = (font.num_tables << 4) - search_range;
+  Store16(search_range, &offset, dst);
+  Store16(max_pow2, &offset, dst);
+  Store16(range_shift, &offset, dst);
+  for (const auto& i : font.tables) {
+    const Font::Table& table = i.second;
+    StoreU32(table.tag, &offset, dst);
+    StoreU32(table.checksum, &offset, dst);
+    StoreU32(table.offset, &offset, dst);
+    StoreU32(table.length, &offset, dst);
+    if (table.offset + table.length < table.offset ||
+        dst_size < table.offset + table.length) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    memcpy(dst + table.offset, table.data, table.length);
+    size_t padding_size = (4 - (table.length & 3)) & 3;
+    if (table.offset + table.length + padding_size < padding_size ||
+        dst_size < table.offset + table.length + padding_size) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    memset(dst + table.offset + table.length, 0, padding_size);
+  }
+  return true;
+}
+
+int NumGlyphs(const Font& font) {
+  const Font::Table* head_table = font.FindTable(kHeadTableTag);
+  const Font::Table* loca_table = font.FindTable(kLocaTableTag);
+  if (head_table == NULL || loca_table == NULL || head_table->length < 52) {
+    return 0;
+  }
+  int index_fmt = IndexFormat(font);
+  int num_glyphs = (loca_table->length / (index_fmt == 0 ? 2 : 4)) - 1;
+  return num_glyphs;
+}
+
+int IndexFormat(const Font& font) {
+  const Font::Table* head_table = font.FindTable(kHeadTableTag);
+  if (head_table == NULL) {
+    return 0;
+  }
+  return head_table->data[51];
+}
+
+bool GetGlyphData(const Font& font, int glyph_index,
+                  const uint8_t** glyph_data, size_t* glyph_size) {
+  if (glyph_index < 0) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  const Font::Table* head_table = font.FindTable(kHeadTableTag);
+  const Font::Table* loca_table = font.FindTable(kLocaTableTag);
+  const Font::Table* glyf_table = font.FindTable(kGlyfTableTag);
+  if (head_table == NULL || loca_table == NULL || glyf_table == NULL ||
+      head_table->length < 52) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+
+  int index_fmt = IndexFormat(font);
+
+  Buffer loca_buf(loca_table->data, loca_table->length);
+  if (index_fmt == 0) {
+    uint16_t offset1, offset2;
+    if (!loca_buf.Skip(2 * glyph_index) ||
+        !loca_buf.ReadU16(&offset1) ||
+        !loca_buf.ReadU16(&offset2) ||
+        offset2 < offset1 ||
+        2 * offset2 > glyf_table->length) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    *glyph_data = glyf_table->data + 2 * offset1;
+    *glyph_size = 2 * (offset2 - offset1);
+  } else {
+    uint32_t offset1, offset2;
+    if (!loca_buf.Skip(4 * glyph_index) ||
+        !loca_buf.ReadU32(&offset1) ||
+        !loca_buf.ReadU32(&offset2) ||
+        offset2 < offset1 ||
+        offset2 > glyf_table->length) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    *glyph_data = glyf_table->data + offset1;
+    *glyph_size = offset2 - offset1;
+  }
+  return true;
+}
+
+bool RemoveDigitalSignature(Font* font) {
+  std::map<uint32_t, Font::Table>::iterator it =
+      font->tables.find(kDsigTableTag);
+  if (it != font->tables.end()) {
+    font->tables.erase(it);
+    font->num_tables = font->tables.size();
+  }
+  return true;
+}
+
+} // namespace woff2
diff --git a/third_party/woff2/src/font.h b/third_party/woff2/src/font.h
new file mode 100644
index 0000000..08c414c
--- /dev/null
+++ b/third_party/woff2/src/font.h
@@ -0,0 +1,82 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Data model for a font file in sfnt format, reading and writing functions and
+// accessors for the glyph data.
+
+#ifndef WOFF2_FONT_H_
+#define WOFF2_FONT_H_
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <map>
+#include <vector>
+
+namespace woff2 {
+
+// Represents an sfnt font file. Only the table directory is parsed, for the
+// table data we only store a raw pointer, therefore a font object is valid only
+// as long the data from which it was parsed is around.
+struct Font {
+  uint32_t flavor;
+  uint16_t num_tables;
+
+  struct Table {
+    uint32_t tag;
+    uint32_t checksum;
+    uint32_t offset;
+    uint32_t length;
+    const uint8_t* data;
+
+    // Buffer used to mutate the data before writing out.
+    std::vector<uint8_t> buffer;
+  };
+  std::map<uint32_t, Table> tables;
+
+  Table* FindTable(uint32_t tag);
+  const Table* FindTable(uint32_t tag) const;
+};
+
+// Parses the font from the given data. Returns false on parsing failure or
+// buffer overflow. The font is valid only so long the input data pointer is
+// valid.
+bool ReadFont(const uint8_t* data, size_t len, Font* font);
+
+// Returns the file size of the font.
+size_t FontFileSize(const Font& font);
+
+// Writes the font into the specified dst buffer. The dst_size should be the
+// same as returned by FontFileSize(). Returns false upon buffer overflow (which
+// should not happen if dst_size was computed by FontFileSize()).
+bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size);
+
+// Returns the number of glyphs in the font.
+// NOTE: Currently this works only for TrueType-flavored fonts, will return
+// zero for CFF-flavored fonts.
+int NumGlyphs(const Font& font);
+
+// Returns the index format of the font
+int IndexFormat(const Font& font);
+
+// Sets *glyph_data and *glyph_size to point to the location of the glyph data
+// with the given index. Returns false if the glyph is not found.
+bool GetGlyphData(const Font& font, int glyph_index,
+                  const uint8_t** glyph_data, size_t* glyph_size);
+
+// Removes the digital signature (DSIG) table
+bool RemoveDigitalSignature(Font* font);
+
+} // namespace woff2
+
+#endif  // WOFF2_FONT_H_
diff --git a/third_party/woff2/src/glyph.cc b/third_party/woff2/src/glyph.cc
new file mode 100644
index 0000000..4cef0d9
--- /dev/null
+++ b/third_party/woff2/src/glyph.cc
@@ -0,0 +1,380 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Glyph manipulation
+
+#include "./glyph.h"
+
+#include <stdlib.h>
+#include <limits>
+#include "./buffer.h"
+#include "./store_bytes.h"
+
+namespace woff2 {
+
+static const int32_t kFLAG_ONCURVE = 1;
+static const int32_t kFLAG_XSHORT = 1 << 1;
+static const int32_t kFLAG_YSHORT = 1 << 2;
+static const int32_t kFLAG_REPEAT = 1 << 3;
+static const int32_t kFLAG_XREPEATSIGN = 1 << 4;
+static const int32_t kFLAG_YREPEATSIGN = 1 << 5;
+static const int32_t kFLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
+static const int32_t kFLAG_WE_HAVE_A_SCALE = 1 << 3;
+static const int32_t kFLAG_MORE_COMPONENTS = 1 << 5;
+static const int32_t kFLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
+static const int32_t kFLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
+static const int32_t kFLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
+
+bool ReadCompositeGlyphData(Buffer* buffer, Glyph* glyph) {
+  glyph->have_instructions = false;
+  glyph->composite_data = buffer->buffer() + buffer->offset();
+  size_t start_offset = buffer->offset();
+  uint16_t flags = kFLAG_MORE_COMPONENTS;
+  while (flags & kFLAG_MORE_COMPONENTS) {
+    if (!buffer->ReadU16(&flags)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    glyph->have_instructions |= (flags & kFLAG_WE_HAVE_INSTRUCTIONS) != 0;
+    size_t arg_size = 2;  // glyph index
+    if (flags & kFLAG_ARG_1_AND_2_ARE_WORDS) {
+      arg_size += 4;
+    } else {
+      arg_size += 2;
+    }
+    if (flags & kFLAG_WE_HAVE_A_SCALE) {
+      arg_size += 2;
+    } else if (flags & kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
+      arg_size += 4;
+    } else if (flags & kFLAG_WE_HAVE_A_TWO_BY_TWO) {
+      arg_size += 8;
+    }
+    if (!buffer->Skip(arg_size)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+  }
+  if (buffer->offset() - start_offset > std::numeric_limits<uint32_t>::max()) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  glyph->composite_data_size = buffer->offset() - start_offset;
+  return true;
+}
+
+bool ReadGlyph(const uint8_t* data, size_t len, Glyph* glyph) {
+  Buffer buffer(data, len);
+
+  int16_t num_contours;
+  if (!buffer.ReadS16(&num_contours)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+
+  if (num_contours == 0) {
+    // Empty glyph.
+    return true;
+  }
+
+  // Read the bounding box.
+  if (!buffer.ReadS16(&glyph->x_min) ||
+      !buffer.ReadS16(&glyph->y_min) ||
+      !buffer.ReadS16(&glyph->x_max) ||
+      !buffer.ReadS16(&glyph->y_max)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+
+  if (num_contours > 0) {
+    // Simple glyph.
+    glyph->contours.resize(num_contours);
+
+    // Read the number of points per contour.
+    uint16_t last_point_index = 0;
+    for (int i = 0; i < num_contours; ++i) {
+      uint16_t point_index;
+      if (!buffer.ReadU16(&point_index)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      uint16_t num_points = point_index - last_point_index + (i == 0 ? 1 : 0);
+      glyph->contours[i].resize(num_points);
+      last_point_index = point_index;
+    }
+
+    // Read the instructions.
+    if (!buffer.ReadU16(&glyph->instructions_size)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    glyph->instructions_data = data + buffer.offset();
+    if (!buffer.Skip(glyph->instructions_size)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+
+    // Read the run-length coded flags.
+    std::vector<std::vector<uint8_t> > flags(num_contours);
+    uint8_t flag = 0;
+    uint8_t flag_repeat = 0;
+    for (int i = 0; i < num_contours; ++i) {
+      flags[i].resize(glyph->contours[i].size());
+      for (int j = 0; j < glyph->contours[i].size(); ++j) {
+        if (flag_repeat == 0) {
+          if (!buffer.ReadU8(&flag)) {
+            return FONT_COMPRESSION_FAILURE();
+          }
+          if (flag & kFLAG_REPEAT) {
+            if (!buffer.ReadU8(&flag_repeat)) {
+              return FONT_COMPRESSION_FAILURE();
+            }
+          }
+        } else {
+          flag_repeat--;
+        }
+        flags[i][j] = flag;
+        glyph->contours[i][j].on_curve = flag & kFLAG_ONCURVE;
+      }
+    }
+
+    // Read the x coordinates.
+    int prev_x = 0;
+    for (int i = 0; i < num_contours; ++i) {
+      for (int j = 0; j < glyph->contours[i].size(); ++j) {
+        uint8_t flag = flags[i][j];
+        if (flag & kFLAG_XSHORT) {
+          // single byte x-delta coord value
+          uint8_t x_delta;
+          if (!buffer.ReadU8(&x_delta)) {
+            return FONT_COMPRESSION_FAILURE();
+          }
+          int sign = (flag & kFLAG_XREPEATSIGN) ? 1 : -1;
+          glyph->contours[i][j].x = prev_x + sign * x_delta;
+        } else {
+          // double byte x-delta coord value
+          int16_t x_delta = 0;
+          if (!(flag & kFLAG_XREPEATSIGN)) {
+            if (!buffer.ReadS16(&x_delta)) {
+              return FONT_COMPRESSION_FAILURE();
+            }
+          }
+          glyph->contours[i][j].x = prev_x + x_delta;
+        }
+        prev_x = glyph->contours[i][j].x;
+      }
+    }
+
+    // Read the y coordinates.
+    int prev_y = 0;
+    for (int i = 0; i < num_contours; ++i) {
+      for (int j = 0; j < glyph->contours[i].size(); ++j) {
+        uint8_t flag = flags[i][j];
+        if (flag & kFLAG_YSHORT) {
+          // single byte y-delta coord value
+          uint8_t y_delta;
+          if (!buffer.ReadU8(&y_delta)) {
+            return FONT_COMPRESSION_FAILURE();
+          }
+          int sign = (flag & kFLAG_YREPEATSIGN) ? 1 : -1;
+          glyph->contours[i][j].y = prev_y + sign * y_delta;
+        } else {
+          // double byte y-delta coord value
+          int16_t y_delta = 0;
+          if (!(flag & kFLAG_YREPEATSIGN)) {
+            if (!buffer.ReadS16(&y_delta)) {
+              return FONT_COMPRESSION_FAILURE();
+            }
+          }
+          glyph->contours[i][j].y = prev_y + y_delta;
+        }
+        prev_y = glyph->contours[i][j].y;
+      }
+    }
+  } else if (num_contours == -1) {
+    // Composite glyph.
+    if (!ReadCompositeGlyphData(&buffer, glyph)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    // Read the instructions.
+    if (glyph->have_instructions) {
+      if (!buffer.ReadU16(&glyph->instructions_size)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      glyph->instructions_data = data + buffer.offset();
+      if (!buffer.Skip(glyph->instructions_size)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+    } else {
+      glyph->instructions_size = 0;
+    }
+  } else {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  return true;
+}
+
+namespace {
+
+void StoreBbox(const Glyph& glyph, size_t* offset, uint8_t* dst) {
+  Store16(glyph.x_min, offset, dst);
+  Store16(glyph.y_min, offset, dst);
+  Store16(glyph.x_max, offset, dst);
+  Store16(glyph.y_max, offset, dst);
+}
+
+void StoreInstructions(const Glyph& glyph, size_t* offset, uint8_t* dst) {
+  Store16(glyph.instructions_size, offset, dst);
+  StoreBytes(glyph.instructions_data, glyph.instructions_size, offset, dst);
+}
+
+bool StoreEndPtsOfContours(const Glyph& glyph, size_t* offset, uint8_t* dst) {
+  int end_point = -1;
+  for (const auto& contour : glyph.contours) {
+    end_point += contour.size();
+    if (contour.size() > std::numeric_limits<uint16_t>::max() ||
+        end_point > std::numeric_limits<uint16_t>::max()) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    Store16(end_point, offset, dst);
+  }
+  return true;
+}
+
+bool StorePoints(const Glyph& glyph, size_t* offset,
+                 uint8_t* dst, size_t dst_size) {
+  int last_flag = -1;
+  int repeat_count = 0;
+  int last_x = 0;
+  int last_y = 0;
+  size_t x_bytes = 0;
+  size_t y_bytes = 0;
+
+  // Store the flags and calculate the total size of the x and y coordinates.
+  for (const auto& contour : glyph.contours) {
+    for (const auto& point : contour) {
+      int flag = point.on_curve ? kFLAG_ONCURVE : 0;
+      int dx = point.x - last_x;
+      int dy = point.y - last_y;
+      if (dx == 0) {
+        flag |= kFLAG_XREPEATSIGN;
+      } else if (dx > -256 && dx < 256) {
+        flag |= kFLAG_XSHORT | (dx > 0 ? kFLAG_XREPEATSIGN : 0);
+        x_bytes += 1;
+      } else {
+        x_bytes += 2;
+      }
+      if (dy == 0) {
+        flag |= kFLAG_YREPEATSIGN;
+      } else if (dy > -256 && dy < 256) {
+        flag |= kFLAG_YSHORT | (dy > 0 ? kFLAG_YREPEATSIGN : 0);
+        y_bytes += 1;
+      } else {
+        y_bytes += 2;
+      }
+      if (flag == last_flag && repeat_count != 255) {
+        dst[*offset - 1] |= kFLAG_REPEAT;
+        repeat_count++;
+      } else {
+        if (repeat_count != 0) {
+          if (*offset >= dst_size) {
+            return FONT_COMPRESSION_FAILURE();
+          }
+          dst[(*offset)++] = repeat_count;
+        }
+        if (*offset >= dst_size) {
+          return FONT_COMPRESSION_FAILURE();
+        }
+        dst[(*offset)++] = flag;
+        repeat_count = 0;
+      }
+      last_x = point.x;
+      last_y = point.y;
+      last_flag = flag;
+    }
+  }
+  if (repeat_count != 0) {
+    if (*offset >= dst_size) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    dst[(*offset)++] = repeat_count;
+  }
+
+  if (*offset + x_bytes + y_bytes > dst_size) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+
+  // Store the x and y coordinates.
+  size_t x_offset = *offset;
+  size_t y_offset = *offset + x_bytes;
+  last_x = 0;
+  last_y = 0;
+  for (const auto& contour : glyph.contours) {
+    for (const auto& point : contour) {
+      int dx = point.x - last_x;
+      int dy = point.y - last_y;
+      if (dx == 0) {
+        // pass
+      } else if (dx > -256 && dx < 256) {
+        dst[x_offset++] = std::abs(dx);
+      } else {
+        Store16(dx, &x_offset, dst);
+      }
+      if (dy == 0) {
+        // pass
+      } else if (dy > -256 && dy < 256) {
+        dst[y_offset++] = std::abs(dy);
+      } else {
+        Store16(dy, &y_offset, dst);
+      }
+      last_x += dx;
+      last_y += dy;
+    }
+  }
+  *offset = y_offset;
+  return true;
+}
+
+}  // namespace
+
+bool StoreGlyph(const Glyph& glyph, uint8_t* dst, size_t* dst_size) {
+  size_t offset = 0;
+  if (glyph.composite_data_size > 0) {
+    // Composite glyph.
+    if (*dst_size < ((10ULL + glyph.composite_data_size) +
+                     ((glyph.have_instructions ? 2ULL : 0) +
+                      glyph.instructions_size))) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    Store16(-1, &offset, dst);
+    StoreBbox(glyph, &offset, dst);
+    StoreBytes(glyph.composite_data, glyph.composite_data_size, &offset, dst);
+    if (glyph.have_instructions) {
+      StoreInstructions(glyph, &offset, dst);
+    }
+  } else if (glyph.contours.size() > 0) {
+    // Simple glyph.
+    if (glyph.contours.size() > std::numeric_limits<int16_t>::max()) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    if (*dst_size < ((12ULL + 2 * glyph.contours.size()) +
+                     glyph.instructions_size)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    Store16(glyph.contours.size(), &offset, dst);
+    StoreBbox(glyph, &offset, dst);
+    if (!StoreEndPtsOfContours(glyph, &offset, dst)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    StoreInstructions(glyph, &offset, dst);
+    if (!StorePoints(glyph, &offset, dst, *dst_size)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+  }
+  *dst_size = offset;
+  return true;
+}
+
+} // namespace woff2
diff --git a/third_party/woff2/src/glyph.h b/third_party/woff2/src/glyph.h
new file mode 100644
index 0000000..0ee755c
--- /dev/null
+++ b/third_party/woff2/src/glyph.h
@@ -0,0 +1,71 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Data model and I/O for glyph data within sfnt format files for the purpose of
+// performing the preprocessing step of the WOFF 2.0 conversion.
+
+#ifndef WOFF2_GLYPH_H_
+#define WOFF2_GLYPH_H_
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <vector>
+
+namespace woff2 {
+
+// Represents a parsed simple or composite glyph. The composite glyph data and
+// instructions are un-parsed and we keep only pointers to the raw data,
+// therefore the glyph is valid only so long the data from which it was parsed
+// is around.
+class Glyph {
+ public:
+  Glyph() : instructions_size(0), composite_data_size(0) {}
+
+  // Bounding box.
+  int16_t x_min;
+  int16_t x_max;
+  int16_t y_min;
+  int16_t y_max;
+
+  // Instructions.
+  uint16_t instructions_size;
+  const uint8_t* instructions_data;
+
+  // Data model for simple glyphs.
+  struct Point {
+    int x;
+    int y;
+    bool on_curve;
+  };
+  std::vector<std::vector<Point> > contours;
+
+  // Data for composite glyphs.
+  const uint8_t* composite_data;
+  uint32_t composite_data_size;
+  bool have_instructions;
+};
+
+// Parses the glyph from the given data. Returns false on parsing failure or
+// buffer overflow. The glyph is valid only so long the input data pointer is
+// valid.
+bool ReadGlyph(const uint8_t* data, size_t len, Glyph* glyph);
+
+// Stores the glyph into the specified dst buffer. The *dst_size is the buffer
+// size on entry and is set to the actual (unpadded) stored size on exit.
+// Returns false on buffer overflow.
+bool StoreGlyph(const Glyph& glyph, uint8_t* dst, size_t* dst_size);
+
+} // namespace woff2
+
+#endif  // WOFF2_GLYPH_H_
diff --git a/third_party/woff2/src/normalize.cc b/third_party/woff2/src/normalize.cc
new file mode 100644
index 0000000..a816feb
--- /dev/null
+++ b/third_party/woff2/src/normalize.cc
@@ -0,0 +1,253 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Glyph normalization
+
+#include "./normalize.h"
+
+#include <inttypes.h>
+#include <stddef.h>
+
+#include "./buffer.h"
+#include "./port.h"
+#include "./font.h"
+#include "./glyph.h"
+#include "./round.h"
+#include "./store_bytes.h"
+#include "./table_tags.h"
+
+namespace woff2 {
+
+namespace {
+
+void StoreLoca(int index_fmt, uint32_t value, size_t* offset, uint8_t* dst) {
+  if (index_fmt == 0) {
+    Store16(value >> 1, offset, dst);
+  } else {
+    StoreU32(value, offset, dst);
+  }
+}
+
+void NormalizeSimpleGlyphBoundingBox(Glyph* glyph) {
+  if (glyph->contours.empty() || glyph->contours[0].empty()) {
+    return;
+  }
+  int16_t x_min = glyph->contours[0][0].x;
+  int16_t y_min = glyph->contours[0][0].y;
+  int16_t x_max = x_min;
+  int16_t y_max = y_min;
+  for (const auto& contour : glyph->contours) {
+    for (const auto& point : contour) {
+      if (point.x < x_min) x_min = point.x;
+      if (point.x > x_max) x_max = point.x;
+      if (point.y < y_min) y_min = point.y;
+      if (point.y > y_max) y_max = point.y;
+    }
+  }
+  glyph->x_min = x_min;
+  glyph->y_min = y_min;
+  glyph->x_max = x_max;
+  glyph->y_max = y_max;
+}
+
+}  // namespace
+
+namespace {
+
+bool WriteNormalizedLoca(int index_fmt, int num_glyphs, Font* font) {
+  Font::Table* glyf_table = font->FindTable(kGlyfTableTag);
+  Font::Table* loca_table = font->FindTable(kLocaTableTag);
+
+  int glyph_sz = index_fmt == 0 ? 2 : 4;
+  loca_table->buffer.resize(Round4(num_glyphs + 1) * glyph_sz);
+  loca_table->length = (num_glyphs + 1) * glyph_sz;
+
+  uint8_t* glyf_dst = &glyf_table->buffer[0];
+  uint8_t* loca_dst = &loca_table->buffer[0];
+  uint32_t glyf_offset = 0;
+  size_t loca_offset = 0;
+
+  for (int i = 0; i < num_glyphs; ++i) {
+    StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst);
+    Glyph glyph;
+    const uint8_t* glyph_data;
+    size_t glyph_size;
+    if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) ||
+        (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    NormalizeSimpleGlyphBoundingBox(&glyph);
+    size_t glyf_dst_size = glyf_table->buffer.size() - glyf_offset;
+    if (!StoreGlyph(glyph, glyf_dst + glyf_offset, &glyf_dst_size)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    glyf_dst_size = Round4(glyf_dst_size);
+    if (glyf_dst_size > std::numeric_limits<uint32_t>::max() ||
+        glyf_offset + static_cast<uint32_t>(glyf_dst_size) < glyf_offset ||
+        (index_fmt == 0 && glyf_offset + glyf_dst_size >= (1UL << 17))) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    glyf_offset += glyf_dst_size;
+  }
+  if (glyf_offset == 0) {
+    return false;
+  }
+
+  StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst);
+
+  glyf_table->buffer.resize(glyf_offset);
+  glyf_table->data = &glyf_table->buffer[0];
+  glyf_table->length = glyf_offset;
+  loca_table->data = &loca_table->buffer[0];
+
+  return true;
+}
+
+}  // namespace
+
+namespace {
+
+bool MakeEditableBuffer(Font* font, int tableTag) {
+  Font::Table* table = font->FindTable(tableTag);
+  if (table == NULL) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  int sz = Round4(table->length);
+  table->buffer.resize(sz);
+  uint8_t* buf = &table->buffer[0];
+  memcpy(buf, table->data, sz);
+  table->data = buf;
+  return true;
+}
+
+}  // namespace
+
+bool NormalizeGlyphs(Font* font) {
+  Font::Table* cff_table = font->FindTable(kCffTableTag);
+  Font::Table* head_table = font->FindTable(kHeadTableTag);
+  Font::Table* glyf_table = font->FindTable(kGlyfTableTag);
+  Font::Table* loca_table = font->FindTable(kLocaTableTag);
+  if (head_table == NULL) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  // CFF, no loca, no glyf is OK for CFF. If so, don't normalize.
+  if (cff_table != NULL && loca_table == NULL && glyf_table == NULL) {
+    return true;
+  }
+  if (loca_table == NULL || glyf_table == NULL) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  int index_fmt = head_table->data[51];
+  int num_glyphs = NumGlyphs(*font);
+
+  // We need to allocate a bit more than its original length for the normalized
+  // glyf table, since it can happen that the glyphs in the original table are
+  // 2-byte aligned, while in the normalized table they are 4-byte aligned.
+  // That gives a maximum of 2 bytes increase per glyph. However, there is no
+  // theoretical guarantee that the total size of the flags plus the coordinates
+  // is the smallest possible in the normalized version, so we have to allow
+  // some general overhead.
+  // TODO(user) Figure out some more precise upper bound on the size of
+  // the overhead.
+  size_t max_normalized_glyf_size = 1.1 * glyf_table->length + 2 * num_glyphs;
+
+  glyf_table->buffer.resize(max_normalized_glyf_size);
+
+  // if we can't write a loca using short's (index_fmt 0)
+  // try again using longs (index_fmt 1)
+  if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) {
+    if (index_fmt != 0) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+
+    // Rewrite loca with 4-byte entries & update head to match
+    index_fmt = 1;
+    if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    head_table->buffer[51] = 1;
+  }
+
+  return true;
+}
+
+bool NormalizeOffsets(Font* font) {
+  uint32_t offset = 12 + 16 * font->num_tables;
+  for (auto& i : font->tables) {
+    i.second.offset = offset;
+    offset += Round4(i.second.length);
+  }
+  return true;
+}
+
+namespace {
+
+uint32_t ComputeChecksum(const uint8_t* buf, size_t size) {
+  uint32_t checksum = 0;
+  for (size_t i = 0; i < size; i += 4) {
+    checksum += ((buf[i] << 24) |
+                 (buf[i + 1] << 16) |
+                 (buf[i + 2] << 8) |
+                 buf[i + 3]);
+  }
+  return checksum;
+}
+
+uint32_t ComputeHeaderChecksum(const Font& font) {
+  uint32_t checksum = font.flavor;
+  uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0;
+  uint16_t search_range = max_pow2 ? 1 << (max_pow2 + 4) : 0;
+  uint16_t range_shift = (font.num_tables << 4) - search_range;
+  checksum += (font.num_tables << 16 | search_range);
+  checksum += (max_pow2 << 16 | range_shift);
+  for (const auto& i : font.tables) {
+    checksum += i.second.tag;
+    checksum += i.second.checksum;
+    checksum += i.second.offset;
+    checksum += i.second.length;
+  }
+  return checksum;
+}
+
+}  // namespace
+
+bool FixChecksums(Font* font) {
+  Font::Table* head_table = font->FindTable(kHeadTableTag);
+  if (head_table == NULL || head_table->length < 12) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  uint8_t* head_buf = &head_table->buffer[0];
+  size_t offset = 8;
+  StoreU32(0, &offset, head_buf);
+  uint32_t file_checksum = 0;
+  for (auto& i : font->tables) {
+    Font::Table* table = &i.second;
+    table->checksum = ComputeChecksum(table->data, table->length);
+    file_checksum += table->checksum;
+  }
+  file_checksum += ComputeHeaderChecksum(*font);
+  offset = 8;
+  StoreU32(0xb1b0afba - file_checksum, &offset, head_buf);
+  return true;
+}
+
+bool NormalizeFont(Font* font) {
+  return (MakeEditableBuffer(font, kHeadTableTag) &&
+          RemoveDigitalSignature(font) &&
+          NormalizeGlyphs(font) &&
+          NormalizeOffsets(font) &&
+          FixChecksums(font));
+}
+
+} // namespace woff2
diff --git a/third_party/woff2/src/normalize.h b/third_party/woff2/src/normalize.h
new file mode 100644
index 0000000..dcb473b6
--- /dev/null
+++ b/third_party/woff2/src/normalize.h
@@ -0,0 +1,45 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Functions for normalizing fonts. Since the WOFF 2.0 decoder creates font
+// files in normalized form, the WOFF 2.0 conversion is guaranteed to be
+// lossless (in a bitwise sense) only for normalized font files.
+
+#ifndef WOFF2_NORMALIZE_H_
+#define WOFF2_NORMALIZE_H_
+
+namespace woff2 {
+
+struct Font;
+
+// Changes the offset fields of the table headers so that the data for the
+// tables will be written in order of increasing tag values, without any gaps
+// other than the 4-byte padding.
+bool NormalizeOffsets(Font* font);
+
+// Changes the checksum fields of the table headers and the checksum field of
+// the head table so that it matches the current data.
+bool FixChecksums(Font* font);
+
+// Parses each of the glyphs in the font and writes them again to the glyf
+// table in normalized form, as defined by the StoreGlyph() function. Changes
+// the loca table accordigly.
+bool NormalizeGlyphs(Font* font);
+
+// Performs all of the normalization steps above.
+bool NormalizeFont(Font* font);
+
+} // namespace woff2
+
+#endif  // WOFF2_NORMALIZE_H_
diff --git a/third_party/woff2/src/port.h b/third_party/woff2/src/port.h
new file mode 100644
index 0000000..fd5498e
--- /dev/null
+++ b/third_party/woff2/src/port.h
@@ -0,0 +1,46 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Helper function for bit twiddling
+
+#ifndef WOFF2_PORT_H_
+#define WOFF2_PORT_H_
+
+namespace woff2 {
+
+typedef unsigned int       uint32;
+
+inline int Log2Floor(uint32 n) {
+#if defined(__GNUC__)
+  return n == 0 ? -1 : 31 ^ __builtin_clz(n);
+#else
+  if (n == 0)
+    return -1;
+  int log = 0;
+  uint32 value = n;
+  for (int i = 4; i >= 0; --i) {
+    int shift = (1 << i);
+    uint32 x = value >> shift;
+    if (x != 0) {
+      value = x;
+      log += shift;
+    }
+  }
+  assert(value == 1);
+  return log;
+#endif
+}
+
+} // namespace woff2
+#endif  // WOFF2_PORT_H_
diff --git a/third_party/woff2/src/round.h b/third_party/woff2/src/round.h
new file mode 100644
index 0000000..abb81f82
--- /dev/null
+++ b/third_party/woff2/src/round.h
@@ -0,0 +1,35 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Helper for rounding
+
+#ifndef WOFF2_ROUND_H_
+#define WOFF2_ROUND_H_
+
+#include <limits>
+
+namespace woff2 {
+
+// Round a value up to the nearest multiple of 4. Don't round the value in the
+// case that rounding up overflows.
+template<typename T> T Round4(T value) {
+  if (std::numeric_limits<T>::max() - value < 3) {
+    return value;
+  }
+  return (value + 3) & ~3;
+}
+
+} // namespace woff2
+
+#endif  // WOFF2_ROUND_H_
diff --git a/third_party/woff2/src/store_bytes.h b/third_party/woff2/src/store_bytes.h
new file mode 100644
index 0000000..a9a34016
--- /dev/null
+++ b/third_party/woff2/src/store_bytes.h
@@ -0,0 +1,61 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Helper functions for storing integer values into byte streams.
+// No bounds checking is performed, that is the responsibility of the caller.
+
+#ifndef WOFF2_STORE_BYTES_H_
+#define WOFF2_STORE_BYTES_H_
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <string.h>
+
+namespace woff2 {
+
+inline size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) {
+  dst[offset] = x >> 24;
+  dst[offset + 1] = x >> 16;
+  dst[offset + 2] = x >> 8;
+  dst[offset + 3] = x;
+  return offset + 4;
+}
+
+inline size_t Store16(uint8_t* dst, size_t offset, int x) {
+  dst[offset] = x >> 8;
+  dst[offset + 1] = x;
+  return offset + 2;
+}
+
+inline void StoreU32(uint32_t val, size_t* offset, uint8_t* dst) {
+  dst[(*offset)++] = val >> 24;
+  dst[(*offset)++] = val >> 16;
+  dst[(*offset)++] = val >> 8;
+  dst[(*offset)++] = val;
+}
+
+inline void Store16(int val, size_t* offset, uint8_t* dst) {
+  dst[(*offset)++] = val >> 8;
+  dst[(*offset)++] = val;
+}
+
+inline void StoreBytes(const uint8_t* data, size_t len,
+                       size_t* offset, uint8_t* dst) {
+  memcpy(&dst[*offset], data, len);
+  *offset += len;
+}
+
+} // namespace woff2
+
+#endif  // WOFF2_STORE_BYTES_H_
diff --git a/third_party/woff2/src/table_tags.cc b/third_party/woff2/src/table_tags.cc
new file mode 100644
index 0000000..0071e00
--- /dev/null
+++ b/third_party/woff2/src/table_tags.cc
@@ -0,0 +1,90 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Font table tags
+
+#include "./table_tags.h"
+
+namespace woff2 {
+
+// Note that the byte order is big-endian, not the same as ots.cc
+#define TAG(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d)
+
+const uint32_t kKnownTags[63] = {
+  TAG('c', 'm', 'a', 'p'),  // 0
+  TAG('h', 'e', 'a', 'd'),  // 1
+  TAG('h', 'h', 'e', 'a'),  // 2
+  TAG('h', 'm', 't', 'x'),  // 3
+  TAG('m', 'a', 'x', 'p'),  // 4
+  TAG('n', 'a', 'm', 'e'),  // 5
+  TAG('O', 'S', '/', '2'),  // 6
+  TAG('p', 'o', 's', 't'),  // 7
+  TAG('c', 'v', 't', ' '),  // 8
+  TAG('f', 'p', 'g', 'm'),  // 9
+  TAG('g', 'l', 'y', 'f'),  // 10
+  TAG('l', 'o', 'c', 'a'),  // 11
+  TAG('p', 'r', 'e', 'p'),  // 12
+  TAG('C', 'F', 'F', ' '),  // 13
+  TAG('V', 'O', 'R', 'G'),  // 14
+  TAG('E', 'B', 'D', 'T'),  // 15
+  TAG('E', 'B', 'L', 'C'),  // 16
+  TAG('g', 'a', 's', 'p'),  // 17
+  TAG('h', 'd', 'm', 'x'),  // 18
+  TAG('k', 'e', 'r', 'n'),  // 19
+  TAG('L', 'T', 'S', 'H'),  // 20
+  TAG('P', 'C', 'L', 'T'),  // 21
+  TAG('V', 'D', 'M', 'X'),  // 22
+  TAG('v', 'h', 'e', 'a'),  // 23
+  TAG('v', 'm', 't', 'x'),  // 24
+  TAG('B', 'A', 'S', 'E'),  // 25
+  TAG('G', 'D', 'E', 'F'),  // 26
+  TAG('G', 'P', 'O', 'S'),  // 27
+  TAG('G', 'S', 'U', 'B'),  // 28
+  TAG('E', 'B', 'S', 'C'),  // 29
+  TAG('J', 'S', 'T', 'F'),  // 30
+  TAG('M', 'A', 'T', 'H'),  // 31
+  TAG('C', 'B', 'D', 'T'),  // 32
+  TAG('C', 'B', 'L', 'C'),  // 33
+  TAG('C', 'O', 'L', 'R'),  // 34
+  TAG('C', 'P', 'A', 'L'),  // 35
+  TAG('S', 'V', 'G', ' '),  // 36
+  TAG('s', 'b', 'i', 'x'),  // 37
+  TAG('a', 'c', 'n', 't'),  // 38
+  TAG('a', 'v', 'a', 'r'),  // 39
+  TAG('b', 'd', 'a', 't'),  // 40
+  TAG('b', 'l', 'o', 'c'),  // 41
+  TAG('b', 's', 'l', 'n'),  // 42
+  TAG('c', 'v', 'a', 'r'),  // 43
+  TAG('f', 'd', 's', 'c'),  // 44
+  TAG('f', 'e', 'a', 't'),  // 45
+  TAG('f', 'm', 't', 'x'),  // 46
+  TAG('f', 'v', 'a', 'r'),  // 47
+  TAG('g', 'v', 'a', 'r'),  // 48
+  TAG('h', 's', 't', 'y'),  // 49
+  TAG('j', 'u', 's', 't'),  // 50
+  TAG('l', 'c', 'a', 'r'),  // 51
+  TAG('m', 'o', 'r', 't'),  // 52
+  TAG('m', 'o', 'r', 'x'),  // 53
+  TAG('o', 'p', 'b', 'd'),  // 54
+  TAG('p', 'r', 'o', 'p'),  // 55
+  TAG('t', 'r', 'a', 'k'),  // 56
+  TAG('Z', 'a', 'p', 'f'),  // 57
+  TAG('S', 'i', 'l', 'f'),  // 58
+  TAG('G', 'l', 'a', 't'),  // 59
+  TAG('G', 'l', 'o', 'c'),  // 60
+  TAG('F', 'e', 'a', 't'),  // 61
+  TAG('S', 'i', 'l', 'l'),  // 62
+};
+
+} // namespace woff2
diff --git a/third_party/woff2/src/table_tags.h b/third_party/woff2/src/table_tags.h
new file mode 100644
index 0000000..c9b09bb6
--- /dev/null
+++ b/third_party/woff2/src/table_tags.h
@@ -0,0 +1,35 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Font table tags
+
+#ifndef WOFF2_TABLE_TAGS_H_
+#define WOFF2_TABLE_TAGS_H_
+
+#include <inttypes.h>
+
+namespace woff2 {
+
+// Tags of popular tables.
+static const uint32_t kGlyfTableTag = 0x676c7966;
+static const uint32_t kHeadTableTag = 0x68656164;
+static const uint32_t kLocaTableTag = 0x6c6f6361;
+static const uint32_t kDsigTableTag = 0x44534947;
+static const uint32_t kCffTableTag = 0x43464620;
+
+extern const uint32_t kKnownTags[];
+
+} // namespace woff2
+
+#endif  // WOFF2_TABLE_TAGS_H_
diff --git a/third_party/woff2/src/transform.cc b/third_party/woff2/src/transform.cc
new file mode 100644
index 0000000..44a47815
--- /dev/null
+++ b/third_party/woff2/src/transform.cc
@@ -0,0 +1,270 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Library for preprocessing fonts as part of the WOFF 2.0 conversion.
+
+#include "./transform.h"
+
+#include <complex>  // for std::abs
+
+#include "./buffer.h"
+#include "./font.h"
+#include "./glyph.h"
+#include "./table_tags.h"
+
+namespace woff2 {
+
+namespace {
+
+const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
+const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
+
+void WriteBytes(std::vector<uint8_t>* out, const uint8_t* data, size_t len) {
+  if (len == 0) return;
+  size_t offset = out->size();
+  out->resize(offset + len);
+  memcpy(&(*out)[offset], data, len);
+}
+
+void WriteBytes(std::vector<uint8_t>* out, const std::vector<uint8_t>& in) {
+  for (int i = 0; i < in.size(); ++i) {
+    out->push_back(in[i]);
+  }
+}
+
+void WriteUShort(std::vector<uint8_t>* out, int value) {
+  out->push_back(value >> 8);
+  out->push_back(value & 255);
+}
+
+void WriteLong(std::vector<uint8_t>* out, int value) {
+  out->push_back((value >> 24) & 255);
+  out->push_back((value >> 16) & 255);
+  out->push_back((value >> 8) & 255);
+  out->push_back(value & 255);
+}
+
+void Write255UShort(std::vector<uint8_t>* out, int value) {
+  if (value < 253) {
+    out->push_back(value);
+  } else if (value < 506) {
+    out->push_back(255);
+    out->push_back(value - 253);
+  } else if (value < 762) {
+    out->push_back(254);
+    out->push_back(value - 506);
+  } else {
+    out->push_back(253);
+    out->push_back(value >> 8);
+    out->push_back(value & 0xff);
+  }
+}
+
+// Glyf table preprocessing, based on
+// GlyfEncoder.java
+// but only the "sbbox" and "cbbox" options are supported.
+class GlyfEncoder {
+ public:
+  explicit GlyfEncoder(int num_glyphs)
+      : sbbox_(false), cbbox_(true), n_glyphs_(num_glyphs) {
+    bbox_bitmap_.resize(((num_glyphs + 31) >> 5) << 2);
+  }
+
+  bool Encode(int glyph_id, const Glyph& glyph) {
+    if (glyph.composite_data_size > 0) {
+      WriteCompositeGlyph(glyph_id, glyph);
+    } else if (glyph.contours.size() > 0) {
+      WriteSimpleGlyph(glyph_id, glyph);
+    } else {
+      WriteUShort(&n_contour_stream_, 0);
+    }
+    return true;
+  }
+
+  void GetTransformedGlyfBytes(std::vector<uint8_t>* result) {
+    WriteLong(result, 0);  // version
+    WriteUShort(result, n_glyphs_);
+    WriteUShort(result, 0);  // index_format, will be set later
+    WriteLong(result, n_contour_stream_.size());
+    WriteLong(result, n_points_stream_.size());
+    WriteLong(result, flag_byte_stream_.size());
+    WriteLong(result, glyph_stream_.size());
+    WriteLong(result, composite_stream_.size());
+    WriteLong(result, bbox_bitmap_.size() + bbox_stream_.size());
+    WriteLong(result, instruction_stream_.size());
+    WriteBytes(result, n_contour_stream_);
+    WriteBytes(result, n_points_stream_);
+    WriteBytes(result, flag_byte_stream_);
+    WriteBytes(result, glyph_stream_);
+    WriteBytes(result, composite_stream_);
+    WriteBytes(result, bbox_bitmap_);
+    WriteBytes(result, bbox_stream_);
+    WriteBytes(result, instruction_stream_);
+  }
+
+ private:
+  void WriteInstructions(const Glyph& glyph) {
+    Write255UShort(&glyph_stream_, glyph.instructions_size);
+    WriteBytes(&instruction_stream_,
+               glyph.instructions_data, glyph.instructions_size);
+  }
+
+  void WriteSimpleGlyph(int glyph_id, const Glyph& glyph) {
+    int num_contours = glyph.contours.size();
+    WriteUShort(&n_contour_stream_, num_contours);
+    if (sbbox_) {
+      WriteBbox(glyph_id, glyph);
+    }
+    // TODO: check that bbox matches, write bbox if not
+    for (int i = 0; i < num_contours; i++) {
+      Write255UShort(&n_points_stream_, glyph.contours[i].size());
+    }
+    int lastX = 0;
+    int lastY = 0;
+    for (int i = 0; i < num_contours; i++) {
+      int num_points = glyph.contours[i].size();
+      for (int j = 0; j < num_points; j++) {
+        int x = glyph.contours[i][j].x;
+        int y = glyph.contours[i][j].y;
+        int dx = x - lastX;
+        int dy = y - lastY;
+        WriteTriplet(glyph.contours[i][j].on_curve, dx, dy);
+        lastX = x;
+        lastY = y;
+      }
+    }
+    if (num_contours > 0) {
+      WriteInstructions(glyph);
+    }
+  }
+
+  void WriteCompositeGlyph(int glyph_id, const Glyph& glyph) {
+    WriteUShort(&n_contour_stream_, -1);
+    if (cbbox_) {
+      WriteBbox(glyph_id, glyph);
+    }
+    WriteBytes(&composite_stream_,
+               glyph.composite_data,
+               glyph.composite_data_size);
+    if (glyph.have_instructions) {
+      WriteInstructions(glyph);
+    }
+  }
+
+  void WriteBbox(int glyph_id, const Glyph& glyph) {
+    bbox_bitmap_[glyph_id >> 3] |= 0x80 >> (glyph_id & 7);
+    WriteUShort(&bbox_stream_, glyph.x_min);
+    WriteUShort(&bbox_stream_, glyph.y_min);
+    WriteUShort(&bbox_stream_, glyph.x_max);
+    WriteUShort(&bbox_stream_, glyph.y_max);
+  }
+
+  void WriteTriplet(bool on_curve, int x, int y) {
+    int abs_x = std::abs(x);
+    int abs_y = std::abs(y);
+    int on_curve_bit = on_curve ? 0 : 128;
+    int x_sign_bit = (x < 0) ? 0 : 1;
+    int y_sign_bit = (y < 0) ? 0 : 1;
+    int xy_sign_bits = x_sign_bit + 2 * y_sign_bit;
+    if (x == 0 && abs_y < 1280) {
+      flag_byte_stream_.push_back(on_curve_bit +
+                                  ((abs_y & 0xf00) >> 7) + y_sign_bit);
+      glyph_stream_.push_back(abs_y & 0xff);
+    } else if (y == 0 && abs_x < 1280) {
+      flag_byte_stream_.push_back(on_curve_bit + 10 +
+                                  ((abs_x & 0xf00) >> 7) + x_sign_bit);
+      glyph_stream_.push_back(abs_x & 0xff);
+    } else if (abs_x < 65 && abs_y < 65) {
+      flag_byte_stream_.push_back(on_curve_bit + 20 +
+                                  ((abs_x - 1) & 0x30) +
+                                  (((abs_y - 1) & 0x30) >> 2) +
+                                  xy_sign_bits);
+      glyph_stream_.push_back((((abs_x - 1) & 0xf) << 4) | ((abs_y - 1) & 0xf));
+    } else if (abs_x < 769 && abs_y < 769) {
+      flag_byte_stream_.push_back(on_curve_bit + 84 +
+                                  12 * (((abs_x - 1) & 0x300) >> 8) +
+                                  (((abs_y - 1) & 0x300) >> 6) + xy_sign_bits);
+      glyph_stream_.push_back((abs_x - 1) & 0xff);
+      glyph_stream_.push_back((abs_y - 1) & 0xff);
+    } else if (abs_x < 4096 && abs_y < 4096) {
+      flag_byte_stream_.push_back(on_curve_bit + 120 + xy_sign_bits);
+      glyph_stream_.push_back(abs_x >> 4);
+      glyph_stream_.push_back(((abs_x & 0xf) << 4) | (abs_y >> 8));
+      glyph_stream_.push_back(abs_y & 0xff);
+    } else {
+      flag_byte_stream_.push_back(on_curve_bit + 124 + xy_sign_bits);
+      glyph_stream_.push_back(abs_x >> 8);
+      glyph_stream_.push_back(abs_x & 0xff);
+      glyph_stream_.push_back(abs_y >> 8);
+      glyph_stream_.push_back(abs_y & 0xff);
+    }
+  }
+
+  std::vector<uint8_t> n_contour_stream_;
+  std::vector<uint8_t> n_points_stream_;
+  std::vector<uint8_t> flag_byte_stream_;
+  std::vector<uint8_t> composite_stream_;
+  std::vector<uint8_t> bbox_bitmap_;
+  std::vector<uint8_t> bbox_stream_;
+  std::vector<uint8_t> glyph_stream_;
+  std::vector<uint8_t> instruction_stream_;
+  bool sbbox_;
+  bool cbbox_;
+  int n_glyphs_;
+};
+
+}  // namespace
+
+bool TransformGlyfAndLocaTables(Font* font) {
+  // no transform for CFF
+  if (font->FindTable(kCffTableTag) != NULL
+      && font->FindTable(kGlyfTableTag) == NULL
+      && font->FindTable(kLocaTableTag) == NULL) {
+    return true;
+  }
+  Font::Table* transformed_glyf = &font->tables[kGlyfTableTag ^ 0x80808080];
+  Font::Table* transformed_loca = &font->tables[kLocaTableTag ^ 0x80808080];
+
+  int num_glyphs = NumGlyphs(*font);
+  GlyfEncoder encoder(num_glyphs);
+  for (int i = 0; i < num_glyphs; ++i) {
+    Glyph glyph;
+    const uint8_t* glyph_data;
+    size_t glyph_size;
+    if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) ||
+        (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    encoder.Encode(i, glyph);
+  }
+  encoder.GetTransformedGlyfBytes(&transformed_glyf->buffer);
+
+  const Font::Table* head_table = font->FindTable(kHeadTableTag);
+  if (head_table == NULL || head_table->length < 52) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  transformed_glyf->buffer[7] = head_table->data[51];  // index_format
+
+  transformed_glyf->tag = kGlyfTableTag ^ 0x80808080;
+  transformed_glyf->length = transformed_glyf->buffer.size();
+  transformed_glyf->data = transformed_glyf->buffer.data();
+
+  transformed_loca->tag = kLocaTableTag ^ 0x80808080;
+  transformed_loca->length = 0;
+  transformed_loca->data = NULL;
+
+  return true;
+}
+
+} // namespace woff2
diff --git a/third_party/woff2/src/transform.h b/third_party/woff2/src/transform.h
new file mode 100644
index 0000000..15dc73e
--- /dev/null
+++ b/third_party/woff2/src/transform.h
@@ -0,0 +1,31 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Library for preprocessing fonts as part of the WOFF 2.0 conversion.
+
+#ifndef WOFF2_TRANSFORM_H_
+#define WOFF2_TRANSFORM_H_
+
+#include "./font.h"
+
+namespace woff2 {
+
+// Adds the transformed versions of the glyf and loca tables to the font. The
+// transformed loca table has zero length. The tag of the transformed tables is
+// derived from the original tag by flipping the MSBs of every byte.
+bool TransformGlyfAndLocaTables(Font* font);
+
+} // namespace woff2
+
+#endif  // WOFF2_TRANSFORM_H_
diff --git a/third_party/woff2/src/woff2_common.h b/third_party/woff2/src/woff2_common.h
new file mode 100644
index 0000000..159300a6
--- /dev/null
+++ b/third_party/woff2/src/woff2_common.h
@@ -0,0 +1,54 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Common definition for WOFF2 encoding/decoding
+
+#ifndef WOFF2_WOFF2_COMMON_H_
+#define WOFF2_WOFF2_COMMON_H_
+
+#include <inttypes.h>
+
+namespace woff2 {
+
+static const uint32_t kWoff2Signature = 0x774f4632;  // "wOF2"
+
+const unsigned int kWoff2FlagsContinueStream = 1 << 4;
+const unsigned int kWoff2FlagsTransform = 1 << 5;
+
+struct Point {
+  int x;
+  int y;
+  bool on_curve;
+};
+
+struct Table {
+  uint32_t tag;
+  uint32_t flags;
+  uint32_t src_offset;
+  uint32_t src_length;
+
+  uint32_t transform_length;
+
+  uint32_t dst_offset;
+  uint32_t dst_length;
+  const uint8_t* dst_data;
+
+  bool operator<(const Table& other) const {
+    return tag < other.tag;
+  }
+};
+
+} // namespace woff2
+
+#endif  // WOFF2_WOFF2_COMMON_H_
diff --git a/third_party/woff2/src/woff2_compress.cc b/third_party/woff2/src/woff2_compress.cc
new file mode 100644
index 0000000..057566f8
--- /dev/null
+++ b/third_party/woff2/src/woff2_compress.cc
@@ -0,0 +1,52 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// A commandline tool for compressing ttf format files to woff2.
+
+#include <string>
+
+#include "file.h"
+#include "./woff2_enc.h"
+
+
+int main(int argc, char **argv) {
+  using std::string;
+
+  if (argc != 2) {
+    fprintf(stderr, "One argument, the input filename, must be provided.\n");
+    return 1;
+  }
+
+  string filename(argv[1]);
+  string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2";
+  fprintf(stdout, "Processing %s => %s\n",
+    filename.c_str(), outfilename.c_str());
+  string input = woff2::GetFileContent(filename);
+
+  const uint8_t* input_data = reinterpret_cast<const uint8_t*>(input.data());
+  size_t output_size = woff2::MaxWOFF2CompressedSize(input_data, input.size());
+  string output(output_size, 0);
+  uint8_t* output_data = reinterpret_cast<uint8_t*>(&output[0]);
+
+  if (!woff2::ConvertTTFToWOFF2(input_data, input.size(),
+                                output_data, &output_size)) {
+    fprintf(stderr, "Compression failed.\n");
+    return 1;
+  }
+  output.resize(output_size);
+
+  woff2::SetFileContents(outfilename, output);
+
+  return 0;
+}
diff --git a/third_party/woff2/src/woff2_dec.cc b/third_party/woff2/src/woff2_dec.cc
new file mode 100644
index 0000000..e2bf6fb
--- /dev/null
+++ b/third_party/woff2/src/woff2_dec.cc
@@ -0,0 +1,901 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Library for converting WOFF2 format font files to their TTF versions.
+
+#include "./woff2_dec.h"
+
+#include <stdlib.h>
+#include <complex>
+#include <cstring>
+#include <limits>
+#include <string>
+#include <algorithm>
+#include <vector>
+
+#include "./buffer.h"
+#include "./decode.h"
+#include "./round.h"
+#include "./store_bytes.h"
+#include "./table_tags.h"
+#include "./woff2_common.h"
+
+namespace woff2 {
+
+namespace {
+
+using std::string;
+using std::vector;
+
+
+// simple glyph flags
+const int kGlyfOnCurve = 1 << 0;
+const int kGlyfXShort = 1 << 1;
+const int kGlyfYShort = 1 << 2;
+const int kGlyfRepeat = 1 << 3;
+const int kGlyfThisXIsSame = 1 << 4;
+const int kGlyfThisYIsSame = 1 << 5;
+
+// composite glyph flags
+// See CompositeGlyph.java in sfntly for full definitions
+const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
+const int FLAG_WE_HAVE_A_SCALE = 1 << 3;
+const int FLAG_MORE_COMPONENTS = 1 << 5;
+const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
+const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
+const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
+
+const size_t kSfntHeaderSize = 12;
+const size_t kSfntEntrySize = 16;
+const size_t kCheckSumAdjustmentOffset = 8;
+
+const size_t kEndPtsOfContoursOffset = 10;
+const size_t kCompositeGlyphBegin = 10;
+
+// Based on section 6.1.1 of MicroType Express draft spec
+bool Read255UShort(Buffer* buf, unsigned int* value) {
+  static const int kWordCode = 253;
+  static const int kOneMoreByteCode2 = 254;
+  static const int kOneMoreByteCode1 = 255;
+  static const int kLowestUCode = 253;
+  uint8_t code = 0;
+  if (!buf->ReadU8(&code)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  if (code == kWordCode) {
+    uint16_t result = 0;
+    if (!buf->ReadU16(&result)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    *value = result;
+    return true;
+  } else if (code == kOneMoreByteCode1) {
+    uint8_t result = 0;
+    if (!buf->ReadU8(&result)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    *value = result + kLowestUCode;
+    return true;
+  } else if (code == kOneMoreByteCode2) {
+    uint8_t result = 0;
+    if (!buf->ReadU8(&result)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    *value = result + kLowestUCode * 2;
+    return true;
+  } else {
+    *value = code;
+    return true;
+  }
+}
+
+bool ReadBase128(Buffer* buf, uint32_t* value) {
+  uint32_t result = 0;
+  for (size_t i = 0; i < 5; ++i) {
+    uint8_t code = 0;
+    if (!buf->ReadU8(&code)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    // If any of the top seven bits are set then we're about to overflow.
+    if (result & 0xfe000000) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    result = (result << 7) | (code & 0x7f);
+    if ((code & 0x80) == 0) {
+      *value = result;
+      return true;
+    }
+  }
+  // Make sure not to exceed the size bound
+  return FONT_COMPRESSION_FAILURE();
+}
+
+int WithSign(int flag, int baseval) {
+  // Precondition: 0 <= baseval < 65536 (to avoid integer overflow)
+  return (flag & 1) ? baseval : -baseval;
+}
+
+bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size,
+    unsigned int n_points, std::vector<Point>* result,
+    size_t* in_bytes_consumed) {
+  int x = 0;
+  int y = 0;
+
+  if (n_points > in_size) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  unsigned int triplet_index = 0;
+
+  for (unsigned int i = 0; i < n_points; ++i) {
+    uint8_t flag = flags_in[i];
+    bool on_curve = !(flag >> 7);
+    flag &= 0x7f;
+    unsigned int n_data_bytes;
+    if (flag < 84) {
+      n_data_bytes = 1;
+    } else if (flag < 120) {
+      n_data_bytes = 2;
+    } else if (flag < 124) {
+      n_data_bytes = 3;
+    } else {
+      n_data_bytes = 4;
+    }
+    if (triplet_index + n_data_bytes > in_size ||
+        triplet_index + n_data_bytes < triplet_index) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    int dx, dy;
+    if (flag < 10) {
+      dx = 0;
+      dy = WithSign(flag, ((flag & 14) << 7) + in[triplet_index]);
+    } else if (flag < 20) {
+      dx = WithSign(flag, (((flag - 10) & 14) << 7) + in[triplet_index]);
+      dy = 0;
+    } else if (flag < 84) {
+      int b0 = flag - 20;
+      int b1 = in[triplet_index];
+      dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4));
+      dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f));
+    } else if (flag < 120) {
+      int b0 = flag - 84;
+      dx = WithSign(flag, 1 + ((b0 / 12) << 8) + in[triplet_index]);
+      dy = WithSign(flag >> 1,
+                    1 + (((b0 % 12) >> 2) << 8) + in[triplet_index + 1]);
+    } else if (flag < 124) {
+      int b2 = in[triplet_index + 1];
+      dx = WithSign(flag, (in[triplet_index] << 4) + (b2 >> 4));
+      dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + in[triplet_index + 2]);
+    } else {
+      dx = WithSign(flag, (in[triplet_index] << 8) + in[triplet_index + 1]);
+      dy = WithSign(flag >> 1,
+          (in[triplet_index + 2] << 8) + in[triplet_index + 3]);
+    }
+    triplet_index += n_data_bytes;
+    // Possible overflow but coordinate values are not security sensitive
+    x += dx;
+    y += dy;
+    result->push_back(Point());
+    Point& back = result->back();
+    back.x = x;
+    back.y = y;
+    back.on_curve = on_curve;
+  }
+  *in_bytes_consumed = triplet_index;
+  return true;
+}
+
+// This function stores just the point data. On entry, dst points to the
+// beginning of a simple glyph. Returns true on success.
+bool StorePoints(const std::vector<Point>& points,
+    unsigned int n_contours, unsigned int instruction_length,
+    uint8_t* dst, size_t dst_size, size_t* glyph_size) {
+  // I believe that n_contours < 65536, in which case this is safe. However, a
+  // comment and/or an assert would be good.
+  unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 +
+    instruction_length;
+  int last_flag = -1;
+  int repeat_count = 0;
+  int last_x = 0;
+  int last_y = 0;
+  unsigned int x_bytes = 0;
+  unsigned int y_bytes = 0;
+
+  for (unsigned int i = 0; i < points.size(); ++i) {
+    const Point& point = points[i];
+    int flag = point.on_curve ? kGlyfOnCurve : 0;
+    int dx = point.x - last_x;
+    int dy = point.y - last_y;
+    if (dx == 0) {
+      flag |= kGlyfThisXIsSame;
+    } else if (dx > -256 && dx < 256) {
+      flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0);
+      x_bytes += 1;
+    } else {
+      x_bytes += 2;
+    }
+    if (dy == 0) {
+      flag |= kGlyfThisYIsSame;
+    } else if (dy > -256 && dy < 256) {
+      flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0);
+      y_bytes += 1;
+    } else {
+      y_bytes += 2;
+    }
+
+    if (flag == last_flag && repeat_count != 255) {
+      dst[flag_offset - 1] |= kGlyfRepeat;
+      repeat_count++;
+    } else {
+      if (repeat_count != 0) {
+        if (flag_offset >= dst_size) {
+          return FONT_COMPRESSION_FAILURE();
+        }
+        dst[flag_offset++] = repeat_count;
+      }
+      if (flag_offset >= dst_size) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      dst[flag_offset++] = flag;
+      repeat_count = 0;
+    }
+    last_x = point.x;
+    last_y = point.y;
+    last_flag = flag;
+  }
+
+  if (repeat_count != 0) {
+    if (flag_offset >= dst_size) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    dst[flag_offset++] = repeat_count;
+  }
+  unsigned int xy_bytes = x_bytes + y_bytes;
+  if (xy_bytes < x_bytes ||
+      flag_offset + xy_bytes < flag_offset ||
+      flag_offset + xy_bytes > dst_size) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+
+  int x_offset = flag_offset;
+  int y_offset = flag_offset + x_bytes;
+  last_x = 0;
+  last_y = 0;
+  for (unsigned int i = 0; i < points.size(); ++i) {
+    int dx = points[i].x - last_x;
+    if (dx == 0) {
+      // pass
+    } else if (dx > -256 && dx < 256) {
+      dst[x_offset++] = std::abs(dx);
+    } else {
+      // will always fit for valid input, but overflow is harmless
+      x_offset = Store16(dst, x_offset, dx);
+    }
+    last_x += dx;
+    int dy = points[i].y - last_y;
+    if (dy == 0) {
+      // pass
+    } else if (dy > -256 && dy < 256) {
+      dst[y_offset++] = std::abs(dy);
+    } else {
+      y_offset = Store16(dst, y_offset, dy);
+    }
+    last_y += dy;
+  }
+  *glyph_size = y_offset;
+  return true;
+}
+
+// Compute the bounding box of the coordinates, and store into a glyf buffer.
+// A precondition is that there are at least 10 bytes available.
+void ComputeBbox(const std::vector<Point>& points, uint8_t* dst) {
+  int x_min = 0;
+  int y_min = 0;
+  int x_max = 0;
+  int y_max = 0;
+
+  for (unsigned int i = 0; i < points.size(); ++i) {
+    int x = points[i].x;
+    int y = points[i].y;
+    if (i == 0 || x < x_min) x_min = x;
+    if (i == 0 || x > x_max) x_max = x;
+    if (i == 0 || y < y_min) y_min = y;
+    if (i == 0 || y > y_max) y_max = y;
+  }
+  size_t offset = 2;
+  offset = Store16(dst, offset, x_min);
+  offset = Store16(dst, offset, y_min);
+  offset = Store16(dst, offset, x_max);
+  offset = Store16(dst, offset, y_max);
+}
+
+// Process entire bbox stream. This is done as a separate pass to allow for
+// composite bbox computations (an optional more aggressive transform).
+bool ProcessBboxStream(Buffer* bbox_stream, unsigned int n_glyphs,
+    const std::vector<uint32_t>& loca_values, uint8_t* glyf_buf,
+    size_t glyf_buf_length) {
+  const uint8_t* buf = bbox_stream->buffer();
+  if (n_glyphs >= 65536 || loca_values.size() != n_glyphs + 1) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  // Safe because n_glyphs is bounded
+  unsigned int bitmap_length = ((n_glyphs + 31) >> 5) << 2;
+  if (!bbox_stream->Skip(bitmap_length)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  for (unsigned int i = 0; i < n_glyphs; ++i) {
+    if (buf[i >> 3] & (0x80 >> (i & 7))) {
+      uint32_t loca_offset = loca_values[i];
+      if (loca_values[i + 1] - loca_offset < kEndPtsOfContoursOffset) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      if (glyf_buf_length < 2 + 10 ||
+          loca_offset > glyf_buf_length - 2 - 10) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      if (!bbox_stream->Read(glyf_buf + loca_offset + 2, 8)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+    }
+  }
+  return true;
+}
+
+bool ProcessComposite(Buffer* composite_stream, uint8_t* dst,
+    size_t dst_size, size_t* glyph_size, bool* have_instructions) {
+  size_t start_offset = composite_stream->offset();
+  bool we_have_instructions = false;
+
+  uint16_t flags = FLAG_MORE_COMPONENTS;
+  while (flags & FLAG_MORE_COMPONENTS) {
+    if (!composite_stream->ReadU16(&flags)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0;
+    size_t arg_size = 2;  // glyph index
+    if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) {
+      arg_size += 4;
+    } else {
+      arg_size += 2;
+    }
+    if (flags & FLAG_WE_HAVE_A_SCALE) {
+      arg_size += 2;
+    } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
+      arg_size += 4;
+    } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) {
+      arg_size += 8;
+    }
+    if (!composite_stream->Skip(arg_size)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+  }
+  size_t composite_glyph_size = composite_stream->offset() - start_offset;
+  if (composite_glyph_size + kCompositeGlyphBegin > dst_size) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  Store16(dst, 0, 0xffff);  // nContours = -1 for composite glyph
+  std::memcpy(dst + kCompositeGlyphBegin,
+      composite_stream->buffer() + start_offset,
+      composite_glyph_size);
+  *glyph_size = kCompositeGlyphBegin + composite_glyph_size;
+  *have_instructions = we_have_instructions;
+  return true;
+}
+
+// Build TrueType loca table
+bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format,
+    uint8_t* dst, size_t dst_size) {
+  const uint64_t loca_size = loca_values.size();
+  const uint64_t offset_size = index_format ? 4 : 2;
+  if ((loca_size << 2) >> 2 != loca_size) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  if (offset_size * loca_size > dst_size) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  size_t offset = 0;
+  for (size_t i = 0; i < loca_values.size(); ++i) {
+    uint32_t value = loca_values[i];
+    if (index_format) {
+      offset = StoreU32(dst, offset, value);
+    } else {
+      offset = Store16(dst, offset, value >> 1);
+    }
+  }
+  return true;
+}
+
+// Reconstruct entire glyf table based on transformed original
+bool ReconstructGlyf(const uint8_t* data, size_t data_size,
+    uint8_t* dst, size_t dst_size,
+    uint8_t* loca_buf, size_t loca_size) {
+  static const int kNumSubStreams = 7;
+  Buffer file(data, data_size);
+  uint32_t version;
+  std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams);
+
+  if (!file.ReadU32(&version)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  uint16_t num_glyphs;
+  uint16_t index_format;
+  if (!file.ReadU16(&num_glyphs) ||
+      !file.ReadU16(&index_format)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  unsigned int offset = (2 + kNumSubStreams) * 4;
+  if (offset > data_size) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  // Invariant from here on: data_size >= offset
+  for (int i = 0; i < kNumSubStreams; ++i) {
+    uint32_t substream_size;
+    if (!file.ReadU32(&substream_size)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    if (substream_size > data_size - offset) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    substreams[i] = std::make_pair(data + offset, substream_size);
+    offset += substream_size;
+  }
+  Buffer n_contour_stream(substreams[0].first, substreams[0].second);
+  Buffer n_points_stream(substreams[1].first, substreams[1].second);
+  Buffer flag_stream(substreams[2].first, substreams[2].second);
+  Buffer glyph_stream(substreams[3].first, substreams[3].second);
+  Buffer composite_stream(substreams[4].first, substreams[4].second);
+  Buffer bbox_stream(substreams[5].first, substreams[5].second);
+  Buffer instruction_stream(substreams[6].first, substreams[6].second);
+
+  std::vector<uint32_t> loca_values(num_glyphs + 1);
+  std::vector<unsigned int> n_points_vec;
+  std::vector<Point> points;
+  uint32_t loca_offset = 0;
+  for (unsigned int i = 0; i < num_glyphs; ++i) {
+    size_t glyph_size = 0;
+    uint16_t n_contours = 0;
+    if (!n_contour_stream.ReadU16(&n_contours)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    uint8_t* glyf_dst = dst + loca_offset;
+    size_t glyf_dst_size = dst_size - loca_offset;
+    if (n_contours == 0xffff) {
+      // composite glyph
+      bool have_instructions = false;
+      unsigned int instruction_size = 0;
+      if (!ProcessComposite(&composite_stream, glyf_dst, glyf_dst_size,
+            &glyph_size, &have_instructions)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      if (have_instructions) {
+        if (!Read255UShort(&glyph_stream, &instruction_size)) {
+          return FONT_COMPRESSION_FAILURE();
+        }
+        if (instruction_size + 2 > glyf_dst_size - glyph_size) {
+          return FONT_COMPRESSION_FAILURE();
+        }
+        Store16(glyf_dst, glyph_size, instruction_size);
+        if (!instruction_stream.Read(glyf_dst + glyph_size + 2,
+              instruction_size)) {
+          return FONT_COMPRESSION_FAILURE();
+        }
+        glyph_size += instruction_size + 2;
+      }
+    } else if (n_contours > 0) {
+      // simple glyph
+      n_points_vec.clear();
+      points.clear();
+      unsigned int total_n_points = 0;
+      unsigned int n_points_contour;
+      for (unsigned int j = 0; j < n_contours; ++j) {
+        if (!Read255UShort(&n_points_stream, &n_points_contour)) {
+          return FONT_COMPRESSION_FAILURE();
+        }
+        n_points_vec.push_back(n_points_contour);
+        if (total_n_points + n_points_contour < total_n_points) {
+          return FONT_COMPRESSION_FAILURE();
+        }
+        total_n_points += n_points_contour;
+      }
+      unsigned int flag_size = total_n_points;
+      if (flag_size > flag_stream.length() - flag_stream.offset()) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset();
+      const uint8_t* triplet_buf = glyph_stream.buffer() +
+        glyph_stream.offset();
+      size_t triplet_size = glyph_stream.length() - glyph_stream.offset();
+      size_t triplet_bytes_consumed = 0;
+      if (!TripletDecode(flags_buf, triplet_buf, triplet_size, total_n_points,
+            &points, &triplet_bytes_consumed)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      const uint32_t header_and_endpts_contours_size =
+          kEndPtsOfContoursOffset + 2 * n_contours;
+      if (glyf_dst_size < header_and_endpts_contours_size) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      Store16(glyf_dst, 0, n_contours);
+      ComputeBbox(points, glyf_dst);
+      size_t offset = kEndPtsOfContoursOffset;
+      int end_point = -1;
+      for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) {
+        end_point += n_points_vec[contour_ix];
+        if (end_point >= 65536) {
+          return FONT_COMPRESSION_FAILURE();
+        }
+        offset = Store16(glyf_dst, offset, end_point);
+      }
+      if (!flag_stream.Skip(flag_size)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      if (!glyph_stream.Skip(triplet_bytes_consumed)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      unsigned int instruction_size;
+      if (!Read255UShort(&glyph_stream, &instruction_size)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      if (glyf_dst_size - header_and_endpts_contours_size <
+          instruction_size + 2) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      uint8_t* instruction_dst = glyf_dst + header_and_endpts_contours_size;
+      Store16(instruction_dst, 0, instruction_size);
+      if (!instruction_stream.Read(instruction_dst + 2, instruction_size)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      if (!StorePoints(points, n_contours, instruction_size,
+            glyf_dst, glyf_dst_size, &glyph_size)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+    } else {
+      glyph_size = 0;
+    }
+    loca_values[i] = loca_offset;
+    if (glyph_size + 3 < glyph_size) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    glyph_size = Round4(glyph_size);
+    if (glyph_size > dst_size - loca_offset) {
+      // This shouldn't happen, but this test defensively maintains the
+      // invariant that loca_offset <= dst_size.
+      return FONT_COMPRESSION_FAILURE();
+    }
+    loca_offset += glyph_size;
+  }
+  loca_values[num_glyphs] = loca_offset;
+  if (!ProcessBboxStream(&bbox_stream, num_glyphs, loca_values,
+          dst, dst_size)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  return StoreLoca(loca_values, index_format, loca_buf, loca_size);
+}
+
+// This is linear search, but could be changed to binary because we
+// do have a guarantee that the tables are sorted by tag. But the total
+// cpu time is expected to be very small in any case.
+const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) {
+  size_t n_tables = tables.size();
+  for (size_t i = 0; i < n_tables; ++i) {
+    if (tables[i].tag == tag) {
+      return &tables[i];
+    }
+  }
+  return NULL;
+}
+
+bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
+    const uint8_t* transformed_buf, size_t transformed_size,
+    uint8_t* dst, size_t dst_length) {
+  if (tag == kGlyfTableTag) {
+    const Table* glyf_table = FindTable(tables, tag);
+    const Table* loca_table = FindTable(tables, kLocaTableTag);
+    if (glyf_table == NULL || loca_table == NULL) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    if (static_cast<uint64_t>(glyf_table->dst_offset + glyf_table->dst_length) >
+        dst_length) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    if (static_cast<uint64_t>(loca_table->dst_offset + loca_table->dst_length) >
+        dst_length) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    return ReconstructGlyf(transformed_buf, transformed_size,
+        dst + glyf_table->dst_offset, glyf_table->dst_length,
+        dst + loca_table->dst_offset, loca_table->dst_length);
+  } else if (tag == kLocaTableTag) {
+    // processing was already done by glyf table, but validate
+    if (!FindTable(tables, kGlyfTableTag)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+  } else {
+    // transform for the tag is not known
+    return FONT_COMPRESSION_FAILURE();
+  }
+  return true;
+}
+
+uint32_t ComputeChecksum(const uint8_t* buf, size_t size) {
+  uint32_t checksum = 0;
+  for (size_t i = 0; i < size; i += 4) {
+    // We assume the addition is mod 2^32, which is valid because unsigned
+    checksum += (buf[i] << 24) | (buf[i + 1] << 16) |
+      (buf[i + 2] << 8) | buf[i + 3];
+  }
+  return checksum;
+}
+
+bool FixChecksums(const std::vector<Table>& tables, uint8_t* dst) {
+  const Table* head_table = FindTable(tables, kHeadTableTag);
+  if (head_table == NULL ||
+      head_table->dst_length < kCheckSumAdjustmentOffset + 4) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  size_t adjustment_offset = head_table->dst_offset + kCheckSumAdjustmentOffset;
+  StoreU32(dst, adjustment_offset, 0);
+  size_t n_tables = tables.size();
+  uint32_t file_checksum = 0;
+  for (size_t i = 0; i < n_tables; ++i) {
+    const Table* table = &tables[i];
+    size_t table_length = table->dst_length;
+    uint8_t* table_data = dst + table->dst_offset;
+    uint32_t checksum = ComputeChecksum(table_data, table_length);
+    StoreU32(dst, kSfntHeaderSize + i * kSfntEntrySize + 4, checksum);
+    file_checksum += checksum;
+  }
+  file_checksum += ComputeChecksum(dst,
+      kSfntHeaderSize + kSfntEntrySize * n_tables);
+  uint32_t checksum_adjustment = 0xb1b0afba - file_checksum;
+  StoreU32(dst, adjustment_offset, checksum_adjustment);
+  return true;
+}
+
+bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size,
+  const uint8_t* src_buf, size_t src_size) {
+  size_t uncompressed_size = dst_size;
+  int ok = BrotliDecompressBuffer(src_size, src_buf,
+                                  &uncompressed_size, dst_buf);
+  if (!ok || uncompressed_size != dst_size) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  return true;
+}
+
+bool ReadShortDirectory(Buffer* file, std::vector<Table>* tables,
+    size_t num_tables) {
+  for (size_t i = 0; i < num_tables; ++i) {
+    Table* table = &(*tables)[i];
+    uint8_t flag_byte;
+    if (!file->ReadU8(&flag_byte)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    uint32_t tag;
+    if ((flag_byte & 0x3f) == 0x3f) {
+      if (!file->ReadU32(&tag)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+    } else {
+      tag = kKnownTags[flag_byte & 0x3f];
+    }
+    // Bits 6 and 7 are reserved and must be 0.
+    if ((flag_byte & 0xC0) != 0) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    uint32_t flags = 0;
+    if (i > 0) {
+      flags |= kWoff2FlagsContinueStream;
+    }
+    // Always transform the glyf and loca tables
+    if (tag == kGlyfTableTag || tag == kLocaTableTag) {
+      flags |= kWoff2FlagsTransform;
+    }
+    uint32_t dst_length;
+    if (!ReadBase128(file, &dst_length)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    uint32_t transform_length = dst_length;
+    if ((flags & kWoff2FlagsTransform) != 0) {
+      if (!ReadBase128(file, &transform_length)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+    }
+    table->tag = tag;
+    table->flags = flags;
+    table->transform_length = transform_length;
+    table->dst_length = dst_length;
+  }
+  return true;
+}
+
+}  // namespace
+
+size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) {
+  Buffer file(data, length);
+  uint32_t total_length;
+
+  if (!file.Skip(16) ||
+      !file.ReadU32(&total_length)) {
+    return 0;
+  }
+  return total_length;
+}
+
+bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
+                       const uint8_t* data, size_t length) {
+  Buffer file(data, length);
+
+  uint32_t signature;
+  uint32_t flavor;
+  if (!file.ReadU32(&signature) || signature != kWoff2Signature ||
+      !file.ReadU32(&flavor)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+
+  // TODO(user): Should call IsValidVersionTag() here.
+
+  uint32_t reported_length;
+  if (!file.ReadU32(&reported_length) || length != reported_length) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  uint16_t num_tables;
+  if (!file.ReadU16(&num_tables) || !num_tables) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  // We don't care about these fields of the header:
+  //   uint16_t reserved
+  //   uint32_t total_sfnt_size
+  if (!file.Skip(6)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  uint32_t compressed_length;
+  if (!file.ReadU32(&compressed_length)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  // We don't care about these fields of the header:
+  //   uint16_t major_version, minor_version
+  //   uint32_t meta_offset, meta_length, meta_orig_length
+  //   uint32_t priv_offset, priv_length
+  if (!file.Skip(24)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  std::vector<Table> tables(num_tables);
+  // Note: change below to ReadLongDirectory to enable long format.
+  if (!ReadShortDirectory(&file, &tables, num_tables)) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  uint64_t src_offset = file.offset();
+  uint64_t dst_offset = kSfntHeaderSize +
+      kSfntEntrySize * static_cast<uint64_t>(num_tables);
+  uint64_t uncompressed_sum = 0;
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    Table* table = &tables[i];
+    table->src_offset = src_offset;
+    table->src_length = (i == 0 ? compressed_length : 0);
+    src_offset += table->src_length;
+    if (src_offset > std::numeric_limits<uint32_t>::max()) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    src_offset = Round4(src_offset);  // TODO: reconsider
+    table->dst_offset = dst_offset;
+    dst_offset += table->dst_length;
+    if (dst_offset > std::numeric_limits<uint32_t>::max()) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+    dst_offset = Round4(dst_offset);
+
+    uncompressed_sum += table->src_length;
+    if (uncompressed_sum > std::numeric_limits<uint32_t>::max()) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+  }
+  // Enforce same 30M limit on uncompressed tables as OTS
+  if (uncompressed_sum > 30 * 1024 * 1024) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+  if (src_offset > length || dst_offset > result_length) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+
+  const uint32_t sfnt_header_and_table_directory_size = 12 + 16 * num_tables;
+  if (sfnt_header_and_table_directory_size > result_length) {
+    return FONT_COMPRESSION_FAILURE();
+  }
+
+  // Start building the font
+  size_t offset = 0;
+  offset = StoreU32(result, offset, flavor);
+  offset = Store16(result, offset, num_tables);
+  unsigned max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= num_tables) {
+    max_pow2++;
+  }
+  const uint16_t output_search_range = (1u << max_pow2) << 4;
+  offset = Store16(result, offset, output_search_range);
+  offset = Store16(result, offset, max_pow2);
+  offset = Store16(result, offset, (num_tables << 4) - output_search_range);
+
+  // sort tags in the table directory in ascending alphabetical order
+  std::vector<Table> sorted_tables(tables);
+  std::sort(sorted_tables.begin(), sorted_tables.end());
+
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    const Table* table = &sorted_tables[i];
+    offset = StoreU32(result, offset, table->tag);
+    offset = StoreU32(result, offset, 0);  // checksum, to fill in later
+    offset = StoreU32(result, offset, table->dst_offset);
+    offset = StoreU32(result, offset, table->dst_length);
+  }
+  std::vector<uint8_t> uncompressed_buf;
+  bool continue_valid = false;
+  const uint8_t* transform_buf = NULL;
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    const Table* table = &tables[i];
+    uint32_t flags = table->flags;
+    const uint8_t* src_buf = data + table->src_offset;
+    size_t transform_length = table->transform_length;
+    if ((flags & kWoff2FlagsContinueStream) != 0) {
+      if (!continue_valid) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+    } else if ((flags & kWoff2FlagsContinueStream) == 0) {
+      uint64_t total_size = transform_length;
+      for (uint16_t j = i + 1; j < num_tables; ++j) {
+        if ((tables[j].flags & kWoff2FlagsContinueStream) == 0) {
+          break;
+        }
+        total_size += tables[j].transform_length;
+        if (total_size > std::numeric_limits<uint32_t>::max()) {
+          return FONT_COMPRESSION_FAILURE();
+        }
+      }
+      uncompressed_buf.resize(total_size);
+      if (!Woff2Uncompress(&uncompressed_buf[0], total_size,
+                           src_buf, compressed_length)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      transform_buf = &uncompressed_buf[0];
+      continue_valid = true;
+    } else {
+      return FONT_COMPRESSION_FAILURE();
+    }
+
+    if ((flags & kWoff2FlagsTransform) == 0) {
+      if (transform_length != table->dst_length) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      if (static_cast<uint64_t>(table->dst_offset + transform_length) >
+          result_length) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+      std::memcpy(result + table->dst_offset, transform_buf,
+          transform_length);
+    } else {
+      if (!ReconstructTransformed(tables, table->tag,
+            transform_buf, transform_length, result, result_length)) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+    }
+    if (continue_valid) {
+      transform_buf += transform_length;
+      if (transform_buf > &uncompressed_buf[0] + uncompressed_buf.size()) {
+        return FONT_COMPRESSION_FAILURE();
+      }
+    }
+  }
+
+  return FixChecksums(sorted_tables, result);
+}
+
+} // namespace woff2
diff --git a/third_party/woff2/src/woff2_dec.h b/third_party/woff2/src/woff2_dec.h
new file mode 100644
index 0000000..74ba7f5
--- /dev/null
+++ b/third_party/woff2/src/woff2_dec.h
@@ -0,0 +1,36 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Library for converting WOFF2 format font files to their TTF versions.
+
+#ifndef WOFF2_WOFF2_DEC_H_
+#define WOFF2_WOFF2_DEC_H_
+
+#include <stddef.h>
+#include <inttypes.h>
+
+namespace woff2 {
+
+// Compute the size of the final uncompressed font, or 0 on error.
+size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length);
+
+// Decompresses the font into the target buffer. The result_length should
+// be the same as determined by ComputeFinalSize(). Returns true on successful
+// decompression.
+bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length,
+                       const uint8_t *data, size_t length);
+
+} // namespace woff2
+
+#endif  // WOFF2_WOFF2_DEC_H_
diff --git a/third_party/woff2/src/woff2_decompress.cc b/third_party/woff2/src/woff2_decompress.cc
new file mode 100644
index 0000000..7668c4e
--- /dev/null
+++ b/third_party/woff2/src/woff2_decompress.cc
@@ -0,0 +1,54 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// A very simple commandline tool for decompressing woff2 format files to true
+// type font files.
+
+#include <string>
+
+
+#include "file.h"
+#include "./woff2_dec.h"
+
+int main(int argc, char **argv) {
+  using std::string;
+
+  if (argc != 2) {
+    fprintf(stderr, "One argument, the input filename, must be provided.\n");
+    return 1;
+  }
+
+  string filename(argv[1]);
+  string outfilename = filename.substr(0, filename.find_last_of(".")) + ".ttf";
+  fprintf(stdout, "Processing %s => %s\n",
+    filename.c_str(), outfilename.c_str());
+  string input = woff2::GetFileContent(filename);
+
+  size_t decompressed_size = woff2::ComputeWOFF2FinalSize(
+      reinterpret_cast<const uint8_t*>(input.data()), input.size());
+  string output(decompressed_size, 0);
+  const bool ok = woff2::ConvertWOFF2ToTTF(
+      reinterpret_cast<uint8_t*>(&output[0]), decompressed_size,
+      reinterpret_cast<const uint8_t*>(input.data()), input.size());
+
+  if (!ok) {
+    fprintf(stderr, "Decompression failed\n");
+    return 1;
+  }
+
+  woff2::SetFileContents(outfilename, output);
+
+  return 0;
+}
+
diff --git a/third_party/woff2/src/woff2_enc.cc b/third_party/woff2/src/woff2_enc.cc
new file mode 100644
index 0000000..7caeca7
--- /dev/null
+++ b/third_party/woff2/src/woff2_enc.cc
@@ -0,0 +1,346 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Library for converting TTF format font files to their WOFF2 versions.
+
+#include "./woff2_enc.h"
+
+#include <stdlib.h>
+#include <complex>
+#include <cstring>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "./buffer.h"
+#include "./encode.h"
+#include "./font.h"
+#include "./normalize.h"
+#include "./round.h"
+#include "./store_bytes.h"
+#include "./table_tags.h"
+#include "./transform.h"
+#include "./woff2_common.h"
+
+namespace woff2 {
+
+namespace {
+
+using std::string;
+using std::vector;
+
+
+const size_t kWoff2HeaderSize = 48;
+const size_t kWoff2EntrySize = 20;
+
+size_t Base128Size(size_t n) {
+  size_t size = 1;
+  for (; n >= 128; n >>= 7) ++size;
+  return size;
+}
+
+void StoreBase128(size_t len, size_t* offset, uint8_t* dst) {
+  size_t size = Base128Size(len);
+  for (int i = 0; i < size; ++i) {
+    int b = static_cast<int>((len >> (7 * (size - i - 1))) & 0x7f);
+    if (i < size - 1) {
+      b |= 0x80;
+    }
+    dst[(*offset)++] = b;
+  }
+}
+
+bool Compress(const uint8_t* data, const size_t len,
+              uint8_t* result, uint32_t* result_len,
+              brotli::BrotliParams::Mode mode) {
+  size_t compressed_len = *result_len;
+  brotli::BrotliParams params;
+  params.mode = mode;
+  if (brotli::BrotliCompressBuffer(params, len, data, &compressed_len, result)
+      == 0) {
+    return false;
+  }
+  *result_len = compressed_len;
+  return true;
+}
+
+bool Woff2Compress(const uint8_t* data, const size_t len,
+                   uint8_t* result, uint32_t* result_len) {
+  return Compress(data, len, result, result_len,
+                  brotli::BrotliParams::MODE_FONT);
+}
+
+bool TextCompress(const uint8_t* data, const size_t len,
+                   uint8_t* result, uint32_t* result_len) {
+  return Compress(data, len, result, result_len,
+                  brotli::BrotliParams::MODE_TEXT);
+}
+
+bool ReadLongDirectory(Buffer* file, std::vector<Table>* tables,
+    size_t num_tables) {
+  for (size_t i = 0; i < num_tables; ++i) {
+    Table* table = &(*tables)[i];
+    if (!file->ReadU32(&table->tag) ||
+        !file->ReadU32(&table->flags) ||
+        !file->ReadU32(&table->src_length) ||
+        !file->ReadU32(&table->transform_length) ||
+        !file->ReadU32(&table->dst_length)) {
+      return FONT_COMPRESSION_FAILURE();
+    }
+  }
+  return true;
+}
+
+int KnownTableIndex(uint32_t tag) {
+  for (int i = 0; i < 63; ++i) {
+    if (tag == kKnownTags[i]) return i;
+  }
+  return 63;
+}
+
+void StoreTableEntry(const Table& table, size_t* offset, uint8_t* dst) {
+  uint8_t flag_byte = KnownTableIndex(table.tag);
+  dst[(*offset)++] = flag_byte;
+  // The index here is treated as a set of flag bytes because
+  // bits 6 and 7 of the byte are reserved for future use as flags.
+  // 0x3f or 63 means an arbitrary table tag.
+  if ((flag_byte & 0x3f) == 0x3f) {
+    StoreU32(table.tag, offset, dst);
+  }
+  StoreBase128(table.src_length, offset, dst);
+  if ((table.flags & kWoff2FlagsTransform) != 0) {
+    StoreBase128(table.transform_length, offset, dst);
+  }
+}
+
+size_t TableEntrySize(const Table& table) {
+  uint8_t flag_byte = KnownTableIndex(table.tag);
+  size_t size = ((flag_byte & 0x3f) != 0x3f) ? 1 : 5;
+  size += Base128Size(table.src_length);
+  if ((table.flags & kWoff2FlagsTransform) != 0) {
+     size += Base128Size(table.transform_length);
+  }
+  return size;
+}
+
+size_t ComputeWoff2Length(const std::vector<Table>& tables,
+                          size_t extended_metadata_length) {
+  size_t size = kWoff2HeaderSize;
+  for (const auto& table : tables) {
+    size += TableEntrySize(table);
+  }
+  for (const auto& table : tables) {
+    size += table.dst_length;
+    size = Round4(size);
+  }
+  size += extended_metadata_length;
+  return size;
+}
+
+size_t ComputeTTFLength(const std::vector<Table>& tables) {
+  size_t size = 12 + 16 * tables.size();  // sfnt header
+  for (const auto& table : tables) {
+    size += Round4(table.src_length);
+  }
+  return size;
+}
+
+size_t ComputeTotalTransformLength(const Font& font) {
+  size_t total = 0;
+  for (const auto& i : font.tables) {
+    const Font::Table& table = i.second;
+    if (table.tag & 0x80808080 || !font.FindTable(table.tag ^ 0x80808080)) {
+      // Count transformed tables and non-transformed tables that do not have
+      // transformed versions.
+      total += table.length;
+    }
+  }
+  return total;
+}
+
+}  // namespace
+
+size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length) {
+  return MaxWOFF2CompressedSize(data, length, "");
+}
+
+size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length,
+    const string& extended_metadata) {
+  // Except for the header size, which is 32 bytes larger in woff2 format,
+  // all other parts should be smaller (table header in short format,
+  // transformations and compression). Just to be sure, we will give some
+  // headroom anyway.
+  return length + 1024 + extended_metadata.length();
+}
+
+uint32_t CompressedBufferSize(uint32_t original_size) {
+  return 1.2 * original_size + 10240;
+}
+
+bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
+                       uint8_t *result, size_t *result_length) {
+  return ConvertTTFToWOFF2(data, length, result, result_length, "");
+}
+
+bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
+                       uint8_t *result, size_t *result_length,
+                       const string& extended_metadata) {
+  Font font;
+  if (!ReadFont(data, length, &font)) {
+    fprintf(stderr, "Parsing of the input font failed.\n");
+    return false;
+  }
+
+  if (!NormalizeFont(&font)) {
+    fprintf(stderr, "Font normalization failed.\n");
+    return false;
+  }
+
+  if (!TransformGlyfAndLocaTables(&font)) {
+    fprintf(stderr, "Font transformation failed.\n");
+    return false;
+  }
+
+  const Font::Table* head_table = font.FindTable(kHeadTableTag);
+  if (head_table == NULL) {
+    fprintf(stderr, "Missing head table.\n");
+    return false;
+  }
+
+  // Although the compressed size of each table in the final woff2 file won't
+  // be larger than its transform_length, we have to allocate a large enough
+  // buffer for the compressor, since the compressor can potentially increase
+  // the size. If the compressor overflows this, it should return false and
+  // then this function will also return false.
+  size_t total_transform_length = ComputeTotalTransformLength(font);
+  size_t compression_buffer_size = CompressedBufferSize(total_transform_length);
+  std::vector<uint8_t> compression_buf(compression_buffer_size);
+  uint32_t total_compressed_length = compression_buffer_size;
+
+  // Collect all transformed data into one place.
+  std::vector<uint8_t> transform_buf(total_transform_length);
+  size_t transform_offset = 0;
+  for (const auto& i : font.tables) {
+    if (i.second.tag & 0x80808080) continue;
+    const Font::Table* table = font.FindTable(i.second.tag ^ 0x80808080);
+    if (table == NULL) table = &i.second;
+    StoreBytes(table->data, table->length,
+               &transform_offset, &transform_buf[0]);
+  }
+  // Compress all transformed data in one stream.
+  if (!Woff2Compress(transform_buf.data(), total_transform_length,
+                     &compression_buf[0],
+                     &total_compressed_length)) {
+    fprintf(stderr, "Compression of combined table failed.\n");
+    return false;
+  }
+
+  // Compress the extended metadata
+  uint32_t compressed_metadata_buf_length =
+    CompressedBufferSize(extended_metadata.length());
+  std::vector<uint8_t> compressed_metadata_buf(compressed_metadata_buf_length);
+
+  if (extended_metadata.length() > 0) {
+    if (!TextCompress((const uint8_t*)extended_metadata.data(),
+                      extended_metadata.length(),
+                      compressed_metadata_buf.data(),
+                      &compressed_metadata_buf_length)) {
+      fprintf(stderr, "Compression of extended metadata failed.\n");
+      return false;
+    }
+  } else {
+    compressed_metadata_buf_length = 0;
+  }
+
+  std::vector<Table> tables;
+  for (const auto& i : font.tables) {
+    const Font::Table& src_table = i.second;
+    if (src_table.tag & 0x80808080) {
+      // This is a transformed table, we will write it together with the
+      // original version.
+      continue;
+    }
+    Table table;
+    table.tag = src_table.tag;
+    table.flags = 0;
+    table.src_length = src_table.length;
+    table.transform_length = src_table.length;
+    const uint8_t* transformed_data = src_table.data;
+    const Font::Table* transformed_table =
+        font.FindTable(src_table.tag ^ 0x80808080);
+    if (transformed_table != NULL) {
+      table.flags |= kWoff2FlagsTransform;
+      table.transform_length = transformed_table->length;
+      transformed_data = transformed_table->data;
+    }
+    if (tables.empty()) {
+      table.dst_length = total_compressed_length;
+      table.dst_data = &compression_buf[0];
+    } else {
+      table.dst_length = 0;
+      table.dst_data = NULL;
+      table.flags |= kWoff2FlagsContinueStream;
+    }
+    tables.push_back(table);
+  }
+
+  size_t woff2_length =
+    ComputeWoff2Length(tables, compressed_metadata_buf_length);
+  if (woff2_length > *result_length) {
+    fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n",
+           *result_length, woff2_length);
+    return false;
+  }
+  *result_length = woff2_length;
+
+  size_t offset = 0;
+  StoreU32(kWoff2Signature, &offset, result);
+  StoreU32(font.flavor, &offset, result);
+  StoreU32(woff2_length, &offset, result);
+  Store16(tables.size(), &offset, result);
+  Store16(0, &offset, result);  // reserved
+  StoreU32(ComputeTTFLength(tables), &offset, result);
+  StoreU32(total_compressed_length, &offset, result);
+  StoreBytes(head_table->data + 4, 4, &offset, result);  // font revision
+  if (compressed_metadata_buf_length > 0) {
+    StoreU32(woff2_length - compressed_metadata_buf_length,
+             &offset, result);  // metaOffset
+    StoreU32(compressed_metadata_buf_length, &offset, result);  // metaLength
+    StoreU32(extended_metadata.length(), &offset, result);  // metaOrigLength
+  } else {
+    StoreU32(0, &offset, result);  // metaOffset
+    StoreU32(0, &offset, result);  // metaLength
+    StoreU32(0, &offset, result);  // metaOrigLength
+  }
+  StoreU32(0, &offset, result);  // privOffset
+  StoreU32(0, &offset, result);  // privLength
+  for (const auto& table : tables) {
+    StoreTableEntry(table, &offset, result);
+  }
+  for (const auto& table : tables) {
+    StoreBytes(table.dst_data, table.dst_length, &offset, result);
+    offset = Round4(offset);
+  }
+  StoreBytes(compressed_metadata_buf.data(), compressed_metadata_buf_length,
+             &offset, result);
+
+  if (*result_length != offset) {
+    fprintf(stderr, "Mismatch between computed and actual length "
+            "(%zd vs %zd)\n", *result_length, offset);
+    return false;
+  }
+  return true;
+}
+
+} // namespace woff2
diff --git a/third_party/woff2/src/woff2_enc.h b/third_party/woff2/src/woff2_enc.h
new file mode 100644
index 0000000..d6eb4dbf
--- /dev/null
+++ b/third_party/woff2/src/woff2_enc.h
@@ -0,0 +1,46 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Library for converting WOFF2 format font files to their TTF versions.
+
+#ifndef WOFF2_WOFF2_ENC_H_
+#define WOFF2_WOFF2_ENC_H_
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <string>
+
+using std::string;
+
+
+namespace woff2 {
+
+// Returns an upper bound on the size of the compressed file.
+size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length);
+size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length,
+                              const string& extended_metadata);
+
+// Compresses the font into the target buffer. *result_length should be at least
+// the value returned by MaxWOFF2CompressedSize(), upon return, it is set to the
+// actual compressed size. Returns true on successful compression.
+bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
+                       uint8_t *result, size_t *result_length);
+
+bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
+                       uint8_t *result, size_t *result_length,
+                       const string& extended_metadata);
+
+} // namespace woff2
+
+#endif  // WOFF2_WOFF2_ENC_H_
diff --git a/third_party/woff2/woff2.gyp b/third_party/woff2/woff2.gyp
new file mode 100644
index 0000000..985c6386
--- /dev/null
+++ b/third_party/woff2/woff2.gyp
@@ -0,0 +1,33 @@
+# 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'woff2_dec',
+      'type': 'static_library',
+      'include_dirs': [
+        'src',
+        '<(DEPTH)/third_party/brotli/dec',
+      ],
+      'dependencies': [
+        '<(DEPTH)/third_party/brotli/brotli.gyp:brotli',
+      ],
+      'sources': [
+        'src/buffer.h',
+        'src/round.h',
+        'src/store_bytes.h',
+        'src/table_tags.cc',
+        'src/table_tags.h',
+        'src/woff2_common.h',
+        'src/woff2_dec.cc',
+        'src/woff2_dec.h',
+      ],
+      # TODO(ksakamoto): http://crbug.com/167187
+      'msvs_disabled_warnings': [
+        4267,
+      ],
+    },
+  ],
+}
diff --git a/tools/chrome_proxy/OWNERS b/tools/chrome_proxy/OWNERS
index b4ac2d2..7921c4b7 100644
--- a/tools/chrome_proxy/OWNERS
+++ b/tools/chrome_proxy/OWNERS
@@ -4,3 +4,4 @@
 marq@chromium.org
 megjablon@chromium.org
 sclittle@chromium.org
+jeremyim@chromium.org
diff --git a/tools/chrome_proxy/integration_tests/chrome_proxy_metrics.py b/tools/chrome_proxy/integration_tests/chrome_proxy_metrics.py
index 264b7da..f21e3d8 100644
--- a/tools/chrome_proxy/integration_tests/chrome_proxy_metrics.py
+++ b/tools/chrome_proxy/integration_tests/chrome_proxy_metrics.py
@@ -283,34 +283,25 @@
     lo_fi_count = 0
 
     for resp in self.IterResponses(tab):
-      if resp.HasChromeProxyViaHeader():
+      if resp.HasChromeProxyLoFi():
         lo_fi_count += 1
       else:
-        r = resp.response
         raise ChromeProxyMetricException, (
-            '%s: LoFi not in request header.' % (r.url))
+            '%s: LoFi not in request header.' % (resp.response.url))
 
-      cl = resp.content_length
-      resource = resp.response.url
-    results.AddValue(scalar.ScalarValue(
-        results.current_page, 'lo_fi', 'count', lo_fi_count))
-
-    for resp in self.IterResponses(tab):
-      r = resp.response
-      cl = resp.content_length
-      ocl = resp.original_content_length
-      saving = resp.data_saving_rate * 100
-      if cl > 100:
+      if resp.content_length > 100:
         raise ChromeProxyMetricException, (
             'Image %s is %d bytes. Expecting less than 100 bytes.' %
-            (resource, cl))
+            (resp.response.url, resp.content_length))
+
+    if lo_fi_count == 0:
+      raise ChromeProxyMetricException, (
+          'Expected at least one LoFi response, but zero such responses were '
+          'received.')
 
     results.AddValue(scalar.ScalarValue(
-        results.current_page, 'content_length', 'bytes', cl))
-    results.AddValue(scalar.ScalarValue(
-        results.current_page, 'original_content_length', 'bytes', ocl))
-    results.AddValue(scalar.ScalarValue(
-        results.current_page, 'data_saving', 'percent', saving))
+        results.current_page, 'lo_fi', 'count', lo_fi_count))
+    super(ChromeProxyMetric, self).AddResults(tab, results)
 
   def AddResultsForBypass(self, tab, results):
     bypass_count = 0
diff --git a/tools/chrome_proxy/integration_tests/network_metrics_unittest.py b/tools/chrome_proxy/integration_tests/network_metrics_unittest.py
index f5d117a..a78dd678 100644
--- a/tools/chrome_proxy/integration_tests/network_metrics_unittest.py
+++ b/tools/chrome_proxy/integration_tests/network_metrics_unittest.py
@@ -6,8 +6,8 @@
 import unittest
 
 from integration_tests import network_metrics
-from telemetry.unittest_util import test_page_test_results
 from telemetry.timeline import event
+from telemetry.unittest_util import test_page_test_results
 
 
 HTML_BODY = """<!DOCTYPE HTML>
diff --git a/tools/clang/scripts/update.sh b/tools/clang/scripts/update.sh
index c842a58..10a4645 100755
--- a/tools/clang/scripts/update.sh
+++ b/tools/clang/scripts/update.sh
@@ -8,7 +8,7 @@
 # Do NOT CHANGE this if you don't know what you're doing -- see
 # https://code.google.com/p/chromium/wiki/UpdatingClang
 # Reverting problematic clang rolls is safe, though.
-CLANG_REVISION=231690
+CLANG_REVISION=233105
 
 # This is incremented when pushing a new build of Clang at the same revision.
 CLANG_SUB_REVISION=1
diff --git a/tools/export_tarball/export_tarball.py b/tools/export_tarball/export_tarball.py
index 08a8756..8d27f3fa 100755
--- a/tools/export_tarball/export_tarball.py
+++ b/tools/export_tarball/export_tarball.py
@@ -134,6 +134,7 @@
   # TODO(phajdan.jr): Remove --xz option when it's not needed for compatibility.
   parser.add_option("--xz", action="store_true")
   parser.add_option("--verbose", action="store_true", default=False)
+  parser.add_option("--progress", action="store_true", default=False)
 
   options, args = parser.parse_args(argv)
 
@@ -174,7 +175,19 @@
   finally:
     archive.close()
 
-  if subprocess.call(['xz', '-9', output_fullname]) != 0:
+  if options.progress:
+    sys.stdout.flush()
+    pv = subprocess.Popen(
+        ['pv', '--force', output_fullname],
+        stdout=subprocess.PIPE,
+        stderr=sys.stdout)
+    with open(output_fullname + '.xz', 'w') as f:
+      rc = subprocess.call(['xz', '-9', '-'], stdin=pv.stdout, stdout=f)
+    pv.wait()
+  else:
+    rc = subprocess.call(['xz', '-9', output_fullname])
+
+  if rc != 0:
     print 'xz -9 failed!'
     return 1
 
diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn
index 97c0f94..ccbb6bb6 100644
--- a/tools/gn/BUILD.gn
+++ b/tools/gn/BUILD.gn
@@ -255,7 +255,6 @@
     "parser_unittest.cc",
     "path_output_unittest.cc",
     "pattern_unittest.cc",
-    "run_all_unittests.cc",
     "scope_per_file_provider_unittest.cc",
     "scope_unittest.cc",
     "source_dir_unittest.cc",
@@ -274,6 +273,7 @@
   ]
   deps = [
     ":gn_lib",
+    "//base/test:run_all_unittests",
     "//base/test:test_support",
     "//testing/gtest",
   ]
diff --git a/tools/gn/command_clean.cc b/tools/gn/command_clean.cc
index 8872478f..cef35262 100644
--- a/tools/gn/command_clean.cc
+++ b/tools/gn/command_clean.cc
@@ -30,10 +30,10 @@
 
   std::string result;
   int num_blank_lines = 0;
-  for (size_t i = 0; i < lines.size(); ++i) {
-    result += lines[i];
+  for (const auto& line : lines) {
+    result += line;
     result += "\n";
-    if (lines[i].empty()) {
+    if (line.empty()) {
       ++num_blank_lines;
     }
     if (num_blank_lines == 2)
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
index ecb2fad..8b321e4 100644
--- a/tools/gn/command_desc.cc
+++ b/tools/gn/command_desc.cc
@@ -257,9 +257,9 @@
     OutputString("\n" + heading + " (in order applying):\n");
 
   Label toolchain_label = target->label().GetToolchainLabel();
-  for (size_t i = 0; i < configs.size(); i++) {
-    OutputString("  " +
-        configs[i].label.GetUserVisibleName(toolchain_label) + "\n");
+  for (const auto& config : configs) {
+    OutputString("  " + config.label.GetUserVisibleName(toolchain_label) +
+                 "\n");
   }
 }
 
@@ -275,9 +275,9 @@
     OutputString("\n" + heading + " (in order applying):\n");
 
   Label toolchain_label = target->label().GetToolchainLabel();
-  for (size_t i = 0; i < configs.size(); i++) {
-    OutputString("  " +
-        configs[i].label.GetUserVisibleName(toolchain_label) + "\n");
+  for (const auto& config : configs) {
+    OutputString("  " + config.label.GetUserVisibleName(toolchain_label) +
+                 "\n");
   }
 }
 
@@ -310,8 +310,8 @@
 
   Target::FileList sorted = files;
   std::sort(sorted.begin(), sorted.end());
-  for (size_t i = 0; i < sorted.size(); i++)
-    OutputString(indent + sorted[i].value() + "\n");
+  for (const auto& elem : sorted)
+    OutputString(indent + elem.value() + "\n");
 }
 
 void PrintSources(const Target* target, bool display_header) {
@@ -328,11 +328,8 @@
 
   if (target->output_type() == Target::ACTION) {
     // Action, print out outputs, don't apply sources to it.
-    for (size_t i = 0; i < target->action_values().outputs().list().size();
-         i++) {
-      OutputString("  " +
-                   target->action_values().outputs().list()[i].AsString() +
-                   "\n");
+    for (const auto& elem : target->action_values().outputs().list()) {
+      OutputString("  " + elem.AsString() + "\n");
     }
   } else {
     const SubstitutionList& outputs = target->action_values().outputs();
@@ -340,8 +337,8 @@
       // Display the pattern and resolved pattern separately, since there are
       // subtitutions used.
       OutputString("  Output pattern:\n");
-      for (size_t i = 0; i < outputs.list().size(); i++)
-        OutputString("    " + outputs.list()[i].AsString() + "\n");
+      for (const auto& elem : outputs.list())
+        OutputString("    " + elem.AsString() + "\n");
 
       // Now display what that resolves to given the sources.
       OutputString("\n  Resolved output file list:\n");
@@ -364,9 +361,8 @@
 void PrintArgs(const Target* target, bool display_header) {
   if (display_header)
     OutputString("\nargs:\n");
-  for (size_t i = 0; i < target->action_values().args().list().size(); i++) {
-    OutputString("  " +
-                 target->action_values().args().list()[i].AsString() + "\n");
+  for (const auto& elem : target->action_values().args().list()) {
+    OutputString("  " + elem.AsString() + "\n");
   }
 }
 
diff --git a/tools/gn/command_help.cc b/tools/gn/command_help.cc
index 0f8c347..d4f5ca5 100644
--- a/tools/gn/command_help.cc
+++ b/tools/gn/command_help.cc
@@ -12,6 +12,7 @@
 #include "tools/gn/functions.h"
 #include "tools/gn/input_conversion.h"
 #include "tools/gn/label_pattern.h"
+#include "tools/gn/parser.h"
 #include "tools/gn/setup.h"
 #include "tools/gn/standard_out.h"
 #include "tools/gn/substitution_writer.h"
@@ -58,6 +59,7 @@
   OutputString("\nOther help topics:\n");
   PrintShortHelp("buildargs: How build arguments work.");
   PrintShortHelp("dotfile: Info about the toplevel .gn file.");
+  PrintShortHelp("grammar: Formal grammar for GN build files.");
   PrintShortHelp("label_pattern: Matching more than one label.");
   PrintShortHelp(
       "input_conversion: Processing input from exec_script and read_file.");
@@ -169,6 +171,10 @@
     PrintLongHelp(kDotfile_Help);
     return 0;
   }
+  if (what == "grammar") {
+    PrintLongHelp(kGrammar_Help);
+    return 0;
+  }
   if (what == "input_conversion") {
     PrintLongHelp(kInputConversion_Help);
     return 0;
diff --git a/tools/gn/err.cc b/tools/gn/err.cc
index 6a9033d..319bfa8 100644
--- a/tools/gn/err.cc
+++ b/tools/gn/err.cc
@@ -64,8 +64,8 @@
     highlight[i] = ' ';
 
   // Highlight all the ranges on the line.
-  for (size_t i = 0; i < ranges.size(); i++)
-    FillRangeOnLine(ranges[i], location.line_number(), &highlight);
+  for (const auto& range : ranges)
+    FillRangeOnLine(range, location.line_number(), &highlight);
 
   // Allow the marker to be one past the end of the line for marking the end.
   highlight.push_back(' ');
diff --git a/tools/gn/escape.cc b/tools/gn/escape.cc
index 3104cb83..08d893e 100644
--- a/tools/gn/escape.cc
+++ b/tools/gn/escape.cc
@@ -44,18 +44,18 @@
                                 const EscapeOptions& options,
                                 DestString* dest,
                                 bool* needed_quoting) {
-  for (size_t i = 0; i < str.size(); i++)
-    NinjaEscapeChar(str[i], dest);
+  for (const auto& elem : str)
+    NinjaEscapeChar(elem, dest);
 }
 
 template<typename DestString>
 void EscapeStringToString_NinjaPreformatted(const base::StringPiece& str,
                                             DestString* dest) {
   // Only Ninja-escape $.
-  for (size_t i = 0; i < str.size(); i++) {
-    if (str[i] == '$')
+  for (const auto& elem : str) {
+    if (elem == '$')
       dest->push_back('$');
-    dest->push_back(str[i]);
+    dest->push_back(elem);
   }
 }
 
@@ -118,26 +118,26 @@
                                          const EscapeOptions& options,
                                          DestString* dest,
                                          bool* needed_quoting) {
-  for (size_t i = 0; i < str.size(); i++) {
-    if (str[i] == '$' || str[i] == ' ') {
+  for (const auto& elem : str) {
+    if (elem == '$' || elem == ' ') {
       // Space and $ are special to both Ninja and the shell. '$' escape for
       // Ninja, then backslash-escape for the shell.
       dest->push_back('\\');
       dest->push_back('$');
-      dest->push_back(str[i]);
-    } else if (str[i] == ':') {
+      dest->push_back(elem);
+    } else if (elem == ':') {
       // Colon is the only other Ninja special char, which is not special to
       // the shell.
       dest->push_back('$');
       dest->push_back(':');
-    } else if (static_cast<unsigned>(str[i]) >= 0x80 ||
-               !kShellValid[static_cast<int>(str[i])]) {
+    } else if (static_cast<unsigned>(elem) >= 0x80 ||
+               !kShellValid[static_cast<int>(elem)]) {
       // All other invalid shell chars get backslash-escaped.
       dest->push_back('\\');
-      dest->push_back(str[i]);
+      dest->push_back(elem);
     } else {
       // Everything else is a literal.
-      dest->push_back(str[i]);
+      dest->push_back(elem);
     }
   }
 }
diff --git a/tools/gn/exec_process.cc b/tools/gn/exec_process.cc
index d499921..cc4a475 100644
--- a/tools/gn/exec_process.cc
+++ b/tools/gn/exec_process.cc
@@ -194,8 +194,8 @@
         // Adding another element here? Remeber to increase the argument to
         // reserve(), above.
 
-        for (size_t i = 0; i < fd_shuffle1.size(); ++i)
-          fd_shuffle2.push_back(fd_shuffle1[i]);
+        for (const auto& elem : fd_shuffle1)
+          fd_shuffle2.push_back(elem);
 
         if (!ShuffleFileDescriptors(&fd_shuffle1))
           _exit(127);
diff --git a/tools/gn/generate_test_gn_data.cc b/tools/gn/generate_test_gn_data.cc
index 05197f7..11dbf361 100644
--- a/tools/gn/generate_test_gn_data.cc
+++ b/tools/gn/generate_test_gn_data.cc
@@ -30,8 +30,8 @@
 
 base::FilePath RepoPathToPathName(const std::vector<int>& repo_path) {
   base::FilePath ret;
-  for (size_t i = 0; i < repo_path.size(); i++) {
-    ret = ret.Append(UTF8ToFilePath(base::IntToString(repo_path[i])));
+  for (const auto& elem : repo_path) {
+    ret = ret.Append(UTF8ToFilePath(base::IntToString(elem)));
   }
   return ret;
 }
@@ -58,9 +58,9 @@
 std::string RepoPathToFullTargetName(const std::vector<int>& repo_path,
                                  int target_index) {
   std::string ret;
-  for (size_t i = 0; i < repo_path.size(); i++) {
+  for (const auto& elem : repo_path) {
     ret.push_back('/');
-    ret.append(base::IntToString(repo_path[i]));
+    ret.append(base::IntToString(elem));
   }
 
   ret += ":" + RepoPathToTargetName(repo_path, target_index);
diff --git a/tools/gn/gn.gyp b/tools/gn/gn.gyp
index 0b6d204..ed2cf3d5 100644
--- a/tools/gn/gn.gyp
+++ b/tools/gn/gn.gyp
@@ -232,7 +232,6 @@
         'parser_unittest.cc',
         'path_output_unittest.cc',
         'pattern_unittest.cc',
-        'run_all_unittests.cc',
         'scope_per_file_provider_unittest.cc',
         'scope_unittest.cc',
         'source_dir_unittest.cc',
@@ -251,6 +250,7 @@
       ],
       'dependencies': [
         'gn_lib',
+        '../../base/base.gyp:run_all_unittests',
         '../../base/base.gyp:test_support_base',
         '../../testing/gtest.gyp:gtest',
       ],
diff --git a/tools/gn/header_checker.cc b/tools/gn/header_checker.cc
index 423929b0..4d2f4cb 100644
--- a/tools/gn/header_checker.cc
+++ b/tools/gn/header_checker.cc
@@ -330,10 +330,10 @@
   Err last_error;
 
   bool found_dependency = false;
-  for (size_t i = 0; i < targets.size(); i++) {
+  for (const auto& target : targets) {
     // We always allow source files in a target to include headers also in that
     // target.
-    const Target* to_target = targets[i].target;
+    const Target* to_target = target.target;
     if (to_target == from_target)
       return true;
 
@@ -357,20 +357,19 @@
 
       found_dependency = true;
 
-      if (targets[i].is_public && is_permitted_chain) {
+      if (target.is_public && is_permitted_chain) {
         // This one is OK, we're done.
         last_error = Err();
         break;
       }
 
       // Diagnose the error.
-      if (!targets[i].is_public) {
+      if (!target.is_public) {
         // Danger: must call CreatePersistentRange to put in Err.
-        last_error = Err(
-            CreatePersistentRange(source_file, range),
-            "Including a private header.",
-            "This file is private to the target " +
-                targets[i].target->label().GetUserVisibleName(false));
+        last_error = Err(CreatePersistentRange(source_file, range),
+                         "Including a private header.",
+                         "This file is private to the target " +
+                             target.target->label().GetUserVisibleName(false));
       } else if (!is_permitted_chain) {
         last_error = Err(
             CreatePersistentRange(source_file, range),
diff --git a/tools/gn/ninja_binary_target_writer.cc b/tools/gn/ninja_binary_target_writer.cc
index e6b0e91d..84b819b 100644
--- a/tools/gn/ninja_binary_target_writer.cc
+++ b/tools/gn/ninja_binary_target_writer.cc
@@ -441,10 +441,9 @@
     out_ << " ||";
 
     // Non-linkable targets.
-    for (size_t i = 0; i < non_linkable_deps.size(); i++) {
+    for (const auto& non_linkable_dep : non_linkable_deps) {
       out_ << " ";
-      path_output_.WriteFile(
-          out_, non_linkable_deps[i]->dependency_output_file());
+      path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file());
     }
   }
 }
diff --git a/tools/gn/ninja_build_writer.cc b/tools/gn/ninja_build_writer.cc
index 8957661c..3ed0a627 100644
--- a/tools/gn/ninja_build_writer.cc
+++ b/tools/gn/ninja_build_writer.cc
@@ -157,13 +157,13 @@
   dep_out_ << "build.ninja:";
   std::vector<base::FilePath> input_files;
   g_scheduler->input_file_manager()->GetAllPhysicalInputFileNames(&input_files);
-  for (size_t i = 0; i < input_files.size(); i++)
-    dep_out_ << " " << FilePathToUTF8(input_files[i]);
+  for (const auto& input_file : input_files)
+    dep_out_ << " " << FilePathToUTF8(input_file);
 
   // Other files read by the build.
   std::vector<base::FilePath> other_files = g_scheduler->GetGenDependencies();
-  for (size_t i = 0; i < other_files.size(); i++)
-    dep_out_ << " " << FilePathToUTF8(other_files[i]);
+  for (const auto& other_file : other_files)
+    dep_out_ << " " << FilePathToUTF8(other_file);
 
   out_ << std::endl;
 }
@@ -175,9 +175,9 @@
 }
 
 void NinjaBuildWriter::WriteSubninjas() {
-  for (size_t i = 0; i < all_settings_.size(); i++) {
+  for (const auto& elem : all_settings_) {
     out_ << "subninja ";
-    path_output_.WriteFile(out_, GetNinjaFileForToolchain(all_settings_[i]));
+    path_output_.WriteFile(out_, GetNinjaFileForToolchain(elem));
     out_ << std::endl;
   }
   out_ << std::endl;
@@ -193,8 +193,7 @@
   std::map<std::string, int> small_name_count;
   std::vector<const Target*> toplevel_targets;
   base::hash_set<std::string> target_files;
-  for (size_t i = 0; i < default_toolchain_targets_.size(); i++) {
-    const Target* target = default_toolchain_targets_[i];
+  for (const auto& target : default_toolchain_targets_) {
     const Label& label = target->label();
     small_name_count[label.name()]++;
 
@@ -211,8 +210,7 @@
       toplevel_targets.push_back(target);
   }
 
-  for (size_t i = 0; i < default_toolchain_targets_.size(); i++) {
-    const Target* target = default_toolchain_targets_[i];
+  for (const auto& target : default_toolchain_targets_) {
     const Label& label = target->label();
     OutputFile target_file(target->dependency_output_file());
     // The output files may have leading "./" so normalize those away.
@@ -249,11 +247,10 @@
 
   // Pick up phony rules for the toplevel targets with non-unique names (which
   // would have been skipped in the above loop).
-  for (size_t i = 0; i < toplevel_targets.size(); i++) {
-    if (small_name_count[toplevel_targets[i]->label().name()] > 1) {
-      const Target* target = toplevel_targets[i];
-      WritePhonyRule(target, target->dependency_output_file(),
-                     target->label().name());
+  for (const auto& toplevel_target : toplevel_targets) {
+    if (small_name_count[toplevel_target->label().name()] > 1) {
+      WritePhonyRule(toplevel_target, toplevel_target->dependency_output_file(),
+                     toplevel_target->label().name());
     }
   }
 
@@ -261,8 +258,8 @@
   // target (rather than building 'all' by default). By convention
   // we use group("default") but it doesn't have to be a group.
   bool default_target_exists = false;
-  for (size_t i = 0; i < default_toolchain_targets_.size(); i++) {
-    const Label& label = default_toolchain_targets_[i]->label();
+  for (const auto& target : default_toolchain_targets_) {
+    const Label& label = target->label();
     if (label.dir().value() == "//" && label.name() == "default")
       default_target_exists = true;
   }
diff --git a/tools/gn/ninja_copy_target_writer.cc b/tools/gn/ninja_copy_target_writer.cc
index 9cd1b72..b0313be 100644
--- a/tools/gn/ninja_copy_target_writer.cc
+++ b/tools/gn/ninja_copy_target_writer.cc
@@ -100,9 +100,7 @@
   // where a command might need to make sure something else runs before it runs
   // to avoid conflicts. Such cases should be avoided where possible, but
   // sometimes that's not possible.
-  for (size_t i = 0; i < target_->sources().size(); i++) {
-    const SourceFile& input_file = target_->sources()[i];
-
+  for (const auto& input_file : target_->sources()) {
     OutputFile output_file =
         SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
             target_->settings(), output_subst, input_file);
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc
index db94a1b..0434ed6 100644
--- a/tools/gn/ninja_target_writer.cc
+++ b/tools/gn/ninja_target_writer.cc
@@ -195,16 +195,14 @@
   }
 
   // Input files are order-only deps.
-  const Target::FileList& prereqs = target_->inputs();
-  for (size_t i = 0; i < prereqs.size(); i++) {
+  for (const auto& input : target_->inputs()) {
     out_ << " ";
-    path_output_.WriteFile(out_, prereqs[i]);
+    path_output_.WriteFile(out_, input);
   }
   if (list_sources_as_input_deps) {
-    const Target::FileList& sources = target_->sources();
-    for (size_t i = 0; i < sources.size(); i++) {
+    for (const auto& source : target_->sources()) {
       out_ << " ";
-      path_output_.WriteFile(out_, sources[i]);
+      path_output_.WriteFile(out_, source);
     }
   }
 
@@ -225,8 +223,8 @@
   // toolchains often have more than one dependency, we could consider writing
   // a toolchain-specific stamp file and only include the stamp here.
   const LabelTargetVector& toolchain_deps = target_->toolchain()->deps();
-  for (size_t i = 0; i < toolchain_deps.size(); i++)
-    unique_deps.insert(toolchain_deps[i].ptr);
+  for (const auto& toolchain_dep : toolchain_deps)
+    unique_deps.insert(toolchain_dep.ptr);
 
   for (const auto& dep : unique_deps) {
     DCHECK(!dep->dependency_output_file().value().empty());
diff --git a/tools/gn/ninja_toolchain_writer.cc b/tools/gn/ninja_toolchain_writer.cc
index 0122471..c22fb2c4 100644
--- a/tools/gn/ninja_toolchain_writer.cc
+++ b/tools/gn/ninja_toolchain_writer.cc
@@ -128,9 +128,9 @@
 
 void NinjaToolchainWriter::WriteSubninjas() {
   // Write subninja commands for each generated target.
-  for (size_t i = 0; i < targets_.size(); i++) {
-    OutputFile ninja_file(targets_[i]->settings()->build_settings(),
-                          GetNinjaFileForTarget(targets_[i]));
+  for (const auto& target : targets_) {
+    OutputFile ninja_file(target->settings()->build_settings(),
+                          GetNinjaFileForTarget(target));
     out_ << "subninja ";
     path_output_.WriteFile(out_, ninja_file);
     out_ << std::endl;
diff --git a/tools/gn/ninja_writer.cc b/tools/gn/ninja_writer.cc
index 37c982f..23b9a72f 100644
--- a/tools/gn/ninja_writer.cc
+++ b/tools/gn/ninja_writer.cc
@@ -52,11 +52,11 @@
   CategorizedMap categorized;
 
   std::vector<const BuilderRecord*> all_records = builder_->GetAllRecords();
-  for (size_t i = 0; i < all_records.size(); i++) {
-    if (all_records[i]->type() == BuilderRecord::ITEM_TARGET &&
-        all_records[i]->should_generate()) {
-      categorized[all_records[i]->label().GetToolchainLabel()].push_back(
-          all_records[i]->item()->AsTarget());
+  for (const auto& all_record : all_records) {
+    if (all_record->type() == BuilderRecord::ITEM_TARGET &&
+        all_record->should_generate()) {
+      categorized[all_record->label().GetToolchainLabel()].push_back(
+          all_record->item()->AsTarget());
       }
   }
   if (categorized.empty()) {
diff --git a/tools/gn/operators.cc b/tools/gn/operators.cc
index 17e31a7..b78e935 100644
--- a/tools/gn/operators.cc
+++ b/tools/gn/operators.cc
@@ -98,10 +98,10 @@
     }
 
     case Value::LIST:  // Filter out each individual thing.
-      for (size_t i = 0; i < to_remove.list_value().size(); i++) {
+      for (const auto& elem : to_remove.list_value()) {
         // TODO(brettw) if the nested item is a list, we may want to search
         // for the literal list rather than remote the items in it.
-        RemoveMatchesFromList(op_node, list, to_remove.list_value()[i], err);
+        RemoveMatchesFromList(op_node, list, elem, err);
         if (err->has_error())
           return;
       }
diff --git a/tools/gn/parser.cc b/tools/gn/parser.cc
index 2a037fb2..1fa432e 100644
--- a/tools/gn/parser.cc
+++ b/tools/gn/parser.cc
@@ -9,14 +9,105 @@
 #include "tools/gn/operators.h"
 #include "tools/gn/token.h"
 
-// grammar:
-//
-// file       := (statement)*
-// statement  := block | if | assignment
-// block      := '{' statement* '}'
-// if         := 'if' '(' expr ')' statement [ else ]
-// else       := 'else' (if | statement)*
-// assignment := ident {'=' | '+=' | '-='} expr
+const char kGrammar_Help[] =
+    "GN build language grammar\n"
+    "\n"
+    "Tokens\n"
+    "\n"
+    "  GN build files are read as sequences of tokens.  While splitting the\n"
+    "  file into tokens, the next token is the longest sequence of characters\n"
+    "  that form a valid token.\n"
+    "\n"
+    "White space and comments\n"
+    "\n"
+    "  White space is comprised of spaces (U+0020), horizontal tabs (U+0009),\n"
+    "  carriage returns (U+000D), and newlines (U+000A).\n"
+    "\n"
+    "  Comments start at the character \"#\" and stop at the next newline.\n"
+    "\n"
+    "  White space and comments are ignored except that they may separate\n"
+    "  tokens that would otherwise combine into a single token.\n"
+    "\n"
+    "Identifiers\n"
+    "\n"
+    "  Identifiers name variables and functions.\n"
+    "\n"
+    "      identifier = letter { letter | digit } .\n"
+    "      letter     = \"A\" ... \"Z\" | \"a\" ... \"z\" | \"_\" .\n"
+    "      digit      = \"0\" ... \"9\" .\n"
+    "\n"
+    "Keywords\n"
+    "\n"
+    "  The following keywords are reserved and may not be used as\n"
+    "  identifiers:\n"
+    "\n"
+    "          else    false   if      true\n"
+    "\n"
+    "Integer literals\n"
+    "\n"
+    "  An integer literal represents a decimal integer value.\n"
+    "\n"
+    "      integer = [ \"-\" ] digit { digit } .\n"
+    "\n"
+    "String literals\n"
+    "\n"
+    "  A string literal represents a string value consisting of the quoted\n"
+    "  characters with possible escape sequences and variable expansions.\n"
+    "\n"
+    "      string    = `\"` { char | escape | expansion } `\"` .\n"
+    "      escape    = `\\` ( \"$\" | `\"` | char ) .\n"
+    "      expansion = \"$\" ( identifier | \"{\" identifier \"}\" ) .\n"
+    "      char      = /* any character except \"$\", `\"`, or newline */ .\n"
+    "\n"
+    "  After a backslash, certain sequences represent special characters:\n"
+    "\n"
+    "          \\\"    U+0022    quotation mark\n"
+    "          \\$    U+0024    dollar sign\n"
+    "          \\\\    U+005C    backslash\n"
+    "\n"
+    "  All other backslashes represent themselves.\n"
+    "\n"
+    "Punctuation\n"
+    "\n"
+    "  The following character sequences represent punctuation:\n"
+    "\n"
+    "          +       +=      ==      !=      (       )\n"
+    "          -       -=      <       <=      [       ]\n"
+    "          !       =       >       >=      {       }\n"
+    "                          &&      ||      .       ,\n"
+    "\n"
+    "Grammar\n"
+    "\n"
+    "  The input tokens form a syntax tree following a context-free grammar:\n"
+    "\n"
+    "      File = StatementList .\n"
+    "\n"
+    "      Statement     = Assignment | Call | Condition .\n"
+    "      Assignment    = identifier AssignOp Expr .\n"
+    "      Call          = identifier \"(\" [ ExprList ] \")\" [ Block ] .\n"
+    "      Condition     = \"if\" \"(\" Expr \")\" Block\n"
+    "                      [ \"else\" ( Condition | Block ) ] .\n"
+    "      Block         = \"{\" StatementList \"}\" .\n"
+    "      StatementList = { Statement } .\n"
+    "\n"
+    "      Expr        = UnaryExpr | Expr BinaryOp Expr .\n"
+    "      UnaryExpr   = PrimaryExpr | UnaryOp UnaryExpr .\n"
+    "      PrimaryExpr = identifier | integer | string | Call\n"
+    "                  | identifier \"[\" Expr \"]\"\n"
+    "                  | identifier \".\" identifier\n"
+    "                  | \"(\" Expr \")\"\n"
+    "                  | \"[\" [ ExprList [ \",\" ] ] \"]\" .\n"
+    "      ExprList    = Expr { \",\" Expr } .\n"
+    "\n"
+    "      AssignOp = \"=\" | \"+=\" | \"-=\" .\n"
+    "      UnaryOp  = \"!\" .\n"
+    "      BinaryOp = \"+\" | \"-\"                  // highest priority\n"
+    "               | \"<\" | \"<=\" | \">\" | \">=\"\n"
+    "               | \"==\" | \"!=\"\n"
+    "               | \"&&\"\n"
+    "               | \"||\" .                     // lowest priority\n"
+    "\n"
+    "  All binary operators are left-associative.\n";
 
 enum Precedence {
   PRECEDENCE_ASSIGNMENT = 1,  // Lowest precedence.
diff --git a/tools/gn/parser.h b/tools/gn/parser.h
index 530596f6..ce24e0d 100644
--- a/tools/gn/parser.h
+++ b/tools/gn/parser.h
@@ -19,6 +19,8 @@
 typedef scoped_ptr<ParseNode> (Parser::*InfixFunc)(scoped_ptr<ParseNode> left,
                                                    Token token);
 
+extern const char kGrammar_Help[];
+
 struct ParserHelper {
   PrefixFunc prefix;
   InfixFunc infix;
diff --git a/tools/gn/pattern.cc b/tools/gn/pattern.cc
index 10ff5a4..4b14d2a 100644
--- a/tools/gn/pattern.cc
+++ b/tools/gn/pattern.cc
@@ -167,16 +167,16 @@
   }
 
   const std::vector<Value>& list = v.list_value();
-  for (size_t i = 0; i < list.size(); i++) {
-    if (!list[i].VerifyTypeIs(Value::STRING, err))
+  for (const auto& elem : list) {
+    if (!elem.VerifyTypeIs(Value::STRING, err))
       return;
-    patterns_.push_back(Pattern(list[i].string_value()));
+    patterns_.push_back(Pattern(elem.string_value()));
   }
 }
 
 bool PatternList::MatchesString(const std::string& s) const {
-  for (size_t i = 0; i < patterns_.size(); i++) {
-    if (patterns_[i].MatchesString(s))
+  for (const auto& pattern : patterns_) {
+    if (pattern.MatchesString(s))
       return true;
   }
   return false;
diff --git a/tools/gn/run_all_unittests.cc b/tools/gn/run_all_unittests.cc
deleted file mode 100644
index 77b9e547..0000000
--- a/tools/gn/run_all_unittests.cc
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/test/test_suite.h"
-
-// Don't use the multiprocess test harness. This test suite is fast enough and
-// it makes the output more difficult to read.
-int main(int argc, char** argv) {
-  base::TestSuite test_suite(argc, argv);
-  return test_suite.Run();
-}
diff --git a/tools/gn/standard_out.cc b/tools/gn/standard_out.cc
index b318fe4..69d443be 100644
--- a/tools/gn/standard_out.cc
+++ b/tools/gn/standard_out.cc
@@ -179,12 +179,12 @@
 
     // Check for a comment.
     TextDecoration dec = DECORATION_NONE;
-    for (size_t char_i = 0; char_i < line.size(); char_i++) {
-      if (line[char_i] == '#') {
+    for (const auto& elem : line) {
+      if (elem == '#') {
         // Got a comment, draw dimmed.
         dec = DECORATION_DIM;
         break;
-      } else if (line[char_i] != ' ') {
+      } else if (elem != ' ') {
         break;
       }
     }
diff --git a/tools/gn/substitution_pattern.cc b/tools/gn/substitution_pattern.cc
index 60ec191bc..838a5a7 100644
--- a/tools/gn/substitution_pattern.cc
+++ b/tools/gn/substitution_pattern.cc
@@ -101,19 +101,19 @@
 
 std::string SubstitutionPattern::AsString() const {
   std::string result;
-  for (size_t i = 0; i < ranges_.size(); i++) {
-    if (ranges_[i].type == SUBSTITUTION_LITERAL)
-      result.append(ranges_[i].literal);
+  for (const auto& elem : ranges_) {
+    if (elem.type == SUBSTITUTION_LITERAL)
+      result.append(elem.literal);
     else
-      result.append(kSubstitutionNames[ranges_[i].type]);
+      result.append(kSubstitutionNames[elem.type]);
   }
   return result;
 }
 
 void SubstitutionPattern::FillRequiredTypes(SubstitutionBits* bits) const {
-  for (size_t i = 0; i < ranges_.size(); i++) {
-    if (ranges_[i].type != SUBSTITUTION_LITERAL)
-      bits->used[static_cast<size_t>(ranges_[i].type)] = true;
+  for (const auto& elem : ranges_) {
+    if (elem.type != SUBSTITUTION_LITERAL)
+      bits->used[static_cast<size_t>(elem.type)] = true;
   }
 }
 
diff --git a/tools/gn/toolchain.cc b/tools/gn/toolchain.cc
index 341a48cf..dbc1173 100644
--- a/tools/gn/toolchain.cc
+++ b/tools/gn/toolchain.cc
@@ -89,9 +89,9 @@
 
 void Toolchain::ToolchainSetupComplete() {
   // Collect required bits from all tools.
-  for (size_t i = 0; i < TYPE_NUMTYPES; i++) {
-    if (tools_[i])
-      substitution_bits_.MergeFrom(tools_[i]->substitution_bits());
+  for (const auto& tool : tools_) {
+    if (tool)
+      substitution_bits_.MergeFrom(tool->substitution_bits());
   }
 
   setup_complete_ = true;
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index 43a4315..659523b 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -234,10 +234,10 @@
     "messages": [30000],
   },
   "components/resources/components_resources.grd": {
-    "includes": [30250],
+    "includes": [30225],
   },
   "components/resources/components_scaled_resources.grd": {
-    "structures": [30300],
+    "structures": [30280],
   },
   "third_party/WebKit/public/blink_resources.grd": {
     "includes": [30350],
diff --git a/tools/json_schema_compiler/code.py b/tools/json_schema_compiler/code.py
index 8ce6afa..10a5d88 100644
--- a/tools/json_schema_compiler/code.py
+++ b/tools/json_schema_compiler/code.py
@@ -85,7 +85,7 @@
       self.Append(line)
     return self
 
-  def Comment(self, comment, comment_prefix='// '):
+  def Comment(self, comment, comment_prefix='// ', wrap_indent=0):
     """Adds the given string as a comment.
 
     Will split the comment if it's too long. Use mainly for variable length
@@ -93,17 +93,31 @@
 
     Unaffected by code.Substitute().
     """
-    max_len = self._comment_length - self._indent_level - len(comment_prefix)
-    while len(comment) >= max_len:
-      line = comment[0:max_len]
-      last_space = line.rfind(' ')
+    # Helper function to trim a comment to the maximum length, and return one
+    # line and the remainder of the comment.
+    def trim_comment(comment, max_len):
+      if len(comment) <= max_len:
+        return comment, ''
+      last_space = comment.rfind(' ', 0, max_len + 1)
       if last_space != -1:
-        line = line[0:last_space]
+        line = comment[0:last_space]
         comment = comment[last_space + 1:]
       else:
+        line = comment[0:max_len]
         comment = comment[max_len:]
-      self.Append(comment_prefix + line, substitute=False)
-    self.Append(comment_prefix + comment, substitute=False)
+      return line, comment
+
+    # First line has the full maximum length.
+    max_len = self._comment_length - self._indent_level - len(comment_prefix)
+    line, comment = trim_comment(comment, max_len)
+    self.Append(comment_prefix + line, substitute=False)
+
+    # Any subsequent lines be subject to the wrap indent.
+    max_len = max_len - wrap_indent
+    while len(comment):
+      line, comment = trim_comment(comment, max_len)
+      self.Append(comment_prefix + (' ' * wrap_indent) + line, substitute=False)
+
     return self
 
   def Substitute(self, d):
diff --git a/tools/json_schema_compiler/code_test.py b/tools/json_schema_compiler/code_test.py
index ca36524..c6ca33f 100755
--- a/tools/json_schema_compiler/code_test.py
+++ b/tools/json_schema_compiler/code_test.py
@@ -117,14 +117,14 @@
     self.assertFalse(c.IsEmpty())
 
   def testComment(self):
-    long_comment = ('This comment is eighty nine characters in longness, '
-        'that is, to use another word, length')
+    long_comment = ('This comment is ninety one characters in longness, '
+        'that is, using a different word, length.')
     c = Code()
     c.Comment(long_comment)
     self.assertEquals(
-        '// This comment is eighty nine characters '
-        'in longness, that is, to use another\n'
-        '// word, length',
+        '// This comment is ninety one characters '
+        'in longness, that is, using a different\n'
+        '// word, length.',
         c.Render())
     c = Code()
     c.Sblock('sblock')
@@ -133,13 +133,13 @@
     c.Comment(long_comment)
     self.assertEquals(
         'sblock\n'
-        '  // This comment is eighty nine characters '
-        'in longness, that is, to use\n'
-        '  // another word, length\n'
+        '  // This comment is ninety one characters '
+        'in longness, that is, using a\n'
+        '  // different word, length.\n'
         'eblock\n'
-        '// This comment is eighty nine characters in '
-        'longness, that is, to use another\n'
-        '// word, length',
+        '// This comment is ninety one characters in '
+        'longness, that is, using a different\n'
+        '// word, length.',
         c.Render())
     long_word = 'x' * 100
     c = Code()
@@ -148,6 +148,14 @@
         '// ' + 'x' * 77 + '\n'
         '// ' + 'x' * 23,
         c.Render())
+    c = Code(indent_size=2, comment_length=40)
+    c.Comment('Pretend this is a Closure Compiler style comment, which should '
+        'both wrap and indent', comment_prefix=' * ', wrap_indent=4)
+    self.assertEquals(
+        ' * Pretend this is a Closure Compiler\n'
+        ' *     style comment, which should both\n'
+        ' *     wrap and indent',
+        c.Render())
 
   def testCommentWithSpecialCharacters(self):
     c = Code()
diff --git a/tools/json_schema_compiler/compiler.py b/tools/json_schema_compiler/compiler.py
index 0d4b264..e09366b2 100755
--- a/tools/json_schema_compiler/compiler.py
+++ b/tools/json_schema_compiler/compiler.py
@@ -24,6 +24,7 @@
 from cpp_bundle_generator import CppBundleGenerator
 from cpp_generator import CppGenerator
 from cpp_type_generator import CppTypeGenerator
+from js_externs_generator import JsExternsGenerator
 import json_schema
 from cpp_namespace_environment import CppNamespaceEnvironment
 from model import Model
@@ -31,7 +32,7 @@
 
 # Names of supported code generators, as specified on the command-line.
 # First is default.
-GENERATORS = ['cpp', 'cpp-bundle-registration', 'cpp-bundle-schema']
+GENERATORS = ['cpp', 'cpp-bundle-registration', 'cpp-bundle-schema', 'externs']
 
 def GenerateSchema(generator_name,
                    file_paths,
@@ -115,6 +116,10 @@
       ('%s.h' % filename_base, cpp_generator.h_generator),
       ('%s.cc' % filename_base, cpp_generator.cc_generator)
     ]
+  elif generator_name == 'externs':
+    generators = [
+      ('%s_externs.js' % namespace.unix_name, JsExternsGenerator())
+    ]
   else:
     raise Exception('Unrecognised generator %s' % generator_name)
 
diff --git a/tools/json_schema_compiler/idl_schema.py b/tools/json_schema_compiler/idl_schema.py
index 31e0a273..7364800 100755
--- a/tools/json_schema_compiler/idl_schema.py
+++ b/tools/json_schema_compiler/idl_schema.py
@@ -471,6 +471,16 @@
   contents = f.read()
   f.close()
 
+  return Process(contents, filename)
+
+
+def Process(contents, filename):
+  '''
+  Processes the contents of a file and returns an equivalent Python dictionary
+  in a format that the JSON schema compiler expects to see. (Separate from
+  Load primarily for testing purposes.)
+  '''
+
   idl = idl_parser.IDLParser().ParseData(contents, filename)
   idl_schema = IDLSchema(idl)
   return idl_schema.process()
diff --git a/tools/json_schema_compiler/js_externs_generator.py b/tools/json_schema_compiler/js_externs_generator.py
new file mode 100644
index 0000000..587c04e6
--- /dev/null
+++ b/tools/json_schema_compiler/js_externs_generator.py
@@ -0,0 +1,260 @@
+# 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.
+"""
+Generator that produces an externs file for the Closure Compiler.
+Note: This is a work in progress, and generated externs may require tweaking.
+
+See https://developers.google.com/closure/compiler/docs/api-tutorial3#externs
+"""
+
+from code import Code
+from model import *
+from schema_util import *
+
+import os
+from datetime import datetime
+
+LICENSE = ("""// Copyright %s 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.
+""" % datetime.now().year)
+
+class JsExternsGenerator(object):
+  def Generate(self, namespace):
+    return _Generator(namespace).Generate()
+
+class _Generator(object):
+  def __init__(self, namespace):
+    self._namespace = namespace
+
+  def Generate(self):
+    """Generates a Code object with the schema for the entire namespace.
+    """
+    c = Code()
+    (c.Append(LICENSE)
+        .Append()
+        .Append('/** @fileoverview Externs generated from namespace: %s */' %
+                self._namespace.name)
+        .Append())
+
+    c.Cblock(self._GenerateNamespaceObject())
+
+    for js_type in self._namespace.types.values():
+      c.Cblock(self._GenerateType(js_type))
+
+    for function in self._namespace.functions.values():
+      c.Cblock(self._GenerateFunction(function))
+
+    for event in self._namespace.events.values():
+      c.Cblock(self._GenerateEvent(event))
+
+    return c
+
+  def _GenerateType(self, js_type):
+    """Given a Type object, returns the Code for this type's definition.
+    """
+    c = Code()
+    if js_type.property_type is PropertyType.ENUM:
+      c.Concat(self._GenerateEnumJsDoc(js_type))
+    else:
+      c.Concat(self._GenerateTypeJsDoc(js_type))
+
+    return c
+
+  def _GenerateEnumJsDoc(self, js_type):
+    """ Given an Enum Type object, returns the Code for the enum's definition.
+    """
+    c = Code()
+    c.Append('/**').Append(' * @enum {string}').Append(' */')
+    c.Append('chrome.%s.%s = {' % (self._namespace.name, js_type.name))
+    c.Append('\n'.join(
+        ["  %s: '%s'," % (v.name, v.name) for v in js_type.enum_values]))
+    c.Append('};')
+    return c
+
+  def _IsTypeConstructor(self, js_type):
+    """Returns true if the given type should be a @constructor. If this returns
+       false, the type is a typedef.
+    """
+    return any(prop.type_.property_type is PropertyType.FUNCTION
+               for prop in js_type.properties.values())
+
+  def _GenerateTypeJsDoc(self, js_type):
+    """Generates the documentation for a type as a Code.
+
+    Returns an empty code object if the object has no documentation.
+    """
+    c = Code()
+    c.Append('/**')
+
+    if js_type.description:
+      for line in js_type.description.splitlines():
+        c.Comment(line, comment_prefix = ' * ')
+
+    is_constructor = self._IsTypeConstructor(js_type)
+    if is_constructor:
+      c.Comment('@constructor', comment_prefix = ' * ', wrap_indent=4)
+    else:
+      c.Concat(self._GenerateTypedef(js_type.properties))
+
+    c.Append(' */')
+
+    var = 'var ' + js_type.simple_name
+    if is_constructor: var += ' = function() {}'
+    var += ';'
+    c.Append(var)
+
+    return c
+
+  def _GenerateTypedef(self, properties):
+    """Given an OrderedDict of properties, returns a Code containing a @typedef.
+    """
+    if not properties: return Code()
+
+    lines = []
+    lines.append('@typedef {{')
+    for field, prop in properties.items():
+      js_type = self._TypeToJsType(prop.type_)
+      if prop.optional:
+        js_type = '(%s|undefined)' % js_type
+      lines.append('  %s: %s,' % (field, js_type))
+
+    # Remove last trailing comma.
+    # TODO(devlin): This will be unneeded, if when
+    # https://github.com/google/closure-compiler/issues/796 is fixed.
+    lines[-1] = lines[-1][:-1]
+    lines.append('}}')
+    # TODO(tbreisacher): Add '@see <link to documentation>'.
+
+    c = Code()
+    c.Append('\n'.join([' * ' + line for line in lines]))
+    return c
+
+  def _GenerateFunctionJsDoc(self, function):
+    """Generates the documentation for a function as a Code.
+
+    Returns an empty code object if the object has no documentation.
+    """
+    c = Code()
+    c.Append('/**')
+
+    lines = []
+    if function.description:
+      for line in function.description.splitlines():
+        c.Comment(line, comment_prefix=' * ')
+
+    for param in function.params:
+      js_type = self._TypeToJsType(param.type_)
+      if param.optional:
+        js_type += '='
+      lines.append('@param {%s} %s %s' % (js_type,
+                                          param.name,
+                                          param.description or ''))
+
+    if function.callback:
+      lines.append('@param {%s} %s %s' % (
+          self._FunctionToJsFunction(function.callback),
+          function.callback.name,
+          function.callback.description or ''))
+
+    if function.returns:
+      lines.append('@return {%s} %s' % (self._TypeToJsType(function.returns),
+                                        function.returns.description or ''))
+
+    if function.deprecated:
+      lines.append('@deprecated %s' % function.deprecated)
+
+    for line in lines:
+      c.Comment(line, comment_prefix=' * ', wrap_indent=4);
+
+    c.Append(' */')
+    return c
+
+  def _FunctionToJsFunction(self, function):
+    """Converts a model.Function to a JS type (i.e., function([params])...)"""
+    params = ', '.join(
+        [self._TypeToJsType(param.type_) for param in function.params])
+    return_type = (
+        self._TypeToJsType(function.returns) if function.returns else 'void')
+    optional = '=' if function.optional else ''
+    return 'function(%s):%s%s' % (params, return_type, optional)
+
+  def _TypeToJsType(self, js_type):
+    """Converts a model.Type to a JS type (number, Array, etc.)"""
+    if js_type.property_type in (PropertyType.INTEGER, PropertyType.DOUBLE):
+      return 'number'
+    elif js_type.property_type is PropertyType.OBJECT:
+      return 'Object'
+    elif js_type.property_type is PropertyType.ARRAY:
+      return '!Array<%s>' % self._TypeToJsType(js_type.item_type)
+    elif js_type.property_type is PropertyType.REF:
+      ref_type = js_type.ref_type
+      # Enums are defined as chrome.fooAPI.MyEnum, but types are defined simply
+      # as MyType.
+      if self._namespace.types[ref_type].property_type is PropertyType.ENUM:
+        ref_type = '!chrome.%s.%s' % (self._namespace.name, ref_type)
+      return ref_type
+    elif js_type.property_type is PropertyType.CHOICES:
+      return '(%s)' % '|'.join(
+          [self._TypeToJsType(choice) for choice in js_type.choices])
+    elif js_type.property_type is PropertyType.FUNCTION:
+      return self._FunctionToJsFunction(js_type.function)
+    elif js_type.property_type is PropertyType.ANY:
+      return '*'
+    elif js_type.property_type.is_fundamental:
+      return js_type.property_type.name
+    else:
+      return '?' # TODO(tbreisacher): Make this more specific.
+
+  def _GenerateFunction(self, function):
+    """Generates the code representing a function, including its documentation.
+       For example:
+
+       /**
+        * @param {string} title The new title.
+        */
+       chrome.window.setTitle = function(title) {};
+    """
+    c = Code()
+    params = self._GenerateFunctionParams(function)
+    (c.Concat(self._GenerateFunctionJsDoc(function))
+      .Append('chrome.%s.%s = function(%s) {};' % (self._namespace.name,
+                                                   function.name,
+                                                   params))
+    )
+    return c
+
+  def _GenerateEvent(self, event):
+    """Generates the code representing an event.
+       For example:
+
+       /** @type {!ChromeEvent} */
+       chrome.bookmarks.onChildrenReordered;
+    """
+    c = Code()
+    (c.Append('/** @type {!ChromeEvent} */')
+      .Append('chrome.%s.%s;' % (self._namespace.name, event.name)))
+    return c
+
+  def _GenerateNamespaceObject(self):
+    """Generates the code creating namespace object.
+       For example:
+
+       /**
+        * @const
+        */
+       chrome.bookmarks = {};
+    """
+    c = Code()
+    (c.Append("""/**
+ * @const
+ */""")
+      .Append('chrome.%s = {};' % self._namespace.name))
+    return c
+
+  def _GenerateFunctionParams(self, function):
+    params = function.params[:]
+    if function.callback:
+      params.append(function.callback)
+    return ', '.join(param.name for param in params)
diff --git a/tools/json_schema_compiler/js_externs_generator_test.py b/tools/json_schema_compiler/js_externs_generator_test.py
new file mode 100755
index 0000000..2ca31695
--- /dev/null
+++ b/tools/json_schema_compiler/js_externs_generator_test.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+# 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.
+
+import idl_schema
+from js_externs_generator import JsExternsGenerator
+from datetime import datetime
+import model
+import unittest
+
+
+# The contents of a fake idl file.
+fake_idl = """
+// Copyright %s 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.
+
+// A totally fake API.
+namespace fakeApi {
+  enum Greek {
+    ALPHA,
+    BETA,
+    GAMMA,
+    DELTA
+  };
+
+  dictionary Bar {
+    long num;
+  };
+
+  dictionary Baz {
+    DOMString str;
+    long num;
+    boolean b;
+    Greek letter;
+    Greek? optionalLetter;
+    long[] arr;
+    Bar[]? optionalObjArr;
+    Greek[] enumArr;
+    any[] anythingGoes;
+    Bar obj;
+    long? maybe;
+    (DOMString or Greek or long[]) choice;
+  };
+
+  callback VoidCallback = void();
+
+  callback BazGreekCallback = void(Baz baz, Greek greek);
+
+  interface Functions {
+    // Does something exciting! And what's more, this is a multiline function
+    // comment! It goes onto multiple lines!
+    // |baz| : The baz to use.
+    static void doSomething(Baz baz, VoidCallback callback);
+
+    // |callback| : The callback which will most assuredly in all cases be
+    // called; that is, of course, iff such a callback was provided and is
+    // not at all null.
+    static void bazGreek(optional BazGreekCallback callback);
+
+    [deprecated="Use a new method."] static DOMString returnString();
+  };
+};
+"""
+
+# The output we expect from our fake idl file.
+expected_output = """// Copyright %s 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 Externs generated from namespace: fakeApi */
+
+/**
+ * @const
+ */
+chrome.fakeApi = {};
+
+/**
+ * @enum {string}
+ */
+chrome.fakeApi.Greek = {
+  ALPHA: 'ALPHA',
+  BETA: 'BETA',
+  GAMMA: 'GAMMA',
+  DELTA: 'DELTA',
+};
+
+/**
+ * @typedef {{
+ *   num: number
+ * }}
+ */
+var Bar;
+
+/**
+ * @typedef {{
+ *   str: string,
+ *   num: number,
+ *   b: boolean,
+ *   letter: !chrome.fakeApi.Greek,
+ *   optionalLetter: (!chrome.fakeApi.Greek|undefined),
+ *   arr: !Array<number>,
+ *   optionalObjArr: (!Array<Bar>|undefined),
+ *   enumArr: !Array<!chrome.fakeApi.Greek>,
+ *   anythingGoes: !Array<*>,
+ *   obj: Bar,
+ *   maybe: (number|undefined),
+ *   choice: (string|!chrome.fakeApi.Greek|!Array<number>)
+ * }}
+ */
+var Baz;
+
+/**
+ * Does something exciting! And what's more, this is a multiline function
+ * comment! It goes onto multiple lines!
+ * @param {Baz} baz The baz to use.
+ * @param {function():void} callback
+ */
+chrome.fakeApi.doSomething = function(baz, callback) {};
+
+/**
+ * @param {function(Baz, !chrome.fakeApi.Greek):void=} callback The callback
+ *     which will most assuredly in all cases be called; that is, of course, iff
+ *     such a callback was provided and is not at all null.
+ */
+chrome.fakeApi.bazGreek = function(callback) {};
+
+/**
+ * @return {string}
+ * @deprecated Use a new method.
+ */
+chrome.fakeApi.returnString = function() {};
+""" % datetime.now().year
+
+
+class JsExternGeneratorTest(unittest.TestCase):
+  def testBasic(self):
+    self.maxDiff = None # Lets us see the full diff when inequal.
+    filename = 'fake_api.idl'
+    api_def = idl_schema.Process(fake_idl, filename)
+    m = model.Model()
+    namespace = m.AddNamespace(api_def[0], filename)
+    self.assertMultiLineEqual(expected_output,
+                              JsExternsGenerator().Generate(namespace).Render())
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py
index 7745dcc..3f7b2fe 100644
--- a/tools/json_schema_compiler/model.py
+++ b/tools/json_schema_compiler/model.py
@@ -456,6 +456,8 @@
     _Enum.__init__(self, name)
     self.is_fundamental = is_fundamental
 
+  def __repr__(self):
+    return self.name
 
 class PropertyType(object):
   """Enum of different types of properties/parameters.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index dcf9937..8f7a6ab 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -2344,6 +2344,14 @@
   </summary>
 </histogram>
 
+<histogram name="CaptivePortal.RedirectTime" units="milliseconds">
+  <owner>alemate@chromium.org</owner>
+  <summary>
+    Number of milliseconds between start of request to gstatic.com/generate_204
+    and receipt of response with redirect to captive portal login page.
+  </summary>
+</histogram>
+
 <histogram name="CaptivePortal.Session.DetectionDuration" units="milliseconds">
   <owner>alemate@chromium.org</owner>
   <summary>
@@ -12715,14 +12723,28 @@
   </summary>
 </histogram>
 
+<histogram name="LevelDBEnv.IDB.IOError.BFE" enum="PlatformFileError">
+  <owner>cmumford@chromium.org</owner>
+  <summary>
+    Errors (base::File::Error) encountered by a single leveldb env method.
+  </summary>
+</histogram>
+
 <histogram name="LevelDBEnv.IDB.IOError.NewLogger" enum="OSAgnosticErrno">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use LevelDBEnv.IDB.IOError.BFE.NewLogger.
+  </obsolete>
   <summary>Errno of errors encountered in NewLogger.</summary>
 </histogram>
 
 <histogram name="LevelDBEnv.IDB.IOError.NewSequentialFile"
     enum="OSAgnosticErrno">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use
+    LevelDBEnv.IDB.IOError.BFE.NewSequentialFile.
+  </obsolete>
   <summary>Errno of errors encountered in NewSequentialFile.</summary>
 </histogram>
 
@@ -12739,12 +12761,20 @@
 <histogram name="LevelDBEnv.IDB.IOError.WritableFileAppend"
     enum="OSAgnosticErrno">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use
+    LevelDBEnv.IDB.IOError.BFE.WritableFileAppend.
+  </obsolete>
   <summary>Errno of errors encountered in WritableFileAppend.</summary>
 </histogram>
 
 <histogram name="LevelDBEnv.IDB.IOError.WritableFileFlush"
     enum="OSAgnosticErrno">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use
+    LevelDBEnv.IDB.IOError.BFE.WritableFileFlush.
+  </obsolete>
   <summary>Errno of errors encountered in WritableFileFlush.</summary>
 </histogram>
 
@@ -12815,16 +12845,32 @@
 
 <histogram name="LevelDBEnv.IOError." enum="PlatformFileError">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use LevelDBEnv.IOError.BFE.
+  </obsolete>
   <summary>PlatformFileErrors encountered by a single leveldb method.</summary>
 </histogram>
 
+<histogram name="LevelDBEnv.IOError.BFE" enum="PlatformFileError">
+  <owner>cmumford@chromium.org</owner>
+  <summary>
+    Errors (base::File::Error) encountered by a single leveldb method.
+  </summary>
+</histogram>
+
 <histogram name="LevelDBEnv.IOError.NewLogger" enum="OSAgnosticErrno">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use LevelDBEnv.IOError.BFE.NewLogger.
+  </obsolete>
   <summary>Errno of errors encountered in NewLogger.</summary>
 </histogram>
 
 <histogram name="LevelDBEnv.IOError.NewSequentialFile" enum="OSAgnosticErrno">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use LevelDBEnv.IOError.BFE.NewSequentialFile.
+  </obsolete>
   <summary>Errno of errors encountered in NewSequentialFile.</summary>
 </histogram>
 
@@ -12840,11 +12886,17 @@
 
 <histogram name="LevelDBEnv.IOError.WritableFileAppend" enum="OSAgnosticErrno">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use LevelDBEnv.IOError.BFE.WritableFileAppend.
+  </obsolete>
   <summary>Errno of errors encountered in WritableFileAppend.</summary>
 </histogram>
 
 <histogram name="LevelDBEnv.IOError.WritableFileFlush" enum="OSAgnosticErrno">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use LevelDBEnv.IOError.BFE.WritableFileFlush.
+  </obsolete>
   <summary>Errno of errors encountered in WritableFileFlush.</summary>
 </histogram>
 
@@ -18122,6 +18174,9 @@
 </histogram>
 
 <histogram name="Net.HttpConnectionLatency" units="milliseconds">
+  <obsolete>
+    Deprecated as of 03/2015.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Time between the HttpNetworkTransaction requesting a connection and the time
@@ -18299,6 +18354,9 @@
 </histogram>
 
 <histogram name="Net.HttpSocketType" enum="HttpSocketType">
+  <obsolete>
+    Deprecated as of 03/2015.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The counts of the type of sockets (all HTTP sockets, regardless of any proxy
@@ -19862,11 +19920,17 @@
 </histogram>
 
 <histogram name="Net.SocketIdleTimeBeforeNextUse_ReusedSocket">
+  <obsolete>
+    Deprecated as of 03/2015.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>The time an already used socket sat idle before being used.</summary>
 </histogram>
 
 <histogram name="Net.SocketIdleTimeBeforeNextUse_UnusedSocket">
+  <obsolete>
+    Deprecated as of 03/2015.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The time an unused socket (all HTTP sockets, regardless of any proxy used)
@@ -19891,6 +19955,9 @@
 </histogram>
 
 <histogram name="Net.SocketInitErrorCodes" enum="NetErrorCodes">
+  <obsolete>
+    Deprecated as of 03/2015.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Net error codes that socket initializations end with, including net::OK and
@@ -19910,6 +19977,9 @@
 </histogram>
 
 <histogram name="Net.SocketRequestTime">
+  <obsolete>
+    Deprecated as of 03/2015.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     Time in milliseconds from initial RequestSocket() call until successfully
@@ -20014,6 +20084,9 @@
 </histogram>
 
 <histogram name="Net.SocketType" enum="HttpSocketType">
+  <obsolete>
+    Deprecated as of 03/2015.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
     The counts of the type of sockets returned by the socket pools.
@@ -20112,6 +20185,9 @@
 </histogram>
 
 <histogram name="Net.SpdyHpackEncodedCharacterFrequency" units="ASCII codes">
+  <obsolete>
+    Obsolete as HTTP/2 standard is finalized.
+  </obsolete>
   <owner>bnc@chromium.org</owner>
   <summary>
     Frequencies of characters observed in request and response headers.
@@ -24203,6 +24279,9 @@
 </histogram>
 
 <histogram name="OSX.CatSixtyFour" enum="CatSixtyFour">
+  <obsolete>
+    Obselete as of Chrome 43. See OmahaProxy for more relevant statistics.
+  </obsolete>
   <owner>mark@chromium.org</owner>
   <summary>The cat's flavor and how many bits there are in it.</summary>
 </histogram>
@@ -27496,6 +27575,13 @@
   <summary>The result from an attempt to load a PPAPI plugin.</summary>
 </histogram>
 
+<histogram name="Plugin.PpapiSyncIPCTime" units="ms">
+  <owner>gab@chromium.org</owner>
+  <summary>
+    The time it took to complete a synchronous IPC made from the PPAPI process.
+  </summary>
+</histogram>
+
 <histogram name="Power.BacklightLevelOnAC" units="%">
   <owner>derat@chromium.org</owner>
   <summary>
@@ -29301,10 +29387,11 @@
 
 <histogram name="Profile.DeleteProfileAction" enum="ProfileDeleteAction">
   <owner>mlerman@chromium.org</owner>
+  <owner>anthonyvd@chromium.org</owner>
   <summary>
-    This histogram tracks which UI screen was used to delete a profile. This
-    does not track when the profile is actually deleted, which is an
-    asycnhronous process that happens later.
+    This histogram tracks which UI screen was used to initiate and confirm the
+    deletion of a profile. This does not track when the profile is actually
+    deleted, which is an asynchronous process that happens later.
   </summary>
 </histogram>
 
@@ -31930,6 +32017,10 @@
 </histogram>
 
 <histogram name="SB2.ExtendedReportingIsEnabled" enum="BooleanEnabled">
+  <obsolete>
+    Deprecated 03/2015. Replaced by
+    SecurityInterstitialInteraction::EXTENDED_REPORTING_IS_ENABLED.
+  </obsolete>
   <owner>felt@chromium.org</owner>
   <summary>
     Whether the user has Safe Browsing extended reporting enabled at the time a
@@ -32394,6 +32485,10 @@
 </histogram>
 
 <histogram name="SB2.SetExtendedReportingEnabled" enum="BooleanEnabled">
+  <obsolete>
+    Deprecated 03/2015. Replaced by
+    SecurityInterstitialInteraction::SET_EXTENDED_REPORTING_ENABLED.
+  </obsolete>
   <owner>felt@chromium.org</owner>
   <summary>
     Tracks changes to the Safe Browsing extended reporting opt-in which is shown
@@ -33805,6 +33900,14 @@
   <summary>Execution time of ServiceWorkerGlobalScope.onactivate.</summary>
 </histogram>
 
+<histogram name="ServiceWorker.Database.DestroyDatabaseResult"
+    enum="ServiceWorkerDatabaseStatus">
+  <owner>nhiroki@chromium.org</owner>
+  <summary>
+    Records result of destroy database operations in ServiceWorkerDatabase.
+  </summary>
+</histogram>
+
 <histogram name="ServiceWorker.Database.OpenResult"
     enum="ServiceWorkerDatabaseStatus">
   <owner>nhiroki@chromium.org</owner>
@@ -33919,6 +34022,14 @@
   </summary>
 </histogram>
 
+<histogram name="ServiceWorker.Storage.DeleteAndStartOverResult"
+    enum="ServiceWorkerDeleteAndStartOverResult">
+  <owner>nhiroki@chromium.org</owner>
+  <summary>
+    Records result of storage recovery operations in ServiceWorkerStorage.
+  </summary>
+</histogram>
+
 <histogram name="ServiceWorkerCache.Cache" units="milliseconds">
   <owner>dmurph@chromium.org</owner>
   <summary>
@@ -42742,6 +42853,15 @@
   </summary>
 </histogram>
 
+<histogram name="WebCore.IndexedDB.LevelDBOpenErrors.BFE"
+    enum="PlatformFileError">
+  <owner>cmumford@chromium.org</owner>
+  <summary>
+    Errors (base::File::Error) encountered by a single LevelDBEnv method when
+    opening an IndexedDB instance.
+  </summary>
+</histogram>
+
 <histogram name="WebCore.IndexedDB.LevelDBOpenErrors.Corruption"
     enum="LevelDBCorruptionTypes">
   <owner>dgrogan@chromium.org</owner>
@@ -42759,6 +42879,9 @@
 <histogram name="WebCore.IndexedDB.LevelDBOpenErrors.Errno"
     enum="OSAgnosticErrno">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use WebCore.IndexedDB.LevelDBOpenErrors.BFE.
+  </obsolete>
   <summary>
     Errno errors encountered by a single LevelDBEnv method when opening an
     IndexedDB instance.
@@ -42768,6 +42891,9 @@
 <histogram name="WebCore.IndexedDB.LevelDBOpenErrors.PFE"
     enum="PlatformFileError">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use WebCore.IndexedDB.LevelDBOpenErrors.BFE.
+  </obsolete>
   <summary>
     PlatformFileErrors encountered by a single LevelDBEnv method when opening an
     IndexedDB instance.
@@ -42781,6 +42907,15 @@
   </summary>
 </histogram>
 
+<histogram name="WebCore.IndexedDB.LevelDBReadErrors.BFE"
+    enum="PlatformFileError">
+  <owner>cmumford@chromium.org</owner>
+  <summary>
+    Errors (base::File::Error) encountered by a single LevelDBEnv method when
+    reading from an IndexedDB instance.
+  </summary>
+</histogram>
+
 <histogram name="WebCore.IndexedDB.LevelDBReadErrors.Corruption"
     enum="LevelDBCorruptionTypes">
   <owner>dgrogan@chromium.org</owner>
@@ -42798,6 +42933,9 @@
 <histogram name="WebCore.IndexedDB.LevelDBReadErrors.Errno"
     enum="OSAgnosticErrno">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use WebCore.IndexedDB.LevelDBReadErrors.BFE.
+  </obsolete>
   <summary>
     Errno errors encountered by a single LevelDBEnv method when reading an
     IndexedDB instance.
@@ -42807,6 +42945,9 @@
 <histogram name="WebCore.IndexedDB.LevelDBReadErrors.PFE"
     enum="PlatformFileError">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use WebCore.IndexedDB.LevelDBReadErrors.BFE.
+  </obsolete>
   <summary>
     PlatformFileErrors encountered by a single LevelDBEnv method when opening an
     IndexedDB instance.
@@ -42820,6 +42961,15 @@
   </summary>
 </histogram>
 
+<histogram name="WebCore.IndexedDB.LevelDBWriteErrors.BFE"
+    enum="PlatformFileError">
+  <owner>cmumford@chromium.org</owner>
+  <summary>
+    Errors (base::File::Error) encountered by a single LevelDBEnv method when
+    writing to an IndexedDB instance.
+  </summary>
+</histogram>
+
 <histogram name="WebCore.IndexedDB.LevelDBWriteErrors.Corruption"
     enum="LevelDBCorruptionTypes">
   <owner>dgrogan@chromium.org</owner>
@@ -42837,6 +42987,9 @@
 <histogram name="WebCore.IndexedDB.LevelDBWriteErrors.Errno"
     enum="OSAgnosticErrno">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use WebCore.IndexedDB.LevelDBWriteErrors.BFE.
+  </obsolete>
   <summary>
     Errno errors encountered by a single LevelDBEnv method when writing to an
     IndexedDB instance.
@@ -42846,6 +42999,9 @@
 <histogram name="WebCore.IndexedDB.LevelDBWriteErrors.PFE"
     enum="PlatformFileError">
   <owner>dgrogan@chromium.org</owner>
+  <obsolete>
+    Deprecated 2015-05. As of M43 use WebCore.IndexedDB.LevelDBWriteErrors.BFE.
+  </obsolete>
   <summary>
     PlatformFileErrors encountered by a single LevelDBEnv method when writing to
     an IndexedDB instance.
@@ -45026,8 +45182,22 @@
   <int value="11" label="Submitted with server suggestion filled (once)"/>
   <int value="12"
       label="Submitted with masked server card suggestion filled (once)"/>
-  <int value="13" label="Masked server card suggestion selected"/>
-  <int value="14" label="Masked server card suggestion selected (once)"/>
+  <int value="13" label="Masked server card suggestion selected">
+    The user selected a masked server card that triggered an unmask attempt.
+  </int>
+  <int value="14" label="Masked server card suggestion selected (once)">
+    The user selected a masked server card that triggered an unmask attempt at
+    least once.
+  </int>
+  <int value="15"
+      label="About to be submitted with no suggestion filled (once)"/>
+  <int value="16"
+      label="About to be submitted with local suggestion filled (once)"/>
+  <int value="17"
+      label="About to be submitted with server suggestion filled (once)"/>
+  <int value="18"
+      label="About to be submitted with masked server card suggestion filled
+             (once)"/>
 </enum>
 
 <enum name="AutofillGetRealPanResult" type="int">
@@ -49373,6 +49543,13 @@
   <int value="982" label="DEVELOPERPRIVATE_DELETEEXTENSIONERRORS"/>
   <int value="983" label="FILEMANAGERPRIVATE_ISPIEXLOADERENABLED"/>
   <int value="984" label="HOTWORDPRIVATE_SPEAKERMODELEXISTSRESULT"/>
+  <int value="985" label="SETTINGSPRIVATE_SETBOOLEANPREF"/>
+  <int value="986" label="SETTINGSPRIVATE_SETNUMERICPREF"/>
+  <int value="987" label="SETTINGSPRIVATE_SETSTRINGPREF"/>
+  <int value="988" label="SETTINGSPRIVATE_SETURLPREF"/>
+  <int value="989" label="SETTINGSPRIVATE_GETALLPREFS"/>
+  <int value="990" label="SETTINGSPRIVATE_GETPREF"/>
+  <int value="991" label="NETWORKINGPRIVATE_FORGETNETWORK"/>
 </enum>
 
 <enum name="ExtensionInstallCause" type="int">
@@ -49511,12 +49688,12 @@
   <int value="17" label="kTtsEngine"/>
   <int value="18" label="kContentSettings"/>
   <int value="19" label="kPrivacy"/>
-  <int value="20" label="kManagedMode"/>
+  <int value="20" label="kSupervisedUser"/>
   <int value="21" label="kInput"/>
   <int value="22" label="kAudioCapture"/>
   <int value="23" label="kVideoCapture"/>
   <int value="24" label="kDownloads"/>
-  <int value="25" label="kFileSystemWrite"/>
+  <int value="25" label="kDeleted_FileSystemWrite"/>
   <int value="26" label="kMediaGalleriesAllGalleriesRead"/>
   <int value="27" label="kSerial"/>
   <int value="28" label="kSocketAnyHost"/>
@@ -49562,7 +49739,15 @@
   <int value="68" label="kDocumentScan"/>
   <int value="69" label="kNetworkingConfig"/>
   <int value="70" label="kPlatformKeys"/>
-  <int value="71" label="kMdns"/>
+  <int value="71" label="kMDns"/>
+  <int value="72" label="kVpnProvider"/>
+  <int value="73" label="kHosts1ReadOnly"/>
+  <int value="74" label="kHosts2ReadOnly"/>
+  <int value="75" label="kHosts3ReadOnly"/>
+  <int value="76" label="kHosts4OrMoreReadOnly"/>
+  <int value="77" label="kHostsAllReadOnly"/>
+  <int value="78" label="kInterceptAllKeys"/>
+  <int value="79" label="kSettingsPrivate"/>
 </enum>
 
 <enum name="ExtensionServiceVerifyAllSuccess" type="int">
@@ -50430,6 +50615,7 @@
   <int value="736" label="CryptoAlgorithmEcdh"/>
   <int value="737" label="CryptoAlgorithmHkdf"/>
   <int value="738" label="CryptoAlgorithmPbkdf2"/>
+  <int value="739" label="DocumentSetDomain"/>
 </enum>
 
 <enum name="FFmpegCodecs" type="int">
@@ -53121,6 +53307,7 @@
   <int value="-1832575380" label="show-saved-copy"/>
   <int value="-1821058653" label="enable-delay-agnostic-aec"/>
   <int value="-1767470652" label="out-of-process-pdf"/>
+  <int value="-1751928267" label="disable-icon-ntp"/>
   <int value="-1746767834" label="ssl-interstitial-v2-gray"/>
   <int value="-1740519217" label="disable-software-rasterizer"/>
   <int value="-1735643253" label="enable-display-list-2d-canvas"/>
@@ -53171,6 +53358,7 @@
   <int value="-1285021473" label="save-page-as-mhtml"/>
   <int value="-1268836676" label="disable-out-of-process-pdf"/>
   <int value="-1267958145" label="disable-pdf-material-ui"/>
+  <int value="-1251411236" label="disable-new-md-input-view"/>
   <int value="-1241747717" label="enable-android-password-link"/>
   <int value="-1218608640" label="disable-offline-load-stale-cache"/>
   <int value="-1216837777" label="clear-data-reduction-proxy-data-savings"/>
@@ -53465,11 +53653,13 @@
   <int value="1900529524" label="disable-touch-drag-drop"/>
   <int value="1906942630" label="enable-easy-unlock"/>
   <int value="1930901873" label="disable-sync-app-list"/>
+  <int value="1939413645" label="enable-invalid-cert-collection"/>
   <int value="1944156526" label="sync-url"/>
   <int value="1961425320" label="force-qtkit"/>
   <int value="1966730288" label="disable-threaded-compositing"/>
   <int value="1969604362" label="enable-pinch-virtual-viewport"/>
   <int value="1980011075" label="debug-packed-apps"/>
+  <int value="1993258379" label="enable-icon-ntp"/>
   <int value="2000091128" label="enable-touch-hover"/>
   <int value="2004829262" label="enable-webgl-draft-extensions"/>
   <int value="2037756154" label="enable-impl-side-painting"/>
@@ -57537,8 +57727,21 @@
 </enum>
 
 <enum name="ProfileDeleteAction" type="int">
-  <int value="0" label="Settings Page"/>
-  <int value="1" label="User Manager"/>
+  <int value="0" label="Settings Page">
+    The user confirmed deletion of a profile from the settings page.
+  </int>
+  <int value="1" label="User Manager">
+    The user confirmed deletion of a profile from the User Manager.
+  </int>
+  <int value="2" label="User Manager Warning Shown">
+    The user clicked 'Remove this person' in the user manager and was shown the
+    profile deletion warning. No profile deletion action has been started yet.
+  </int>
+  <int value="3" label="Settings Page Warning Shown">
+    The user clicked 'Remove...' or the 'X' in the settings page and was shown
+    the profile deletion warning. No profile deletion action has been started
+    yet.
+  </int>
 </enum>
 
 <enum name="ProfileDesktopMenu" type="int">
@@ -59128,6 +59331,9 @@
   <int value="4" label="SHOW_LEARN_MORE"/>
   <int value="5" label="RELOAD"/>
   <int value="6" label="OPEN_TIME_SETTINGS"/>
+  <int value="7" label="SET_EXTENDED_REPORTING_ENABLED"/>
+  <int value="8" label="SET_EXTENDED_REPORTING_DISABLED"/>
+  <int value="9" label="EXTENDED_REPORTING_IS_ENABLED"/>
 </enum>
 
 <enum name="ServiceProcessEventType" type="int">
@@ -59186,6 +59392,12 @@
   <int value="4" label="Operation Error"/>
 </enum>
 
+<enum name="ServiceWorkerDeleteAndStartOverResult" type="int">
+  <int value="0" label="OK"/>
+  <int value="1" label="Failed to delete ServiceWorkerDatabase"/>
+  <int value="2" label="Failed to delete ServiceWorkerDiskCache"/>
+</enum>
+
 <enum name="ServiceWorkerReadResponseResult" type="int">
   <int value="0" label="OK"/>
   <int value="1" label="Read headers error"/>
@@ -63294,6 +63506,38 @@
   <affected-histogram name="Renderer4.StartToFinish"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="LevelDBBFEMethods" separator=".">
+  <owner>cmumford@chromium.org</owner>
+  <suffix name="CreateDir" label="ChromiumEnv::CreateDir"/>
+  <suffix name="DeleteDir" label="ChromiumEnv::DeleteDir"/>
+  <suffix name="DeleteFile" label="ChromiumEnv::DeleteFile"/>
+  <suffix name="GetChildren" label="ChromiumEnv::GetChildren"/>
+  <suffix name="GetFileSize" label="ChromiumEnv::GetFileSize"/>
+  <suffix name="GetTestDirectory" label="ChromiumEnv::GetTestDirectory"/>
+  <suffix name="LockFile" label="ChromiumEnv::LockFile"/>
+  <suffix name="NewAppendableFile" label="ChromiumEnv::NewAppendableFile"/>
+  <suffix name="NewLogger" label="ChromiumEnv::NewLogger"/>
+  <suffix name="NewRandomAccessFile" label="ChromiumEnv::NewRandomAccessFile"/>
+  <suffix name="NewSequentialFile" label="ChromiumEnv::NewSequentialFile"/>
+  <suffix name="NewWritableFile" label="ChromiumEnv::NewWritableFile"/>
+  <suffix name="RandomAccessFileRead" label="ChromiumRandomAccessFile::Read"/>
+  <suffix name="RenameFile" label="ChromiumEnv::RenameFile"/>
+  <suffix name="SequentialFileRead" label="ChromiumSequentialFile::Read"/>
+  <suffix name="SequentialFileSkip" label="ChromiumSequentialFile::Skip"/>
+  <suffix name="UnlockFile" label="ChromiumEnv::UnlockFile"/>
+  <suffix name="WritableFileAppend" label="ChromiumWritableFile::Append"/>
+  <suffix name="WritableFileClose" label="ChromiumWritableFile::Close"/>
+  <suffix name="WritableFileFlush" label="ChromiumWritableFile::Flush"/>
+  <suffix name="WritableFileSync" label="ChromiumWritableFile::Sync"/>
+  <suffix name="WritableFileSyncParent"
+      label="ChromiumWritableFile::SyncParent"/>
+  <affected-histogram name="LevelDBEnv.IDB.IOError.BFE"/>
+  <affected-histogram name="LevelDBEnv.IOError.BFE"/>
+  <affected-histogram name="WebCore.IndexedDB.LevelDBOpenErrors.BFE"/>
+  <affected-histogram name="WebCore.IndexedDB.LevelDBReadErrors.BFE"/>
+  <affected-histogram name="WebCore.IndexedDB.LevelDBWriteErrors.BFE"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="LevelDBEnvBackupRestore" separator="">
   <suffix name="Backup" label="Backing up an ldb file."/>
   <suffix name="Restore" label="Restoring an ldb file."/>
@@ -64767,7 +65011,8 @@
 
 <histogram_suffixes name="NewTabPageProviders" separator=".">
   <suffix name="client" label="Suggestions coming from the client."/>
-  <suffix name="client0" label="Suggestions coming from the client source 0."/>
+  <suffix name="client0"
+      label="deprecated - Suggestions coming from the client source 0."/>
 <!-- Server providers 1 through 8 have never been logged. -->
 
   <suffix name="server" label="Suggestions coming from the server."/>
@@ -65901,6 +66146,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="SocketType">
+  <obsolete>
+    Deprecated as of 03/2015.
+  </obsolete>
   <suffix name="HTTPProxy" label="HTTP proxy socket"/>
   <suffix name="SOCK" label="SOCKS socket"/>
   <suffix name="SSL" label="(Obsolete, SSL socket)"/>
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml
index 99d3c71..54743a68 100644
--- a/tools/metrics/rappor/rappor.xml
+++ b/tools/metrics/rappor/rappor.xml
@@ -117,6 +117,91 @@
   </summary>
 </rappor-metric>
 
+<rappor-metric name="ContentSettings.PermissionActions_Geolocation.Denied.Url"
+    type="ETLD_PLUS_ONE">
+  <owner>miguelg@chromium.org</owner>
+  <summary>
+    The domain for which a Geolocation permission prompt was Denied.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Geolocation.Dismissed.Url"
+    type="ETLD_PLUS_ONE">
+  <owner>miguelg@chromium.org</owner>
+  <summary>
+    The domain for which a Geolocation permission prompt was Dismissed.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="ContentSettings.PermissionActions_Geolocation.Granted.Url"
+    type="ETLD_PLUS_ONE">
+  <owner>miguelg@chromium.org</owner>
+  <summary>
+    The domain for which a Geolocation permission prompt was accepted.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="ContentSettings.PermissionActions_Geolocation.Ignored.Url"
+    type="ETLD_PLUS_ONE">
+  <owner>miguelg@chromium.org</owner>
+  <summary>
+    The domain for which a Geolocation permission prompt was Ignored.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Notifications.Denied.Url"
+    type="ETLD_PLUS_ONE">
+  <owner>miguelg@chromium.org</owner>
+  <summary>
+    The domain for which a Notification permission prompt was Denied.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Notifications.Dismissed.Url"
+    type="ETLD_PLUS_ONE">
+  <owner>miguelg@chromium.org</owner>
+  <summary>
+    The domain for which a Notification permission prompt was Dismissed.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Notifications.Granted.Url"
+    type="ETLD_PLUS_ONE">
+  <owner>miguelg@chromium.org</owner>
+  <summary>
+    The domain for which a Notification permission prompt was accepted.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="ContentSettings.PermissionActions_Notifications.Ignored.Url"
+    type="ETLD_PLUS_ONE">
+  <owner>miguelg@chromium.org</owner>
+  <summary>
+    The domain for which a Notification permission prompt was Ignored.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="ContentSettings.PermissionRequested.Geolocation.Url"
+    type="ETLD_PLUS_ONE">
+  <owner>miguelg@chromium.org</owner>
+  <summary>
+    The domain that issues a Geolocation permission prompt.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="ContentSettings.PermissionRequested.Notifications.Url"
+    type="ETLD_PLUS_ONE">
+  <owner>miguelg@chromium.org</owner>
+  <summary>
+    The domain that issues a Notification permission prompt.
+  </summary>
+</rappor-metric>
+
 <rappor-metric name="Extensions.PossibleAdInjection2" type="ETLD_PLUS_ONE">
   <owner>rdevlin.cronin@chromium.org</owner>
   <summary>
diff --git a/tools/perf/benchmarks/benchmark_unittest.py b/tools/perf/benchmarks/benchmark_unittest.py
index 1b6cc10..6d900ce 100644
--- a/tools/perf/benchmarks/benchmark_unittest.py
+++ b/tools/perf/benchmarks/benchmark_unittest.py
@@ -4,10 +4,10 @@
 
 """For all the benchmarks that set options, test that the options are valid."""
 
+from collections import defaultdict
 import logging
 import os
 import unittest
-from collections import defaultdict
 
 from telemetry import benchmark as benchmark_module
 from telemetry.core import browser_options
diff --git a/tools/perf/benchmarks/blink_perf.py b/tools/perf/benchmarks/blink_perf.py
index ee05bd04..dfb1653 100644
--- a/tools/perf/benchmarks/blink_perf.py
+++ b/tools/perf/benchmarks/blink_perf.py
@@ -5,8 +5,8 @@
 import os
 
 from telemetry import benchmark
-from telemetry import page as page_module
 from telemetry.core import util
+from telemetry import page as page_module
 from telemetry.page import page_set
 from telemetry.page import page_test
 from telemetry.value import list_of_scalar_values
diff --git a/tools/perf/benchmarks/dom_perf.py b/tools/perf/benchmarks/dom_perf.py
index 05ce7fb2..b68620ac 100644
--- a/tools/perf/benchmarks/dom_perf.py
+++ b/tools/perf/benchmarks/dom_perf.py
@@ -7,8 +7,8 @@
 import os
 
 from telemetry import benchmark
-from telemetry import page as page_module
 from telemetry.core import util
+from telemetry import page as page_module
 from telemetry.page import page_set
 from telemetry.page import page_test
 from telemetry.value import merge_values
diff --git a/tools/perf/benchmarks/dromaeo.py b/tools/perf/benchmarks/dromaeo.py
index c8bfda4e..b0f7d60c 100644
--- a/tools/perf/benchmarks/dromaeo.py
+++ b/tools/perf/benchmarks/dromaeo.py
@@ -5,13 +5,14 @@
 import math
 import os
 
-from metrics import power
 from telemetry import benchmark
 from telemetry import page as page_module
 from telemetry.page import page_set
 from telemetry.page import page_test
 from telemetry.value import scalar
 
+from metrics import power
+
 
 class _DromaeoMeasurement(page_test.PageTest):
   def __init__(self):
diff --git a/tools/perf/benchmarks/gpu_times.py b/tools/perf/benchmarks/gpu_times.py
index 011e6f1..8ecd9ea9 100644
--- a/tools/perf/benchmarks/gpu_times.py
+++ b/tools/perf/benchmarks/gpu_times.py
@@ -1,12 +1,13 @@
 # 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.
-from benchmarks import silk_flags
-import page_sets
 from telemetry import benchmark
 from telemetry.core.platform import tracing_category_filter
-from telemetry.web_perf import timeline_based_measurement
 from telemetry.web_perf.metrics import gpu_timeline
+from telemetry.web_perf import timeline_based_measurement
+
+from benchmarks import silk_flags
+import page_sets
 
 TOPLEVEL_GL_CATEGORY = 'gpu_toplevel'
 TOPLEVEL_CATEGORIES = ['disabled-by-default-gpu.device',
diff --git a/tools/perf/benchmarks/inbox_benchmark.py b/tools/perf/benchmarks/inbox_benchmark.py
index 02300e5..c25382fb 100644
--- a/tools/perf/benchmarks/inbox_benchmark.py
+++ b/tools/perf/benchmarks/inbox_benchmark.py
@@ -2,10 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from page_sets import inbox
 from telemetry import benchmark
 from telemetry.web_perf import timeline_based_measurement
 
+from page_sets import inbox
+
+
 @benchmark.Disabled  # http://crbug.com/452257
 class Inbox(benchmark.Benchmark):
   """Runs the timeline based measurement against inbox pageset."""
diff --git a/tools/perf/benchmarks/indexeddb_perf.py b/tools/perf/benchmarks/indexeddb_perf.py
index d0e5ed2..65a8710f 100644
--- a/tools/perf/benchmarks/indexeddb_perf.py
+++ b/tools/perf/benchmarks/indexeddb_perf.py
@@ -22,15 +22,17 @@
 import json
 import os
 
-from metrics import memory
-from metrics import power
 from telemetry import benchmark
-from telemetry import page as page_module
 from telemetry.core import util
+from telemetry import page as page_module
 from telemetry.page import page_set
 from telemetry.page import page_test
 from telemetry.value import scalar
 
+from metrics import memory
+from metrics import power
+
+
 class _IndexedDbMeasurement(page_test.PageTest):
   def __init__(self):
     super(_IndexedDbMeasurement, self).__init__()
diff --git a/tools/perf/benchmarks/kraken.py b/tools/perf/benchmarks/kraken.py
index 11b3c5b..724dcf87 100644
--- a/tools/perf/benchmarks/kraken.py
+++ b/tools/perf/benchmarks/kraken.py
@@ -6,7 +6,6 @@
 
 import os
 
-from metrics import power
 from telemetry import benchmark
 from telemetry import page as page_module
 from telemetry.page import page_set
@@ -14,6 +13,9 @@
 from telemetry.value import list_of_scalar_values
 from telemetry.value import scalar
 
+from metrics import power
+
+
 DESCRIPTIONS = {
     'ai-astar':
         'This benchmark uses the [A* search algorithm]'
diff --git a/tools/perf/benchmarks/media.py b/tools/perf/benchmarks/media.py
index 6a4990f..eff3345 100644
--- a/tools/perf/benchmarks/media.py
+++ b/tools/perf/benchmarks/media.py
@@ -2,13 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from measurements import media
-import page_sets
 from telemetry import benchmark
 from telemetry.page import page_test
 from telemetry.value import list_of_scalar_values
 from telemetry.value import scalar
 
+from measurements import media
+import page_sets
+
 
 class _MSEMeasurement(page_test.PageTest):
   def __init__(self):
diff --git a/tools/perf/benchmarks/memory.py b/tools/perf/benchmarks/memory.py
index a41d700..b5eb386 100644
--- a/tools/perf/benchmarks/memory.py
+++ b/tools/perf/benchmarks/memory.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry import benchmark
+
 from measurements import memory
 import page_sets
-from telemetry import benchmark
 
 
 @benchmark.Enabled('android')
diff --git a/tools/perf/benchmarks/octane.py b/tools/perf/benchmarks/octane.py
index a3d1d4b1..657d52d 100644
--- a/tools/perf/benchmarks/octane.py
+++ b/tools/perf/benchmarks/octane.py
@@ -13,7 +13,6 @@
 
 import os
 
-from metrics import power
 from telemetry import benchmark
 from telemetry import page as page_module
 from telemetry.page import page_set
@@ -21,6 +20,8 @@
 from telemetry.util import statistics
 from telemetry.value import scalar
 
+from metrics import power
+
 _GB = 1024 * 1024 * 1024
 
 DESCRIPTIONS = {
diff --git a/tools/perf/benchmarks/oilpan_gc_times.py b/tools/perf/benchmarks/oilpan_gc_times.py
index 88ea4897..6f55e1e 100644
--- a/tools/perf/benchmarks/oilpan_gc_times.py
+++ b/tools/perf/benchmarks/oilpan_gc_times.py
@@ -4,14 +4,15 @@
 
 import os
 
-import page_sets
-from benchmarks import blink_perf
-from benchmarks import silk_flags
-from measurements import oilpan_gc_times
 from telemetry import benchmark
 from telemetry.core import util
 from telemetry import page
 
+from benchmarks import blink_perf
+from benchmarks import silk_flags
+from measurements import oilpan_gc_times
+import page_sets
+
 
 class OilpanGCTimesBlinkPerfAnimation(benchmark.Benchmark):
   tag = 'blink_perf_animation'
diff --git a/tools/perf/benchmarks/page_cycler.py b/tools/perf/benchmarks/page_cycler.py
index 37e5c31..be80c95 100644
--- a/tools/perf/benchmarks/page_cycler.py
+++ b/tools/perf/benchmarks/page_cycler.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry import benchmark
+
 from measurements import page_cycler
 import page_sets
-from telemetry import benchmark
 
 
 class _PageCycler(benchmark.Benchmark):
@@ -70,7 +71,6 @@
     return 'page_cycler.intl_es_fr_pt-BR'
 
 
-@benchmark.Disabled('win', 'mac', 'linux')  # crbug.com/470071
 class PageCyclerIntlHiRu(_PageCycler):
   """Page load time benchmark for a variety of pages in Hindi and Russian.
 
@@ -199,8 +199,7 @@
 
 
 # crbug.com/273986: This test is flakey on Windows.
-# crbug.com/470071: This test is failing on N10
-@benchmark.Disabled('win', 'android')
+@benchmark.Disabled('win')
 class PageCyclerTypical25(_PageCycler):
   """Page load time benchmark for a 25 typical web pages.
 
diff --git a/tools/perf/benchmarks/polymer_load.py b/tools/perf/benchmarks/polymer_load.py
index 66305d7..17caf5a 100644
--- a/tools/perf/benchmarks/polymer_load.py
+++ b/tools/perf/benchmarks/polymer_load.py
@@ -2,14 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from measurements import polymer_load
-import page_sets
 from telemetry import benchmark
 
+from measurements import polymer_load
+import page_sets
 
-# Failing on N4, crbug.com/470071
-#@benchmark.Enabled('android')
-@benchmark.Disabled
+
+@benchmark.Enabled('android')
 class PolymerLoadPica(benchmark.Benchmark):
   """Measures time to polymer-ready for Pica (News Reader)."""
   test = polymer_load.PolymerLoadMeasurement
diff --git a/tools/perf/benchmarks/power.py b/tools/perf/benchmarks/power.py
index d697f1b..9cbda538 100644
--- a/tools/perf/benchmarks/power.py
+++ b/tools/perf/benchmarks/power.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry import benchmark
+
 from measurements import power
 import page_sets
-from telemetry import benchmark
 
 
 @benchmark.Enabled('android')
diff --git a/tools/perf/benchmarks/rasterize_and_record_micro.py b/tools/perf/benchmarks/rasterize_and_record_micro.py
index 45af267..3b11a17d 100644
--- a/tools/perf/benchmarks/rasterize_and_record_micro.py
+++ b/tools/perf/benchmarks/rasterize_and_record_micro.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry import benchmark
+
 from measurements import rasterize_and_record_micro
 import page_sets
-from telemetry import benchmark
 
 
 class _RasterizeAndRecordMicro(benchmark.Benchmark):
diff --git a/tools/perf/benchmarks/repaint.py b/tools/perf/benchmarks/repaint.py
index b39a623..cce589d 100644
--- a/tools/perf/benchmarks/repaint.py
+++ b/tools/perf/benchmarks/repaint.py
@@ -2,10 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry import benchmark
+
 from benchmarks import silk_flags
 from measurements import repaint as repaint_measurement
 import page_sets
-from telemetry import benchmark
 
 
 class _Repaint(benchmark.Benchmark):
diff --git a/tools/perf/benchmarks/robohornet_pro.py b/tools/perf/benchmarks/robohornet_pro.py
index 119cb951..f299089c 100644
--- a/tools/perf/benchmarks/robohornet_pro.py
+++ b/tools/perf/benchmarks/robohornet_pro.py
@@ -6,13 +6,14 @@
 
 import os
 
-from metrics import power
 from telemetry import benchmark
 from telemetry import page as page_module
 from telemetry.page import page_set
 from telemetry.page import page_test
 from telemetry.value import scalar
 
+from metrics import power
+
 
 class _RobohornetProMeasurement(page_test.PageTest):
   def __init__(self):
diff --git a/tools/perf/benchmarks/scheduler.py b/tools/perf/benchmarks/scheduler.py
index 48b79cd8..39b17023 100644
--- a/tools/perf/benchmarks/scheduler.py
+++ b/tools/perf/benchmarks/scheduler.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 from telemetry import benchmark
+
 from measurements import smoothness
 import page_sets
 
diff --git a/tools/perf/benchmarks/service_worker.py b/tools/perf/benchmarks/service_worker.py
index 427198c..e9d4f09 100644
--- a/tools/perf/benchmarks/service_worker.py
+++ b/tools/perf/benchmarks/service_worker.py
@@ -6,8 +6,6 @@
 import page_sets
 import re
 
-from measurements import timeline_controller
-from metrics import speedindex
 from telemetry import benchmark
 from telemetry.core import util
 from telemetry.page import page_test
@@ -15,6 +13,9 @@
 from telemetry.timeline import slice as slice_module
 from telemetry.value import scalar
 
+from measurements import timeline_controller
+from metrics import speedindex
+
 
 class _ServiceWorkerTimelineMetric(object):
   def AddResultsOfCounters(self, process, counter_regex_string, results):
diff --git a/tools/perf/benchmarks/session_restore.py b/tools/perf/benchmarks/session_restore.py
index b381cee..82d86515 100644
--- a/tools/perf/benchmarks/session_restore.py
+++ b/tools/perf/benchmarks/session_restore.py
@@ -5,11 +5,12 @@
 import os
 import tempfile
 
+from telemetry import benchmark
+
 from measurements import session_restore
 import page_sets
 from profile_creators import profile_generator
 from profile_creators import small_profile_creator
-from telemetry import benchmark
 
 
 class _SessionRestoreTypical25(benchmark.Benchmark):
diff --git a/tools/perf/benchmarks/skpicture_printer.py b/tools/perf/benchmarks/skpicture_printer.py
index 80b683c..8ba3d8960 100644
--- a/tools/perf/benchmarks/skpicture_printer.py
+++ b/tools/perf/benchmarks/skpicture_printer.py
@@ -2,11 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from measurements import skpicture_printer
 from telemetry import benchmark
 from telemetry.core import discover
 from telemetry.page import page_set
 
+from measurements import skpicture_printer
+
 
 def _MatchPageSetName(page_set_name, page_set_base_dir):
   page_sets = []
diff --git a/tools/perf/benchmarks/smoothness.py b/tools/perf/benchmarks/smoothness.py
index b5debef8..8ade1be 100644
--- a/tools/perf/benchmarks/smoothness.py
+++ b/tools/perf/benchmarks/smoothness.py
@@ -2,11 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry import benchmark
+
 from benchmarks import silk_flags
 from benchmarks import webgl_expectations
 from measurements import smoothness
 import page_sets
-from telemetry import benchmark
 
 
 class SmoothnessTop25(benchmark.Benchmark):
@@ -175,6 +176,22 @@
   def Name(cls):
     return 'smoothness.simple_mobile_sites'
 
+@benchmark.Enabled('android')
+class SmoothnessFlingSimpleMobilePages(benchmark.Benchmark):
+  """Measures rendering statistics for flinging a simple mobile sites page set.
+  """
+  test = smoothness.Smoothness
+  page_set = page_sets.SimpleMobileSitesFlingPageSet
+
+  def CustomizeBrowserOptions(self, options):
+    # As the fling parameters cannot be analytically determined to not
+    # overscroll, disable overscrolling explicitly. Overscroll behavior is
+    # orthogonal to fling performance, and its activation is only more noise.
+    options.AppendExtraBrowserArgs('--disable-overscroll-edge-effect')
+
+  @classmethod
+  def Name(cls):
+    return 'smoothness.fling.simple_mobile_sites'
 
 @benchmark.Enabled('android', 'chromeos')
 class SmoothnessToughPinchZoomCases(benchmark.Benchmark):
diff --git a/tools/perf/benchmarks/spaceport.py b/tools/perf/benchmarks/spaceport.py
index bdcf5e69..75a715c6 100644
--- a/tools/perf/benchmarks/spaceport.py
+++ b/tools/perf/benchmarks/spaceport.py
@@ -8,8 +8,8 @@
 import os
 
 from telemetry import benchmark
-from telemetry import page as page_module
 from telemetry.core import util
+from telemetry import page as page_module
 from telemetry.page import page_set
 from telemetry.page import page_test
 from telemetry.value import list_of_scalar_values
diff --git a/tools/perf/benchmarks/speedometer.py b/tools/perf/benchmarks/speedometer.py
index 870895e..f463abc28 100644
--- a/tools/perf/benchmarks/speedometer.py
+++ b/tools/perf/benchmarks/speedometer.py
@@ -18,13 +18,14 @@
 
 import os
 
-from metrics import keychain_metric
 from telemetry import benchmark
 from telemetry import page as page_module
 from telemetry.page import page_set
 from telemetry.page import page_test
 from telemetry.value import list_of_scalar_values
 
+from metrics import keychain_metric
+
 
 class SpeedometerMeasurement(page_test.PageTest):
   enabled_suites = [
diff --git a/tools/perf/benchmarks/start_with_url.py b/tools/perf/benchmarks/start_with_url.py
index b331bc9..43a17986 100644
--- a/tools/perf/benchmarks/start_with_url.py
+++ b/tools/perf/benchmarks/start_with_url.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry import benchmark
+
 from measurements import startup
 import page_sets
-from telemetry import benchmark
 
 
 class _StartWithUrl(benchmark.Benchmark):
diff --git a/tools/perf/benchmarks/startup.py b/tools/perf/benchmarks/startup.py
index 957619a0d..d69a45f4 100644
--- a/tools/perf/benchmarks/startup.py
+++ b/tools/perf/benchmarks/startup.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry import benchmark
+
 from measurements import startup
 import page_sets
-from telemetry import benchmark
 
 
 class _StartupCold(benchmark.Benchmark):
diff --git a/tools/perf/benchmarks/sunspider.py b/tools/perf/benchmarks/sunspider.py
index b3f5586..fcb7ebe 100644
--- a/tools/perf/benchmarks/sunspider.py
+++ b/tools/perf/benchmarks/sunspider.py
@@ -5,13 +5,14 @@
 import json
 import os
 
-from metrics import power
 from telemetry import benchmark
 from telemetry import page as page_module
 from telemetry.page import page_set
 from telemetry.page import page_test
 from telemetry.value import list_of_scalar_values
 
+from metrics import power
+
 
 _URL = 'http://www.webkit.org/perf/sunspider-1.0.2/sunspider-1.0.2/driver.html'
 
diff --git a/tools/perf/benchmarks/task_execution_time.py b/tools/perf/benchmarks/task_execution_time.py
index b8560647..5bb38f2 100644
--- a/tools/perf/benchmarks/task_execution_time.py
+++ b/tools/perf/benchmarks/task_execution_time.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry import benchmark
+
 from measurements import task_execution_time
 import page_sets
-from telemetry import benchmark
 
 
 @benchmark.Enabled('android')
diff --git a/tools/perf/benchmarks/thread_times.py b/tools/perf/benchmarks/thread_times.py
index a94c324f..9d7400f 100644
--- a/tools/perf/benchmarks/thread_times.py
+++ b/tools/perf/benchmarks/thread_times.py
@@ -2,10 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry import benchmark
+
 from benchmarks import silk_flags
 from measurements import thread_times
 import page_sets
-from telemetry import benchmark
 
 class _ThreadTimes(benchmark.Benchmark):
   @classmethod
diff --git a/tools/perf/benchmarks/v8.py b/tools/perf/benchmarks/v8.py
index d4003a9..8b90855 100644
--- a/tools/perf/benchmarks/v8.py
+++ b/tools/perf/benchmarks/v8.py
@@ -2,10 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import page_sets
+from telemetry import benchmark
+
 from measurements import v8_detached_context_age_in_gc
 from measurements import v8_gc_times
-from telemetry import benchmark
+import page_sets
 
 
 @benchmark.Disabled('win')  # crbug.com/416502
diff --git a/tools/perf/benchmarks/webrtc.py b/tools/perf/benchmarks/webrtc.py
index c8bdc8f..ffd66d9 100644
--- a/tools/perf/benchmarks/webrtc.py
+++ b/tools/perf/benchmarks/webrtc.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry import benchmark
+
 from measurements import webrtc
 import page_sets
-from telemetry import benchmark
 
 
 class WebRTC(benchmark.Benchmark):
diff --git a/tools/perf/measurements/image_decoding.py b/tools/perf/measurements/image_decoding.py
index f360853..0391126 100644
--- a/tools/perf/measurements/image_decoding.py
+++ b/tools/perf/measurements/image_decoding.py
@@ -2,14 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from metrics import power
-
 from telemetry.core.platform import tracing_category_filter
 from telemetry.core.platform import tracing_options
 from telemetry.page import page_test
 from telemetry.timeline import model
 from telemetry.value import scalar
 
+from metrics import power
+
 
 class ImageDecoding(page_test.PageTest):
   def __init__(self):
diff --git a/tools/perf/measurements/measurement_smoke_test.py b/tools/perf/measurements/measurement_smoke_test.py
index 74b614ae..c8debc9 100644
--- a/tools/perf/measurements/measurement_smoke_test.py
+++ b/tools/perf/measurements/measurement_smoke_test.py
@@ -4,9 +4,9 @@
 """Measurement smoke test to make sure that no new action_name_to_run is
 defined."""
 
-import os
-import optparse
 import logging
+import optparse
+import os
 import unittest
 
 from telemetry import benchmark as benchmark_module
diff --git a/tools/perf/measurements/media.py b/tools/perf/measurements/media.py
index 12934b9..3537cda 100644
--- a/tools/perf/measurements/media.py
+++ b/tools/perf/measurements/media.py
@@ -2,11 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry.page import page_test
+
 from metrics import cpu
 from metrics import media
-from metrics import system_memory
 from metrics import power
-from telemetry.page import page_test
+from metrics import system_memory
 
 
 class Media(page_test.PageTest):
diff --git a/tools/perf/measurements/memory.py b/tools/perf/measurements/memory.py
index 3349f56..198871e 100644
--- a/tools/perf/measurements/memory.py
+++ b/tools/perf/measurements/memory.py
@@ -2,9 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry.page import page_test
+
 from metrics import memory
 from metrics import power
-from telemetry.page import page_test
+
 
 class Memory(page_test.PageTest):
   def __init__(self):
@@ -22,6 +24,9 @@
     self._memory_metric.Start(page, tab)
     self._power_metric.Start(page, tab)
 
+  def CleanUpAfterPage(self, page, tab):
+    tab.CollectGarbage()
+
   def CustomizeBrowserOptions(self, options):
     memory.MemoryMetric.CustomizeBrowserOptions(options)
     # Since this is a memory benchmark, we want to sample memory histograms at
diff --git a/tools/perf/measurements/memory_multi_tab.py b/tools/perf/measurements/memory_multi_tab.py
index 6855707..36896ef2 100644
--- a/tools/perf/measurements/memory_multi_tab.py
+++ b/tools/perf/measurements/memory_multi_tab.py
@@ -8,9 +8,10 @@
 the entire test rather than each single page.
 """
 
+from telemetry.page import page_test
 
 from metrics import memory
-from telemetry.page import page_test
+
 
 class MemoryMultiTab(page_test.PageTest):
   def __init__(self):
diff --git a/tools/perf/measurements/oilpan_gc_times.py b/tools/perf/measurements/oilpan_gc_times.py
index 1d927556..7389ec3 100644
--- a/tools/perf/measurements/oilpan_gc_times.py
+++ b/tools/perf/measurements/oilpan_gc_times.py
@@ -4,18 +4,19 @@
 
 import os
 
-from measurements import smoothness_controller
-from measurements import timeline_controller
 from telemetry.core.platform import tracing_category_filter
 from telemetry.core.platform import tracing_options
-from telemetry.page import page_test
 from telemetry.page.actions import action_runner
+from telemetry.page import page_test
 from telemetry.timeline.model import TimelineModel
 from telemetry.util import statistics
 from telemetry.value import list_of_scalar_values
 from telemetry.value import scalar
 from telemetry.value import trace
 
+from measurements import smoothness_controller
+from measurements import timeline_controller
+
 
 _CR_RENDERER_MAIN = 'CrRendererMain'
 _RUN_SMOOTH_ACTIONS = 'RunSmoothAllActions'
diff --git a/tools/perf/measurements/oilpan_gc_times_unittest.py b/tools/perf/measurements/oilpan_gc_times_unittest.py
index ae359758..dc2d87b 100644
--- a/tools/perf/measurements/oilpan_gc_times_unittest.py
+++ b/tools/perf/measurements/oilpan_gc_times_unittest.py
@@ -2,10 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from measurements import oilpan_gc_times
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import page_test_test_case
 
+from measurements import oilpan_gc_times
+
+
 class OilpanGCTimesTest(page_test_test_case.PageTestTestCase):
   """Smoke test for Oilpan GC pause time measurements.
 
diff --git a/tools/perf/measurements/page_cycler.py b/tools/perf/measurements/page_cycler.py
index e2cb054..4434ea0c 100644
--- a/tools/perf/measurements/page_cycler.py
+++ b/tools/perf/measurements/page_cycler.py
@@ -18,14 +18,15 @@
 import collections
 import os
 
+from telemetry.core import util
+from telemetry.page import page_test
+from telemetry.value import scalar
+
 from metrics import cpu
 from metrics import keychain_metric
 from metrics import memory
 from metrics import power
 from metrics import speedindex
-from telemetry.core import util
-from telemetry.page import page_test
-from telemetry.value import scalar
 
 
 class PageCycler(page_test.PageTest):
diff --git a/tools/perf/measurements/page_cycler_unittest.py b/tools/perf/measurements/page_cycler_unittest.py
index 478bb6b..c6ce636 100644
--- a/tools/perf/measurements/page_cycler_unittest.py
+++ b/tools/perf/measurements/page_cycler_unittest.py
@@ -5,13 +5,13 @@
 import sys
 import unittest
 
-from metrics import keychain_metric
 from telemetry.core import browser_options
 from telemetry.results import page_test_results
 from telemetry.unittest_util import simple_mock
 from telemetry.user_story import user_story_runner
 
 from measurements import page_cycler
+from metrics import keychain_metric
 
 
 # Allow testing protected members in the unit test.
diff --git a/tools/perf/measurements/power.py b/tools/perf/measurements/power.py
index f82fbe5..2552539 100644
--- a/tools/perf/measurements/power.py
+++ b/tools/perf/measurements/power.py
@@ -2,9 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from metrics import power
 from telemetry.page import page_test
 
+from metrics import power
+
+
 class Power(page_test.PageTest):
   def __init__(self):
     super(Power, self).__init__()
diff --git a/tools/perf/measurements/rasterize_and_record_micro_unittest.py b/tools/perf/measurements/rasterize_and_record_micro_unittest.py
index 7b1007f..1914ab0 100644
--- a/tools/perf/measurements/rasterize_and_record_micro_unittest.py
+++ b/tools/perf/measurements/rasterize_and_record_micro_unittest.py
@@ -4,13 +4,14 @@
 
 import logging
 
-from measurements import rasterize_and_record_micro
-from telemetry import decorators
 from telemetry.core import wpr_modes
+from telemetry import decorators
 from telemetry.page import page_test
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import page_test_test_case
 
+from measurements import rasterize_and_record_micro
+
 
 class RasterizeAndRecordMicroUnitTest(page_test_test_case.PageTestTestCase):
   """Smoke test for rasterize_and_record_micro measurement
diff --git a/tools/perf/measurements/record_per_area.py b/tools/perf/measurements/record_per_area.py
index c0fc5bc..367e548c 100644
--- a/tools/perf/measurements/record_per_area.py
+++ b/tools/perf/measurements/record_per_area.py
@@ -4,10 +4,11 @@
 
 import time
 
-from measurements import smoothness
 from telemetry.page import page_test
 from telemetry.value import scalar
 
+from measurements import smoothness
+
 
 class RecordPerArea(page_test.PageTest):
   def __init__(self, start_wait_time=2):
diff --git a/tools/perf/measurements/record_per_area_unittest.py b/tools/perf/measurements/record_per_area_unittest.py
index b782b20..7ee7119 100644
--- a/tools/perf/measurements/record_per_area_unittest.py
+++ b/tools/perf/measurements/record_per_area_unittest.py
@@ -2,12 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from measurements import record_per_area
 from telemetry.core import wpr_modes
 from telemetry import decorators
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import page_test_test_case
 
+from measurements import record_per_area
+
 
 class RecordPerAreaUnitTest(page_test_test_case.PageTestTestCase):
   """Smoke test for record_per_area measurement
diff --git a/tools/perf/measurements/repaint.py b/tools/perf/measurements/repaint.py
index 96ce88a..d7e56cb 100644
--- a/tools/perf/measurements/repaint.py
+++ b/tools/perf/measurements/repaint.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from measurements import smoothness_controller
 from telemetry.page import page_test
 
+from measurements import smoothness_controller
+
 
 class Repaint(page_test.PageTest):
   def __init__(self, mode='viewport', width=None, height=None):
diff --git a/tools/perf/measurements/repaint_unittest.py b/tools/perf/measurements/repaint_unittest.py
index 7964e4e..d71fd51 100644
--- a/tools/perf/measurements/repaint_unittest.py
+++ b/tools/perf/measurements/repaint_unittest.py
@@ -2,13 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from measurements import repaint
-from telemetry import decorators
 from telemetry.core import wpr_modes
+from telemetry import decorators
 from telemetry.page import page as page_module
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import page_test_test_case
 
+from measurements import repaint
+
 
 class TestRepaintPage(page_module.Page):
   def __init__(self, page_set, base_dir):
diff --git a/tools/perf/measurements/session_restore.py b/tools/perf/measurements/session_restore.py
index 76b973f01..99f323d 100644
--- a/tools/perf/measurements/session_restore.py
+++ b/tools/perf/measurements/session_restore.py
@@ -4,13 +4,15 @@
 
 import collections
 
-from measurements import startup
-from metrics import cpu
-from metrics import startup_metric
 from telemetry.core import util
 from telemetry.value import histogram
 from telemetry.value import histogram_util
 
+from measurements import startup
+from metrics import cpu
+from metrics import startup_metric
+
+
 _HISTOGRAMS = [
     {
         'name': 'SessionRestore.ForegroundTabFirstLoaded',
diff --git a/tools/perf/measurements/skpicture_printer_unittest.py b/tools/perf/measurements/skpicture_printer_unittest.py
index d57c3e3..51a395b 100644
--- a/tools/perf/measurements/skpicture_printer_unittest.py
+++ b/tools/perf/measurements/skpicture_printer_unittest.py
@@ -5,11 +5,12 @@
 import shutil
 import tempfile
 
-from measurements import skpicture_printer
 from telemetry import decorators
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import page_test_test_case
 
+from measurements import skpicture_printer
+
 
 class SkpicturePrinterUnitTest(page_test_test_case.PageTestTestCase):
   def setUp(self):
diff --git a/tools/perf/measurements/smooth_gesture_util_unittest.py b/tools/perf/measurements/smooth_gesture_util_unittest.py
index b9b6106d..e0b9781e 100644
--- a/tools/perf/measurements/smooth_gesture_util_unittest.py
+++ b/tools/perf/measurements/smooth_gesture_util_unittest.py
@@ -4,10 +4,9 @@
 import time
 import unittest
 
-from measurements import smooth_gesture_util as sg_util
-from telemetry import decorators
 from telemetry.core.platform import tracing_category_filter
 from telemetry.core.platform import tracing_options
+from telemetry import decorators
 from telemetry.page import page as page_module
 from telemetry.page import page_test
 from telemetry.timeline import async_slice
@@ -15,6 +14,8 @@
 from telemetry.unittest_util import page_test_test_case
 from telemetry.web_perf import timeline_interaction_record as tir_module
 
+from measurements import smooth_gesture_util as sg_util
+
 
 class SmoothGestureUtilTest(unittest.TestCase):
   def testGetAdjustedInteractionIfContainGesture(self):
diff --git a/tools/perf/measurements/smoothness.py b/tools/perf/measurements/smoothness.py
index 587e8a0..b97a2f8 100644
--- a/tools/perf/measurements/smoothness.py
+++ b/tools/perf/measurements/smoothness.py
@@ -2,10 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from metrics import power
-from measurements import smoothness_controller
 from telemetry.page import page_test
 
+from measurements import smoothness_controller
+from metrics import power
+
 
 class Smoothness(page_test.PageTest):
   def __init__(self):
diff --git a/tools/perf/measurements/smoothness_controller.py b/tools/perf/measurements/smoothness_controller.py
index 835a0ff..0818f18 100644
--- a/tools/perf/measurements/smoothness_controller.py
+++ b/tools/perf/measurements/smoothness_controller.py
@@ -3,18 +3,19 @@
 # found in the LICENSE file.
 import sys
 
-from measurements import smooth_gesture_util
 from telemetry.core.platform import tracing_category_filter
 from telemetry.core.platform import tracing_options
-from telemetry.timeline import trace_data as trace_data_module
-from telemetry.timeline.model import TimelineModel
-from telemetry.page import page_test
 from telemetry.page.actions import action_runner
+from telemetry.page import page_test
+from telemetry.timeline.model import TimelineModel
+from telemetry.timeline import trace_data as trace_data_module
 from telemetry.value import list_of_scalar_values
 from telemetry.value import scalar
 from telemetry.value import trace
-from telemetry.web_perf import timeline_interaction_record as tir_module
 from telemetry.web_perf.metrics import smoothness
+from telemetry.web_perf import timeline_interaction_record as tir_module
+
+from measurements import smooth_gesture_util
 
 
 RUN_SMOOTH_ACTIONS = 'RunSmoothAllActions'
diff --git a/tools/perf/measurements/smoothness_unittest.py b/tools/perf/measurements/smoothness_unittest.py
index 7e79108..c8805f1 100644
--- a/tools/perf/measurements/smoothness_unittest.py
+++ b/tools/perf/measurements/smoothness_unittest.py
@@ -3,15 +3,17 @@
 # found in the LICENSE file.
 import sys
 
-from measurements import smoothness
-from metrics import power
-from telemetry import decorators
 from telemetry.core import exceptions
 from telemetry.core import wpr_modes
+from telemetry import decorators
 from telemetry.page import page
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import page_test_test_case
 
+from measurements import smoothness
+from metrics import power
+
+
 class FakeTracingController(object):
   def __init__(self):
     self.category_filter = None
diff --git a/tools/perf/measurements/startup.py b/tools/perf/measurements/startup.py
index a365f6d9..6acf9d2c 100644
--- a/tools/perf/measurements/startup.py
+++ b/tools/perf/measurements/startup.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry.page import page_test
+
 from metrics import keychain_metric
 from metrics import startup_metric
-from telemetry.page import page_test
 
 
 class Startup(page_test.PageTest):
diff --git a/tools/perf/measurements/tab_switching.py b/tools/perf/measurements/tab_switching.py
index bc79ca08..d4110e17 100644
--- a/tools/perf/measurements/tab_switching.py
+++ b/tools/perf/measurements/tab_switching.py
@@ -12,13 +12,14 @@
 
 import time
 
-from metrics import keychain_metric
-from metrics import power
 from telemetry.core import util
 from telemetry.page import page_test
 from telemetry.value import histogram
 from telemetry.value import histogram_util
 
+from metrics import keychain_metric
+from metrics import power
+
 # TODO: Revisit this test once multitab support is finalized.
 
 class TabSwitching(page_test.PageTest):
diff --git a/tools/perf/measurements/task_execution_time_unittest.py b/tools/perf/measurements/task_execution_time_unittest.py
index 0cfb59e..e2137e1 100644
--- a/tools/perf/measurements/task_execution_time_unittest.py
+++ b/tools/perf/measurements/task_execution_time_unittest.py
@@ -2,9 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from measurements import task_execution_time
-from telemetry import decorators
 from telemetry.core import wpr_modes
+from telemetry import decorators
 from telemetry.page import page as page_module
 from telemetry.results import page_test_results
 from telemetry.timeline import model as model_module
@@ -12,6 +11,8 @@
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import page_test_test_case
 
+from measurements import task_execution_time
+
 
 class TestTaskExecutionTimePage(page_module.Page):
 
diff --git a/tools/perf/measurements/thread_times.py b/tools/perf/measurements/thread_times.py
index ab0bbd9..6b517d06 100644
--- a/tools/perf/measurements/thread_times.py
+++ b/tools/perf/measurements/thread_times.py
@@ -1,12 +1,15 @@
 # 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.
-from measurements import timeline_controller
-from metrics import timeline
+
 from telemetry.core.platform import tracing_category_filter
 from telemetry.page import page_test
 from telemetry.web_perf.metrics import layout
 
+from measurements import timeline_controller
+from metrics import timeline
+
+
 class ThreadTimes(page_test.PageTest):
   def __init__(self, report_silk_details=False):
     super(ThreadTimes, self).__init__()
diff --git a/tools/perf/measurements/thread_times_unittest.py b/tools/perf/measurements/thread_times_unittest.py
index 91bad6e..0d40f1b 100644
--- a/tools/perf/measurements/thread_times_unittest.py
+++ b/tools/perf/measurements/thread_times_unittest.py
@@ -2,14 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from measurements import thread_times
-from measurements import smoothness_unittest
-from metrics import timeline
-from telemetry import decorators
 from telemetry.core import wpr_modes
-from telemetry.web_perf.metrics.layout import LayoutMetric
+from telemetry import decorators
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import page_test_test_case
+from telemetry.web_perf.metrics.layout import LayoutMetric
+
+from measurements import smoothness_unittest
+from measurements import thread_times
+from metrics import timeline
 
 
 class ThreadTimesUnitTest(page_test_test_case.PageTestTestCase):
diff --git a/tools/perf/measurements/timeline_controller.py b/tools/perf/measurements/timeline_controller.py
index 857384a4..6392859 100644
--- a/tools/perf/measurements/timeline_controller.py
+++ b/tools/perf/measurements/timeline_controller.py
@@ -1,15 +1,16 @@
 # 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.
-from measurements import smooth_gesture_util
 
 from telemetry.core.platform import tracing_category_filter
 from telemetry.core.platform import tracing_options
-from telemetry.timeline.model import TimelineModel
 from telemetry.page.actions import action_runner
+from telemetry.timeline.model import TimelineModel
 from telemetry.value import trace
 from telemetry.web_perf import timeline_interaction_record as tir_module
 
+from measurements import smooth_gesture_util
+
 
 RUN_SMOOTH_ACTIONS = 'RunSmoothAllActions'
 
diff --git a/tools/perf/measurements/v8_detached_context_age_in_gc.py b/tools/perf/measurements/v8_detached_context_age_in_gc.py
index 35367a8e..0e0cf82 100644
--- a/tools/perf/measurements/v8_detached_context_age_in_gc.py
+++ b/tools/perf/measurements/v8_detached_context_age_in_gc.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import json
+
 from telemetry.page import page_test
 from telemetry.value import histogram
 from telemetry.value import histogram_util
diff --git a/tools/perf/measurements/v8_detached_context_age_in_gc_unittest.py b/tools/perf/measurements/v8_detached_context_age_in_gc_unittest.py
index ed34e016e..4ae6f8d 100644
--- a/tools/perf/measurements/v8_detached_context_age_in_gc_unittest.py
+++ b/tools/perf/measurements/v8_detached_context_age_in_gc_unittest.py
@@ -2,15 +2,16 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from measurements import v8_detached_context_age_in_gc
 from telemetry.core import wpr_modes
-from telemetry.page import page_test
 from telemetry.page import page as page_module
+from telemetry.page import page_test
 from telemetry.results import page_test_results
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import page_test_test_case
 from telemetry.value import skip
 
+from measurements import v8_detached_context_age_in_gc
+
 
 class FakePage(object):
   def __init__(self, url):
diff --git a/tools/perf/measurements/v8_gc_times_unittest.py b/tools/perf/measurements/v8_gc_times_unittest.py
index a5f7dbe..c13089c 100644
--- a/tools/perf/measurements/v8_gc_times_unittest.py
+++ b/tools/perf/measurements/v8_gc_times_unittest.py
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from measurements import v8_gc_times
 from telemetry.core import wpr_modes
 from telemetry.page import page as page_module
 from telemetry.results import page_test_results
@@ -10,6 +9,8 @@
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import page_test_test_case
 
+from measurements import v8_gc_times
+
 
 class V8GCTimesTestPageHelper(object):
 
diff --git a/tools/perf/measurements/webrtc.py b/tools/perf/measurements/webrtc.py
index 7587a68c..c8ac7d6 100644
--- a/tools/perf/measurements/webrtc.py
+++ b/tools/perf/measurements/webrtc.py
@@ -2,12 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry.page import page_test
+
 from metrics import cpu
 from metrics import media
 from metrics import memory
 from metrics import power
 from metrics import webrtc_stats
-from telemetry.page import page_test
 
 
 class WebRTC(page_test.PageTest):
diff --git a/tools/perf/metrics/cpu.py b/tools/perf/metrics/cpu.py
index d0a0304..35c3403 100644
--- a/tools/perf/metrics/cpu.py
+++ b/tools/perf/metrics/cpu.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from metrics import Metric
 from telemetry.value import scalar
 
+from metrics import Metric
+
 
 class CpuMetric(Metric):
   """Calulates CPU load over a span of time."""
diff --git a/tools/perf/metrics/keychain_metric.py b/tools/perf/metrics/keychain_metric.py
index a2c89fa9..4950186 100644
--- a/tools/perf/metrics/keychain_metric.py
+++ b/tools/perf/metrics/keychain_metric.py
@@ -5,11 +5,12 @@
 import logging
 import sys
 
-from metrics import Metric
 from telemetry.util.mac import keychain_helper
 from telemetry.value import histogram_util
 from telemetry.value import scalar
 
+from metrics import Metric
+
 
 class KeychainMetric(Metric):
   """KeychainMetric gathers keychain statistics from the browser object.
diff --git a/tools/perf/metrics/loading.py b/tools/perf/metrics/loading.py
index a05e073a..9d0ff90 100644
--- a/tools/perf/metrics/loading.py
+++ b/tools/perf/metrics/loading.py
@@ -2,9 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from metrics import Metric
 from telemetry.value import scalar
 
+from metrics import Metric
+
+
 class LoadingMetric(Metric):
   """A metric for page loading time based entirely on window.performance"""
 
diff --git a/tools/perf/metrics/media.py b/tools/perf/metrics/media.py
index 6680d84..37cadd7 100644
--- a/tools/perf/metrics/media.py
+++ b/tools/perf/metrics/media.py
@@ -4,10 +4,11 @@
 import logging
 import os
 
-from metrics import Metric
 from telemetry.value import list_of_scalar_values
 from telemetry.value import scalar
 
+from metrics import Metric
+
 
 class MediaMetric(Metric):
   """MediaMetric class injects and calls JS responsible for recording metrics.
diff --git a/tools/perf/metrics/memory.py b/tools/perf/metrics/memory.py
index 70b2ccf..4c3cd52 100644
--- a/tools/perf/metrics/memory.py
+++ b/tools/perf/metrics/memory.py
@@ -4,11 +4,12 @@
 
 import sys
 
-from metrics import Metric
 from telemetry.value import histogram
 from telemetry.value import histogram_util
 from telemetry.value import scalar
 
+from metrics import Metric
+
 
 _HISTOGRAMS = [
     {
diff --git a/tools/perf/metrics/power.py b/tools/perf/metrics/power.py
index 0f33d2d..ef258b4 100644
--- a/tools/perf/metrics/power.py
+++ b/tools/perf/metrics/power.py
@@ -4,10 +4,11 @@
 
 import time
 
-from metrics import Metric
 from telemetry.core.platform import process_statistic_timeline_data
 from telemetry.value import scalar
 
+from metrics import Metric
+
 
 class PowerMetric(Metric):
   """A metric for measuring power usage."""
diff --git a/tools/perf/metrics/speedindex.py b/tools/perf/metrics/speedindex.py
index 4ada008..634c8bda 100644
--- a/tools/perf/metrics/speedindex.py
+++ b/tools/perf/metrics/speedindex.py
@@ -5,11 +5,12 @@
 import collections
 import logging
 
-from metrics import Metric
 from telemetry.image_processing import image_util
 from telemetry.image_processing import rgba_color
 from telemetry.value import scalar
 
+from metrics import Metric
+
 
 class SpeedIndexMetric(Metric):
   """The speed index metric is one way of measuring page load speed.
diff --git a/tools/perf/metrics/speedindex_unittest.py b/tools/perf/metrics/speedindex_unittest.py
index b2474d90..760ff73f 100644
--- a/tools/perf/metrics/speedindex_unittest.py
+++ b/tools/perf/metrics/speedindex_unittest.py
@@ -9,10 +9,11 @@
 import os
 import unittest
 
-from metrics import speedindex
 from telemetry.image_processing import histogram
 from telemetry.image_processing import rgba_color
 
+from metrics import speedindex
+
 
 class FakeImageUtil(object):
   # pylint: disable=W0613
diff --git a/tools/perf/metrics/startup_metric.py b/tools/perf/metrics/startup_metric.py
index af1e3ff..0a532af 100644
--- a/tools/perf/metrics/startup_metric.py
+++ b/tools/perf/metrics/startup_metric.py
@@ -5,12 +5,12 @@
 import json
 import logging
 
-from metrics import Metric
-
 from telemetry.core import exceptions
 from telemetry.value import histogram_util
 from telemetry.value import scalar
 
+from metrics import Metric
+
 
 class StartupMetric(Metric):
   "A metric for browser startup time."
diff --git a/tools/perf/metrics/system_memory.py b/tools/perf/metrics/system_memory.py
index 6909d16a..ec3ce2e 100644
--- a/tools/perf/metrics/system_memory.py
+++ b/tools/perf/metrics/system_memory.py
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry.value import scalar
+
 from metrics import memory
 from metrics import Metric
-from telemetry.value import scalar
 
 
 class SystemMemoryMetric(Metric):
diff --git a/tools/perf/metrics/timeline.py b/tools/perf/metrics/timeline.py
index 54ac395..1a1ca25 100644
--- a/tools/perf/metrics/timeline.py
+++ b/tools/perf/metrics/timeline.py
@@ -2,10 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 import collections
-from telemetry.util.statistics import DivideIfPossibleOrZero
 
-from telemetry.web_perf.metrics import timeline_based_metric
+from telemetry.util.statistics import DivideIfPossibleOrZero
 from telemetry.value import scalar
+from telemetry.web_perf.metrics import timeline_based_metric
 
 
 class LoadTimesTimelineMetric(timeline_based_metric.TimelineBasedMetric):
diff --git a/tools/perf/metrics/timeline_unittest.py b/tools/perf/metrics/timeline_unittest.py
index 08aa9ee..5ae1a5d 100644
--- a/tools/perf/metrics/timeline_unittest.py
+++ b/tools/perf/metrics/timeline_unittest.py
@@ -4,11 +4,13 @@
 
 import unittest
 
-from metrics import timeline
-from telemetry.unittest_util import test_page_test_results
 from telemetry.timeline import model as model_module
+from telemetry.unittest_util import test_page_test_results
 from telemetry.web_perf import timeline_interaction_record as tir_module
 
+from metrics import timeline
+
+
 def _GetInteractionRecord(start, end):
   return tir_module.TimelineInteractionRecord("test-record", start, end)
 
diff --git a/tools/perf/metrics/webrtc_stats.py b/tools/perf/metrics/webrtc_stats.py
index 4a787dd1..5032c82 100644
--- a/tools/perf/metrics/webrtc_stats.py
+++ b/tools/perf/metrics/webrtc_stats.py
@@ -6,10 +6,12 @@
 import logging
 import re
 
-from metrics import Metric
 from telemetry.core import camel_case
 from telemetry.value import list_of_scalar_values
 
+from metrics import Metric
+
+
 INTERESTING_METRICS = {
     'packetsReceived': {
         'units': 'packets',
diff --git a/tools/perf/metrics/webrtc_stats_unittest.py b/tools/perf/metrics/webrtc_stats_unittest.py
index 066f105..e40549e 100644
--- a/tools/perf/metrics/webrtc_stats_unittest.py
+++ b/tools/perf/metrics/webrtc_stats_unittest.py
@@ -4,9 +4,10 @@
 
 import unittest
 
-from metrics import webrtc_stats
 from telemetry.unittest_util import simple_mock
 
+from metrics import webrtc_stats
+
 
 SAMPLE_JSON = '''
 [[
diff --git a/tools/perf/page_sets/alexa1-10000.py b/tools/perf/page_sets/alexa1-10000.py
index d94ed15..0f890c6 100644
--- a/tools/perf/page_sets/alexa1-10000.py
+++ b/tools/perf/page_sets/alexa1-10000.py
@@ -4,8 +4,8 @@
 import json
 import os
 
-from telemetry.page.page_set import PageSet
 from telemetry.page.page import Page
+from telemetry.page.page_set import PageSet
 
 
 __location__ = os.path.realpath(
diff --git a/tools/perf/page_sets/fling_gesture_supported_shared_state.py b/tools/perf/page_sets/fling_gesture_supported_shared_state.py
new file mode 100644
index 0000000..3d40a6c7
--- /dev/null
+++ b/tools/perf/page_sets/fling_gesture_supported_shared_state.py
@@ -0,0 +1,14 @@
+# 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.
+import logging
+
+from telemetry.page import shared_page_state
+
+
+class FlingGestureSupportedSharedState(shared_page_state.SharedPageState):
+  def CanRunOnBrowser(self, browser_info):
+    if not browser_info.HasFlingGestureSupport():
+      logging.warning('Browser does not support fling gestures, skipping test')
+      return False
+    return True
diff --git a/tools/perf/page_sets/maps.py b/tools/perf/page_sets/maps.py
index 931a51b..960112e65 100644
--- a/tools/perf/page_sets/maps.py
+++ b/tools/perf/page_sets/maps.py
@@ -1,11 +1,12 @@
 # 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.
-from page_sets import webgl_supported_shared_state
 
 from telemetry.page import page as page_module
 from telemetry.page import page_set as page_set_module
 
+from page_sets import webgl_supported_shared_state
+
 
 class MapsPage(page_module.Page):
 
diff --git a/tools/perf/page_sets/pica.py b/tools/perf/page_sets/pica.py
index fa1120e..ab5bb3d 100644
--- a/tools/perf/page_sets/pica.py
+++ b/tools/perf/page_sets/pica.py
@@ -1,7 +1,9 @@
 # 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.
+
 from telemetry.page import page_set as page_set_module
+
 from measurements import polymer_load
 
 
diff --git a/tools/perf/page_sets/simple_mobile_sites_fling.py b/tools/perf/page_sets/simple_mobile_sites_fling.py
new file mode 100644
index 0000000..1e56165
--- /dev/null
+++ b/tools/perf/page_sets/simple_mobile_sites_fling.py
@@ -0,0 +1,57 @@
+# 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.
+from page_sets import fling_gesture_supported_shared_state
+
+from telemetry.page import shared_page_state
+from telemetry.page import page as page_module
+from telemetry.page import page_set as page_set_module
+
+
+class SimpleFlingPage(page_module.Page):
+
+  def __init__(self, url, page_set):
+    super(SimpleFlingPage, self).__init__(
+        url=url,
+        page_set=page_set,
+        credentials_path='data/credentials.json',
+        shared_page_state_class=(fling_gesture_supported_shared_state\
+            .FlingGestureSupportedSharedState))
+    self.archive_data_file = 'data/simple_mobile_sites.json'
+
+  def RunNavigateSteps(self, action_runner):
+    super(SimpleFlingPage, self).RunNavigateSteps(action_runner)
+    # TODO(epenner): Remove this wait (http://crbug.com/366933)
+    action_runner.Wait(5)
+
+  def RunPageInteractions(self, action_runner):
+    interaction = action_runner.BeginGestureInteraction('FlingAction')
+    # Swiping up induces a downward fling, and 500 pixels of touch scrolling
+    # runway ensures consistent fling velocities.
+    action_runner.SwipePage(direction='up',
+                            distance='500',
+                            speed_in_pixels_per_second=5000)
+    interaction.End()
+
+class SimpleMobileSitesFlingPageSet(page_set_module.PageSet):
+
+  """ Simple mobile sites """
+
+  def __init__(self):
+    super(SimpleMobileSitesFlingPageSet, self).__init__(
+      user_agent_type='tablet_10_inch',
+      archive_data_file='data/simple_mobile_sites.json',
+      bucket=page_set_module.PUBLIC_BUCKET)
+
+    fling_page_list = [
+      # Why: Scrolls moderately complex pages (up to 60 layers)
+      'http://www.ebay.co.uk/',
+      'https://www.flickr.com/',
+      'http://www.apple.com/mac/',
+      'http://www.nyc.gov',
+      'http://m.nytimes.com/'
+    ]
+
+    for url in fling_page_list:
+      self.AddUserStory(SimpleFlingPage(url, self))
+
diff --git a/tools/perf/page_sets/top_desktop_sites_2012Q3.py b/tools/perf/page_sets/top_desktop_sites_2012Q3.py
index 24e2a80..65d0942 100644
--- a/tools/perf/page_sets/top_desktop_sites_2012Q3.py
+++ b/tools/perf/page_sets/top_desktop_sites_2012Q3.py
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry.page import page_set
 from telemetry.page import page
+from telemetry.page import page_set
 
 
 TOP_2013_URLS = [
diff --git a/tools/perf/page_sets/topeka.py b/tools/perf/page_sets/topeka.py
index 1e5f334..5227efa9 100644
--- a/tools/perf/page_sets/topeka.py
+++ b/tools/perf/page_sets/topeka.py
@@ -1,7 +1,9 @@
 # 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.
+
 from telemetry.page import page_set as page_set_module
+
 from measurements import polymer_load
 
 
diff --git a/tools/perf/page_sets/tough_scheduling_cases/second_batch_js_generator.py b/tools/perf/page_sets/tough_scheduling_cases/second_batch_js_generator.py
index a617d863..8932d18 100755
--- a/tools/perf/page_sets/tough_scheduling_cases/second_batch_js_generator.py
+++ b/tools/perf/page_sets/tough_scheduling_cases/second_batch_js_generator.py
@@ -6,8 +6,8 @@
 from __future__ import print_function
 
 import argparse
-import StringIO
 import random
+import StringIO
 import sys
 import zlib
 
diff --git a/tools/perf/page_sets/tough_webgl_cases.py b/tools/perf/page_sets/tough_webgl_cases.py
index 6fe1af2..1b964de 100644
--- a/tools/perf/page_sets/tough_webgl_cases.py
+++ b/tools/perf/page_sets/tough_webgl_cases.py
@@ -1,11 +1,12 @@
 # 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.
-from page_sets import webgl_supported_shared_state
 
-from telemetry.page import shared_page_state
 from telemetry.page import page as page_module
 from telemetry.page import page_set as page_set_module
+from telemetry.page import shared_page_state
+
+from page_sets import webgl_supported_shared_state
 
 
 class ToughWebglCasesPage(page_module.Page):
diff --git a/tools/perf/profile_creators/history_profile_extender.py b/tools/perf/profile_creators/history_profile_extender.py
index 94cd636..58293c3 100644
--- a/tools/perf/profile_creators/history_profile_extender.py
+++ b/tools/perf/profile_creators/history_profile_extender.py
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 import multiprocessing
-import tempfile
 import os
+import tempfile
 
 from profile_creators import fast_navigation_profile_extender
 
diff --git a/tools/perf/profile_creators/history_profile_extender_unittest.py b/tools/perf/profile_creators/history_profile_extender_unittest.py
index 4f9cc867..c0b0b0f0 100644
--- a/tools/perf/profile_creators/history_profile_extender_unittest.py
+++ b/tools/perf/profile_creators/history_profile_extender_unittest.py
@@ -7,8 +7,8 @@
 import unittest
 
 from profile_creators.history_profile_extender import HistoryProfileExtender
-from telemetry import decorators
 from telemetry.core import util
+from telemetry import decorators
 from telemetry.unittest_util import options_for_unittests
 
 util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'mock')
diff --git a/tools/telemetry/examples/measure_trace.py b/tools/telemetry/examples/measure_trace.py
index 31e49e7..22fcc13 100755
--- a/tools/telemetry/examples/measure_trace.py
+++ b/tools/telemetry/examples/measure_trace.py
@@ -9,19 +9,19 @@
 import sys
 
 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
-sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
-                             'perf'))
-
 from telemetry.page import page as page_module
+from telemetry.results import buildbot_output_formatter
+from telemetry.results import page_test_results
 from telemetry.timeline import model
 from telemetry.timeline import tracing_timeline_data
-from telemetry.results import page_test_results
-from telemetry.results import buildbot_output_formatter
-from telemetry.web_perf import timeline_interaction_record as tir_module
 from telemetry.web_perf.metrics import smoothness
+from telemetry.web_perf import timeline_interaction_record as tir_module
+
+sys.path.append(os.path.join(
+  os.path.dirname(__file__), os.pardir, os.pardir, 'perf'))
 # pylint: disable=F0401
-from measurements import smoothness_controller
 from measurements import smooth_gesture_util
+from measurements import smoothness_controller
 
 
 def _ExtractInteractionsRecordFromThread(thread, timeline_model):
diff --git a/tools/telemetry/telemetry/benchmark.py b/tools/telemetry/telemetry/benchmark.py
index 1c42c0c..60087c6 100644
--- a/tools/telemetry/telemetry/benchmark.py
+++ b/tools/telemetry/telemetry/benchmark.py
@@ -9,16 +9,16 @@
 import sys
 import zipfile
 
-from telemetry import decorators
-from telemetry import page
 from telemetry.core import browser_finder
 from telemetry.core import command_line
 from telemetry.core import util
-from telemetry.user_story import user_story_runner
+from telemetry import decorators
+from telemetry import page
 from telemetry.page import page_set
 from telemetry.page import page_test
 from telemetry.page import test_expectations
 from telemetry.results import results_options
+from telemetry.user_story import user_story_runner
 from telemetry.util import cloud_storage
 from telemetry.util import exception_formatter
 from telemetry.web_perf import timeline_based_measurement
@@ -147,13 +147,13 @@
     results.
 
     Args:
-        value: a value.Value instance.
-        is_first_result: True if |value| is the first result for its
-            corresponding user story.
+      value: a value.Value instance.
+      is_first_result: True if |value| is the first result for its
+          corresponding user story.
 
     Returns:
-        True if |value| should be added to the test results.
-        Otherwise, it returns False.
+      True if |value| should be added to the test results.
+      Otherwise, it returns False.
     """
     return True
 
diff --git a/tools/telemetry/telemetry/benchmark_runner.py b/tools/telemetry/telemetry/benchmark_runner.py
index 83552ea..a9b7ec6a 100644
--- a/tools/telemetry/telemetry/benchmark_runner.py
+++ b/tools/telemetry/telemetry/benchmark_runner.py
@@ -14,12 +14,12 @@
 import sys
 
 from telemetry import benchmark
-from telemetry import decorators
 from telemetry.core import browser_finder
 from telemetry.core import browser_options
 from telemetry.core import command_line
 from telemetry.core import discover
 from telemetry.core import util
+from telemetry import decorators
 from telemetry.util import find_dependencies
 
 
diff --git a/tools/telemetry/telemetry/benchmark_unittest.py b/tools/telemetry/telemetry/benchmark_unittest.py
index 2bdcccc..3917d8b 100644
--- a/tools/telemetry/telemetry/benchmark_unittest.py
+++ b/tools/telemetry/telemetry/benchmark_unittest.py
@@ -6,11 +6,11 @@
 import unittest
 
 from telemetry import benchmark
-from telemetry import page
-from telemetry import user_story
 from telemetry.core import browser_options
+from telemetry import page
 from telemetry.page import page_test
 from telemetry.page import shared_page_state
+from telemetry import user_story
 from telemetry.user_story import android
 from telemetry.user_story import shared_user_story_state
 from telemetry.user_story import user_story_runner
diff --git a/tools/telemetry/telemetry/core/_bitmap.py b/tools/telemetry/telemetry/core/_bitmap.py
index 606bd376..89aedfc 100644
--- a/tools/telemetry/telemetry/core/_bitmap.py
+++ b/tools/telemetry/telemetry/core/_bitmap.py
@@ -13,8 +13,8 @@
 import struct
 import subprocess
 
-from telemetry.core import util
 from telemetry.core import platform
+from telemetry.core import util
 from telemetry.image_processing import histogram
 from telemetry.image_processing import rgba_color
 from telemetry.util import support_binaries
diff --git a/tools/telemetry/telemetry/core/android_app_unittest.py b/tools/telemetry/telemetry/core/android_app_unittest.py
index 25d7aa2..91a5a0d 100644
--- a/tools/telemetry/telemetry/core/android_app_unittest.py
+++ b/tools/telemetry/telemetry/core/android_app_unittest.py
@@ -8,8 +8,8 @@
 
 from pylib.device import intent
 from telemetry.core import android_app
-from telemetry.core import platform as platform_module
 from telemetry.core.backends import android_app_backend
+from telemetry.core import platform as platform_module
 from telemetry.core.platform import android_device
 from telemetry.unittest_util import options_for_unittests
 
diff --git a/tools/telemetry/telemetry/core/android_process.py b/tools/telemetry/telemetry/core/android_process.py
index cd8219ee..62134c2 100644
--- a/tools/telemetry/telemetry/core/android_process.py
+++ b/tools/telemetry/telemetry/core/android_process.py
@@ -2,9 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry.core import web_contents
 from telemetry.core.backends import adb_commands
 from telemetry.core.backends.chrome_inspector import devtools_client_backend
+from telemetry.core import web_contents
 
 class WebViewNotFoundException(Exception):
   pass
diff --git a/tools/telemetry/telemetry/core/backends/android_app_backend.py b/tools/telemetry/telemetry/core/backends/android_app_backend.py
index 1960b6e..d218657e 100644
--- a/tools/telemetry/telemetry/core/backends/android_app_backend.py
+++ b/tools/telemetry/telemetry/core/backends/android_app_backend.py
@@ -6,12 +6,12 @@
 import time
 
 from telemetry.core import android_process
-from telemetry.core import util
-from telemetry.core import web_contents
 from telemetry.core.backends import adb_commands
-from telemetry.core.backends import app_backend
 from telemetry.core.backends import android_browser_backend_settings
 from telemetry.core.backends import android_command_line_backend
+from telemetry.core.backends import app_backend
+from telemetry.core import util
+from telemetry.core import web_contents
 
 
 class AndroidAppBackend(app_backend.AppBackend):
diff --git a/tools/telemetry/telemetry/core/backends/browser_backend.py b/tools/telemetry/telemetry/core/backends/browser_backend.py
index 0f95f78..e28bbe4 100644
--- a/tools/telemetry/telemetry/core/backends/browser_backend.py
+++ b/tools/telemetry/telemetry/core/backends/browser_backend.py
@@ -2,11 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry import decorators
-from telemetry.core import platform
-from telemetry.core import web_contents
 from telemetry.core.backends import app_backend
+from telemetry.core import platform
 from telemetry.core.platform import profiling_controller_backend
+from telemetry.core import web_contents
+from telemetry import decorators
 
 
 class ExtensionsNotSupportedException(Exception):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py
index b2a2326..c3f1560 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py
@@ -7,15 +7,15 @@
 import sys
 import time
 
-from telemetry.core import exceptions
-from telemetry.core import forwarders
-from telemetry.core import util
 from telemetry.core.backends import adb_commands
 from telemetry.core.backends import android_command_line_backend
 from telemetry.core.backends import browser_backend
 from telemetry.core.backends.chrome import chrome_browser_backend
+from telemetry.core import exceptions
+from telemetry.core import forwarders
 from telemetry.core.platform import android_platform_backend as \
   android_platform_backend_module
+from telemetry.core import util
 
 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
 from pylib.device import device_errors  # pylint: disable=F0401
diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py
index 4a06c12..447853f 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py
@@ -7,16 +7,16 @@
 import logging
 import os
 
-from telemetry import decorators
-from telemetry.core import browser
-from telemetry.core import exceptions
-from telemetry.core import possible_browser
-from telemetry.core import platform
-from telemetry.core import util
 from telemetry.core.backends import adb_commands
 from telemetry.core.backends import android_browser_backend_settings
 from telemetry.core.backends.chrome import android_browser_backend
+from telemetry.core import browser
+from telemetry.core import exceptions
+from telemetry.core import platform
 from telemetry.core.platform import android_device
+from telemetry.core import possible_browser
+from telemetry.core import util
+from telemetry import decorators
 
 
 CHROME_PACKAGE_NAMES = {
diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder_unittest.py
index 073062b..ad9bd8e6 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder_unittest.py
@@ -4,8 +4,8 @@
 
 import unittest
 
-from telemetry.core import browser_options
 from telemetry.core.backends.chrome import android_browser_finder
+from telemetry.core import browser_options
 from telemetry.unittest_util import system_stub
 
 
diff --git a/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend.py
index 4a4408b..7a5ebac 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend.py
@@ -12,19 +12,19 @@
 import sys
 import urllib2
 
-from telemetry import decorators
-from telemetry.core import exceptions
-from telemetry.core import forwarders
-from telemetry.core import user_agent
-from telemetry.core import util
-from telemetry.core import web_contents
-from telemetry.core import wpr_modes
 from telemetry.core.backends import browser_backend
 from telemetry.core.backends.chrome import extension_backend
 from telemetry.core.backends.chrome import system_info_backend
 from telemetry.core.backends.chrome import tab_list_backend
 from telemetry.core.backends.chrome_inspector import devtools_client_backend
 from telemetry.core.backends.chrome_inspector import devtools_http
+from telemetry.core import exceptions
+from telemetry.core import forwarders
+from telemetry.core import user_agent
+from telemetry.core import util
+from telemetry.core import web_contents
+from telemetry.core import wpr_modes
+from telemetry import decorators
 from telemetry.unittest_util import options_for_unittests
 
 
diff --git a/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend_unittest.py
index 98ccb81..db633a7 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend_unittest.py
@@ -4,8 +4,8 @@
 
 import unittest
 
-from telemetry.core import wpr_modes
 from telemetry.core.backends.chrome import chrome_browser_backend
+from telemetry.core import wpr_modes
 
 
 class FakePlatformBackend(object):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py
index fef7479..6eb512e 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py
@@ -5,12 +5,12 @@
 import logging
 import os
 
-from telemetry import decorators
+from telemetry.core.backends.chrome import chrome_browser_backend
+from telemetry.core.backends.chrome import misc_web_contents_backend
 from telemetry.core import exceptions
 from telemetry.core import forwarders
 from telemetry.core import util
-from telemetry.core.backends.chrome import chrome_browser_backend
-from telemetry.core.backends.chrome import misc_web_contents_backend
+from telemetry import decorators
 
 
 class CrOSBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_browser_finder.py b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_finder.py
index 7acc298..044739b 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/cros_browser_finder.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_finder.py
@@ -6,14 +6,14 @@
 
 import logging
 
-from telemetry.core import platform as platform_module
-from telemetry.core import browser
-from telemetry.core import browser_finder_exceptions
-from telemetry.core import possible_browser
-from telemetry.core.platform import cros_device
-from telemetry.core.platform import cros_interface
 from telemetry.core.backends.chrome import cros_browser_backend
 from telemetry.core.backends.chrome import cros_browser_with_oobe
+from telemetry.core import browser
+from telemetry.core import browser_finder_exceptions
+from telemetry.core import platform as platform_module
+from telemetry.core.platform import cros_device
+from telemetry.core.platform import cros_interface
+from telemetry.core import possible_browser
 
 
 class PossibleCrOSBrowser(possible_browser.PossibleBrowser):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_browser_with_oobe.py b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_with_oobe.py
index afec18c..7d7dbbb 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/cros_browser_with_oobe.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_with_oobe.py
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry.core import browser
 from telemetry.core.backends.chrome import cros_browser_backend
+from telemetry.core import browser
 
 
 class CrOSBrowserWithOOBE(browser.Browser):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_test_case.py b/tools/telemetry/telemetry/core/backends/chrome/cros_test_case.py
index 04d4f58..de07cc83 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/cros_test_case.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_test_case.py
@@ -7,8 +7,8 @@
 
 from telemetry.core import browser_finder
 from telemetry.core import extension_to_load
-from telemetry.core import util
 from telemetry.core.platform import cros_interface
+from telemetry.core import util
 from telemetry.unittest_util import options_for_unittests
 
 
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/cros_unittest.py
index 93b20f33..17053f3 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/cros_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_unittest.py
@@ -4,10 +4,10 @@
 
 import logging
 
-from telemetry import decorators
+from telemetry.core.backends.chrome import cros_test_case
 from telemetry.core import exceptions
 from telemetry.core import util
-from telemetry.core.backends.chrome import cros_test_case
+from telemetry import decorators
 
 
 class CrOSCryptohomeTest(cros_test_case.CrOSTestCase):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_backend.py
index 1f0620cc..90c9abe 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_backend.py
@@ -14,10 +14,10 @@
 import tempfile
 import time
 
-from telemetry.core import exceptions
-from telemetry.core import util
 from telemetry.core.backends import browser_backend
 from telemetry.core.backends.chrome import chrome_browser_backend
+from telemetry.core import exceptions
+from telemetry.core import util
 from telemetry.util import path
 from telemetry.util import support_binaries
 
diff --git a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder.py b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder.py
index 13018f9..26e9b84 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder.py
@@ -8,12 +8,12 @@
 import subprocess
 import sys
 
-from telemetry.core import platform as platform_module
+from telemetry.core.backends.chrome import desktop_browser_backend
 from telemetry.core import browser
 from telemetry.core import exceptions
-from telemetry.core import possible_browser
-from telemetry.core.backends.chrome import desktop_browser_backend
+from telemetry.core import platform as platform_module
 from telemetry.core.platform import desktop_device
+from telemetry.core import possible_browser
 from telemetry.util import path
 
 
diff --git a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder_unittest.py
index 14abe60..b06ae70a 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder_unittest.py
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 import unittest
 
-from telemetry.core import browser_options
 from telemetry.core.backends.chrome import desktop_browser_finder
+from telemetry.core import browser_options
 from telemetry.core.platform import desktop_device
 from telemetry.unittest_util import system_stub
 
diff --git a/tools/telemetry/telemetry/core/backends/chrome/extension_backend.py b/tools/telemetry/telemetry/core/backends/chrome/extension_backend.py
index b4e868d..aba475d 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/extension_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/extension_backend.py
@@ -4,8 +4,8 @@
 
 import collections
 
-from telemetry.core import extension_page
 from telemetry.core.backends.chrome_inspector import inspector_backend_list
+from telemetry.core import extension_page
 
 
 class ExtensionBackendList(inspector_backend_list.InspectorBackendList):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/ios_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/ios_browser_backend.py
index 9166f14..ad65049 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/ios_browser_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/ios_browser_backend.py
@@ -8,10 +8,10 @@
 import re
 import urllib2
 
-from telemetry.core import exceptions
-from telemetry.core import util
 from telemetry.core.backends.chrome import chrome_browser_backend
 from telemetry.core.backends.chrome import system_info_backend
+from telemetry.core import exceptions
+from telemetry.core import util
 
 
 class IosBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/ios_browser_finder.py b/tools/telemetry/telemetry/core/backends/chrome/ios_browser_finder.py
index 589f207..b788a45 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/ios_browser_finder.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/ios_browser_finder.py
@@ -8,14 +8,14 @@
 import re
 import subprocess
 
-from telemetry import decorators
+from telemetry.core.backends.chrome import ios_browser_backend
+from telemetry.core.backends.chrome_inspector import inspector_backend
 from telemetry.core import browser
 from telemetry.core import platform
-from telemetry.core import possible_browser
-from telemetry.core.backends.chrome_inspector import inspector_backend
-from telemetry.core.backends.chrome import ios_browser_backend
 from telemetry.core.platform import ios_device
 from telemetry.core.platform import ios_platform_backend
+from telemetry.core import possible_browser
+from telemetry import decorators
 
 
 # Key matches output from ios-webkit-debug-proxy and the value is a readable
diff --git a/tools/telemetry/telemetry/core/backends/chrome/ios_browser_finder_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/ios_browser_finder_unittest.py
index e0e783fb..0b05318 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/ios_browser_finder_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/ios_browser_finder_unittest.py
@@ -3,10 +3,10 @@
 # found in the LICENSE file.
 import unittest
 
-from telemetry import decorators
-from telemetry.core import browser_options
 from telemetry.core.backends.chrome import ios_browser_finder
+from telemetry.core import browser_options
 from telemetry.core.platform import ios_device
+from telemetry import decorators
 
 
 class IosBrowserFinderUnitTest(unittest.TestCase):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/misc_web_contents_backend.py b/tools/telemetry/telemetry/core/backends/chrome/misc_web_contents_backend.py
index 6b46dad..b430312 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/misc_web_contents_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/misc_web_contents_backend.py
@@ -2,9 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry.core import exceptions
-from telemetry.core.backends.chrome_inspector import inspector_backend_list
 from telemetry.core.backends.chrome import oobe
+from telemetry.core.backends.chrome_inspector import inspector_backend_list
+from telemetry.core import exceptions
 
 
 class MiscWebContentsBackend(inspector_backend_list.InspectorBackendList):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/system_info_backend.py b/tools/telemetry/telemetry/core/backends/chrome/system_info_backend.py
index 44c8dc96..2a4c856 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/system_info_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/system_info_backend.py
@@ -2,9 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry.core.backends.chrome_inspector import inspector_websocket
 from telemetry.core import camel_case
 from telemetry.core import system_info
-from telemetry.core.backends.chrome_inspector import inspector_websocket
 
 
 class SystemInfoBackend(object):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend.py b/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend.py
index 7ce0a58f..0e97113 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend.py
@@ -2,10 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from telemetry.core.backends.chrome_inspector import inspector_backend_list
 from telemetry.core import exceptions
 from telemetry.core import tab
 from telemetry.core import util
-from telemetry.core.backends.chrome_inspector import inspector_backend_list
 
 
 class TabUnexpectedResponseException(exceptions.Error):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend_unittest.py
index 5ceacbbb..c5a81c4 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend_unittest.py
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry import decorators
 from telemetry.core import exceptions
+from telemetry import decorators
 from telemetry.unittest_util import tab_test_case
 
 
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_client_backend.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_client_backend.py
index 6e5935f1..8b2648fc 100644
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_client_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_client_backend.py
@@ -6,12 +6,12 @@
 import re
 import sys
 
-from telemetry import decorators
-from telemetry.core import exceptions
 from telemetry.core.backends.chrome_inspector import devtools_http
 from telemetry.core.backends.chrome_inspector import inspector_backend
 from telemetry.core.backends.chrome_inspector import tracing_backend
+from telemetry.core import exceptions
 from telemetry.core.platform.tracing_agent import chrome_tracing_agent
+from telemetry import decorators
 from telemetry.timeline import trace_data as trace_data_module
 
 
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_client_backend_unittest.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_client_backend_unittest.py
index 64ba2c89..b824b4e 100644
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_client_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_client_backend_unittest.py
@@ -2,9 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry import decorators
 from telemetry.core.backends.chrome_inspector import devtools_client_backend
 from telemetry.core.backends.chrome_inspector import devtools_http
+from telemetry import decorators
 from telemetry.unittest_util import browser_test_case
 
 
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend.py
index d9e8c972..c172013 100644
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend.py
@@ -8,9 +8,6 @@
 import socket
 import sys
 
-from telemetry import decorators
-from telemetry.core import exceptions
-from telemetry.core import util
 from telemetry.core.backends.chrome_inspector import devtools_http
 from telemetry.core.backends.chrome_inspector import inspector_console
 from telemetry.core.backends.chrome_inspector import inspector_memory
@@ -19,6 +16,9 @@
 from telemetry.core.backends.chrome_inspector import inspector_runtime
 from telemetry.core.backends.chrome_inspector import inspector_websocket
 from telemetry.core.backends.chrome_inspector import websocket
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry import decorators
 from telemetry.image_processing import image_util
 from telemetry.timeline import model as timeline_model_module
 from telemetry.timeline import trace_data as trace_data_module
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend_list.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend_list.py
index c0c6a7c6..89a7ada 100644
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend_list.py
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend_list.py
@@ -6,8 +6,8 @@
 import logging
 import sys
 
-from telemetry.core import exceptions
 from telemetry.core.backends.chrome_inspector import inspector_backend
+from telemetry.core import exceptions
 
 
 def DebuggerUrlToId(debugger_url):
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_network_unittest.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_network_unittest.py
index f440c7c..60ecbf9 100644
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_network_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_network_unittest.py
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry import decorators
 from telemetry.core.backends.chrome_inspector import inspector_network
+from telemetry import decorators
 from telemetry.unittest_util import tab_test_case
 
 
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_websocket.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_websocket.py
index 717974c9..3ea734d 100644
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_websocket.py
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_websocket.py
@@ -8,8 +8,8 @@
 import socket
 import time
 
-from telemetry.core import exceptions
 from telemetry.core.backends.chrome_inspector import websocket
+from telemetry.core import exceptions
 
 class WebSocketDisconnected(exceptions.Error):
   """An attempt was made to use a web socket after it had been disconnected."""
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/tracing_backend.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/tracing_backend.py
index 6d327938..33c1c1c 100644
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/tracing_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/tracing_backend.py
@@ -6,10 +6,10 @@
 import socket
 import time
 
-from telemetry import decorators
 from telemetry.core.backends.chrome_inspector import inspector_websocket
 from telemetry.core.backends.chrome_inspector import websocket
 from telemetry.core.platform import tracing_options
+from telemetry import decorators
 from telemetry.timeline import trace_data as trace_data_module
 
 
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/tracing_backend_unittest.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/tracing_backend_unittest.py
index 09bb135e..8253f82 100644
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/tracing_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/tracing_backend_unittest.py
@@ -6,13 +6,13 @@
 import json
 import unittest
 
-from telemetry import decorators
-from telemetry.core import util
 from telemetry.core.backends.chrome_inspector import inspector_websocket
 from telemetry.core.backends.chrome_inspector import tracing_backend
 from telemetry.core.backends.chrome_inspector import websocket
 from telemetry.core.platform import tracing_category_filter
 from telemetry.core.platform import tracing_options
+from telemetry.core import util
+from telemetry import decorators
 from telemetry.timeline import model as model_module
 from telemetry.timeline import trace_data as trace_data_module
 from telemetry.unittest_util import simple_mock
diff --git a/tools/telemetry/telemetry/core/backends/codepen_credentials_backend_unittest.py b/tools/telemetry/telemetry/core/backends/codepen_credentials_backend_unittest.py
index 43ecb5e9..7ec54d9a2 100644
--- a/tools/telemetry/telemetry/core/backends/codepen_credentials_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/codepen_credentials_backend_unittest.py
@@ -1,8 +1,8 @@
 # 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.
-from telemetry.core.backends import form_based_credentials_backend_unittest_base
 from telemetry.core.backends import codepen_credentials_backend
+from telemetry.core.backends import form_based_credentials_backend_unittest_base
 
 
 class TestCodePenCredentialsBackend(
diff --git a/tools/telemetry/telemetry/core/backends/remote/trybot_browser_finder.py b/tools/telemetry/telemetry/core/backends/remote/trybot_browser_finder.py
index 344d1bf5..0196e3a 100644
--- a/tools/telemetry/telemetry/core/backends/remote/trybot_browser_finder.py
+++ b/tools/telemetry/telemetry/core/backends/remote/trybot_browser_finder.py
@@ -12,15 +12,38 @@
 import sys
 import urllib2
 
-from telemetry import decorators
 from telemetry.core import platform
-from telemetry.core import possible_browser
 from telemetry.core.platform import trybot_device
+from telemetry.core import possible_browser
+from telemetry import decorators
 
 CHROMIUM_CONFIG_FILENAME = 'tools/run-perf-test.cfg'
 BLINK_CONFIG_FILENAME = 'Tools/run-perf-test.cfg'
 SUCCESS, NO_CHANGES, ERROR = range(3)
+# Unsupported Perf bisect bots.
+EXCLUDED_BOTS = {
+    'win_xp_perf_bisect',
+    'linux_perf_tester',
+    'linux_perf_bisector',
+    'win_perf_bisect_builder',
+    'win_x64_perf_bisect_builder',
+    'linux_perf_bisect_builder',
+    'mac_perf_bisect_builder',
+    'android_perf_bisect_builder'
+}
 
+INCLUDE_BOTS = [
+    'trybot-all',
+    'trybot-all-win',
+    'trybot-all-mac',
+    'trybot-all-linux',
+    'trybot-all-android'
+]
+
+class TrybotError(Exception):
+
+  def __str__(self):
+    return '%s\nError running tryjob.' % self.args[0]
 
 
 class PossibleTrybotBrowser(possible_browser.PossibleBrowser):
@@ -28,8 +51,7 @@
 
   def __init__(self, browser_type, _):
     target_os = browser_type.split('-')[1]
-    self._buildername = '%s_perf_bisect' % browser_type.replace(
-        'trybot-', '').replace('-', '_')
+    self._builder_names = _GetBuilderNames(browser_type)
     super(PossibleTrybotBrowser, self).__init__(browser_type, target_os, True)
 
   def Create(self, finder_options):
@@ -55,6 +77,96 @@
     returncode = proc.poll()
     return (returncode, out, err)
 
+  def _UpdateConfigAndRunTryjob(self, bot_platform, cfg_file_path):
+    """Updates perf config file, uploads changes and excutes perf try job.
+
+    Args:
+      bot_platform: Name of the platform to be generated.
+      cfg_file_path: Perf config file path.
+
+    Returns:
+      (result, msg) where result is one of:
+          SUCCESS if a tryjob was sent
+          NO_CHANGES if there was nothing to try,
+          ERROR if a tryjob was attempted but an error encountered
+          and msg is an error message if an error was encountered, or rietveld
+          url if success, otherwise throws TrybotError exception.
+    """
+    config = self._GetPerfConfig(bot_platform)
+    try:
+      config_file = open(cfg_file_path, 'w')
+    except IOError:
+      msg = 'Cannot find %s. Please run from src dir.' % cfg_file_path
+      return (ERROR, msg)
+    config_file.write('config = %s' % json.dumps(
+        config, sort_keys=True, indent=2, separators=(',', ': ')))
+    config_file.close()
+    # Commit the config changes locally.
+    returncode, out, err = self._RunProcess(
+        ['git', 'commit', '-a', '-m', 'bisect config: %s' % bot_platform])
+    if returncode:
+      raise TrybotError('Could not commit bisect config change for %s,'
+                        ' error %s' % (bot_platform, err))
+    # Upload the CL to rietveld and run a try job.
+    returncode, out, err = self._RunProcess([
+        'git', 'cl', 'upload', '-f', '--bypass-hooks', '-m',
+        'CL for perf tryjob on %s' % bot_platform
+    ])
+    if returncode:
+      raise TrybotError('Could upload to rietveld for %s, error %s' %
+                        (bot_platform, err))
+
+    match = re.search(r'https://codereview.chromium.org/[\d]+', out)
+    if not match:
+      raise TrybotError('Could not upload CL to rietveld for %s! Output %s' %
+                        (bot_platform, out))
+    rietveld_url = match.group(0)
+    # Generate git try command for available bots.
+    git_try_command = ['git', 'cl', 'try', '-m', 'tryserver.chromium.perf']
+    for bot in self._builder_names[bot_platform]:
+      git_try_command.extend(['-b', bot])
+    returncode, out, err = self._RunProcess(git_try_command)
+    if returncode:
+      raise TrybotError('Could not try CL for %s, error %s' %
+                        (bot_platform, err))
+
+    return (SUCCESS, rietveld_url)
+
+  def _GetPerfConfig(self, bot_platform):
+    """Generates the perf config for try job.
+
+    Args:
+      bot_platform: Name of the platform to be generated.
+
+    Returns:
+      A dictionary with perf config parameters.
+    """
+    # Generate the command line for the perf trybots
+    target_arch = 'ia32'
+    arguments = sys.argv
+    if bot_platform in ['win', 'win-x64']:
+      arguments[0] = 'python tools\\perf\\run_benchmark'
+    else:
+      arguments[0] = './tools/perf/run_benchmark'
+    for index, arg in enumerate(arguments):
+      if arg.startswith('--browser='):
+        if bot_platform == 'android':
+          arguments[index] = '--browser=android-chrome-shell'
+        elif any('x64' in bot for bot in self._builder_names[bot_platform]):
+          arguments[index] = '--browser=release_x64'
+          target_arch = 'x64'
+        else:
+          arguments[index] = '--browser=release'
+    command = ' '.join(arguments)
+
+    return {
+        'command': command,
+        'repeat_count': '1',
+        'max_time_minutes': '120',
+        'truncate_percent': '0',
+        'target_arch': target_arch,
+    }
+
   def _AttemptTryjob(self, cfg_file_path):
     """Attempts to run a tryjob from the current directory.
 
@@ -64,20 +176,24 @@
       cfg_file_path: Path to the config file for the try job.
 
     Returns:
-      (result, msg) where result is one of:
-          SUCCESS if a tryjob was sent
-          NO_CHANGES if there was nothing to try,
-          ERROR if a tryjob was attempted but an error encountered
-          and msg is an error message if an error was encountered, or rietveld
-          url if success.
+      Returns SUCCESS if a tryjob was sent, NO_CHANGES if there was nothing to
+      try, ERROR if a tryjob was attempted but an error encountered.
     """
+    source_repo = 'chromium'
+    if cfg_file_path == BLINK_CONFIG_FILENAME:
+      source_repo = 'blink'
+
+    # TODO(prasadv): This method is quite long, we should consider refactor
+    # this by extracting to helper methods.
     returncode, original_branchname, err = self._RunProcess(
         ['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
     if returncode:
       msg = 'Must be in a git repository to send changes to trybots.'
       if err:
         msg += '\nGit error: %s' % err
-      return (ERROR, msg)
+      logging.error(msg)
+      return ERROR
+
     original_branchname = original_branchname.strip()
 
     # Check if the tree is dirty: make sure the index is up to date and then
@@ -85,105 +201,62 @@
     self._RunProcess(['git', 'update-index', '--refresh', '-q'])
     returncode, out, err = self._RunProcess(['git', 'diff-index', 'HEAD'])
     if out:
-      msg = 'Cannot send a try job with a dirty tree. Commit locally first.'
-      return (ERROR, msg)
+      logging.error(
+          'Cannot send a try job with a dirty tree. Commit locally first.')
+      return ERROR
 
     # Make sure the tree does have local commits.
     returncode, out, err = self._RunProcess(
         ['git', 'log', 'origin/master..HEAD'])
     if not out:
-      return (NO_CHANGES, None)
+      return NO_CHANGES
 
     # Create/check out the telemetry-tryjob branch, and edit the configs
     # for the tryjob there.
     returncode, out, err = self._RunProcess(
         ['git', 'checkout', '-b', 'telemetry-tryjob'])
     if returncode:
-      msg = ('Error creating branch telemetry-tryjob. '
-             'Please delete it if it exists.\n%s' % err)
-      return (ERROR, msg)
-    returncode, out, err = self._RunProcess(
-        ['git', 'branch', '--set-upstream-to', 'origin/master'])
-    if returncode:
-      return (ERROR, 'Error in git branch --set-upstream-to: %s' % err)
-
-    # Generate the command line for the perf trybots
-    arguments = sys.argv
-    if self._target_os == 'win':
-      arguments[0] = 'python tools\\perf\\run_benchmark'
-    else:
-      arguments[0] = './tools/perf/run_benchmark'
-    for index, arg in enumerate(arguments):
-      if arg.startswith('--browser='):
-        if self._target_os == 'android':
-          arguments[index] = '--browser=android-chrome-shell'
-        elif 'x64' in self._buildername:
-          arguments[index] = '--browser=release_x64'
-        else:
-          arguments[index] = '--browser=release'
-    command = ' '.join(arguments)
-
-    # Set target architecture to build 32 or 64 bit binaries.
-    target_arch = 'x64' if 'x64' in self._buildername else 'ia32'
-
-    # Add the correct command to the config file and commit it.
-    config = {
-        'command': command,
-        'repeat_count': '1',
-        'max_time_minutes': '120',
-        'truncate_percent': '0',
-        'target_arch': target_arch,
-    }
+      logging.error('Error creating branch telemetry-tryjob. '
+                    'Please delete it if it exists.\n%s', err)
+      return ERROR
     try:
-      config_file = open(cfg_file_path, 'w')
-    except IOError:
-      msg = 'Cannot find %s. Please run from src dir.' % cfg_file_path
-      return (ERROR, msg)
-    config_file.write('config = %s' % json.dumps(
-        config, sort_keys=True, indent=2, separators=(',', ': ')))
-    config_file.close()
-    returncode, out, err = self._RunProcess(
-        ['git', 'commit', '-a', '-m', 'bisect config'])
-    if returncode:
-      msg = 'Could not commit bisect config change, error %s' % err
-      return (ERROR, msg)
-
-    # Upload the CL to rietveld and run a try job.
-    returncode, out, err = self._RunProcess([
-        'git', 'cl', 'upload', '-f', '--bypass-hooks', '-m',
-        'CL for perf tryjob'
-    ])
-    if returncode:
-      msg = 'Could upload to rietveld, error %s' % err
-      return (ERROR, msg)
-    match = re.search(r'https://codereview.chromium.org/[\d]+', out)
-    if not match:
-      msg = 'Could not upload CL to rietveld! Output %s' % out
-      return (ERROR, msg)
-    rietveld_url = match.group(0)
-    returncode, out, err = self._RunProcess([
-        'git', 'cl', 'try', '-m', 'tryserver.chromium.perf', '-b',
-        self._buildername])
-    if returncode:
-      msg = 'Could not try CL, error %s' % err
-      return (ERROR, msg)
-
-    # Checkout original branch and delete telemetry-tryjob branch.
-    returncode, out, err = self._RunProcess(
-        ['git', 'checkout', original_branchname])
-    if returncode:
-      msg = (
-          ('Could not check out %s. Please check it out and manually '
-           'delete the telemetry-tryjob branch. Error message: %s') %
-          (original_branchname, err))
-      return (ERROR, msg)
-    returncode, out, err = self._RunProcess(
-        ['git', 'branch', '-D', 'telemetry-tryjob'])
-    if returncode:
-      msg = (('Could not delete telemetry-tryjob branch. '
-              'Please delete it manually. Error %s') % err)
-      return (ERROR, msg)
-    return (SUCCESS, rietveld_url)
+      returncode, out, err = self._RunProcess(
+          ['git', 'branch', '--set-upstream-to', 'origin/master'])
+      if returncode:
+        logging.error('Error in git branch --set-upstream-to: %s', err)
+        return ERROR
+      for bot_platform in self._builder_names:
+        try:
+          results, output = self._UpdateConfigAndRunTryjob(
+              bot_platform, cfg_file_path)
+          if results == ERROR:
+            logging.error(output)
+            return ERROR
+          print ('Uploaded %s try job to rietveld for %s platform. '
+                 'View progress at %s' % (source_repo, bot_platform, output))
+        except TrybotError, err:
+          print err
+          logging.error(err)
+    finally:
+      # Checkout original branch and delete telemetry-tryjob branch.
+      # TODO(prasadv): This finally block could be extracted out to be a
+      # separate function called _CleanupBranch.
+      returncode, out, err = self._RunProcess(
+          ['git', 'checkout', original_branchname])
+      if returncode:
+        logging.error('Could not check out %s. Please check it out and '
+                      'manually delete the telemetry-tryjob branch. '
+                      ': %s', original_branchname, err)
+        return ERROR # pylint: disable=lost-exception
+      logging.info('Checked out original branch: %s', original_branchname)
+      returncode, out, err = self._RunProcess(
+          ['git', 'branch', '-D', 'telemetry-tryjob'])
+      if returncode:
+        logging.error('Could not delete telemetry-tryjob branch. '
+                      'Please delete it manually: %s', err)
+        return ERROR # pylint: disable=lost-exception
+      logging.info('Deleted temp branch: telemetry-tryjob')
+    return SUCCESS
 
   def RunRemote(self):
     """Sends a tryjob to a perf trybot.
@@ -193,29 +266,17 @@
     tryjob on the given bot.
     """
     # First check if there are chromium changes to upload.
-    status, msg = self._AttemptTryjob(CHROMIUM_CONFIG_FILENAME)
-    if status == SUCCESS:
-      print 'Uploaded chromium try job to rietveld. View progress at %s' % msg
-      return
-    elif status == ERROR:
-      logging.error(msg)
-      return
-
-    # If we got here, there are no chromium changes to upload. Try blink.
-    os.chdir('third_party/WebKit/')
-    status, msg = self._AttemptTryjob(BLINK_CONFIG_FILENAME)
-    os.chdir('../..')
-    if status == SUCCESS:
-      print 'Uploaded blink try job to rietveld. View progress at %s' % msg
-      return
-    elif status == ERROR:
-      logging.error(msg)
-      return
-    else:
-      logging.error('No local changes found in chromium or blink trees. '
-                    'browser=%s argument sends local changes to the %s '
-                    'perf trybot.', self.browser_type, self._buildername)
-      return
+    status = self._AttemptTryjob(CHROMIUM_CONFIG_FILENAME)
+    if status not in [SUCCESS, ERROR]:
+      # If we got here, there are no chromium changes to upload. Try blink.
+      os.chdir('third_party/WebKit/')
+      status = self._AttemptTryjob(BLINK_CONFIG_FILENAME)
+      os.chdir('../..')
+      if status not in [SUCCESS, ERROR]:
+        logging.error('No local changes found in chromium or blink trees. '
+                      'browser=%s argument sends local changes to the '
+                      'perf trybot(s): %s.', self.browser_type,
+                      self._builder_names.values())
 
   def _InitPlatformIfNeeded(self):
     if self._platform:
@@ -240,12 +301,49 @@
   f = urllib2.urlopen(
       'http://build.chromium.org/p/tryserver.chromium.perf/json')
   builders = json.loads(f.read()).get('builders', {}).keys()
-  builders = ['trybot-%s' % b.replace('_perf_bisect', '').replace('_', '-')
-              for b in builders if not b.endswith('_perf_bisect_builder')]
-  # Perf try jobs do not work on Windows XP
-  if 'trybot-win-xp' in builders:
-    builders.remove('trybot-win-xp')
-  return builders
+  builders = ['trybot-%s' % bot.replace('_perf_bisect', '').replace('_', '-')
+              for bot in builders if bot not in EXCLUDED_BOTS]
+  builders.extend(INCLUDE_BOTS)
+  return sorted(builders)
+
+
+def _GetBuilderNames(browser_type):
+  """ Return platform and its available bot name as dictionary."""
+  if 'all' not in browser_type:
+    bot = ['%s_perf_bisect' % browser_type.replace(
+        'trybot-', '').replace('-', '_')]
+    bot_platform = browser_type.split('-')[1]
+    if 'x64' in browser_type:
+      bot_platform += '-x64'
+    return {bot_platform: bot}
+
+  f = urllib2.urlopen(
+      'http://build.chromium.org/p/tryserver.chromium.perf/json')
+  builders = json.loads(f.read()).get('builders', {}).keys()
+  # Exclude unsupported bots like win xp and some dummy bots.
+  builders = [bot for bot in builders if bot not in EXCLUDED_BOTS]
+
+  platform_and_bots = {}
+  for os_name in ['linux', 'android', 'mac', 'win']:
+    platform_and_bots[os_name] = [bot for bot in builders if os_name in bot]
+
+  # Special case for Windows x64, consider it as separate platform
+  # config config should contain target_arch=x64 and --browser=release_x64.
+  win_x64_bots = [platform_and_bots['win'].pop(i)
+      for i, win_bot in enumerate(platform_and_bots['win']) if 'x64' in win_bot]
+  platform_and_bots['win-x64'] = win_x64_bots
+
+  if 'all-win' in browser_type:
+    return {'win': platform_and_bots['win'],
+            'win-x64': platform_and_bots['win-x64']}
+  if 'all-mac' in browser_type:
+    return {'mac': platform_and_bots['mac']}
+  if 'all-android' in browser_type:
+    return {'android': platform_and_bots['android']}
+  if 'all-linux' in browser_type:
+    return {'linux': platform_and_bots['linux']}
+
+  return platform_and_bots
 
 
 def FindAllBrowserTypes(finder_options):
diff --git a/tools/telemetry/telemetry/core/backends/remote/trybot_browser_finder_unittest.py b/tools/telemetry/telemetry/core/backends/remote/trybot_browser_finder_unittest.py
index d7427c4e..74a25cb 100644
--- a/tools/telemetry/telemetry/core/backends/remote/trybot_browser_finder_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/remote/trybot_browser_finder_unittest.py
@@ -7,8 +7,8 @@
 import StringIO
 import unittest
 
-from telemetry.core import browser_options
 from telemetry.core.backends.remote import trybot_browser_finder
+from telemetry.core import browser_options
 from telemetry.unittest_util import simple_mock
 from telemetry.unittest_util import system_stub
 
@@ -42,35 +42,52 @@
           'Popen').WithArgs(arg[0]).WillReturn(mock_popen)
     trybot_browser_finder.subprocess = mock_subprocess
 
-  def test_find_all_browser_types_list(self):
-    finder_options = browser_options.BrowserFinderOptions(browser_type='list')
+  def _MockTryserverJson(self, bots_dict):
     trybot_browser_finder.urllib2 = simple_mock.MockObject()
     trybot_browser_finder.urllib2.ExpectCall('urlopen').WithArgs(
         'http://build.chromium.org/p/tryserver.chromium.perf/json').WillReturn(
-            StringIO.StringIO(json.dumps({'builders': {
-                'android_nexus4_perf_bisect': 'stuff',
-                'mac_10_9_perf_bisect': 'otherstuff',
-                'win_perf_bisect_builder': 'not a trybot',
-            }})))
+            StringIO.StringIO(json.dumps({'builders': bots_dict})))
+
+  def test_find_all_browser_types_list(self):
+    finder_options = browser_options.BrowserFinderOptions(browser_type='list')
+    self._MockTryserverJson({
+        'android_nexus4_perf_bisect': 'stuff',
+        'mac_10_9_perf_bisect': 'otherstuff',
+        'win_perf_bisect_builder': 'not a trybot',
+    })
+    expected_trybots_list = [
+        'trybot-all',
+        'trybot-all-android',
+        'trybot-all-linux',
+        'trybot-all-mac',
+        'trybot-all-win',
+        'trybot-android-nexus4',
+        'trybot-mac-10-9'
+    ]
+
     self.assertEquals(
-        ['trybot-android-nexus4', 'trybot-mac-10-9'],
-        # pylint: disable=W0212
+        expected_trybots_list,
         sorted(trybot_browser_finder.FindAllBrowserTypes(finder_options)))
 
   def test_find_all_browser_types_trybot(self):
     finder_options = browser_options.BrowserFinderOptions(
         browser_type='trybot-win')
-    trybot_browser_finder.urllib2 = simple_mock.MockObject()
-    trybot_browser_finder.urllib2.ExpectCall('urlopen').WithArgs(
-        'http://build.chromium.org/p/tryserver.chromium.perf/json').WillReturn(
-            StringIO.StringIO(json.dumps({'builders': {
-                'android_nexus4_perf_bisect': 'stuff',
-                'mac_10_9_perf_bisect': 'otherstuff',
-                'win_perf_bisect_builder': 'not a trybot',
-            }})))
+    self._MockTryserverJson({
+        'android_nexus4_perf_bisect': 'stuff',
+        'mac_10_9_perf_bisect': 'otherstuff',
+        'win_perf_bisect_builder': 'not a trybot',
+    })
+    expected_trybots_list = [
+        'trybot-all',
+        'trybot-all-android',
+        'trybot-all-linux',
+        'trybot-all-mac',
+        'trybot-all-win',
+        'trybot-android-nexus4',
+        'trybot-mac-10-9'
+    ]
     self.assertEquals(
-        ['trybot-android-nexus4', 'trybot-mac-10-9'],
-        # pylint: disable=W0212
+        expected_trybots_list,
         sorted(trybot_browser_finder.FindAllBrowserTypes(finder_options)))
 
   def test_find_all_browser_types_non_trybot_browser(self):
@@ -84,15 +101,134 @@
 
   def test_constructor(self):
     finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({
+        'android_nexus4_perf_bisect': 'stuff',
+        'mac_10_9_perf_bisect': 'otherstuff',
+        'win_perf_bisect_builder': 'not a trybot',
+    })
     browser = trybot_browser_finder.PossibleTrybotBrowser(
         'trybot-android-nexus4', finder_options)
+    self.assertEquals('android', browser.target_os)
     # pylint: disable=W0212
-    self.assertEquals('android', browser._target_os)
+    self.assertTrue('android' in browser._builder_names)
+    self.assertEquals(['android_nexus4_perf_bisect'],
+                      browser._builder_names.get('android'))
+
+  def test_constructor_trybot_all(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({
+        'android_nexus4_perf_bisect': 'stuff',
+        'android_nexus5_perf_bisect': 'stuff2',
+        'mac_10_9_perf_bisect': 'otherstuff',
+        'mac_perf_bisect': 'otherstuff1',
+        'win_perf_bisect': 'otherstuff2',
+        'linux_perf_bisect': 'otherstuff3',
+        'win_x64_perf_bisect': 'otherstuff4',
+        'win_perf_bisect_builder': 'not a trybot',
+    })
+    browser = trybot_browser_finder.PossibleTrybotBrowser(
+        'trybot-all', finder_options)
+    self.assertEquals('all', browser.target_os)
     # pylint: disable=W0212
-    self.assertEquals('android_nexus4_perf_bisect', browser._buildername)
+    self.assertEquals(
+        ['android', 'linux', 'mac', 'win', 'win-x64'],
+        sorted(browser._builder_names))
+    self.assertEquals(
+        ['android_nexus4_perf_bisect', 'android_nexus5_perf_bisect'],
+        sorted(browser._builder_names.get('android')))
+    self.assertEquals(
+        ['mac_10_9_perf_bisect', 'mac_perf_bisect'],
+        sorted(browser._builder_names.get('mac')))
+    self.assertEquals(
+        ['linux_perf_bisect'], sorted(browser._builder_names.get('linux')))
+    self.assertEquals(
+        ['win_perf_bisect'], sorted(browser._builder_names.get('win')))
+    self.assertEquals(
+        ['win_x64_perf_bisect'], sorted(browser._builder_names.get('win-x64')))
+
+  def test_constructor_trybot_all_win(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({
+        'android_nexus4_perf_bisect': 'stuff',
+        'android_nexus5_perf_bisect': 'stuff2',
+        'win_8_perf_bisect': 'otherstuff',
+        'win_perf_bisect': 'otherstuff2',
+        'linux_perf_bisect': 'otherstuff3',
+        'win_x64_perf_bisect': 'otherstuff4',
+        'win_perf_bisect_builder': 'not a trybot',
+    })
+    browser = trybot_browser_finder.PossibleTrybotBrowser(
+        'trybot-all-win', finder_options)
+    self.assertEquals('all', browser.target_os)
+    # pylint: disable=W0212
+    self.assertEquals(
+        ['win', 'win-x64'],
+        sorted(browser._builder_names))
+    self.assertEquals(
+        ['win_8_perf_bisect', 'win_perf_bisect'],
+        sorted(browser._builder_names.get('win')))
+    self.assertEquals(
+        ['win_x64_perf_bisect'], sorted(browser._builder_names.get('win-x64')))
+
+  def test_constructor_trybot_all_android(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({
+        'android_nexus4_perf_bisect': 'stuff',
+        'android_nexus5_perf_bisect': 'stuff2',
+        'win_8_perf_bisect': 'otherstuff',
+        'win_perf_bisect': 'otherstuff2',
+        'linux_perf_bisect': 'otherstuff3',
+        'win_x64_perf_bisect': 'otherstuff4',
+        'win_perf_bisect_builder': 'not a trybot',
+    })
+    browser = trybot_browser_finder.PossibleTrybotBrowser(
+        'trybot-all-android', finder_options)
+    self.assertEquals(
+        ['android_nexus4_perf_bisect', 'android_nexus5_perf_bisect'],
+        sorted(browser._builder_names.get('android')))
+
+  def test_constructor_trybot_all_mac(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({
+        'android_nexus4_perf_bisect': 'stuff',
+        'win_8_perf_bisect': 'otherstuff',
+        'mac_perf_bisect': 'otherstuff2',
+        'win_perf_bisect_builder': 'not a trybot',
+    })
+    browser = trybot_browser_finder.PossibleTrybotBrowser(
+        'trybot-all-mac', finder_options)
+    self.assertEquals('all', browser.target_os)
+    # pylint: disable=W0212
+    self.assertEquals(
+        ['mac'],
+        sorted(browser._builder_names))
+    self.assertEquals(
+        ['mac_perf_bisect'],
+        sorted(browser._builder_names.get('mac')))
+
+  def test_constructor_trybot_all_linux(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({
+        'android_nexus4_perf_bisect': 'stuff',
+        'linux_perf_bisect': 'stuff1',
+        'win_8_perf_bisect': 'otherstuff',
+        'mac_perf_bisect': 'otherstuff2',
+        'win_perf_bisect_builder': 'not a trybot',
+    })
+    browser = trybot_browser_finder.PossibleTrybotBrowser(
+        'trybot-all-linux', finder_options)
+    self.assertEquals('all', browser.target_os)
+    # pylint: disable=W0212
+    self.assertEquals(
+        ['linux'],
+        sorted(browser._builder_names))
+    self.assertEquals(
+        ['linux_perf_bisect'],
+        sorted(browser._builder_names.get('linux')))
 
   def test_no_git(self):
     finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({'android_nexus4_perf_bisect': 'stuff'})
     browser = trybot_browser_finder.PossibleTrybotBrowser(
         'trybot-android-nexus4', finder_options)
     self._ExpectProcesses((
@@ -105,6 +241,7 @@
 
   def test_dirty_tree(self):
     finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({'android_nexus4_perf_bisect': 'stuff'})
     browser = trybot_browser_finder.PossibleTrybotBrowser(
         'trybot-android-nexus4', finder_options)
     self._ExpectProcesses((
@@ -120,6 +257,7 @@
 
   def test_no_local_commits(self):
     finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({'android_nexus4_perf_bisect': 'stuff'})
     browser = trybot_browser_finder.PossibleTrybotBrowser(
         'trybot-android-nexus4', finder_options)
     self._ExpectProcesses((
@@ -137,11 +275,13 @@
     self.assertEquals(
         ('No local changes found in chromium or blink trees. '
          'browser=trybot-android-nexus4 argument sends local changes to the '
-         'android_nexus4_perf_bisect perf trybot.\n'),
+         'perf trybot(s): '
+         '[[\'android_nexus4_perf_bisect\']].\n'),
         self.log_output.getvalue())
 
   def test_branch_checkout_fails(self):
     finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({'android_nexus4_perf_bisect': 'stuff'})
     browser = trybot_browser_finder.PossibleTrybotBrowser(
         'trybot-android-nexus4', finder_options)
     self._ExpectProcesses((
@@ -160,10 +300,12 @@
          'fatal: A branch named \'telemetry-try\' already exists.\n'),
         self.log_output.getvalue())
 
-  def _GetConfigForBrowser(self, name, branch, cfg_filename, is_blink=False):
+  def _GetConfigForBrowser(self, name, platform, branch, cfg_filename,
+                           is_blink=False):
     finder_options = browser_options.BrowserFinderOptions()
-    browser = trybot_browser_finder.PossibleTrybotBrowser(name, finder_options)
     bot = '%s_perf_bisect' % name.replace('trybot-', '').replace('-', '_')
+    self._MockTryserverJson({bot: 'stuff'})
+    browser = trybot_browser_finder.PossibleTrybotBrowser(name, finder_options)
     first_processes = ()
     if is_blink:
       first_processes = (
@@ -180,9 +322,10 @@
         (['git', 'checkout', '-b', 'telemetry-tryjob'], (0, None, None)),
         (['git', 'branch', '--set-upstream-to', 'origin/master'],
          (0, None, None)),
-        (['git', 'commit', '-a', '-m', 'bisect config'], (0, None, None)),
+        (['git', 'commit', '-a', '-m', 'bisect config: %s' % platform],
+         (0, None, None)),
         (['git', 'cl', 'upload', '-f', '--bypass-hooks', '-m',
-         'CL for perf tryjob'],
+         'CL for perf tryjob on %s' % platform],
          (0, 'stuff https://codereview.chromium.org/12345 stuff', None)),
         (['git', 'cl', 'try', '-m', 'tryserver.chromium.perf', '-b', bot],
          (0, None, None)),
@@ -201,7 +344,8 @@
 
   def test_config_android(self):
     config = self._GetConfigForBrowser(
-        'trybot-android-nexus4', 'somebranch', 'tools/run-perf-test.cfg')
+        'trybot-android-nexus4', 'android','somebranch',
+        'tools/run-perf-test.cfg')
     self.assertEquals(
         ('config = {\n'
          '  "command": "./tools/perf/run_benchmark '
@@ -214,7 +358,7 @@
 
   def test_config_mac(self):
     config = self._GetConfigForBrowser(
-        'trybot-mac-10-9', 'currentwork', 'tools/run-perf-test.cfg')
+        'trybot-mac-10-9', 'mac', 'currentwork', 'tools/run-perf-test.cfg')
     self.assertEquals(
         ('config = {\n'
          '  "command": "./tools/perf/run_benchmark '
@@ -227,7 +371,7 @@
 
   def test_config_win_x64(self):
     config = self._GetConfigForBrowser(
-        'trybot-win-x64', 'currentwork', 'tools/run-perf-test.cfg')
+        'trybot-win-x64', 'win-x64', 'currentwork', 'tools/run-perf-test.cfg')
     self.assertEquals(
         ('config = {\n'
          '  "command": "python tools\\\\perf\\\\run_benchmark '
@@ -240,7 +384,8 @@
 
   def test_config_blink(self):
     config = self._GetConfigForBrowser(
-        'trybot-mac-10-9', 'blinkbranch', 'Tools/run-perf-test.cfg', True)
+        'trybot-mac-10-9', 'mac', 'blinkbranch',
+        'Tools/run-perf-test.cfg', True)
     self.assertEquals(
         ('config = {\n'
          '  "command": "./tools/perf/run_benchmark '
@@ -250,3 +395,96 @@
          '  "target_arch": "ia32",\n'
          '  "truncate_percent": "0"\n'
          '}'), config)
+
+  def test_update_config_git_commit_tryboterror(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({'android_nexus4_perf_bisect': 'stuff'})
+    browser = trybot_browser_finder.PossibleTrybotBrowser(
+        'trybot-android-nexus4', finder_options)
+    self._ExpectProcesses((
+        (['git', 'commit', '-a', '-m', 'bisect config: android'],
+         (128, 'None', 'commit failed')),
+        (['git', 'cl', 'upload', '-f', '--bypass-hooks', '-m',
+         'CL for perf tryjob on android'],
+         (0, 'stuff https://codereview.chromium.org/12345 stuff', None)),
+        (['git', 'cl', 'try', '-m', 'tryserver.chromium.perf', '-b',
+          'android_nexus4_perf_bisect'], (0, None, None))))
+    self._stubs.sys.argv = [
+      'tools/perf/run_benchmark',
+      '--browser=%s' % browser,
+      'sunspider']
+    cfg_filename = 'tools/run-perf-test.cfg'
+    cfg = StringIO.StringIO()
+    self._stubs.open.files = {cfg_filename: cfg}
+    self.assertRaises(trybot_browser_finder.TrybotError,
+        browser._UpdateConfigAndRunTryjob, 'android', cfg_filename)
+
+  def test_update_config_git_upload_tryboterror(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({'android_nexus4_perf_bisect': 'stuff'})
+    browser = trybot_browser_finder.PossibleTrybotBrowser(
+        'trybot-android-nexus4', finder_options)
+    self._ExpectProcesses((
+        (['git', 'commit', '-a', '-m', 'bisect config: android'],
+         (0, 'None', None)),
+        (['git', 'cl', 'upload', '-f', '--bypass-hooks', '-m',
+         'CL for perf tryjob on android'],
+         (128, None, 'error')),
+        (['git', 'cl', 'try', '-m', 'tryserver.chromium.perf', '-b',
+          'android_nexus4_perf_bisect'], (0, None, None))))
+    self._stubs.sys.argv = [
+      'tools/perf/run_benchmark',
+      '--browser=%s' % browser,
+      'sunspider']
+    cfg_filename = 'tools/run-perf-test.cfg'
+    cfg = StringIO.StringIO()
+    self._stubs.open.files = {cfg_filename: cfg}
+    self.assertRaises(trybot_browser_finder.TrybotError,
+        browser._UpdateConfigAndRunTryjob, 'android', cfg_filename)
+
+  def test_update_config_git_try_tryboterror(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({'android_nexus4_perf_bisect': 'stuff'})
+    browser = trybot_browser_finder.PossibleTrybotBrowser(
+        'trybot-android-nexus4', finder_options)
+    self._ExpectProcesses((
+        (['git', 'commit', '-a', '-m', 'bisect config: android'],
+         (0, 'None', None)),
+        (['git', 'cl', 'upload', '-f', '--bypass-hooks', '-m',
+         'CL for perf tryjob on android'],
+         (0, 'stuff https://codereview.chromium.org/12345 stuff', None)),
+        (['git', 'cl', 'try', '-m', 'tryserver.chromium.perf', '-b',
+          'android_nexus4_perf_bisect'], (128, None, None))))
+    self._stubs.sys.argv = [
+      'tools/perf/run_benchmark',
+      '--browser=%s' % browser,
+      'sunspider']
+    cfg_filename = 'tools/run-perf-test.cfg'
+    cfg = StringIO.StringIO()
+    self._stubs.open.files = {cfg_filename: cfg}
+    self.assertRaises(trybot_browser_finder.TrybotError,
+        browser._UpdateConfigAndRunTryjob, 'android', cfg_filename)
+
+  def test_update_config_git_try(self):
+    finder_options = browser_options.BrowserFinderOptions()
+    self._MockTryserverJson({'android_nexus4_perf_bisect': 'stuff'})
+    browser = trybot_browser_finder.PossibleTrybotBrowser(
+        'trybot-android-nexus4', finder_options)
+    self._ExpectProcesses((
+        (['git', 'commit', '-a', '-m', 'bisect config: android'],
+         (0, 'None', None)),
+        (['git', 'cl', 'upload', '-f', '--bypass-hooks', '-m',
+         'CL for perf tryjob on android'],
+         (0, 'stuff https://codereview.chromium.org/12345 stuff', None)),
+        (['git', 'cl', 'try', '-m', 'tryserver.chromium.perf', '-b',
+          'android_nexus4_perf_bisect'], (0, None, None))))
+    self._stubs.sys.argv = [
+      'tools/perf/run_benchmark',
+      '--browser=%s' % browser,
+      'sunspider']
+    cfg_filename = 'tools/run-perf-test.cfg'
+    cfg = StringIO.StringIO()
+    self._stubs.open.files = {cfg_filename: cfg}
+    self.assertEquals((0, 'https://codereview.chromium.org/12345'),
+        browser._UpdateConfigAndRunTryjob('android', cfg_filename))
+
diff --git a/tools/telemetry/telemetry/core/backends/webdriver/webdriver_browser_backend.py b/tools/telemetry/telemetry/core/backends/webdriver/webdriver_browser_backend.py
index 24deaeff..8e94691 100644
--- a/tools/telemetry/telemetry/core/backends/webdriver/webdriver_browser_backend.py
+++ b/tools/telemetry/telemetry/core/backends/webdriver/webdriver_browser_backend.py
@@ -2,9 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry.core import forwarders
 from telemetry.core.backends import browser_backend
 from telemetry.core.backends.webdriver import webdriver_tab_list_backend
+from telemetry.core import forwarders
 
 
 class WebDriverBrowserBackend(browser_backend.BrowserBackend):
diff --git a/tools/telemetry/telemetry/core/backends/webdriver/webdriver_desktop_browser_finder.py b/tools/telemetry/telemetry/core/backends/webdriver/webdriver_desktop_browser_finder.py
index 0373e1c..aec428b 100644
--- a/tools/telemetry/telemetry/core/backends/webdriver/webdriver_desktop_browser_finder.py
+++ b/tools/telemetry/telemetry/core/backends/webdriver/webdriver_desktop_browser_finder.py
@@ -7,12 +7,12 @@
 import os
 import sys
 
+from telemetry.core.backends.webdriver import webdriver_ie_backend
 from telemetry.core import browser
 from telemetry.core import platform as platform_module
+from telemetry.core.platform import desktop_device
 from telemetry.core import possible_browser
 from telemetry.core import util
-from telemetry.core.backends.webdriver import webdriver_ie_backend
-from telemetry.core.platform import desktop_device
 from telemetry.util import support_binaries
 
 # Try to import the selenium python lib which may be not available.
diff --git a/tools/telemetry/telemetry/core/browser.py b/tools/telemetry/telemetry/core/browser.py
index 52eab75..373f90a 100644
--- a/tools/telemetry/telemetry/core/browser.py
+++ b/tools/telemetry/telemetry/core/browser.py
@@ -4,16 +4,16 @@
 
 import os
 
-from telemetry import decorators
 from telemetry.core import app
+from telemetry.core.backends import browser_backend
 from telemetry.core import browser_credentials
 from telemetry.core import exceptions
 from telemetry.core import extension_dict
 from telemetry.core import local_server
 from telemetry.core import memory_cache_http_server
-from telemetry.core import tab_list
-from telemetry.core.backends import browser_backend
 from telemetry.core.platform import profiling_controller
+from telemetry.core import tab_list
+from telemetry import decorators
 
 
 class Browser(app.App):
diff --git a/tools/telemetry/telemetry/core/browser_credentials.py b/tools/telemetry/telemetry/core/browser_credentials.py
index 013a419..39fbc6d 100644
--- a/tools/telemetry/telemetry/core/browser_credentials.py
+++ b/tools/telemetry/telemetry/core/browser_credentials.py
@@ -6,10 +6,10 @@
 import logging
 import os
 
-from telemetry.core import util
 from telemetry.core.backends import codepen_credentials_backend
 from telemetry.core.backends import facebook_credentials_backend
 from telemetry.core.backends import google_credentials_backend
+from telemetry.core import util
 from telemetry.unittest_util import options_for_unittests
 
 
diff --git a/tools/telemetry/telemetry/core/browser_finder.py b/tools/telemetry/telemetry/core/browser_finder.py
index b863d6e..d1abbc28 100644
--- a/tools/telemetry/telemetry/core/browser_finder.py
+++ b/tools/telemetry/telemetry/core/browser_finder.py
@@ -7,15 +7,15 @@
 import logging
 import operator
 
-from telemetry import decorators
-from telemetry.core import browser_finder_exceptions
-from telemetry.core import device_finder
 from telemetry.core.backends.chrome import android_browser_finder
 from telemetry.core.backends.chrome import cros_browser_finder
 from telemetry.core.backends.chrome import desktop_browser_finder
 from telemetry.core.backends.chrome import ios_browser_finder
 from telemetry.core.backends.remote import trybot_browser_finder
 from telemetry.core.backends.webdriver import webdriver_desktop_browser_finder
+from telemetry.core import browser_finder_exceptions
+from telemetry.core import device_finder
+from telemetry import decorators
 
 BROWSER_FINDERS = [
   desktop_browser_finder,
diff --git a/tools/telemetry/telemetry/core/browser_info.py b/tools/telemetry/telemetry/core/browser_info.py
index 711a17b..4a99c6b 100644
--- a/tools/telemetry/telemetry/core/browser_info.py
+++ b/tools/telemetry/telemetry/core/browser_info.py
@@ -36,3 +36,12 @@
       tab = self._browser.tabs[0]
       result = tab.EvaluateJavaScript(_check_webgl_supported_script)
     return result
+
+  def HasFlingGestureSupport(self):
+    # Synthetic fling gestures weren't properly tracked by telemetry until
+    # Chromium branch number 2339 (see crrev.com/1003023002).
+    # TODO(jdduke): Resolve lack of branch number support for content_shell
+    # targets, see crbug.com/470273.
+    branch_num = (
+        self._browser._browser_backend.devtools_client.GetChromeBranchNumber())
+    return branch_num >= 2339
diff --git a/tools/telemetry/telemetry/core/browser_options.py b/tools/telemetry/telemetry/core/browser_options.py
index 69d0ae6..9bacf1e 100644
--- a/tools/telemetry/telemetry/core/browser_options.py
+++ b/tools/telemetry/telemetry/core/browser_options.py
@@ -14,10 +14,10 @@
 from telemetry.core import browser_finder_exceptions
 from telemetry.core import device_finder
 from telemetry.core import platform
+from telemetry.core.platform.profiler import profiler_finder
 from telemetry.core import profile_types
 from telemetry.core import util
 from telemetry.core import wpr_modes
-from telemetry.core.platform.profiler import profiler_finder
 
 util.AddDirToPythonPath(
     util.GetChromiumSrcDir(), 'third_party', 'webpagereplay')
diff --git a/tools/telemetry/telemetry/core/browser_unittest.py b/tools/telemetry/telemetry/core/browser_unittest.py
index 9d4ed0ac..7048502 100644
--- a/tools/telemetry/telemetry/core/browser_unittest.py
+++ b/tools/telemetry/telemetry/core/browser_unittest.py
@@ -8,14 +8,14 @@
 import tempfile
 import unittest
 
-from telemetry import decorators
 from telemetry.core import browser_finder
 from telemetry.core import gpu_device
 from telemetry.core import gpu_info
-from telemetry.core import system_info
-from telemetry.core import util
 from telemetry.core.platform import tracing_category_filter
 from telemetry.core.platform import tracing_options
+from telemetry.core import system_info
+from telemetry.core import util
+from telemetry import decorators
 from telemetry.unittest_util import browser_test_case
 from telemetry.unittest_util import options_for_unittests
 from telemetry.util import path
diff --git a/tools/telemetry/telemetry/core/discover.py b/tools/telemetry/telemetry/core/discover.py
index 0f2f33f..f612bfe 100644
--- a/tools/telemetry/telemetry/core/discover.py
+++ b/tools/telemetry/telemetry/core/discover.py
@@ -7,8 +7,8 @@
 import os
 import re
 
-from telemetry import decorators
 from telemetry.core import camel_case
+from telemetry import decorators
 
 
 @decorators.Cache
diff --git a/tools/telemetry/telemetry/core/exceptions.py b/tools/telemetry/telemetry/core/exceptions.py
index 6b13129f..cfb4b31 100644
--- a/tools/telemetry/telemetry/core/exceptions.py
+++ b/tools/telemetry/telemetry/core/exceptions.py
@@ -119,3 +119,6 @@
 
 class PackageDetectionError(Error):
   """ Represents an error when parsing an Android APK's package. """
+
+class AndroidDeviceParsingError(Error):
+  """Represents an error when parsing output from an android device"""
diff --git a/tools/telemetry/telemetry/core/forwarders/android_forwarder.py b/tools/telemetry/telemetry/core/forwarders/android_forwarder.py
index 19f5132..b2f58e0 100644
--- a/tools/telemetry/telemetry/core/forwarders/android_forwarder.py
+++ b/tools/telemetry/telemetry/core/forwarders/android_forwarder.py
@@ -9,10 +9,10 @@
 import struct
 import subprocess
 
+from telemetry.core.backends import adb_commands
 from telemetry.core import forwarders
 from telemetry.core import platform
 from telemetry.core import util
-from telemetry.core.backends import adb_commands
 from telemetry.util import support_binaries
 
 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
diff --git a/tools/telemetry/telemetry/core/forwarders/cros_forwarder.py b/tools/telemetry/telemetry/core/forwarders/cros_forwarder.py
index 6757504..636b16c 100644
--- a/tools/telemetry/telemetry/core/forwarders/cros_forwarder.py
+++ b/tools/telemetry/telemetry/core/forwarders/cros_forwarder.py
@@ -6,8 +6,8 @@
 import subprocess
 
 from telemetry.core import forwarders
-from telemetry.core import util
 from telemetry.core.forwarders import do_nothing_forwarder
+from telemetry.core import util
 
 
 class CrOsForwarderFactory(forwarders.ForwarderFactory):
diff --git a/tools/telemetry/telemetry/core/memory_cache_http_server.py b/tools/telemetry/telemetry/core/memory_cache_http_server.py
index eaa812d..1292804 100644
--- a/tools/telemetry/telemetry/core/memory_cache_http_server.py
+++ b/tools/telemetry/telemetry/core/memory_cache_http_server.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import BaseHTTPServer
+from collections import namedtuple
 import errno
 import gzip
 import mimetypes
@@ -13,7 +14,6 @@
 import StringIO
 import sys
 import urlparse
-from collections import namedtuple
 
 from telemetry.core import local_server
 
diff --git a/tools/telemetry/telemetry/core/platform/__init__.py b/tools/telemetry/telemetry/core/platform/__init__.py
index 1421d60..039910ba 100644
--- a/tools/telemetry/telemetry/core/platform/__init__.py
+++ b/tools/telemetry/telemetry/core/platform/__init__.py
@@ -6,10 +6,10 @@
 import sys
 
 from telemetry.core import discover
-from telemetry.core import util
 from telemetry.core.platform import network_controller
 from telemetry.core.platform import platform_backend as platform_backend_module
 from telemetry.core.platform import tracing_controller
+from telemetry.core import util
 
 
 _host_platform = None
diff --git a/tools/telemetry/telemetry/core/platform/android_action_runner.py b/tools/telemetry/telemetry/core/platform/android_action_runner.py
index aa8eda6..8728c9f 100644
--- a/tools/telemetry/telemetry/core/platform/android_action_runner.py
+++ b/tools/telemetry/telemetry/core/platform/android_action_runner.py
@@ -2,8 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import logging
 import time
 
+from telemetry.core import util
 
 class ActionNotSupported(Exception):
   pass
@@ -113,3 +115,55 @@
       dy: Change in the y coordinate due to move.
     """
     self._platform_backend.adb.RunShellCommand('input roll %s %s' % (dx, dy))
+
+  def TurnScreenOn(self):
+    """If device screen is off, turn screen on.
+    If the screen is already on, this method returns immediately.
+
+    Raises:
+      Timeout: If the screen is off and device fails to turn screen on.
+    """
+    if not self._platform_backend.IsScreenOn():
+      self._platform_backend.adb.RunShellCommand('input keyevent 26')
+    else:
+      logging.warning('Screen on when expected off.')
+      return
+
+    util.WaitFor(self._platform_backend.IsScreenOn, 5)
+
+  def TurnScreenOff(self):
+    """If device screen is on, turn screen off.
+    If the screen is already off, this method returns immediately.
+
+    Raises:
+      Timeout: If the screen is on and device fails to turn screen off.
+    """
+    def is_screen_off():
+      return not self._platform_backend.IsScreenOn()
+
+    if self._platform_backend.IsScreenOn():
+      self._platform_backend.adb.RunShellCommand(
+          'input keyevent 26')
+    else:
+      logging.warning('Screen off when expected on.')
+      return
+
+    util.WaitFor(is_screen_off, 5)
+
+  def UnlockScreen(self):
+    """If device screen is locked, unlocks it.
+    If the device is not locked, this method returns immediately.
+
+    Raises:
+      Timeout: If device fails to unlock screen.
+    """
+    def is_screen_unlocked():
+      return not self._platform_backend.IsScreenLocked()
+
+    if self._platform_backend.IsScreenLocked():
+      self._platform_backend.adb.RunShellCommand('input keyevent 82')
+    else:
+      logging.warning('Screen not locked when expected.')
+      return
+
+    util.WaitFor(is_screen_unlocked, 5)
diff --git a/tools/telemetry/telemetry/core/platform/android_device.py b/tools/telemetry/telemetry/core/platform/android_device.py
index e04f2e0..c7dc378 100644
--- a/tools/telemetry/telemetry/core/platform/android_device.py
+++ b/tools/telemetry/telemetry/core/platform/android_device.py
@@ -7,10 +7,10 @@
 import subprocess
 import sys
 
-from telemetry.core import util
 from telemetry.core.backends import adb_commands
 from telemetry.core.platform import device
 from telemetry.core.platform.profiler import monsoon
+from telemetry.core import util
 
 
 class AndroidDevice(device.Device):
diff --git a/tools/telemetry/telemetry/core/platform/android_platform.py b/tools/telemetry/telemetry/core/platform/android_platform.py
index a5d40b7..55699e8b 100644
--- a/tools/telemetry/telemetry/core/platform/android_platform.py
+++ b/tools/telemetry/telemetry/core/platform/android_platform.py
@@ -4,8 +4,8 @@
 
 
 from telemetry.core import android_app
-from telemetry.core import platform
 from telemetry.core.backends import android_app_backend
+from telemetry.core import platform
 from telemetry.core.platform import android_action_runner
 
 class AndroidPlatform(platform.Platform):
diff --git a/tools/telemetry/telemetry/core/platform/android_platform_backend.py b/tools/telemetry/telemetry/core/platform/android_platform_backend.py
index 9fb0ec8c..9230d15 100644
--- a/tools/telemetry/telemetry/core/platform/android_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/android_platform_backend.py
@@ -10,11 +10,8 @@
 import tempfile
 import time
 
-from telemetry import decorators
-from telemetry.core import exceptions
-from telemetry.core import util
-from telemetry.core import video
 from telemetry.core.backends import adb_commands
+from telemetry.core import exceptions
 from telemetry.core.forwarders import android_forwarder
 from telemetry.core import platform
 from telemetry.core.platform import android_device
@@ -26,6 +23,9 @@
 from telemetry.core.platform.power_monitor import monsoon_power_monitor
 from telemetry.core.platform.power_monitor import power_monitor_controller
 from telemetry.core.platform.profiler import android_prebuilt_profiler_helper
+from telemetry.core import util
+from telemetry.core import video
+from telemetry import decorators
 from telemetry.util import exception_formatter
 from telemetry.util import external_modules
 
@@ -37,11 +37,11 @@
 
 # Get build/android scripts into our path.
 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
-from pylib import screenshot  # pylint: disable=F0401
 from pylib.device import device_errors  # pylint: disable=F0401
 from pylib.perf import cache_control  # pylint: disable=F0401
 from pylib.perf import perf_control  # pylint: disable=F0401
 from pylib.perf import thermal_throttle  # pylint: disable=F0401
+from pylib import screenshot  # pylint: disable=F0401
 
 try:
   from pylib.perf import surface_stats_collector  # pylint: disable=F0401
@@ -636,6 +636,70 @@
                                        stdout=subprocess.PIPE).communicate()[0])
     return ret
 
+  @staticmethod
+  def _IsScreenOn(input_methods):
+    """Parser method of IsScreenOn()
+
+    Args:
+      input_methods: Output from dumpsys input_methods
+
+    Returns:
+      boolean: True if screen is on, false if screen is off.
+
+    Raises:
+      ValueError: An unknown value is found for the screen state.
+      AndroidDeviceParsingError: Error in detecting screen state.
+    """
+    for line in input_methods:
+      if 'mScreenOn' in line or 'mInteractive' in line:
+        for pair in line.strip().split(' '):
+          key, value = pair.split('=', 1)
+          if key == 'mScreenOn' or key == 'mInteractive':
+            if value == 'true':
+              return True
+            elif value == 'false':
+              return False
+            else:
+              raise ValueError('Unknown value for %s: %s' % (key, value))
+    raise exceptions.AndroidDeviceParsingError(str(input_methods))
+
+  def IsScreenOn(self):
+    """Determines if device screen is on."""
+    input_methods = self._device.RunShellCommand('dumpsys input_method')
+    return self._IsScreenOn(input_methods)
+
+  @staticmethod
+  def _IsScreenLocked(input_methods):
+    """Parser method for IsScreenLocked()
+
+    Args:
+      input_methods: Output from dumpsys input_methods
+
+    Returns:
+      boolean: True if screen is locked, false if screen is not locked.
+
+    Raises:
+      ValueError: An unknown value is found for the screen lock state.
+      AndroidDeviceParsingError: Error in detecting screen state.
+
+    """
+    for line in input_methods:
+      if 'mHasBeenInactive' in line:
+        for pair in line.strip().split(' '):
+          key, value = pair.split('=', 1)
+          if key == 'mHasBeenInactive':
+            if value == 'true':
+              return True
+            elif value == 'false':
+              return False
+            else:
+              raise ValueError('Unknown value for %s: %s' % (key, value))
+    raise exceptions.AndroidDeviceParsingError(str(input_methods))
+
+  def IsScreenLocked(self):
+    """Determines if device screen is locked."""
+    input_methods = self._device.RunShellCommand('dumpsys input_method')
+    return self._IsScreenLocked(input_methods)
 
 def _FixPossibleAdbInstability():
   """Host side workaround for crbug.com/268450 (adb instability).
diff --git a/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py
index 4d19ff28..2a08ea39 100644
--- a/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py
@@ -4,11 +4,11 @@
 
 import unittest
 
-from telemetry import decorators
 from telemetry.core.platform import android_device
 from telemetry.core.platform import android_platform_backend
-from telemetry.unittest_util import system_stub
+from telemetry import decorators
 from telemetry.unittest_util import options_for_unittests
+from telemetry.unittest_util import system_stub
 
 
 class AndroidPlatformBackendTest(unittest.TestCase):
@@ -85,3 +85,39 @@
         android_device.AndroidDevice('success'), self._options)
     backend.InstallTestCa()
     self.assertTrue(backend.is_test_ca_installed)
+
+  def testIsScreenLockedTrue(self):
+    test_input = ['a=b', 'mHasBeenInactive=true']
+    backend = android_platform_backend.AndroidPlatformBackend(
+        android_device.AndroidDevice('success'), self._options)
+    self.assertTrue(backend._IsScreenLocked(test_input))
+
+  def testIsScreenLockedFalse(self):
+    test_input = ['a=b', 'mHasBeenInactive=false']
+    backend = android_platform_backend.AndroidPlatformBackend(
+        android_device.AndroidDevice('success'), self._options)
+    self.assertFalse(backend._IsScreenLocked(test_input))
+
+  def testIsScreenOnmScreenOnTrue(self):
+    test_input = ['a=b', 'mScreenOn=true']
+    backend = android_platform_backend.AndroidPlatformBackend(
+        android_device.AndroidDevice('success'), self._options)
+    self.assertTrue(backend._IsScreenOn(test_input))
+
+  def testIsScreenOnmScreenOnFalse(self):
+    test_input = ['a=b', 'mScreenOn=false']
+    backend = android_platform_backend.AndroidPlatformBackend(
+        android_device.AndroidDevice('success'), self._options)
+    self.assertFalse(backend._IsScreenOn(test_input))
+
+  def testIsScreenOnmInteractiveTrue(self):
+    test_input = ['a=b', 'mInteractive=true']
+    backend = android_platform_backend.AndroidPlatformBackend(
+        android_device.AndroidDevice('success'), self._options)
+    self.assertTrue(backend._IsScreenOn(test_input))
+
+  def testIsScreenOnmInteractiveFalse(self):
+    test_input = ['a=b', 'mInteractive=false']
+    backend = android_platform_backend.AndroidPlatformBackend(
+        android_device.AndroidDevice('success'), self._options)
+    self.assertFalse(backend._IsScreenOn(test_input))
diff --git a/tools/telemetry/telemetry/core/platform/cros_interface.py b/tools/telemetry/telemetry/core/platform/cros_interface.py
index 1e84502..a3c3316 100644
--- a/tools/telemetry/telemetry/core/platform/cros_interface.py
+++ b/tools/telemetry/telemetry/core/platform/cros_interface.py
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 """A wrapper around ssh for common operations on a CrOS-based device"""
 import logging
-import re
 import os
+import re
 import shutil
 import socket
 import stat
diff --git a/tools/telemetry/telemetry/core/platform/cros_interface_unittest.py b/tools/telemetry/telemetry/core/platform/cros_interface_unittest.py
index eda890e..0b4d911 100644
--- a/tools/telemetry/telemetry/core/platform/cros_interface_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/cros_interface_unittest.py
@@ -10,10 +10,10 @@
 import tempfile
 import unittest
 
-from telemetry import decorators
 from telemetry.core import forwarders
-from telemetry.core.platform import cros_interface
 from telemetry.core.forwarders import cros_forwarder
+from telemetry.core.platform import cros_interface
+from telemetry import decorators
 from telemetry.unittest_util import options_for_unittests
 
 
@@ -151,10 +151,10 @@
   # that need to be run locally based on the platform of the system browser.
   @decorators.Enabled('linux')
   def testEscapeCmdArguments(self):
-    ''' Commands and their arguments that are executed through the cros
+    """Commands and their arguments that are executed through the cros
     interface should follow bash syntax. This test needs to run on remotely
     and locally on the device to check for consistency.
-    '''
+    """
     options = options_for_unittests.GetCopy()
     with cros_interface.CrOSInterface(
         options.cros_remote, options.cros_remote_ssh_port,
diff --git a/tools/telemetry/telemetry/core/platform/cros_platform_backend.py b/tools/telemetry/telemetry/core/platform/cros_platform_backend.py
index cb7f69c..971743d 100644
--- a/tools/telemetry/telemetry/core/platform/cros_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/cros_platform_backend.py
@@ -2,14 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry.core import platform
-from telemetry.core import util
 from telemetry.core.forwarders import cros_forwarder
+from telemetry.core import platform
 from telemetry.core.platform import cros_device
 from telemetry.core.platform import cros_interface
 from telemetry.core.platform import linux_based_platform_backend
-from telemetry.core.platform import ps_util
 from telemetry.core.platform.power_monitor import cros_power_monitor
+from telemetry.core.platform import ps_util
+from telemetry.core import util
 
 
 class CrosPlatformBackend(
diff --git a/tools/telemetry/telemetry/core/platform/linux_based_platform_backend.py b/tools/telemetry/telemetry/core/platform/linux_based_platform_backend.py
index 1a1c2f8f..6da16284 100644
--- a/tools/telemetry/telemetry/core/platform/linux_based_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/linux_based_platform_backend.py
@@ -7,9 +7,9 @@
 except ImportError:
   resource = None  # Not available on all platforms
 
-from telemetry import decorators
 from telemetry.core import exceptions
 from telemetry.core.platform import platform_backend
+from telemetry import decorators
 
 
 class LinuxBasedPlatformBackend(platform_backend.PlatformBackend):
diff --git a/tools/telemetry/telemetry/core/platform/linux_based_platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/linux_based_platform_backend_unittest.py
index d227e30..6e706a5 100644
--- a/tools/telemetry/telemetry/core/platform/linux_based_platform_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/linux_based_platform_backend_unittest.py
@@ -5,8 +5,8 @@
 import os
 import unittest
 
-from telemetry.core import util
 from telemetry.core.platform import linux_based_platform_backend
+from telemetry.core import util
 
 
 class TestBackend(linux_based_platform_backend.LinuxBasedPlatformBackend):
diff --git a/tools/telemetry/telemetry/core/platform/linux_platform_backend.py b/tools/telemetry/telemetry/core/platform/linux_platform_backend.py
index 5c52734..6173325 100644
--- a/tools/telemetry/telemetry/core/platform/linux_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/linux_platform_backend.py
@@ -8,12 +8,12 @@
 import subprocess
 import sys
 
-from telemetry import decorators
-from telemetry.core import util
 from telemetry.core.platform import linux_based_platform_backend
 from telemetry.core.platform import platform_backend
 from telemetry.core.platform import posix_platform_backend
 from telemetry.core.platform.power_monitor import msr_power_monitor
+from telemetry.core import util
+from telemetry import decorators
 from telemetry.util import cloud_storage
 from telemetry.util import support_binaries
 
diff --git a/tools/telemetry/telemetry/core/platform/linux_platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/linux_platform_backend_unittest.py
index 0072ec2a..8727779 100644
--- a/tools/telemetry/telemetry/core/platform/linux_platform_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/linux_platform_backend_unittest.py
@@ -5,9 +5,9 @@
 import os
 import unittest
 
-from telemetry import decorators
-from telemetry.core import util
 from telemetry.core.platform import linux_platform_backend
+from telemetry.core import util
+from telemetry import decorators
 
 class TestBackend(
     linux_platform_backend.LinuxPlatformBackend):
diff --git a/tools/telemetry/telemetry/core/platform/mac_platform_backend.py b/tools/telemetry/telemetry/core/platform/mac_platform_backend.py
index ceadc14..b50f9bd 100644
--- a/tools/telemetry/telemetry/core/platform/mac_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/mac_platform_backend.py
@@ -8,11 +8,11 @@
 import sys
 import time
 
-from telemetry import decorators
 from telemetry.core.platform import platform_backend
 from telemetry.core.platform import posix_platform_backend
-from telemetry.core.platform import process_statistic_timeline_data
 from telemetry.core.platform.power_monitor import powermetrics_power_monitor
+from telemetry.core.platform import process_statistic_timeline_data
+from telemetry import decorators
 
 try:
   import resource  # pylint: disable=F0401
diff --git a/tools/telemetry/telemetry/core/platform/mac_platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/mac_platform_backend_unittest.py
index 03c5ee9..12e7632 100644
--- a/tools/telemetry/telemetry/core/platform/mac_platform_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/mac_platform_backend_unittest.py
@@ -5,9 +5,9 @@
 import os
 import unittest
 
-from telemetry import decorators
 from telemetry.core import platform as platform_module
 from telemetry.core.platform import platform_backend
+from telemetry import decorators
 
 
 class MacPlatformBackendTest(unittest.TestCase):
diff --git a/tools/telemetry/telemetry/core/platform/network_controller_backend_unittest.py b/tools/telemetry/telemetry/core/platform/network_controller_backend_unittest.py
index 504ba33..98b7ea5 100644
--- a/tools/telemetry/telemetry/core/platform/network_controller_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/network_controller_backend_unittest.py
@@ -6,8 +6,8 @@
 import unittest
 
 from telemetry.core import forwarders
-from telemetry.core import wpr_modes
 from telemetry.core.platform import network_controller_backend
+from telemetry.core import wpr_modes
 
 
 class FakePlatformBackend(object):
diff --git a/tools/telemetry/telemetry/core/platform/platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/platform_backend_unittest.py
index 3b79cfb..751e9f19 100644
--- a/tools/telemetry/telemetry/core/platform/platform_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/platform_backend_unittest.py
@@ -6,8 +6,8 @@
 import time
 import unittest
 
-from telemetry import decorators
 from telemetry.core import platform as platform_module
+from telemetry import decorators
 
 
 class PlatformBackendTest(unittest.TestCase):
diff --git a/tools/telemetry/telemetry/core/platform/posix_platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/posix_platform_backend_unittest.py
index e1d6f2bc..841e68f 100644
--- a/tools/telemetry/telemetry/core/platform/posix_platform_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/posix_platform_backend_unittest.py
@@ -5,9 +5,9 @@
 import sys
 import unittest
 
-from telemetry import decorators
 from telemetry.core import platform as platform_module
 from telemetry.core.platform import posix_platform_backend
+from telemetry import decorators
 
 
 class TestBackend(posix_platform_backend.PosixPlatformBackend):
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/android_ds2784_power_monitor.py b/tools/telemetry/telemetry/core/platform/power_monitor/android_ds2784_power_monitor.py
index 8bfef440..25c375b 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/android_ds2784_power_monitor.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/android_ds2784_power_monitor.py
@@ -5,9 +5,9 @@
 import logging
 import os
 
-from telemetry import decorators
 from telemetry.core.platform.power_monitor import sysfs_power_monitor
 from telemetry.core.platform.profiler import android_prebuilt_profiler_helper
+from telemetry import decorators
 
 
 SAMPLE_RATE_HZ = 2 # The data is collected from the ds2784 fuel gauge chip
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/android_dumpsys_power_monitor.py b/tools/telemetry/telemetry/core/platform/power_monitor/android_dumpsys_power_monitor.py
index 6d74b6a1..5e7f3079 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/android_dumpsys_power_monitor.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/android_dumpsys_power_monitor.py
@@ -2,9 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from collections import defaultdict
 import csv
 import logging
-from collections import defaultdict
 
 from telemetry.core.platform.power_monitor import sysfs_power_monitor
 
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/android_temperature_monitor.py b/tools/telemetry/telemetry/core/platform/power_monitor/android_temperature_monitor.py
index 2b61e628..11e1c3f 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/android_temperature_monitor.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/android_temperature_monitor.py
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry.core import util
 from telemetry.core.platform import power_monitor
+from telemetry.core import util
 
 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
 try:
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/cros_power_monitor.py b/tools/telemetry/telemetry/core/platform/power_monitor/cros_power_monitor.py
index 494792c..26d85a2 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/cros_power_monitor.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/cros_power_monitor.py
@@ -5,8 +5,8 @@
 import collections
 import re
 
-from telemetry import decorators
 from telemetry.core.platform.power_monitor import sysfs_power_monitor
+from telemetry import decorators
 
 
 class CrosPowerMonitor(sysfs_power_monitor.SysfsPowerMonitor):
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor.py b/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor.py
index 8564a0da..5a5de1e 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor.py
@@ -13,9 +13,9 @@
 import tempfile
 import zipfile
 
-from telemetry import decorators
 from telemetry.core.platform import platform_backend
 from telemetry.core.platform import power_monitor
+from telemetry import decorators
 from telemetry.util import cloud_storage
 from telemetry.util import path
 from telemetry.util import statistics
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor_unittest.py b/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor_unittest.py
index 3bbbbbbb..180f1767 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/ippet_power_monitor_unittest.py
@@ -5,9 +5,9 @@
 import logging
 import unittest
 
-from telemetry import decorators
-from telemetry.core.platform import win_platform_backend
 from telemetry.core.platform.power_monitor import ippet_power_monitor
+from telemetry.core.platform import win_platform_backend
+from telemetry import decorators
 
 
 class IppetPowerMonitorTest(unittest.TestCase):
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor.py b/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor.py
index c4ecd85..90657023 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor.py
@@ -6,8 +6,8 @@
 import platform
 import re
 
-from telemetry import decorators
 from telemetry.core.platform import power_monitor
+from telemetry import decorators
 
 
 MSR_RAPL_POWER_UNIT = 0x606
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor_unittest.py b/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor_unittest.py
index f3408a75..68f925e2 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor_unittest.py
@@ -6,9 +6,9 @@
 import time
 import unittest
 
-from telemetry import decorators
-from telemetry.core.platform import win_platform_backend
 from telemetry.core.platform.power_monitor import msr_power_monitor
+from telemetry.core.platform import win_platform_backend
+from telemetry import decorators
 
 
 class MsrPowerMonitorTest(unittest.TestCase):
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/powermetrics_power_monitor.py b/tools/telemetry/telemetry/core/platform/power_monitor/powermetrics_power_monitor.py
index b95ab26..9e8f7de 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/powermetrics_power_monitor.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/powermetrics_power_monitor.py
@@ -10,10 +10,10 @@
 import tempfile
 import xml.parsers.expat
 
-from telemetry import decorators
-from telemetry.core import util
 from telemetry.core.platform import platform_backend
 from telemetry.core.platform import power_monitor
+from telemetry.core import util
+from telemetry import decorators
 
 
 class PowerMetricsPowerMonitor(power_monitor.PowerMonitor):
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/powermetrics_power_monitor_unittest.py b/tools/telemetry/telemetry/core/platform/power_monitor/powermetrics_power_monitor_unittest.py
index 1c83a90..cef4cac 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/powermetrics_power_monitor_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/powermetrics_power_monitor_unittest.py
@@ -6,11 +6,11 @@
 import os
 import unittest
 
-from telemetry import decorators
-from telemetry.core import util
 from telemetry.core.platform import mac_platform_backend
 from telemetry.core.platform import platform_backend
 from telemetry.core.platform.power_monitor import powermetrics_power_monitor
+from telemetry.core import util
+from telemetry import decorators
 
 
 def _parsePowerMetricsDataFromTestFile(output_file):
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/sysfs_power_monitor.py b/tools/telemetry/telemetry/core/platform/power_monitor/sysfs_power_monitor.py
index 73732fd66..29dcf28 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/sysfs_power_monitor.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/sysfs_power_monitor.py
@@ -7,8 +7,8 @@
 import os
 import re
 
-from telemetry import decorators
 from telemetry.core.platform import power_monitor
+from telemetry import decorators
 
 
 CPU_PATH = '/sys/devices/system/cpu/'
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/sysfs_power_monitor_unittest.py b/tools/telemetry/telemetry/core/platform/power_monitor/sysfs_power_monitor_unittest.py
index 6ba2cc8..baa9671 100644
--- a/tools/telemetry/telemetry/core/platform/power_monitor/sysfs_power_monitor_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/power_monitor/sysfs_power_monitor_unittest.py
@@ -4,8 +4,8 @@
 
 import unittest
 
-from telemetry.core.platform.power_monitor import sysfs_power_monitor
 from telemetry.core.platform import android_platform_backend
+from telemetry.core.platform.power_monitor import sysfs_power_monitor
 
 
 class SysfsPowerMonitorMonitorTest(unittest.TestCase):
diff --git a/tools/telemetry/telemetry/core/platform/profiler/android_profiling_helper.py b/tools/telemetry/telemetry/core/platform/profiler/android_profiling_helper.py
index d8736079..f38664b26 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/android_profiling_helper.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/android_profiling_helper.py
@@ -11,10 +11,10 @@
 import shutil
 import subprocess
 
-from telemetry import decorators
 from telemetry.core import platform as telemetry_platform
-from telemetry.core import util
 from telemetry.core.platform.profiler import android_prebuilt_profiler_helper
+from telemetry.core import util
+from telemetry import decorators
 from telemetry.util import support_binaries
 
 try:
diff --git a/tools/telemetry/telemetry/core/platform/profiler/android_profiling_helper_unittest.py b/tools/telemetry/telemetry/core/platform/profiler/android_profiling_helper_unittest.py
index fd31de3..94b0864 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/android_profiling_helper_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/android_profiling_helper_unittest.py
@@ -9,9 +9,9 @@
 import tempfile
 import unittest
 
-from telemetry import decorators
-from telemetry.core import util
 from telemetry.core.platform.profiler import android_profiling_helper
+from telemetry.core import util
+from telemetry import decorators
 from telemetry.unittest_util import simple_mock
 from telemetry.unittest_util import tab_test_case
 
diff --git a/tools/telemetry/telemetry/core/platform/profiler/android_screen_recorder_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/android_screen_recorder_profiler.py
index 2874c75..5e3aad8 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/android_screen_recorder_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/android_screen_recorder_profiler.py
@@ -5,9 +5,9 @@
 import os
 import subprocess
 
-from telemetry.core import util
 from telemetry.core.backends.chrome import android_browser_finder
 from telemetry.core.platform import profiler
+from telemetry.core import util
 
 
 class AndroidScreenRecordingProfiler(profiler.Profiler):
diff --git a/tools/telemetry/telemetry/core/platform/profiler/android_systrace_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/android_systrace_profiler.py
index 81a3ffe..a2d0bc3 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/android_systrace_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/android_systrace_profiler.py
@@ -7,10 +7,10 @@
 import subprocess
 import zipfile
 
-from telemetry.core import util
 from telemetry.core.backends.chrome import android_browser_finder
 from telemetry.core.platform import profiler
 from telemetry.core.platform import tracing_options
+from telemetry.core import util
 from telemetry.timeline import trace_data as trace_data_module
 
 _SYSTRACE_CATEGORIES = [
diff --git a/tools/telemetry/telemetry/core/platform/profiler/android_systrace_profiler_unittest.py b/tools/telemetry/telemetry/core/platform/profiler/android_systrace_profiler_unittest.py
index 1af31ff..e2249f3 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/android_systrace_profiler_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/android_systrace_profiler_unittest.py
@@ -6,8 +6,8 @@
 import tempfile
 import zipfile
 
-from telemetry import decorators
 from telemetry.core.platform.profiler import android_systrace_profiler
+from telemetry import decorators
 from telemetry.unittest_util import tab_test_case
 
 
diff --git a/tools/telemetry/telemetry/core/platform/profiler/android_traceview_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/android_traceview_profiler.py
index 5e09f07..5e24526 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/android_traceview_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/android_traceview_profiler.py
@@ -4,9 +4,9 @@
 
 import os
 
-from telemetry.core import util
 from telemetry.core.backends.chrome import android_browser_finder
 from telemetry.core.platform import profiler
+from telemetry.core import util
 
 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
 try:
diff --git a/tools/telemetry/telemetry/core/platform/profiler/iprofiler_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/iprofiler_profiler.py
index 77769f5..177c6d2 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/iprofiler_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/iprofiler_profiler.py
@@ -7,8 +7,8 @@
 import sys
 
 from telemetry.core import exceptions
-from telemetry.core import util
 from telemetry.core.platform import profiler
+from telemetry.core import util
 
 # pexpect is not available on all platforms so use the third_party version.
 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'third_party', 'pexpect')
diff --git a/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py
index e388b4c..b6693bb 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py
@@ -6,9 +6,9 @@
 import subprocess
 import threading
 
-from telemetry.core import util
 from telemetry.core.backends.chrome import android_browser_finder
 from telemetry.core.platform import profiler
+from telemetry.core import util
 
 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
 try:
diff --git a/tools/telemetry/telemetry/core/platform/profiler/oomkiller_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/oomkiller_profiler.py
index 172b888..be4643d 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/oomkiller_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/oomkiller_profiler.py
@@ -4,9 +4,9 @@
 
 import os
 
-from telemetry.core import util
 from telemetry.core.backends.chrome import android_browser_finder
 from telemetry.core.platform import profiler
+from telemetry.core import util
 from telemetry.util import support_binaries
 
 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
diff --git a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py
index 7738748..5065ad63 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py
@@ -13,9 +13,9 @@
 from pylib.device import device_errors  # pylint: disable=F0401
 
 from telemetry.core import platform
-from telemetry.core import util
 from telemetry.core.platform import profiler
 from telemetry.core.platform.profiler import android_profiling_helper
+from telemetry.core import util
 from telemetry.util import support_binaries
 
 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
diff --git a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler_unittest.py b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler_unittest.py
index 05dc5035..ed0b4fc 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler_unittest.py
@@ -5,8 +5,8 @@
 import os
 import unittest
 
-from telemetry.core import util
 from telemetry.core.platform.profiler import perf_profiler
+from telemetry.core import util
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import simple_mock
 
diff --git a/tools/telemetry/telemetry/core/platform/profiler/profiler_finder.py b/tools/telemetry/telemetry/core/platform/profiler/profiler_finder.py
index ab95dc83..dd44711 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/profiler_finder.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/profiler_finder.py
@@ -5,8 +5,8 @@
 import os
 
 from telemetry.core import discover
-from telemetry.core import util
 from telemetry.core.platform import profiler
+from telemetry.core import util
 
 
 def _DiscoverProfilers():
diff --git a/tools/telemetry/telemetry/core/platform/profiler/sample_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/sample_profiler.py
index bc09c4f6..87d73c75 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/sample_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/sample_profiler.py
@@ -8,8 +8,8 @@
 import tempfile
 
 from telemetry.core import exceptions
-from telemetry.core import util
 from telemetry.core.platform import profiler
+from telemetry.core import util
 
 
 class _SingleProcessSampleProfiler(object):
diff --git a/tools/telemetry/telemetry/core/platform/profiler/trace_profiler_unittest.py b/tools/telemetry/telemetry/core/platform/profiler/trace_profiler_unittest.py
index ab2da82..d32a513 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/trace_profiler_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/trace_profiler_unittest.py
@@ -7,8 +7,8 @@
 import tempfile
 import zipfile
 
-from telemetry import decorators
 from telemetry.core.platform.profiler import trace_profiler
+from telemetry import decorators
 from telemetry.unittest_util import tab_test_case
 
 
diff --git a/tools/telemetry/telemetry/core/platform/profiler/vtune_profiler_unittest.py b/tools/telemetry/telemetry/core/platform/profiler/vtune_profiler_unittest.py
index a01c654..b1329c1 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/vtune_profiler_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/vtune_profiler_unittest.py
@@ -5,8 +5,8 @@
 import sys
 import unittest
 
-from telemetry import decorators
 from telemetry.core.platform.profiler import vtune_profiler
+from telemetry import decorators
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import simple_mock
 from telemetry.unittest_util import tab_test_case
diff --git a/tools/telemetry/telemetry/core/platform/tracing_controller_backend.py b/tools/telemetry/telemetry/core/platform/tracing_controller_backend.py
index 98dbd57..fbb0c1cd 100644
--- a/tools/telemetry/telemetry/core/platform/tracing_controller_backend.py
+++ b/tools/telemetry/telemetry/core/platform/tracing_controller_backend.py
@@ -5,12 +5,12 @@
 import os
 
 from telemetry.core import discover
-from telemetry.core import util
-from telemetry.core.platform import tracing_category_filter
-from telemetry.core.platform import tracing_options
 from telemetry.core.platform import tracing_agent
 from telemetry.core.platform.tracing_agent import chrome_tracing_agent
 from telemetry.core.platform.tracing_agent import display_tracing_agent
+from telemetry.core.platform import tracing_category_filter
+from telemetry.core.platform import tracing_options
+from telemetry.core import util
 from telemetry.timeline import trace_data as trace_data_module
 
 
diff --git a/tools/telemetry/telemetry/core/platform/tracing_controller_unittest.py b/tools/telemetry/telemetry/core/platform/tracing_controller_unittest.py
index bf29431..c1db4a4 100644
--- a/tools/telemetry/telemetry/core/platform/tracing_controller_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/tracing_controller_unittest.py
@@ -2,9 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry import decorators
 from telemetry.core.platform import tracing_category_filter
 from telemetry.core.platform import tracing_options
+from telemetry import decorators
 from telemetry.unittest_util import tab_test_case
 
 class TracingControllerTest(tab_test_case.TabTestCase):
diff --git a/tools/telemetry/telemetry/core/platform/win_platform_backend.py b/tools/telemetry/telemetry/core/platform/win_platform_backend.py
index 9cd0f83..206ef8e 100644
--- a/tools/telemetry/telemetry/core/platform/win_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/win_platform_backend.py
@@ -17,12 +17,12 @@
 import time
 import zipfile
 
-from telemetry import decorators
 from telemetry.core import exceptions
-from telemetry.core import util
 from telemetry.core.platform import desktop_platform_backend
 from telemetry.core.platform import platform_backend
 from telemetry.core.platform.power_monitor import msr_power_monitor
+from telemetry.core import util
+from telemetry import decorators
 from telemetry.util import cloud_storage
 from telemetry.util import path
 
diff --git a/tools/telemetry/telemetry/core/tab_unittest.py b/tools/telemetry/telemetry/core/tab_unittest.py
index ffbcb28..fd37f0a 100644
--- a/tools/telemetry/telemetry/core/tab_unittest.py
+++ b/tools/telemetry/telemetry/core/tab_unittest.py
@@ -5,14 +5,14 @@
 import logging
 import tempfile
 
+from telemetry.core import exceptions
+from telemetry.core.platform import tracing_category_filter
+from telemetry.core.platform import tracing_options
+from telemetry.core import util
+from telemetry.core import video
 from telemetry import decorators
 from telemetry.image_processing import image_util
 from telemetry.image_processing import rgba_color
-from telemetry.core import exceptions
-from telemetry.core import util
-from telemetry.core import video
-from telemetry.core.platform import tracing_category_filter
-from telemetry.core.platform import tracing_options
 from telemetry.timeline import model
 from telemetry.unittest_util import tab_test_case
 
diff --git a/tools/telemetry/telemetry/core/video_unittest.py b/tools/telemetry/telemetry/core/video_unittest.py
index c1fcfe8..6df1a62 100644
--- a/tools/telemetry/telemetry/core/video_unittest.py
+++ b/tools/telemetry/telemetry/core/video_unittest.py
@@ -6,11 +6,11 @@
 import os
 import unittest
 
-from telemetry import decorators
-from telemetry.image_processing import image_util
 from telemetry.core import platform
 from telemetry.core import util
 from telemetry.core import video
+from telemetry import decorators
+from telemetry.image_processing import image_util
 
 
 class VideoTest(unittest.TestCase):
diff --git a/tools/telemetry/telemetry/core/webpagereplay.py b/tools/telemetry/telemetry/core/webpagereplay.py
index c553fe0..08303d2d 100644
--- a/tools/telemetry/telemetry/core/webpagereplay.py
+++ b/tools/telemetry/telemetry/core/webpagereplay.py
@@ -10,6 +10,7 @@
 import signal
 import subprocess
 import sys
+import tempfile
 import urllib
 
 from telemetry.core import exceptions
@@ -17,8 +18,6 @@
 
 _REPLAY_DIR = os.path.join(
     util.GetChromiumSrcDir(), 'third_party', 'webpagereplay')
-_LOG_FILE_PATH = os.path.join(
-    util.GetChromiumSrcDir(), 'webpagereplay_logs', 'logs.txt')
 
 
 class ReplayError(Exception):
@@ -72,11 +71,14 @@
       replay_options: an iterable of options strings to forward to replay.py.
     """
     self.archive_path = archive_path
-    self._log_file_path = _LOG_FILE_PATH
     self._replay_host = replay_host
     self._use_dns_server = dns_port is not None
     self._started_ports = {}  # a dict such as {'http': 80, 'https': 443}
 
+    # A temporary path for storing stdout & stderr of the webpagereplay
+    # subprocess.
+    self._temp_log_file_path = None
+
     replay_py = os.path.join(_REPLAY_DIR, 'replay.py')
     self._cmd_line = self._GetCommandLine(
         replay_py, self._replay_host, http_port, https_port, dns_port,
@@ -120,16 +122,16 @@
 
   def _OpenLogFile(self):
     """Opens the log file for writing."""
-    log_dir = os.path.dirname(self._log_file_path)
+    log_dir = os.path.dirname(self._temp_log_file_path)
     if not os.path.exists(log_dir):
       os.makedirs(log_dir)
-    return open(self._log_file_path, 'w')
+    return open(self._temp_log_file_path, 'w')
 
   def _LogLines(self):
     """Yields the log lines."""
-    if not os.path.isfile(self._log_file_path):
+    if not os.path.isfile(self._temp_log_file_path):
       return
-    with open(self._log_file_path) as f:
+    with open(self._temp_log_file_path) as f:
       for line in f:
         yield line
 
@@ -193,6 +195,7 @@
     """
     is_posix = sys.platform.startswith('linux') or sys.platform == 'darwin'
     logging.debug('Starting Web-Page-Replay: %s', self._cmd_line)
+    self._CreateTempLogFilePath()
     with self._OpenLogFile() as log_fh:
       self.replay_process = subprocess.Popen(
           self._cmd_line, stdout=log_fh, stderr=subprocess.STDOUT,
@@ -211,6 +214,12 @@
 
   def StopServer(self):
     """Stop Web Page Replay."""
+    try:
+      self._StopReplayProcess()
+    finally:
+      self._CleanUpTempLogFilePath()
+
+  def _StopReplayProcess(self):
     if not self.replay_process:
       return
 
@@ -250,6 +259,18 @@
           pass
       self.replay_process.wait()
 
+  def _CreateTempLogFilePath(self):
+    assert self._temp_log_file_path is None
+    handle, self._temp_log_file_path = tempfile.mkstemp()
+    os.close(handle)
+
+  def _CleanUpTempLogFilePath(self):
+    assert self._temp_log_file_path
+    # TODO(nednguyen): print the content of the log file path to telemetry's
+    # output before clearing the file.
+    os.remove(self._temp_log_file_path)
+    self._temp_log_file_path = None
+
   def __enter__(self):
     """Add support for with-statement."""
     self.StartServer()
diff --git a/tools/telemetry/telemetry/image_processing/screen_finder_unittest.py b/tools/telemetry/telemetry/image_processing/screen_finder_unittest.py
index 248aedf..3750079f 100644
--- a/tools/telemetry/telemetry/image_processing/screen_finder_unittest.py
+++ b/tools/telemetry/telemetry/image_processing/screen_finder_unittest.py
@@ -23,9 +23,9 @@
       # Import modules with dependencies that may not be preset in test setup so
       # that importing this unit test doesn't cause the test runner to raise an
       # exception.
-      from telemetry.image_processing import screen_finder
       from telemetry.image_processing.io import fake_frame_generator
       from telemetry.image_processing.io import video_file_frame_generator
+      from telemetry.image_processing import screen_finder
       self.FakeFrameGenerator = fake_frame_generator.FakeFrameGenerator
       self.VideoFileFrameGenerator = \
           video_file_frame_generator.VideoFileFrameGenerator
diff --git a/tools/telemetry/telemetry/page/__init__.py b/tools/telemetry/telemetry/page/__init__.py
index 8aa65ab..cdb9c7b 100644
--- a/tools/telemetry/telemetry/page/__init__.py
+++ b/tools/telemetry/telemetry/page/__init__.py
@@ -6,8 +6,8 @@
 import os
 import urlparse
 
-from telemetry import user_story
 from telemetry.page import shared_page_state
+from telemetry import user_story
 from telemetry.util import cloud_storage
 from telemetry.util import path
 
diff --git a/tools/telemetry/telemetry/page/actions/action_runner.py b/tools/telemetry/telemetry/page/actions/action_runner.py
index b942b5c..b54ee8f 100644
--- a/tools/telemetry/telemetry/page/actions/action_runner.py
+++ b/tools/telemetry/telemetry/page/actions/action_runner.py
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import time
 import logging
+import time
 import urlparse
 
 from telemetry.page.actions.drag import DragAction
diff --git a/tools/telemetry/telemetry/page/actions/action_runner_unittest.py b/tools/telemetry/telemetry/page/actions/action_runner_unittest.py
index cd4eff5e9..b5c1001 100644
--- a/tools/telemetry/telemetry/page/actions/action_runner_unittest.py
+++ b/tools/telemetry/telemetry/page/actions/action_runner_unittest.py
@@ -2,10 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry import decorators
 from telemetry.core import exceptions
 from telemetry.core.platform import tracing_category_filter
 from telemetry.core.platform import tracing_options
+from telemetry import decorators
 from telemetry.page.actions import action_runner as action_runner_module
 from telemetry.page.actions import page_action
 from telemetry.timeline import model
diff --git a/tools/telemetry/telemetry/page/actions/loop_unittest.py b/tools/telemetry/telemetry/page/actions/loop_unittest.py
index eb5ae027..3ddc775 100644
--- a/tools/telemetry/telemetry/page/actions/loop_unittest.py
+++ b/tools/telemetry/telemetry/page/actions/loop_unittest.py
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry import decorators
 from telemetry.core import exceptions
+from telemetry import decorators
 from telemetry.page.actions import loop
 from telemetry.unittest_util import tab_test_case
 
diff --git a/tools/telemetry/telemetry/page/actions/play_unittest.py b/tools/telemetry/telemetry/page/actions/play_unittest.py
index 1d982055..d0db1ae 100644
--- a/tools/telemetry/telemetry/page/actions/play_unittest.py
+++ b/tools/telemetry/telemetry/page/actions/play_unittest.py
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry import decorators
 from telemetry.core import exceptions
+from telemetry import decorators
 from telemetry.page.actions import play
 from telemetry.unittest_util import tab_test_case
 
diff --git a/tools/telemetry/telemetry/page/actions/seek_unittest.py b/tools/telemetry/telemetry/page/actions/seek_unittest.py
index 6eefa1a..a70c380 100644
--- a/tools/telemetry/telemetry/page/actions/seek_unittest.py
+++ b/tools/telemetry/telemetry/page/actions/seek_unittest.py
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry import decorators
 from telemetry.core import exceptions
+from telemetry import decorators
 from telemetry.page.actions import seek
 from telemetry.unittest_util import tab_test_case
 
diff --git a/tools/telemetry/telemetry/page/page_run_end_to_end_unittest.py b/tools/telemetry/telemetry/page/page_run_end_to_end_unittest.py
index 6d4d8e5..52fed2a0 100644
--- a/tools/telemetry/telemetry/page/page_run_end_to_end_unittest.py
+++ b/tools/telemetry/telemetry/page/page_run_end_to_end_unittest.py
@@ -3,16 +3,16 @@
 # found in the LICENSE file.
 
 import os
-import unittest
 import shutil
 import tempfile
+import unittest
 
 from telemetry import benchmark
-from telemetry import decorators
 from telemetry.core import browser_finder
 from telemetry.core import exceptions
 from telemetry.core import user_agent
 from telemetry.core import util
+from telemetry import decorators
 from telemetry.page import page as page_module
 from telemetry.page import page_set
 from telemetry.page import page_test
diff --git a/tools/telemetry/telemetry/page/page_test.py b/tools/telemetry/telemetry/page/page_test.py
index 8e297d14..171bca2 100644
--- a/tools/telemetry/telemetry/page/page_test.py
+++ b/tools/telemetry/telemetry/page/page_test.py
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from telemetry.page import test_expectations
 from telemetry.page.actions import action_runner as action_runner_module
+from telemetry.page import test_expectations
 
 
 class TestNotSupportedOnPlatformError(Exception):
diff --git a/tools/telemetry/telemetry/page/page_test_unittest.py b/tools/telemetry/telemetry/page/page_test_unittest.py
index 8f7926f9..c7350d53 100644
--- a/tools/telemetry/telemetry/page/page_test_unittest.py
+++ b/tools/telemetry/telemetry/page/page_test_unittest.py
@@ -6,8 +6,8 @@
 import os
 import unittest
 
-from telemetry import decorators
 from telemetry.core import wpr_modes
+from telemetry import decorators
 from telemetry.page import page as page_module
 from telemetry.page import page_set
 from telemetry.page import page_test
diff --git a/tools/telemetry/telemetry/page/record_wpr_unittest.py b/tools/telemetry/telemetry/page/record_wpr_unittest.py
index cd38aeea..9642d5a 100644
--- a/tools/telemetry/telemetry/page/record_wpr_unittest.py
+++ b/tools/telemetry/telemetry/page/record_wpr_unittest.py
@@ -6,9 +6,9 @@
 import sys
 
 from telemetry import benchmark
-from telemetry import decorators
 from telemetry.core import util
 from telemetry.core import wpr_modes
+from telemetry import decorators
 from telemetry.page import page as page_module
 from telemetry.page import page_set as page_set_module
 from telemetry.page import page_test
diff --git a/tools/telemetry/telemetry/page/shared_page_state.py b/tools/telemetry/telemetry/page/shared_page_state.py
index 0e564f8..a2c79f0c 100644
--- a/tools/telemetry/telemetry/page/shared_page_state.py
+++ b/tools/telemetry/telemetry/page/shared_page_state.py
@@ -5,14 +5,14 @@
 import os
 import sys
 
-from telemetry import decorators
 from telemetry.core import browser_finder
 from telemetry.core import browser_finder_exceptions
 from telemetry.core import browser_info as browser_info_module
 from telemetry.core import exceptions
+from telemetry.core.platform.profiler import profiler_finder
 from telemetry.core import util
 from telemetry.core import wpr_modes
-from telemetry.core.platform.profiler import profiler_finder
+from telemetry import decorators
 from telemetry.page import page_test
 from telemetry.user_story import shared_user_story_state
 from telemetry.util import exception_formatter
@@ -59,7 +59,7 @@
     self._test.SetOptions(self._finder_options)
 
   def _GetPossibleBrowser(self, test, finder_options):
-    ''' Return a possible_browser with the given options. '''
+    """Return a possible_browser with the given options. """
     possible_browser = browser_finder.FindBrowser(finder_options)
     if not possible_browser:
       raise browser_finder_exceptions.BrowserFinderException(
diff --git a/tools/telemetry/telemetry/page/shared_page_state_unittest.py b/tools/telemetry/telemetry/page/shared_page_state_unittest.py
index 9f0e5fc..b45f65b 100644
--- a/tools/telemetry/telemetry/page/shared_page_state_unittest.py
+++ b/tools/telemetry/telemetry/page/shared_page_state_unittest.py
@@ -8,9 +8,9 @@
 from telemetry.core import browser_finder
 from telemetry.core import wpr_modes
 from telemetry.page import page
-from telemetry.page import shared_page_state
 from telemetry.page import page_set
 from telemetry.page import page_test
+from telemetry.page import shared_page_state
 from telemetry.unittest_util import options_for_unittests
 from telemetry import user_story
 from telemetry.user_story import user_story_runner
diff --git a/tools/telemetry/telemetry/results/buildbot_output_formatter.py b/tools/telemetry/telemetry/results/buildbot_output_formatter.py
index ea524e8..2e39ef9 100644
--- a/tools/telemetry/telemetry/results/buildbot_output_formatter.py
+++ b/tools/telemetry/telemetry/results/buildbot_output_formatter.py
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 from telemetry import perf_tests_helper
-from telemetry import value as value_module
 from telemetry.results import output_formatter
+from telemetry import value as value_module
 from telemetry.value import summary as summary_module
 
 
diff --git a/tools/telemetry/telemetry/results/chart_json_output_formatter_unittest.py b/tools/telemetry/telemetry/results/chart_json_output_formatter_unittest.py
index b9e62e38..6b62f16 100644
--- a/tools/telemetry/telemetry/results/chart_json_output_formatter_unittest.py
+++ b/tools/telemetry/telemetry/results/chart_json_output_formatter_unittest.py
@@ -9,11 +9,11 @@
 
 from telemetry import benchmark
 from telemetry import page as page_module
+from telemetry.page import page_set
 from telemetry.results import chart_json_output_formatter
 from telemetry.results import page_test_results
-from telemetry.page import page_set
-from telemetry.value import scalar
 from telemetry.value import list_of_scalar_values
+from telemetry.value import scalar
 
 
 def _MakePageSet():
diff --git a/tools/telemetry/telemetry/results/json_output_formatter_unittest.py b/tools/telemetry/telemetry/results/json_output_formatter_unittest.py
index 5b6bebd..398843fa4 100644
--- a/tools/telemetry/telemetry/results/json_output_formatter_unittest.py
+++ b/tools/telemetry/telemetry/results/json_output_formatter_unittest.py
@@ -5,17 +5,17 @@
 import os
 import shutil
 import StringIO
-import unittest
 import tempfile
+import unittest
 
 from telemetry import benchmark
 from telemetry import page as page_module
 from telemetry.page import page_set
 from telemetry.results import json_output_formatter
 from telemetry.results import page_test_results
+from telemetry.timeline import trace_data
 from telemetry.value import scalar
 from telemetry.value import trace
-from telemetry.timeline import trace_data
 
 
 def _MakePageSet():
diff --git a/tools/telemetry/telemetry/results/page_test_results.py b/tools/telemetry/telemetry/results/page_test_results.py
index 21e8e7c5..723748b0 100644
--- a/tools/telemetry/telemetry/results/page_test_results.py
+++ b/tools/telemetry/telemetry/results/page_test_results.py
@@ -11,10 +11,10 @@
 import sys
 import traceback
 
-from telemetry import value as value_module
 from telemetry.results import progress_reporter as progress_reporter_module
 from telemetry.results import user_story_run
 from telemetry.util import cloud_storage
+from telemetry import value as value_module
 from telemetry.value import failure
 from telemetry.value import skip
 from telemetry.value import trace
diff --git a/tools/telemetry/telemetry/results/user_story_run_unittest.py b/tools/telemetry/telemetry/results/user_story_run_unittest.py
index 4aa3cf6..0af37a2 100644
--- a/tools/telemetry/telemetry/results/user_story_run_unittest.py
+++ b/tools/telemetry/telemetry/results/user_story_run_unittest.py
@@ -5,8 +5,8 @@
 import os
 import unittest
 
-from telemetry import user_story as user_story_module
 from telemetry.results import user_story_run
+from telemetry import user_story as user_story_module
 from telemetry.user_story import shared_user_story_state
 from telemetry.user_story import user_story_set
 from telemetry.value import failure
diff --git a/tools/telemetry/telemetry/timeline/async_slice.py b/tools/telemetry/telemetry/timeline/async_slice.py
index 6216ed2..2a5068fe 100644
--- a/tools/telemetry/telemetry/timeline/async_slice.py
+++ b/tools/telemetry/telemetry/timeline/async_slice.py
@@ -6,10 +6,10 @@
 
 
 class AsyncSlice(event.TimelineEvent):
-  ''' A AsyncSlice represents an interval of time during which an
+  """An AsyncSlice represents an interval of time during which an
   asynchronous operation is in progress. An AsyncSlice consumes no CPU time
   itself and so is only associated with Threads at its start and end point.
-  '''
+  """
   def __init__(self, category, name, timestamp, args=None,
                duration=0, start_thread=None, end_thread=None,
                thread_start=None, thread_duration=None):
diff --git a/tools/telemetry/telemetry/timeline/event.py b/tools/telemetry/telemetry/timeline/event.py
index 293e7754..996cba9 100644
--- a/tools/telemetry/telemetry/timeline/event.py
+++ b/tools/telemetry/telemetry/timeline/event.py
@@ -5,12 +5,12 @@
 class TimelineEvent(object):
   """Represents a timeline event.
 
-     thread_start, thread_duration and thread_end are the start time, duration
-     and end time of this event as measured by the thread-specific CPU clock
-     (ticking when the thread is actually scheduled). Thread time is optional
-     on trace events and the corresponding attributes in TimelineEvent will be
-     set to None (not 0) if not present. Users of this class need to properly
-     handle this case.
+  thread_start, thread_duration and thread_end are the start time, duration
+  and end time of this event as measured by the thread-specific CPU clock
+  (ticking when the thread is actually scheduled). Thread time is optional
+  on trace events and the corresponding attributes in TimelineEvent will be
+  set to None (not 0) if not present. Users of this class need to properly
+  handle this case.
   """
   def __init__(self, category, name, start, duration, thread_start=None,
                thread_duration=None, args=None):
@@ -34,7 +34,7 @@
   def thread_end(self):
     """Thread-specific CPU time when this event ended.
 
-       May be None if the trace event didn't have thread time data.
+    May be None if the trace event didn't have thread time data.
     """
     if self.thread_start == None or self.thread_duration == None:
       return None
diff --git a/tools/telemetry/telemetry/timeline/flow_event.py b/tools/telemetry/telemetry/timeline/flow_event.py
index 548aa45..8a39215 100644
--- a/tools/telemetry/telemetry/timeline/flow_event.py
+++ b/tools/telemetry/telemetry/timeline/flow_event.py
@@ -6,9 +6,9 @@
 
 
 class FlowEvent(event.TimelineEvent):
-  ''' A FlowEvent represents an interval of time plus parameters associated
+  """A FlowEvent represents an interval of time plus parameters associated
   with that interval.
-  '''
+  """
   def __init__(self, category, event_id, name, start, args=None):
     super(FlowEvent, self).__init__(
         category, name, start, duration=0, args=args)
diff --git a/tools/telemetry/telemetry/timeline/importer.py b/tools/telemetry/telemetry/timeline/importer.py
index 333ab336..5c355a9 100644
--- a/tools/telemetry/telemetry/timeline/importer.py
+++ b/tools/telemetry/telemetry/timeline/importer.py
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+
 class TimelineImporter(object):
   """Reads TraceData and populates timeline model with what it finds."""
   def __init__(self, model, trace_data, import_order):
diff --git a/tools/telemetry/telemetry/timeline/inspector_importer.py b/tools/telemetry/telemetry/timeline/inspector_importer.py
index e996942..f25c9a1 100644
--- a/tools/telemetry/telemetry/timeline/inspector_importer.py
+++ b/tools/telemetry/telemetry/timeline/inspector_importer.py
@@ -1,11 +1,11 @@
 # 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.
-'''Imports event data obtained from the inspector's timeline.'''
+"""Imports event data obtained from the inspector's timeline."""
 
+from telemetry.timeline import importer
 import telemetry.timeline.slice as tracing_slice
 import telemetry.timeline.thread as timeline_thread
-from telemetry.timeline import importer
 from telemetry.timeline import trace_data as trace_data_module
 
 
diff --git a/tools/telemetry/telemetry/timeline/model.py b/tools/telemetry/telemetry/timeline/model.py
index c4d4bff2..a147d43df 100644
--- a/tools/telemetry/telemetry/timeline/model.py
+++ b/tools/telemetry/telemetry/timeline/model.py
@@ -1,11 +1,11 @@
 # 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.
-'''A container for timeline-based events and traces and can handle importing
+"""A container for timeline-based events and traces and can handle importing
 raw event data from different sources. This model closely resembles that in the
 trace_viewer project:
 https://code.google.com/p/trace-viewer/
-'''
+"""
 
 from operator import attrgetter
 
@@ -13,8 +13,8 @@
 from telemetry.timeline import bounds
 from telemetry.timeline import event_container
 from telemetry.timeline import inspector_importer
-from telemetry.timeline import slice as slice_module
 from telemetry.timeline import process as process_module
+from telemetry.timeline import slice as slice_module
 from telemetry.timeline import surface_flinger_importer
 from telemetry.timeline import tab_id_importer
 from telemetry.timeline import trace_data as trace_data_module
@@ -270,4 +270,4 @@
 
       importers.sort(key=lambda k: k.import_order)
 
-    return importers
\ No newline at end of file
+    return importers
diff --git a/tools/telemetry/telemetry/timeline/process.py b/tools/telemetry/telemetry/timeline/process.py
index dbf9212..08e2ee05 100644
--- a/tools/telemetry/telemetry/timeline/process.py
+++ b/tools/telemetry/telemetry/timeline/process.py
@@ -9,8 +9,8 @@
 
 
 class Process(event_container.TimelineEventContainer):
-  ''' The Process represents a single userland process in the trace.
-  '''
+  """The Process represents a single userland process in the trace.
+  """
   def __init__(self, parent, pid):
     super(Process, self).__init__('process %s' % pid, parent)
     self.pid = pid
diff --git a/tools/telemetry/telemetry/timeline/sample.py b/tools/telemetry/telemetry/timeline/sample.py
index 002d294..806f60f 100644
--- a/tools/telemetry/telemetry/timeline/sample.py
+++ b/tools/telemetry/telemetry/timeline/sample.py
@@ -6,14 +6,14 @@
 
 
 class Sample(timeline_event.TimelineEvent):
-  ''' A Sample represents a sample taken at an instant in time
+  """A Sample represents a sample taken at an instant in time
   plus parameters associated with that sample.
 
   NOTE: The Sample class implements the same interface as
   Slice. These must be kept in sync.
 
   All time units are stored in milliseconds.
-  '''
+  """
   def __init__(self, parent_thread, category, name, timestamp, args=None):
     super(Sample, self).__init__(
         category, name, timestamp, 0, args=args)
diff --git a/tools/telemetry/telemetry/timeline/surface_flinger_importer.py b/tools/telemetry/telemetry/timeline/surface_flinger_importer.py
index 71dde5d..bc4e72d 100644
--- a/tools/telemetry/telemetry/timeline/surface_flinger_importer.py
+++ b/tools/telemetry/telemetry/timeline/surface_flinger_importer.py
@@ -30,7 +30,7 @@
       thread.EndSlice(event['ts'])
 
   def FinalizeImport(self):
-    '''Called by the Model after all other importers have imported their
-    events.'''
+    """Called by the Model after all other importers have imported their
+    events."""
     self._model.UpdateBounds()
     self._model.surface_flinger_process = self._surface_flinger_process
diff --git a/tools/telemetry/telemetry/timeline/tab_id_importer_unittest.py b/tools/telemetry/telemetry/timeline/tab_id_importer_unittest.py
index 9c9747d1..843e7fa 100644
--- a/tools/telemetry/telemetry/timeline/tab_id_importer_unittest.py
+++ b/tools/telemetry/telemetry/timeline/tab_id_importer_unittest.py
@@ -6,8 +6,8 @@
 import unittest
 
 from telemetry.timeline import model as timeline_model
-from telemetry.timeline import trace_data as trace_data_module
 from telemetry.timeline import tab_id_importer
+from telemetry.timeline import trace_data as trace_data_module
 
 class TabIdImporterUnitTest(unittest.TestCase):
   def testImportOverflowedTrace(self):
diff --git a/tools/telemetry/telemetry/timeline/thread.py b/tools/telemetry/telemetry/timeline/thread.py
index 1c4a6da..23ed4ce 100644
--- a/tools/telemetry/telemetry/timeline/thread.py
+++ b/tools/telemetry/telemetry/timeline/thread.py
@@ -9,11 +9,11 @@
 
 
 class Thread(event_container.TimelineEventContainer):
-  ''' A Thread stores all the trace events collected for a particular
+  """A Thread stores all the trace events collected for a particular
   thread. We organize the synchronous slices on a thread by "subrows," where
   subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on.
   The asynchronous slices are stored in an AsyncSliceGroup object.
-  '''
+  """
   def __init__(self, process, tid):
     super(Thread, self).__init__('thread %s' % tid, parent=process)
     self.tid = tid
@@ -182,7 +182,7 @@
     self._BuildSliceSubRows()
 
   def _BuildSliceSubRows(self):
-    '''This function works by walking through slices by start time.
+    """This function works by walking through slices by start time.
 
      The basic idea here is to insert each slice as deep into the subrow
      list as it can go such that every subslice is fully contained by its
@@ -211,7 +211,7 @@
      it to row 0 (a root slice):
       0:  [    a       ]  [f]
       1:    [  b  ][e]
-    '''
+    """
     def CompareSlices(s1, s2):
       if s1.start == s2.start:
         # Break ties by having the slice with the greatest
@@ -237,14 +237,14 @@
 
 
   def _AddSliceIfBounds(self, root, child):
-    ''' Adds a child slice to a root slice its proper row.
+    """Adds a child slice to a root slice its proper row.
     Return False if the child slice is not in the bounds
     of the root slice.
 
     Because we know that the start time of child is >= the start time
     of all other slices seen so far, we can just check the last slice
     of each row for bounding.
-    '''
+    """
     # The source trace data is in microseconds but we store it as milliseconds
     # in floating-point. Since we can't represent micros as millis perfectly,
     # two end=start+duration combos that should be the same will be slightly
diff --git a/tools/telemetry/telemetry/timeline/trace_data_unittest.py b/tools/telemetry/telemetry/timeline/trace_data_unittest.py
index c578b6f0..4c24317a 100644
--- a/tools/telemetry/telemetry/timeline/trace_data_unittest.py
+++ b/tools/telemetry/telemetry/timeline/trace_data_unittest.py
@@ -77,4 +77,4 @@
     self.assertTrue(d.HasEventsFor(trace_data.CHROME_TRACE_PART))
     self.assertTrue(d.HasEventsFor(trace_data.TAB_ID_PART))
 
-    self.assertRaises(Exception, builder.AsData)
\ No newline at end of file
+    self.assertRaises(Exception, builder.AsData)
diff --git a/tools/telemetry/telemetry/timeline/trace_event_importer.py b/tools/telemetry/telemetry/timeline/trace_event_importer.py
index 97ad2ab..49aad15 100644
--- a/tools/telemetry/telemetry/timeline/trace_event_importer.py
+++ b/tools/telemetry/telemetry/timeline/trace_event_importer.py
@@ -1,11 +1,11 @@
 # 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.
-''' TraceEventImporter imports TraceEvent-formatted data
+"""TraceEventImporter imports TraceEvent-formatted data
 into the provided model.
 This is a port of the trace event importer from
 https://code.google.com/p/trace-viewer/
-'''
+"""
 
 import copy
 import json
@@ -43,9 +43,9 @@
     return copy.deepcopy(obj)
 
   def _ProcessAsyncEvent(self, event):
-    '''Helper to process an 'async finish' event, which will close an
+    """Helper to process an 'async finish' event, which will close an
     open slice.
-    '''
+    """
     thread = (self._GetOrCreateProcess(event['pid'])
         .GetOrCreateThread(event['tid']))
     self._all_async_events.append({
@@ -53,9 +53,9 @@
         'thread': thread})
 
   def _ProcessCounterEvent(self, event):
-    '''Helper that creates and adds samples to a Counter object based on
+    """Helper that creates and adds samples to a Counter object based on
     'C' phase events.
-    '''
+    """
     if 'id' in event:
       ctr_name = event['name'] + '[' + str(event['id']) + ']'
     else:
@@ -181,9 +181,9 @@
         'thread': thread})
 
   def ImportEvents(self):
-    ''' Walks through the events_ list and outputs the structures discovered to
+    """Walks through the events_ list and outputs the structures discovered to
     model_.
-    '''
+    """
     for event in self._events:
       phase = event.get('ph', None)
       if phase == 'B' or phase == 'E':
@@ -213,8 +213,8 @@
     return self._model
 
   def FinalizeImport(self):
-    '''Called by the Model after all other importers have imported their
-    events.'''
+    """Called by the Model after all other importers have imported their
+    events."""
     self._model.UpdateBounds()
 
     # We need to reupdate the bounds in case the minimum start time changes
diff --git a/tools/telemetry/telemetry/timeline/trace_event_importer_unittest.py b/tools/telemetry/telemetry/timeline/trace_event_importer_unittest.py
index e57bd203..e8a3d363 100644
--- a/tools/telemetry/telemetry/timeline/trace_event_importer_unittest.py
+++ b/tools/telemetry/telemetry/timeline/trace_event_importer_unittest.py
@@ -693,11 +693,11 @@
     self.assertEqual(4, slice_event.args['z'])
 
   def testSliceHierarchy(self):
-    ''' The slice hierarchy should look something like this:
+    """The slice hierarchy should look something like this:
            [            a            ]
               [      b      ]  [ d ]
               [ c ]     [ e ]
-    '''
+    """
     events = [
       {'name': 'a', 'args': {}, 'pid': 52, 'ts': 100, 'cat': 'foo',
        'tid': 53, 'ph': 'B'},
@@ -1018,4 +1018,4 @@
 
     trace_data = trace_data_module.TraceData(events)
     m = timeline_model.TimelineModel(trace_data)
-    self.assertEqual(0, len(m.flow_events))
\ No newline at end of file
+    self.assertEqual(0, len(m.flow_events))
diff --git a/tools/telemetry/telemetry/unittest_util/run_chromeos_tests.py b/tools/telemetry/telemetry/unittest_util/run_chromeos_tests.py
index 369be5f..ecfcb814 100644
--- a/tools/telemetry/telemetry/unittest_util/run_chromeos_tests.py
+++ b/tools/telemetry/telemetry/unittest_util/run_chromeos_tests.py
@@ -4,8 +4,8 @@
 import logging
 import os
 
-from telemetry.unittest_util import run_tests
 from telemetry.core import util
+from telemetry.unittest_util import run_tests
 
 
 def RunTestsForChromeOS(browser_type, unit_tests, perf_tests):
diff --git a/tools/telemetry/telemetry/unittest_util/run_tests.py b/tools/telemetry/telemetry/unittest_util/run_tests.py
index b4f6b8db..cf41a6f 100644
--- a/tools/telemetry/telemetry/unittest_util/run_tests.py
+++ b/tools/telemetry/telemetry/unittest_util/run_tests.py
@@ -3,15 +3,15 @@
 # found in the LICENSE file.
 import sys
 
-from telemetry import decorators
 from telemetry.core import browser_finder
 from telemetry.core import browser_finder_exceptions
 from telemetry.core import browser_options
 from telemetry.core import command_line
 from telemetry.core import device_finder
 from telemetry.core import util
-from telemetry.unittest_util import options_for_unittests
+from telemetry import decorators
 from telemetry.unittest_util import browser_test_case
+from telemetry.unittest_util import options_for_unittests
 
 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'third_party', 'typ')
 
diff --git a/tools/telemetry/telemetry/user_story/android/shared_app_state.py b/tools/telemetry/telemetry/user_story/android/shared_app_state.py
index 76177bd..ef3eb8c 100644
--- a/tools/telemetry/telemetry/user_story/android/shared_app_state.py
+++ b/tools/telemetry/telemetry/user_story/android/shared_app_state.py
@@ -4,9 +4,9 @@
 import os
 
 from telemetry.core import platform
+from telemetry.core.platform import android_device
 from telemetry.core.platform import android_platform
 from telemetry.core import wpr_modes
-from telemetry.core.platform import android_device
 from telemetry.user_story import shared_user_story_state
 from telemetry.web_perf import timeline_based_measurement
 
diff --git a/tools/telemetry/telemetry/user_story/user_story_runner.py b/tools/telemetry/telemetry/user_story/user_story_runner.py
index 4520ad9f..b1a083d 100644
--- a/tools/telemetry/telemetry/user_story/user_story_runner.py
+++ b/tools/telemetry/telemetry/user_story/user_story_runner.py
@@ -9,12 +9,12 @@
 import sys
 import time
 
-from telemetry import page as page_module
 from telemetry.core import exceptions
 from telemetry.core import wpr_modes
+from telemetry import page as page_module
+from telemetry.page.actions import page_action
 from telemetry.page import page_set as page_set_module
 from telemetry.page import page_test
-from telemetry.page.actions import page_action
 from telemetry.results import results_options
 from telemetry.user_story import user_story_filter
 from telemetry.user_story import user_story_set as user_story_set_module
diff --git a/tools/telemetry/telemetry/user_story/user_story_runner_unittest.py b/tools/telemetry/telemetry/user_story/user_story_runner_unittest.py
index 17bea6d..cc8489d 100644
--- a/tools/telemetry/telemetry/user_story/user_story_runner_unittest.py
+++ b/tools/telemetry/telemetry/user_story/user_story_runner_unittest.py
@@ -2,12 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import unittest
 import StringIO
 import sys
+import unittest
 
 from telemetry import benchmark
-from telemetry import user_story
 from telemetry.core import exceptions
 from telemetry.page import page as page_module
 from telemetry.page import page_test
@@ -15,6 +14,7 @@
 from telemetry.results import results_options
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import system_stub
+from telemetry import user_story
 from telemetry.user_story import shared_user_story_state
 from telemetry.user_story import user_story_runner
 from telemetry.user_story import user_story_set
diff --git a/tools/telemetry/telemetry/util/cloud_storage.py b/tools/telemetry/telemetry/util/cloud_storage.py
index a6055e6..a40b524b 100644
--- a/tools/telemetry/telemetry/util/cloud_storage.py
+++ b/tools/telemetry/telemetry/util/cloud_storage.py
@@ -4,8 +4,8 @@
 
 """Wrappers for gsutil, for basic interaction with Google Cloud Storage."""
 
-import contextlib
 import collections
+import contextlib
 import cStringIO
 import hashlib
 import logging
@@ -15,8 +15,8 @@
 import tarfile
 import urllib2
 
-from telemetry import decorators
 from telemetry.core import util
+from telemetry import decorators
 from telemetry.util import path
 
 
diff --git a/tools/telemetry/telemetry/util/file_handle_unittest.py b/tools/telemetry/telemetry/util/file_handle_unittest.py
index e17da43..12fc4ca 100644
--- a/tools/telemetry/telemetry/util/file_handle_unittest.py
+++ b/tools/telemetry/telemetry/util/file_handle_unittest.py
@@ -3,9 +3,9 @@
 # found in the LICENSE file.
 
 import os
-import unittest
 import shutil
 import tempfile
+import unittest
 
 from telemetry.util import file_handle
 
diff --git a/tools/telemetry/telemetry/value/failure_unittest.py b/tools/telemetry/telemetry/value/failure_unittest.py
index 28153078..df7d8f5 100644
--- a/tools/telemetry/telemetry/value/failure_unittest.py
+++ b/tools/telemetry/telemetry/value/failure_unittest.py
@@ -8,8 +8,8 @@
 import unittest
 
 from telemetry import page as page_module
-from telemetry import value
 from telemetry.page import page_set
+from telemetry import value
 from telemetry.value import failure
 
 
diff --git a/tools/telemetry/telemetry/value/histogram.py b/tools/telemetry/telemetry/value/histogram.py
index 6f5d4067..b8d29ca 100644
--- a/tools/telemetry/telemetry/value/histogram.py
+++ b/tools/telemetry/telemetry/value/histogram.py
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 import json
 
-from telemetry import value as value_module
 from telemetry import perf_tests_helper
+from telemetry import value as value_module
 from telemetry.value import histogram_util
 
 class HistogramValueBucket(object):
diff --git a/tools/telemetry/telemetry/value/histogram_unittest.py b/tools/telemetry/telemetry/value/histogram_unittest.py
index abb0e28..cc73bdf2 100644
--- a/tools/telemetry/telemetry/value/histogram_unittest.py
+++ b/tools/telemetry/telemetry/value/histogram_unittest.py
@@ -5,8 +5,8 @@
 import unittest
 
 from telemetry import page as page_module
-from telemetry import value
 from telemetry.page import page_set
+from telemetry import value
 from telemetry.value import histogram as histogram_module
 
 
diff --git a/tools/telemetry/telemetry/value/list_of_scalar_values_unittest.py b/tools/telemetry/telemetry/value/list_of_scalar_values_unittest.py
index 47a3c0bdc..336c8c7 100644
--- a/tools/telemetry/telemetry/value/list_of_scalar_values_unittest.py
+++ b/tools/telemetry/telemetry/value/list_of_scalar_values_unittest.py
@@ -5,8 +5,8 @@
 import unittest
 
 from telemetry import page as page_module
-from telemetry import value
 from telemetry.page import page_set
+from telemetry import value
 from telemetry.value import list_of_scalar_values
 from telemetry.value import none_values
 
diff --git a/tools/telemetry/telemetry/value/list_of_string_values_unittest.py b/tools/telemetry/telemetry/value/list_of_string_values_unittest.py
index 731a55b..f60a1c3 100644
--- a/tools/telemetry/telemetry/value/list_of_string_values_unittest.py
+++ b/tools/telemetry/telemetry/value/list_of_string_values_unittest.py
@@ -5,8 +5,8 @@
 import unittest
 
 from telemetry import page as page_module
-from telemetry import value
 from telemetry.page import page_set
+from telemetry import value
 from telemetry.value import list_of_string_values
 from telemetry.value import none_values
 
diff --git a/tools/telemetry/telemetry/value/scalar_unittest.py b/tools/telemetry/telemetry/value/scalar_unittest.py
index 14abd21..16f2df1 100644
--- a/tools/telemetry/telemetry/value/scalar_unittest.py
+++ b/tools/telemetry/telemetry/value/scalar_unittest.py
@@ -5,8 +5,8 @@
 import unittest
 
 from telemetry import page as page_module
-from telemetry import value
 from telemetry.page import page_set
+from telemetry import value
 from telemetry.value import none_values
 from telemetry.value import scalar
 
diff --git a/tools/telemetry/telemetry/value/skip_unittest.py b/tools/telemetry/telemetry/value/skip_unittest.py
index ae3a931..e0a1bac 100644
--- a/tools/telemetry/telemetry/value/skip_unittest.py
+++ b/tools/telemetry/telemetry/value/skip_unittest.py
@@ -6,8 +6,8 @@
 import unittest
 
 from telemetry import page as page_module
-from telemetry import value
 from telemetry.page import page_set
+from telemetry import value
 from telemetry.value import skip
 
 
diff --git a/tools/telemetry/telemetry/value/string_unittest.py b/tools/telemetry/telemetry/value/string_unittest.py
index f629ea0..d5085eb 100644
--- a/tools/telemetry/telemetry/value/string_unittest.py
+++ b/tools/telemetry/telemetry/value/string_unittest.py
@@ -5,8 +5,8 @@
 import unittest
 
 from telemetry import page as page_module
-from telemetry import value
 from telemetry.page import page_set
+from telemetry import value
 from telemetry.value import none_values
 from telemetry.value import string
 
diff --git a/tools/telemetry/telemetry/value/trace.py b/tools/telemetry/telemetry/value/trace.py
index 1860ade..7b660bb 100644
--- a/tools/telemetry/telemetry/value/trace.py
+++ b/tools/telemetry/telemetry/value/trace.py
@@ -4,16 +4,16 @@
 
 import datetime
 import logging
+import os
 import random
 import shutil
-import os
 import sys
 import tempfile
 
-from telemetry import value as value_module
 from telemetry.timeline import trace_data as trace_data_module
 from telemetry.util import cloud_storage
 from telemetry.util import file_handle
+from telemetry import value as value_module
 import telemetry.web_components # pylint: disable=W0611
 from trace_viewer.build import trace2html
 
diff --git a/tools/telemetry/telemetry/value/trace_unittest.py b/tools/telemetry/telemetry/value/trace_unittest.py
index 8f03b8aa..d02b2a40 100644
--- a/tools/telemetry/telemetry/value/trace_unittest.py
+++ b/tools/telemetry/telemetry/value/trace_unittest.py
@@ -3,9 +3,9 @@
 # found in the LICENSE file.
 
 import os
-import unittest
 import shutil
 import tempfile
+import unittest
 
 from telemetry import page as page_module
 from telemetry.page import page_set
diff --git a/tools/telemetry/telemetry/value/value_unittest.py b/tools/telemetry/telemetry/value/value_unittest.py
index f6ca452..22c79516 100644
--- a/tools/telemetry/telemetry/value/value_unittest.py
+++ b/tools/telemetry/telemetry/value/value_unittest.py
@@ -5,8 +5,8 @@
 import unittest
 
 from telemetry import page as page_module
-from telemetry import value
 from telemetry.page import page_set
+from telemetry import value
 
 
 class TestBase(unittest.TestCase):
diff --git a/tools/telemetry/telemetry/web_components/dev_server.py b/tools/telemetry/telemetry/web_components/dev_server.py
index de136c1..d286ffd 100644
--- a/tools/telemetry/telemetry/web_components/dev_server.py
+++ b/tools/telemetry/telemetry/web_components/dev_server.py
@@ -5,6 +5,7 @@
 import optparse
 
 import tvcm
+
 from telemetry.web_components import web_components_project
 
 
diff --git a/tools/telemetry/telemetry/web_components/web_components_unittest.py b/tools/telemetry/telemetry/web_components/web_components_unittest.py
index e09af588..7ce6f20 100644
--- a/tools/telemetry/telemetry/web_components/web_components_unittest.py
+++ b/tools/telemetry/telemetry/web_components/web_components_unittest.py
@@ -6,10 +6,10 @@
 import unittest
 
 from telemetry.web_components import web_components_project
-from tvcm import module_test_case
 
 
 def DISABLED_load_tests(_, _2, _3):
+  from tvcm import module_test_case
   project = web_components_project.WebComponentsProject()
   if os.getenv('NO_TVCM'):
     suite = unittest.TestSuite()
diff --git a/tools/telemetry/telemetry/web_perf/metrics/gpu_timeline.py b/tools/telemetry/telemetry/web_perf/metrics/gpu_timeline.py
index acc4b8d4..2667f22 100644
--- a/tools/telemetry/telemetry/web_perf/metrics/gpu_timeline.py
+++ b/tools/telemetry/telemetry/web_perf/metrics/gpu_timeline.py
@@ -6,8 +6,8 @@
 import sys
 
 from telemetry.timeline import model as model_module
-from telemetry.value import scalar
 from telemetry.value import list_of_scalar_values
+from telemetry.value import scalar
 from telemetry.web_perf.metrics import timeline_based_metric
 
 TOPLEVEL_GL_CATEGORY = 'gpu_toplevel'
diff --git a/tools/telemetry/telemetry/web_perf/metrics/gpu_timeline_unittest.py b/tools/telemetry/telemetry/web_perf/metrics/gpu_timeline_unittest.py
index 77bb684..52a60ed 100644
--- a/tools/telemetry/telemetry/web_perf/metrics/gpu_timeline_unittest.py
+++ b/tools/telemetry/telemetry/web_perf/metrics/gpu_timeline_unittest.py
@@ -4,12 +4,12 @@
 
 import unittest
 
-from telemetry.unittest_util import test_page_test_results
 from telemetry.timeline import async_slice as async_slice_module
-from telemetry.timeline import slice as slice_module
 from telemetry.timeline import model as model_module
-from telemetry.web_perf import timeline_interaction_record as tir_module
+from telemetry.timeline import slice as slice_module
+from telemetry.unittest_util import test_page_test_results
 from telemetry.web_perf.metrics import gpu_timeline
+from telemetry.web_perf import timeline_interaction_record as tir_module
 
 SERVICE_FRAME_END_CATEGORY, SERVICE_FRAME_END_NAME = \
     gpu_timeline.SERVICE_FRAME_END_MARKER
diff --git a/tools/telemetry/telemetry/web_perf/metrics/layout_unittest.py b/tools/telemetry/telemetry/web_perf/metrics/layout_unittest.py
index 1a76d084..00e27f1 100644
--- a/tools/telemetry/telemetry/web_perf/metrics/layout_unittest.py
+++ b/tools/telemetry/telemetry/web_perf/metrics/layout_unittest.py
@@ -4,10 +4,10 @@
 
 import unittest
 
-from telemetry.results import page_test_results
-from telemetry.page import page
-from telemetry.web_perf.metrics import layout
 from collections import namedtuple
+from telemetry.page import page
+from telemetry.results import page_test_results
+from telemetry.web_perf.metrics import layout
 
 FakeEvent = namedtuple('Event', 'name, start, end')
 
diff --git a/tools/telemetry/telemetry/web_perf/metrics/mainthread_jank_stats_unittest.py b/tools/telemetry/telemetry/web_perf/metrics/mainthread_jank_stats_unittest.py
index 0c1bca3..e12d440 100644
--- a/tools/telemetry/telemetry/web_perf/metrics/mainthread_jank_stats_unittest.py
+++ b/tools/telemetry/telemetry/web_perf/metrics/mainthread_jank_stats_unittest.py
@@ -4,10 +4,10 @@
 
 import unittest
 
-from telemetry.timeline import model as model_module
 from telemetry.timeline import async_slice
-from telemetry.web_perf import timeline_interaction_record as tir_module
+from telemetry.timeline import model as model_module
 from telemetry.web_perf.metrics import mainthread_jank_stats
+from telemetry.web_perf import timeline_interaction_record as tir_module
 
 
 class MainthreadJankTests(unittest.TestCase):
diff --git a/tools/telemetry/telemetry/web_perf/metrics/rendering_frame.py b/tools/telemetry/telemetry/web_perf/metrics/rendering_frame.py
index e06213e..48f8546f 100644
--- a/tools/telemetry/telemetry/web_perf/metrics/rendering_frame.py
+++ b/tools/telemetry/telemetry/web_perf/metrics/rendering_frame.py
@@ -4,8 +4,8 @@
 
 from collections import defaultdict
 
-from telemetry.timeline import slice as slice_module
 from telemetry.timeline import bounds
+from telemetry.timeline import slice as slice_module
 
 
 class MissingData(Exception):
diff --git a/tools/telemetry/telemetry/web_perf/metrics/rendering_frame_unittest.py b/tools/telemetry/telemetry/web_perf/metrics/rendering_frame_unittest.py
index 2ee9e73..95f6d93b 100644
--- a/tools/telemetry/telemetry/web_perf/metrics/rendering_frame_unittest.py
+++ b/tools/telemetry/telemetry/web_perf/metrics/rendering_frame_unittest.py
@@ -5,8 +5,8 @@
 import unittest
 
 import telemetry.timeline.bounds as timeline_bounds
-import telemetry.timeline.slice as tracing_slice
 from telemetry.timeline import model
+import telemetry.timeline.slice as tracing_slice
 from telemetry.web_perf.metrics. \
     rendering_frame import GetFrameEventsInsideRange
 from telemetry.web_perf.metrics.rendering_frame import MissingData
diff --git a/tools/telemetry/telemetry/web_perf/metrics/rendering_stats_unittest.py b/tools/telemetry/telemetry/web_perf/metrics/rendering_stats_unittest.py
index 7c0f2fc..0c35a40 100644
--- a/tools/telemetry/telemetry/web_perf/metrics/rendering_stats_unittest.py
+++ b/tools/telemetry/telemetry/web_perf/metrics/rendering_stats_unittest.py
@@ -5,9 +5,9 @@
 import random
 import unittest
 
+from telemetry.perf_tests_helper import FlattenList
 import telemetry.timeline.async_slice as tracing_async_slice
 import telemetry.timeline.bounds as timeline_bounds
-from telemetry.perf_tests_helper import FlattenList
 from telemetry.timeline import model
 from telemetry.util.statistics import DivideIfPossibleOrZero
 from telemetry.web_perf.metrics.rendering_stats import (
diff --git a/tools/telemetry/telemetry/web_perf/metrics/responsiveness_metric.py b/tools/telemetry/telemetry/web_perf/metrics/responsiveness_metric.py
index 1864fc7..2697b35c 100644
--- a/tools/telemetry/telemetry/web_perf/metrics/responsiveness_metric.py
+++ b/tools/telemetry/telemetry/web_perf/metrics/responsiveness_metric.py
@@ -4,9 +4,9 @@
 import logging
 
 from telemetry.value import scalar
-from telemetry.web_perf import timeline_interaction_record as tir_module
 from telemetry.web_perf.metrics import mainthread_jank_stats
 from telemetry.web_perf.metrics import timeline_based_metric
+from telemetry.web_perf import timeline_interaction_record as tir_module
 
 
 class ResponsivenessMetric(timeline_based_metric.TimelineBasedMetric):
diff --git a/tools/telemetry/telemetry/web_perf/metrics/smoothness_unittest.py b/tools/telemetry/telemetry/web_perf/metrics/smoothness_unittest.py
index 6b3277a..603af25 100644
--- a/tools/telemetry/telemetry/web_perf/metrics/smoothness_unittest.py
+++ b/tools/telemetry/telemetry/web_perf/metrics/smoothness_unittest.py
@@ -4,8 +4,8 @@
 
 import unittest
 
-from telemetry.results import page_test_results
 from telemetry.page import page as page_module
+from telemetry.results import page_test_results
 from telemetry.web_perf.metrics import smoothness
 
 
diff --git a/tools/telemetry/telemetry/web_perf/timeline_based_measurement.py b/tools/telemetry/telemetry/web_perf/timeline_based_measurement.py
index 6b3a60f3..7c7264b 100644
--- a/tools/telemetry/telemetry/web_perf/timeline_based_measurement.py
+++ b/tools/telemetry/telemetry/web_perf/timeline_based_measurement.py
@@ -9,10 +9,10 @@
 from telemetry.page import page_test
 from telemetry.timeline import model as model_module
 from telemetry.value import trace
-from telemetry.web_perf import timeline_interaction_record as tir_module
+from telemetry.web_perf.metrics import layout
 from telemetry.web_perf.metrics import responsiveness_metric
 from telemetry.web_perf.metrics import smoothness
-from telemetry.web_perf.metrics import layout
+from telemetry.web_perf import timeline_interaction_record as tir_module
 
 # TimelineBasedMeasurement considers all instrumentation as producing a single
 # timeline. But, depending on the amount of instrumentation that is enabled,
diff --git a/tools/telemetry/telemetry/web_perf/timeline_based_measurement_unittest.py b/tools/telemetry/telemetry/web_perf/timeline_based_measurement_unittest.py
index 663d5029..777e4e18 100644
--- a/tools/telemetry/telemetry/web_perf/timeline_based_measurement_unittest.py
+++ b/tools/telemetry/telemetry/web_perf/timeline_based_measurement_unittest.py
@@ -5,21 +5,21 @@
 import os
 import unittest
 
-from telemetry import decorators
 from telemetry.core import platform
 from telemetry.core import wpr_modes
+from telemetry import decorators
 from telemetry.page import page as page_module
 from telemetry.page import page_set
 from telemetry.results import page_test_results
-from telemetry.timeline import model as model_module
 from telemetry.timeline import async_slice
+from telemetry.timeline import model as model_module
 from telemetry.unittest_util import browser_test_case
 from telemetry.unittest_util import options_for_unittests
 from telemetry.unittest_util import page_test_test_case
 from telemetry.value import scalar
+from telemetry.web_perf.metrics import timeline_based_metric
 from telemetry.web_perf import timeline_based_measurement as tbm_module
 from telemetry.web_perf import timeline_interaction_record as tir_module
-from telemetry.web_perf.metrics import timeline_based_metric
 
 
 class FakeSmoothMetric(timeline_based_metric.TimelineBasedMetric):
diff --git a/tools/telemetry/telemetry/web_perf/timeline_interaction_record.py b/tools/telemetry/telemetry/web_perf/timeline_interaction_record.py
index 4b047e5..7d6ce26 100644
--- a/tools/telemetry/telemetry/web_perf/timeline_interaction_record.py
+++ b/tools/telemetry/telemetry/web_perf/timeline_interaction_record.py
@@ -4,8 +4,8 @@
 
 import re
 
-import telemetry.timeline.bounds as timeline_bounds
 from telemetry import decorators
+import telemetry.timeline.bounds as timeline_bounds
 
 # Allows multiple duplicate interactions of the same type
 REPEATABLE = 'repeatable'
diff --git a/tools/telemetry/telemetry/web_perf/timeline_interaction_record_unittest.py b/tools/telemetry/telemetry/web_perf/timeline_interaction_record_unittest.py
index fe1fb33..870b66b 100644
--- a/tools/telemetry/telemetry/web_perf/timeline_interaction_record_unittest.py
+++ b/tools/telemetry/telemetry/web_perf/timeline_interaction_record_unittest.py
@@ -4,9 +4,9 @@
 
 import unittest
 
+from telemetry.timeline import async_slice
 from telemetry.timeline import model as model_module
 from telemetry.timeline import slice as slice_module
-from telemetry.timeline import async_slice
 from telemetry.web_perf import timeline_interaction_record as tir_module
 
 
diff --git a/tools/valgrind/drmemory/suppressions.txt b/tools/valgrind/drmemory/suppressions.txt
index 4e084a7..f16c149 100644
--- a/tools/valgrind/drmemory/suppressions.txt
+++ b/tools/valgrind/drmemory/suppressions.txt
@@ -481,7 +481,6 @@
 ipc.dll!IPC::Channel::ChannelImpl::CreatePipe
 ipc.dll!IPC::Channel::ChannelImpl::ChannelImpl
 ipc.dll!IPC::Channel::Channel
-ipc.dll!IPC::ChannelProxy::Context::CreateChannel
 
 HANDLE LEAK
 name=http://crbug.com/371942
@@ -755,7 +754,7 @@
 name=http://crbug.com/463261
 ...
 blink_web.dll!blink::WebFrameWidgetImpl::selectionBounds
-content.dll!content::RenderWidget::GetSelectionBounds
+...
 content.dll!content::RenderWidget::UpdateSelectionBounds
 content.dll!content::RenderWidget::willBeginCompositorFrame
 cc.dll!base::internal::InvokeHelper<>::MakeItSo
diff --git a/tools/valgrind/drmemory/suppressions_full.txt b/tools/valgrind/drmemory/suppressions_full.txt
index 399b799..71e88a1 100644
--- a/tools/valgrind/drmemory/suppressions_full.txt
+++ b/tools/valgrind/drmemory/suppressions_full.txt
@@ -1922,3 +1922,8 @@
 blink_web.dll!blink::WebFrameWidgetImpl::selectionBounds
 content.dll!content::RenderWidget::UpdateSelectionBounds
 content.dll!content::RenderWidget::willBeginCompositorFrame
+
+UNINITIALIZED READ
+name=bug_470848
+blink_platform.dll!blink::Heap::RegionTree::lookup
+*!testing::internal::HandleExceptionsInMethodIfSupported<>
diff --git a/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt b/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt
index 2ec3183..98043de 100644
--- a/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt
+++ b/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt
@@ -35,3 +35,6 @@
 # http://crbug.com/464033
 RenderFrameHostImplBrowserTest.IsFocused_AtLoad
 RenderFrameHostImplBrowserTest.IsFocused_Widget
+
+# http://crbug.com/470507
+SRC_ClearKey/EncryptedMediaTest.Playback_VideoClearAudio_WebM/0
diff --git a/tools/valgrind/gtest_exclude/media_unittests.gtest-drmemory.txt b/tools/valgrind/gtest_exclude/media_unittests.gtest-drmemory.txt
new file mode 100644
index 0000000..d73c572
--- /dev/null
+++ b/tools/valgrind/gtest_exclude/media_unittests.gtest-drmemory.txt
@@ -0,0 +1,2 @@
+# http://crbug.com/470517
+VideoRendererImplTest.Underflow
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 3ecb4e46..8538200 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -154,6 +154,11 @@
   return atk_relation_set;
 }
 
+static AtkAttributeSet* ax_platform_node_auralinux_get_attributes(
+    AtkObject* atk_object) {
+  return NULL;
+}
+
 static AtkRole ax_platform_node_auralinux_get_role(AtkObject* atk_object) {
   ui::AXPlatformNodeAuraLinux* obj =
       AtkObjectToAXPlatformNodeAuraLinux(atk_object);
@@ -166,17 +171,15 @@
     AtkObject* atk_object) {
   ui::AXPlatformNodeAuraLinux* obj =
       AtkObjectToAXPlatformNodeAuraLinux(atk_object);
-  AtkStateSet* state_set =
+  if (!obj)
+    return NULL;
+
+  AtkStateSet* atk_state_set =
       ATK_OBJECT_CLASS(ax_platform_node_auralinux_parent_class)->
       ref_state_set(atk_object);
 
-  if (!obj) {
-    atk_state_set_add_state(state_set, ATK_STATE_DEFUNCT);
-    return state_set;
-  }
-  obj->GetAtkState(state_set);
-
-  return state_set;
+  obj->GetAtkState(atk_state_set);
+  return atk_state_set;
 }
 
 //
@@ -214,6 +217,7 @@
   klass->ref_state_set = ax_platform_node_auralinux_ref_state_set;
   klass->get_index_in_parent = ax_platform_node_auralinux_get_index_in_parent;
   klass->ref_relation_set = ax_platform_node_auralinux_ref_relation_set;
+  klass->get_attributes = ax_platform_node_auralinux_get_attributes;
 }
 
 GType ax_platform_node_auralinux_get_type() {
@@ -329,29 +333,29 @@
   }
 }
 
-void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* state_set) {
+void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) {
   uint32 state = GetData().state;
 
   if (state & (1 << ui::AX_STATE_CHECKED))
-    atk_state_set_add_state(state_set, ATK_STATE_CHECKED);
+    atk_state_set_add_state(atk_state_set, ATK_STATE_CHECKED);
   if (state & (1 << ui::AX_STATE_DEFAULT))
-    atk_state_set_add_state(state_set, ATK_STATE_DEFAULT);
+    atk_state_set_add_state(atk_state_set, ATK_STATE_DEFAULT);
   if (state & (1 << ui::AX_STATE_EDITABLE))
-    atk_state_set_add_state(state_set, ATK_STATE_EDITABLE);
+    atk_state_set_add_state(atk_state_set, ATK_STATE_EDITABLE);
   if (state & (1 << ui::AX_STATE_ENABLED))
-    atk_state_set_add_state(state_set, ATK_STATE_ENABLED);
+    atk_state_set_add_state(atk_state_set, ATK_STATE_ENABLED);
   if (state & (1 << ui::AX_STATE_EXPANDED))
-    atk_state_set_add_state(state_set, ATK_STATE_EXPANDED);
+    atk_state_set_add_state(atk_state_set, ATK_STATE_EXPANDED);
   if (state & (1 << ui::AX_STATE_FOCUSABLE))
-    atk_state_set_add_state(state_set, ATK_STATE_FOCUSABLE);
+    atk_state_set_add_state(atk_state_set, ATK_STATE_FOCUSABLE);
   if (state & (1 << ui::AX_STATE_FOCUSED))
-    atk_state_set_add_state(state_set, ATK_STATE_FOCUSED);
+    atk_state_set_add_state(atk_state_set, ATK_STATE_FOCUSED);
   if (state & (1 << ui::AX_STATE_PRESSED))
-    atk_state_set_add_state(state_set, ATK_STATE_PRESSED);
+    atk_state_set_add_state(atk_state_set, ATK_STATE_PRESSED);
   if (state & (1 << ui::AX_STATE_SELECTABLE))
-    atk_state_set_add_state(state_set, ATK_STATE_SELECTABLE);
+    atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTABLE);
   if (state & (1 << ui::AX_STATE_SELECTED))
-    atk_state_set_add_state(state_set, ATK_STATE_SELECTED);
+    atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTED);
 }
 
 void AXPlatformNodeAuraLinux::GetAtkRelations(AtkRelationSet* atk_relation_set)
diff --git a/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java
index b32cd9b..5c20d72 100644
--- a/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java
@@ -10,6 +10,9 @@
 import android.content.Intent;
 import android.content.IntentSender.SendIntentException;
 
+import org.chromium.base.ActivityState;
+import org.chromium.base.ApplicationStatus;
+
 import java.lang.ref.WeakReference;
 
 /**
@@ -17,7 +20,8 @@
  * Activity Instance.
  * Only instantiate this class when you need the implemented features.
  */
-public class ActivityWindowAndroid extends WindowAndroid {
+public class ActivityWindowAndroid
+        extends WindowAndroid implements ApplicationStatus.ActivityStateListener {
     // Constants used for intent request code bounding.
     private static final int REQUEST_CODE_PREFIX = 1000;
     private static final int REQUEST_CODE_RANGE_SIZE = 100;
@@ -26,9 +30,27 @@
     private final WeakReference<Activity> mActivityRef;
     private int mNextRequestCode = 0;
 
+    /**
+     * Creates an Activity-specific WindowAndroid with associated intent functionality.
+     * TODO(jdduke): Remove this overload when all callsites have been updated to
+     * indicate their activity state listening preference.
+     * @param activity The activity associated with the WindowAndroid.
+     */
     public ActivityWindowAndroid(Activity activity) {
+        this(activity, true);
+    }
+
+    /**
+     * Creates an Activity-specific WindowAndroid with associated intent functionality.
+     * @param activity The activity associated with the WindowAndroid.
+     * @param listenToActivityState Whether to listen to activity state changes.
+     */
+    public ActivityWindowAndroid(Activity activity, boolean listenToActivityState) {
         super(activity.getApplicationContext());
         mActivityRef = new WeakReference<Activity>(activity);
+        if (listenToActivityState) {
+            ApplicationStatus.registerStateListenerForActivity(this, activity);
+        }
     }
 
     @Override
@@ -99,6 +121,15 @@
         return new WeakReference<Activity>(mActivityRef.get());
     }
 
+    @Override
+    public void onActivityStateChange(Activity activity, int newState) {
+        if (newState == ActivityState.PAUSED) {
+            onActivityPaused();
+        } else if (newState == ActivityState.RESUMED) {
+            onActivityResumed();
+        }
+    }
+
     private int generateNextRequestCode() {
         int requestCode = REQUEST_CODE_PREFIX + mNextRequestCode;
         mNextRequestCode = (mNextRequestCode + 1) % REQUEST_CODE_RANGE_SIZE;
diff --git a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
index 0870221..0bf7bd1 100644
--- a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
@@ -236,6 +236,24 @@
     }
 
     /**
+     * For window instances associated with an activity, notifies any listeners
+     * that the activity has been paused.
+     */
+    protected void onActivityPaused() {
+        if (mNativeWindowAndroid == 0) return;
+        nativeOnActivityPaused(mNativeWindowAndroid);
+    }
+
+    /**
+     * For window instances associated with an activity, notifies any listeners
+     * that the activity has been paused.
+     */
+    protected void onActivityResumed() {
+        if (mNativeWindowAndroid == 0) return;
+        nativeOnActivityResumed(mNativeWindowAndroid);
+    }
+
+    /**
      * Responds to the intent result if the intent was created by the native window.
      * @param requestCode Request code of the requested intent.
      * @param resultCode Result code of the requested intent.
@@ -351,6 +369,8 @@
     private native void nativeOnVSync(long nativeWindowAndroid,
                                       long vsyncTimeMicros,
                                       long vsyncPeriodMicros);
+    private native void nativeOnActivityPaused(long nativeWindowAndroid);
+    private native void nativeOnActivityResumed(long nativeWindowAndroid);
     private native void nativeDestroy(long nativeWindowAndroid);
 
 }
diff --git a/ui/android/window_android.cc b/ui/android/window_android.cc
index cb163ff..9a476892 100644
--- a/ui/android/window_android.cc
+++ b/ui/android/window_android.cc
@@ -17,8 +17,8 @@
 using base::android::AttachCurrentThread;
 using base::android::ScopedJavaLocalRef;
 
-WindowAndroid::WindowAndroid(JNIEnv* env, jobject obj)
-  : weak_java_window_(env, obj), compositor_(NULL) {
+WindowAndroid::WindowAndroid(JNIEnv* env, jobject obj) : compositor_(NULL) {
+  java_window_.Reset(env, obj);
 }
 
 void WindowAndroid::Destroy(JNIEnv* env, jobject obj) {
@@ -26,7 +26,7 @@
 }
 
 ScopedJavaLocalRef<jobject> WindowAndroid::GetJavaObject() {
-  return weak_java_window_.get(AttachCurrentThread());
+  return base::android::ScopedJavaLocalRef<jobject>(java_window_);
 }
 
 bool WindowAndroid::RegisterWindowAndroid(JNIEnv* env) {
@@ -43,6 +43,11 @@
                     OnCompositingDidCommit());
 }
 
+void WindowAndroid::OnVisibilityChanged(bool visible) {
+  FOR_EACH_OBSERVER(WindowAndroidObserver, observer_list_,
+                    OnRootWindowVisibilityChanged(visible));
+}
+
 void WindowAndroid::AddObserver(WindowAndroidObserver* observer) {
   if (!observer_list_.HasObserver(observer))
     observer_list_.AddObserver(observer);
@@ -72,10 +77,7 @@
 
 void WindowAndroid::RequestVSyncUpdate() {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> obj = GetJavaObject();
-  if (obj.is_null())
-    return;
-  Java_WindowAndroid_requestVSyncUpdate(env, obj.obj());
+  Java_WindowAndroid_requestVSyncUpdate(env, GetJavaObject().obj());
 }
 
 void WindowAndroid::SetNeedsAnimate() {
@@ -103,6 +105,14 @@
     compositor_->OnVSync(frame_time, vsync_period);
 }
 
+void WindowAndroid::OnActivityResumed(JNIEnv* env, jobject obj) {
+  FOR_EACH_OBSERVER(WindowAndroidObserver, observer_list_, OnActivityResumed());
+}
+
+void WindowAndroid::OnActivityPaused(JNIEnv* env, jobject obj) {
+  FOR_EACH_OBSERVER(WindowAndroidObserver, observer_list_, OnActivityPaused());
+}
+
 // ----------------------------------------------------------------------------
 // Native JNI methods
 // ----------------------------------------------------------------------------
diff --git a/ui/android/window_android.h b/ui/android/window_android.h
index cb201cd9..005f4d67 100644
--- a/ui/android/window_android.h
+++ b/ui/android/window_android.h
@@ -43,6 +43,7 @@
 
   // Compositor callback relay.
   void OnCompositingDidCommit();
+  void OnVisibilityChanged(bool visible);
 
   void AttachCompositor(WindowAndroidCompositor* compositor);
   void DetachCompositor();
@@ -59,11 +60,13 @@
                jlong time_micros,
                jlong period_micros);
   void Animate(base::TimeTicks begin_frame_time);
+  void OnActivityPaused(JNIEnv* env, jobject obj);
+  void OnActivityResumed(JNIEnv* env, jobject obj);
 
  private:
   ~WindowAndroid();
 
-  JavaObjectWeakGlobalRef weak_java_window_;
+  base::android::ScopedJavaGlobalRef<jobject> java_window_;
   gfx::Vector2dF content_offset_;
   WindowAndroidCompositor* compositor_;
 
diff --git a/ui/android/window_android_observer.h b/ui/android/window_android_observer.h
index 05db5ce..4e97d2a5 100644
--- a/ui/android/window_android_observer.h
+++ b/ui/android/window_android_observer.h
@@ -12,12 +12,19 @@
 class UI_ANDROID_EXPORT WindowAndroidObserver {
  public:
   virtual void OnCompositingDidCommit() = 0;
+  virtual void OnRootWindowVisibilityChanged(bool visible) = 0;
   virtual void OnAttachCompositor() = 0;
   virtual void OnDetachCompositor() = 0;
   virtual void OnVSync(base::TimeTicks frame_time,
                        base::TimeDelta vsync_period) = 0;
   virtual void OnAnimate(base::TimeTicks frame_begin_time) {}
 
+  // Note that activity state callbacks will only be made if the WindowAndroid
+  // has been explicitly subscribed to receive them. The observer instance
+  // should account for whether or not this is the case.
+  virtual void OnActivityPaused() = 0;
+  virtual void OnActivityResumed() = 0;
+
  protected:
   virtual ~WindowAndroidObserver() {}
 };
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index a6aa8eb3..a78fa45 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -343,8 +343,8 @@
   return app_list_main_view_->GetPreferredSize();
 }
 
-void AppListView::Paint(gfx::Canvas* canvas, const views::CullSet& cull_set) {
-  views::BubbleDelegateView::Paint(canvas, cull_set);
+void AppListView::OnPaint(gfx::Canvas* canvas) {
+  views::BubbleDelegateView::OnPaint(canvas);
   if (!next_paint_callback_.is_null()) {
     next_paint_callback_.Run();
     next_paint_callback_.Reset();
diff --git a/ui/app_list/views/app_list_view.h b/ui/app_list/views/app_list_view.h
index b62f9d35..27e6e8a 100644
--- a/ui/app_list/views/app_list_view.h
+++ b/ui/app_list/views/app_list_view.h
@@ -99,7 +99,7 @@
 
   // Overridden from views::View:
   gfx::Size GetPreferredSize() const override;
-  void Paint(gfx::Canvas* canvas, const views::CullSet& cull_set) override;
+  void OnPaint(gfx::Canvas* canvas) override;
   void OnThemeChanged() override;
 
   // WidgetDelegate overrides:
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index 14763823..5f2ec39b 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -15,6 +15,7 @@
 #include "ui/app_list/views/app_list_menu_views.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/search_box_view_delegate.h"
+#include "ui/base/ime/text_input_flags.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/events/event.h"
@@ -163,6 +164,8 @@
   search_box_->SetTextColor(kSearchTextColor);
   search_box_->set_placeholder_text_color(kHintTextColor);
   search_box_->set_controller(this);
+  search_box_->SetTextInputType(ui::TEXT_INPUT_TYPE_SEARCH);
+  search_box_->SetTextInputFlags(ui::TEXT_INPUT_FLAG_AUTOCORRECT_OFF);
   content_container_->AddChildView(search_box_);
   layout->SetFlexForView(search_box_, 1);
 
diff --git a/ui/base/layout.cc b/ui/base/layout.cc
index 5aaa7aa4..e49c24b 100644
--- a/ui/base/layout.cc
+++ b/ui/base/layout.cc
@@ -92,6 +92,14 @@
   return kScaleFactorScales[scale_factor];
 }
 
+bool IsSupportedScale(float scale) {
+  for (auto scale_factor_idx : *g_supported_scale_factors) {
+    if (kScaleFactorScales[scale_factor_idx] == scale)
+      return true;
+  }
+  return false;
+}
+
 namespace test {
 
 ScopedSetSupportedScaleFactors::ScopedSetSupportedScaleFactors(
diff --git a/ui/base/layout.h b/ui/base/layout.h
index ab83878..c0ca3cb 100644
--- a/ui/base/layout.h
+++ b/ui/base/layout.h
@@ -54,6 +54,10 @@
 // Returns the image scale for the scale factor passed in.
 UI_BASE_EXPORT float GetScaleForScaleFactor(ScaleFactor scale_factor);
 
+// Returns true if the scale passed in is the list of supported scales for
+// the platform.
+UI_BASE_EXPORT bool IsSupportedScale(float scale);
+
 namespace test {
 // Class which changes the value of GetSupportedScaleFactors() to
 // |new_scale_factors| for the duration of its lifetime.
diff --git a/ui/chromeos/network/network_connect.cc b/ui/chromeos/network/network_connect.cc
index 8222391..380a6db 100644
--- a/ui/chromeos/network/network_connect.cc
+++ b/ui/chromeos/network/network_connect.cc
@@ -142,12 +142,18 @@
     return;
   }
 
-  if (network->type() == shill::kTypeWimax ||
-      network->type() == shill::kTypeVPN) {
+  if (network->type() == shill::kTypeWimax) {
     delegate_->ShowNetworkConfigure(service_path);
     return;
   }
 
+  if (network->type() == shill::kTypeVPN) {
+    // Third-party VPNs handle configuration UI themselves.
+    if (network->vpn_provider_type() != shill::kProviderThirdPartyVpn)
+      delegate_->ShowNetworkConfigure(service_path);
+    return;
+  }
+
   if (network->type() == shill::kTypeCellular) {
     if (network->RequiresActivation()) {
       ActivateCellular(service_path);
diff --git a/ui/events/event.cc b/ui/events/event.cc
index fa115f1..f0804d87 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -505,22 +505,6 @@
 const int MouseWheelEvent::kWheelDelta = 53;
 #endif
 
-void MouseWheelEvent::UpdateForRootTransform(
-    const gfx::Transform& inverted_root_transform) {
-  LocatedEvent::UpdateForRootTransform(inverted_root_transform);
-  gfx::DecomposedTransform decomp;
-  bool success = gfx::DecomposeTransform(&decomp, inverted_root_transform);
-  DCHECK(success);
-  if (decomp.scale[0]) {
-    offset_.set_x(
-        gfx::ToRoundedInt(SkMScalarToFloat(offset_.x() * decomp.scale[0])));
-  }
-  if (decomp.scale[1]) {
-    offset_.set_y(
-        gfx::ToRoundedInt(SkMScalarToFloat(offset_.y() * decomp.scale[1])));
-  }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // TouchEvent
 
diff --git a/ui/events/event.h b/ui/events/event.h
index 70a269d..6a27b0e9 100644
--- a/ui/events/event.h
+++ b/ui/events/event.h
@@ -473,10 +473,6 @@
   int y_offset() const { return offset_.y(); }
   const gfx::Vector2d& offset() const { return offset_; }
 
-  // Overridden from LocatedEvent.
-  void UpdateForRootTransform(
-      const gfx::Transform& inverted_root_transform) override;
-
  private:
   gfx::Vector2d offset_;
 };
diff --git a/ui/events/gesture_detection/motion_event_generic.cc b/ui/events/gesture_detection/motion_event_generic.cc
index 3789ec6..475e9c5 100644
--- a/ui/events/gesture_detection/motion_event_generic.cc
+++ b/ui/events/gesture_detection/motion_event_generic.cc
@@ -197,9 +197,15 @@
   return cancel_event.Pass();
 }
 
-void MotionEventGeneric::PushPointer(const PointerProperties& pointer) {
+size_t MotionEventGeneric::PushPointer(const PointerProperties& pointer) {
   DCHECK_EQ(0U, GetHistorySize());
   pointers_->push_back(pointer);
+  return pointers_->size() - 1;
+}
+
+void MotionEventGeneric::RemovePointerAt(size_t index) {
+  DCHECK_LT(index, pointers_->size());
+  pointers_->erase(pointers_->begin() + index);
 }
 
 void MotionEventGeneric::PushHistoricalEvent(scoped_ptr<MotionEvent> event) {
diff --git a/ui/events/gesture_detection/motion_event_generic.h b/ui/events/gesture_detection/motion_event_generic.h
index 9d7733f..f12ec44 100644
--- a/ui/events/gesture_detection/motion_event_generic.h
+++ b/ui/events/gesture_detection/motion_event_generic.h
@@ -70,7 +70,16 @@
   float GetHistoricalY(size_t pointer_index,
                        size_t historical_index) const override;
 
-  void PushPointer(const PointerProperties& pointer);
+  // Adds |pointer| to the set of pointers returning the index it was added at.
+  size_t PushPointer(const PointerProperties& pointer);
+
+  // Removes the PointerProperties at |index|.
+  void RemovePointerAt(size_t index);
+
+  PointerProperties& pointer(size_t index) { return pointers_[index]; }
+  const PointerProperties& pointer(size_t index) const {
+    return pointers_[index];
+  }
 
   // Add an event to the history. |this| and |event| must have the same pointer
   // count and must both have an action of ACTION_MOVE.
@@ -93,11 +102,6 @@
 
   void PopPointer();
 
-  PointerProperties& pointer(size_t index) { return pointers_[index]; }
-  const PointerProperties& pointer(size_t index) const {
-    return pointers_[index];
-  }
-
  private:
   enum { kTypicalMaxPointerCount = 5 };
 
diff --git a/ui/events/gesture_detection/motion_event_generic_unittest.cc b/ui/events/gesture_detection/motion_event_generic_unittest.cc
index 23762e3f..a67646b 100644
--- a/ui/events/gesture_detection/motion_event_generic_unittest.cc
+++ b/ui/events/gesture_detection/motion_event_generic_unittest.cc
@@ -130,7 +130,7 @@
 
   MotionEventGeneric event1(event0);
   pointer.id = 7;
-  event1.PushPointer(pointer);
+  EXPECT_EQ(1u, event1.PushPointer(pointer));
   EXPECT_EQ(0, event1.FindPointerIndexOfId(0));
   EXPECT_EQ(1, event1.FindPointerIndexOfId(7));
   EXPECT_EQ(-1, event1.FindPointerIndexOfId(6));
@@ -138,7 +138,7 @@
 
   MotionEventGeneric event2(event1);
   pointer.id = 3;
-  event2.PushPointer(pointer);
+  EXPECT_EQ(2u, event2.PushPointer(pointer));
   EXPECT_EQ(0, event2.FindPointerIndexOfId(0));
   EXPECT_EQ(1, event2.FindPointerIndexOfId(7));
   EXPECT_EQ(2, event2.FindPointerIndexOfId(3));
@@ -146,6 +146,32 @@
   EXPECT_EQ(-1, event2.FindPointerIndexOfId(2));
 }
 
+TEST(MotionEventGenericTest, RemovePointerAt) {
+  base::TimeTicks event_time = base::TimeTicks::Now();
+  PointerProperties pointer;
+  pointer.id = 0;
+  MotionEventGeneric event(MotionEvent::ACTION_DOWN, event_time, pointer);
+
+  pointer.id = 7;
+  EXPECT_EQ(1u, event.PushPointer(pointer));
+  EXPECT_EQ(2u, event.GetPointerCount());
+
+  // Remove from the end.
+  event.RemovePointerAt(1);
+  EXPECT_EQ(1u, event.GetPointerCount());
+  EXPECT_EQ(-1, event.FindPointerIndexOfId(7));
+  EXPECT_EQ(0, event.FindPointerIndexOfId(0));
+
+  EXPECT_EQ(1u, event.PushPointer(pointer));
+  EXPECT_EQ(2u, event.GetPointerCount());
+
+  // Remove from the beginning.
+  event.RemovePointerAt(0);
+  EXPECT_EQ(1u, event.GetPointerCount());
+  EXPECT_EQ(0, event.FindPointerIndexOfId(7));
+  EXPECT_EQ(-1, event.FindPointerIndexOfId(0));
+}
+
 TEST(MotionEventGenericTest, ToString) {
   base::TimeTicks event_time = base::TimeTicks::Now();
   base::TimeTicks historical_event_time0 =
diff --git a/ui/events/ozone/evdev/capture_device_capabilities.py b/ui/events/ozone/evdev/capture_device_capabilities.py
new file mode 100755
index 0000000..b3b3d3d71
--- /dev/null
+++ b/ui/events/ozone/evdev/capture_device_capabilities.py
@@ -0,0 +1,136 @@
+#!/usr/bin/python
+# 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.
+
+"""Code generator DeviceCapabilities literal."""
+
+import argparse
+import ctypes
+import evdev
+import os
+import sys
+
+
+TEST_DATA_GROUP_SIZE = 64  # Aligns with sysfs on 64-bit devices.
+
+
+def bits_to_groups(bits):
+  return (bits + TEST_DATA_GROUP_SIZE - 1) / TEST_DATA_GROUP_SIZE
+
+
+# As in /sys/class/input/input*/capabilities/*
+def serialize_bitfield(bitfield, max_bit):
+  result = ""
+  group_count = bits_to_groups(max_bit)
+  for group in xrange(group_count - 1, -1, -1):
+    group_val = 0
+    for group_bit in xrange(TEST_DATA_GROUP_SIZE):
+      code = group * TEST_DATA_GROUP_SIZE + group_bit
+      if code in bitfield:
+        group_val |= (1 << group_bit)
+    if group_val or result:
+      result += '%x' % group_val
+      if group:
+        result += ' '
+  if not result:
+    return '0'
+  return result
+
+
+def dump_absinfo(out, capabilities, identifier):
+  out.write('const DeviceAbsoluteAxis %s[] = {\n' % identifier)
+
+  for code, absinfo in capabilities[evdev.ecodes.EV_ABS]:
+    # Set value := 0 to make it deterministic.
+    code_name = evdev.ecodes.bytype[evdev.ecodes.EV_ABS][code]
+    absinfo_struct = (0, absinfo.min, absinfo.max, absinfo.fuzz, absinfo.flat,
+                      absinfo.resolution)
+    data = (code_name,) + absinfo_struct
+    out.write('    {%s, {%d, %d, %d, %d, %d, %d}},\n' % data)
+
+  out.write('};\n')
+
+
+def dump_capabilities(out, dev, identifier):
+  capabilities = dev.capabilities()
+  has_abs = evdev.ecodes.EV_ABS in capabilities
+
+  sysfs_path = '/sys/class/input/' + os.path.basename(dev.fn)
+
+  # python-evdev is missing some features
+  uniq = open(sysfs_path + '/device/uniq', 'r').read().strip()
+  prop = open(sysfs_path + '/device/properties', 'r').read().strip()
+  ff = open(sysfs_path + '/device/capabilities/ff', 'r').read().strip()
+
+  # python-evdev parses the id wrong.
+  bustype = open(sysfs_path + '/device/id/bustype', 'r').read().strip()
+  vendor = open(sysfs_path + '/device/id/vendor', 'r').read().strip()
+  product = open(sysfs_path + '/device/id/product', 'r').read().strip()
+  version = open(sysfs_path + '/device/id/version', 'r').read().strip()
+
+  # python-evdev drops EV_REP from the event set.
+  ev = open(sysfs_path + '/device/capabilities/ev', 'r').read().strip()
+
+  if ctypes.sizeof(ctypes.c_long()) != 8:
+    # /sys/class/input/*/properties format is word size dependent.
+    # Could be fixed by regrouping but for now, just raise an error.
+    raise ValueError("Must be run on 64-bit machine")
+
+  key_bits = capabilities.get(evdev.ecodes.EV_KEY, [])
+  rel_bits = capabilities.get(evdev.ecodes.EV_REL, [])
+  abs_bits = [abs[0] for abs in capabilities.get(evdev.ecodes.EV_ABS, [])]
+  msc_bits = capabilities.get(evdev.ecodes.EV_MSC, [])
+  sw_bits = capabilities.get(evdev.ecodes.EV_SW, [])
+  led_bits = capabilities.get(evdev.ecodes.EV_LED, [])
+
+  fields = [
+    ('path', os.path.realpath(sysfs_path)),
+    ('name', dev.name),
+    ('phys', dev.phys),
+    ('uniq', uniq),
+    ('bustype', bustype),
+    ('vendor', vendor),
+    ('product', product),
+    ('version', version),
+    ('prop', prop),
+    ('ev', ev),
+    ('key', serialize_bitfield(key_bits, evdev.ecodes.KEY_CNT)),
+    ('rel', serialize_bitfield(rel_bits, evdev.ecodes.REL_CNT)),
+    ('abs', serialize_bitfield(abs_bits, evdev.ecodes.ABS_CNT)),
+    ('msc', serialize_bitfield(msc_bits, evdev.ecodes.MSC_CNT)),
+    ('sw', serialize_bitfield(sw_bits, evdev.ecodes.SW_CNT)),
+    ('led', serialize_bitfield(led_bits, evdev.ecodes.LED_CNT)),
+    ('ff', ff),
+  ]
+
+  if has_abs:
+    absinfo_identifier = identifier + 'AbsAxes'
+    dump_absinfo(out, capabilities, absinfo_identifier)
+
+  out.write('const DeviceCapabilities %s = {\n' % identifier)
+  for name, val in fields:
+    out.write('    /* %s */ "%s",\n' % (name, val))
+
+  if has_abs:
+    out.write('    %s,\n' % absinfo_identifier)
+    out.write('    arraysize(%s),\n' % absinfo_identifier)
+
+  out.write('};\n')
+
+
+def main(argv):
+  parser = argparse.ArgumentParser()
+  parser.add_argument('device')
+  parser.add_argument('identifier')
+  args = parser.parse_args(argv)
+
+  dev = evdev.InputDevice(args.device)
+  out = sys.stdout
+
+  dump_capabilities(out, dev, args.identifier)
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/ui/events/ozone/evdev/event_device_test_util.cc b/ui/events/ozone/evdev/event_device_test_util.cc
index b2709b1..b5256b7 100644
--- a/ui/events/ozone/evdev/event_device_test_util.cc
+++ b/ui/events/ozone/evdev/event_device_test_util.cc
@@ -76,30 +76,6 @@
 
 }  // namespace
 
-// # Script to generate DeviceCapabilities literal.
-// cd /sys/class/input/input?
-//
-// test "$(uname -m)" = x86_64 && cat <<EOF
-// const DeviceCapabilities device = {
-//     /* path */ "$(readlink -f .)",
-//     /* name */ "$(cat device/name)",
-//     /* phys */ "$(cat device/phys)",
-//     /* uniq */ "$(cat device/uniq)",
-//     /* bustype */ "$(cat device/id/bustype)",
-//     /* vendor */ "$(cat device/id/vendor)",
-//     /* product */ "$(cat device/id/product)",
-//     /* version */ "$(cat device/id/version)",
-//     /* prop */ "$(cat device/properties)",
-//     /* ev */ "$(cat device/capabilities/ev)",
-//     /* key */ "$(cat device/capabilities/key)",
-//     /* rel */ "$(cat device/capabilities/rel)",
-//     /* abs */ "$(cat device/capabilities/abs)",
-//     /* msc */ "$(cat device/capabilities/msc)",
-//     /* led */ "$(cat device/capabilities/led)",
-//     /* ff */ "$(cat device/capabilities/ff)",
-// };
-// EOF
-
 // Captured from Chromebook Pixel.
 const DeviceCapabilities kLinkKeyboard = {
     /* path */ "/sys/devices/platform/i8042/serio0/input/input6/event6",
@@ -116,11 +92,24 @@
     /* rel */ "0",
     /* abs */ "0",
     /* msc */ "10",
+    /* sw */ "0",
     /* led */ "7",
     /* ff */ "0",
 };
 
 // Captured from Chromebook Pixel.
+const DeviceAbsoluteAxis kLinkTouchscreenAbsAxes[] = {
+    {ABS_X, {0, 0, 2559, 0, 0, 20}},
+    {ABS_Y, {0, 0, 1699, 0, 0, 20}},
+    {ABS_PRESSURE, {0, 0, 255, 0, 0, 0}},
+    {ABS_MT_SLOT, {0, 0, 15, 0, 0, 0}},
+    {ABS_MT_TOUCH_MAJOR, {0, 0, 938, 0, 0, 0}},
+    {ABS_MT_ORIENTATION, {0, -3, 4, 0, 0, 0}},
+    {ABS_MT_POSITION_X, {0, 0, 2559, 0, 0, 20}},
+    {ABS_MT_POSITION_Y, {0, 0, 1699, 0, 0, 20}},
+    {ABS_MT_TRACKING_ID, {0, 0, 65535, 0, 0, 0}},
+    {ABS_MT_PRESSURE, {0, 0, 255, 0, 0, 0}},
+};
 const DeviceCapabilities kLinkTouchscreen = {
     /* path */ "/sys/devices/pci0000:00/0000:00:02.0/i2c-2/2-004a/"
                "input/input7/event7",
@@ -137,11 +126,26 @@
     /* rel */ "0",
     /* abs */ "671800001000003",
     /* msc */ "0",
+    /* sw */ "0",
     /* led */ "0",
     /* ff */ "0",
+    kLinkTouchscreenAbsAxes,
+    arraysize(kLinkTouchscreenAbsAxes),
 };
 
 // Captured from Chromebook Pixel.
+const DeviceAbsoluteAxis kLinkTouchpadAbsAxes[] = {
+    {ABS_X, {0, 0, 2040, 0, 0, 20}},
+    {ABS_Y, {0, 0, 1360, 0, 0, 20}},
+    {ABS_PRESSURE, {0, 0, 255, 0, 0, 0}},
+    {ABS_MT_SLOT, {0, 0, 9, 0, 0, 0}},
+    {ABS_MT_TOUCH_MAJOR, {0, 0, 1878, 0, 0, 0}},
+    {ABS_MT_ORIENTATION, {0, -3, 4, 0, 0, 0}},
+    {ABS_MT_POSITION_X, {0, 0, 2040, 0, 0, 20}},
+    {ABS_MT_POSITION_Y, {0, 0, 1360, 0, 0, 20}},
+    {ABS_MT_TRACKING_ID, {0, 0, 65535, 0, 0, 0}},
+    {ABS_MT_PRESSURE, {0, 0, 255, 0, 0, 0}},
+};
 const DeviceCapabilities kLinkTouchpad = {
     /* path */ "/sys/devices/pci0000:00/0000:00:02.0/i2c-1/1-004b/"
                "input/input8/event8",
@@ -158,8 +162,11 @@
     /* rel */ "0",
     /* abs */ "671800001000003",
     /* msc */ "0",
+    /* sw */ "0",
     /* led */ "0",
     /* ff */ "0",
+    kLinkTouchpadAbsAxes,
+    arraysize(kLinkTouchpadAbsAxes),
 };
 
 // Captured from generic HP KU-1156 USB keyboard.
@@ -180,11 +187,15 @@
     /* rel */ "0",
     /* abs */ "0",
     /* msc */ "10",
+    /* sw */ "0",
     /* led */ "7",
     /* ff */ "0",
 };
 
 // Captured from generic HP KU-1156 USB keyboard (2nd device with media keys).
+const DeviceAbsoluteAxis kHpUsbKeyboard_ExtraAbsAxes[] = {
+    {ABS_VOLUME, {0, 0, 767, 0, 0, 0}},
+};
 const DeviceCapabilities kHpUsbKeyboard_Extra = {
     /* path */ "/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/"
                "input/input18/event16",
@@ -202,8 +213,11 @@
     /* rel */ "40",
     /* abs */ "100000000",
     /* msc */ "10",
+    /* sw */ "0",
     /* led */ "0",
     /* ff */ "0",
+    kHpUsbKeyboard_ExtraAbsAxes,
+    arraysize(kHpUsbKeyboard_ExtraAbsAxes),
 };
 
 // Captured from Dell MS111-L 3-Button Optical USB Mouse.
@@ -223,11 +237,16 @@
     /* rel */ "143",
     /* abs */ "0",
     /* msc */ "10",
+    /* sw */ "0",
     /* led */ "0",
     /* ff */ "0",
 };
 
 // Captured from "Mimo Touch 2" Universal DisplayLink monitor.
+const DeviceAbsoluteAxis kMimoTouch2TouchscreenAbsAxes[] = {
+    {ABS_X, {0, 0, 2047, 0, 0, 0}},
+    {ABS_Y, {0, 0, 2047, 0, 0, 0}},
+};
 const DeviceCapabilities kMimoTouch2Touchscreen = {
     /* path */ "/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3.2/"
                "2-1.3.2:1.0/input/input15/event14",
@@ -244,11 +263,20 @@
     /* rel */ "0",
     /* abs */ "3",
     /* msc */ "0",
+    /* sw */ "0",
     /* led */ "0",
     /* ff */ "0",
+    kMimoTouch2TouchscreenAbsAxes,
+    arraysize(kMimoTouch2TouchscreenAbsAxes),
 };
 
 // Captured from Wacom Intuos Pen and Touch Small Tablet.
+const DeviceAbsoluteAxis kWacomIntuosPtS_PenAbsAxes[] = {
+    {ABS_X, {0, 0, 15200, 4, 0, 100}},
+    {ABS_Y, {0, 0, 9500, 4, 0, 100}},
+    {ABS_PRESSURE, {0, 0, 1023, 0, 0, 0}},
+    {ABS_DISTANCE, {0, 0, 31, 0, 0, 0}},
+};
 const DeviceCapabilities kWacomIntuosPtS_Pen = {
     /* path */ "/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2.3/"
                "2-1.2.3:1.0/input/input9/event9",
@@ -265,11 +293,24 @@
     /* rel */ "0",
     /* abs */ "3000003",
     /* msc */ "0",
+    /* sw */ "0",
     /* led */ "0",
     /* ff */ "0",
+    kWacomIntuosPtS_PenAbsAxes,
+    arraysize(kWacomIntuosPtS_PenAbsAxes),
 };
 
 // Captured from Wacom Intuos Pen and Touch Small Tablet.
+const DeviceAbsoluteAxis kWacomIntuosPtS_FingerAbsAxes[] = {
+    {ABS_X, {0, 0, 4096, 4, 0, 26}},
+    {ABS_Y, {0, 0, 4096, 4, 0, 43}},
+    {ABS_MT_SLOT, {0, 0, 15, 0, 0, 0}},
+    {ABS_MT_TOUCH_MAJOR, {0, 0, 4096, 0, 0, 0}},
+    {ABS_MT_TOUCH_MINOR, {0, 0, 4096, 0, 0, 0}},
+    {ABS_MT_POSITION_X, {0, 0, 4096, 4, 0, 26}},
+    {ABS_MT_POSITION_Y, {0, 0, 4096, 4, 0, 43}},
+    {ABS_MT_TRACKING_ID, {0, 0, 65535, 0, 0, 0}},
+};
 const DeviceCapabilities kWacomIntuosPtS_Finger = {
     /* path */ "/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2.3/"
                "2-1.2.3:1.1/input/input10/event10",
@@ -286,11 +327,17 @@
     /* rel */ "0",
     /* abs */ "263800000000003",
     /* msc */ "0",
+    /* sw */ "4000",
     /* led */ "0",
     /* ff */ "0",
+    kWacomIntuosPtS_FingerAbsAxes,
+    arraysize(kWacomIntuosPtS_FingerAbsAxes),
 };
 
 // Captured from Logitech Wireless Touch Keyboard K400.
+const DeviceAbsoluteAxis kLogitechTouchKeyboardK400AbsAxes[] = {
+    {ABS_VOLUME, {0, 1, 652, 0, 0, 0}},
+};
 const DeviceCapabilities kLogitechTouchKeyboardK400 = {
     /* path */ "/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2.3/"
                "2-1.2.3:1.2/0003:046D:C52B.0006/input/input19/event17",
@@ -309,8 +356,11 @@
     /* rel */ "1c3",
     /* abs */ "100000000",
     /* msc */ "10",
+    /* sw */ "0",
     /* led */ "1f",
     /* ff */ "0",
+    kLogitechTouchKeyboardK400AbsAxes,
+    arraysize(kLogitechTouchKeyboardK400AbsAxes),
 };
 
 bool CapabilitiesToDeviceInfo(const DeviceCapabilities& capabilities,
@@ -350,6 +400,11 @@
     return false;
   devinfo->SetProps(&prop_bits[0], prop_bits.size());
 
+  for (size_t i = 0; i < capabilities.abs_axis_count; ++i) {
+    const DeviceAbsoluteAxis& axis = capabilities.abs_axis[i];
+    devinfo->SetAbsInfo(axis.code, axis.absinfo);
+  }
+
   return true;
 }
 
diff --git a/ui/events/ozone/evdev/event_device_test_util.h b/ui/events/ozone/evdev/event_device_test_util.h
index fecd44a..46afb6ab 100644
--- a/ui/events/ozone/evdev/event_device_test_util.h
+++ b/ui/events/ozone/evdev/event_device_test_util.h
@@ -5,10 +5,17 @@
 #ifndef UI_EVENTS_OZONE_EVDEV_EVENT_DEVICE_TEST_UTIL_H_
 #define UI_EVENTS_OZONE_EVDEV_EVENT_DEVICE_TEST_UTIL_H_
 
+#include <linux/input.h>
+
 namespace ui {
 
 class EventDeviceInfo;
 
+struct DeviceAbsoluteAxis {
+  unsigned int code;
+  input_absinfo absinfo;
+};
+
 struct DeviceCapabilities {
   // Full sysfs path (readlink -f /sys/class/input/event*)
   const char* path;
@@ -42,6 +49,10 @@
   const char* sw;
   const char* led;
   const char* ff;
+
+  // EVIOCGABS.
+  const DeviceAbsoluteAxis* abs_axis;
+  size_t abs_axis_count;
 };
 
 bool CapabilitiesToDeviceInfo(const DeviceCapabilities& capabilities,
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev.cc b/ui/events/ozone/evdev/touch_event_converter_evdev.cc
index 2be21ce..bfcb940e 100644
--- a/ui/events/ozone/evdev/touch_event_converter_evdev.cc
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev.cc
@@ -80,6 +80,7 @@
     : EventConverterEvdev(fd, path, id, type),
       dispatcher_(dispatcher),
       syn_dropped_(false),
+      is_type_a_(false),
       touch_points_(0),
       current_slot_(0) {
 }
@@ -114,8 +115,6 @@
                                   cal.bezel_bottom);
   }
 
-  native_size_ = gfx::Size(x_num_tuxels_, y_num_tuxels_);
-
   events_.resize(touch_points_);
   for (size_t i = 0; i < events_.size(); ++i) {
     events_[i].finger_ = info.GetSlotValue(ABS_MT_TRACKING_ID, i);
@@ -143,7 +142,7 @@
 }
 
 gfx::Size TouchEventConverterEvdev::GetTouchscreenSize() const {
-  return native_size_;
+  return gfx::Size(x_num_tuxels_, y_num_tuxels_);
 }
 
 int TouchEventConverterEvdev::GetTouchPoints() const {
@@ -254,6 +253,16 @@
       } else {
         ReportEvents(EventConverterEvdev::TimeDeltaFromInputEvent(input));
       }
+      if (is_type_a_)
+        current_slot_ = 0;
+      break;
+    case SYN_MT_REPORT:
+      // For type A devices, we just get a stream of all current contacts,
+      // in some arbitrary order.
+      events_[current_slot_].type_ = ET_TOUCH_PRESSED;
+      if (events_.size() - 1 > current_slot_)
+        current_slot_++;
+      is_type_a_ = true;
       break;
     case SYN_DROPPED:
       // Some buffer has overrun. We ignore all events up to and
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev.h b/ui/events/ozone/evdev/touch_event_converter_evdev.h
index f25ec7e7..6f09a979 100644
--- a/ui/events/ozone/evdev/touch_event_converter_evdev.h
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev.h
@@ -77,6 +77,9 @@
   // Set if we have seen a SYN_DROPPED and not yet re-synced with the device.
   bool syn_dropped_;
 
+  // Set if this is a type A device (uses SYN_MT_REPORT).
+  bool is_type_a_;
+
   // Pressure values.
   int pressure_min_;
   int pressure_max_;  // Used to normalize pressure values.
@@ -89,9 +92,6 @@
   float y_min_tuxels_;
   float y_num_tuxels_;
 
-  // Size of the touchscreen as reported by the driver.
-  gfx::Size native_size_;
-
   // Number of touch points reported by driver
   int touch_points_;
 
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
index c6d93c4..eaec9b1 100644
--- a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
@@ -448,6 +448,28 @@
   EXPECT_FLOAT_EQ(.5f, ev1.pressure);
 }
 
+TEST_F(TouchEventConverterEvdevTest, TypeA) {
+  ui::MockTouchEventConverterEvdev* dev = device();
+
+  struct input_event mock_kernel_queue_press0[] = {
+    {{0, 0}, EV_ABS, ABS_MT_TOUCH_MAJOR, 3},
+    {{0, 0}, EV_ABS, ABS_MT_PRESSURE, 45},
+    {{0, 0}, EV_ABS, ABS_MT_POSITION_X, 42},
+    {{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 51},
+    {{0, 0}, EV_SYN, SYN_MT_REPORT, 0},
+    {{0, 0}, EV_ABS, ABS_MT_PRESSURE, 45},
+    {{0, 0}, EV_ABS, ABS_MT_POSITION_X, 61},
+    {{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 71},
+    {{0, 0}, EV_SYN, SYN_MT_REPORT, 0},
+    {{0, 0}, EV_SYN, SYN_REPORT, 0}
+  };
+
+  // Check that two events are generated.
+  dev->ConfigureReadMock(mock_kernel_queue_press0, 10, 0);
+  dev->ReadNow();
+  EXPECT_EQ(2u, size());
+}
+
 TEST_F(TouchEventConverterEvdevTest, Unsync) {
   ui::MockTouchEventConverterEvdev* dev = device();
 
diff --git a/ui/file_manager/audio_player/manifest.json b/ui/file_manager/audio_player/manifest.json
index 7a15cf2..2ef7522 100644
--- a/ui/file_manager/audio_player/manifest.json
+++ b/ui/file_manager/audio_player/manifest.json
@@ -19,7 +19,6 @@
   "permissions": [
     "commandLinePrivate",
     "fileSystem",
-    "fileBrowserHandler",
     "fileManagerPrivate",
     {
       "fileSystem": ["requestFileSystem", "write"]
diff --git a/ui/file_manager/file_manager/background/js/media_scanner.js b/ui/file_manager/file_manager/background/js/media_scanner.js
index 4a6227de..02e411e 100644
--- a/ui/file_manager/file_manager/background/js/media_scanner.js
+++ b/ui/file_manager/file_manager/background/js/media_scanner.js
@@ -12,11 +12,22 @@
 /**
  * Initiates scanning.
  *
- * @param {!Array.<!Entry>} entries Must be non-empty.
+ * @param {!DirectoryEntry} directory
  * @return {!importer.ScanResult} ScanResult object representing the scan
  *     job both while in-progress and when completed.
  */
-importer.MediaScanner.prototype.scan;
+importer.MediaScanner.prototype.scanDirectory;
+
+/**
+ * Initiates scanning.
+ *
+ * @param {!Array<!FileEntry>} entries Must be non-empty, and all entires
+ *     must be of a supported media type. Individually supplied files
+ *     are not subject to deduplication.
+ * @return {!importer.ScanResult} ScanResult object representing the scan
+ *     job for the explicitly supplied entries.
+ */
+importer.MediaScanner.prototype.scanFiles;
 
 /**
  * Adds an observer, which will be notified on scan events.
@@ -57,7 +68,7 @@
     return new importer.DefaultScanResult(hashGenerator);
   };
 
-  /** @private {!Array.<!importer.ScanObserver>} */
+  /** @private {!Array<!importer.ScanObserver>} */
   this.observers_ = [];
 
   /**
@@ -90,11 +101,7 @@
 };
 
 /** @override */
-importer.DefaultMediaScanner.prototype.scan = function(entries) {
-  if (entries.length == 0) {
-    throw new Error('Cannot scan empty list of entries.');
-  }
-
+importer.DefaultMediaScanner.prototype.scanDirectory = function(directory) {
   var scan = this.createScanResult_();
   var watcher = this.watcherFactory_(
       /** @this {importer.DefaultMediaScanner} */
@@ -103,8 +110,35 @@
         this.notify_(importer.ScanEvent.INVALIDATED, scan);
       }.bind(this));
 
-  var scanPromises = entries.map(
-      this.scanEntry_.bind(this, scan, watcher));
+  this.crawlDirectory_(directory, watcher)
+      .then(this.scanMediaFiles_.bind(this, scan))
+      .then(scan.resolve)
+      .catch(scan.reject);
+
+  scan.whenFinal()
+      .then(
+          /** @this {importer.DefaultMediaScanner} */
+          function() {
+            this.notify_(importer.ScanEvent.FINALIZED, scan);
+          }.bind(this));
+
+  return scan;
+};
+
+/** @override */
+importer.DefaultMediaScanner.prototype.scanFiles = function(entries) {
+  if (entries.length === 0) {
+    throw new Error('Cannot scan empty list.');
+  }
+  var scan = this.createScanResult_();
+  var watcher = this.watcherFactory_(
+      /** @this {importer.DefaultMediaScanner} */
+      function() {
+        scan.invalidateScan();
+        this.notify_(importer.ScanEvent.INVALIDATED, scan);
+      }.bind(this));
+
+  var scanPromises = entries.map(this.onUniqueFileFound_.bind(this, scan));
 
   Promise.all(scanPromises)
       .then(scan.resolve)
@@ -120,6 +154,40 @@
   return scan;
 };
 
+/** @const {number} */
+importer.DefaultMediaScanner.SCAN_BATCH_SIZE = 1;
+
+/**
+ * @param {!importer.DefaultScanResult} scan
+ * @param  {!Array<!FileEntry>} entries
+ * @return {!Promise} Resolves when scanning is finished.
+ * @private
+ */
+importer.DefaultMediaScanner.prototype.scanMediaFiles_ =
+    function(scan, entries) {
+  var handleFileEntry = this.onFileEntryFound_.bind(this, scan);
+
+  /**
+   * @param {number} begin The beginning offset in the list of entries
+   *     to process.
+   * @return {!Promise}
+   */
+  var scanChunk = function(begin) {
+    // the second arg to slice is an exclusive end index, so we +1 batch size.
+    var end = begin + importer.DefaultMediaScanner.SCAN_BATCH_SIZE + 1;
+    return Promise.all(
+        entries.slice(begin, end).map(handleFileEntry))
+        .then(
+            function() {
+              if (end < entries.length) {
+                return scanChunk(end);
+              }
+            });
+  };
+
+  return scanChunk(0);
+};
+
 /**
  * Notifies all listeners at some point in the near future.
  *
@@ -136,55 +204,21 @@
 };
 
 /**
- * Resolves the entry by either:
- * a) recursing on it (when a directory)
- * b) adding it to the results (when a media type file)
- * c) ignoring it, if neither a or b
+ * Finds all files media files beneath directory AND adds directory
+ * watchers for each encountered directory.
  *
- * @param {!importer.DefaultScanResult} scan
+ * @param {!DirectoryEntry} directory
  * @param {!importer.DirectoryWatcher} watcher
- * @param {!Entry} entry
- *
- * @return {!Promise}
+ * @return {!Promise<!Array<!FileEntry>>}
  * @private
  */
-importer.DefaultMediaScanner.prototype.scanEntry_ =
-    function(scan, watcher, entry) {
-
-  if (entry.isDirectory) {
-    return this.scanDirectory_(
-      scan,
-      watcher,
-      /** @type {!DirectoryEntry} */ (entry));
-  }
-
-  // Since this entry is by client code (and presumably the user)
-  // we add it directly (skipping over the history dupe check).
-  return this.onUniqueFileFound_(scan, /** @type {!FileEntry} */ (entry));
-};
-
-/**
- * Finds all files beneath directory.
- *
- * @param {!importer.DefaultScanResult} scan
- * @param {!importer.DirectoryWatcher} watcher
- * @param {!DirectoryEntry} entry
- * @return {!Promise}
- * @private
- */
-importer.DefaultMediaScanner.prototype.scanDirectory_ =
-    function(scan, watcher, entry) {
-  // Collect promises for all files being added to results.
-  // The directory scan promise can't resolve until all
-  // file entries are completely promised.
-  var promises = [];
+importer.DefaultMediaScanner.prototype.crawlDirectory_ =
+    function(directory, watcher) {
+  var mediaFiles = [];
 
   return fileOperationUtil.findEntriesRecursively(
-      entry,
-      /**
-       * @param  {!Entry} entry
-       * @this {importer.DefaultMediaScanner}
-       */
+      directory,
+      /** @param  {!Entry} entry */
       function(entry) {
         if (watcher.triggered) {
           return;
@@ -195,14 +229,14 @@
           // function findEntriesRecursively does that. So we
           // just watch the directory for modifications, and that's it.
           watcher.addDirectory(/** @type {!DirectoryEntry} */(entry));
-          return;
+        } else if (importer.isEligibleType(entry)) {
+          mediaFiles.push(/** @type {!FileEntry} */ (entry));
         }
-
-        promises.push(
-            this.onFileEntryFound_(scan, /** @type {!FileEntry} */(entry)));
-
-      }.bind(this))
-      .then(Promise.all.bind(Promise, promises));
+      })
+      .then(
+          function() {
+            return mediaFiles;
+          });
 };
 
 /**
@@ -343,7 +377,7 @@
 
   /**
    * List of file entries found while scanning.
-   * @private {!Array.<!FileEntry>}
+   * @private {!Array<!FileEntry>}
    */
   this.fileEntries_ = [];
 
diff --git a/ui/file_manager/file_manager/background/js/media_scanner_unittest.js b/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
index 7706930..6eae02e 100644
--- a/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
+++ b/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
@@ -58,7 +58,7 @@
 function testEmptySourceList() {
   assertThrows(
     function() {
-      scanner.scan([]);
+      scanner.scanFiles([]);
     });
 }
 
@@ -76,7 +76,7 @@
                * @param {!DirectoryEntry} root
                */
               function(root) {
-                var results = scanner.scan([root]);
+                var results = scanner.scanDirectory(root);
                 assertFalse(results.isFinal());
               }),
       callback);
@@ -99,7 +99,7 @@
             // We kick this off first so we can capture the result for
             // use in an assert. Promises ensure the scan won't finish
             // until after our funciton is fully processed.
-            var result = scanner.scan([root]);
+            var result = scanner.scanDirectory(root);
             scanner.addObserver(
                 function(eventType, scanResult) {
                   assertEquals(importer.ScanEvent.FINALIZED, eventType);
@@ -114,6 +114,34 @@
 }
 
 /**
+ * Verifies that scanFiles slurps up all specified files.
+ */
+function testScanFiles(callback) {
+  var filenames = [
+    'foo',
+    'foo.jpg',
+    'bar.gif',
+    'baz.avi'
+  ];
+  var expectedFiles = [
+    '/testScanFiles/foo.jpg',
+    '/testScanFiles/bar.gif',
+    '/testScanFiles/baz.avi'
+  ];
+  reportPromise(
+      makeTestFileSystemRoot('testScanFiles')
+          .then(populateDir.bind(null, filenames))
+          .then(fileOperationUtil.gatherEntriesRecursively)
+          .then(
+              /** @param {!Array<!FileEntry>} files */
+              function(files) {
+                return scanner.scanFiles(files).whenFinal();
+              })
+          .then(assertResults.bind(null, expectedFiles)),
+      callback);
+}
+
+/**
  * Verifies that scanning a simple single-level directory structure works.
  */
 function testEmptyScanResults(callback) {
@@ -130,7 +158,7 @@
                * @param {!DirectoryEntry} root
                */
               function(root) {
-                return scanner.scan([root]).whenFinal();
+                return scanner.scanDirectory(root).whenFinal();
               })
           .then(assertResults.bind(null, [])),
       callback);
@@ -162,7 +190,7 @@
                * @param {!DirectoryEntry} root
                */
               function(root) {
-                return scanner.scan([root]).whenFinal();
+                return scanner.scanDirectory(root).whenFinal();
               })
           .then(assertResults.bind(null, expectedFiles)),
       callback);
@@ -209,7 +237,7 @@
                * @param {!DirectoryEntry} root
                */
               function(root) {
-                return scanner.scan([root]).whenFinal();
+                return scanner.scanDirectory(root).whenFinal();
               })
           .then(assertResults.bind(null, expectedFiles)),
       callback);
@@ -220,23 +248,23 @@
     'foo.jpg',
     'bar',
     [
-      'foo.0',
+      'dir1',
       'bar.0.jpg'
     ],
     [
-      'foo.1',
+      'dir2',
       'bar.1.gif',
       [
-        'foo.1.0',
+        'dir3',
         'bar.1.0.avi'
       ]
     ]
   ];
   var expectedFiles = [
     '/testMultiLevel/foo.jpg',
-    '/testMultiLevel/foo.0/bar.0.jpg',
-    '/testMultiLevel/foo.1/bar.1.gif',
-    '/testMultiLevel/foo.1/foo.1.0/bar.1.0.avi'
+    '/testMultiLevel/dir1/bar.0.jpg',
+    '/testMultiLevel/dir2/bar.1.gif',
+    '/testMultiLevel/dir2/dir3/bar.1.0.avi'
   ];
 
   reportPromise(
@@ -248,53 +276,7 @@
                * @param {!DirectoryEntry} root
                */
               function(root) {
-                return scanner.scan([root]).whenFinal();
-              })
-          .then(assertResults.bind(null, expectedFiles)),
-      callback);
-}
-
-function testMultipleDirectories(callback) {
-  var filenames = [
-    'foo',
-    'bar',
-    [
-      'foo.0',
-      'bar.0.jpg'
-    ],
-    [
-      'foo.1',
-      'bar.1.jpg',
-    ]
-  ];
-  // Expected file paths from the scan.  We're scanning the two subdirectories
-  // only.
-  var expectedFiles = [
-    '/testMultipleDirectories/foo.0/bar.0.jpg',
-    '/testMultipleDirectories/foo.1/bar.1.jpg'
-  ];
-
-  var getDirectory = function(root, dirname) {
-    return new Promise(function(resolve, reject) {
-      root.getDirectory(
-          dirname, {create: false}, resolve, reject);
-    });
-  };
-
-  reportPromise(
-      makeTestFileSystemRoot('testMultipleDirectories')
-          .then(populateDir.bind(null, filenames))
-          .then(
-              /**
-               * Scans the directories.
-               * @param {!DirectoryEntry} root
-               */
-              function(root) {
-                return Promise.all(['foo.0', 'foo.1'].map(
-                    getDirectory.bind(null, root))).then(
-                        function(directories) {
-                          return scanner.scan(directories).whenFinal();
-                        });
+                return scanner.scanDirectory(root).whenFinal();
               })
           .then(assertResults.bind(null, expectedFiles)),
       callback);
@@ -302,47 +284,39 @@
 
 function testDedupesFilesInScanResult(callback) {
   var filenames = [
+    'foo.jpg',
+    'bar.jpg',
     [
-      'a',
+      'dir1',
       'foo.jpg',
       'bar.jpg'
     ],
     [
-      'b',
+      'dir2',
       'foo.jpg',
       'bar.jpg',
-      'wee.jpg'
+      [
+        'dir3',
+        'foo.jpg',
+        'bar.jpg'
+      ]
     ]
   ];
-  // Expected file paths from the scan.  We're scanning the two subdirectories
-  // only.
   var expectedFiles = [
-    '/testDedupesFiles/a/foo.jpg',
-    '/testDedupesFiles/a/bar.jpg',
-    '/testDedupesFiles/b/wee.jpg'
+    '/testDedupesFilesInScanResult/foo.jpg',
+    '/testDedupesFilesInScanResult/bar.jpg'
   ];
 
-  var getDirectory = function(root, dirname) {
-    return new Promise(function(resolve, reject) {
-      root.getDirectory(
-          dirname, {create: false}, resolve, reject);
-    });
-  };
-
   reportPromise(
-      makeTestFileSystemRoot('testDedupesFiles')
+      makeTestFileSystemRoot('testDedupesFilesInScanResult')
           .then(populateDir.bind(null, filenames))
           .then(
               /**
-               * Scans the directories.
+               * Scans the directory.
                * @param {!DirectoryEntry} root
                */
               function(root) {
-                return Promise.all(['a', 'b'].map(
-                    getDirectory.bind(null, root))).then(
-                        function(directories) {
-                          return scanner.scan(directories).whenFinal();
-                        });
+                return scanner.scanDirectory(root).whenFinal();
               })
           .then(assertResults.bind(null, expectedFiles)),
       callback);
@@ -361,7 +335,7 @@
                * @param {!DirectoryEntry} root
                */
               function(root) {
-                scan = scanner.scan([root]);
+                scan = scanner.scanDirectory(root);
                 watcher.callback();
                 return invalidatePromise;
               }),
diff --git a/ui/file_manager/file_manager/background/js/mock_media_scanner.js b/ui/file_manager/file_manager/background/js/mock_media_scanner.js
index 5c804aa8..ddc26da 100644
--- a/ui/file_manager/file_manager/background/js/mock_media_scanner.js
+++ b/ui/file_manager/file_manager/background/js/mock_media_scanner.js
@@ -46,7 +46,16 @@
 };
 
 /** @override */
-TestMediaScanner.prototype.scan = function(entries) {
+TestMediaScanner.prototype.scanDirectory = function(directory) {
+  var scan = new TestScanResult(this.fileEntries);
+  scan.totalBytes = this.totalBytes;
+  scan.scanDuration = this.scanDuration;
+  this.scans_.push(scan);
+  return scan;
+};
+
+/** @override */
+TestMediaScanner.prototype.scanFiles = function(entries) {
   var scan = new TestScanResult(this.fileEntries);
   scan.totalBytes = this.totalBytes;
   scan.scanDuration = this.scanDuration;
diff --git a/ui/file_manager/file_manager/background/js/test_util_base.js b/ui/file_manager/file_manager/background/js/test_util_base.js
index 4e0e8c0..e495a0e 100644
--- a/ui/file_manager/file_manager/background/js/test_util_base.js
+++ b/ui/file_manager/file_manager/background/js/test_util_base.js
@@ -194,8 +194,8 @@
  * @param {?string} iframeQuery Iframe selector or null if no iframe.
  * @param {Array.<string>=} opt_styleNames List of CSS property name to be
  *     obtained.
- * @return {{attributes:Object.<string, string>, text:string,
- *                  styles:Object.<string, string>, hidden:boolean}=} Element
+ * @return {?{attributes:Object.<string, string>, text:string,
+ *                  styles:Object.<string, string>, hidden:boolean}} Element
  *     information that contains contentText, attribute names and
  *     values, hidden attribute, and style names and values. If there is no
  *     active element, returns null.
diff --git a/ui/file_manager/file_manager/common/js/importer_common.js b/ui/file_manager/file_manager/common/js/importer_common.js
index 8e9cd72..47e300c4 100644
--- a/ui/file_manager/file_manager/common/js/importer_common.js
+++ b/ui/file_manager/file_manager/common/js/importer_common.js
@@ -197,6 +197,75 @@
 };
 
 /**
+ * @param {!DirectoryEntry} directory Presumably the root of a filesystem.
+ * @return {!Promise<boolean>} True if the directory contains a
+ *     child media directory (like 'DCIM').
+ */
+importer.hasMediaDirectory = function(directory) {
+  /**
+   * @param {boolean} answer
+   * @return {boolean}
+   */
+  var isTrue = function(answer) {
+    return answer;
+  };
+  var dirNames = Object.keys(importer.ValidImportRoots_);
+  return Promise.all(dirNames.map(importer.hasDirectory_.bind(null, directory)))
+      .then(
+          /**
+           * @param {!Array<boolean>} results
+           * @return {!Promise<boolean>}
+           */
+          function(results) {
+            if (results.some(isTrue)) {
+              return Promise.resolve(true);
+            } else {
+              // If standard (upper case) forms are not present,
+              // check for a lower-case "DCIM".
+              return importer.hasDirectory_(directory, 'dcim');
+            }
+          });
+};
+
+/**
+ * @param {!DirectoryEntry} parent
+ * @param {string} name
+ * @return {!Promise<boolean>} True if parent contains a directory
+ *     with the specified name.
+ * @private
+ */
+importer.hasDirectory_ = function(parent, name) {
+  return importer.getDirectory_(parent, name)
+      .then(
+          function() {
+            return true;
+          },
+          function() {
+            return false;
+          });
+};
+
+/**
+ * @param {!DirectoryEntry} parent
+ * @param {string} name
+ * @return {!Promise<!DirectoryEntry>}
+ * @private
+ */
+importer.getDirectory_ = function(parent, name) {
+  return new Promise(
+    function(resolve, reject) {
+      parent.getDirectory(
+          name,
+          {
+            create: false,
+            exclusive: false
+          },
+          resolve,
+          reject);
+    });
+};
+
+/**
  * @return {!Promise<boolean>} Resolves with true when Cloud Import feature
  *     is enabled.
  */
@@ -329,7 +398,7 @@
       .then(
           /** @param {!DirectoryEntry} root */
           function(root) {
-            return fileOperationUtil.listEntries(
+            return importer.listEntries_(
                 root,
                 /** @param {Entry} entry */
                 function(entry) {
@@ -375,6 +444,37 @@
 };
 
 /**
+ * Calls {@code callback} for each child entry of {@code directory}.
+ *
+ * @param {!DirectoryEntry} directory
+ * @param {function(!Entry)} callback
+ * @return {!Promise} Resolves when listing is complete.
+ * @private
+ */
+importer.listEntries_ = function(directory, callback) {
+  return new Promise(
+      function(resolve, reject) {
+        var reader = directory.createReader();
+
+        var readEntries = function() {
+          reader.readEntries (
+              /** @param {!Array<!Entry>} entries */
+              function(entries) {
+                if (entries.length === 0) {
+                  resolve(undefined);
+                  return;
+                }
+                entries.forEach(callback);
+                readEntries();
+              },
+              reject);
+        };
+
+        readEntries();
+      });
+};
+
+/**
  * A Promise wrapper that provides public access to resolve and reject methods.
  *
  * @constructor
@@ -665,7 +765,6 @@
                             resolve,
                             reject);
                       });
-
                 });
           });
 
diff --git a/ui/file_manager/file_manager/common/js/importer_common_unittest.js b/ui/file_manager/file_manager/common/js/importer_common_unittest.js
index f0694d7..62944f7 100644
--- a/ui/file_manager/file_manager/common/js/importer_common_unittest.js
+++ b/ui/file_manager/file_manager/common/js/importer_common_unittest.js
@@ -38,10 +38,6 @@
   new MockChromeStorageAPI();
   importer.setupTestLogger();
 
-  var cameraFileSystem = new MockFileSystem(
-      'camera-fs', 'filesystem:camera-123');
-  var sdFileSystem = new MockFileSystem(
-      'sd-fs', 'filesystem:sd-123');
   cameraVolume = MockVolumeManager.createMockVolumeInfo(
           VolumeManagerCommon.VolumeType.MTP,
           'camera-fs',
@@ -265,6 +261,14 @@
   reportPromise(promise, callback);
 }
 
+function testHasMediaDirectory(callback) {
+  var dir = createDirectoryEntry(sdVolume, '/DCIM');
+  var promise = importer.hasMediaDirectory(sdVolume.fileSystem.root)
+      .then(assertTrue.bind(null));
+
+  reportPromise(promise, callback);
+}
+
 /** @param {string} path */
 function assertIsMediaDir(path) {
   var dir = createDirectoryEntry(sdVolume, path);
@@ -297,4 +301,3 @@
   entry.volumeId = volume.volumeId;
   return entry;
 }
-
diff --git a/ui/file_manager/file_manager/common/js/metrics.js b/ui/file_manager/file_manager/common/js/metrics.js
index fa84d7e5..7fcfc96f 100644
--- a/ui/file_manager/file_manager/common/js/metrics.js
+++ b/ui/file_manager/file_manager/common/js/metrics.js
@@ -50,7 +50,12 @@
  * @private
  */
 metrics.createTracker_ = function() {
-  metrics.analytics_ = analytics.getService('Files app');
+  var chromeVersion = /Chrome\/([0-9]*)\.[0-9.]*/.exec(navigator.userAgent);
+  if (chromeVersion && chromeVersion[1]) {
+    metrics.analytics_ = analytics.getService('Files app', chromeVersion[1]);
+  } else {
+    metrics.analytics_ = analytics.getService('Files app', '0.0');
+  }
 
   // Create a tracker, add a filter that only enables analytics when UMA is
   // enabled.
diff --git a/ui/file_manager/file_manager/common/js/metrics_events.js b/ui/file_manager/file_manager/common/js/metrics_events.js
index 5b3bdbe..a25dc1e 100644
--- a/ui/file_manager/file_manager/common/js/metrics_events.js
+++ b/ui/file_manager/file_manager/common/js/metrics_events.js
@@ -18,7 +18,8 @@
 
 /** @enum {string} */
 metrics.Categories = {
-  ACQUISITION: 'Acquisition'
+  ACQUISITION: 'Acquisition',
+  INTERNALS: 'Internals'
 };
 
 /**
@@ -32,6 +33,32 @@
 };
 
 /**
+ * Enumeration of known FSPs used to qualify "provided" extensions
+ * "screens" on analytics. All FSPs NOT present in this list
+ * will be reported to analytics as 'provided-unknown'.
+ *
+ * NOTE: When an unknown provider is encountered, a separate event will be
+ * sent to analytics with the id. Consulation of that event will provided
+ * an indication when an extension is popular enough to be added to the
+ * whitelist.
+ *
+ * @enum {string}
+ */
+metrics.FileSystemProviders = {
+  oedeeodfidgoollimchfdnbmhcpnklnd: 'ZipUnpacker'
+};
+
+/**
+ * Returns a new "screen" name for a provided file system type.
+ * @param {string|undefined} extensionId The FSP provider extension ID.
+ * @param {string} defaultName
+ * @return {string} Name or undefined if extension is unrecognized.
+ */
+metrics.getFileSystemProviderName = function(extensionId, defaultName) {
+  return metrics.FileSystemProviders[extensionId] || defaultName;
+};
+
+/**
  * @enum {!analytics.EventBuilder.Dimension}
  */
 metrics.Dimensions = {
@@ -70,12 +97,12 @@
  */
 metrics.event.Builders_ = {
   IMPORT: analytics.EventBuilder.builder()
-      .category(metrics.Categories.ACQUISITION)
+      .category(metrics.Categories.ACQUISITION),
+  INTERNALS: analytics.EventBuilder.builder()
+      .category(metrics.Categories.INTERNALS)
 };
 
-/**
- * @enum {!analytics.EventBuilder}
- */
+/** @enum {!analytics.EventBuilder} */
 metrics.ImportEvents = {
   DEVICE_YANKED: metrics.event.Builders_.IMPORT
       .action('Device Yanked'),
@@ -104,6 +131,12 @@
       .dimension(metrics.Dimensions.CONSUMER_TYPE_IMPORTER)
 };
 
+/** @enum {!analytics.EventBuilder} */
+metrics.Internals = {
+  UNRECOGNIZED_FILE_SYSTEM_PROVIDER: metrics.event.Builders_.INTERNALS
+      .action('Unrecognized File System Provider')
+};
+
 // namespace
 metrics.timing = metrics.timing || {};
 
diff --git a/ui/file_manager/file_manager/foreground/css/common.css b/ui/file_manager/file_manager/foreground/css/common.css
index da442fe..06a1597 100644
--- a/ui/file_manager/file_manager/foreground/css/common.css
+++ b/ui/file_manager/file_manager/foreground/css/common.css
@@ -489,6 +489,11 @@
   width: 44px;
 }
 
+html[dir='rtl'] .cr-dialog-close {
+  left: 0;
+  right: auto;
+}
+
 .cr-dialog-close:hover {
   background-image: url(chrome://theme/IDR_CLOSE_DIALOG_H);
 }
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index a33f532..588c616 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -976,11 +976,11 @@
   /* On the right side, we have less margin to pack items as long as they are
      fully visible. */
   -webkit-padding-end: 2px;
-  -webkit-padding-start: 7px;
+  -webkit-padding-start: 4px;
   box-sizing: border-box;
   overflow-y: auto;
-  padding-bottom: 7px;
-  padding-top: 7px;
+  padding-bottom: 16px;
+  padding-top: 4px;
   width: 100%;
 }
 
@@ -1024,10 +1024,11 @@
   border-radius: 2px;
   box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37);
   height: 180px;
-  margin-top: 8px;
+  margin-top: 12px;
   overflow: hidden;
   position: relative;
   transition: box-shadow 220ms ease;
+  vertical-align: top;  /* Prevent vertical spacing for wrapped inline box. */
   width: 180px;
 }
 
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.js b/ui/file_manager/file_manager/foreground/js/directory_model.js
index c7c82138..21f08ab 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_model.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_model.js
@@ -11,6 +11,9 @@
 /**
  * Data model of the file manager.
  *
+ * @constructor
+ * @extends {cr.EventTarget}
+ *
  * @param {boolean} singleSelection True if only one file could be selected
  *                                  at the time.
  * @param {FileFilter} fileFilter Instance of FileFilter.
@@ -18,12 +21,15 @@
  *     service.
  * @param {VolumeManagerWrapper} volumeManager The volume manager.
  * @param {!FileOperationManager} fileOperationManager File operation manager.
- * @constructor
- * @extends {cr.EventTarget}
+ * @param {!analytics.Tracker} tracker
  */
-function DirectoryModel(singleSelection, fileFilter,
-                        metadataModel,
-                        volumeManager, fileOperationManager) {
+function DirectoryModel(
+    singleSelection,
+    fileFilter,
+    metadataModel,
+    volumeManager,
+    fileOperationManager,
+    tracker) {
   this.fileListSelection_ = singleSelection ?
       new FileListSingleSelectionModel() : new FileListSelectionModel();
 
@@ -65,6 +71,9 @@
       fileOperationManager,
       'entries-changed',
       this.onEntriesChanged_.bind(this));
+
+  /** @private {!analytics.Tracker} */
+  this.tracker_ = tracker;
 }
 
 /**
@@ -921,17 +930,23 @@
 
           var previousDirEntry =
               this.currentDirContents_.getDirectoryEntry();
-          this.clearAndScan_(
-              newDirectoryContents,
-              function(result) {
-                // Calls the callback of the method when successful.
-                if (result && opt_callback)
-                  opt_callback();
 
-                // Notify that the current task of this.directoryChangeQueue_
-                // is completed.
-                setTimeout(queueTaskCallback, 0);
-              });
+          var promises = [];
+
+          promises.push(
+              new Promise(
+                  /** @this {DirectoryModel} */
+                  function(resolve) {
+                    this.clearAndScan_(
+                        newDirectoryContents,
+                        function(result) {
+                          // Calls the callback of the method when successful.
+                          if (result && opt_callback)
+                            opt_callback();
+
+                          resolve(undefined);
+                        });
+                  }.bind(this)));
 
           // For tests that open the dialog to empty directories, everything
           // is loaded at this point.
@@ -945,12 +960,68 @@
           event.previousDirEntry = previousDirEntry;
           event.newDirEntry = dirEntry;
           event.volumeChanged = previousVolumeInfo !== currentVolumeInfo;
+
+          if (event.volumeChanged) {
+            promises.push(this.onVolumeChanged_(currentVolumeInfo));
+          }
+
           this.dispatchEvent(event);
+          Promise.all(promises).then(queueTaskCallback);
         }.bind(this));
   }.bind(this, this.changeDirectorySequence_));
 };
 
 /**
+ * Handles volume changed by sending an analytics appView event.
+ *
+ * @param {!VolumeInfo} volumeInfo The new volume info.
+ * @return {!Promise} resolves once handling is done.
+ * @private
+ */
+DirectoryModel.prototype.onVolumeChanged_ = function(volumeInfo) {
+  // NOTE: That dynamic values, like volume name MUST NOT
+  // be sent to GA as that value can contain PII.
+  // VolumeType is an enum.
+  // ...
+  // But we can do stuff like figure out if this is a media device or vanilla
+  // removable device.
+  return Promise.resolve(undefined)
+      .then(
+          function() {
+            switch (volumeInfo.volumeType) {
+              case VolumeManagerCommon.VolumeType.REMOVABLE:
+                return importer.hasMediaDirectory(volumeInfo.fileSystem.root)
+                    .then(
+                        /**
+                         * @param {boolean} hasMedia
+                         * @return {string}
+                         */
+                        function(hasMedia) {
+                          return hasMedia ?
+                              volumeInfo.volumeType + ':with-media-dir' :
+                              volumeInfo.volumeType;
+                        });
+              case VolumeManagerCommon.VolumeType.PROVIDED:
+                var extensionId = volumeInfo.extensionId;
+                var extensionName =
+                    metrics.getFileSystemProviderName(extensionId, 'unknown');
+                // Make note of an unrecognized extension id. When we see
+                // high counts for a particular id, we should add it to the
+                // whitelist in metrics_events.js.
+                if (extensionId && extensionName == 'unknown') {
+                  this.tracker_.sendEvent(
+                      metrics.Internals.UNRECOGNIZED_FILE_SYSTEM_PROVIDER
+                          .label(extensionId));
+                }
+                return volumeInfo.volumeType + ':' + extensionName;
+              default:
+                return volumeInfo.volumeType;
+            }
+          })
+      .then(this.tracker_.sendAppView.bind(this.tracker_));
+};
+
+/**
  * Activates the given directory.
  * This method:
  *  - Changes the current directory, if the given directory is the current
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index d415a17..2ccc3b9 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -850,7 +850,8 @@
         this.fileFilter_,
         this.metadataModel_,
         this.volumeManager_,
-        this.fileOperationManager_);
+        this.fileOperationManager_,
+        assert(this.tracker_));
 
     this.folderShortcutsModel_ = new FolderShortcutsDataModel(
         this.volumeManager_);
@@ -861,25 +862,12 @@
         this.selectionHandler_.onFileSelectionChanged.bind(
             this.selectionHandler_));
 
-    this.directoryModel_.addEventListener(
-        'directory-changed',
-        function(event) {
-          if (event.volumeChanged) {
-            var volumeInfo =
-                this.volumeManager_.getVolumeInfo(event.newDirEntry);
-            // NOTE: That dynamic values, like volume name MUST NOT
-            // be sent to GA as that value can contain PII.
-            // VolumeType is an enum.
-            this.tracker_.sendAppView(volumeInfo.volumeType);
-          }
-        }.bind(this));
-
     // TODO(mtomasz, yoshiki): Create navigation list earlier, and here just
     // attach the directory model.
     this.initDirectoryTree_();
 
     this.ui_.listContainer.listThumbnailLoader = new ListThumbnailLoader(
-        assert(this.directoryModel_.getFileList()),
+        this.directoryModel_,
         assert(this.thumbnailModel_),
         this.volumeManager_);
     this.ui_.listContainer.dataModel = this.directoryModel_.getFileList();
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller.js b/ui/file_manager/file_manager/foreground/js/import_controller.js
index 01acf52..058e2dd 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/import_controller.js
@@ -879,7 +879,7 @@
 importer.ScanManager.prototype.getSelectionScan = function(entries) {
   console.assert(!this.selectionScan_,
       'Cannot create new selection scan with another in the cache.');
-  this.selectionScan_ = this.scanner_.scan(entries);
+  this.selectionScan_ = this.scanner_.scanFiles(entries);
   return this.selectionScan_;
 };
 
@@ -897,7 +897,8 @@
   var url = directory.toURL();
   var scan = this.directoryScans_[url];
   if (!scan) {
-    scan = this.scanner_.scan([directory]);
+    scan = this.scanner_.scanDirectory(
+        /** @type {!DirectoryEntry} */ (directory));
     this.directoryScans_[url] = scan;
   }
 
diff --git a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js
index 3184679..c09c769 100644
--- a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js
+++ b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js
@@ -10,7 +10,7 @@
  * is responsible to return dataUrls of thumbnails and fetch them with proper
  * priority.
  *
- * @param {!FileListModel} dataModel A file list model.
+ * @param {!DirectoryModel} directoryModel A directory model.
  * @param {!ThumbnailModel} thumbnailModel Thumbnail metadata model.
  * @param {!VolumeManagerWrapper} volumeManager Volume manager.
  * @param {Function=} opt_thumbnailLoaderConstructor A constructor of thumbnail
@@ -21,11 +21,12 @@
  * @suppress {checkStructDictInheritance}
  */
 function ListThumbnailLoader(
-    dataModel, thumbnailModel, volumeManager, opt_thumbnailLoaderConstructor) {
+    directoryModel, thumbnailModel, volumeManager,
+    opt_thumbnailLoaderConstructor) {
   /**
-   * @private {!FileListModel}
+   * @private {!DirectoryModel}
    */
-  this.dataModel_ = dataModel;
+  this.directoryModel_ = directoryModel;
 
   /**
    * @private {!ThumbnailModel}
@@ -76,6 +77,13 @@
    */
   this.currentVolumeType_ = null;
 
+  /**
+   * @private {!FileListModel}
+   */
+  this.dataModel_ = assert(this.directoryModel_.getFileList());
+
+  this.directoryModel_.addEventListener(
+      'scan-completed', this.onScanCompleted_.bind(this));
   this.dataModel_.addEventListener('splice', this.onSplice_.bind(this));
   this.dataModel_.addEventListener('sorted', this.onSorted_.bind(this));
   this.dataModel_.addEventListener('change', this.onChange_.bind(this));
@@ -144,6 +152,19 @@
 };
 
 /**
+ * An event handler for scan-completed event of directory model. When directory
+ * scan is running, we don't fetch thumbnail in order not to block IO for
+ * directory scan. i.e. modification events during directory scan is ignored.
+ * We need to check thumbnail loadings after directory scan is completed.
+ *
+ * @param {!Event} event Event
+ */
+ListThumbnailLoader.prototype.onScanCompleted_ = function(event) {
+  this.cursor_ = this.beginIndex_;
+  this.continue_();
+};
+
+/**
  * An event handler for splice event of data model. When list is changed, start
  * to rescan items.
  *
@@ -216,8 +237,9 @@
  * Enqueues tasks if available.
  */
 ListThumbnailLoader.prototype.continue_ = function() {
-  // If all items are scanned, do nothing.
-  if (!(this.cursor_ < this.dataModel_.length))
+  // If directory scan is running or all items are scanned, do nothing.
+  if (this.directoryModel_.isScanning() ||
+      !(this.cursor_ < this.dataModel_.length))
     return;
 
   var entry = /** @type {Entry} */ (this.dataModel_.item(this.cursor_));
diff --git a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
index 6e2ea082..80ff510 100644
--- a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
@@ -45,7 +45,9 @@
 var getCallbacks;
 var thumbnailLoadedEvents;
 var fileListModel;
+var directoryModel;
 var currentVolumeType;
+var isScanningForTest = false;
 var fileSystem = new MockFileSystem('volume-id');
 var directory1 = new MockDirectoryEntry(fileSystem, '/TestDirectory');
 var entry1 = new MockEntry(fileSystem, '/Test1.jpg');
@@ -75,8 +77,18 @@
 
   fileListModel = new FileListModel(thumbnailModel);
 
+  directoryModel = {
+    __proto__: cr.EventTarget.prototype,
+    getFileList: function() {
+      return fileListModel;
+    },
+    isScanning: function() {
+      return isScanningForTest;
+    }
+  };
+
   listThumbnailLoader = new ListThumbnailLoader(
-      fileListModel,
+      directoryModel,
       thumbnailModel,
       // Mocking volume manager
       {
@@ -337,3 +349,22 @@
   // Only one request should be enqueued on MTP volume.
   assertEquals(1, Object.keys(getCallbacks).length);
 }
+
+/**
+ * Test case that directory scan is running.
+ */
+function testDirectoryScanIsRunning() {
+  // Items are added during directory scan.
+  isScanningForTest = true;
+
+  listThumbnailLoader.setHighPriorityRange(0,2);
+  fileListModel.push(directory1, entry1, entry2);
+  assertEquals(0, Object.keys(getCallbacks).length);
+
+  // Scan completed after adding the last item.
+  fileListModel.push(entry3);
+  isScanningForTest = false;
+  directoryModel.dispatchEvent(new Event('scan-completed'));
+
+  assertEquals(2, Object.keys(getCallbacks).length);
+}
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
index 407b9f7..64b15ba 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
@@ -72,6 +72,12 @@
 
   self.relayoutRateLimiter_ =
       new AsyncUtil.RateLimiter(self.relayoutImmediately_.bind(self));
+
+  var style = window.getComputedStyle(self);
+  /** @private {number} */
+  self.paddingLeft_ = parseFloat(style.paddingLeft);
+  /** @private {number} */
+  self.paddingTop_ = parseFloat(style.paddingTop);
 };
 
 /**
@@ -434,17 +440,22 @@
  */
 FileGrid.prototype.getHitElements = function(x, y, opt_width, opt_height) {
   var currentSelection = [];
-  var right = x + (opt_width || 0);
-  var bottom = y + (opt_height || 0);
+  var xInAvailableSpace = Math.max(0, x - this.paddingLeft_);
+  var yInAvailableSpace = Math.max(0, y - this.paddingTop_);
+  var right = xInAvailableSpace + (opt_width || 0);
+  var bottom = yInAvailableSpace + (opt_height || 0);
   var itemMetrics = this.measureItem();
   var horizontalStartIndex = this.getHitIndex_(
-      x, itemMetrics.width, itemMetrics.width - itemMetrics.marginRight);
+      xInAvailableSpace, itemMetrics.width,
+      itemMetrics.width - itemMetrics.marginRight);
   var horizontalEndIndex = Math.min(this.columns, this.getHitIndex_(
       right, itemMetrics.width, itemMetrics.marginLeft));
   var verticalStartIndex = this.getHitIndex_(
-      y, itemMetrics.height, itemMetrics.height - itemMetrics.marginBottom);
+      yInAvailableSpace, itemMetrics.height,
+      itemMetrics.height - itemMetrics.marginBottom);
   var verticalEndIndex = this.getHitIndex_(
       bottom, itemMetrics.height, itemMetrics.marginTop);
+
   for (var verticalIndex = verticalStartIndex;
        verticalIndex < verticalEndIndex;
        verticalIndex++) {
diff --git a/ui/file_manager/file_manager_resources.grd b/ui/file_manager/file_manager_resources.grd
index 15c9ea0c..8279a746 100644
--- a/ui/file_manager/file_manager_resources.grd
+++ b/ui/file_manager/file_manager_resources.grd
@@ -151,7 +151,13 @@
         <include name="IDR_IMAGE_LOADER_CLIENT_JS" file="image_loader/image_loader_client.js" type="BINDATA" />
         <include name="IDR_IMAGE_LOADER_PIEX_LOADER_JS" file="image_loader/piex_loader.js" flattenhtml="true" type="BINDATA" />
         <!-- Internal resources. -->
-        <include name="IDR_IMAGE_LOADER_PIEX_LOADER_PNACL" file="image_loader/piex/piex.pexe.txt" type="BINDATA" />
+        <if expr="not _google_chrome">
+          <include name="IDR_IMAGE_LOADER_PIEX_LOADER_PNACL" file="image_loader/piex/piex.pexe.txt" type="BINDATA" />
+        </if>
+        <if expr="_google_chrome">
+          <!-- This entry overrides IDR_IMAGE_LOADER_PIEX_LOADER_PNACL with content of ../internal/piex/piex.pexe. -->
+          <include name="IDR_IMAGE_LOADER_PIEX_LOADER_PNACL" file="internal/piex/piex.pexe" type="BINDATA" />
+        </if>
         <include name="IDR_IMAGE_LOADER_PIEX_LOADER_MANIFEST" file="image_loader/piex/piex.nmf.txt" type="BINDATA" />
       </if>
     </includes>
diff --git a/ui/file_manager/gallery/manifest.json b/ui/file_manager/gallery/manifest.json
index 09c3c84..24ef8f7 100644
--- a/ui/file_manager/gallery/manifest.json
+++ b/ui/file_manager/gallery/manifest.json
@@ -21,7 +21,6 @@
     "chrome://extension-icon/",
     "chrome://resources/",
     "chrome://theme/",
-    "fileBrowserHandler",
     "fileManagerPrivate",
     {
       "fileSystem": ["requestFileSystem", "write"]
diff --git a/ui/file_manager/image_loader/image_loader.js b/ui/file_manager/image_loader/image_loader.js
index 2a995b9b..8a89825 100644
--- a/ui/file_manager/image_loader/image_loader.js
+++ b/ui/file_manager/image_loader/image_loader.js
@@ -42,7 +42,7 @@
       var requestPromise = new Promise(function(callback) {
         chrome.fileSystem.requestFileSystem(
             {volumeId: volumeMetadata.volumeId},
-            callback);
+            /** @type {function(FileSystem=)} */(callback));
       });
       return requestPromise;
     });
diff --git a/ui/file_manager/image_loader/manifest.json b/ui/file_manager/image_loader/manifest.json
index 3d2daf0..1262046 100644
--- a/ui/file_manager/image_loader/manifest.json
+++ b/ui/file_manager/image_loader/manifest.json
@@ -7,7 +7,6 @@
   "incognito" : "split",
   "manifest_version": 2,
   "permissions": [
-    "fileBrowserHandler",
     {
       "fileSystem": ["requestFileSystem"]
     },
diff --git a/ui/file_manager/video_player/manifest.json b/ui/file_manager/video_player/manifest.json
index 77f8e55..9c5c4a0 100644
--- a/ui/file_manager/video_player/manifest.json
+++ b/ui/file_manager/video_player/manifest.json
@@ -19,7 +19,6 @@
   "permissions": [
     "commandLinePrivate",
     "fileSystem",
-    "fileBrowserHandler",
     "fileManagerPrivate",
     {
       "fileSystem": ["requestFileSystem", "write"]
diff --git a/ui/gfx/font_render_params.cc b/ui/gfx/font_render_params.cc
index 7297cdcb..44c6bcf1 100644
--- a/ui/gfx/font_render_params.cc
+++ b/ui/gfx/font_render_params.cc
@@ -4,6 +4,8 @@
 
 #include "ui/gfx/font_render_params.h"
 
+#include "base/logging.h"
+
 namespace gfx {
 
 FontRenderParams::FontRenderParams()
@@ -17,6 +19,41 @@
 
 FontRenderParams::~FontRenderParams() {}
 
+// static
+SkFontHost::LCDOrder FontRenderParams::SubpixelRenderingToSkiaLCDOrder(
+    FontRenderParams::SubpixelRendering subpixel_rendering) {
+  switch (subpixel_rendering) {
+    case gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE:
+    case gfx::FontRenderParams::SUBPIXEL_RENDERING_RGB:
+    case gfx::FontRenderParams::SUBPIXEL_RENDERING_VRGB:
+      return SkFontHost::kRGB_LCDOrder;
+    case gfx::FontRenderParams::SUBPIXEL_RENDERING_BGR:
+    case gfx::FontRenderParams::SUBPIXEL_RENDERING_VBGR:
+      return SkFontHost::kBGR_LCDOrder;
+  }
+
+  NOTREACHED();
+  return SkFontHost::kRGB_LCDOrder;
+}
+
+// static
+SkFontHost::LCDOrientation
+FontRenderParams::SubpixelRenderingToSkiaLCDOrientation(
+    FontRenderParams::SubpixelRendering subpixel_rendering) {
+  switch (subpixel_rendering) {
+    case gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE:
+    case gfx::FontRenderParams::SUBPIXEL_RENDERING_RGB:
+    case gfx::FontRenderParams::SUBPIXEL_RENDERING_BGR:
+      return SkFontHost::kHorizontal_LCDOrientation;
+    case gfx::FontRenderParams::SUBPIXEL_RENDERING_VRGB:
+    case gfx::FontRenderParams::SUBPIXEL_RENDERING_VBGR:
+      return SkFontHost::kVertical_LCDOrientation;
+  }
+
+  NOTREACHED();
+  return SkFontHost::kHorizontal_LCDOrientation;
+}
+
 FontRenderParamsQuery::FontRenderParamsQuery(bool for_web_contents)
     : for_web_contents(for_web_contents),
       pixel_size(0),
diff --git a/ui/gfx/font_render_params.h b/ui/gfx/font_render_params.h
index 977a495..0bf10d4 100644
--- a/ui/gfx/font_render_params.h
+++ b/ui/gfx/font_render_params.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "third_party/skia/include/core/SkFontHost.h"
 #include "ui/gfx/gfx_export.h"
 
 namespace gfx {
@@ -57,6 +58,11 @@
   // Whether subpixel rendering should be used or not, and if so, the display's
   // subpixel order.
   SubpixelRendering subpixel_rendering;
+
+  static SkFontHost::LCDOrder SubpixelRenderingToSkiaLCDOrder(
+      SubpixelRendering subpixel_rendering);
+  static SkFontHost::LCDOrientation SubpixelRenderingToSkiaLCDOrientation(
+      SubpixelRendering subpixel_rendering);
 };
 
 // A query used to determine the appropriate FontRenderParams.
diff --git a/ui/gfx/font_render_params_win.cc b/ui/gfx/font_render_params_win.cc
index 9f8409c..f7a0148 100644
--- a/ui/gfx/font_render_params_win.cc
+++ b/ui/gfx/font_render_params_win.cc
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/gfx/font_render_params.h"
-
+#include "base/files/file_path.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/singleton.h"
+#include "base/win/registry.h"
+#include "ui/gfx/font_render_params.h"
 #include "ui/gfx/win/direct_write.h"
 #include "ui/gfx/win/singleton_hwnd.h"
 
@@ -13,6 +14,33 @@
 
 namespace {
 
+FontRenderParams::SubpixelRendering GetSubpixelRenderingGeometry() {
+  DISPLAY_DEVICE display_device = {sizeof(DISPLAY_DEVICE), 0};
+  for (int i = 0; EnumDisplayDevices(nullptr, i, &display_device, 0); ++i) {
+    // TODO(scottmg): We only support the primary device currently.
+    if (display_device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
+      base::FilePath trimmed =
+          base::FilePath(display_device.DeviceName).BaseName();
+      base::win::RegKey key(
+          HKEY_LOCAL_MACHINE,
+          (L"SOFTWARE\\Microsoft\\Avalon.Graphics\\" + trimmed.value()).c_str(),
+          KEY_READ);
+      DWORD pixel_structure;
+      if (key.ReadValueDW(L"PixelStructure", &pixel_structure) ==
+          ERROR_SUCCESS) {
+        if (pixel_structure == 1)
+          return FontRenderParams::SUBPIXEL_RENDERING_RGB;
+        if (pixel_structure == 2)
+          return FontRenderParams::SUBPIXEL_RENDERING_BGR;
+      }
+      break;
+    }
+  }
+
+  // No explicit ClearType settings, default to RGB.
+  return FontRenderParams::SUBPIXEL_RENDERING_RGB;
+}
+
 // Caches font render params and updates them on system notifications.
 class CachedFontRenderParams : public gfx::SingletonHwnd::Observer {
  public:
@@ -41,7 +69,7 @@
       UINT type = 0;
       if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &type, 0) &&
           type == FE_FONTSMOOTHINGCLEARTYPE) {
-        params_->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB;
+        params_->subpixel_rendering = GetSubpixelRenderingGeometry();
       }
     }
     gfx::SingletonHwnd::GetInstance()->AddObserver(this);
diff --git a/ui/gfx/gpu_memory_buffer.h b/ui/gfx/gpu_memory_buffer.h
index 3aa0964c..e56009f 100644
--- a/ui/gfx/gpu_memory_buffer.h
+++ b/ui/gfx/gpu_memory_buffer.h
@@ -61,11 +61,12 @@
 
   virtual ~GpuMemoryBuffer() {}
 
-  // Maps the buffer into the client's address space so it can be written to by
-  // the CPU. This call may block, for instance if the GPU needs to finish
-  // accessing the buffer or if CPU caches need to be synchronized. Returns NULL
-  // on failure.
-  virtual void* Map() = 0;
+  // Maps each plane of the buffer into the client's address space so it can be
+  // written to by the CPU. A pointer to plane K is stored at index K-1 of the
+  // |data| array. This call may block, for instance if the GPU needs to finish
+  // accessing the buffer or if CPU caches need to be synchronized. Returns
+  // false on failure.
+  virtual bool Map(void** data) = 0;
 
   // Unmaps the buffer. It's illegal to use the pointer returned by Map() after
   // this has been called.
@@ -77,8 +78,9 @@
   // Returns the format for the buffer.
   virtual Format GetFormat() const = 0;
 
-  // Returns the stride in bytes for the buffer.
-  virtual uint32 GetStride() const = 0;
+  // Fills the stride in bytes for the each plane of the buffer. The stride of
+  // plane K is stored at index K-1 of the |stride| array.
+  virtual void GetStride(uint32* stride) const = 0;
 
   // Returns a platform specific handle for this buffer.
   virtual GpuMemoryBufferHandle GetHandle() const = 0;
diff --git a/ui/gfx/image/image_skia_unittest.cc b/ui/gfx/image/image_skia_unittest.cc
index 49348e23..7f87202 100644
--- a/ui/gfx/image/image_skia_unittest.cc
+++ b/ui/gfx/image/image_skia_unittest.cc
@@ -158,7 +158,7 @@
 };
 
 TEST_F(ImageSkiaTest, FixedSource) {
-  ImageSkiaRep image(Size(100, 200), 1.0f);
+  ImageSkiaRep image(Size(100, 200), 0.0f);
   ImageSkia image_skia(new FixedSource(image), Size(100, 200));
   EXPECT_EQ(0U, image_skia.image_reps().size());
 
@@ -177,10 +177,20 @@
   EXPECT_EQ(1.0f, result_200p.scale());
   EXPECT_EQ(1U, image_skia.image_reps().size());
 
+  const ImageSkiaRep& result_300p = image_skia.GetRepresentation(3.0f);
+
+  EXPECT_EQ(100, result_300p.GetWidth());
+  EXPECT_EQ(200, result_300p.GetHeight());
+  EXPECT_EQ(100, result_300p.pixel_width());
+  EXPECT_EQ(200, result_300p.pixel_height());
+  EXPECT_EQ(1.0f, result_300p.scale());
+  EXPECT_EQ(1U, image_skia.image_reps().size());
+
   // Get the representation again and make sure it doesn't
   // generate new image skia rep.
   image_skia.GetRepresentation(1.0f);
   image_skia.GetRepresentation(2.0f);
+  image_skia.GetRepresentation(3.0f);
   EXPECT_EQ(1U, image_skia.image_reps().size());
 }
 
diff --git a/ui/gfx/mac/nsimage_cache_unittest.cc.README b/ui/gfx/mac/nsimage_cache_unittest.cc.README
deleted file mode 100644
index 558426f..0000000
--- a/ui/gfx/mac/nsimage_cache_unittest.cc.README
+++ /dev/null
@@ -1,3 +0,0 @@
-The unit test for this file is in
-chrome/browser/ui/cocoa/nsimage_cache_unittest.mm since it uses certain Chrome
-resources for the test.
diff --git a/ui/gfx/platform_font_win.cc b/ui/gfx/platform_font_win.cc
index 8d8702e..7d63a71 100644
--- a/ui/gfx/platform_font_win.cc
+++ b/ui/gfx/platform_font_win.cc
@@ -488,11 +488,17 @@
           base::SysWideToUTF8(font_info.lfFaceName).c_str(),
                               static_cast<SkTypeface::Style>(skia_style)));
 
-  BOOL antialiasing = TRUE;
-  SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &antialiasing, 0);
+  gfx::FontRenderParams font_params =
+      gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(false), nullptr);
+  SkFontHost::SetSubpixelOrder(
+      gfx::FontRenderParams::SubpixelRenderingToSkiaLCDOrder(
+          font_params.subpixel_rendering));
+  SkFontHost::SetSubpixelOrientation(
+      gfx::FontRenderParams::SubpixelRenderingToSkiaLCDOrientation(
+          font_params.subpixel_rendering));
 
   SkPaint paint;
-  paint.setAntiAlias(!!antialiasing);
+  paint.setAntiAlias(font_params.antialiasing);
   paint.setTypeface(skia_face.get());
   paint.setTextSize(-font_info.lfHeight);
   SkPaint::FontMetrics skia_metrics;
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index c3a4f00e..0fbf10f 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -1407,7 +1407,7 @@
     render_text->styles_ = styles_;
     for (size_t style = 0; style < NUM_TEXT_STYLES; ++style)
       RestoreBreakList(render_text.get(), render_text->styles_[style]);
-    RestoreBreakList(render_text.get(), baselines_);
+    RestoreBreakList(render_text.get(), render_text->baselines_);
 
     // We check the width of the whole desired string at once to ensure we
     // handle kerning/ligatures/etc. correctly.
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index fe70f47..1b33b3c 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -618,6 +618,7 @@
   FRIEND_TEST_ALL_PREFIXES(RenderTextTest, DefaultStyles);
   FRIEND_TEST_ALL_PREFIXES(RenderTextTest, SetStyles);
   FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyStyles);
+  FRIEND_TEST_ALL_PREFIXES(RenderTextTest, AppendTextKeepsStyles);
   FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ObscuredText);
   FRIEND_TEST_ALL_PREFIXES(RenderTextTest, RevealObscuredText);
   FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ElidedText);
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index 3c71cff..f34dce7 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -5,11 +5,13 @@
 #include "ui/gfx/render_text_harfbuzz.h"
 
 #include <limits>
+#include <set>
 
 #include "base/i18n/bidi_line_iterator.h"
 #include "base/i18n/break_iterator.h"
 #include "base/i18n/char_iterator.h"
 #include "base/profiler/scoped_tracker.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "third_party/harfbuzz-ng/src/hb.h"
@@ -298,7 +300,6 @@
   // |*next_char| will be greater than |start_char| (to avoid constructing empty
   // lines).
   // Returns whether to skip the line before |*next_char|.
-  // TODO(ckocagil): Check clusters to avoid breaking ligatures and diacritics.
   // TODO(ckocagil): We might have to reshape after breaking at ligatures.
   //                 See whether resolving the TODO above resolves this too.
   // TODO(ckocagil): Do not reserve width for whitespace at the end of lines.
@@ -315,7 +316,8 @@
     SkScalar word_width = 0;
     *width = 0;
 
-    for (size_t i = start_char; i < run.range.end(); ++i) {
+    Range char_range;
+    for (size_t i = start_char; i < run.range.end(); i += char_range.length()) {
       // |word| holds the word boundary at or before |i|, and |next_word| holds
       // the word boundary right after |i|. Advance both |word| and |next_word|
       // when |i| reaches |next_word|.
@@ -324,7 +326,10 @@
         word_width = 0;
       }
 
-      Range glyph_range = run.CharRangeToGlyphRange(Range(i, i + 1));
+      Range glyph_range;
+      run.GetClusterAt(i, &char_range, &glyph_range);
+      DCHECK_LT(0U, char_range.length());
+
       SkScalar char_width = ((glyph_range.end() >= run.glyph_count)
                                  ? SkFloatToScalar(run.width)
                                  : run.positions[glyph_range.end()].x()) -
@@ -344,7 +349,7 @@
           *next_char = i;
         } else {
           // Continue from the next character.
-          *next_char = i + 1;
+          *next_char = i + char_range.length();
         }
         return true;
       }
@@ -466,6 +471,13 @@
   DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker);
 };
 
+// Function object for case insensitive string comparison.
+struct CaseInsensitiveCompare {
+  bool operator() (const std::string& a, const std::string& b) const {
+    return base::strncasecmp(a.c_str(), b.c_str(), b.length()) < 0;
+  }
+};
+
 }  // namespace
 
 namespace internal {
@@ -1152,6 +1164,9 @@
   // Use an empty color BreakList to avoid breaking runs at color boundaries.
   BreakList<SkColor> empty_colors;
   empty_colors.SetMax(text.length());
+  DCHECK_LE(text.size(), baselines().max());
+  for (const BreakList<bool>& style : styles())
+    DCHECK_LE(text.size(), style.max());
   internal::StyleIterator style(empty_colors, baselines(), styles());
 
   for (size_t run_break = 0; run_break < text.length();) {
@@ -1296,24 +1311,17 @@
   // http://crbug.com/467459. On some Windows configurations the default font
   // could be a raster font like System, which would not give us a reasonable
   // fallback font list.
-  std::vector<std::string> default_fallback_families =
-      GetFallbackFontFamilies("Segoe UI");
-  fallback_families.insert(fallback_families.end(),
-      default_fallback_families.begin(), default_fallback_families.end());
+  if (!LowerCaseEqualsASCII(primary_family, "segoe ui") &&
+      !LowerCaseEqualsASCII(uniscribe_family, "segoe ui")) {
+    std::vector<std::string> default_fallback_families =
+        GetFallbackFontFamilies("Segoe UI");
+    fallback_families.insert(fallback_families.end(),
+        default_fallback_families.begin(), default_fallback_families.end());
+  }
 #endif
 
-  // Get rid of duplicate fonts in the fallback list. We use the std::unique
-  // algorithm for this. However for this function to work we need to sort
-  // the font list as the unique algorithm relies on duplicates being adjacent.
-  // TODO(ananta)
-  // Sorting the list changes the order in which fonts are evaluated. This may
-  // cause problems in the way some characters appear. It may be best to do
-  // font fallback on the same lines as blink or skia which do this based on
-  // character glyph mapping.
-  std::sort(fallback_families.begin(), fallback_families.end());
-  fallback_families.erase(std::unique(
-      fallback_families.begin(), fallback_families.end()),
-      fallback_families.end());
+  // Use a set to track the fallback fonts and avoid duplicate entries.
+  std::set<std::string, CaseInsensitiveCompare> fallback_fonts;
 
   // Try shaping with the fallback fonts.
   for (const auto& family : fallback_families) {
@@ -1323,6 +1331,11 @@
     if (family == uniscribe_family)
       continue;
 #endif
+    if (fallback_fonts.find(family) != fallback_fonts.end())
+      continue;
+
+    fallback_fonts.insert(family);
+
     FontRenderParamsQuery query(false);
     query.families.push_back(family);
     query.pixel_size = run->font_size;
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 4282557..b982f5bf 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -352,6 +352,39 @@
 #endif  // OS_MACOSX
 }
 
+TEST_F(RenderTextTest, AppendTextKeepsStyles) {
+  scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
+  // Setup basic functionality.
+  render_text->SetText(ASCIIToUTF16("abc"));
+  render_text->ApplyColor(SK_ColorRED, Range(0, 1));
+  render_text->ApplyBaselineStyle(SUPERSCRIPT, Range(1, 2));
+  render_text->ApplyStyle(UNDERLINE, true, Range(2, 3));
+  // Verify basic functionality.
+  std::vector<std::pair<size_t, SkColor>> expected_color;
+  expected_color.push_back(std::pair<size_t, SkColor>(0, SK_ColorRED));
+  expected_color.push_back(std::pair<size_t, SkColor>(1, SK_ColorBLACK));
+  EXPECT_TRUE(render_text->colors().EqualsForTesting(expected_color));
+  std::vector<std::pair<size_t, BaselineStyle>> expected_baseline;
+  expected_baseline.push_back(
+      std::pair<size_t, BaselineStyle>(0, NORMAL_BASELINE));
+  expected_baseline.push_back(std::pair<size_t, BaselineStyle>(1, SUPERSCRIPT));
+  expected_baseline.push_back(
+      std::pair<size_t, BaselineStyle>(2, NORMAL_BASELINE));
+  EXPECT_TRUE(render_text->baselines().EqualsForTesting(expected_baseline));
+  std::vector<std::pair<size_t, bool>> expected_style;
+  expected_style.push_back(std::pair<size_t, bool>(0, false));
+  expected_style.push_back(std::pair<size_t, bool>(2, true));
+  EXPECT_TRUE(
+      render_text->styles()[UNDERLINE].EqualsForTesting(expected_style));
+  // Ensure AppendText maintains current text styles.
+  render_text->AppendText(ASCIIToUTF16("def"));
+  EXPECT_EQ(render_text->GetDisplayText(), ASCIIToUTF16("abcdef"));
+  EXPECT_TRUE(render_text->colors().EqualsForTesting(expected_color));
+  EXPECT_TRUE(render_text->baselines().EqualsForTesting(expected_baseline));
+  EXPECT_TRUE(
+      render_text->styles()[UNDERLINE].EqualsForTesting(expected_style));
+}
+
 // TODO(asvitkine): Cursor movements tests disabled on Mac because RenderTextMac
 //                  does not implement this yet. http://crbug.com/131618
 #if !defined(OS_MACOSX)
@@ -609,6 +642,20 @@
   EXPECT_EQ(WideToUTF16(L"**\x2026"), render_text->GetDisplayText());
 }
 
+TEST_F(RenderTextTest, ElidedEmail) {
+  scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
+  render_text->SetText(ASCIIToUTF16("test@example.com"));
+  const gfx::Size size = render_text->GetStringSize();
+
+  const base::string16 long_email =
+      ASCIIToUTF16("longemailaddresstest@example.com");
+  render_text->SetText(long_email);
+  render_text->SetElideBehavior(ELIDE_EMAIL);
+  render_text->SetDisplayRect(gfx::Rect(size));
+  EXPECT_GE(size.width(), render_text->GetStringSize().width());
+  EXPECT_GT(long_email.size(), render_text->GetDisplayText().size());
+}
+
 TEST_F(RenderTextTest, TruncatedText) {
   struct {
     const wchar_t* text;
@@ -2577,6 +2624,23 @@
   EXPECT_EQ(Range(0, 0), glyphs);
 }
 
+// Ensure the line breaker doesn't compute the word's width bigger than the
+// actual size. See http://crbug.com/470073
+TEST_F(RenderTextTest, HarfBuzz_WordWidthWithDiacritics) {
+  RenderTextHarfBuzz render_text;
+  const base::string16 kWord = WideToUTF16(L"\u0906\u092A\u0915\u0947 ");
+  render_text.SetText(kWord);
+  const gfx::SizeF text_size = render_text.GetStringSizeF();
+
+  render_text.SetText(kWord + kWord);
+  render_text.SetMultiline(true);
+  EXPECT_EQ(text_size.width() * 2, render_text.GetStringSizeF().width());
+  EXPECT_EQ(text_size.height(), render_text.GetStringSizeF().height());
+  render_text.SetDisplayRect(gfx::Rect(0, 0, std::ceil(text_size.width()), 0));
+  EXPECT_NEAR(text_size.width(), render_text.GetStringSizeF().width(), 1.0f);
+  EXPECT_EQ(text_size.height() * 2, render_text.GetStringSizeF().height());
+}
+
 // Ensure a string fits in a display rect with a width equal to the string's.
 TEST_F(RenderTextTest, StringFitsOwnWidth) {
   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
diff --git a/ui/gl/gl_surface_glx.cc b/ui/gl/gl_surface_glx.cc
index d66078c8..80390609 100644
--- a/ui/gl/gl_surface_glx.cc
+++ b/ui/gl/gl_surface_glx.cc
@@ -466,6 +466,7 @@
     if (event_source)
       event_source->RemovePlatformEventDispatcher(this);
     XDestroyWindow(g_display, window_);
+    window_ = 0;
     XFlush(g_display);
   }
 }
diff --git a/ui/gl/gpu_timing.cc b/ui/gl/gpu_timing.cc
index a84d3f7..ee90490 100644
--- a/ui/gl/gpu_timing.cc
+++ b/ui/gl/gpu_timing.cc
@@ -44,7 +44,16 @@
 }
 
 GPUTimer::~GPUTimer() {
-  glDeleteQueries(2, queries_);
+  // Destroy() must be called before the destructor.
+  DCHECK(queries_[0] == 0);
+  DCHECK(queries_[1] == 0);
+}
+
+void GPUTimer::Destroy(bool have_context) {
+  if (have_context) {
+    glDeleteQueries(2, queries_);
+  }
+  memset(queries_, 0, sizeof(queries_));
 }
 
 void GPUTimer::Start() {
diff --git a/ui/gl/gpu_timing.h b/ui/gl/gpu_timing.h
index a749ef15..5658097b 100644
--- a/ui/gl/gpu_timing.h
+++ b/ui/gl/gpu_timing.h
@@ -76,6 +76,10 @@
  public:
   ~GPUTimer();
 
+  // Destroy the timer object. This must be explicitly called before destroying
+  // this object.
+  void Destroy(bool have_context);
+
   void Start();
   void End();
   bool IsAvailable();
diff --git a/ui/keyboard/keyboard_resources.grd b/ui/keyboard/keyboard_resources.grd
index 0b1f700bb..7a6ef8a 100644
--- a/ui/keyboard/keyboard_resources.grd
+++ b/ui/keyboard/keyboard_resources.grd
@@ -57,6 +57,11 @@
       <include name="IDR_KEYBOARD_LAYOUTS_COMPACT_NUMBERPAD" file="${input_tools_root_dir}/inputview/layouts/compactkbd_numberpad_layout.js" type="BINDATA" />
       <include name="IDR_KEYBOARD_LAYOUTS_EMOJI" file="${input_tools_root_dir}/inputview/layouts/emoji_layout.js" type="BINDATA" />
       <include name="IDR_KEYBOARD_LAYOUTS_HWT" file="${input_tools_root_dir}/inputview/layouts/handwriting_layout.js" type="BINDATA" />
+      <include name="IDR_KEYBOARD_LAYOUTS_MATERIAL_101" file="${input_tools_root_dir}/inputview/layouts/material/101kbd_layout.js" type="BINDATA"/>
+      <include name="IDR_KEYBOARD_LAYOUTS_MATERIAL_COMPACT_QWERTY" file="${input_tools_root_dir}/inputview/layouts/material/compactkbd_qwerty_layout.js" type="BINDATA" />
+      <include name="IDR_KEYBOARD_LAYOUTS_MATERIAL_COMPACT_NUMBERPAD" file="${input_tools_root_dir}/inputview/layouts/material/compactkbd_numberpad_layout.js" type="BINDATA" />
+      <include name="IDR_KEYBOARD_LAYOUTS_MATERIAL_EMOJI" file="${input_tools_root_dir}/inputview/layouts/material/emoji_layout.js" type="BINDATA" />
+      <include name="IDR_KEYBOARD_LAYOUTS_MATERIAL_HWT" file="${input_tools_root_dir}/inputview/layouts/material/handwriting_layout.js" type="BINDATA" />
       <include name="IDR_KEYBOARD_MANIFEST" file="resources/manifest.json" type="BINDATA" />
       <include name="IDR_KEYBOARD_SOUNDS_KEYPRESS_DELETE" file="${input_tools_root_dir}/sounds/keypress-delete.wav" type="BINDATA" />
       <include name="IDR_KEYBOARD_SOUNDS_KEYPRESS_RETURN" file="${input_tools_root_dir}/sounds/keypress-return.wav" type="BINDATA" />
diff --git a/ui/keyboard/keyboard_util.cc b/ui/keyboard/keyboard_util.cc
index 480dd67d..c15d5613 100644
--- a/ui/keyboard/keyboard_util.cc
+++ b/ui/keyboard/keyboard_util.cc
@@ -370,6 +370,16 @@
        IDR_KEYBOARD_LAYOUTS_COMPACT_NUMBERPAD},
       {"keyboard/inputview_layouts/emoji.js", IDR_KEYBOARD_LAYOUTS_EMOJI},
       {"keyboard/inputview_layouts/handwriting.js", IDR_KEYBOARD_LAYOUTS_HWT},
+      {"keyboard/inputview_layouts/m-101kbd.js",
+       IDR_KEYBOARD_LAYOUTS_MATERIAL_101},
+      {"keyboard/inputview_layouts/m-compactkbd-qwerty.js",
+       IDR_KEYBOARD_LAYOUTS_MATERIAL_COMPACT_QWERTY},
+      {"keyboard/inputview_layouts/m-compactkbd-numberpad.js",
+       IDR_KEYBOARD_LAYOUTS_MATERIAL_COMPACT_NUMBERPAD},
+      {"keyboard/inputview_layouts/m-emoji.js",
+       IDR_KEYBOARD_LAYOUTS_MATERIAL_EMOJI},
+      {"keyboard/inputview_layouts/m-handwriting.js",
+       IDR_KEYBOARD_LAYOUTS_MATERIAL_HWT},
       {"keyboard/manifest.json", IDR_KEYBOARD_MANIFEST},
       {"keyboard/sounds/keypress-delete.wav",
        IDR_KEYBOARD_SOUNDS_KEYPRESS_DELETE},
diff --git a/ui/login/account_picker/user_pod_row.js b/ui/login/account_picker/user_pod_row.js
index af79026..7119e9ae 100644
--- a/ui/login/account_picker/user_pod_row.js
+++ b/ui/login/account_picker/user_pod_row.js
@@ -1415,6 +1415,7 @@
         this.actionBoxMenu.classList.add('menu-moved-up');
         this.actionBoxAreaElement.classList.add('menu-moved-up');
       }
+      chrome.send('logRemoveUserWarningShown');
     },
 
     /**
diff --git a/ui/message_center/views/bounded_label.cc b/ui/message_center/views/bounded_label.cc
index ca9f0f9..e9b0f65e 100644
--- a/ui/message_center/views/bounded_label.cc
+++ b/ui/message_center/views/bounded_label.cc
@@ -45,11 +45,11 @@
 
   // Overridden from views::Label.
   void SetText(const base::string16& text) override;
+  void OnPaint(gfx::Canvas* canvas) override;
 
  protected:
   // Overridden from views::Label.
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
-  void OnPaint(gfx::Canvas* canvas) override;
 
  private:
   int GetTextFlags();
@@ -319,9 +319,8 @@
          label_->GetSizeForWidthAndLines(width, line_limit_).height() : 0;
 }
 
-void BoundedLabel::Paint(gfx::Canvas* canvas, const views::CullSet& cull_set) {
-  if (visible())
-    label_->Paint(canvas, cull_set);
+void BoundedLabel::OnPaint(gfx::Canvas* canvas) {
+  label_->OnPaint(canvas);
 }
 
 bool BoundedLabel::CanProcessEventsWithinSubtree() const {
diff --git a/ui/message_center/views/bounded_label.h b/ui/message_center/views/bounded_label.h
index 65611e21..076f91d 100644
--- a/ui/message_center/views/bounded_label.h
+++ b/ui/message_center/views/bounded_label.h
@@ -53,7 +53,7 @@
   int GetBaseline() const override;
   gfx::Size GetPreferredSize() const override;
   int GetHeightForWidth(int width) const override;
-  void Paint(gfx::Canvas* canvas, const views::CullSet& cull_set) override;
+  void OnPaint(gfx::Canvas* canvas) override;
   bool CanProcessEventsWithinSubtree() const override;
   void GetAccessibleState(ui::AXViewState* state) override;
 
diff --git a/ui/resources/default_100_percent/common/checkmark.png b/ui/resources/default_100_percent/common/checkmark.png
index 6d52c3e..1593616 100644
--- a/ui/resources/default_100_percent/common/checkmark.png
+++ b/ui/resources/default_100_percent/common/checkmark.png
Binary files differ
diff --git a/ui/resources/default_200_percent/common/checkmark.png b/ui/resources/default_200_percent/common/checkmark.png
index bcbc19c2..24d308f 100644
--- a/ui/resources/default_200_percent/common/checkmark.png
+++ b/ui/resources/default_200_percent/common/checkmark.png
Binary files differ
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index 599d5965..dff1e993 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -7,12 +7,14 @@
 #include <algorithm>
 
 #include "ui/base/hit_test.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/path.h"
 #include "ui/gfx/screen.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/resources/grit/ui_resources.h"
+#include "ui/strings/grit/ui_strings.h"
 #include "ui/views/bubble/bubble_border.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/image_view.h"
@@ -103,6 +105,7 @@
                   *rb.GetImageNamed(IDR_CLOSE_DIALOG_P).ToImageSkia());
   close->SetBorder(nullptr);
   close->SetSize(close->GetPreferredSize());
+  close->SetTooltipText(l10n_util::GetStringUTF16(IDS_APP_CLOSE));
   return close;
 }
 
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 717d9483..6ca37f9 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -268,6 +268,7 @@
       selection_background_color_(SK_ColorBLUE),
       placeholder_text_color_(kDefaultPlaceholderTextColor),
       text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
+      text_input_flags_(0),
       performing_user_action_(false),
       skip_input_method_cancel_composition_(false),
       cursor_visible_(false),
@@ -308,6 +309,10 @@
   SchedulePaint();
 }
 
+void Textfield::SetTextInputFlags(int flags) {
+  text_input_flags_ = flags;
+}
+
 void Textfield::SetText(const base::string16& new_text) {
   model_->SetText(new_text);
   OnCaretBoundsChanged();
@@ -1467,7 +1472,7 @@
 }
 
 int Textfield::GetTextInputFlags() const {
-  return 0;
+  return text_input_flags_;
 }
 
 bool Textfield::CanComposeInline() const {
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h
index b87953c..b931b70a 100644
--- a/ui/views/controls/textfield/textfield.h
+++ b/ui/views/controls/textfield/textfield.h
@@ -66,6 +66,10 @@
   // Sets the input type; displays only asterisks for TEXT_INPUT_TYPE_PASSWORD.
   void SetTextInputType(ui::TextInputType type);
 
+  // Sets the input flags so that the system input methods can turn on/off some
+  // features. The flags is the bit map of ui::TextInputFlags.
+  void SetTextInputFlags(int flags);
+
   // Gets the text currently displayed in the Textfield.
   const base::string16& text() const { return model_->text(); }
 
@@ -431,6 +435,9 @@
   // The input type of this text field.
   ui::TextInputType text_input_type_;
 
+  // The input flags of this text field.
+  int text_input_flags_;
+
   // The duration and timer to reveal the last typed password character.
   base::TimeDelta password_reveal_duration_;
   base::OneShotTimer<Textfield> password_reveal_timer_;
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 996b5483..e1906c23 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -764,16 +764,22 @@
 }
 
 void View::Paint(gfx::Canvas* canvas, const CullSet& cull_set) {
+  if (!visible_)
+    return;
   // The cull_set may allow us to skip painting without canvas construction or
   // even canvas rect intersection.
-  if (cull_set.ShouldPaint(this)) {
-    TRACE_EVENT1("views", "View::Paint", "class", GetClassName());
+  if (!cull_set.ShouldPaint(this))
+    return;
 
-    gfx::ScopedCanvas scoped_canvas(canvas);
+  TRACE_EVENT1("views", "View::Paint", "class", GetClassName());
 
-    // Paint this View and its children, setting the clip rect to the bounds
-    // of this View and translating the origin to the local bounds' top left
-    // point.
+  gfx::ScopedCanvas scoped_canvas(canvas);
+
+  // If the view is backed by a layer, it should paint with itself as the origin
+  // rather than relative to its parent.
+  if (!layer()) {
+    // Set the clip rect to the bounds of this View and translating the origin
+    // to the local bounds' top left point.
     //
     // Note that the X (or left) position we pass to ClipRectInt takes into
     // consideration whether or not the view uses a right-to-left layout so that
@@ -790,39 +796,55 @@
     // this view is located (related to its parent).
     canvas->Translate(GetMirroredPosition().OffsetFromOrigin());
     canvas->Transform(GetTransform());
-
-    // If we are a paint root, we need to construct our own CullSet object for
-    // propagation to our children.
-    if (IsPaintRoot()) {
-      if (!bounds_tree_)
-        bounds_tree_.reset(new BoundsTree(2, 5));
-
-      // Recompute our bounds tree as needed.
-      UpdateRootBounds(bounds_tree_.get(), gfx::Vector2d());
-
-      // Grab the clip rect from the supplied canvas to use as the query rect.
-      gfx::Rect canvas_bounds;
-      if (!canvas->GetClipBounds(&canvas_bounds)) {
-        NOTREACHED() << "Failed to get clip bounds from the canvas!";
-        return;
-      }
-
-      // Now query our bounds_tree_ for a set of damaged views that intersect
-      // our canvas bounds.
-      scoped_ptr<base::hash_set<intptr_t> > damaged_views(
-          new base::hash_set<intptr_t>());
-      bounds_tree_->AppendIntersectingRecords(
-          canvas_bounds, damaged_views.get());
-      // Construct a CullSet to wrap the damaged views set, it will delete it
-      // for us on scope exit.
-      CullSet paint_root_cull_set(damaged_views.Pass());
-      // Paint all descendents using our new cull set.
-      PaintCommon(canvas, paint_root_cull_set);
-    } else {
-      // Not a paint root, so we can proceed as normal.
-      PaintCommon(canvas, cull_set);
-    }
   }
+
+  {
+    // If the View we are about to paint requested the canvas to be flipped, we
+    // should change the transform appropriately.
+    // The canvas mirroring is undone once the View is done painting so that we
+    // don't pass the canvas with the mirrored transform to Views that didn't
+    // request the canvas to be flipped.
+    gfx::ScopedCanvas scoped(canvas);
+    if (FlipCanvasOnPaintForRTLUI()) {
+      canvas->Translate(gfx::Vector2d(width(), 0));
+      canvas->Scale(-1, 1);
+    }
+
+    // Delegate painting the contents of the View to the virtual OnPaint method.
+    OnPaint(canvas);
+  }
+
+  // If the view is not a paint root, it should come with a CullSet already
+  // constructed for use.
+  // TODO(danakj): However, if it is a paint root but is also backed by a
+  // layer(), we are unable to construct a CullSet because the bounds() do not
+  // match the position we will be painting at and UpdateRootBounds() does the
+  // wrong thing.
+  if (!IsPaintRoot() || layer()) {
+    PaintChildren(canvas, cull_set);
+    return;
+  }
+
+  // We are a paint root, so we construct our own CullSet object for propagation
+  // to our children.
+  if (!bounds_tree_)
+    bounds_tree_.reset(new BoundsTree(2, 5));
+
+  // Recompute our bounds tree as needed.
+  UpdateRootBounds(bounds_tree_.get(), gfx::Vector2d());
+
+  // Grab the clip rect from the supplied canvas to use as the query rect.
+  gfx::Rect canvas_bounds;
+  bool got_clip = canvas->GetClipBounds(&canvas_bounds);
+  DCHECK(got_clip) << "Failed to get clip bounds from the canvas!";
+
+  // Now query our bounds_tree_ for a set of damaged views that intersect
+  // our canvas bounds.
+  scoped_ptr<base::hash_set<intptr_t>> damaged_views(
+      new base::hash_set<intptr_t>);
+  bounds_tree_->AppendIntersectingRecords(canvas_bounds, damaged_views.get());
+  CullSet paint_root_cull_set(damaged_views.Pass());
+  PaintChildren(canvas, paint_root_cull_set);
 }
 
 void View::set_background(Background* b) {
@@ -1462,9 +1484,11 @@
 }
 
 void View::OnPaintLayer(gfx::Canvas* canvas) {
-  if (!layer() || !layer()->fills_bounds_opaquely())
+  if (!layer()->fills_bounds_opaquely())
     canvas->DrawColor(SK_ColorBLACK, SkXfermode::kClear_Mode);
-  PaintCommon(canvas, CullSet());
+  if (!visible_)
+    return;
+  Paint(canvas, CullSet());
 }
 
 void View::OnDelegatedFrameDamage(
@@ -1747,28 +1771,6 @@
   }
 }
 
-void View::PaintCommon(gfx::Canvas* canvas, const CullSet& cull_set) {
-  if (!visible_)
-    return;
-
-  {
-    // If the View we are about to paint requested the canvas to be flipped, we
-    // should change the transform appropriately.
-    // The canvas mirroring is undone once the View is done painting so that we
-    // don't pass the canvas with the mirrored transform to Views that didn't
-    // request the canvas to be flipped.
-    gfx::ScopedCanvas scoped(canvas);
-    if (FlipCanvasOnPaintForRTLUI()) {
-      canvas->Translate(gfx::Vector2d(width(), 0));
-      canvas->Scale(-1, 1);
-    }
-
-    OnPaint(canvas);
-  }
-
-  PaintChildren(canvas, cull_set);
-}
-
 // Tree operations -------------------------------------------------------------
 
 void View::DoRemoveChildView(View* view,
diff --git a/ui/views/view.h b/ui/views/view.h
index 659dff1..093be07b 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -497,7 +497,7 @@
   // for View coordinates and language direction as required, allows the View
   // to paint itself via the various OnPaint*() event handlers and then paints
   // the hierarchy beneath it.
-  virtual void Paint(gfx::Canvas* canvas, const CullSet& cull_set);
+  void Paint(gfx::Canvas* canvas, const CullSet& cull_set);
 
   // The background object is owned by this object and may be NULL.
   void set_background(Background* b);
@@ -1244,10 +1244,6 @@
   // new bounds.
   void SchedulePaintBoundsChanged(SchedulePaintType type);
 
-  // Common Paint() code shared by accelerated and non-accelerated code paths to
-  // invoke OnPaint() on the View.
-  void PaintCommon(gfx::Canvas* canvas, const CullSet& cull_set);
-
   // Tree operations -----------------------------------------------------------
 
   // Removes |view| from the hierarchy tree.  If |update_focus_cycle| is true,
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index 707d9f2f..cd6b30fc 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -240,7 +240,7 @@
   void OnMouseEntered(const ui::MouseEvent& event) override;
   void OnMouseExited(const ui::MouseEvent& event) override;
 
-  void Paint(gfx::Canvas* canvas, const CullSet& cull_set) override;
+  void OnPaint(gfx::Canvas* canvas) override;
   void SchedulePaintInRect(const gfx::Rect& rect) override;
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
 
@@ -423,7 +423,7 @@
 // Painting
 ////////////////////////////////////////////////////////////////////////////////
 
-void TestView::Paint(gfx::Canvas* canvas, const CullSet& cull_set) {
+void TestView::OnPaint(gfx::Canvas* canvas) {
   canvas->sk_canvas()->getClipBounds(&last_clip_);
 }
 
diff --git a/ui/webui/resources/cr_elements/cr_network_icon/badge_1x.png b/ui/webui/resources/cr_elements/cr_network_icon/badge_1x.png
new file mode 100644
index 0000000..2d11aee
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_network_icon/badge_1x.png
Binary files differ
diff --git a/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.js b/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.js
index c70079e..322a7dc 100644
--- a/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.js
+++ b/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.js
@@ -40,7 +40,7 @@
   else if (networkType == CrOnc.Type.VPN)
     return 'vpn';
 
-  console.error('Unrecognized network type: ' + networkType);
+  console.error('Unrecognized network type for icon: ' + networkType);
   return 'ethernet';
 }
 
@@ -232,6 +232,9 @@
       this.secure = false;
       var oncTechnology = networkState.getCellularTechnology();
       switch (oncTechnology) {
+        case CrOnc.NetworkTechnology.CDMA1XRTT:
+          this.technology = '1x';
+          break;
         case CrOnc.NetworkTechnology.EDGE:
           this.technology = 'edge';
           break;
diff --git a/ui/webui/resources/cr_elements/cr_onc/cr_onc_types.js b/ui/webui/resources/cr_elements/cr_onc/cr_onc_types.js
index 3497315..87bc6d3f 100644
--- a/ui/webui/resources/cr_elements/cr_onc/cr_onc_types.js
+++ b/ui/webui/resources/cr_elements/cr_onc/cr_onc_types.js
@@ -29,14 +29,15 @@
 
 /** @enum {string} */
 CrOnc.NetworkTechnology = {
+  CDMA1XRTT: 'CDMA1XRTT',
   EDGE: 'EDGE',
   EVDO: 'EVDO',
   GPRS: 'GPRS',
   GSM: 'GSM',
   HSPA: 'HSPA',
-  HSPA_PLUS: 'HSPA+',
+  HSPA_PLUS: 'HSPAPlus',
   LTE: 'LTE',
-  LTE_ADVANCED: 'LTE Advanced',
+  LTE_ADVANCED: 'LTEAdvanced',
   UMTS: 'UMTS',
   UNKNOWN: 'Unknown',
 };
diff --git a/ui/webui/resources/cr_elements_images.grdp b/ui/webui/resources/cr_elements_images.grdp
index 46a3689..571301a 100644
--- a/ui/webui/resources/cr_elements_images.grdp
+++ b/ui/webui/resources/cr_elements_images.grdp
@@ -1,5 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
+  <include name="IDR_WEBUI_CR_ELEMENTS_NETWORK_BADGE_CDMA1XRTT"
+           file="cr_elements/cr_network_icon/badge_1x.png" 
+           type="BINDATA" />
   <include name="IDR_WEBUI_CR_ELEMENTS_NETWORK_BADGE_3G"
            file="cr_elements/cr_network_icon/badge_3g.png" 
            type="BINDATA" />