diff --git a/AUTHORS b/AUTHORS
index 6eca1f9d..24bc010 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -106,6 +106,7 @@
 Chris Greene <cwgreene@amazon.com>
 Chris Harrelson <chrishtr@gmail.com>
 Chris Nardi <hichris123@gmail.com>
+Chris Tserng <tserng@amazon.com>
 Chris Vasselli <clindsay@gmail.com>
 Christophe Dumez <ch.dumez@samsung.com>
 Christopher Dale <chrelad@gmail.com>
@@ -202,7 +203,6 @@
 Haojian Wu <hokein.wu@gmail.com>
 Hari Singh <hari.singh1@samsung.com>
 Harpreet Singh Khurana <harpreet.sk@samsung.com>
-Hari Singh <hari.singh1@samsung.com>
 Harshikesh Kumar <harshikeshnobug@gmail.com>
 Hautio Kari <khautio@gmail.com>
 Heejin R. Chung <heejin.r.chung@samsung.com>
@@ -323,6 +323,7 @@
 Lauri Oherd <lauri.oherd@gmail.com>
 Legend Lee <guanxian.li@intel.com>
 Leith Bade <leith@leithalweapon.geek.nz>
+Leon Han <leon.han@intel.com>
 Li Yin <li.yin@intel.com>
 Lidwine Genevet <lgenevet@cisco.com>
 Lionel Landwerlin <lionel.g.landwerlin@intel.com>
diff --git a/BUILD.gn b/BUILD.gn
index 0f946dc..0e1e675 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -579,6 +579,10 @@
   } else if (!is_android) {
     deps += [ "//breakpad:symupload" ]
   }
+
+  if (!is_ios) {
+    deps += [ "//gpu/skia_runner:skia_runner" ]
+  }
 }
 
 group("gn_only") {
diff --git a/DEPS b/DEPS
index 304bf52..8d2784bb 100644
--- a/DEPS
+++ b/DEPS
@@ -30,15 +30,15 @@
   # Use this googlecode_url variable only if there is an internal mirror for it.
   # If you do not know, use the full path while defining your new deps entry.
   'googlecode_url': 'http://%s.googlecode.com/svn',
-  'webkit_revision': 'bf7d69edf081345477c922676187c35a786944b6', # from svn revision 197281
+  'webkit_revision': 'b6886e5f5daf410f914b5aeb863a2e150d9fdf46', # from svn revision 197475
   'chromium_git': 'https://chromium.googlesource.com',
   'libvpx_revision': 'db3f34772338a7e02fc5a7fb9da4d72de68e71b9',
   'sfntly_revision': '1bdaae8fc788a5ac8936d68bf24f37d977a13dac',
-  'skia_revision': '5a9e2994c9915f76b1e3720f107e87fc952ffab2',
+  'skia_revision': 'ce777c9ea30f7c1192f75bbfa34df3d7b34cd962',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'f15e93845900eee140eb2b7274a6e3658039302a',
+  'v8_revision': 'd297cd44a256ae716c9042335f2f06c3a6c78a5d',
   # 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.
@@ -46,7 +46,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '2539fffa36857396a0c0671a82a3ec9e605e6658',
+  'angle_revision': 'f0c1d0292d2f04e82ed0aea80359a85f32672a4e',
   # 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.
@@ -82,7 +82,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': '22fcd7c6a995b92434432aafe8ba49858bd7f5d3',
+  'nacl_revision': 'aecd00db6b864f86947a55e0827fe74c922df76d',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -126,7 +126,7 @@
    Var('chromium_git') + '/chromium/blink.git' + '@' +  Var('webkit_revision'),
 
   'src/third_party/icu':
-   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'a05f412f70489b77da0ddeb8baa31c68e3eff2c6',
+   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '00af1a4e512477e48ba3d5efc97adbc95462685e',
 
   'src/third_party/libexif/sources':
    Var('chromium_git') + '/chromium/deps/libexif/sources.git' + '@' + 'ed98343daabd7b4497f97fda972e132e6877c48a',
@@ -138,7 +138,7 @@
     Var('chromium_git') + '/external/google-safe-browsing/testing.git' + '@' + '9d7e8064f3ca2e45891470c9b5b1dce54af6a9d6',
 
   'src/third_party/leveldatabase/src':
-    Var('chromium_git') + '/external/leveldb.git' + '@' + '251ebf5dc70129ad3c38193fe6c99a5b0ec6b9fa',
+    Var('chromium_git') + '/external/leveldb.git' + '@' + '40c17c0b84ac0b791fb434096fd5c05f3819ad55',
 
   'src/third_party/snappy/src':
     Var('chromium_git') + '/external/snappy.git' + '@' + '762bb32f0c9d2f31ba4958c7c0933d22e80c20bf',
@@ -147,7 +147,7 @@
     Var('chromium_git') + '/external/grit-i18n.git' + '@' + 'c1b1591a05209c1ad467e845ba8543c22f9072af', # from svn revision 189
 
   'src/tools/gyp':
-    Var('chromium_git') + '/external/gyp.git' + '@' + 'fdcd8bc10c935eff13b391644b01460593c46861',
+    Var('chromium_git') + '/external/gyp.git' + '@' + '5122240c5e5c4d8da12c543d82b03d6089eb77c5',
 
   'src/tools/swarming_client':
    Var('chromium_git') + '/external/swarming.client.git' + '@' +  Var('swarming_revision'),
diff --git a/WATCHLISTS b/WATCHLISTS
index 4316700..fab5e31 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -269,6 +269,10 @@
       'filepath': 'chrome/browser/custom_handlers/|'\
                   'chrome/common/custom_handlers/',
     },
+    'custom_tabs': {
+      'filepath': 'chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/|'\
+                  'chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/',
+    },
     'deep_memory_profiler': {
       'filepath': 'tools/(deep_memory_profiler|find_runtime_symbols)',
     },
@@ -333,6 +337,11 @@
       'filepath': 'chrome/browser/enhanced_bookmarks/' \
                   '|components/enhanced_bookmarks/'
     },
+    'enhanced_bookmarks_android': {
+      'filepath': 'chrome/android/java_staging/src/org/chromium/'\
+                    'chrome/browser/enhancedbookmarks/|'\
+                  'chrome/android/java/res/layout/eb*.xml'
+    },
     'events': {
       'filepath': 'ui/events/',
     },
@@ -416,6 +425,9 @@
                   'chrome/browser/resources/local_ntp/|'\
                   'chrome/common/search_types.*',
     },
+    'ios_chrome': {
+      'filepath': 'ios/chrome',
+    },
     'ipc': {
       'filepath': 'ipc/ipc',
     },
@@ -944,6 +956,7 @@
     'content_worker': ['kinuko+watch@chromium.org'],
     'cookie_monster': ['erikwright@chromium.org'],
     'custom_handlers': ['vabr+watchlist@chromium.org'],
+    'custom_tabs': ['ianwen+watch@chromium.org'],
     'deep_memory_profiler': ['dmikurube@chromium.org'],
     'device_bluetooth': ['scheib+watch@chromium.org'],
     'device_sensors': ['timvolodine@chromium.org',
@@ -963,6 +976,7 @@
     'drive_resource_metadata': ['hashimoto+watch@chromium.org'],
     'eme': ['eme-reviews@chromium.org'],
     'enhanced_bookmarks': ['noyau+watch@chromium.org'],
+    'enhanced_bookmarks_android': ['ianwen+watch@chromium.org'],
     'events': ['tdresser+watch@chromium.org', 'jdduke+watch@chromium.org'],
     'extension': ['chromium-apps-reviews@chromium.org',
                   'extensions-reviews@chromium.org'],
@@ -992,6 +1006,7 @@
                 'melevin+watch@chromium.org', 'dougw+watch@chromium.org',
                 'kmadhusu+watch@chromium.org', 'dhollowa+watch@chromium.org',
                 'jfweitz+watch@chromium.org', 'skanuj+watch@chromium.org'],
+    'ios_chrome': ['sdefresne+watch@chromium.org'],
     'ipc': ['jam@chromium.org', 'darin-cc@chromium.org'],
     'libwebp': ['urvang@chromium.org', 'jzern@chromium.org',
                 'skal@google.com', 'vikasa@google.com'],
@@ -1008,8 +1023,7 @@
                     'orenb+watch-md-settings@chromium.org',
                     'stevenjb+watch-md-settings@chromium.org'],
     'media': ['feature-media-reviews@chromium.org'],
-    'media_galleries': ['gbillock@chromium.org', 'thestig@chromium.org',
-                        'tommycli@chromium.org', 'vandebo@chromium.org'],
+    'media_galleries': ['thestig@chromium.org', 'tommycli@chromium.org'],
     'media_router': ['media-router+watch@chromium.org'],
     'message_loop': ['sadrul@chromium.org'],
     'metrics': ['asvitkine+watch@chromium.org'],
diff --git a/android_webview/android_webview_telemetry_shell.gyp b/android_webview/android_webview_telemetry_shell.gyp
deleted file mode 100644
index 7d812ff..0000000
--- a/android_webview/android_webview_telemetry_shell.gyp
+++ /dev/null
@@ -1,17 +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.
-{
-  'targets': [
-    {
-      'target_name': 'android_webview_telemetry_shell_apk',
-      'type': 'none',
-      'variables': {
-        'apk_name': 'AndroidWebViewTelemetryShell',
-        'java_in_dir': 'tools/WebViewTelemetryShell',
-        'resource_dir': 'tools/WebViewTelemetryShell/res',
-      },
-      'includes': [ '../build/java_apk.gypi' ],
-    },
-  ],
-}
diff --git a/android_webview/browser/hardware_renderer.cc b/android_webview/browser/hardware_renderer.cc
index 7e271ce3..224630d 100644
--- a/android_webview/browser/hardware_renderer.cc
+++ b/android_webview/browser/hardware_renderer.cc
@@ -150,15 +150,15 @@
 
   cc::SharedQuadState* quad_state =
       render_pass->CreateAndAppendSharedQuadState();
-  quad_state->content_to_target_transform = transform;
-  quad_state->content_bounds = frame_size_;
-  quad_state->visible_content_rect = gfx::Rect(frame_size_);
+  quad_state->quad_to_target_transform = transform;
+  quad_state->quad_layer_bounds = frame_size_;
+  quad_state->visible_quad_layer_rect = gfx::Rect(frame_size_);
   quad_state->opacity = 1.f;
 
   cc::SurfaceDrawQuad* surface_quad =
       render_pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
-  surface_quad->SetNew(quad_state, gfx::Rect(quad_state->content_bounds),
-                       gfx::Rect(quad_state->content_bounds), child_id_);
+  surface_quad->SetNew(quad_state, gfx::Rect(quad_state->quad_layer_bounds),
+                       gfx::Rect(quad_state->quad_layer_bounds), child_id_);
 
   scoped_ptr<cc::DelegatedFrameData> delegated_frame(
       new cc::DelegatedFrameData);
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 8e0a6c2f..fa89021c 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -2596,6 +2596,7 @@
         final int y = oldY + deltaY;
         final int scrollRangeX = mScrollOffsetManager.computeMaximumHorizontalScrollOffset();
         final int scrollRangeY = mScrollOffsetManager.computeMaximumVerticalScrollOffset();
+        // absorbGlow() will release the glow if it is not finished.
         mOverScrollGlow.absorbGlow(x, y, oldX, oldY, scrollRangeX, scrollRangeY,
                 (float) Math.hypot(velocityX, velocityY));
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClientCallbackHelper.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClientCallbackHelper.java
index 838dbe0..93e4d67 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsClientCallbackHelper.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClientCallbackHelper.java
@@ -90,6 +90,8 @@
     private static final int MSG_ON_RECEIVED_HTTP_ERROR = 8;
     private static final int MSG_ON_PAGE_FINISHED = 9;
     private static final int MSG_ON_RECEIVED_TITLE = 10;
+    private static final int MSG_ON_PROGRESS_CHANGED = 11;
+    private static final int MSG_SYNTHESIZE_PAGE_LOADING = 12;
 
     // Minimum period allowed between consecutive onNewPicture calls, to rate-limit the callbacks.
     private static final long ON_NEW_PICTURE_MIN_PERIOD_MILLIS = 500;
@@ -169,6 +171,18 @@
                     mContentsClient.onReceivedTitle(title);
                     break;
                 }
+                case MSG_ON_PROGRESS_CHANGED: {
+                    mContentsClient.onProgressChanged(msg.arg1);
+                    break;
+                }
+                case MSG_SYNTHESIZE_PAGE_LOADING: {
+                    final String url = (String) msg.obj;
+                    mContentsClient.onPageStarted(url);
+                    mContentsClient.onLoadResource(url);
+                    mContentsClient.onProgressChanged(100);
+                    mContentsClient.onPageFinished(url);
+                    break;
+                }
                 default:
                     throw new IllegalStateException(
                             "AwContentsClientCallbackHelper: unhandled message " + msg.what);
@@ -239,4 +253,12 @@
     public void postOnReceivedTitle(String title) {
         mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_RECEIVED_TITLE, title));
     }
+
+    public void postOnProgressChanged(int progress) {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_PROGRESS_CHANGED, progress, 0));
+    }
+
+    public void postSynthesizedPageLoadingForUrlBarUpdate(String url) {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_SYNTHESIZE_PAGE_LOADING, url));
+    }
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
index 4528484..dc767461 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
@@ -53,7 +53,7 @@
 
     @Override
     public void onLoadProgressChanged(int progress) {
-        mContentsClient.onProgressChanged(progress);
+        mContentsClient.getCallbackHelper().postOnProgressChanged(progress);
     }
 
     @Override
@@ -256,10 +256,7 @@
             // the pending entry.
             String url = mAwContents.getLastCommittedUrl();
             url = TextUtils.isEmpty(url) ? "about:blank" : url;
-            mContentsClient.onPageStarted(url);
-            mContentsClient.onLoadResource(url);
-            mContentsClient.onProgressChanged(100);
-            mContentsClient.onPageFinished(url);
+            mContentsClient.getCallbackHelper().postSynthesizedPageLoadingForUrlBarUpdate(url);
         }
     }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/OverScrollGlow.java b/android_webview/java/src/org/chromium/android_webview/OverScrollGlow.java
index 8c60d95..9e3b709d 100644
--- a/android_webview/java/src/org/chromium/android_webview/OverScrollGlow.java
+++ b/android_webview/java/src/org/chromium/android_webview/OverScrollGlow.java
@@ -101,6 +101,12 @@
      */
     public void absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY,
             float currentFlingVelocity) {
+        if (mShouldPull) {
+            // Not absorb the glow because the user is pulling the glow now.
+            // TODO(hush): crbug.com/501556. Do not use "mShouldPull" to switch
+            // between absorbGlow and pullGlow. Use the velocity instead.
+            return;
+        }
         if (rangeY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
             if (y < 0 && oldY >= 0) {
                 mEdgeGlowTop.onAbsorb((int) currentFlingVelocity);
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java
index 5e1a0701..070b1a67 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java
@@ -21,9 +21,7 @@
 
     private static final String TAG = "AwShellApplication";
     /** The minimum set of .pak files the test runner needs. */
-    private static final String[] MANDATORY_PAKS = { "icudtl.dat",
-                                                     "natives_blob.bin",
-                                                     "snapshot_blob.bin" };
+    private static final String[] MANDATORY_PAKS = {"natives_blob.bin", "snapshot_blob.bin"};
 
     @Override
     public void onCreate() {
diff --git a/android_webview/tools/WebViewTelemetryShell/AndroidManifest.xml b/android_webview/tools/WebViewTelemetryShell/AndroidManifest.xml
deleted file mode 100644
index dbdda35..0000000
--- a/android_webview/tools/WebViewTelemetryShell/AndroidManifest.xml
+++ /dev/null
@@ -1,40 +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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="org.chromium.telemetry_shell"
-    android:versionCode="1"
-    android:versionName="1.0" >
-
-    <uses-sdk android:minSdkVersion="17"
-      android:targetSdkVersion="19" />
-
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
-
-  <application
-        android:icon="@drawable/ic_launcher"
-        android:label="@string/app_name"
-        android:theme="@android:style/Theme.Light" >
-        <activity
-            android:name=".TelemetryActivity"
-            android:label="@string/title_activity_telemetry" >
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name=".JankActivity"
-            android:label="@string/title_activity_jank"
-            android:noHistory="true">
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/android_webview/tools/WebViewTelemetryShell/res/drawable/ic_launcher.png b/android_webview/tools/WebViewTelemetryShell/res/drawable/ic_launcher.png
deleted file mode 100644
index 96a442e..0000000
--- a/android_webview/tools/WebViewTelemetryShell/res/drawable/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/android_webview/tools/WebViewTelemetryShell/res/layout/activity_telemetry.xml b/android_webview/tools/WebViewTelemetryShell/res/layout/activity_telemetry.xml
deleted file mode 100644
index 0fd1ab5..0000000
--- a/android_webview/tools/WebViewTelemetryShell/res/layout/activity_telemetry.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/container"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:gravity="center">
-
-    <WebView
-        android:id="@+id/webview"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-</LinearLayout>
-
diff --git a/android_webview/tools/WebViewTelemetryShell/res/values/strings.xml b/android_webview/tools/WebViewTelemetryShell/res/values/strings.xml
deleted file mode 100644
index 420ab8f..0000000
--- a/android_webview/tools/WebViewTelemetryShell/res/values/strings.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?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>
-    <string name="app_name">WebView Telemetry</string>
-    <string name="title_activity_telemetry">WebView Telemetry</string>
-    <string name="title_activity_jank">WebView Jank Tester</string>
-</resources>
diff --git a/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/JankActivity.java b/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/JankActivity.java
deleted file mode 100644
index bb83667..0000000
--- a/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/JankActivity.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.telemetry_shell;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.webkit.CookieManager;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-
-/**
- * This activity is designed for Android Jank testing of WebView.
- * It takes a URL as an argument, and displays the page ready for the Jank tester to test
- * scrolling etc.
- */
-public class JankActivity extends Activity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().setTitle(
-                getResources().getString(R.string.title_activity_jank));
-        setContentView(R.layout.activity_telemetry);
-
-        WebView webView = (WebView) findViewById(R.id.webview);
-        CookieManager.setAcceptFileSchemeCookies(true);
-
-        webView.setWebViewClient(new WebViewClient() {
-            @Override
-            public boolean shouldOverrideUrlLoading(WebView webView, String url) {
-                return false;
-            }
-        });
-
-        String url = getUrlFromIntent(getIntent());
-        webView.loadUrl(url);
-    }
-
-    private static String getUrlFromIntent(Intent intent) {
-        return intent != null ? intent.getDataString() : null;
-    }
-
-}
diff --git a/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/TelemetryActivity.java b/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/TelemetryActivity.java
deleted file mode 100644
index 005a27e8..0000000
--- a/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/TelemetryActivity.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.telemetry_shell;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.webkit.CookieManager;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-
-/**
- * This activity is designed for Telemetry testing of WebView.
- */
-public class TelemetryActivity extends Activity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().setTitle(
-                getResources().getString(R.string.title_activity_telemetry));
-        setContentView(R.layout.activity_telemetry);
-
-        WebView webView = (WebView) findViewById(R.id.webview);
-        CookieManager.setAcceptFileSchemeCookies(true);
-        webView.getSettings().setJavaScriptEnabled(true);
-
-        webView.setWebViewClient(new WebViewClient() {
-                @Override
-                public boolean shouldOverrideUrlLoading(WebView webView, String url) {
-                    return false;
-                }
-        });
-
-        webView.loadUrl("about:blank");
-    }
-}
diff --git a/ash/display/display_layout.cc b/ash/display/display_layout.cc
index 9d8942e..2cf54a6 100644
--- a/ash/display/display_layout.cc
+++ b/ash/display/display_layout.cc
@@ -4,6 +4,7 @@
 
 #include "ash/display/display_layout.h"
 
+#include "ash/ash_switches.h"
 #include "ash/display/display_pref_util.h"
 #include "base/json/json_value_converter.h"
 #include "base/logging.h"
@@ -70,7 +71,11 @@
     : position(RIGHT),
       offset(0),
       mirrored(false),
+#if defined(OS_CHROMEOS)
+      default_unified(switches::UnifiedDesktopEnabled()),
+#else
       default_unified(false),
+#endif
       primary_id(gfx::Display::kInvalidDisplayID) {
 }
 
@@ -78,7 +83,11 @@
     : position(position),
       offset(offset),
       mirrored(false),
+#if defined(OS_CHROMEOS)
+      default_unified(switches::UnifiedDesktopEnabled()),
+#else
       default_unified(false),
+#endif
       primary_id(gfx::Display::kInvalidDisplayID) {
   DCHECK_LE(TOP, position);
   DCHECK_GE(LEFT, position);
diff --git a/ash/display/display_layout_store.cc b/ash/display/display_layout_store.cc
index 436b1937..3ef9134 100644
--- a/ash/display/display_layout_store.cc
+++ b/ash/display/display_layout_store.cc
@@ -48,7 +48,13 @@
     int64 id1,
     int64 id2,
     const DisplayLayout& layout) {
-  paired_layouts_[std::make_pair(id1, id2)] = layout;
+  auto key = std::make_pair(id1, id2);
+  paired_layouts_[key] = layout;
+#if defined(OS_CHROMEOS)
+  // Force disabling unified desktop if the flag is not set.
+  if (!switches::UnifiedDesktopEnabled())
+    paired_layouts_[key].default_unified = false;
+#endif
 }
 
 DisplayLayout DisplayLayoutStore::GetRegisteredDisplayLayout(
@@ -88,9 +94,6 @@
 DisplayLayout DisplayLayoutStore::CreateDisplayLayout(
     const DisplayIdPair& pair) {
   DisplayLayout layout = default_display_layout_;
-  layout.default_unified =
-      Shell::GetInstance()->display_manager()->default_multi_display_mode() ==
-      DisplayManager::UNIFIED;
   layout.primary_id = pair.first;
   paired_layouts_[pair] = layout;
   return layout;
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index bb8dafe..519ddb5 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -1505,10 +1505,7 @@
 
   UpdateDisplay("300x200,400x500");
 
-  // Switch to unified desktop.
-  display_manager()->SetDefaultMultiDisplayMode(DisplayManager::UNIFIED);
-  display_manager()->ReconfigureDisplays();
-
+  // Defaults to the unified desktop.
   gfx::Screen* screen =
       gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_ALTERNATE);
   EXPECT_EQ("700x500", screen->GetPrimaryDisplay().size().ToString());
@@ -1542,7 +1539,6 @@
   // Don't check root window destruction in unified mode.
   Shell::GetPrimaryRootWindow()->RemoveObserver(this);
 
-  display_manager()->SetDefaultMultiDisplayMode(DisplayManager::UNIFIED);
   UpdateDisplay("300x200,400x500");
   // Mirror windows are created in a posted task.
   RunAllPendingInMessageLoop();
@@ -1559,8 +1555,6 @@
   // Don't check root window destruction in unified mode.
   Shell::GetPrimaryRootWindow()->RemoveObserver(this);
 
-  display_manager()->SetDefaultMultiDisplayMode(DisplayManager::UNIFIED);
-  display_manager()->SetMultiDisplayMode(DisplayManager::UNIFIED);
   UpdateDisplay("300x200,400x500");
 
   gfx::Screen* screen =
@@ -1588,8 +1582,6 @@
   // Don't check root window destruction in unified mode.
   Shell::GetPrimaryRootWindow()->RemoveObserver(this);
 
-  display_manager()->SetDefaultMultiDisplayMode(DisplayManager::UNIFIED);
-  display_manager()->SetMultiDisplayMode(DisplayManager::UNIFIED);
   UpdateDisplay("300x200,400x500");
 
   scoped_ptr<aura::Window> docked(
diff --git a/ash/display/projecting_observer_chromeos.cc b/ash/display/projecting_observer_chromeos.cc
index 1e55cd2..0bf6af47 100644
--- a/ash/display/projecting_observer_chromeos.cc
+++ b/ash/display/projecting_observer_chromeos.cc
@@ -17,9 +17,6 @@
       casting_session_count_(0),
       power_manager_client_(power_manager_client) {
   DCHECK(power_manager_client);
-#if defined(USE_OZONE)
-  is_initial_configuration_ = true;
-#endif
 }
 
 ProjectingObserver::~ProjectingObserver() {}
@@ -36,13 +33,6 @@
     }
   }
 
-#if defined(USE_OZONE)
-  if (is_initial_configuration_) {
-    is_initial_configuration_ = false;
-    return;
-  }
-#endif
-
   SetIsProjecting();
 }
 
diff --git a/ash/display/projecting_observer_chromeos.h b/ash/display/projecting_observer_chromeos.h
index b9305dd..0c6b4d14 100644
--- a/ash/display/projecting_observer_chromeos.h
+++ b/ash/display/projecting_observer_chromeos.h
@@ -47,14 +47,6 @@
   // Weak pointer to the DBusClient PowerManagerClient;
   chromeos::PowerManagerClient* power_manager_client_;
 
-#if defined(USE_OZONE)
-  // TODO(dnicoara) Remove once merged to M43.
-  // Used to skip the first call to the power management during the initial
-  // display configuration to avoid changing power settings due to possibly
-  // invalid display configuration.
-  bool is_initial_configuration_;
-#endif
-
   DISALLOW_COPY_AND_ASSIGN(ProjectingObserver);
 };
 
diff --git a/ash/display/projecting_observer_chromeos_unittest.cc b/ash/display/projecting_observer_chromeos_unittest.cc
index 4196800..06eb798 100644
--- a/ash/display/projecting_observer_chromeos_unittest.cc
+++ b/ash/display/projecting_observer_chromeos_unittest.cc
@@ -30,14 +30,6 @@
 
   ~ProjectingObserverTest() override {}
 
-#if defined(USE_OZONE)
-  void SetUp() override {
-    // First configuration event is ignored on Ozone to work around setting the
-    // wrong power state during startup.
-    observer_.OnDisplayModeChanged(std::vector<ui::DisplaySnapshot*>());
-  }
-#endif
-
  protected:
   chromeos::FakePowerManagerClient fake_power_client_;
   ProjectingObserver observer_;
diff --git a/ash/display/unified_mouse_warp_controller_unittest.cc b/ash/display/unified_mouse_warp_controller_unittest.cc
index ecbf496..fa450ce0 100644
--- a/ash/display/unified_mouse_warp_controller_unittest.cc
+++ b/ash/display/unified_mouse_warp_controller_unittest.cc
@@ -4,8 +4,6 @@
 
 #include "ash/display/unified_mouse_warp_controller.h"
 
-#include "ash/display/display_layout_store.h"
-#include "ash/display/display_manager.h"
 #include "ash/display/mouse_cursor_event_filter.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -25,9 +23,6 @@
   void SetUp() override {
     test::AshTestBase::SetUp();
     test::DisplayManagerTestApi::EnableUnifiedDesktopForTest();
-    DisplayManager* display_manager = Shell::GetInstance()->display_manager();
-    display_manager->SetDefaultMultiDisplayMode(DisplayManager::UNIFIED);
-    display_manager->SetMultiDisplayMode(DisplayManager::UNIFIED);
   }
 
  protected:
diff --git a/ash/magnifier/magnification_controller_unittest.cc b/ash/magnifier/magnification_controller_unittest.cc
index aeb89df..20dfb43e 100644
--- a/ash/magnifier/magnification_controller_unittest.cc
+++ b/ash/magnifier/magnification_controller_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "ash/magnifier/magnification_controller.h"
 
-#include "ash/display/display_manager.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/display_manager_test_api.h"
@@ -673,10 +672,6 @@
     return;
   test::DisplayManagerTestApi::EnableUnifiedDesktopForTest();
 
-  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
-  display_manager->SetDefaultMultiDisplayMode(DisplayManager::UNIFIED);
-  display_manager->SetMultiDisplayMode(DisplayManager::UNIFIED);
-
   EXPECT_EQ(1.0f, GetMagnificationController()->GetScale());
 
   GetMagnificationController()->SetEnabled(true);
diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd
index c1eb94e5..be2bae7 100644
--- a/ash/resources/ash_resources.grd
+++ b/ash/resources/ash_resources.grd
@@ -84,7 +84,6 @@
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_CAST_DISABLED_HOVER" file="cros/status/status_cast_disabled_hover.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_CAST_ENABLED" file="cros/status/status_cast_enabled.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_CAST_ENABLED_HOVER" file="cros/status/status_cast_enabled_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_CAST_STATUS" file="cros/status/status_cast_tray.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_DRIVE" file="cros/status/status_drive.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_DRIVE_CANCEL" file="cros/status/status_drive_item_cancel.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_DRIVE_CANCEL_HOVER" file="cros/status/status_drive_item_cancel_hover.png" />
@@ -105,6 +104,8 @@
 
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_MORE" file="cros/status/status_more.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_OVERVIEW_MODE" file="cros/status/status_overview_mode.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SCREENSHARE" file="cros/status/status_screenshare.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SCREENSHARE_DARK" file="cros/status/status_screenshare_dark.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SHUTDOWN" file="cros/status/status_shutdown.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SHUTDOWN_HOVER" file="cros/status/status_shutdown_hover.png" />
 
@@ -163,8 +164,6 @@
         <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_POWER_SMALL_DARK" file="cros/status/status_power_small_all_dark.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_RECORDING" file="cros/status/status_recording.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_RECORDING_RED" file="cros/status/status_recording_red.png" />
-        <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SCREENSHARE" file="cros/status/status_screenshare.png" />
-        <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SCREENSHARE_DARK" file="cros/status/status_screenshare_dark.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SETTINGS" file="cros/status/status_settings.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SMS" file="cros/status/status_sms.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_SMS_DISMISS" file="cros/status/status_sms_dismiss.png" />
diff --git a/ash/resources/default_100_percent/cros/status/status_cast_tray.png b/ash/resources/default_100_percent/cros/status/status_cast_tray.png
deleted file mode 100644
index 01186e5b..0000000
--- a/ash/resources/default_100_percent/cros/status/status_cast_tray.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_cast_tray.png b/ash/resources/default_200_percent/cros/status/status_cast_tray.png
deleted file mode 100644
index e2853c4b..0000000
--- a/ash/resources/default_200_percent/cros/status/status_cast_tray.png
+++ /dev/null
Binary files differ
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index 8189581..88e254f6 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -295,9 +295,6 @@
     return;
   test::DisplayManagerTestApi::EnableUnifiedDesktopForTest();
 
-  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
-  display_manager->SetDefaultMultiDisplayMode(DisplayManager::UNIFIED);
-  display_manager->SetMultiDisplayMode(DisplayManager::UNIFIED);
   UpdateDisplay("500x500");
   const int kLockScreenWindowId = 1000;
   const int kLockBackgroundWindowId = 1001;
@@ -338,6 +335,7 @@
   EXPECT_EQ("0,0 500x500", lock_screen->GetNativeWindow()->bounds().ToString());
 
   // Switch to mirror.
+  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
   display_manager->SetMirrorMode(true);
   EXPECT_TRUE(display_manager->IsInMirrorMode());
 
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index e09f5fd..b737e80 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -22,6 +22,7 @@
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_item.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/display_manager_test_api.h"
 #include "ash/test/shelf_test_api.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
@@ -2342,10 +2343,8 @@
 TEST_F(ShelfLayoutManagerTest, ShelfLayoutInUnifiedDesktop) {
   if (!SupportsMultipleDisplays())
     return;
+  test::DisplayManagerTestApi::EnableUnifiedDesktopForTest();
 
-  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
-  display_manager->SetDefaultMultiDisplayMode(DisplayManager::UNIFIED);
-  display_manager->SetMultiDisplayMode(DisplayManager::UNIFIED);
   UpdateDisplay("500x500, 500x500");
 
   StatusAreaWidget* status_area_widget =
diff --git a/ash/shell/content_client/shell_content_browser_client.cc b/ash/shell/content_client/shell_content_browser_client.cc
index 5f2adc5..d75e759 100644
--- a/ash/shell/content_client/shell_content_browser_client.cc
+++ b/ash/shell/content_client/shell_content_browser_client.cc
@@ -6,22 +6,14 @@
 
 #include "ash/shell/content_client/shell_browser_main_parts.h"
 #include "base/command_line.h"
-#include "content/public/common/content_descriptors.h"
-#include "content/public/common/content_switches.h"
 #include "content/shell/browser/shell_browser_context.h"
-#include "gin/v8_initializer.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 namespace ash {
 namespace shell {
 
 ShellContentBrowserClient::ShellContentBrowserClient()
-    :
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-      v8_natives_fd_(-1),
-      v8_snapshot_fd_(-1),
-#endif  // OS_POSIX && !OS_MACOSX
-      shell_browser_main_parts_(nullptr) {
+    : shell_browser_main_parts_(nullptr) {
 }
 
 ShellContentBrowserClient::~ShellContentBrowserClient() {
@@ -43,46 +35,6 @@
                                              request_interceptors.Pass());
 }
 
-void ShellContentBrowserClient::AppendMappedFileCommandLineSwitches(
-    base::CommandLine* command_line) {
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
-  std::string process_type =
-      command_line->GetSwitchValueASCII(switches::kProcessType);
-  if (process_type != switches::kZygoteProcess) {
-    DCHECK(natives_fd_exists());
-    command_line->AppendSwitch(::switches::kV8NativesPassedByFD);
-    if (snapshot_fd_exists())
-      command_line->AppendSwitch(::switches::kV8SnapshotPassedByFD);
-  }
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // OS_POSIX && !OS_MACOSX
-}
-
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-void ShellContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
-    const base::CommandLine& command_line,
-    int child_process_id,
-    content::FileDescriptorInfo* mappings) {
-#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
-  if (!natives_fd_exists()) {
-    int v8_natives_fd = -1;
-    int v8_snapshot_fd = -1;
-    if (gin::V8Initializer::OpenV8FilesForChildProcesses(&v8_natives_fd,
-                                                         &v8_snapshot_fd)) {
-      v8_natives_fd_.reset(v8_natives_fd);
-      v8_snapshot_fd_.reset(v8_snapshot_fd);
-    }
-  }
-  // V8 can't start up without the source of the natives, but it can
-  // start up (slower) without the snapshot.
-  DCHECK(natives_fd_exists());
-  mappings->Share(kV8NativesDataDescriptor, v8_natives_fd_.get());
-  mappings->Share(kV8SnapshotDataDescriptor, v8_snapshot_fd_.get());
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-}
-#endif  // OS_POSIX && !OS_MACOSX
-
 content::ShellBrowserContext* ShellContentBrowserClient::browser_context() {
   return shell_browser_main_parts_->browser_context();
 }
diff --git a/ash/shell/content_client/shell_content_browser_client.h b/ash/shell/content_client/shell_content_browser_client.h
index 432f45a5..1a5eb44 100644
--- a/ash/shell/content_client/shell_content_browser_client.h
+++ b/ash/shell/content_client/shell_content_browser_client.h
@@ -33,26 +33,10 @@
       content::BrowserContext* browser_context,
       content::ProtocolHandlerMap* protocol_handlers,
       content::URLRequestInterceptorScopedVector request_interceptors) override;
-  void AppendMappedFileCommandLineSwitches(
-      base::CommandLine* command_line) override;
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-  void GetAdditionalMappedFilesForChildProcess(
-      const base::CommandLine& command_line,
-      int child_process_id,
-      content::FileDescriptorInfo* mappings) override;
-#endif
 
   content::ShellBrowserContext* browser_context();
 
  private:
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-  bool natives_fd_exists() { return v8_natives_fd_.is_valid(); }
-  bool snapshot_fd_exists() { return v8_snapshot_fd_.is_valid(); }
-
-  base::ScopedFD v8_natives_fd_;
-  base::ScopedFD v8_snapshot_fd_;
-#endif
-
   ShellBrowserMainParts* shell_browser_main_parts_;
 
   DISALLOW_COPY_AND_ASSIGN(ShellContentBrowserClient);
diff --git a/ash/system/cast/tray_cast.cc b/ash/system/cast/tray_cast.cc
index 916be01..ceb000e5 100644
--- a/ash/system/cast/tray_cast.cc
+++ b/ash/system/cast/tray_cast.cc
@@ -354,7 +354,7 @@
   CreateImageView();
 
   image_view()->SetImage(ui::ResourceBundle::GetSharedInstance()
-                             .GetImageNamed(IDR_AURA_UBER_TRAY_CAST_STATUS)
+                             .GetImageNamed(IDR_AURA_UBER_TRAY_SCREENSHARE)
                              .ToImageSkia());
 }
 
diff --git a/ash/test/display_manager_test_api.cc b/ash/test/display_manager_test_api.cc
index 751553dc..7627289 100644
--- a/ash/test/display_manager_test_api.cc
+++ b/ash/test/display_manager_test_api.cc
@@ -8,6 +8,7 @@
 
 #include "ash/ash_switches.h"
 #include "ash/display/display_info.h"
+#include "ash/display/display_layout_store.h"
 #include "ash/display/display_manager.h"
 #include "ash/display/display_util.h"
 #include "ash/display/extended_mouse_warp_controller.h"
@@ -99,6 +100,10 @@
 #if defined(OS_CHROMEOS)
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kAshEnableUnifiedDesktop);
+  Shell::GetInstance()
+      ->display_manager()
+      ->layout_store()
+      ->SetDefaultDisplayLayout(DisplayLayout());
 #endif
 }
 
diff --git a/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_ozone.cc b/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_ozone.cc
index 8bae2a7..a6d0b64 100644
--- a/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_ozone.cc
+++ b/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_ozone.cc
@@ -19,26 +19,28 @@
   ui::InputController* input_controller =
       ui::OzonePlatform::GetInstance()->GetInputController();
   if (input_controller->HasTouchpad()) {
-    input_controller->DisableInternalTouchpad();
+    input_controller->SetInternalTouchpadEnabled(false);
     aura::client::GetCursorClient(Shell::GetInstance()->GetPrimaryRootWindow())
         ->HideCursor();
   }
 
   // Allow the acccessible keys present on the side of some devices to continue
   // working.
-  scoped_ptr<std::set<ui::DomCode>> excepted_keys(new std::set<ui::DomCode>);
-  excepted_keys->insert(ui::DomCode::VOLUME_DOWN);
-  excepted_keys->insert(ui::DomCode::VOLUME_UP);
-  excepted_keys->insert(ui::DomCode::POWER);
-  input_controller->DisableInternalKeyboardExceptKeys(excepted_keys.Pass());
+  std::vector<ui::DomCode> allowed_keys;
+  allowed_keys.push_back(ui::DomCode::VOLUME_DOWN);
+  allowed_keys.push_back(ui::DomCode::VOLUME_UP);
+  allowed_keys.push_back(ui::DomCode::POWER);
+  input_controller->SetInternalKeyboardFilter(true /* enable_filter */,
+                                              allowed_keys);
 }
 
 ScopedDisableInternalMouseAndKeyboardOzone::
     ~ScopedDisableInternalMouseAndKeyboardOzone() {
   ui::InputController* input_controller =
       ui::OzonePlatform::GetInstance()->GetInputController();
-  input_controller->EnableInternalTouchpad();
-  input_controller->EnableInternalKeyboard();
+  input_controller->SetInternalTouchpadEnabled(true);
+  input_controller->SetInternalKeyboardFilter(false /* enable_filter */,
+                                              std::vector<ui::DomCode>());
 }
 
 }  // namespace ash
diff --git a/base/android/java/src/org/chromium/base/TraceEvent.java b/base/android/java/src/org/chromium/base/TraceEvent.java
index 3d3b11a..9ace4a17 100644
--- a/base/android/java/src/org/chromium/base/TraceEvent.java
+++ b/base/android/java/src/org/chromium/base/TraceEvent.java
@@ -20,6 +20,7 @@
 public class TraceEvent {
 
     private static volatile boolean sEnabled = false;
+    private static volatile boolean sATraceEnabled = false; // True when taking an Android systrace.
 
     private static class BasicLooperMonitor implements Printer {
         @Override
@@ -176,6 +177,8 @@
     @CalledByNative
     public static void setEnabled(boolean enabled) {
         sEnabled = enabled;
+        // Android M+ systrace logs this on its own. Only log it if not writing to Android systrace.
+        if (sATraceEnabled) return;
         ThreadUtils.getUiThreadLooper().setMessageLogging(
                 enabled ? LooperMonitorHolder.sInstance : null);
     }
@@ -186,10 +189,15 @@
      * systrace, this is for WebView only.
      */
     public static void setATraceEnabled(boolean enabled) {
-        if (sEnabled == enabled) return;
+        if (sATraceEnabled == enabled) return;
+        sATraceEnabled = enabled;
         if (enabled) {
+            // Calls TraceEvent.setEnabled(true) via
+            // TraceLog::EnabledStateObserver::OnTraceLogEnabled
             nativeStartATrace();
         } else {
+            // Calls TraceEvent.setEnabled(false) via
+            // TraceLog::EnabledStateObserver::OnTraceLogDisabled
             nativeStopATrace();
         }
     }
diff --git a/base/base_nacl.gyp b/base/base_nacl.gyp
index 7e7d34f..47cf840 100644
--- a/base/base_nacl.gyp
+++ b/base/base_nacl.gyp
@@ -96,6 +96,10 @@
               # For GetKnownDeadTerminationStatus and GetTerminationStatus.
               'process/kill_posix.cc',
 
+              # For ForkWithFlags.
+              'process/launch.h',
+              'process/launch_posix.cc',
+
               # Unlike libbase_nacl, for Non-SFI build, we need to use
               # rand_util_posix for random implementation, instead of
               # rand_util_nacl.cc, which is based on IRT. rand_util_nacl.cc is
diff --git a/base/files/memory_mapped_file.cc b/base/files/memory_mapped_file.cc
index bad1792..227a41f 100644
--- a/base/files/memory_mapped_file.cc
+++ b/base/files/memory_mapped_file.cc
@@ -10,18 +10,7 @@
 
 namespace base {
 
-const MemoryMappedFile::Region MemoryMappedFile::Region::kWholeFile(
-    base::LINKER_INITIALIZED);
-
-MemoryMappedFile::Region::Region(base::LinkerInitialized) : offset(0), size(0) {
-}
-
-MemoryMappedFile::Region::Region() : offset(-1), size(-1) {
-}
-
-MemoryMappedFile::Region::Region(int64 offset, int64 size)
-    : offset(offset), size(size) {
-}
+const MemoryMappedFile::Region MemoryMappedFile::Region::kWholeFile = {0, 0};
 
 bool MemoryMappedFile::Region::operator==(
     const MemoryMappedFile::Region& other) const {
diff --git a/base/files/memory_mapped_file.h b/base/files/memory_mapped_file.h
index 96d1d91..9ff29b9 100644
--- a/base/files/memory_mapped_file.h
+++ b/base/files/memory_mapped_file.h
@@ -28,9 +28,6 @@
   struct BASE_EXPORT Region {
     static const Region kWholeFile;
 
-    Region();
-    Region(int64 offset, int64 size);
-
     bool operator==(const Region& other) const;
     bool operator!=(const Region& other) const;
 
@@ -39,10 +36,6 @@
 
     // Length of the region in bytes.
     int64 size;
-
-   private:
-    // Used by kWholeFile.
-    Region(base::LinkerInitialized);
   };
 
   // Opens an existing file and maps it into memory. Access is restricted to
diff --git a/base/files/memory_mapped_file_unittest.cc b/base/files/memory_mapped_file_unittest.cc
index d0833b5c..05b941c 100644
--- a/base/files/memory_mapped_file_unittest.cc
+++ b/base/files/memory_mapped_file_unittest.cc
@@ -107,7 +107,8 @@
   MemoryMappedFile map;
 
   File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
-  map.Initialize(file.Pass(), MemoryMappedFile::Region(0, kPartialSize));
+  MemoryMappedFile::Region region = {0, kPartialSize};
+  map.Initialize(file.Pass(), region);
   ASSERT_EQ(kPartialSize, map.length());
   ASSERT_TRUE(map.data() != NULL);
   EXPECT_TRUE(map.IsValid());
@@ -122,7 +123,8 @@
   MemoryMappedFile map;
 
   File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
-  map.Initialize(file.Pass(), MemoryMappedFile::Region(kOffset, kPartialSize));
+  MemoryMappedFile::Region region = {kOffset, kPartialSize};
+  map.Initialize(file.Pass(), region);
   ASSERT_EQ(kPartialSize, map.length());
   ASSERT_TRUE(map.data() != NULL);
   EXPECT_TRUE(map.IsValid());
@@ -138,7 +140,8 @@
   MemoryMappedFile map;
 
   File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
-  map.Initialize(file.Pass(), MemoryMappedFile::Region(kOffset, kPartialSize));
+  MemoryMappedFile::Region region = {kOffset, kPartialSize};
+  map.Initialize(file.Pass(), region);
   ASSERT_EQ(kPartialSize, map.length());
   ASSERT_TRUE(map.data() != NULL);
   EXPECT_TRUE(map.IsValid());
@@ -154,7 +157,8 @@
   MemoryMappedFile map;
 
   File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
-  map.Initialize(file.Pass(), MemoryMappedFile::Region(kOffset, kPartialSize));
+  MemoryMappedFile::Region region = {kOffset, kPartialSize};
+  map.Initialize(file.Pass(), region);
   ASSERT_EQ(kPartialSize, map.length());
   ASSERT_TRUE(map.data() != NULL);
   EXPECT_TRUE(map.IsValid());
diff --git a/base/i18n/icu_util.cc b/base/i18n/icu_util.cc
index a9f0b1296..1dd54cd6 100644
--- a/base/i18n/icu_util.cc
+++ b/base/i18n/icu_util.cc
@@ -23,6 +23,10 @@
 #include "third_party/icu/source/i18n/unicode/timezone.h"
 #endif
 
+#if defined(OS_ANDROID)
+#include "base/android/apk_assets.h"
+#endif
+
 #if defined(OS_MACOSX)
 #include "base/mac/foundation_util.h"
 #endif
@@ -34,11 +38,6 @@
 namespace base {
 namespace i18n {
 
-// Use an unversioned file name to simplify a icu version update down the road.
-// No need to change the filename in multiple places (gyp files, windows
-// build pkg configurations, etc). 'l' stands for Little Endian.
-// This variable is exported through the header file.
-const char kIcuDataFileName[] = "icudtl.dat";
 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED
 #define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat"
 #if defined(OS_WIN)
@@ -47,44 +46,140 @@
 #endif
 
 namespace {
-
+#if !defined(OS_NACL)
 #if !defined(NDEBUG)
 // 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;
+bool g_called_once = false;
+#endif  // !defined(NDEBUG)
+
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+// Use an unversioned file name to simplify a icu version update down the road.
+// No need to change the filename in multiple places (gyp files, windows
+// build pkg configurations, etc). 'l' stands for Little Endian.
+// This variable is exported through the header file.
+const char kIcuDataFileName[] = "icudtl.dat";
+#if defined(OS_ANDROID)
+const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat";
 #endif
+
+// File handle intentionally never closed. Not using File here because its
+// Windows implementation guards against two instances owning the same
+// PlatformFile (which we allow since we know it is never freed).
+const PlatformFile kInvalidPlatformFile =
+#if defined(OS_WIN)
+    INVALID_HANDLE_VALUE;
+#else
+    -1;
+#endif
+PlatformFile g_icudtl_pf = kInvalidPlatformFile;
+MemoryMappedFile* g_icudtl_mapped_file = nullptr;
+MemoryMappedFile::Region g_icudtl_region;
+
+void LazyInitIcuDataFile() {
+  if (g_icudtl_pf != kInvalidPlatformFile) {
+    return;
+  }
+#if defined(OS_ANDROID)
+  int fd = base::android::OpenApkAsset(kAndroidAssetsIcuDataFileName,
+                                       &g_icudtl_region);
+  g_icudtl_pf = fd;
+  if (fd != -1) {
+    return;
+  }
+// For unit tests, data file is located on disk, so try there as a fallback.
+#endif  // defined(OS_ANDROID)
+#if !defined(OS_MACOSX)
+  FilePath data_path;
+#if defined(OS_WIN)
+  // The data file will be in the same directory as the current module.
+  bool path_ok = PathService::Get(DIR_MODULE, &data_path);
+  wchar_t tmp_buffer[_MAX_PATH] = {0};
+  wcscpy_s(tmp_buffer, data_path.value().c_str());
+  debug::Alias(tmp_buffer);
+  CHECK(path_ok);  // TODO(scottmg): http://crbug.com/445616
+#elif defined(OS_ANDROID)
+  bool path_ok = PathService::Get(DIR_ANDROID_APP_DATA, &data_path);
+#else
+  // For now, expect the data file to be alongside the executable.
+  // This is sufficient while we work on unit tests, but will eventually
+  // likely live in a data directory.
+  bool path_ok = PathService::Get(DIR_EXE, &data_path);
+#endif
+  DCHECK(path_ok);
+  data_path = data_path.AppendASCII(kIcuDataFileName);
+
+#if defined(OS_WIN)
+  // TODO(scottmg): http://crbug.com/445616
+  wchar_t tmp_buffer2[_MAX_PATH] = {0};
+  wcscpy_s(tmp_buffer2, data_path.value().c_str());
+  debug::Alias(tmp_buffer2);
+#endif
+
+#else
+  // Assume it is in the framework bundle's Resources directory.
+  ScopedCFTypeRef<CFStringRef> data_file_name(
+      SysUTF8ToCFStringRef(kIcuDataFileName));
+  FilePath data_path = mac::PathForFrameworkBundleResource(data_file_name);
+  if (data_path.empty()) {
+    LOG(ERROR) << kIcuDataFileName << " not found in bundle";
+    return;
+  }
+#endif  // !defined(OS_MACOSX)
+  File file(data_path, File::FLAG_OPEN | File::FLAG_READ);
+  if (file.IsValid()) {
+    g_icudtl_pf = file.TakePlatformFile();
+    g_icudtl_region = MemoryMappedFile::Region::kWholeFile;
+  }
 }
 
+bool InitializeICUWithFileDescriptorInternal(
+    PlatformFile data_fd,
+    const MemoryMappedFile::Region& data_region) {
+  // This can be called multiple times in tests.
+  if (g_icudtl_mapped_file) {
+    return true;
+  }
+  if (data_fd == kInvalidPlatformFile) {
+    return false;
+  }
+
+  scoped_ptr<MemoryMappedFile> icudtl_mapped_file(new MemoryMappedFile());
+  if (!icudtl_mapped_file->Initialize(File(data_fd), data_region)) {
+    LOG(ERROR) << "Couldn't mmap icu data file";
+    return false;
+  }
+  g_icudtl_mapped_file = icudtl_mapped_file.release();
+
+  UErrorCode err = U_ZERO_ERROR;
+  udata_setCommonData(const_cast<uint8*>(g_icudtl_mapped_file->data()), &err);
+  return err == U_ZERO_ERROR;
+}
+#endif  // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#endif  // !defined(OS_NACL)
+
+}  // namespace
+
 #if !defined(OS_NACL)
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
 bool InitializeICUWithFileDescriptor(
     PlatformFile data_fd,
-    MemoryMappedFile::Region data_region) {
+    const MemoryMappedFile::Region& data_region) {
 #if !defined(NDEBUG)
   DCHECK(!g_check_called_once || !g_called_once);
   g_called_once = true;
 #endif
-
-#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
-  // The ICU data is statically linked.
-  return true;
-#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
-  CR_DEFINE_STATIC_LOCAL(MemoryMappedFile, mapped_file, ());
-  if (!mapped_file.IsValid()) {
-    if (!mapped_file.Initialize(File(data_fd), data_region)) {
-      LOG(ERROR) << "Couldn't mmap icu data file";
-      return false;
-    }
-  }
-  UErrorCode err = U_ZERO_ERROR;
-  udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err);
-  return err == U_ZERO_ERROR;
-#endif // ICU_UTIL_DATA_FILE
+  return InitializeICUWithFileDescriptorInternal(data_fd, data_region);
 }
 
+PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) {
+  CHECK_NE(g_icudtl_pf, kInvalidPlatformFile);
+  *out_region = g_icudtl_region;
+  return g_icudtl_pf;
+}
+#endif  // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
 
 bool InitializeICU() {
 #if !defined(NDEBUG)
@@ -123,60 +218,9 @@
   // it is needed.  This can fail if the process is sandboxed at that time.
   // Instead, we map the file in and hand off the data so the sandbox won't
   // cause any problems.
-
-  // Chrome doesn't normally shut down ICU, so the mapped data shouldn't ever
-  // be released.
-  CR_DEFINE_STATIC_LOCAL(MemoryMappedFile, mapped_file, ());
-  if (!mapped_file.IsValid()) {
-#if !defined(OS_MACOSX)
-    FilePath data_path;
-#if defined(OS_WIN)
-    // The data file will be in the same directory as the current module.
-    bool path_ok = PathService::Get(DIR_MODULE, &data_path);
-    wchar_t tmp_buffer[_MAX_PATH] = {0};
-    wcscpy_s(tmp_buffer, data_path.value().c_str());
-    debug::Alias(tmp_buffer);
-    CHECK(path_ok);  // TODO(scottmg): http://crbug.com/445616
-#elif defined(OS_ANDROID)
-    bool path_ok = PathService::Get(DIR_ANDROID_APP_DATA, &data_path);
-#else
-    // For now, expect the data file to be alongside the executable.
-    // This is sufficient while we work on unit tests, but will eventually
-    // likely live in a data directory.
-    bool path_ok = PathService::Get(DIR_EXE, &data_path);
-#endif
-    DCHECK(path_ok);
-    data_path = data_path.AppendASCII(kIcuDataFileName);
-
-#if defined(OS_WIN)
-    // TODO(scottmg): http://crbug.com/445616
-    wchar_t tmp_buffer2[_MAX_PATH] = {0};
-    wcscpy_s(tmp_buffer2, data_path.value().c_str());
-    debug::Alias(tmp_buffer2);
-#endif
-
-#else
-    // Assume it is in the framework bundle's Resources directory.
-    ScopedCFTypeRef<CFStringRef> data_file_name(
-        SysUTF8ToCFStringRef(kIcuDataFileName));
-    FilePath data_path =
-      mac::PathForFrameworkBundleResource(data_file_name);
-    if (data_path.empty()) {
-      LOG(ERROR) << kIcuDataFileName << " not found in bundle";
-      return false;
-    }
-#endif  // OS check
-    if (!mapped_file.Initialize(data_path)) {
-#if defined(OS_WIN)
-      CHECK(false);  // TODO(scottmg): http://crbug.com/445616
-#endif
-      LOG(ERROR) << "Couldn't mmap " << data_path.AsUTF8Unsafe();
-      return false;
-    }
-  }
-  UErrorCode err = U_ZERO_ERROR;
-  udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err);
-  result = (err == U_ZERO_ERROR);
+  LazyInitIcuDataFile();
+  result =
+      InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region);
 #if defined(OS_WIN)
   CHECK(result);  // TODO(scottmg): http://crbug.com/445616
 #endif
@@ -193,10 +237,10 @@
 #endif
   return result;
 }
-#endif
+#endif  // !defined(OS_NACL)
 
 void AllowMultipleInitializeCallsForTesting() {
-#if !defined(NDEBUG)
+#if !defined(NDEBUG) && !defined(OS_NACL)
   g_check_called_once = false;
 #endif
 }
diff --git a/base/i18n/icu_util.h b/base/i18n/icu_util.h
index 65de0ad..d62f8bac 100644
--- a/base/i18n/icu_util.h
+++ b/base/i18n/icu_util.h
@@ -12,19 +12,24 @@
 namespace base {
 namespace i18n {
 
-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();
 
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+// Returns the PlatformFile and Region that was initialized by InitializeICU().
+// Use with InitializeICUWithFileDescriptor().
+BASE_I18N_EXPORT PlatformFile GetIcuDataFileHandle(
+    MemoryMappedFile::Region* out_region);
+
 // Android and html_viewer use a file descriptor passed by browser process to
 // initialize ICU in render processes.
 BASE_I18N_EXPORT bool InitializeICUWithFileDescriptor(
     PlatformFile data_fd,
-    MemoryMappedFile::Region data_region);
-#endif
+    const MemoryMappedFile::Region& data_region);
+#endif  // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#endif  // !defined(OS_NACL)
 
 // In a test binary, the call above might occur twice.
 BASE_I18N_EXPORT void AllowMultipleInitializeCallsForTesting();
diff --git a/base/memory/shared_memory.h b/base/memory/shared_memory.h
index 008bb01..68db65c 100644
--- a/base/memory/shared_memory.h
+++ b/base/memory/shared_memory.h
@@ -257,27 +257,11 @@
     return ShareToProcessCommon(process, new_handle, true, SHARE_CURRENT_MODE);
   }
 
-  // DEPRECATED (crbug.com/345734):
-  // Locks the shared memory.
-  //
-  // WARNING: on POSIX the memory locking primitive only works across
-  // processes, not across threads.  The LockDeprecated method is not currently
-  // used in inner loops, so we protect against multiple threads in a
-  // critical section using a class global lock.
-  void LockDeprecated();
-
-  // DEPRECATED (crbug.com/345734):
-  // Releases the shared memory lock.
-  void UnlockDeprecated();
-
  private:
-#if defined(OS_POSIX) && !defined(OS_NACL)
-#if !defined(OS_ANDROID)
+#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID)
   bool PrepareMapFile(ScopedFILE fp, ScopedFD readonly);
   bool FilePathForMemoryName(const std::string& mem_name, FilePath* path);
-#endif
-  void LockOrUnlockCommon(int function);
-#endif  // defined(OS_POSIX) && !defined(OS_NACL)
+#endif  // defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID)
   enum ShareMode {
     SHARE_READONLY,
     SHARE_CURRENT_MODE,
@@ -298,32 +282,9 @@
   void*              memory_;
   bool               read_only_;
   size_t             requested_size_;
-#if !defined(OS_POSIX)
-  HANDLE             lock_;
-#endif
 
   DISALLOW_COPY_AND_ASSIGN(SharedMemory);
 };
-
-// DEPRECATED (crbug.com/345734):
-// A helper class that acquires the shared memory lock while
-// the SharedMemoryAutoLockDeprecated is in scope.
-class SharedMemoryAutoLockDeprecated {
- public:
-  explicit SharedMemoryAutoLockDeprecated(SharedMemory* shared_memory)
-      : shared_memory_(shared_memory) {
-    shared_memory_->LockDeprecated();
-  }
-
-  ~SharedMemoryAutoLockDeprecated() {
-    shared_memory_->UnlockDeprecated();
-  }
-
- private:
-  SharedMemory* shared_memory_;
-  DISALLOW_COPY_AND_ASSIGN(SharedMemoryAutoLockDeprecated);
-};
-
 }  // namespace base
 
 #endif  // BASE_MEMORY_SHARED_MEMORY_H_
diff --git a/base/memory/shared_memory_nacl.cc b/base/memory/shared_memory_nacl.cc
index 26dd4a3..4a1fa11 100644
--- a/base/memory/shared_memory_nacl.cc
+++ b/base/memory/shared_memory_nacl.cc
@@ -139,14 +139,6 @@
   }
 }
 
-void SharedMemory::LockDeprecated() {
-  NOTIMPLEMENTED();
-}
-
-void SharedMemory::UnlockDeprecated() {
-  NOTIMPLEMENTED();
-}
-
 bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
                                         SharedMemoryHandle *new_handle,
                                         bool close_self,
diff --git a/base/memory/shared_memory_posix.cc b/base/memory/shared_memory_posix.cc
index 35d746e..8aaa9ce 100644
--- a/base/memory/shared_memory_posix.cc
+++ b/base/memory/shared_memory_posix.cc
@@ -4,16 +4,13 @@
 
 #include "base/memory/shared_memory.h"
 
-#include <errno.h>
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
-#include <sys/types.h>
 #include <unistd.h>
 
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/posix/safe_strerror.h"
@@ -21,9 +18,6 @@
 #include "base/profiler/scoped_tracker.h"
 #include "base/scoped_generic.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread_restrictions.h"
 
 #if defined(OS_MACOSX)
 #include "base/mac/foundation_util.h"
@@ -38,8 +32,6 @@
 
 namespace {
 
-LazyInstance<Lock>::Leaky g_thread_lock_ = LAZY_INSTANCE_INITIALIZER;
-
 struct ScopedPathUnlinkerTraits {
   static FilePath* InvalidValue() { return nullptr; }
 
@@ -414,16 +406,6 @@
   }
 }
 
-void SharedMemory::LockDeprecated() {
-  g_thread_lock_.Get().Acquire();
-  LockOrUnlockCommon(F_LOCK);
-}
-
-void SharedMemory::UnlockDeprecated() {
-  LockOrUnlockCommon(F_ULOCK);
-  g_thread_lock_.Get().Release();
-}
-
 #if !defined(OS_ANDROID)
 bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) {
   DCHECK_EQ(-1, mapped_file_);
@@ -491,25 +473,6 @@
 }
 #endif  // !defined(OS_ANDROID)
 
-void SharedMemory::LockOrUnlockCommon(int function) {
-  DCHECK_GE(mapped_file_, 0);
-  while (lockf(mapped_file_, function, 0) < 0) {
-    if (errno == EINTR) {
-      continue;
-    } else if (errno == ENOLCK) {
-      // temporary kernel resource exaustion
-      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(500));
-      continue;
-    } else {
-      NOTREACHED() << "lockf() failed."
-                   << " function:" << function
-                   << " fd:" << mapped_file_
-                   << " errno:" << errno
-                   << " msg:" << base::safe_strerror(errno);
-    }
-  }
-}
-
 bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
                                         SharedMemoryHandle* new_handle,
                                         bool close_self,
diff --git a/base/memory/shared_memory_unittest.cc b/base/memory/shared_memory_unittest.cc
index 6fe5706..7ed8955 100644
--- a/base/memory/shared_memory_unittest.cc
+++ b/base/memory/shared_memory_unittest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/atomicops.h"
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/shared_memory.h"
@@ -33,7 +34,7 @@
 #endif
 
 static const int kNumThreads = 5;
-#if !defined(OS_IOS)  // iOS does not allow multiple processes.
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
 static const int kNumTasks = 5;
 #endif
 
@@ -90,56 +91,6 @@
 const char* const MultipleThreadMain::s_test_name_ =
     "SharedMemoryOpenThreadTest";
 
-// TODO(port):
-// This test requires the ability to pass file descriptors between processes.
-// We haven't done that yet in Chrome for POSIX.
-#if defined(OS_WIN)
-// Each thread will open the shared memory.  Each thread will take the memory,
-// and keep changing it while trying to lock it, with some small pauses in
-// between. Verify that each thread's value in the shared memory is always
-// correct.
-class MultipleLockThread : public PlatformThread::Delegate {
- public:
-  explicit MultipleLockThread(int id) : id_(id) {}
-  ~MultipleLockThread() override {}
-
-  // PlatformThread::Delegate interface.
-  void ThreadMain() override {
-    const uint32 kDataSize = sizeof(int);
-    SharedMemoryHandle handle = NULL;
-    {
-      SharedMemory memory1;
-      EXPECT_TRUE(memory1.CreateNamedDeprecated(
-          "SharedMemoryMultipleLockThreadTest", true, kDataSize));
-      EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle));
-      // TODO(paulg): Implement this once we have a posix version of
-      // SharedMemory::ShareToProcess.
-      EXPECT_TRUE(true);
-    }
-
-    SharedMemory memory2(handle, false);
-    EXPECT_TRUE(memory2.Map(kDataSize));
-    volatile int* const ptr = static_cast<int*>(memory2.memory());
-
-    for (int idx = 0; idx < 20; idx++) {
-      memory2.LockDeprecated();
-      int i = (id_ << 16) + idx;
-      *ptr = i;
-      PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
-      EXPECT_EQ(*ptr, i);
-      memory2.UnlockDeprecated();
-    }
-
-    memory2.Close();
-  }
-
- private:
-  int id_;
-
-  DISALLOW_COPY_AND_ASSIGN(MultipleLockThread);
-};
-#endif
-
 }  // namespace
 
 // Android doesn't support SharedMemory::Open/Delete/
@@ -320,34 +271,6 @@
   MultipleThreadMain::CleanUp();
 }
 
-// TODO(port): this test requires the MultipleLockThread class
-// (defined above), which requires the ability to pass file
-// descriptors between processes.  We haven't done that yet in Chrome
-// for POSIX.
-#if defined(OS_WIN)
-// Create a set of threads to each open a shared memory segment and write to it
-// with the lock held. Verify that they are always reading/writing consistent
-// data.
-TEST(SharedMemoryTest, Lock) {
-  PlatformThreadHandle thread_handles[kNumThreads];
-  MultipleLockThread* thread_delegates[kNumThreads];
-
-  // Spawn the threads.
-  for (int index = 0; index < kNumThreads; ++index) {
-    PlatformThreadHandle pth;
-    thread_delegates[index] = new MultipleLockThread(index);
-    EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth));
-    thread_handles[index] = pth;
-  }
-
-  // Wait for the threads to finish.
-  for (int index = 0; index < kNumThreads; ++index) {
-    PlatformThread::Join(thread_handles[index]);
-    delete thread_delegates[index];
-  }
-}
-#endif
-
 // Allocate private (unique) shared memory with an empty string for a
 // name.  Make sure several of them don't point to the same thing as
 // we might expect if the names are equal.
@@ -647,7 +570,9 @@
   shared_memory.Close();
 }
 
-#if !defined(OS_IOS)  // iOS does not allow multiple processes.
+// iOS does not allow multiple processes.
+// Android ashmem doesn't support named shared memory.
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
 
 // On POSIX it is especially important we test shmem across processes,
 // not just across threads.  But the test is enabled on all platforms.
@@ -664,53 +589,61 @@
 #if defined(OS_MACOSX)
     mac::ScopedNSAutoreleasePool pool;
 #endif
-    const uint32 kDataSize = 1024;
     SharedMemory memory;
-    bool rv = memory.CreateNamedDeprecated(s_test_name_, true, kDataSize);
+    bool rv = memory.CreateNamedDeprecated(s_test_name_, true, s_data_size_);
     EXPECT_TRUE(rv);
     if (rv != true)
       errors++;
-    rv = memory.Map(kDataSize);
+    rv = memory.Map(s_data_size_);
     EXPECT_TRUE(rv);
     if (rv != true)
       errors++;
     int *ptr = static_cast<int*>(memory.memory());
 
-    for (int idx = 0; idx < 20; idx++) {
-      memory.LockDeprecated();
-      int i = (1 << 16) + idx;
-      *ptr = i;
-      PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
-      if (*ptr != i)
-        errors++;
-      memory.UnlockDeprecated();
-    }
-
+    // This runs concurrently in multiple processes. Writes need to be atomic.
+    base::subtle::Barrier_AtomicIncrement(ptr, 1);
     memory.Close();
     return errors;
   }
 
- private:
   static const char* const s_test_name_;
+  static const uint32 s_data_size_;
 };
 
 const char* const SharedMemoryProcessTest::s_test_name_ = "MPMem";
+const uint32 SharedMemoryProcessTest::s_data_size_ = 1024;
 
-TEST_F(SharedMemoryProcessTest, Tasks) {
+TEST_F(SharedMemoryProcessTest, SharedMemoryAcrossProcesses) {
   SharedMemoryProcessTest::CleanUp();
 
+  // Create a shared memory region. Set the first word to 0.
+  SharedMemory memory;
+  bool rv = memory.CreateNamedDeprecated(s_test_name_, true, s_data_size_);
+  ASSERT_TRUE(rv);
+  rv = memory.Map(s_data_size_);
+  ASSERT_TRUE(rv);
+  int* ptr = static_cast<int*>(memory.memory());
+  *ptr = 0;
+
+  // Start |kNumTasks| processes, each of which atomically increments the first
+  // word by 1.
   Process processes[kNumTasks];
   for (int index = 0; index < kNumTasks; ++index) {
     processes[index] = SpawnChild("SharedMemoryTestMain");
     ASSERT_TRUE(processes[index].IsValid());
   }
 
+  // Check that each process exited correctly.
   int exit_code = 0;
   for (int index = 0; index < kNumTasks; ++index) {
     EXPECT_TRUE(processes[index].WaitForExit(&exit_code));
     EXPECT_EQ(0, exit_code);
   }
 
+  // Check that the shared memory region reflects |kNumTasks| increments.
+  ASSERT_EQ(kNumTasks, *ptr);
+
+  memory.Close();
   SharedMemoryProcessTest::CleanUp();
 }
 
@@ -718,6 +651,6 @@
   return SharedMemoryProcessTest::TaskTestMain();
 }
 
-#endif  // !OS_IOS
+#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
 
 }  // namespace base
diff --git a/base/memory/shared_memory_win.cc b/base/memory/shared_memory_win.cc
index eacf0d6..b1f85da 100644
--- a/base/memory/shared_memory_win.cc
+++ b/base/memory/shared_memory_win.cc
@@ -32,8 +32,7 @@
       memory_(NULL),
       read_only_(false),
       mapped_size_(0),
-      requested_size_(0),
-      lock_(NULL) {
+      requested_size_(0) {
 }
 
 SharedMemory::SharedMemory(const std::wstring& name)
@@ -42,7 +41,6 @@
       read_only_(false),
       requested_size_(0),
       mapped_size_(0),
-      lock_(NULL),
       name_(name) {
 }
 
@@ -51,18 +49,17 @@
       memory_(NULL),
       read_only_(read_only),
       requested_size_(0),
-      mapped_size_(0),
-      lock_(NULL) {
+      mapped_size_(0) {
 }
 
-SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
+SharedMemory::SharedMemory(SharedMemoryHandle handle,
+                           bool read_only,
                            ProcessHandle process)
     : mapped_file_(NULL),
       memory_(NULL),
       read_only_(read_only),
       requested_size_(0),
-      mapped_size_(0),
-      lock_(NULL) {
+      mapped_size_(0) {
   ::DuplicateHandle(process, handle,
                     GetCurrentProcess(), &mapped_file_,
                     read_only_ ? FILE_MAP_READ : FILE_MAP_READ |
@@ -73,8 +70,6 @@
 SharedMemory::~SharedMemory() {
   Unmap();
   Close();
-  if (lock_ != NULL)
-    CloseHandle(lock_);
 }
 
 // static
@@ -270,26 +265,6 @@
   }
 }
 
-void SharedMemory::LockDeprecated() {
-  if (lock_ == NULL) {
-    std::wstring name = name_;
-    name.append(L"lock");
-    lock_ = CreateMutex(NULL, FALSE, name.c_str());
-    if (lock_ == NULL) {
-      DPLOG(ERROR) << "Could not create mutex.";
-      NOTREACHED();
-      return;  // There is nothing good we can do here.
-    }
-  }
-  DWORD result = WaitForSingleObject(lock_, INFINITE);
-  DCHECK_EQ(result, WAIT_OBJECT_0);
-}
-
-void SharedMemory::UnlockDeprecated() {
-  DCHECK(lock_ != NULL);
-  ReleaseMutex(lock_);
-}
-
 SharedMemoryHandle SharedMemory::handle() const {
   return mapped_file_;
 }
diff --git a/base/message_loop/incoming_task_queue.cc b/base/message_loop/incoming_task_queue.cc
index 642222e..5e9a461 100644
--- a/base/message_loop/incoming_task_queue.cc
+++ b/base/message_loop/incoming_task_queue.cc
@@ -119,11 +119,6 @@
     ScheduleWork();
 }
 
-TimeTicks IncomingTaskQueue::GetNewlyAddedTaskDelay() {
-  return !incoming_queue_.empty() ? incoming_queue_.front().delayed_run_time :
-      TimeTicks();
-}
-
 IncomingTaskQueue::~IncomingTaskQueue() {
   // Verify that WillDestroyCurrentMessageLoop() has been called.
   DCHECK(!message_loop_);
diff --git a/base/message_loop/incoming_task_queue.h b/base/message_loop/incoming_task_queue.h
index 544f3e9..7dd1e82 100644
--- a/base/message_loop/incoming_task_queue.h
+++ b/base/message_loop/incoming_task_queue.h
@@ -57,9 +57,6 @@
   // scheduling work.
   void StartScheduling();
 
-  // Returns the delay for the most recently added task.
-  TimeTicks GetNewlyAddedTaskDelay();
-
  private:
   friend class RefCountedThreadSafe<IncomingTaskQueue>;
   virtual ~IncomingTaskQueue();
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index b3c895c..4222c774 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -640,10 +640,6 @@
   return false;
 }
 
-TimeTicks MessageLoop::GetNewlyAddedTaskDelay() {
-  return incoming_task_queue_->GetNewlyAddedTaskDelay();
-}
-
 void MessageLoop::DeleteSoonInternal(const tracked_objects::Location& from_here,
                                      void(*deleter)(const void*),
                                      const void* object) {
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h
index 2d67fe51..f2f89d0 100644
--- a/base/message_loop/message_loop.h
+++ b/base/message_loop/message_loop.h
@@ -475,7 +475,6 @@
   bool DoWork() override;
   bool DoDelayedWork(TimeTicks* next_delayed_work_time) override;
   bool DoIdleWork() override;
-  TimeTicks GetNewlyAddedTaskDelay() override;
 
   const Type type_;
 
diff --git a/base/message_loop/message_pump.h b/base/message_loop/message_pump.h
index 77a49ce..a2edb45 100644
--- a/base/message_loop/message_pump.h
+++ b/base/message_loop/message_pump.h
@@ -42,9 +42,6 @@
     // Returns true to indicate that idle work was done. Returning false means
     // the pump will now wait.
     virtual bool DoIdleWork() = 0;
-
-    // Returns the delay for the newly added task.
-    virtual TimeTicks GetNewlyAddedTaskDelay() = 0;
   };
 
   MessagePump();
diff --git a/base/message_loop/message_pump_win.cc b/base/message_loop/message_pump_win.cc
index a84e04e..cdbf0c26 100644
--- a/base/message_loop/message_pump_win.cc
+++ b/base/message_loop/message_pump_win.cc
@@ -12,7 +12,6 @@
 #include "base/process/memory.h"
 #include "base/profiler/scoped_tracker.h"
 #include "base/strings/stringprintf.h"
-#include "base/threading/thread.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/wrapped_window_proc.h"
 
@@ -35,10 +34,6 @@
 // task (a series of such messages creates a continuous task pump).
 static const int kMsgHaveWork = WM_USER + 1;
 
-// The default delay for the waitable timer used to wake up the UI worker
-// thread.
-static const int64 kDefaultUIWorkerThreadWakeupTimerMs = 3;
-
 //-----------------------------------------------------------------------------
 // MessagePumpWin public:
 
@@ -95,39 +90,35 @@
 MessagePumpForUI::MessagePumpForUI()
     : atom_(0) {
   InitMessageWnd();
-
-  ui_worker_thread_timer_.Set(::CreateWaitableTimer(NULL, FALSE, NULL));
-  ui_worker_thread_.reset(new base::Thread("UI Pump Worker thread"));
-  ui_worker_thread_->Start();
-  ui_worker_thread_->WaitUntilThreadStarted();
-  ui_worker_thread_->task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&MessagePumpForUI::DoWorkerThreadRunLoop,
-                 base::Unretained(this)));
 }
 
 MessagePumpForUI::~MessagePumpForUI() {
   DestroyWindow(message_hwnd_);
   UnregisterClass(MAKEINTATOM(atom_),
                   GetModuleFromAddress(&WndProcThunk));
-
-  ::QueueUserAPC(
-        reinterpret_cast<PAPCFUNC>(&MessagePumpForUI::ShutdownWorkerThread),
-        ui_worker_thread_->thread_handle().platform_handle(), NULL);
-  ui_worker_thread_->Stop();
 }
 
 void MessagePumpForUI::ScheduleWork() {
-  // If we have a regular posted task at the head of queue then we need to
-  // process it quickly.
-  if (state_ && state_->delegate->GetNewlyAddedTaskDelay().is_null()) {
-    // Make sure the MessagePump does some work for us.
-    PostWorkMessage();
-    return;
-  }
-  // Set a one shot timer to fire after 3 milliseconds. The actual resolution
-  // of the timer is dependent on timeBeginPeriod being called.
-  SetWakeupTimer(kDefaultUIWorkerThreadWakeupTimerMs);
+  if (InterlockedExchange(&have_work_, 1))
+    return;  // Someone else continued the pumping.
+
+  // Make sure the MessagePump does some work for us.
+  BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork,
+                         reinterpret_cast<WPARAM>(this), 0);
+  if (ret)
+    return;  // There was room in the Window Message queue.
+
+  // We have failed to insert a have-work message, so there is a chance that we
+  // will starve tasks/timers while sitting in a nested message loop.  Nested
+  // loops only look at Windows Message queues, and don't look at *our* task
+  // queues, etc., so we might not get a time slice in such. :-(
+  // We could abort here, but the fear is that this failure mode is plausibly
+  // common (queue is full, of about 2000 messages), so we'll do a near-graceful
+  // recovery.  Nested loops are pretty transient (we think), so this will
+  // probably be recoverable.
+  InterlockedExchange(&have_work_, 0);  // Clarify that we didn't really insert.
+  UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR,
+                            MESSAGE_LOOP_PROBLEM_MAX);
 }
 
 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
@@ -418,65 +409,45 @@
 }
 
 bool MessagePumpForUI::ProcessPumpReplacementMessage() {
+  // When we encounter a kMsgHaveWork message, this method is called to peek
+  // and process a replacement message, such as a WM_PAINT or WM_TIMER.  The
+  // goal is to make the kMsgHaveWork as non-intrusive as possible, even though
+  // a continuous stream of such messages are posted.  This method carefully
+  // peeks a message while there is no chance for a kMsgHaveWork to be pending,
+  // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
+  // possibly be posted), and finally dispatches that peeked replacement.  Note
+  // that the re-post of kMsgHaveWork may be asynchronous to this thread!!
+
+  bool have_message = false;
+  MSG msg;
+  // We should not process all window messages if we are in the context of an
+  // OS modal loop, i.e. in the context of a windows API call like MessageBox.
+  // This is to ensure that these messages are peeked out by the OS modal loop.
+  if (MessageLoop::current()->os_modal_loop()) {
+    // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above.
+    have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
+                   PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
+  } else {
+    have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE;
+  }
+
+  DCHECK(!have_message || kMsgHaveWork != msg.message ||
+         msg.hwnd != message_hwnd_);
+
   // Since we discarded a kMsgHaveWork message, we must update the flag.
-  InterlockedExchange(&have_work_, 0);
-  return true;
-}
+  int old_have_work = InterlockedExchange(&have_work_, 0);
+  DCHECK(old_have_work);
 
-void MessagePumpForUI::DoWorkerThreadRunLoop() {
-  DCHECK(ui_worker_thread_timer_.Get());
-  while (TRUE) {
-    DWORD ret = WaitForSingleObjectEx(
-        ui_worker_thread_timer_.Get(), INFINITE, TRUE);
-    // The only APC this thread could receive is the Shutdown APC.
-    if (ret == WAIT_IO_COMPLETION)
-      return;
+  // We don't need a special time slice if we didn't have_message to process.
+  if (!have_message)
+    return false;
 
-    // Make sure the MessagePump does some work for us.
-    PostWorkMessage();
-
-    // Set a one shot timer to process pending delayed tasks if any in the
-    // queue. The actual resolution of the timer is dependent on the
-    // timeBeginPeriod API being called.
-    SetWakeupTimer(kDefaultUIWorkerThreadWakeupTimerMs);
-  }
-}
-
-// static
-void CALLBACK MessagePumpForUI::ShutdownWorkerThread(ULONG_PTR param) {
-  // This function is empty because we only use the fact that an APC was posted
-  // to the worker thread to shut it down.
-  return;
-}
-
-void MessagePumpForUI::PostWorkMessage() {
-  BOOL posted = PostMessage(message_hwnd_, kMsgHaveWork,
-                            reinterpret_cast<WPARAM>(this),
-                            0);
-  if (!posted) {
-    // We have failed to insert a have-work message, so there is a chance
-    // that we will starve tasks/timers while sitting in a nested message
-    // loop. Nested loops only look at Windows Message queues, and don't
-    // look at *our* task queues, etc., so we might not get a time slice in
-    // such. :-(
-    // We could abort here, but the fear is that this failure mode is
-    // plausibly common (queue is full, of about 2000 messages), so we'll
-    // do a near-graceful recovery. Nested loops are pretty transient
-    // (we think), so this will probably be recoverable.
-    UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem",
-                              MESSAGE_POST_ERROR,
-                              MESSAGE_LOOP_PROBLEM_MAX);
-  }
-}
-
-void MessagePumpForUI::SetWakeupTimer(int64 delay_ms) {
-  // Set the timer for the delay passed in. The actual resolution of the
-  // timer is dependent on whether timeBeginPeriod was called.
-  LARGE_INTEGER due_time = {0};
-  due_time.QuadPart = -delay_ms * 10000;
-  BOOL timer_set = ::SetWaitableTimer(ui_worker_thread_timer_.Get(),
-      &due_time, 0, NULL, NULL, FALSE);
-  CHECK(timer_set);
+  // Guarantee we'll get another time slice in the case where we go into native
+  // windows code.   This ScheduleWork() may hurt performance a tiny bit when
+  // tasks appear very infrequently, but when the event queue is busy, the
+  // kMsgHaveWork events get (percentage wise) rarer and rarer.
+  ScheduleWork();
+  return ProcessMessageHelper(msg);
 }
 
 //-----------------------------------------------------------------------------
diff --git a/base/message_loop/message_pump_win.h b/base/message_loop/message_pump_win.h
index 042efdf..00a1e77 100644
--- a/base/message_loop/message_pump_win.h
+++ b/base/message_loop/message_pump_win.h
@@ -11,7 +11,6 @@
 
 #include "base/base_export.h"
 #include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_pump.h"
 #include "base/message_loop/message_pump_dispatcher.h"
 #include "base/observer_list.h"
@@ -20,8 +19,6 @@
 
 namespace base {
 
-class Thread;
-
 // MessagePumpWin serves as the base for specialized versions of the MessagePump
 // for Windows. It provides basic functionality like handling of observers and
 // controlling the lifetime of the message pump.
@@ -138,39 +135,11 @@
   bool ProcessMessageHelper(const MSG& msg);
   bool ProcessPumpReplacementMessage();
 
-  // We spawn a worker thread to periodically post (3 ms) the kMsgHaveWork
-  // message to the UI message pump. This is to ensure that the main thread
-  // gets to process tasks and delayed tasks when there is no activity in the
-  // Windows message pump or when there is a nested modal loop (sizing/moving/
-  // drag drop/message boxes) etc.
-  void DoWorkerThreadRunLoop();
-
-  // This function is posted as part of a user mode APC to shutdown the worker
-  // thread when the main message pump is shutting down.
-  static void CALLBACK ShutdownWorkerThread(ULONG_PTR param);
-
-  // Helper function for posting the kMsgHaveWork message to wake up the pump
-  // for processing tasks.
-  void PostWorkMessage();
-
-  // Helper function to set the waitable timer used to wake up the UI worker
-  // thread for processing delayed tasks.
-  // |delay_ms| : The delay in milliseconds.
-  void SetWakeupTimer(int64 delay_ms);
-
   // Atom representing the registered window class.
   ATOM atom_;
 
   // A hidden message-only window.
   HWND message_hwnd_;
-
-  // This thread is used to periodically wake up the main thread to process
-  // tasks.
-  scoped_ptr<base::Thread> ui_worker_thread_;
-
-  // The UI worker thread waits on this timer indefinitely. When the main
-  // thread has tasks ready to be processed it sets the timer.
-  base::win::ScopedHandle ui_worker_thread_timer_;
 };
 
 //-----------------------------------------------------------------------------
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index 0a14f223..7dfb5528 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -131,6 +131,23 @@
                     flags);
 }
 
+HistogramBase* Histogram::FactoryGet(const char* name,
+                                     Sample minimum,
+                                     Sample maximum,
+                                     size_t bucket_count,
+                                     int32 flags) {
+  return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags);
+}
+
+HistogramBase* Histogram::FactoryTimeGet(const char* name,
+                                         TimeDelta minimum,
+                                         TimeDelta maximum,
+                                         size_t bucket_count,
+                                         int32 flags) {
+  return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count,
+                        flags);
+}
+
 // Calculate what range of values are held in each bucket.
 // We have to be careful that we don't pick a ratio between starting points in
 // consecutive buckets that is sooo small, that the integer bounds are the same
@@ -532,6 +549,23 @@
                     flags);
 }
 
+HistogramBase* LinearHistogram::FactoryGet(const char* name,
+                                           Sample minimum,
+                                           Sample maximum,
+                                           size_t bucket_count,
+                                           int32 flags) {
+  return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags);
+}
+
+HistogramBase* LinearHistogram::FactoryTimeGet(const char* name,
+                                               TimeDelta minimum,
+                                               TimeDelta maximum,
+                                               size_t bucket_count,
+                                               int32 flags) {
+  return FactoryTimeGet(std::string(name),  minimum, maximum, bucket_count,
+                        flags);
+}
+
 HistogramBase* LinearHistogram::FactoryGetWithRangeDescription(
       const std::string& name,
       Sample minimum,
@@ -677,6 +711,10 @@
   return histogram;
 }
 
+HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32 flags) {
+  return FactoryGet(std::string(name), flags);
+}
+
 HistogramType BooleanHistogram::GetHistogramType() const {
   return BOOLEAN_HISTOGRAM;
 }
@@ -737,6 +775,13 @@
   return histogram;
 }
 
+HistogramBase* CustomHistogram::FactoryGet(
+    const char* name,
+    const std::vector<Sample>& custom_ranges,
+    int32 flags) {
+  return FactoryGet(std::string(name), custom_ranges, flags);
+}
+
 HistogramType CustomHistogram::GetHistogramType() const {
   return CUSTOM_HISTOGRAM;
 }
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h
index c13f05e1..58bc0297 100644
--- a/base/metrics/histogram.h
+++ b/base/metrics/histogram.h
@@ -121,6 +121,20 @@
                                        size_t bucket_count,
                                        int32 flags);
 
+  // Overloads of the above two functions that take a const char* |name| param,
+  // to avoid code bloat from the std::string constructor being inlined into
+  // call sites.
+  static HistogramBase* FactoryGet(const char* name,
+                                   Sample minimum,
+                                   Sample maximum,
+                                   size_t bucket_count,
+                                   int32 flags);
+  static HistogramBase* FactoryTimeGet(const char* name,
+                                       base::TimeDelta minimum,
+                                       base::TimeDelta maximum,
+                                       size_t bucket_count,
+                                       int32 flags);
+
   static void InitializeBucketRanges(Sample minimum,
                                      Sample maximum,
                                      BucketRanges* ranges);
@@ -277,6 +291,20 @@
                                        size_t bucket_count,
                                        int32 flags);
 
+  // Overloads of the above two functions that take a const char* |name| param,
+  // to avoid code bloat from the std::string constructor being inlined into
+  // call sites.
+  static HistogramBase* FactoryGet(const char* name,
+                                   Sample minimum,
+                                   Sample maximum,
+                                   size_t bucket_count,
+                                   int32 flags);
+  static HistogramBase* FactoryTimeGet(const char* name,
+                                       TimeDelta minimum,
+                                       TimeDelta maximum,
+                                       size_t bucket_count,
+                                       int32 flags);
+
   struct DescriptionPair {
     Sample sample;
     const char* description;  // Null means end of a list of pairs.
@@ -339,6 +367,11 @@
  public:
   static HistogramBase* FactoryGet(const std::string& name, int32 flags);
 
+  // Overload of the above function that takes a const char* |name| param,
+  // to avoid code bloat from the std::string constructor being inlined into
+  // call sites.
+  static HistogramBase* FactoryGet(const char* name, int32 flags);
+
   HistogramType GetHistogramType() const override;
 
  private:
@@ -364,6 +397,13 @@
                                    const std::vector<Sample>& custom_ranges,
                                    int32 flags);
 
+  // Overload of the above function that takes a const char* |name| param,
+  // to avoid code bloat from the std::string constructor being inlined into
+  // call sites.
+  static HistogramBase* FactoryGet(const char* name,
+                                   const std::vector<Sample>& custom_ranges,
+                                   int32 flags);
+
   // Overridden from Histogram:
   HistogramType GetHistogramType() const override;
 
diff --git a/base/process/launch.h b/base/process/launch.h
index 56f27a8..0e42cd00 100644
--- a/base/process/launch.h
+++ b/base/process/launch.h
@@ -297,7 +297,7 @@
 // binary. This should not be called in production/released code.
 BASE_EXPORT LaunchOptions LaunchOptionsForTest();
 
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_NACL_NONSFI)
 // A wrapper for clone with fork-like behavior, meaning that it returns the
 // child's pid in the parent and 0 in the child. |flags|, |ptid|, and |ctid| are
 // as in the clone system call (the CLONE_VM flag is not supported).
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc
index 77edc12..99d8e3aa 100644
--- a/base/process/launch_posix.cc
+++ b/base/process/launch_posix.cc
@@ -65,6 +65,8 @@
 
 namespace base {
 
+#if !defined(OS_NACL_NONSFI)
+
 namespace {
 
 // Get the process's "environment" (i.e. the thing that setenv/getenv
@@ -188,55 +190,6 @@
 }
 #endif  // !defined(OS_LINUX) ||
         // (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
-
-#if defined(OS_LINUX)
-bool IsRunningOnValgrind() {
-  return RUNNING_ON_VALGRIND;
-}
-
-// This function runs on the stack specified on the clone call. It uses longjmp
-// to switch back to the original stack so the child can return from sys_clone.
-int CloneHelper(void* arg) {
-  jmp_buf* env_ptr = reinterpret_cast<jmp_buf*>(arg);
-  longjmp(*env_ptr, 1);
-
-  // Should not be reached.
-  RAW_CHECK(false);
-  return 1;
-}
-
-// This function is noinline to ensure that stack_buf is below the stack pointer
-// that is saved when setjmp is called below. This is needed because when
-// compiled with FORTIFY_SOURCE, glibc's longjmp checks that the stack is moved
-// upwards. See crbug.com/442912 for more details.
-#if defined(ADDRESS_SANITIZER)
-// Disable AddressSanitizer instrumentation for this function to make sure
-// |stack_buf| is allocated on thread stack instead of ASan's fake stack.
-// Under ASan longjmp() will attempt to clean up the area between the old and
-// new stack pointers and print a warning that may confuse the user.
-__attribute__((no_sanitize_address))
-#endif
-NOINLINE pid_t CloneAndLongjmpInChild(unsigned long flags,
-                                      pid_t* ptid,
-                                      pid_t* ctid,
-                                      jmp_buf* env) {
-  // We use the libc clone wrapper instead of making the syscall
-  // directly because making the syscall may fail to update the libc's
-  // internal pid cache. The libc interface unfortunately requires
-  // specifying a new stack, so we use setjmp/longjmp to emulate
-  // fork-like behavior.
-  char stack_buf[PTHREAD_STACK_MIN];
-#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
-    defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
-  // The stack grows downward.
-  void* stack = stack_buf + sizeof(stack_buf);
-#else
-#error "Unsupported architecture"
-#endif
-  return clone(&CloneHelper, stack, flags, env, ptid, nullptr, ctid);
-}
-#endif  // defined(OS_LINUX)
-
 }  // anonymous namespace
 
 // Functor for |ScopedDIR| (below).
@@ -741,7 +694,59 @@
   return result == EXECUTE_SUCCESS;
 }
 
-#if defined(OS_LINUX)
+#endif  // !defined(OS_NACL_NONSFI)
+
+#if defined(OS_LINUX) || defined(OS_NACL_NONSFI)
+namespace {
+
+bool IsRunningOnValgrind() {
+  return RUNNING_ON_VALGRIND;
+}
+
+// This function runs on the stack specified on the clone call. It uses longjmp
+// to switch back to the original stack so the child can return from sys_clone.
+int CloneHelper(void* arg) {
+  jmp_buf* env_ptr = reinterpret_cast<jmp_buf*>(arg);
+  longjmp(*env_ptr, 1);
+
+  // Should not be reached.
+  RAW_CHECK(false);
+  return 1;
+}
+
+// This function is noinline to ensure that stack_buf is below the stack pointer
+// that is saved when setjmp is called below. This is needed because when
+// compiled with FORTIFY_SOURCE, glibc's longjmp checks that the stack is moved
+// upwards. See crbug.com/442912 for more details.
+#if defined(ADDRESS_SANITIZER)
+// Disable AddressSanitizer instrumentation for this function to make sure
+// |stack_buf| is allocated on thread stack instead of ASan's fake stack.
+// Under ASan longjmp() will attempt to clean up the area between the old and
+// new stack pointers and print a warning that may confuse the user.
+__attribute__((no_sanitize_address))
+#endif
+NOINLINE pid_t CloneAndLongjmpInChild(unsigned long flags,
+                                      pid_t* ptid,
+                                      pid_t* ctid,
+                                      jmp_buf* env) {
+  // We use the libc clone wrapper instead of making the syscall
+  // directly because making the syscall may fail to update the libc's
+  // internal pid cache. The libc interface unfortunately requires
+  // specifying a new stack, so we use setjmp/longjmp to emulate
+  // fork-like behavior.
+  char stack_buf[PTHREAD_STACK_MIN];
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+    defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
+  // The stack grows downward.
+  void* stack = stack_buf + sizeof(stack_buf);
+#else
+#error "Unsupported architecture"
+#endif
+  return clone(&CloneHelper, stack, flags, env, ptid, nullptr, ctid);
+}
+
+}  // anonymous namespace
+
 pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid) {
   const bool clone_tls_used = flags & CLONE_SETTLS;
   const bool invalid_ctid =
@@ -780,6 +785,6 @@
 
   return 0;
 }
-#endif  // defined(OS_LINUX)
+#endif  // defined(OS_LINUX) || defined(OS_NACL_NONSFI)
 
 }  // namespace base
diff --git a/base/process/process_util_unittest.cc b/base/process/process_util_unittest.cc
index 1f7f1b2..be1a0004 100644
--- a/base/process/process_util_unittest.cc
+++ b/base/process/process_util_unittest.cc
@@ -1025,6 +1025,7 @@
   return kSuccess;
 }
 
+#if defined(CLONE_NEWUSER) && defined(CLONE_NEWPID)
 TEST_F(ProcessUtilTest, CloneFlags) {
   if (RunningOnValgrind() ||
       !base::PathExists(FilePath("/proc/self/ns/user")) ||
@@ -1043,6 +1044,7 @@
   EXPECT_TRUE(process.WaitForExit(&exit_code));
   EXPECT_EQ(kSuccess, exit_code);
 }
+#endif
 
 TEST(ForkWithFlagsTest, UpdatesPidCache) {
   // The libc clone function, which allows ForkWithFlags to keep the pid cache
diff --git a/base/process/process_win.cc b/base/process/process_win.cc
index 2ad72c7..30cd9dc7 100644
--- a/base/process/process_win.cc
+++ b/base/process/process_win.cc
@@ -9,6 +9,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/process/kill.h"
+#include "base/strings/string_util.h"
 #include "base/win/windows_version.h"
 
 namespace {
@@ -189,8 +190,10 @@
     DWORD background_priority = IDLE_PRIORITY_CLASS;
     base::FieldTrial* trial =
         base::FieldTrialList::Find("BackgroundRendererProcesses");
-    if (trial && trial->group_name() == "AllowBelowNormalFromBrowser")
+    if (trial && StartsWith(trial->group_name(), "AllowBelowNormalFromBrowser",
+                            CompareCase::SENSITIVE)) {
       background_priority = BELOW_NORMAL_PRIORITY_CLASS;
+    }
 
     priority = value ? background_priority : NORMAL_PRIORITY_CLASS;
   }
diff --git a/base/test/test_support_ios.mm b/base/test/test_support_ios.mm
index 3b31da6..9e82612 100644
--- a/base/test/test_support_ios.mm
+++ b/base/test/test_support_ios.mm
@@ -80,6 +80,10 @@
   label.textAlignment = NSTextAlignmentCenter;
   [window_ addSubview:label];
 
+  // An NSInternalInconsistencyException is thrown if the app doesn't have a
+  // root view controller. Set an empty one here.
+  [window_ setRootViewController:[[[UIViewController alloc] init] autorelease]];
+
   if ([self shouldRedirectOutputToFile])
     [self redirectOutput];
 
diff --git a/base/threading/thread.cc b/base/threading/thread.cc
index 63b07cb..6ca1cae 100644
--- a/base/threading/thread.cc
+++ b/base/threading/thread.cc
@@ -190,13 +190,6 @@
   return running_;
 }
 
-void Thread::SetPriority(ThreadPriority priority) {
-  // The thread must be started (and id known) for this to be
-  // compatible with all platforms.
-  DCHECK(message_loop_ != nullptr);
-  PlatformThread::SetThreadPriority(thread_, priority);
-}
-
 void Thread::Run(MessageLoop* message_loop) {
   message_loop->Run();
 }
diff --git a/base/threading/thread.h b/base/threading/thread.h
index 6b70058..1b8b59db 100644
--- a/base/threading/thread.h
+++ b/base/threading/thread.h
@@ -188,9 +188,6 @@
   // Returns true if the thread has been started, and not yet stopped.
   bool IsRunning() const;
 
-  // Sets the thread priority. The thread must already be started.
-  void SetPriority(ThreadPriority priority);
-
  protected:
   // Called just prior to starting the message loop
   virtual void Init() {}
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index 54f50eb..fc3824e 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -58,6 +58,7 @@
 }
 }
 namespace net {
+class NetworkChangeNotifierMac;
 namespace internal {
 class AddressTrackerLinux;
 }
@@ -206,6 +207,7 @@
   friend class disk_cache::BackendImpl;           // http://crbug.com/74623
   friend class disk_cache::InFlightIO;            // http://crbug.com/74623
   friend class net::internal::AddressTrackerLinux;  // http://crbug.com/125097
+  friend class net::NetworkChangeNotifierMac;     // http://crbug.com/502005
   friend class ::BrowserProcessImpl;              // http://crbug.com/125207
   friend class ::NativeBackendKWallet;            // http://crbug.com/125331
   // END USAGE THAT NEEDS TO BE FIXED.
diff --git a/base/trace_event/memory_allocator_dump_guid.h b/base/trace_event/memory_allocator_dump_guid.h
index 84c12ef0..634ca813 100644
--- a/base/trace_event/memory_allocator_dump_guid.h
+++ b/base/trace_event/memory_allocator_dump_guid.h
@@ -23,6 +23,8 @@
   // global scope of all the traced processes.
   explicit MemoryAllocatorDumpGuid(const std::string& guid_str);
 
+  uint64 ToUint64() const { return guid_; }
+
   // Returns a (hex-encoded) string representation of the guid.
   std::string ToString() const;
 
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 82fe10a..61e5d199 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -153,6 +153,9 @@
 const char* const MemoryDumpManager::kTraceCategoryForTesting = kTraceCategory;
 
 // static
+const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3;
+
+// static
 MemoryDumpManager* MemoryDumpManager::GetInstance() {
   if (g_instance_for_testing)
     return g_instance_for_testing;
@@ -349,11 +352,19 @@
                                                  ProcessMemoryDump* pmd) {
   lock_.AssertAcquired();
   bool dump_successful = mdp->OnMemoryDump(pmd);
-  if (!dump_successful) {
-    LOG(ERROR) << "The memory dumper failed, possibly due to sandboxing "
-                  "(crbug.com/461788), disabling it for current process. Try "
-                  "restarting chrome with the --no-sandbox switch.";
-    dump_providers_.find(mdp)->second.disabled = true;
+  MemoryDumpProviderInfo* mdp_info = &dump_providers_.find(mdp)->second;
+  if (dump_successful) {
+    mdp_info->consecutive_failures = 0;
+  } else {
+    // Disable the MDP if it fails kMaxConsecutiveFailuresCount times
+    // consecutively.
+    mdp_info->consecutive_failures++;
+    if (mdp_info->consecutive_failures >= kMaxConsecutiveFailuresCount) {
+      mdp_info->disabled = true;
+      LOG(ERROR) << "The memory dumper failed, possibly due to sandboxing "
+                    "(crbug.com/461788), disabling it for current process. Try "
+                    "restarting chrome with the --no-sandbox switch.";
+    }
   }
   return dump_successful;
 }
@@ -409,6 +420,7 @@
   for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) {
     MemoryDumpProviderInfo& mdp_info = it->second;
     mdp_info.disabled = false;
+    mdp_info.consecutive_failures = 0;
     if (mdp_info.task_runner) {
       // The thread local event buffer must be initialized at this point as it
       // registers its own dump provider (for tracing overhead acounting).
@@ -439,7 +451,7 @@
 
 MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo(
     const scoped_refptr<SingleThreadTaskRunner>& task_runner)
-    : task_runner(task_runner), disabled(false) {
+    : task_runner(task_runner), consecutive_failures(0), disabled(false) {
 }
 MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() {
 }
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index 3645ac18..e34bdb08 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -90,6 +90,8 @@
     ~MemoryDumpProviderInfo();
 
     scoped_refptr<SingleThreadTaskRunner> task_runner;  // Optional.
+    int consecutive_failures;  // Number of times the provider failed (to
+                               // disable the MDPs).
     bool disabled;  // For fail-safe logic (auto-disable failing MDPs).
   };
 
@@ -97,6 +99,9 @@
   friend struct DefaultSingletonTraits<MemoryDumpManager>;
   friend class MemoryDumpManagerDelegate;
   friend class MemoryDumpManagerTest;
+  FRIEND_TEST_ALL_PREFIXES(MemoryDumpManagerTest, DisableFailingDumpers);
+
+  static const int kMaxConsecutiveFailuresCount;
 
   static void SetInstanceForTesting(MemoryDumpManager* instance);
 
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index c15748c..d90b7c6073 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -250,9 +250,8 @@
   DisableTracing();
 }
 
-// Enable both dump providers, make mdp1 fail and assert that only mdp2 is
-// invoked the 2nd time.
-// FIXME(primiano): remove once crbug.com/461788 gets fixed.
+// Enable both dump providers, make sure that mdp gets disabled after 3 failures
+// and not disabled after 1.
 TEST_F(MemoryDumpManagerTest, DisableFailingDumpers) {
   MockDumpProvider mdp1;
   MockDumpProvider mdp2;
@@ -261,13 +260,17 @@
   mdm_->RegisterDumpProvider(&mdp2);
   EnableTracing(kTraceCategory);
 
-  EXPECT_CALL(mdp1, OnMemoryDump(_)).Times(1).WillRepeatedly(Return(false));
-  EXPECT_CALL(mdp2, OnMemoryDump(_)).Times(1).WillRepeatedly(Return(true));
-  mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
-
-  EXPECT_CALL(mdp1, OnMemoryDump(_)).Times(0);
-  EXPECT_CALL(mdp2, OnMemoryDump(_)).Times(1).WillRepeatedly(Return(false));
-  mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
+  EXPECT_CALL(mdp1, OnMemoryDump(_))
+      .Times(MemoryDumpManager::kMaxConsecutiveFailuresCount)
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(mdp2, OnMemoryDump(_))
+      .Times(1 + MemoryDumpManager::kMaxConsecutiveFailuresCount)
+      .WillOnce(Return(false))
+      .WillRepeatedly(Return(true));
+  for (int i = 0; i < 1 + MemoryDumpManager::kMaxConsecutiveFailuresCount;
+       i++) {
+    mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
+  }
 
   DisableTracing();
 }
diff --git a/build/all.gyp b/build/all.gyp
index 6b8ad83..73eb7c66a 100644
--- a/build/all.gyp
+++ b/build/all.gyp
@@ -86,7 +86,6 @@
                 '../android_webview/android_webview.gyp:android_webview_apk',
                 '../android_webview/android_webview.gyp:system_webview_apk',
                 '../android_webview/android_webview_shell.gyp:android_webview_shell_apk',
-                '../android_webview/android_webview_telemetry_shell.gyp:android_webview_telemetry_shell_apk',
                 '../chrome/android/chrome_apk.gyp:chrome_public_apk',
                 '../chrome/chrome.gyp:chrome_shell_apk',
                 '../chrome/chrome.gyp:chrome_sync_shell_apk',
@@ -161,6 +160,7 @@
           'dependencies': [
             '../device/bluetooth/bluetooth.gyp:*',
             '../device/device_tests.gyp:*',
+            '../gpu/skia_runner/skia_runner.gyp:*',
           ],
         }],
         ['use_openssl==0 and (OS=="mac" or OS=="ios" or OS=="win")', {
diff --git a/build/android/adb_install_apk.py b/build/android/adb_install_apk.py
index 00bd38e..1e370f8 100755
--- a/build/android/adb_install_apk.py
+++ b/build/android/adb_install_apk.py
@@ -6,84 +6,86 @@
 
 """Utility script to install APKs from the command line quickly."""
 
-import optparse
+import argparse
+import logging
 import os
 import sys
 
 from pylib import constants
+from pylib.device import device_blacklist
 from pylib.device import device_errors
 from pylib.device import device_utils
+from pylib.utils import run_tests_helper
 
 
-def AddInstallAPKOption(option_parser):
-  """Adds apk option used to install the APK to the OptionParser."""
-  option_parser.add_option('--apk',
-                           help=('DEPRECATED The name of the apk containing the'
-                                 ' application (with the .apk extension).'))
-  option_parser.add_option('--apk_package',
-                           help=('DEPRECATED The package name used by the apk '
-                                 'containing the application.'))
-  option_parser.add_option('--keep_data',
-                           action='store_true',
-                           default=False,
-                           help=('Keep the package data when installing '
-                                 'the application.'))
-  option_parser.add_option('--debug', action='store_const', const='Debug',
-                           dest='build_type',
-                           default=os.environ.get('BUILDTYPE', 'Debug'),
-                           help='If set, run test suites under out/Debug. '
+def main():
+  parser = argparse.ArgumentParser()
+
+  apk_group = parser.add_mutually_exclusive_group(required=True)
+  apk_group.add_argument('--apk', dest='apk_name',
+                         help='DEPRECATED The name of the apk containing the'
+                              ' application (with the .apk extension).')
+  apk_group.add_argument('apk_path', nargs='?',
+                         help='The path to the APK to install.')
+
+  # TODO(jbudorick): Remove once no clients pass --apk_package
+  parser.add_argument('--apk_package', help='DEPRECATED unused')
+  parser.add_argument('--keep_data',
+                      action='store_true',
+                      default=False,
+                      help='Keep the package data when installing '
+                           'the application.')
+  parser.add_argument('--debug', action='store_const', const='Debug',
+                      dest='build_type',
+                      default=os.environ.get('BUILDTYPE', 'Debug'),
+                      help='If set, run test suites under out/Debug. '
                            'Default is env var BUILDTYPE or Debug')
-  option_parser.add_option('--release', action='store_const', const='Release',
-                           dest='build_type',
-                           help='If set, run test suites under out/Release. '
+  parser.add_argument('--release', action='store_const', const='Release',
+                      dest='build_type',
+                      help='If set, run test suites under out/Release. '
                            'Default is env var BUILDTYPE or Debug.')
-  option_parser.add_option('-d', '--device', dest='device',
-                           help='Target device for apk to install on.')
+  parser.add_argument('-d', '--device', dest='device',
+                      help='Target device for apk to install on.')
+  parser.add_argument('-v', '--verbose', action='count',
+                      help='Enable verbose logging.')
 
+  args = parser.parse_args()
 
-def ValidateInstallAPKOption(option_parser, options, args):
-  """Validates the apk option and potentially qualifies the path."""
-  if not options.apk:
-    if len(args) > 1:
-      options.apk = args[1]
-    else:
-      option_parser.error('apk target not specified.')
+  run_tests_helper.SetLogLevel(args.verbose)
+  constants.SetBuildType(args.build_type)
 
-  if not options.apk.endswith('.apk'):
-    options.apk += '.apk'
-
-  if not os.path.exists(options.apk):
-    options.apk = os.path.join(constants.GetOutDirectory(), 'apks',
-                               options.apk)
-
-
-def main(argv):
-  parser = optparse.OptionParser()
-  parser.set_usage("usage: %prog [options] target")
-  AddInstallAPKOption(parser)
-  options, args = parser.parse_args(argv)
-
-  if len(args) > 1 and options.apk:
-    parser.error("Appending the apk as argument can't be used with --apk.")
-  elif len(args) > 2:
-    parser.error("Too many arguments.")
-
-  constants.SetBuildType(options.build_type)
-  ValidateInstallAPKOption(parser, options, args)
+  apk = args.apk_path or args.apk_name
+  if not apk.endswith('.apk'):
+    apk += '.apk'
+  if not os.path.exists(apk):
+    apk = os.path.join(constants.GetOutDirectory(), 'apks', apk)
+    if not os.path.exists(apk):
+      parser.error('%s not found.' % apk)
 
   devices = device_utils.DeviceUtils.HealthyDevices()
 
-  if options.device:
-    devices = [d for d in devices if d == options.device]
+  if args.device:
+    devices = [d for d in devices if d == args.device]
     if not devices:
-      raise device_errors.DeviceUnreachableError(options.device)
+      raise device_errors.DeviceUnreachableError(args.device)
   elif not devices:
     raise device_errors.NoDevicesError()
 
-  device_utils.DeviceUtils.parallel(devices).Install(
-      options.apk, reinstall=options.keep_data)
+  def blacklisting_install(device):
+    try:
+      device.Install(apk, reinstall=args.keep_data)
+    except device_errors.CommandFailedError:
+      logging.exception('Failed to install %s', args.apk)
+      device_blacklist.ExtendBlacklist([str(device)])
+      logging.warning('Blacklisting %s', str(device))
+    except device_errors.CommandTimeoutError:
+      logging.exception('Timed out while installing %s', args.apk)
+      device_blacklist.ExtendBlacklist([str(device)])
+      logging.warning('Blacklisting %s', str(device))
+
+  device_utils.DeviceUtils.parallel(devices).pMap(blacklisting_install)
 
 
 if __name__ == '__main__':
-  sys.exit(main(sys.argv))
+  sys.exit(main())
 
diff --git a/build/android/gyp/generate_split_manifest.py b/build/android/gyp/generate_split_manifest.py
index 1a312ed..93b5502 100755
--- a/build/android/gyp/generate_split_manifest.py
+++ b/build/android/gyp/generate_split_manifest.py
@@ -90,7 +90,7 @@
   if options.depfile:
     build_utils.WriteDepfile(
         options.depfile,
-        [main_manifest] + build_utils.GetPythonDependencies())
+        [options.main_manifest] + build_utils.GetPythonDependencies())
 
 
 if __name__ == '__main__':
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 8507a95..3773e98 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -181,7 +181,8 @@
   possible_deps_config_paths = build_utils.ParseGypList(
       options.possible_deps_configs)
 
-  allow_unknown_deps = options.type == 'android_apk'
+  allow_unknown_deps = (options.type == 'android_apk' or
+                        options.type == 'android_resources')
   unknown_deps = [
       c for c in possible_deps_config_paths if not os.path.exists(c)]
   if unknown_deps and not allow_unknown_deps:
diff --git a/build/common.gypi b/build/common.gypi
index 04368e7..c7f68ec9 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -983,15 +983,6 @@
             'enable_mdns%' : 1,
         }],
 
-        # Turns on compiler optimizations in V8 in Debug build, except
-        # on android_clang, where we're hitting a weird linker error.
-        # TODO(dpranke): http://crbug.com/266155 .
-        ['OS=="android"', {
-          'v8_optimized_debug%': 1,
-        }, {
-          'v8_optimized_debug%': 2,
-        }],
-
         # Disable various features by default on embedded.
         ['embedded==1', {
           'remoting%': 0,
@@ -1016,6 +1007,14 @@
         }, {
           'pkg-config': 'pkg-config'
         }],
+
+        # Enable WebVR support by default on Android
+        # Still requires command line flag to access API
+        ['OS=="android"', {
+          'enable_webvr%': 1,
+        }, {
+          'enable_webvr%': 0,
+        }],
       ],
 
       # Setting this to '0' will cause V8's startup snapshot to be
@@ -1219,7 +1218,6 @@
     'enable_service_discovery%' : '<(enable_service_discovery)',
     'enable_wifi_bootstrapping%': '<(enable_wifi_bootstrapping)',
     'enable_hangout_services_extension%' : '<(enable_hangout_services_extension)',
-    'v8_optimized_debug%': '<(v8_optimized_debug)',
     'proprietary_codecs%': '<(proprietary_codecs)',
     'use_goma%': '<(use_goma)',
     'gomadir%': '<(gomadir)',
@@ -1232,6 +1230,10 @@
     'mac_views_browser%': '<(mac_views_browser)',
     'android_app_version_name%': '<(android_app_version_name)',
     'android_app_version_code%': '<(android_app_version_code)',
+    'enable_webvr%': '<(enable_webvr)',
+
+    # Turns on compiler optimizations in V8 in Debug build.
+    'v8_optimized_debug%': 1,
 
     # Use system protobuf instead of bundled one.
     'use_system_protobuf%': 0,
@@ -3030,6 +3032,9 @@
       ['v8_use_external_startup_data==1', {
        'defines': ['V8_USE_EXTERNAL_STARTUP_DATA'],
       }],
+      ['enable_webvr==1', {
+        'defines': ['ENABLE_WEBVR'],
+      }],
 
       # SAFE_BROWSING_SERVICE - browser manages a safe-browsing service.
       # SAFE_BROWSING_DB_LOCAL - service manages a local database.
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index d1e6ee7..9fc86fd 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -269,6 +269,9 @@
   if (enable_media_router) {
     defines += [ "ENABLE_MEDIA_ROUTER=1" ]
   }
+  if (enable_webvr) {
+    defines += [ "ENABLE_WEBVR" ]
+  }
 }
 
 # Debug/release ----------------------------------------------------------------
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index f9893e4..0933ceb 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -678,6 +678,12 @@
         outputs += [ "${_resource_packaged_apk_path}-${_density}" ]
       }
     }
+    if (defined(invoker.extensions_to_not_compress)) {
+      args += [
+        "--no-compress",
+        invoker.extensions_to_not_compress,
+      ]
+    }
   }
 
   package_target = "${target_name}__package"
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index d687112..56d1030 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1609,6 +1609,12 @@
     dex_path = final_dex_path
     load_library_from_apk = _load_library_from_apk
     create_density_splits = _create_density_splits
+    if (defined(invoker.extensions_to_not_compress)) {
+      extensions_to_not_compress = invoker.extensions_to_not_compress
+    } else {
+      # Allow icu data to be loaded directly from the .apk.
+      extensions_to_not_compress = "dat"
+    }
 
     version_code = _version_code
     version_name = _version_name
diff --git a/build/config/features.gni b/build/config/features.gni
index daf51771..8ef35f80 100644
--- a/build/config/features.gni
+++ b/build/config/features.gni
@@ -195,3 +195,7 @@
 
 # Whether to back up data before sync.
 enable_pre_sync_backup = is_win || is_mac || (is_linux && !is_chromeos)
+
+# Enable WebVR support by default on Android
+# Still requires command line flag to access API
+enable_webvr = is_android
diff --git a/build/gn_migration.gypi b/build/gn_migration.gypi
index 8108100..1106a9f 100644
--- a/build/gn_migration.gypi
+++ b/build/gn_migration.gypi
@@ -629,9 +629,9 @@
             '../chrome/tools/crash_service/caps/caps.gyp:caps',
             '../cloud_print/gcp20/prototype/gcp20_device.gyp:gcp20_device',
             '../cloud_print/gcp20/prototype/gcp20_device.gyp:gcp20_device_unittests',
-            '../cloud_print/service/service.gyp:cloud_print_service',
-            '../cloud_print/service/service.gyp:cloud_print_service_config',
-            '../cloud_print/service/service.gyp:cloud_print_service_setup',
+            '../cloud_print/service/win/service.gyp:cloud_print_service',
+            '../cloud_print/service/win/service.gyp:cloud_print_service_config',
+            '../cloud_print/service/win/service.gyp:cloud_print_service_setup',
             '../cloud_print/virtual_driver/win/install/virtual_driver_install.gyp:virtual_driver_setup',
             '../cloud_print/virtual_driver/win/virtual_driver.gyp:gcp_portmon',
             '../components/test_runner/test_runner.gyp:layout_test_helper',
diff --git a/build/java_apk.gypi b/build/java_apk.gypi
index 976c90e..e07ce5a 100644
--- a/build/java_apk.gypi
+++ b/build/java_apk.gypi
@@ -89,7 +89,8 @@
     'additional_res_packages': [],
     'additional_bundled_libs%': [],
     'is_test_apk%': 0,
-    'extensions_to_not_compress%': '',
+    # Allow icu data to be loaded directly from the .apk.
+    'extensions_to_not_compress%': 'dat',
     'resource_input_paths': [],
     'intermediate_dir': '<(PRODUCT_DIR)/<(_target_name)',
     'asset_location%': '<(intermediate_dir)/assets',
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index ace6075..4010db2 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -270,9 +270,6 @@
 "race:g_next_user_script_id\n"
 
 // http://crbug.com/389098
-"race:webrtc::RtpToNtpMs\n"
-"race:webrtc::UpdateRtcpList\n"
-"race:webrtc::RemoteNtpTimeEstimator::Estimate\n"
 "race:webrtc::voe::TransmitMixer::EnableStereoChannelSwapping\n"
 
 // http://crbug.com/397022
diff --git a/build/util/lastchange.py b/build/util/lastchange.py
index 1a7f519..3f3ee4a 100755
--- a/build/util/lastchange.py
+++ b/build/util/lastchange.py
@@ -121,7 +121,7 @@
   return VersionInfo('git', '%s-%s' % (hsh, pos))
 
 
-def FetchGitSVNURLAndRevision(directory, svn_url_regex):
+def FetchGitSVNURLAndRevision(directory, svn_url_regex, go_deeper):
   """
   Fetch the Subversion URL and revision through Git.
 
@@ -130,7 +130,10 @@
   Returns:
     A tuple containing the Subversion URL and revision.
   """
-  proc = RunGitCommand(directory, ['log', '-1', '--format=%b'])
+  git_args = ['log', '-1', '--format=%b']
+  if go_deeper:
+    git_args.append('--grep=git-svn-id')
+  proc = RunGitCommand(directory, git_args)
   if proc:
     output = proc.communicate()[0].strip()
     if proc.returncode == 0 and output:
@@ -149,20 +152,21 @@
   return None, None
 
 
-def FetchGitSVNRevision(directory, svn_url_regex):
+def FetchGitSVNRevision(directory, svn_url_regex, go_deeper):
   """
   Fetch the Git-SVN identifier for the local tree.
 
   Errors are swallowed.
   """
-  url, revision = FetchGitSVNURLAndRevision(directory, svn_url_regex)
+  url, revision = FetchGitSVNURLAndRevision(directory, svn_url_regex, go_deeper)
   if url and revision:
     return VersionInfo(url, revision)
   return None
 
 
 def FetchVersionInfo(default_lastchange, directory=None,
-                     directory_regex_prior_to_src_url='chrome|blink|svn'):
+                     directory_regex_prior_to_src_url='chrome|blink|svn',
+                     go_deeper=False):
   """
   Returns the last change (in the form of a branch, revision tuple),
   from some appropriate revision control system.
@@ -171,7 +175,7 @@
       r'.*/(' + directory_regex_prior_to_src_url + r')(/.*)')
 
   version_info = (FetchSVNRevision(directory, svn_url_regex) or
-                  FetchGitSVNRevision(directory, svn_url_regex) or
+                  FetchGitSVNRevision(directory, svn_url_regex, go_deeper) or
                   FetchGitRevision(directory))
   if not version_info:
     if default_lastchange and os.path.exists(default_lastchange):
@@ -256,6 +260,9 @@
                     "file-output-related options.")
   parser.add_option("-s", "--source-dir", metavar="DIR",
                     help="Use repository in the given directory.")
+  parser.add_option("--git-svn-go-deeper", action='store_true',
+                    help="In a Git-SVN repo, dig down to the last committed " +
+                    "SVN change (historic behaviour).")
   opts, args = parser.parse_args(argv[1:])
 
   out_file = opts.output
@@ -274,7 +281,9 @@
   else:
     src_dir = os.path.dirname(os.path.abspath(__file__))
 
-  version_info = FetchVersionInfo(opts.default_lastchange, src_dir)
+  version_info = FetchVersionInfo(opts.default_lastchange,
+                                  directory=src_dir,
+                                  go_deeper=opts.git_svn_go_deeper)
 
   if version_info.revision == None:
     version_info.revision = '0'
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 31eab76..ca2f3d2 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -82,6 +82,8 @@
     "debug/rendering_stats_instrumentation.cc",
     "debug/rendering_stats_instrumentation.h",
     "debug/ring_buffer.h",
+    "debug/traced_display_item_list.cc",
+    "debug/traced_display_item_list.h",
     "debug/traced_picture.cc",
     "debug/traced_picture.h",
     "debug/traced_value.cc",
diff --git a/cc/animation/animation.h b/cc/animation/animation.h
index 2677fde..153e4ef 100644
--- a/cc/animation/animation.h
+++ b/cc/animation/animation.h
@@ -151,6 +151,9 @@
 
   scoped_ptr<Animation> CloneAndInitialize(RunState initial_run_state) const;
 
+  void set_is_controlling_instance_for_test(bool is_controlling_instance) {
+    is_controlling_instance_ = is_controlling_instance;
+  }
   bool is_controlling_instance() const { return is_controlling_instance_; }
 
   void PushPropertiesTo(Animation* other) const;
diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc
index e6e720f..54b1a71 100644
--- a/cc/animation/layer_animation_controller.cc
+++ b/cc/animation/layer_animation_controller.cc
@@ -755,9 +755,14 @@
           !animations_[i]->needs_synchronized_start_time())
         animations_[i]->set_start_time(monotonic_time);
       if (events) {
+        base::TimeTicks start_time;
+        if (animations_[i]->has_set_start_time())
+          start_time = animations_[i]->start_time();
+        else
+          start_time = monotonic_time;
         AnimationEvent started_event(
             AnimationEvent::STARTED, id_, animations_[i]->group(),
-            animations_[i]->target_property(), monotonic_time);
+            animations_[i]->target_property(), start_time);
         started_event.is_impl_only = animations_[i]->is_impl_only();
         if (started_event.is_impl_only)
           NotifyAnimationStarted(started_event);
@@ -810,7 +815,9 @@
     // on the impl thread, we only mark a FINISHED main thread animation for
     // deletion once it has received a FINISHED event from the impl thread.
     bool animation_i_will_send_or_has_received_finish_event =
-        events || animations_[i]->received_finished_event();
+        animations_[i]->is_controlling_instance() ||
+        animations_[i]->is_impl_only() ||
+        animations_[i]->received_finished_event();
     // If an animation is finished, and not already marked for deletion,
     // find out if all other animations in the same group are also finished.
     if (animations_[i]->run_state() == Animation::FINISHED &&
@@ -822,7 +829,9 @@
       all_anims_with_same_id_are_finished = true;
       for (size_t j = 0; j < animations_.size(); ++j) {
         bool animation_j_will_send_or_has_received_finish_event =
-            events || animations_[j]->received_finished_event();
+            animations_[j]->is_controlling_instance() ||
+            animations_[j]->is_impl_only() ||
+            animations_[j]->received_finished_event();
         if (group_id == animations_[j]->group()) {
           if (!animations_[j]->is_finished() ||
               (animations_[j]->run_state() == Animation::FINISHED &&
diff --git a/cc/animation/layer_animation_controller_unittest.cc b/cc/animation/layer_animation_controller_unittest.cc
index c563776..e267eea 100644
--- a/cc/animation/layer_animation_controller_unittest.cc
+++ b/cc/animation/layer_animation_controller_unittest.cc
@@ -946,13 +946,13 @@
 class FakeAnimationDelegate : public AnimationDelegate {
  public:
   FakeAnimationDelegate()
-      : started_(false),
-        finished_(false) {}
+      : started_(false), finished_(false), start_time_(base::TimeTicks()) {}
 
   void NotifyAnimationStarted(TimeTicks monotonic_time,
                               Animation::TargetProperty target_property,
                               int group) override {
     started_ = true;
+    start_time_ = monotonic_time;
   }
 
   void NotifyAnimationFinished(TimeTicks monotonic_time,
@@ -965,9 +965,12 @@
 
   bool finished() { return finished_; }
 
+  TimeTicks start_time() { return start_time_; }
+
  private:
   bool started_;
   bool finished_;
+  TimeTicks start_time_;
 };
 
 // Tests that impl-only animations lead to start and finished notifications
@@ -1007,6 +1010,97 @@
   EXPECT_TRUE(delegate.finished());
 }
 
+// Tests that specified start times are sent to the main thread delegate
+TEST(LayerAnimationControllerTest,
+     SpecifiedStartTimesAreSentToMainThreadDelegate) {
+  FakeLayerAnimationValueObserver dummy_impl;
+  scoped_refptr<LayerAnimationController> controller_impl(
+      LayerAnimationController::Create(0));
+  controller_impl->AddValueObserver(&dummy_impl);
+  FakeLayerAnimationValueObserver dummy;
+  scoped_refptr<LayerAnimationController> controller(
+      LayerAnimationController::Create(0));
+  controller->AddValueObserver(&dummy);
+  FakeAnimationDelegate delegate;
+  controller->set_layer_animation_delegate(&delegate);
+
+  int animation_id =
+      AddOpacityTransitionToController(controller.get(), 1, 0, 1, false);
+
+  const TimeTicks start_time = TicksFromSecondsF(123);
+  controller->GetAnimation(Animation::OPACITY)->set_start_time(start_time);
+
+  controller->PushAnimationUpdatesTo(controller_impl.get());
+  controller_impl->ActivateAnimations();
+
+  EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
+            controller_impl->GetAnimationById(animation_id)->run_state());
+
+  AnimationEventsVector events;
+  controller_impl->Animate(kInitialTickTime);
+  controller_impl->UpdateState(true, &events);
+
+  // Synchronize the start times.
+  EXPECT_EQ(1u, events.size());
+  controller->NotifyAnimationStarted(events[0]);
+
+  // Validate start time on the main thread delegate.
+  EXPECT_EQ(start_time, delegate.start_time());
+}
+
+class FakeLayerAnimationEventObserver : public LayerAnimationEventObserver {
+ public:
+  FakeLayerAnimationEventObserver() : start_time_(base::TimeTicks()) {}
+
+  void OnAnimationStarted(const AnimationEvent& event) override {
+    start_time_ = event.monotonic_time;
+  }
+
+  TimeTicks start_time() { return start_time_; }
+
+ private:
+  TimeTicks start_time_;
+};
+
+// Tests that specified start times are sent to the event observers
+TEST(LayerAnimationControllerTest, SpecifiedStartTimesAreSentToEventObservers) {
+  FakeLayerAnimationValueObserver dummy_impl;
+  scoped_refptr<LayerAnimationController> controller_impl(
+      LayerAnimationController::Create(0));
+  controller_impl->AddValueObserver(&dummy_impl);
+  FakeLayerAnimationValueObserver dummy;
+  scoped_refptr<LayerAnimationController> controller(
+      LayerAnimationController::Create(0));
+  controller->AddValueObserver(&dummy);
+  FakeLayerAnimationEventObserver observer;
+  controller->AddEventObserver(&observer);
+
+  int animation_id =
+      AddOpacityTransitionToController(controller.get(), 1, 0, 1, false);
+
+  const TimeTicks start_time = TicksFromSecondsF(123);
+  controller->GetAnimation(Animation::OPACITY)->set_start_time(start_time);
+
+  controller->PushAnimationUpdatesTo(controller_impl.get());
+  controller_impl->ActivateAnimations();
+
+  EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
+            controller_impl->GetAnimationById(animation_id)->run_state());
+
+  AnimationEventsVector events;
+  controller_impl->Animate(kInitialTickTime);
+  controller_impl->UpdateState(true, &events);
+
+  // Synchronize the start times.
+  EXPECT_EQ(1u, events.size());
+  controller->NotifyAnimationStarted(events[0]);
+
+  // Validate start time on the event observer.
+  EXPECT_EQ(start_time, observer.start_time());
+}
+
 // Tests animations that are waiting for a synchronized start time do not
 // finish.
 TEST(LayerAnimationControllerTest,
@@ -1433,16 +1527,20 @@
       LayerAnimationController::Create(0));
   controller->AddValueObserver(&dummy);
 
-  controller->AddAnimation(CreateAnimation(
+  scoped_ptr<Animation> first_animation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(), 1,
       Animation::TRANSFORM));
+  first_animation->set_is_controlling_instance_for_test(true);
+  controller->AddAnimation(first_animation.Pass());
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
 
-  controller->AddAnimation(CreateAnimation(
+  scoped_ptr<Animation> second_animation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
       2, Animation::OPACITY));
+  second_animation->set_is_controlling_instance_for_test(true);
+  controller->AddAnimation(second_animation.Pass());
 
   // Animate but don't UpdateState.
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
@@ -1744,12 +1842,17 @@
   const int group_id = 1;
 
   // Add two animations with the same group id but different durations.
-  controller_impl->AddAnimation(Animation::Create(
+  scoped_ptr<Animation> first_animation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(2.0)).Pass(), 1,
       group_id, Animation::TRANSFORM));
-  controller_impl->AddAnimation(Animation::Create(
+  first_animation->set_is_controlling_instance_for_test(true);
+  controller_impl->AddAnimation(first_animation.Pass());
+
+  scoped_ptr<Animation> second_animation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
       2, group_id, Animation::OPACITY));
+  second_animation->set_is_controlling_instance_for_test(true);
+  controller_impl->AddAnimation(second_animation.Pass());
 
   controller_impl->Animate(kInitialTickTime);
   controller_impl->UpdateState(true, events.get());
@@ -1794,12 +1897,17 @@
   controller_impl->AddValueObserver(&dummy_impl);
 
   // Add two animations with the same group id.
-  controller_impl->AddAnimation(CreateAnimation(
+  scoped_ptr<Animation> first_animation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(1.0)).Pass(), 1,
       Animation::TRANSFORM));
-  controller_impl->AddAnimation(CreateAnimation(
+  first_animation->set_is_controlling_instance_for_test(true);
+  controller_impl->AddAnimation(first_animation.Pass());
+
+  scoped_ptr<Animation> second_animation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
       1, Animation::OPACITY));
+  second_animation->set_is_controlling_instance_for_test(true);
+  controller_impl->AddAnimation(second_animation.Pass());
 
   controller_impl->Animate(kInitialTickTime);
   controller_impl->UpdateState(true, events.get());
diff --git a/cc/animation/transform_operations.cc b/cc/animation/transform_operations.cc
index 970eb0d..fb1c17d5 100644
--- a/cc/animation/transform_operations.cc
+++ b/cc/animation/transform_operations.cc
@@ -163,16 +163,14 @@
 }
 
 bool TransformOperations::MatchesTypes(const TransformOperations& other) const {
-  if (IsIdentity() || other.IsIdentity())
+  if (operations_.size() == 0 || other.operations_.size() == 0)
     return true;
 
   if (operations_.size() != other.operations_.size())
     return false;
 
   for (size_t i = 0; i < operations_.size(); ++i) {
-    if (operations_[i].type != other.operations_[i].type
-      && !operations_[i].IsIdentity()
-      && !other.operations_[i].IsIdentity())
+    if (operations_[i].type != other.operations_[i].type)
       return false;
   }
 
diff --git a/cc/animation/transform_operations_unittest.cc b/cc/animation/transform_operations_unittest.cc
index 7aae696..c5fb3c6 100644
--- a/cc/animation/transform_operations_unittest.cc
+++ b/cc/animation/transform_operations_unittest.cc
@@ -139,14 +139,34 @@
   operations->push_back(to_add);
 }
 
-TEST(TransformOperationTest, IdentityAlwaysMatches) {
+TEST(TransformOperationTest, MatchTypesOrder) {
+  TransformOperations mix_order_identity;
+  mix_order_identity.AppendTranslate(0, 0, 0);
+  mix_order_identity.AppendScale(1, 1, 1);
+  mix_order_identity.AppendTranslate(0, 0, 0);
+
+  TransformOperations mix_order_one;
+  mix_order_one.AppendTranslate(0, 1, 0);
+  mix_order_one.AppendScale(2, 1, 3);
+  mix_order_one.AppendTranslate(1, 0, 0);
+
+  TransformOperations mix_order_two;
+  mix_order_two.AppendTranslate(0, 1, 0);
+  mix_order_two.AppendTranslate(1, 0, 0);
+  mix_order_two.AppendScale(2, 1, 3);
+
+  EXPECT_TRUE(mix_order_identity.MatchesTypes(mix_order_one));
+  EXPECT_FALSE(mix_order_identity.MatchesTypes(mix_order_two));
+  EXPECT_FALSE(mix_order_one.MatchesTypes(mix_order_two));
+}
+
+TEST(TransformOperationTest, NoneAlwaysMatches) {
   ScopedVector<TransformOperations> operations;
   GetIdentityOperations(&operations);
 
-  for (size_t i = 0; i < operations.size(); ++i) {
-    for (size_t j = 0; j < operations.size(); ++j)
-      EXPECT_TRUE(operations[i]->MatchesTypes(*operations[j]));
-  }
+  TransformOperations none_operation;
+  for (size_t i = 0; i < operations.size(); ++i)
+    EXPECT_TRUE(operations[i]->MatchesTypes(none_operation));
 }
 
 TEST(TransformOperationTest, ApplyTranslate) {
@@ -427,12 +447,12 @@
 
   for (size_t i = 0; i < identity_operations.size(); ++i) {
     TransformOperations operations;
-    operations.AppendRotate(0, 0, 1, 360);
+    operations.AppendRotate(0, 0, 1, 90);
 
     SkMScalar progress = 0.5f;
 
     gfx::Transform expected;
-    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180);
+    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 45);
 
     EXPECT_TRANSFORMATION_MATRIX_EQ(
         expected, operations.Blend(*identity_operations[i], progress));
@@ -440,7 +460,7 @@
     progress = -0.5f;
 
     expected.MakeIdentity();
-    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), -180);
+    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), -45);
 
     EXPECT_TRANSFORMATION_MATRIX_EQ(
         expected, operations.Blend(*identity_operations[i], progress));
@@ -448,7 +468,7 @@
     progress = 1.5f;
 
     expected.MakeIdentity();
-    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 540);
+    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 135);
 
     EXPECT_TRANSFORMATION_MATRIX_EQ(
         expected, operations.Blend(*identity_operations[i], progress));
@@ -523,41 +543,38 @@
   }
 }
 
-TEST(TransformOperationTest, BlendSkewFromIdentity) {
-  ScopedVector<TransformOperations> identity_operations;
-  GetIdentityOperations(&identity_operations);
+TEST(TransformOperationTest, BlendSkewFromEmpty) {
+  TransformOperations empty_operation;
 
-  for (size_t i = 0; i < identity_operations.size(); ++i) {
-    TransformOperations operations;
-    operations.AppendSkew(2, 2);
+  TransformOperations operations;
+  operations.AppendSkew(2, 2);
 
-    SkMScalar progress = 0.5f;
+  SkMScalar progress = 0.5f;
 
-    gfx::Transform expected;
-    expected.SkewX(1);
-    expected.SkewY(1);
+  gfx::Transform expected;
+  expected.SkewX(1);
+  expected.SkewY(1);
 
-    EXPECT_TRANSFORMATION_MATRIX_EQ(
-        expected, operations.Blend(*identity_operations[i], progress));
+  EXPECT_TRANSFORMATION_MATRIX_EQ(expected,
+                                  operations.Blend(empty_operation, progress));
 
-    progress = -0.5f;
+  progress = -0.5f;
 
-    expected.MakeIdentity();
-    expected.SkewX(-1);
-    expected.SkewY(-1);
+  expected.MakeIdentity();
+  expected.SkewX(-1);
+  expected.SkewY(-1);
 
-    EXPECT_TRANSFORMATION_MATRIX_EQ(
-        expected, operations.Blend(*identity_operations[i], progress));
+  EXPECT_TRANSFORMATION_MATRIX_EQ(expected,
+                                  operations.Blend(empty_operation, progress));
 
-    progress = 1.5f;
+  progress = 1.5f;
 
-    expected.MakeIdentity();
-    expected.SkewX(3);
-    expected.SkewY(3);
+  expected.MakeIdentity();
+  expected.SkewX(3);
+  expected.SkewY(3);
 
-    EXPECT_TRANSFORMATION_MATRIX_EQ(
-        expected, operations.Blend(*identity_operations[i], progress));
-  }
+  EXPECT_TRANSFORMATION_MATRIX_EQ(expected,
+                                  operations.Blend(empty_operation, progress));
 }
 
 TEST(TransformOperationTest, BlendPerspectiveFromIdentity) {
@@ -584,12 +601,12 @@
 
   for (size_t i = 0; i < identity_operations.size(); ++i) {
     TransformOperations operations;
-    operations.AppendRotate(0, 0, 1, 360);
+    operations.AppendRotate(0, 0, 1, 90);
 
     SkMScalar progress = 0.5f;
 
     gfx::Transform expected;
-    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180);
+    expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 45);
 
     EXPECT_TRANSFORMATION_MATRIX_EQ(
         expected, identity_operations[i]->Blend(operations, progress));
@@ -632,23 +649,20 @@
   }
 }
 
-TEST(TransformOperationTest, BlendSkewToIdentity) {
-  ScopedVector<TransformOperations> identity_operations;
-  GetIdentityOperations(&identity_operations);
+TEST(TransformOperationTest, BlendSkewToEmpty) {
+  TransformOperations empty_operation;
 
-  for (size_t i = 0; i < identity_operations.size(); ++i) {
-    TransformOperations operations;
-    operations.AppendSkew(2, 2);
+  TransformOperations operations;
+  operations.AppendSkew(2, 2);
 
-    SkMScalar progress = 0.5f;
+  SkMScalar progress = 0.5f;
 
-    gfx::Transform expected;
-    expected.SkewX(1);
-    expected.SkewY(1);
+  gfx::Transform expected;
+  expected.SkewX(1);
+  expected.SkewY(1);
 
-    EXPECT_TRANSFORMATION_MATRIX_EQ(
-        expected, identity_operations[i]->Blend(operations, progress));
-  }
+  EXPECT_TRANSFORMATION_MATRIX_EQ(expected,
+                                  empty_operation.Blend(operations, progress));
 }
 
 TEST(TransformOperationTest, BlendPerspectiveToIdentity) {
diff --git a/cc/cc.gyp b/cc/cc.gyp
index ca76e63..7d33d3f 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -138,6 +138,8 @@
         'debug/rendering_stats_instrumentation.cc',
         'debug/rendering_stats_instrumentation.h',
         'debug/ring_buffer.h',
+        'debug/traced_display_item_list.cc',
+        'debug/traced_display_item_list.h',
         'debug/traced_picture.cc',
         'debug/traced_picture.h',
         'debug/traced_value.cc',
diff --git a/cc/debug/debug_rect_history.cc b/cc/debug/debug_rect_history.cc
index c49294b..9fa90a5b 100644
--- a/cc/debug/debug_rect_history.cc
+++ b/cc/debug/debug_rect_history.cc
@@ -164,12 +164,10 @@
   for (Region::Iterator iter(layer->touch_event_handler_region());
        iter.has_rect();
        iter.next()) {
-    gfx::Rect touch_rect = gfx::ScaleToEnclosingRect(
-        iter.rect(), layer->contents_scale_x(), layer->contents_scale_y());
     debug_rects_.push_back(
         DebugRect(TOUCH_EVENT_HANDLER_RECT_TYPE,
                   MathUtil::MapEnclosingClippedRect(
-                      layer->screen_space_transform(), touch_rect)));
+                      layer->screen_space_transform(), iter.rect())));
   }
 }
 
@@ -183,13 +181,10 @@
   if (!layer->have_wheel_event_handlers())
     return;
 
-  gfx::Rect wheel_rect = gfx::ScaleToEnclosingRect(gfx::Rect(layer->bounds()),
-                                                   layer->contents_scale_x(),
-                                                   layer->contents_scale_y());
-  debug_rects_.push_back(
-      DebugRect(WHEEL_EVENT_HANDLER_RECT_TYPE,
-                MathUtil::MapEnclosingClippedRect(
-                    layer->screen_space_transform(), wheel_rect)));
+  debug_rects_.push_back(DebugRect(
+      WHEEL_EVENT_HANDLER_RECT_TYPE,
+      MathUtil::MapEnclosingClippedRect(layer->screen_space_transform(),
+                                        gfx::Rect(layer->bounds()))));
 }
 
 void DebugRectHistory::SaveScrollEventHandlerRects(LayerImpl* layer) {
@@ -202,13 +197,10 @@
   if (!layer->have_scroll_event_handlers())
     return;
 
-  gfx::Rect scroll_rect = gfx::ScaleToEnclosingRect(gfx::Rect(layer->bounds()),
-                                                    layer->contents_scale_x(),
-                                                    layer->contents_scale_y());
-  debug_rects_.push_back(
-      DebugRect(SCROLL_EVENT_HANDLER_RECT_TYPE,
-                MathUtil::MapEnclosingClippedRect(
-                    layer->screen_space_transform(), scroll_rect)));
+  debug_rects_.push_back(DebugRect(
+      SCROLL_EVENT_HANDLER_RECT_TYPE,
+      MathUtil::MapEnclosingClippedRect(layer->screen_space_transform(),
+                                        gfx::Rect(layer->bounds()))));
 }
 
 void DebugRectHistory::SaveNonFastScrollableRects(LayerImpl* layer) {
@@ -221,21 +213,17 @@
   for (Region::Iterator iter(layer->non_fast_scrollable_region());
        iter.has_rect();
        iter.next()) {
-    gfx::Rect scroll_rect = gfx::ScaleToEnclosingRect(
-        iter.rect(), layer->contents_scale_x(), layer->contents_scale_y());
     debug_rects_.push_back(
         DebugRect(NON_FAST_SCROLLABLE_RECT_TYPE,
                   MathUtil::MapEnclosingClippedRect(
-                      layer->screen_space_transform(), scroll_rect)));
+                      layer->screen_space_transform(), iter.rect())));
   }
 }
 
 void DebugRectHistory::SaveLayerAnimationBoundsRects(
     const LayerImplList& render_surface_layer_list) {
-  typedef LayerIterator<LayerImpl> LayerIteratorType;
-  LayerIteratorType end = LayerIteratorType::End(&render_surface_layer_list);
-  for (LayerIteratorType it =
-           LayerIteratorType::Begin(&render_surface_layer_list);
+  LayerIterator end = LayerIterator::End(&render_surface_layer_list);
+  for (LayerIterator it = LayerIterator::Begin(&render_surface_layer_list);
        it != end; ++it) {
     if (!it.represents_itself())
       continue;
diff --git a/cc/debug/invalidation_benchmark.cc b/cc/debug/invalidation_benchmark.cc
index 6fd3d3e..8a9fc88 100644
--- a/cc/debug/invalidation_benchmark.cc
+++ b/cc/debug/invalidation_benchmark.cc
@@ -71,9 +71,9 @@
   switch (mode_) {
     case FIXED_SIZE: {
       // Invalidation with a random position and fixed size.
-      gfx::Rect visible_content_rect = layer->visible_content_rect();
-      int x = LCGRandom() * (visible_content_rect.width() - width_);
-      int y = LCGRandom() * (visible_content_rect.height() - height_);
+      gfx::Rect visible_layer_rect = layer->visible_layer_rect();
+      int x = LCGRandom() * (visible_layer_rect.width() - width_);
+      int y = LCGRandom() * (visible_layer_rect.height() - height_);
       gfx::Rect invalidation_rect(x, y, width_, height_);
       layer->SetNeedsDisplayRect(invalidation_rect);
       break;
@@ -85,11 +85,11 @@
     }
     case RANDOM: {
       // Random invalidation inside the viewport.
-      gfx::Rect visible_content_rect = layer->visible_content_rect();
-      int x_min = LCGRandom() * visible_content_rect.width();
-      int x_max = LCGRandom() * visible_content_rect.width();
-      int y_min = LCGRandom() * visible_content_rect.height();
-      int y_max = LCGRandom() * visible_content_rect.height();
+      gfx::Rect visible_layer_rect = layer->visible_layer_rect();
+      int x_min = LCGRandom() * visible_layer_rect.width();
+      int x_max = LCGRandom() * visible_layer_rect.width();
+      int y_min = LCGRandom() * visible_layer_rect.height();
+      int y_max = LCGRandom() * visible_layer_rect.height();
       if (x_min > x_max)
         std::swap(x_min, x_max);
       if (y_min > y_max)
@@ -100,7 +100,7 @@
     }
     case VIEWPORT: {
       // Invalidate entire viewport.
-      layer->SetNeedsDisplayRect(layer->visible_content_rect());
+      layer->SetNeedsDisplayRect(layer->visible_layer_rect());
       break;
     }
   }
diff --git a/cc/debug/micro_benchmark_controller_unittest.cc b/cc/debug/micro_benchmark_controller_unittest.cc
index 92cfc468..b9f953cc 100644
--- a/cc/debug/micro_benchmark_controller_unittest.cc
+++ b/cc/debug/micro_benchmark_controller_unittest.cc
@@ -76,9 +76,8 @@
       base::Bind(&IncrementCallCount, base::Unretained(&run_count)));
   EXPECT_GT(id, 0);
 
-  scoped_ptr<ResourceUpdateQueue> queue(new ResourceUpdateQueue);
   layer_tree_host_->SetOutputSurfaceLostForTesting(false);
-  layer_tree_host_->UpdateLayers(queue.get());
+  layer_tree_host_->UpdateLayers();
 
   EXPECT_EQ(1, run_count);
 }
@@ -96,9 +95,8 @@
       base::Bind(&IncrementCallCount, base::Unretained(&run_count)));
   EXPECT_GT(id, 0);
 
-  scoped_ptr<ResourceUpdateQueue> queue(new ResourceUpdateQueue);
   layer_tree_host_->SetOutputSurfaceLostForTesting(false);
-  layer_tree_host_->UpdateLayers(queue.get());
+  layer_tree_host_->UpdateLayers();
 
   EXPECT_EQ(2, run_count);
 
@@ -113,10 +111,10 @@
       base::Bind(&IncrementCallCount, base::Unretained(&run_count)));
   EXPECT_GT(id, 0);
 
-  layer_tree_host_->UpdateLayers(queue.get());
+  layer_tree_host_->UpdateLayers();
   EXPECT_EQ(4, run_count);
 
-  layer_tree_host_->UpdateLayers(queue.get());
+  layer_tree_host_->UpdateLayers();
   EXPECT_EQ(4, run_count);
 }
 
diff --git a/cc/debug/rasterize_and_record_benchmark.cc b/cc/debug/rasterize_and_record_benchmark.cc
index e68f5de..bed367d 100644
--- a/cc/debug/rasterize_and_record_benchmark.cc
+++ b/cc/debug/rasterize_and_record_benchmark.cc
@@ -109,8 +109,7 @@
 void RasterizeAndRecordBenchmark::RunOnLayer(PictureLayer* layer) {
   DCHECK(host_);
 
-  gfx::Rect visible_layer_rect = gfx::ScaleToEnclosingRect(
-      layer->visible_content_rect(), 1.f / layer->contents_scale_x());
+  gfx::Rect visible_layer_rect = layer->visible_layer_rect();
   if (visible_layer_rect.IsEmpty())
     return;
 
diff --git a/cc/debug/rasterize_and_record_benchmark_impl.cc b/cc/debug/rasterize_and_record_benchmark_impl.cc
index a012f584..51e20ee 100644
--- a/cc/debug/rasterize_and_record_benchmark_impl.cc
+++ b/cc/debug/rasterize_and_record_benchmark_impl.cc
@@ -158,7 +158,7 @@
     rasterize_results_.total_picture_layers_with_no_content++;
     return;
   }
-  if (layer->visible_content_rect().IsEmpty()) {
+  if (layer->visible_layer_rect().IsEmpty()) {
     rasterize_results_.total_picture_layers_off_screen++;
     return;
   }
@@ -176,14 +176,13 @@
       settings.skewport_target_time_in_seconds,
       settings.skewport_extrapolation_limit_in_content_pixels);
 
-  PictureLayerTiling* tiling = tiling_set->AddTiling(layer->contents_scale_x(),
-                                                     layer->GetRasterSource());
+  PictureLayerTiling* tiling =
+      tiling_set->AddTiling(1.f, layer->GetRasterSource());
   tiling->CreateAllTilesForTesting();
   RasterSource* raster_source = tiling->raster_source();
-  for (PictureLayerTiling::CoverageIterator it(
-           tiling, layer->contents_scale_x(), layer->visible_content_rect());
-       it;
-       ++it) {
+  for (PictureLayerTiling::CoverageIterator it(tiling, 1.f,
+                                               layer->visible_layer_rect());
+       it; ++it) {
     DCHECK(*it);
 
     gfx::Rect content_rect = (*it)->content_rect();
diff --git a/cc/debug/traced_display_item_list.cc b/cc/debug/traced_display_item_list.cc
new file mode 100644
index 0000000..1b45a593
--- /dev/null
+++ b/cc/debug/traced_display_item_list.cc
@@ -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.
+#include "cc/debug/traced_display_item_list.h"
+
+#include "base/json/json_writer.h"
+#include "cc/debug/traced_value.h"
+#include "cc/playback/display_item_list.h"
+
+namespace cc {
+
+TracedDisplayItemList::TracedDisplayItemList(
+    scoped_refptr<const DisplayItemList> list,
+    bool include_items)
+    : display_item_list_(list), include_items_(include_items) {
+}
+
+TracedDisplayItemList::~TracedDisplayItemList() {
+}
+
+void TracedDisplayItemList::AppendAsTraceFormat(std::string* out) const {
+  scoped_refptr<base::trace_event::ConvertableToTraceFormat> convertable =
+      display_item_list_->AsValue(include_items_);
+  convertable->AppendAsTraceFormat(out);
+}
+
+}  // namespace cc
diff --git a/cc/debug/traced_display_item_list.h b/cc/debug/traced_display_item_list.h
new file mode 100644
index 0000000..f7caa04
--- /dev/null
+++ b/cc/debug/traced_display_item_list.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 CC_DEBUG_TRACED_DISPLAY_ITEM_LIST_H_
+#define CC_DEBUG_TRACED_DISPLAY_ITEM_LIST_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/debug/traced_value.h"
+
+namespace cc {
+
+class DisplayItemList;
+
+class TracedDisplayItemList
+    : public base::trace_event::ConvertableToTraceFormat {
+ public:
+  static scoped_refptr<ConvertableToTraceFormat> AsTraceableDisplayItemList(
+      scoped_refptr<const DisplayItemList> list,
+      bool include_items) {
+    return scoped_refptr<ConvertableToTraceFormat>(
+        new TracedDisplayItemList(list, include_items));
+  }
+  void AppendAsTraceFormat(std::string* out) const override;
+
+ private:
+  explicit TracedDisplayItemList(scoped_refptr<const DisplayItemList> list,
+                                 bool include_items);
+  ~TracedDisplayItemList() override;
+
+  scoped_refptr<const DisplayItemList> display_item_list_;
+  bool include_items_;
+
+  DISALLOW_COPY_AND_ASSIGN(TracedDisplayItemList);
+};
+
+}  // namespace cc
+
+#endif  // CC_DEBUG_TRACED_DISPLAY_ITEM_LIST_H_
diff --git a/cc/layers/append_quads_data.h b/cc/layers/append_quads_data.h
index 50f58775..45b0c17 100644
--- a/cc/layers/append_quads_data.h
+++ b/cc/layers/append_quads_data.h
@@ -14,7 +14,7 @@
   AppendQuadsData()
       : num_incomplete_tiles(0),
         num_missing_tiles(0),
-        visible_content_area(0),
+        visible_layer_area(0),
         approximated_visible_content_area(0),
         checkerboarded_visible_content_area(0) {}
 
@@ -23,7 +23,7 @@
   // Set by the layer appending quads.
   int64 num_missing_tiles;
   // Set by the layer appending quads.
-  int64 visible_content_area;
+  int64 visible_layer_area;
   // Set by the layer appending quads.
   int64 approximated_visible_content_area;
   // Set by the layer appending quads.
diff --git a/cc/layers/delegated_renderer_layer.cc b/cc/layers/delegated_renderer_layer.cc
index cf2c787..b48d1eae 100644
--- a/cc/layers/delegated_renderer_layer.cc
+++ b/cc/layers/delegated_renderer_layer.cc
@@ -83,8 +83,8 @@
   SetNextCommitWaitsForActivation();
 }
 
-bool DelegatedRendererLayer::Update(ResourceUpdateQueue* queue) {
-  bool updated = Layer::Update(queue);
+bool DelegatedRendererLayer::Update() {
+  bool updated = Layer::Update();
   if (!should_collect_new_frame_)
     return updated;
 
diff --git a/cc/layers/delegated_renderer_layer.h b/cc/layers/delegated_renderer_layer.h
index 4182289c..124b389 100644
--- a/cc/layers/delegated_renderer_layer.h
+++ b/cc/layers/delegated_renderer_layer.h
@@ -24,7 +24,7 @@
 
   scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
-  bool Update(ResourceUpdateQueue* queue) override;
+  bool Update() override;
   void PushPropertiesTo(LayerImpl* impl) override;
 
   // Called by the DelegatedFrameProvider when a new frame is available to be
diff --git a/cc/layers/delegated_renderer_layer_impl.cc b/cc/layers/delegated_renderer_layer_impl.cc
index 01ae74f..9235af5 100644
--- a/cc/layers/delegated_renderer_layer_impl.cc
+++ b/cc/layers/delegated_renderer_layer_impl.cc
@@ -429,7 +429,7 @@
       output_shared_quad_state->CopyFrom(delegated_shared_quad_state);
 
       if (is_root_delegated_render_pass) {
-        output_shared_quad_state->content_to_target_transform.ConcatTransform(
+        output_shared_quad_state->quad_to_target_transform.ConcatTransform(
             delegated_frame_to_target_transform);
 
         if (render_target() == this) {
@@ -457,7 +457,7 @@
     DCHECK(output_shared_quad_state);
 
     gfx::Transform quad_content_to_delegated_target_space =
-        output_shared_quad_state->content_to_target_transform;
+        output_shared_quad_state->quad_to_target_transform;
     if (!is_root_delegated_render_pass) {
       quad_content_to_delegated_target_space.ConcatTransform(
           delegated_render_pass->transform_to_root_target);
diff --git a/cc/layers/delegated_renderer_layer_impl_unittest.cc b/cc/layers/delegated_renderer_layer_impl_unittest.cc
index b5743dd6..a2e8d70 100644
--- a/cc/layers/delegated_renderer_layer_impl_unittest.cc
+++ b/cc/layers/delegated_renderer_layer_impl_unittest.cc
@@ -72,19 +72,16 @@
 
     layer_before->SetPosition(gfx::Point(20, 20));
     layer_before->SetBounds(gfx::Size(14, 14));
-    layer_before->SetContentBounds(gfx::Size(14, 14));
     layer_before->SetDrawsContent(true);
     layer_before->SetHasRenderSurface(true);
 
     layer_after->SetPosition(gfx::Point(5, 5));
     layer_after->SetBounds(gfx::Size(15, 15));
-    layer_after->SetContentBounds(gfx::Size(15, 15));
     layer_after->SetDrawsContent(true);
     layer_after->SetHasRenderSurface(true);
 
     delegated_renderer_layer->SetPosition(gfx::Point(3, 3));
     delegated_renderer_layer->SetBounds(gfx::Size(10, 10));
-    delegated_renderer_layer->SetContentBounds(gfx::Size(10, 10));
     delegated_renderer_layer->SetDrawsContent(true);
     gfx::Transform transform;
     transform.Translate(1.0, 1.0);
@@ -148,7 +145,6 @@
 
     delegated_renderer_layer->SetPosition(gfx::Point(3, 3));
     delegated_renderer_layer->SetBounds(gfx::Size(10, 10));
-    delegated_renderer_layer->SetContentBounds(gfx::Size(10, 10));
     delegated_renderer_layer->SetDrawsContent(true);
     delegated_renderer_layer->SetHasRenderSurface(true);
     gfx::Transform transform;
@@ -228,7 +224,6 @@
 
     delegated_renderer_layer->SetPosition(gfx::Point(3, 3));
     delegated_renderer_layer->SetBounds(gfx::Size(10, 10));
-    delegated_renderer_layer->SetContentBounds(gfx::Size(10, 10));
     delegated_renderer_layer->SetDrawsContent(true);
     delegated_renderer_layer->SetHasRenderSurface(true);
     gfx::Transform transform;
@@ -409,7 +404,7 @@
   EXPECT_TRANSFORMATION_MATRIX_EQ(
       transform, frame.render_passes[3]
                      ->quad_list.front()
-                     ->shared_quad_state->content_to_target_transform);
+                     ->shared_quad_state->quad_to_target_transform);
 
   // Quads from non-root RenderPasses should not be shifted though.
   ASSERT_EQ(2u, frame.render_passes[2]->quad_list.size());
@@ -417,16 +412,16 @@
   EXPECT_TRANSFORMATION_MATRIX_EQ(
       gfx::Transform(), frame.render_passes[2]
                             ->quad_list.front()
-                            ->shared_quad_state->content_to_target_transform);
+                            ->shared_quad_state->quad_to_target_transform);
   EXPECT_TRANSFORMATION_MATRIX_EQ(
       gfx::Transform(), frame.render_passes[2]
                             ->quad_list.ElementAt(1)
-                            ->shared_quad_state->content_to_target_transform);
+                            ->shared_quad_state->quad_to_target_transform);
   ASSERT_EQ(1u, frame.render_passes[1]->quad_list.size());
   EXPECT_TRANSFORMATION_MATRIX_EQ(
       gfx::Transform(), frame.render_passes[1]
                             ->quad_list.front()
-                            ->shared_quad_state->content_to_target_transform);
+                            ->shared_quad_state->quad_to_target_transform);
 
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
@@ -589,7 +584,7 @@
   EXPECT_TRANSFORMATION_MATRIX_EQ(
       gfx::Transform(), frame.render_passes[3]
                             ->quad_list.front()
-                            ->shared_quad_state->content_to_target_transform);
+                            ->shared_quad_state->quad_to_target_transform);
 
   // Quads from non-root RenderPasses should not be shifted either.
   ASSERT_EQ(2u, frame.render_passes[2]->quad_list.size());
@@ -597,16 +592,16 @@
   EXPECT_TRANSFORMATION_MATRIX_EQ(
       gfx::Transform(), frame.render_passes[2]
                             ->quad_list.front()
-                            ->shared_quad_state->content_to_target_transform);
+                            ->shared_quad_state->quad_to_target_transform);
   EXPECT_TRANSFORMATION_MATRIX_EQ(
       gfx::Transform(), frame.render_passes[2]
                             ->quad_list.ElementAt(1)
-                            ->shared_quad_state->content_to_target_transform);
+                            ->shared_quad_state->quad_to_target_transform);
   ASSERT_EQ(1u, frame.render_passes[1]->quad_list.size());
   EXPECT_TRANSFORMATION_MATRIX_EQ(
       gfx::Transform(), frame.render_passes[1]
                             ->quad_list.front()
-                            ->shared_quad_state->content_to_target_transform);
+                            ->shared_quad_state->quad_to_target_transform);
 
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
@@ -633,7 +628,6 @@
 
     delegated_renderer_layer->SetPosition(gfx::Point(20, 20));
     delegated_renderer_layer->SetBounds(gfx::Size(75, 75));
-    delegated_renderer_layer->SetContentBounds(gfx::Size(75, 75));
     delegated_renderer_layer->SetDrawsContent(true);
     gfx::Transform transform;
     transform.Scale(2.0, 2.0);
@@ -835,7 +829,7 @@
   expected.Scale(1.5, 1.5);
   expected.Translate(7.0, 7.0);
   EXPECT_TRANSFORMATION_MATRIX_EQ(
-      expected, root_delegated_shared_quad_state->content_to_target_transform);
+      expected, root_delegated_shared_quad_state->quad_to_target_transform);
 
   // The contributing render pass should not be transformed from its input.
   EXPECT_EQ(gfx::Rect(21, 21, 3, 3).ToString(),
@@ -845,8 +839,7 @@
   expected.Scale(0.8f, 0.8f);
   expected.Translate(9.0, 9.0);
   EXPECT_TRANSFORMATION_MATRIX_EQ(
-      expected,
-      contrib_delegated_shared_quad_state->content_to_target_transform);
+      expected, contrib_delegated_shared_quad_state->quad_to_target_transform);
 
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
@@ -894,7 +887,7 @@
   expected.Scale(1.5, 1.5);
   expected.Translate(7.0, 7.0);
   EXPECT_TRANSFORMATION_MATRIX_EQ(
-      expected, root_delegated_shared_quad_state->content_to_target_transform);
+      expected, root_delegated_shared_quad_state->quad_to_target_transform);
 
   // The contributing render pass should not be transformed from its input.
   EXPECT_EQ(gfx::Rect(21, 21, 3, 3).ToString(),
@@ -904,8 +897,7 @@
   expected.Scale(0.8f, 0.8f);
   expected.Translate(9.0, 9.0);
   EXPECT_TRANSFORMATION_MATRIX_EQ(
-      expected,
-      contrib_delegated_shared_quad_state->content_to_target_transform);
+      expected, contrib_delegated_shared_quad_state->quad_to_target_transform);
 
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
@@ -943,7 +935,7 @@
   expected.Scale(3.0, 3.0);
   expected.Translate(7.0, 7.0);
   EXPECT_TRANSFORMATION_MATRIX_EQ(
-      expected, root_delegated_shared_quad_state->content_to_target_transform);
+      expected, root_delegated_shared_quad_state->quad_to_target_transform);
 
   // The contributing render pass should not be transformed from its input.
   EXPECT_EQ(gfx::Rect(21, 21, 3, 3).ToString(),
@@ -953,8 +945,7 @@
   expected.Scale(0.8f, 0.8f);
   expected.Translate(9.0, 9.0);
   EXPECT_TRANSFORMATION_MATRIX_EQ(
-      expected,
-      contrib_delegated_shared_quad_state->content_to_target_transform);
+      expected, contrib_delegated_shared_quad_state->quad_to_target_transform);
 
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
@@ -991,7 +982,7 @@
   expected.Scale(3.0, 3.0);
   expected.Translate(7.0, 7.0);
   EXPECT_TRANSFORMATION_MATRIX_EQ(
-      expected, root_delegated_shared_quad_state->content_to_target_transform);
+      expected, root_delegated_shared_quad_state->quad_to_target_transform);
 
   // The contributing render pass should not be transformed from its input.
   EXPECT_EQ(gfx::Rect(21, 21, 3, 3).ToString(),
@@ -1001,8 +992,7 @@
   expected.Scale(0.8f, 0.8f);
   expected.Translate(9.0, 9.0);
   EXPECT_TRANSFORMATION_MATRIX_EQ(
-      expected,
-      contrib_delegated_shared_quad_state->content_to_target_transform);
+      expected, contrib_delegated_shared_quad_state->quad_to_target_transform);
 
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
@@ -1041,7 +1031,7 @@
   expected.Scale(1.5, 1.5);
   expected.Translate(7.0, 7.0);
   EXPECT_TRANSFORMATION_MATRIX_EQ(
-      expected, root_delegated_shared_quad_state->content_to_target_transform);
+      expected, root_delegated_shared_quad_state->quad_to_target_transform);
 
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
@@ -1066,7 +1056,6 @@
 
     delegated_renderer_layer->SetPosition(gfx::Point(20, 20));
     delegated_renderer_layer->SetBounds(gfx::Size(50, 50));
-    delegated_renderer_layer->SetContentBounds(gfx::Size(50, 50));
     delegated_renderer_layer->SetDrawsContent(true);
 
     RenderPassList delegated_render_passes;
@@ -1174,7 +1163,6 @@
 
       clip_layer->SetPosition(clip_rect.origin());
       clip_layer->SetBounds(clip_rect.size());
-      clip_layer->SetContentBounds(clip_rect.size());
       clip_layer->SetMasksToBounds(true);
 
       origin_layer->SetPosition(
@@ -1446,7 +1434,6 @@
   FakeDelegatedRendererLayerImpl* delegated_renderer_layer_impl =
       impl.AddChildToRoot<FakeDelegatedRendererLayerImpl>();
   delegated_renderer_layer_impl->SetBounds(layer_size);
-  delegated_renderer_layer_impl->SetContentBounds(layer_size);
   delegated_renderer_layer_impl->SetDrawsContent(true);
 
   // Contributing render pass is offset by a transform and holds a quad that
@@ -1505,7 +1492,7 @@
   {
     SCOPED_TRACE("Full occlusion");
     {
-      gfx::Rect occluded(delegated_renderer_layer_impl->visible_content_rect());
+      gfx::Rect occluded(delegated_renderer_layer_impl->visible_layer_rect());
 
       SCOPED_TRACE("Root render pass");
       impl.AppendQuadsForPassWithOcclusion(delegated_renderer_layer_impl, pass1,
@@ -1515,7 +1502,7 @@
       EXPECT_EQ(pass1->quad_list.size(), 0u);
     }
     {
-      gfx::Rect occluded(delegated_renderer_layer_impl->visible_content_rect());
+      gfx::Rect occluded(delegated_renderer_layer_impl->visible_layer_rect());
 
       SCOPED_TRACE("Contributing render pass");
       impl.AppendQuadsForPassWithOcclusion(delegated_renderer_layer_impl, pass2,
@@ -1614,7 +1601,6 @@
   FakeDelegatedRendererLayerImpl* delegated_renderer_layer_impl =
       impl.AddChildToRoot<FakeDelegatedRendererLayerImpl>();
   delegated_renderer_layer_impl->SetBounds(layer_size);
-  delegated_renderer_layer_impl->SetContentBounds(layer_size);
   delegated_renderer_layer_impl->SetDrawsContent(true);
 
   // Contributing render pass is offset by a transform and holds a quad that
@@ -1672,7 +1658,6 @@
   scoped_ptr<FakeDelegatedRendererLayerImpl> delegated_renderer_layer_impl =
       FakeDelegatedRendererLayerImpl::Create(host_impl_->active_tree(), 5);
   delegated_renderer_layer_impl->SetBounds(layer_size);
-  delegated_renderer_layer_impl->SetContentBounds(layer_size);
   delegated_renderer_layer_impl->SetDrawsContent(true);
 
   RenderPassList delegated_render_passes;
diff --git a/cc/layers/draw_properties.h b/cc/layers/draw_properties.h
index ab1b166..b5ac389 100644
--- a/cc/layers/draw_properties.h
+++ b/cc/layers/draw_properties.h
@@ -27,8 +27,6 @@
         can_use_lcd_text(false),
         is_clipped(false),
         render_target(nullptr),
-        contents_scale_x(1.f),
-        contents_scale_y(1.f),
         num_unclipped_descendants(0),
         layer_or_descendant_has_copy_request(false),
         layer_or_descendant_has_input_handler(false),
@@ -83,8 +81,9 @@
   // ancestor of this layer.
   LayerType* render_target;
 
-  // This rect is in the layer's content space.
-  gfx::Rect visible_content_rect;
+  // This rect is a bounding box around what part of the layer is visible, in
+  // the layer's coordinate space.
+  gfx::Rect visible_layer_rect;
 
   // In target surface space, the rect that encloses the clipped, drawable
   // content of the layer.
@@ -94,13 +93,6 @@
   // value is used to avoid unnecessarily changing GL scissor state.
   gfx::Rect clip_rect;
 
-  // The scale used to move between layer space and content space, and bounds
-  // of the space. One is always a function of the other, but which one
-  // depends on the layer type. For picture layers, this is an ideal scale,
-  // and not always the one used.
-  float contents_scale_x;
-  float contents_scale_y;
-
   // Number of descendants with a clip parent that is our ancestor. NB - this
   // does not include our clip children because they are clipped by us.
   size_t num_unclipped_descendants;
diff --git a/cc/layers/io_surface_layer.cc b/cc/layers/io_surface_layer.cc
index ffcb0e2..c2e35304 100644
--- a/cc/layers/io_surface_layer.cc
+++ b/cc/layers/io_surface_layer.cc
@@ -44,9 +44,8 @@
   io_surface_layer->SetIOSurfaceProperties(io_surface_id_, io_surface_size_);
 }
 
-bool IOSurfaceLayer::Update(ResourceUpdateQueue* queue) {
-  bool updated = Layer::Update(queue);
-
+bool IOSurfaceLayer::Update() {
+  bool updated = Layer::Update();
   // This layer doesn't update any resources from the main thread side,
   // but repaint rects need to be sent to the layer impl via commit.
   return updated || !update_rect_.IsEmpty();
diff --git a/cc/layers/io_surface_layer.h b/cc/layers/io_surface_layer.h
index 56823f34..14eabd9 100644
--- a/cc/layers/io_surface_layer.h
+++ b/cc/layers/io_surface_layer.h
@@ -18,7 +18,7 @@
 
   scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
   void PushPropertiesTo(LayerImpl* layer) override;
-  bool Update(ResourceUpdateQueue* queue) override;
+  bool Update() override;
 
  protected:
   bool HasDrawableContent() const override;
diff --git a/cc/layers/io_surface_layer_impl_unittest.cc b/cc/layers/io_surface_layer_impl_unittest.cc
index 1c9256c..75c2cf77 100644
--- a/cc/layers/io_surface_layer_impl_unittest.cc
+++ b/cc/layers/io_surface_layer_impl_unittest.cc
@@ -19,7 +19,6 @@
   IOSurfaceLayerImpl* io_surface_layer_impl =
       impl.AddChildToRoot<IOSurfaceLayerImpl>();
   io_surface_layer_impl->SetBounds(layer_size);
-  io_surface_layer_impl->SetContentBounds(layer_size);
   io_surface_layer_impl->SetDrawsContent(true);
 
   io_surface_layer_impl->SetIOSurfaceProperties(1, gfx::Size(1, 1));
@@ -40,7 +39,7 @@
 
   {
     SCOPED_TRACE("Full occlusion");
-    gfx::Rect occluded(io_surface_layer_impl->visible_content_rect());
+    gfx::Rect occluded(io_surface_layer_impl->visible_layer_rect());
     impl.AppendQuadsWithOcclusion(io_surface_layer_impl, occluded);
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index d1c173e..45c9839 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -240,15 +240,6 @@
   return !layer_tree_host_->in_paint_layer_contents();
 }
 
-// TODO(danakj): Remove this after impl_side_painting.
-gfx::Rect Layer::LayerRectToContentRect(const gfx::Rect& layer_rect) const {
-  gfx::Rect content_rect = layer_rect;
-  // Intersect with content rect to avoid the extra pixel because for some
-  // values x and y, ceil((x / y) * y) may be x + 1.
-  content_rect.Intersect(gfx::Rect(bounds()));
-  return content_rect;
-}
-
 skia::RefPtr<SkPicture> Layer::GetPicture() const {
   return skia::RefPtr<SkPicture>();
 }
@@ -470,17 +461,6 @@
   return color;
 }
 
-void Layer::CalculateContentsScale(float ideal_contents_scale,
-                                   float* contents_scale_x,
-                                   float* contents_scale_y,
-                                   gfx::Size* content_bounds) {
-  DCHECK(layer_tree_host_);
-
-  *contents_scale_x = 1;
-  *contents_scale_y = 1;
-  *content_bounds = bounds();
-}
-
 void Layer::SetMasksToBounds(bool masks_to_bounds) {
   DCHECK(IsPropertyChangeAllowed());
   if (masks_to_bounds_ == masks_to_bounds)
@@ -1153,8 +1133,6 @@
   layer->SetBackgroundColor(background_color_);
   layer->SetBounds(use_paint_properties ? paint_properties_.bounds
                                         : bounds_);
-  layer->SetContentsScale(1.f, 1.f);
-  layer->SetContentBounds(bounds());
 
   if (frame_viewer_instrumentation::IsTracingLayerTreeSnapshots())
     layer->SetDebugInfo(TakeDebugInfo());
@@ -1369,7 +1347,7 @@
       layer_tree_host_->source_frame_number();
 }
 
-bool Layer::Update(ResourceUpdateQueue* queue) {
+bool Layer::Update() {
   DCHECK(layer_tree_host_);
   DCHECK_EQ(layer_tree_host_->source_frame_number(),
             paint_properties_.source_frame_number) <<
@@ -1542,12 +1520,6 @@
   layer_animation_controller_->RemoveEventObserver(animation_observer);
 }
 
-SimpleEnclosedRegion Layer::VisibleContentOpaqueRegion() const {
-  if (contents_opaque())
-    return SimpleEnclosedRegion(visible_content_rect());
-  return SimpleEnclosedRegion();
-}
-
 ScrollbarLayerInterface* Layer::ToScrollbarLayer() {
   return nullptr;
 }
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index d344c2d..6a35ec7 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -254,8 +254,8 @@
   gfx::Rect drawable_content_rect() const {
     return draw_properties_.drawable_content_rect;
   }
-  gfx::Rect visible_content_rect() const {
-    return draw_properties_.visible_content_rect;
+  gfx::Rect visible_layer_rect() const {
+    return draw_properties_.visible_layer_rect;
   }
   Layer* render_target() {
     DCHECK(!draw_properties_.render_target ||
@@ -374,8 +374,8 @@
 
   // This methods typically need to be overwritten by derived classes.
   virtual void SavePaintProperties();
-  // Returns true iff any resources were updated that need to be committed.
-  virtual bool Update(ResourceUpdateQueue* queue);
+  // Returns true iff anything was updated that needs to be committed.
+  virtual bool Update();
   virtual bool NeedMoreUpdates();
   virtual void SetIsMask(bool is_mask) {}
   virtual void ReduceMemoryUsage() {}
@@ -394,18 +394,6 @@
 
   void ClearRenderSurfaceLayerList();
 
-  // The contents scale converts from logical, non-page-scaled pixels to target
-  // pixels. The contents scale is 1 for the root layer as it is already in
-  // physical pixels. By default contents scale is forced to be 1 except for
-  // subclasses of ContentsScalingLayer.
-  float contents_scale_x() const { return draw_properties_.contents_scale_x; }
-  float contents_scale_y() const { return draw_properties_.contents_scale_y; }
-
-  virtual void CalculateContentsScale(float ideal_contents_scale,
-                                      float* contents_scale_x,
-                                      float* contents_scale_y,
-                                      gfx::Size* content_bounds);
-
   LayerTreeHost* layer_tree_host() { return layer_tree_host_; }
   const LayerTreeHost* layer_tree_host() const { return layer_tree_host_; }
 
@@ -436,12 +424,8 @@
   void RemoveLayerAnimationEventObserver(
       LayerAnimationEventObserver* animation_observer);
 
-  virtual SimpleEnclosedRegion VisibleContentOpaqueRegion() const;
-
   virtual ScrollbarLayerInterface* ToScrollbarLayer();
 
-  gfx::Rect LayerRectToContentRect(const gfx::Rect& layer_rect) const;
-
   virtual skia::RefPtr<SkPicture> GetPicture() const;
 
   // Constructs a LayerImpl of the correct runtime type for this Layer type.
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index d4b1089..3144472 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -267,10 +267,9 @@
       continue;
 
     gfx::Rect request_in_layer_space = request->area();
-    gfx::Rect request_in_content_space =
-        LayerRectToContentRect(request_in_layer_space);
+    request_in_layer_space.Intersect(gfx::Rect(bounds()));
     request->set_area(MathUtil::MapEnclosingClippedRect(
-        draw_properties_.target_space_transform, request_in_content_space));
+        draw_properties_.target_space_transform, request_in_layer_space));
   }
 
   layer_tree_impl()->RemoveLayerWithCopyOutputRequest(this);
@@ -284,10 +283,9 @@
 
 void LayerImpl::PopulateSharedQuadState(SharedQuadState* state) const {
   state->SetAll(draw_properties_.target_space_transform, bounds(),
-                draw_properties_.visible_content_rect,
-                draw_properties_.clip_rect, draw_properties_.is_clipped,
-                draw_properties_.opacity, draw_properties_.blend_mode,
-                sorting_context_id_);
+                draw_properties_.visible_layer_rect, draw_properties_.clip_rect,
+                draw_properties_.is_clipped, draw_properties_.opacity,
+                draw_properties_.blend_mode, sorting_context_id_);
 }
 
 void LayerImpl::PopulateScaledSharedQuadState(SharedQuadState* state,
@@ -296,14 +294,14 @@
       draw_properties_.target_space_transform;
   scaled_draw_transform.Scale(SK_MScalar1 / scale, SK_MScalar1 / scale);
   gfx::Size scaled_bounds = gfx::ToCeiledSize(gfx::ScaleSize(bounds(), scale));
-  gfx::Rect scaled_visible_content_rect =
-      gfx::ScaleToEnclosingRect(visible_content_rect(), scale);
-  scaled_visible_content_rect.Intersect(gfx::Rect(scaled_bounds));
+  gfx::Rect scaled_visible_layer_rect =
+      gfx::ScaleToEnclosingRect(visible_layer_rect(), scale);
+  scaled_visible_layer_rect.Intersect(gfx::Rect(scaled_bounds));
 
-  state->SetAll(scaled_draw_transform, scaled_bounds,
-                scaled_visible_content_rect, draw_properties().clip_rect,
-                draw_properties().is_clipped, draw_properties().opacity,
-                draw_properties().blend_mode, sorting_context_id_);
+  state->SetAll(scaled_draw_transform, scaled_bounds, scaled_visible_layer_rect,
+                draw_properties().clip_rect, draw_properties().is_clipped,
+                draw_properties().opacity, draw_properties().blend_mode,
+                sorting_context_id_);
 }
 
 bool LayerImpl::WillDraw(DrawMode draw_mode,
@@ -465,14 +463,8 @@
       // SCROLL_ON_MAIN_THREAD in this case?
     }
 
-    gfx::PointF hit_test_point_in_content_space =
-        MathUtil::ProjectPoint(inverse_screen_space_transform,
-                               screen_space_point,
-                               &clipped);
-    gfx::PointF hit_test_point_in_layer_space =
-        gfx::ScalePoint(hit_test_point_in_content_space,
-                        1.f / contents_scale_x(),
-                        1.f / contents_scale_y());
+    gfx::PointF hit_test_point_in_layer_space = MathUtil::ProjectPoint(
+        inverse_screen_space_transform, screen_space_point, &clipped);
     if (!clipped &&
         non_fast_scrollable_region().Contains(
             gfx::ToRoundedPoint(hit_test_point_in_layer_space))) {
@@ -510,14 +502,6 @@
   return InputHandler::SCROLL_STARTED;
 }
 
-// TODO(danakj): Remove this after impl_side_painting.
-gfx::Rect LayerImpl::LayerRectToContentRect(
-    const gfx::RectF& layer_rect) const {
-  gfx::RectF content_rect = layer_rect;
-  content_rect.Intersect(gfx::Rect(bounds()));
-  return gfx::ToEnclosingRect(content_rect);
-}
-
 skia::RefPtr<SkPicture> LayerImpl::GetPicture() {
   return skia::RefPtr<SkPicture>();
 }
@@ -530,8 +514,6 @@
   layer->SetTransformOrigin(transform_origin_);
   layer->SetBackgroundColor(background_color_);
   layer->SetBounds(bounds_);
-  layer->SetContentBounds(bounds_);
-  layer->SetContentsScale(contents_scale_x(), contents_scale_y());
   layer->SetDoubleSided(double_sided_);
   layer->SetDrawCheckerboardForMissingTiles(
       draw_checkerboard_for_missing_tiles_);
@@ -1148,21 +1130,6 @@
   damage_rect_ = gfx::UnionRects(damage_rect_, damage_rect);
 }
 
-// TODO(danakj): Remove this after impl_side_painting.
-void LayerImpl::SetContentBounds(const gfx::Size& content_bounds) {
-}
-
-void LayerImpl::SetContentsScale(float contents_scale_x,
-                                 float contents_scale_y) {
-  if (this->contents_scale_x() == contents_scale_x &&
-      this->contents_scale_y() == contents_scale_y)
-    return;
-
-  draw_properties_.contents_scale_x = contents_scale_x;
-  draw_properties_.contents_scale_y = contents_scale_y;
-  NoteLayerPropertyChanged();
-}
-
 bool LayerImpl::IsExternalScrollActive() const {
   return layer_tree_impl_->IsExternalScrollActive();
 }
@@ -1291,9 +1258,9 @@
   NoteLayerPropertyChangedForSubtree();
 }
 
-SimpleEnclosedRegion LayerImpl::VisibleContentOpaqueRegion() const {
+SimpleEnclosedRegion LayerImpl::VisibleOpaqueRegion() const {
   if (contents_opaque())
-    return SimpleEnclosedRegion(visible_content_rect());
+    return SimpleEnclosedRegion(visible_layer_rect());
   return SimpleEnclosedRegion();
 }
 
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 258e27a..90fb694 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -391,8 +391,8 @@
   gfx::Rect drawable_content_rect() const {
     return draw_properties_.drawable_content_rect;
   }
-  gfx::Rect visible_content_rect() const {
-    return draw_properties_.visible_content_rect;
+  gfx::Rect visible_layer_rect() const {
+    return draw_properties_.visible_layer_rect;
   }
   LayerImpl* render_target() {
     DCHECK(!draw_properties_.render_target ||
@@ -421,12 +421,6 @@
   void SetBoundsDelta(const gfx::Vector2dF& bounds_delta);
   gfx::Vector2dF bounds_delta() const { return bounds_delta_; }
 
-  void SetContentBounds(const gfx::Size& content_bounds);
-
-  float contents_scale_x() const { return draw_properties_.contents_scale_x; }
-  float contents_scale_y() const { return draw_properties_.contents_scale_y; }
-  void SetContentsScale(float contents_scale_x, float contents_scale_y);
-
   bool IsExternalScrollActive() const;
 
   void SetCurrentScrollOffset(const gfx::ScrollOffset& scroll_offset);
@@ -564,7 +558,7 @@
     return layer_animation_controller_.get();
   }
 
-  virtual SimpleEnclosedRegion VisibleContentOpaqueRegion() const;
+  virtual SimpleEnclosedRegion VisibleOpaqueRegion() const;
 
   virtual void DidBecomeActive();
 
@@ -593,8 +587,6 @@
     return scroll_clip_layer_ ? scroll_clip_layer_->bounds().height() : 0;
   }
 
-  gfx::Rect LayerRectToContentRect(const gfx::RectF& layer_rect) const;
-
   virtual skia::RefPtr<SkPicture> GetPicture();
 
   virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl);
diff --git a/cc/layers/layer_impl_unittest.cc b/cc/layers/layer_impl_unittest.cc
index 2255646..964b999 100644
--- a/cc/layers/layer_impl_unittest.cc
+++ b/cc/layers/layer_impl_unittest.cc
@@ -174,8 +174,6 @@
   EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetTransform(arbitrary_transform));
 
   // Changing these properties only affects the layer itself.
-  EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(
-      root->SetContentsScale(arbitrary_number, arbitrary_number));
   EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetDrawsContent(true));
   EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(
       root->SetBackgroundColor(arbitrary_color));
@@ -227,8 +225,6 @@
   EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(
       root->PushScrollOffsetFromMainThread(
           gfx::ScrollOffset(arbitrary_vector2d)));
-  EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(
-      root->SetContentsScale(arbitrary_number, arbitrary_number));
   EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetContentsOpaque(true));
   EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetOpacity(arbitrary_number));
   EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(
@@ -324,8 +320,6 @@
 
   VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
       layer->SetDoubleSided(false));  // constructor initializes it to "true".
-  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
-      layer->SetContentsScale(arbitrary_number, arbitrary_number));
   VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetDrawsContent(true));
   VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
       layer->SetBackgroundColor(arbitrary_color));
@@ -347,8 +341,6 @@
   VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->Set3dSortingContextId(1));
   VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(
       layer->SetDoubleSided(false));  // constructor initializes it to "true".
-  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(
-      layer->SetContentsScale(arbitrary_number, arbitrary_number));
   VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetDrawsContent(true));
   VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(
       layer->SetBackgroundColor(arbitrary_color));
diff --git a/cc/layers/layer_iterator.h b/cc/layers/layer_iterator.h
index cd8da68..2c129e4 100644
--- a/cc/layers/layer_iterator.h
+++ b/cc/layers/layer_iterator.h
@@ -6,24 +6,24 @@
 #define CC_LAYERS_LAYER_ITERATOR_H_
 
 #include "cc/base/cc_export.h"
+#include "cc/layers/layer_impl.h"
 #include "cc/trees/layer_tree_host_common.h"
 
 namespace cc {
 
 // These classes provide means to iterate over the
-// RenderSurface-Layer tree.
+// RenderSurfaceImpl-LayerImpl tree.
 
-// Example code follows, for a tree of Layer/RenderSurface objects.
+// Example code follows, for a tree of LayerImpl/RenderSurfaceImpl objects.
 // See below for details.
 //
 // void DoStuffOnLayers(
-//     const RenderSurfaceLayerList& render_surface_layer_list) {
-//   typedef LayerIterator<Layer> LayerIteratorType;
+//     const LayerImplList& render_surface_layer_list) {
 //
-//   LayerIteratorType end =
-//       LayerIteratorType::End(&render_surface_layer_list);
-//   for (LayerIteratorType
-//            it = LayerIteratorType::Begin(&render_surface_layer_list);
+//   LayerIterator end =
+//       LayerIterator::End(&render_surface_layer_list);
+//   for (LayerIterator
+//            it = LayerIterator::Begin(&render_surface_layer_list);
 //        it != end;
 //        ++it) {
 //     // Only one of these will be true
@@ -79,7 +79,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-// Non-templated constants
 struct LayerIteratorValue {
   static const int kInvalidTargetRenderSurfaceLayerIndex = -1;
   // This must be (size_t)-1 since the iterator action code assumes that this
@@ -91,36 +90,30 @@
 
 // The position of a layer iterator that is independent
 // of its many template types.
-template <typename LayerType> struct LayerIteratorPosition {
+struct LayerIteratorPosition {
   bool represents_target_render_surface;
   bool represents_contributing_render_surface;
   bool represents_itself;
-  LayerType* target_render_surface_layer;
-  LayerType* current_layer;
+  LayerImpl* target_render_surface_layer;
+  LayerImpl* current_layer;
 };
 
 // An iterator class for walking over layers in the
 // RenderSurface-Layer tree.
-template <typename LayerType>
+// TODO(enne): This class probably shouldn't be entirely inline and
+// should get moved to a .cc file where it makes sense.
 class LayerIterator {
-  typedef LayerIterator<LayerType> LayerIteratorType;
-  typedef typename LayerType::LayerListType LayerList;
-  typedef typename LayerType::RenderSurfaceListType RenderSurfaceLayerList;
-  typedef typename LayerType::RenderSurfaceType RenderSurfaceType;
-
  public:
   LayerIterator() : render_surface_layer_list_(nullptr) {}
 
-  static LayerIteratorType Begin(
-      const RenderSurfaceLayerList* render_surface_layer_list) {
-    return LayerIteratorType(render_surface_layer_list, true);
+  static LayerIterator Begin(const LayerImplList* render_surface_layer_list) {
+    return LayerIterator(render_surface_layer_list, true);
   }
-  static LayerIteratorType End(
-      const RenderSurfaceLayerList* render_surface_layer_list) {
-    return LayerIteratorType(render_surface_layer_list, false);
+  static LayerIterator End(const LayerImplList* render_surface_layer_list) {
+    return LayerIterator(render_surface_layer_list, false);
   }
 
-  LayerIteratorType& operator++() {
+  LayerIterator& operator++() {
     MoveToNext();
     return *this;
   }
@@ -129,12 +122,12 @@
            other.target_render_surface_layer_index_ &&
            current_layer_index_ == other.current_layer_index_;
   }
-  bool operator!=(const LayerIteratorType& other) const {
+  bool operator!=(const LayerIterator& other) const {
     return !(*this == other);
   }
 
-  LayerType* operator->() const { return current_layer(); }
-  LayerType* operator*() const { return current_layer(); }
+  LayerImpl* operator->() const { return current_layer(); }
+  LayerImpl* operator*() const { return current_layer(); }
 
   bool represents_target_render_surface() const {
     return current_layer_represents_target_render_surface();
@@ -148,12 +141,12 @@
            !represents_contributing_render_surface();
   }
 
-  LayerType* target_render_surface_layer() const {
+  LayerImpl* target_render_surface_layer() const {
     return render_surface_layer_list_->at(target_render_surface_layer_index_);
   }
 
-  operator const LayerIteratorPosition<LayerType>() const {
-    LayerIteratorPosition<LayerType> position;
+  operator const LayerIteratorPosition() const {
+    LayerIteratorPosition position;
     position.represents_target_render_surface =
         represents_target_render_surface();
     position.represents_contributing_render_surface =
@@ -165,8 +158,7 @@
   }
 
  private:
-  LayerIterator(const RenderSurfaceLayerList* render_surface_layer_list,
-                bool start)
+  LayerIterator(const LayerImplList* render_surface_layer_list, bool start)
       : render_surface_layer_list_(render_surface_layer_list),
         target_render_surface_layer_index_(0) {
     for (size_t i = 0; i < render_surface_layer_list->size(); ++i) {
@@ -238,7 +230,7 @@
       int previous_target_render_surface_layer =
           target_render_surface_layer_index_;
 
-      for (LayerType* layer = current_layer();
+      for (LayerImpl* layer = current_layer();
            target_render_surface_layer() != layer;
            ++target_render_surface_layer_index_) {
       }
@@ -249,7 +241,7 @@
     }
   }
 
-  inline LayerType* current_layer() const {
+  inline LayerImpl* current_layer() const {
     return current_layer_represents_target_render_surface()
                ? target_render_surface_layer()
                : LayerTreeHostCommon::get_layer_as_raw_ptr(
@@ -257,7 +249,7 @@
   }
 
   inline bool current_layer_represents_contributing_render_surface() const {
-    return LayerTreeHostCommon::RenderSurfaceContributesToTarget<LayerType>(
+    return LayerTreeHostCommon::RenderSurfaceContributesToTarget<LayerImpl>(
         current_layer(), target_render_surface_layer()->id());
   }
   inline bool current_layer_represents_target_render_surface() const {
@@ -265,14 +257,14 @@
            LayerIteratorValue::kLayerIndexRepresentingTargetRenderSurface;
   }
 
-  inline RenderSurfaceType* target_render_surface() const {
+  inline RenderSurfaceImpl* target_render_surface() const {
     return target_render_surface_layer()->render_surface();
   }
-  inline const LayerList& target_render_surface_children() const {
+  inline const LayerImplList& target_render_surface_children() const {
     return target_render_surface()->layer_list();
   }
 
-  const RenderSurfaceLayerList* render_surface_layer_list_;
+  const LayerImplList* render_surface_layer_list_;
 
   // The iterator's current position.
 
diff --git a/cc/layers/layer_iterator_unittest.cc b/cc/layers/layer_iterator_unittest.cc
index 130222d..01815f9 100644
--- a/cc/layers/layer_iterator_unittest.cc
+++ b/cc/layers/layer_iterator_unittest.cc
@@ -22,28 +22,27 @@
 namespace cc {
 namespace {
 
-class TestLayer : public Layer {
+class TestLayerImpl : public LayerImpl {
  public:
-  static scoped_refptr<TestLayer> Create(const LayerSettings& settings) {
-    return make_scoped_refptr(new TestLayer(settings));
+  static scoped_ptr<TestLayerImpl> Create(LayerTreeImpl* tree, int id) {
+    return make_scoped_ptr(new TestLayerImpl(tree, id));
   }
+  ~TestLayerImpl() override {}
 
   int count_representing_target_surface_;
   int count_representing_contributing_surface_;
   int count_representing_itself_;
 
-  bool DrawsContent() const override { return draws_content_; }
-  void set_draws_content(bool draws_content) { draws_content_ = draws_content; }
-
  private:
-  explicit TestLayer(const LayerSettings& settings)
-      : Layer(settings), draws_content_(true) {
+  explicit TestLayerImpl(LayerTreeImpl* tree, int id)
+      : LayerImpl(tree, id, new SyncedScrollOffset),
+        count_representing_target_surface_(-1),
+        count_representing_contributing_surface_(-1),
+        count_representing_itself_(-1) {
     SetBounds(gfx::Size(100, 100));
     SetPosition(gfx::Point());
+    SetDrawsContent(true);
   }
-  ~TestLayer() override {}
-
-  bool draws_content_;
 };
 
 #define EXPECT_COUNT(layer, target, contrib, itself)                           \
@@ -51,15 +50,13 @@
   EXPECT_EQ(contrib, layer->count_representing_contributing_surface_);         \
   EXPECT_EQ(itself, layer->count_representing_itself_);
 
-typedef LayerIterator<Layer> FrontToBack;
-
-void ResetCounts(RenderSurfaceLayerList* render_surface_layer_list) {
+void ResetCounts(LayerImplList* render_surface_layer_list) {
   for (unsigned surface_index = 0;
        surface_index < render_surface_layer_list->size();
        ++surface_index) {
-    TestLayer* render_surface_layer = static_cast<TestLayer*>(
+    TestLayerImpl* render_surface_layer = static_cast<TestLayerImpl*>(
         render_surface_layer_list->at(surface_index));
-    RenderSurface* render_surface = render_surface_layer->render_surface();
+    RenderSurfaceImpl* render_surface = render_surface_layer->render_surface();
 
     render_surface_layer->count_representing_target_surface_ = -1;
     render_surface_layer->count_representing_contributing_surface_ = -1;
@@ -68,8 +65,8 @@
     for (unsigned layer_index = 0;
          layer_index < render_surface->layer_list().size();
          ++layer_index) {
-      TestLayer* layer = static_cast<TestLayer*>(
-          render_surface->layer_list().at(layer_index).get());
+      TestLayerImpl* layer = static_cast<TestLayerImpl*>(
+          render_surface->layer_list()[layer_index]);
 
       layer->count_representing_target_surface_ = -1;
       layer->count_representing_contributing_surface_ = -1;
@@ -78,14 +75,12 @@
   }
 }
 
-void IterateFrontToBack(
-    RenderSurfaceLayerList* render_surface_layer_list) {
+void IterateFrontToBack(LayerImplList* render_surface_layer_list) {
   ResetCounts(render_surface_layer_list);
   int count = 0;
-  for (FrontToBack it = FrontToBack::Begin(render_surface_layer_list);
-       it != FrontToBack::End(render_surface_layer_list);
-       ++it, ++count) {
-    TestLayer* layer = static_cast<TestLayer*>(*it);
+  for (LayerIterator it = LayerIterator::Begin(render_surface_layer_list);
+       it != LayerIterator::End(render_surface_layer_list); ++it, ++count) {
+    TestLayerImpl* layer = static_cast<TestLayerImpl*>(*it);
     if (it.represents_target_render_surface())
       layer->count_representing_target_surface_ = count;
     if (it.represents_contributing_render_surface())
@@ -95,135 +90,167 @@
   }
 }
 
-TEST(LayerIteratorTest, EmptyTree) {
-  RenderSurfaceLayerList render_surface_layer_list;
+class LayerIteratorTest : public testing::Test {
+ public:
+  LayerIteratorTest()
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_),
+        id_(1) {}
+
+  scoped_ptr<TestLayerImpl> CreateLayer() {
+    return TestLayerImpl::Create(host_impl_.active_tree(), id_++);
+  }
+
+ protected:
+  FakeImplProxy proxy_;
+  TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
+  FakeLayerTreeHostImpl host_impl_;
+
+  int id_;
+};
+
+TEST_F(LayerIteratorTest, EmptyTree) {
+  LayerImplList render_surface_layer_list;
 
   IterateFrontToBack(&render_surface_layer_list);
 }
 
-TEST(LayerIteratorTest, SimpleTree) {
-  LayerSettings settings;
-  scoped_refptr<TestLayer> root_layer = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> first = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> second = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> third = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> fourth = TestLayer::Create(settings);
+TEST_F(LayerIteratorTest, SimpleTree) {
+  scoped_ptr<TestLayerImpl> root_layer = CreateLayer();
+  scoped_ptr<TestLayerImpl> first = CreateLayer();
+  scoped_ptr<TestLayerImpl> second = CreateLayer();
+  scoped_ptr<TestLayerImpl> third = CreateLayer();
+  scoped_ptr<TestLayerImpl> fourth = CreateLayer();
 
-  root_layer->AddChild(first);
-  root_layer->AddChild(second);
-  root_layer->AddChild(third);
-  root_layer->AddChild(fourth);
+  TestLayerImpl* root_ptr = root_layer.get();
+  TestLayerImpl* first_ptr = first.get();
+  TestLayerImpl* second_ptr = second.get();
+  TestLayerImpl* third_ptr = third.get();
+  TestLayerImpl* fourth_ptr = fourth.get();
 
-  FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D);
-  TestTaskGraphRunner task_graph_runner;
-  scoped_ptr<FakeLayerTreeHost> host =
-      FakeLayerTreeHost::Create(&client, &task_graph_runner);
-  host->SetRootLayer(root_layer);
+  root_layer->AddChild(first.Pass());
+  root_layer->AddChild(second.Pass());
+  root_layer->AddChild(third.Pass());
+  root_layer->AddChild(fourth.Pass());
 
-  RenderSurfaceLayerList render_surface_layer_list;
-  LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
-      root_layer.get(), root_layer->bounds(), &render_surface_layer_list);
+  root_layer->SetHasRenderSurface(true);
+  host_impl_.active_tree()->SetRootLayer(root_layer.Pass());
+
+  LayerImplList render_surface_layer_list;
+  LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
+      root_ptr, root_ptr->bounds(), &render_surface_layer_list);
   LayerTreeHostCommon::CalculateDrawProperties(&inputs);
 
   IterateFrontToBack(&render_surface_layer_list);
-  EXPECT_COUNT(root_layer, 5, -1, 4);
-  EXPECT_COUNT(first, -1, -1, 3);
-  EXPECT_COUNT(second, -1, -1, 2);
-  EXPECT_COUNT(third, -1, -1, 1);
-  EXPECT_COUNT(fourth, -1, -1, 0);
+  EXPECT_COUNT(root_ptr, 5, -1, 4);
+  EXPECT_COUNT(first_ptr, -1, -1, 3);
+  EXPECT_COUNT(second_ptr, -1, -1, 2);
+  EXPECT_COUNT(third_ptr, -1, -1, 1);
+  EXPECT_COUNT(fourth_ptr, -1, -1, 0);
 }
 
-TEST(LayerIteratorTest, ComplexTree) {
-  LayerSettings settings;
-  scoped_refptr<TestLayer> root_layer = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root1 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root2 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root3 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root21 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root22 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root23 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root221 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root231 = TestLayer::Create(settings);
+TEST_F(LayerIteratorTest, ComplexTree) {
+  scoped_ptr<TestLayerImpl> root_layer = CreateLayer();
+  scoped_ptr<TestLayerImpl> root1 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root2 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root3 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root21 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root22 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root23 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root221 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root231 = CreateLayer();
 
-  root_layer->AddChild(root1);
-  root_layer->AddChild(root2);
-  root_layer->AddChild(root3);
-  root2->AddChild(root21);
-  root2->AddChild(root22);
-  root2->AddChild(root23);
-  root22->AddChild(root221);
-  root23->AddChild(root231);
+  TestLayerImpl* root_ptr = root_layer.get();
+  TestLayerImpl* root1_ptr = root1.get();
+  TestLayerImpl* root2_ptr = root2.get();
+  TestLayerImpl* root3_ptr = root3.get();
+  TestLayerImpl* root21_ptr = root21.get();
+  TestLayerImpl* root22_ptr = root22.get();
+  TestLayerImpl* root23_ptr = root23.get();
+  TestLayerImpl* root221_ptr = root221.get();
+  TestLayerImpl* root231_ptr = root231.get();
 
-  FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D);
-  TestTaskGraphRunner task_graph_runner;
-  scoped_ptr<FakeLayerTreeHost> host =
-      FakeLayerTreeHost::Create(&client, &task_graph_runner);
-  host->SetRootLayer(root_layer);
+  root22->AddChild(root221.Pass());
+  root23->AddChild(root231.Pass());
+  root2->AddChild(root21.Pass());
+  root2->AddChild(root22.Pass());
+  root2->AddChild(root23.Pass());
+  root_layer->AddChild(root1.Pass());
+  root_layer->AddChild(root2.Pass());
+  root_layer->AddChild(root3.Pass());
 
-  RenderSurfaceLayerList render_surface_layer_list;
-  LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
-      root_layer.get(), root_layer->bounds(), &render_surface_layer_list);
+  root_layer->SetHasRenderSurface(true);
+  host_impl_.active_tree()->SetRootLayer(root_layer.Pass());
+
+  LayerImplList render_surface_layer_list;
+  LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
+      root_ptr, root_ptr->bounds(), &render_surface_layer_list);
   LayerTreeHostCommon::CalculateDrawProperties(&inputs);
 
   IterateFrontToBack(&render_surface_layer_list);
-  EXPECT_COUNT(root_layer, 9, -1, 8);
-  EXPECT_COUNT(root1, -1, -1, 7);
-  EXPECT_COUNT(root2, -1, -1, 6);
-  EXPECT_COUNT(root21, -1, -1, 5);
-  EXPECT_COUNT(root22, -1, -1, 4);
-  EXPECT_COUNT(root221, -1, -1, 3);
-  EXPECT_COUNT(root23, -1, -1, 2);
-  EXPECT_COUNT(root231, -1, -1, 1);
-  EXPECT_COUNT(root3, -1, -1, 0);
+  EXPECT_COUNT(root_ptr, 9, -1, 8);
+  EXPECT_COUNT(root1_ptr, -1, -1, 7);
+  EXPECT_COUNT(root2_ptr, -1, -1, 6);
+  EXPECT_COUNT(root21_ptr, -1, -1, 5);
+  EXPECT_COUNT(root22_ptr, -1, -1, 4);
+  EXPECT_COUNT(root221_ptr, -1, -1, 3);
+  EXPECT_COUNT(root23_ptr, -1, -1, 2);
+  EXPECT_COUNT(root231_ptr, -1, -1, 1);
+  EXPECT_COUNT(root3_ptr, -1, -1, 0);
 }
 
-TEST(LayerIteratorTest, ComplexTreeMultiSurface) {
-  LayerSettings settings;
-  scoped_refptr<TestLayer> root_layer = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root1 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root2 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root3 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root21 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root22 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root23 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root221 = TestLayer::Create(settings);
-  scoped_refptr<TestLayer> root231 = TestLayer::Create(settings);
+TEST_F(LayerIteratorTest, ComplexTreeMultiSurface) {
+  scoped_ptr<TestLayerImpl> root_layer = CreateLayer();
+  scoped_ptr<TestLayerImpl> root1 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root2 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root3 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root21 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root22 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root23 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root221 = CreateLayer();
+  scoped_ptr<TestLayerImpl> root231 = CreateLayer();
 
-  root_layer->AddChild(root1);
-  root_layer->AddChild(root2);
-  root_layer->AddChild(root3);
-  root2->set_draws_content(false);
-  root2->SetOpacity(0.5f);
-  root2->SetForceRenderSurface(true);  // Force the layer to own a new surface.
-  root2->AddChild(root21);
-  root2->AddChild(root22);
-  root2->AddChild(root23);
-  root22->SetOpacity(0.5f);
-  root22->AddChild(root221);
-  root23->SetOpacity(0.5f);
-  root23->AddChild(root231);
+  TestLayerImpl* root_ptr = root_layer.get();
+  TestLayerImpl* root1_ptr = root1.get();
+  TestLayerImpl* root2_ptr = root2.get();
+  TestLayerImpl* root3_ptr = root3.get();
+  TestLayerImpl* root21_ptr = root21.get();
+  TestLayerImpl* root22_ptr = root22.get();
+  TestLayerImpl* root23_ptr = root23.get();
+  TestLayerImpl* root221_ptr = root221.get();
+  TestLayerImpl* root231_ptr = root231.get();
 
-  FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D);
-  TestTaskGraphRunner task_graph_runner;
-  scoped_ptr<FakeLayerTreeHost> host =
-      FakeLayerTreeHost::Create(&client, &task_graph_runner);
-  host->SetRootLayer(root_layer);
+  root22->SetHasRenderSurface(true);
+  root22->AddChild(root221.Pass());
+  root23->SetHasRenderSurface(true);
+  root23->AddChild(root231.Pass());
+  root2->SetDrawsContent(false);
+  root2->SetHasRenderSurface(true);
+  root2->AddChild(root21.Pass());
+  root2->AddChild(root22.Pass());
+  root2->AddChild(root23.Pass());
+  root_layer->AddChild(root1.Pass());
+  root_layer->AddChild(root2.Pass());
+  root_layer->AddChild(root3.Pass());
 
-  RenderSurfaceLayerList render_surface_layer_list;
-  LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
-      root_layer.get(), root_layer->bounds(), &render_surface_layer_list);
+  root_layer->SetHasRenderSurface(true);
+  host_impl_.active_tree()->SetRootLayer(root_layer.Pass());
+
+  LayerImplList render_surface_layer_list;
+  LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
+      root_ptr, root_ptr->bounds(), &render_surface_layer_list);
   LayerTreeHostCommon::CalculateDrawProperties(&inputs);
 
   IterateFrontToBack(&render_surface_layer_list);
-  EXPECT_COUNT(root_layer, 14, -1, 13);
-  EXPECT_COUNT(root1, -1, -1, 12);
-  EXPECT_COUNT(root2, 10, 11, -1);
-  EXPECT_COUNT(root21, -1, -1, 9);
-  EXPECT_COUNT(root22, 7, 8, 6);
-  EXPECT_COUNT(root221, -1, -1, 5);
-  EXPECT_COUNT(root23, 3, 4, 2);
-  EXPECT_COUNT(root231, -1, -1, 1);
-  EXPECT_COUNT(root3, -1, -1, 0);
+  EXPECT_COUNT(root_ptr, 14, -1, 13);
+  EXPECT_COUNT(root1_ptr, -1, -1, 12);
+  EXPECT_COUNT(root2_ptr, 10, 11, -1);
+  EXPECT_COUNT(root21_ptr, -1, -1, 9);
+  EXPECT_COUNT(root22_ptr, 7, 8, 6);
+  EXPECT_COUNT(root221_ptr, -1, -1, 5);
+  EXPECT_COUNT(root23_ptr, 3, 4, 2);
+  EXPECT_COUNT(root231_ptr, -1, -1, 1);
+  EXPECT_COUNT(root3_ptr, -1, -1, 0);
 }
 
 }  // namespace
diff --git a/cc/layers/nine_patch_layer_impl_unittest.cc b/cc/layers/nine_patch_layer_impl_unittest.cc
index 8537ddb..3535c98 100644
--- a/cc/layers/nine_patch_layer_impl_unittest.cc
+++ b/cc/layers/nine_patch_layer_impl_unittest.cc
@@ -37,7 +37,7 @@
                               bool fill_center,
                               size_t expected_quad_size) {
   scoped_ptr<RenderPass> render_pass = RenderPass::Create();
-  gfx::Rect visible_content_rect(layer_size);
+  gfx::Rect visible_layer_rect(layer_size);
   gfx::Rect expected_remaining(border.x(),
                                border.y(),
                                layer_size.width() - border.width(),
@@ -52,9 +52,8 @@
 
   scoped_ptr<NinePatchLayerImpl> layer =
       NinePatchLayerImpl::Create(host_impl.active_tree(), 1);
-  layer->draw_properties().visible_content_rect = visible_content_rect;
+  layer->draw_properties().visible_layer_rect = visible_layer_rect;
   layer->SetBounds(layer_size);
-  layer->SetContentBounds(layer_size);
   layer->SetHasRenderSurface(true);
   layer->draw_properties().render_target = layer.get();
 
@@ -73,11 +72,11 @@
   const QuadList& quads = render_pass->quad_list;
   EXPECT_EQ(expected_quad_size, quads.size());
 
-  Region remaining(visible_content_rect);
+  Region remaining(visible_layer_rect);
   for (auto iter = quads.cbegin(); iter != quads.cend(); ++iter) {
     gfx::Rect quad_rect = iter->rect;
 
-    EXPECT_TRUE(visible_content_rect.Contains(quad_rect)) << iter.index();
+    EXPECT_TRUE(visible_layer_rect.Contains(quad_rect)) << iter.index();
     EXPECT_TRUE(remaining.Contains(quad_rect)) << iter.index();
     remaining.Subtract(Region(quad_rect));
   }
@@ -231,7 +230,6 @@
   NinePatchLayerImpl* nine_patch_layer_impl =
       impl.AddChildToRoot<NinePatchLayerImpl>();
   nine_patch_layer_impl->SetBounds(layer_size);
-  nine_patch_layer_impl->SetContentBounds(layer_size);
   nine_patch_layer_impl->SetDrawsContent(true);
   nine_patch_layer_impl->SetUIResourceId(uid);
   nine_patch_layer_impl->SetImageBounds(gfx::Size(10, 10));
@@ -254,7 +252,7 @@
 
   {
     SCOPED_TRACE("Full occlusion");
-    gfx::Rect occluded(nine_patch_layer_impl->visible_content_rect());
+    gfx::Rect occluded(nine_patch_layer_impl->visible_layer_rect());
     impl.AppendQuadsWithOcclusion(nine_patch_layer_impl, occluded);
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
@@ -304,7 +302,6 @@
   NinePatchLayerImpl *nine_patch_layer_impl =
       impl.AddChildToRoot<NinePatchLayerImpl>();
   nine_patch_layer_impl->SetBounds(layer_size);
-  nine_patch_layer_impl->SetContentBounds(layer_size);
   nine_patch_layer_impl->SetDrawsContent(true);
 
   impl.CalcDrawProps(viewport_size);
diff --git a/cc/layers/nine_patch_layer_unittest.cc b/cc/layers/nine_patch_layer_unittest.cc
index 579808b..20082a0 100644
--- a/cc/layers/nine_patch_layer_unittest.cc
+++ b/cc/layers/nine_patch_layer_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "cc/resources/prioritized_resource_manager.h"
 #include "cc/resources/resource_provider.h"
-#include "cc/resources/resource_update_queue.h"
 #include "cc/resources/scoped_ui_resource.h"
 #include "cc/test/fake_layer_tree_host.h"
 #include "cc/test/fake_layer_tree_host_client.h"
@@ -58,10 +57,9 @@
   Mock::VerifyAndClearExpectations(layer_tree_host_.get());
   EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host_.get());
 
-  ResourceUpdateQueue queue;
   gfx::Rect screen_space_clip_rect;
   test_layer->SavePaintProperties();
-  test_layer->Update(&queue);
+  test_layer->Update();
 
   EXPECT_FALSE(test_layer->DrawsContent());
 
@@ -73,7 +71,7 @@
   test_layer->SetAperture(aperture);
   test_layer->SetUIResourceId(resource->id());
   test_layer->SetFillCenter(fill_center);
-  test_layer->Update(&queue);
+  test_layer->Update();
 
   EXPECT_TRUE(test_layer->DrawsContent());
 }
diff --git a/cc/layers/painted_scrollbar_layer.cc b/cc/layers/painted_scrollbar_layer.cc
index 53b45c6..f537ffc 100644
--- a/cc/layers/painted_scrollbar_layer.cc
+++ b/cc/layers/painted_scrollbar_layer.cc
@@ -227,11 +227,11 @@
   }
 }
 
-bool PaintedScrollbarLayer::Update(ResourceUpdateQueue* queue) {
+bool PaintedScrollbarLayer::Update() {
   {
     base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
                                                   true);
-    Layer::Update(queue);
+    Layer::Update();
     UpdateInternalContentScale();
   }
 
diff --git a/cc/layers/painted_scrollbar_layer.h b/cc/layers/painted_scrollbar_layer.h
index 2b5c263..1c85a557 100644
--- a/cc/layers/painted_scrollbar_layer.h
+++ b/cc/layers/painted_scrollbar_layer.h
@@ -37,7 +37,7 @@
   ScrollbarOrientation orientation() const override;
 
   // Layer interface
-  bool Update(ResourceUpdateQueue* queue) override;
+  bool Update() override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
   void PushPropertiesTo(LayerImpl* layer) override;
   void PushScrollClipPropertiesTo(LayerImpl* layer) override;
diff --git a/cc/layers/painted_scrollbar_layer_impl_unittest.cc b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
index 3fb3fb2..eac32b6 100644
--- a/cc/layers/painted_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
@@ -38,7 +38,6 @@
   PaintedScrollbarLayerImpl* scrollbar_layer_impl =
       impl.AddChildToRoot<PaintedScrollbarLayerImpl>(orientation);
   scrollbar_layer_impl->SetBounds(layer_size);
-  scrollbar_layer_impl->SetContentBounds(layer_size);
   scrollbar_layer_impl->SetContentsOpaque(true);
   scrollbar_layer_impl->set_internal_contents_scale_and_bounds(
       scale, scaled_layer_size);
@@ -89,7 +88,7 @@
 
   {
     SCOPED_TRACE("Full occlusion");
-    gfx::Rect occluded(scrollbar_layer_impl->visible_content_rect());
+    gfx::Rect occluded(scrollbar_layer_impl->visible_layer_rect());
     impl.AppendQuadsWithOcclusion(scrollbar_layer_impl, occluded);
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
diff --git a/cc/layers/picture_image_layer_impl_unittest.cc b/cc/layers/picture_image_layer_impl_unittest.cc
index cef2d5e35..ff7e9ace 100644
--- a/cc/layers/picture_image_layer_impl_unittest.cc
+++ b/cc/layers/picture_image_layer_impl_unittest.cc
@@ -59,7 +59,6 @@
         new TestablePictureImageLayerImpl(tree, id);
     layer->raster_source_ = FakePicturePileImpl::CreateInfiniteFilledPile();
     layer->SetBounds(layer->raster_source_->GetSize());
-    layer->SetContentBounds(layer->raster_source_->GetSize());
     return make_scoped_ptr(layer);
   }
 
@@ -77,7 +76,7 @@
         maximum_animation_contents_scale;
     layer->draw_properties().screen_space_transform_is_animating =
         animating_transform_to_screen;
-    layer->draw_properties().visible_content_rect = viewport_rect;
+    layer->draw_properties().visible_layer_rect = viewport_rect;
     bool resourceless_software_draw = false;
     layer->UpdateTiles(resourceless_software_draw);
   }
@@ -96,9 +95,6 @@
   gfx::Rect viewport(100, 200);
   SetupDrawPropertiesAndUpdateTiles(
       layer.get(), 2.f, 3.f, 4.f, 1.f, false, viewport);
-
-  EXPECT_FLOAT_EQ(1.f, layer->contents_scale_x());
-  EXPECT_FLOAT_EQ(1.f, layer->contents_scale_y());
   EXPECT_FLOAT_EQ(1.f, layer->MaximumTilingContentsScale());
 }
 
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index 028cbbe2..53d5d97d4 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -113,15 +113,14 @@
   Layer::SetNeedsDisplayRect(layer_rect);
 }
 
-bool PictureLayer::Update(ResourceUpdateQueue* queue) {
+bool PictureLayer::Update() {
   update_source_frame_number_ = layer_tree_host()->source_frame_number();
-  bool updated = Layer::Update(queue);
+  bool updated = Layer::Update();
 
-  gfx::Rect visible_layer_rect = gfx::ScaleToEnclosingRect(
-      visible_content_rect(), 1.f / contents_scale_x());
+  gfx::Rect update_rect = visible_layer_rect();
   gfx::Size layer_size = paint_properties().bounds;
 
-  if (last_updated_visible_content_rect_ == visible_content_rect() &&
+  if (last_updated_visible_layer_rect_ == update_rect &&
       recording_source_->GetSize() == layer_size &&
       pending_invalidation_.IsEmpty()) {
     // Only early out if the visible content rect of this layer hasn't changed.
@@ -146,7 +145,7 @@
   if (layer_tree_host()->settings().record_full_layer) {
     // Workaround for http://crbug.com/235910 - to retain backwards compat
     // the full page content must always be provided in the picture layer.
-    visible_layer_rect = gfx::Rect(layer_size);
+    update_rect = gfx::Rect(layer_size);
   }
 
   // UpdateAndExpandInvalidation will give us an invalidation that covers
@@ -155,9 +154,9 @@
   // for them.
   DCHECK(client_);
   updated |= recording_source_->UpdateAndExpandInvalidation(
-      client_, &recording_invalidation_, layer_size, visible_layer_rect,
+      client_, &recording_invalidation_, layer_size, update_rect,
       update_source_frame_number_, RecordingSource::RECORD_NORMALLY);
-  last_updated_visible_content_rect_ = visible_content_rect();
+  last_updated_visible_layer_rect_ = visible_layer_rect();
 
   if (updated) {
     SetNeedsPushProperties();
diff --git a/cc/layers/picture_layer.h b/cc/layers/picture_layer.h
index 8c7e2e8..771484c 100644
--- a/cc/layers/picture_layer.h
+++ b/cc/layers/picture_layer.h
@@ -30,7 +30,7 @@
   void SetLayerTreeHost(LayerTreeHost* host) override;
   void PushPropertiesTo(LayerImpl* layer) override;
   void SetNeedsDisplayRect(const gfx::Rect& layer_rect) override;
-  bool Update(ResourceUpdateQueue* queue) override;
+  bool Update() override;
   void SetIsMask(bool is_mask) override;
   skia::RefPtr<SkPicture> GetPicture() const override;
   bool IsSuitableForGpuRasterization() const override;
@@ -64,7 +64,7 @@
   InvalidationRegion pending_invalidation_;
   // Invalidation from the last time update was called.
   Region recording_invalidation_;
-  gfx::Rect last_updated_visible_content_rect_;
+  gfx::Rect last_updated_visible_layer_rect_;
 
   int update_source_frame_number_;
   bool is_mask_;
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index b4767141..82ae6fc 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -161,7 +161,7 @@
 
     SolidColorLayerImpl::AppendSolidQuads(
         render_pass, draw_properties().occlusion_in_content_space,
-        shared_quad_state, visible_content_rect(),
+        shared_quad_state, visible_layer_rect(),
         raster_source_->GetSolidColor(), append_quads_data);
     return;
   }
@@ -171,22 +171,22 @@
   Occlusion scaled_occlusion =
       draw_properties()
           .occlusion_in_content_space.GetOcclusionWithGivenDrawTransform(
-              shared_quad_state->content_to_target_transform);
+              shared_quad_state->quad_to_target_transform);
 
   if (current_draw_mode_ == DRAW_MODE_RESOURCELESS_SOFTWARE) {
     AppendDebugBorderQuad(
-        render_pass, shared_quad_state->content_bounds, shared_quad_state,
+        render_pass, shared_quad_state->quad_layer_bounds, shared_quad_state,
         append_quads_data, DebugColors::DirectPictureBorderColor(),
         DebugColors::DirectPictureBorderWidth(layer_tree_impl()));
 
-    gfx::Rect geometry_rect = shared_quad_state->visible_content_rect;
+    gfx::Rect geometry_rect = shared_quad_state->visible_quad_layer_rect;
     gfx::Rect opaque_rect = contents_opaque() ? geometry_rect : gfx::Rect();
     gfx::Rect visible_geometry_rect =
         scaled_occlusion.GetUnoccludedContentRect(geometry_rect);
     if (visible_geometry_rect.IsEmpty())
       return;
 
-    gfx::Rect quad_content_rect = shared_quad_state->visible_content_rect;
+    gfx::Rect quad_content_rect = shared_quad_state->visible_quad_layer_rect;
     gfx::Size texture_size = quad_content_rect.size();
     gfx::RectF texture_rect = gfx::RectF(texture_size);
 
@@ -200,13 +200,13 @@
     return;
   }
 
-  AppendDebugBorderQuad(render_pass, shared_quad_state->content_bounds,
+  AppendDebugBorderQuad(render_pass, shared_quad_state->quad_layer_bounds,
                         shared_quad_state, append_quads_data);
 
   if (ShowDebugBorders()) {
     for (PictureLayerTilingSet::CoverageIterator iter(
              tilings_.get(), max_contents_scale,
-             shared_quad_state->visible_content_rect, ideal_contents_scale_);
+             shared_quad_state->visible_quad_layer_rect, ideal_contents_scale_);
          iter; ++iter) {
       SkColor color;
       float width;
@@ -263,7 +263,7 @@
   only_used_low_res_last_append_quads_ = true;
   for (PictureLayerTilingSet::CoverageIterator iter(
            tilings_.get(), max_contents_scale,
-           shared_quad_state->visible_content_rect, ideal_contents_scale_);
+           shared_quad_state->visible_quad_layer_rect, ideal_contents_scale_);
        iter; ++iter) {
     gfx::Rect geometry_rect = iter.geometry_rect();
     gfx::Rect opaque_rect = contents_opaque() ? geometry_rect : gfx::Rect();
@@ -272,7 +272,7 @@
     if (visible_geometry_rect.IsEmpty())
       continue;
 
-    append_quads_data->visible_content_area +=
+    append_quads_data->visible_layer_area +=
         visible_geometry_rect.width() * visible_geometry_rect.height();
 
     bool has_draw_quad = false;
@@ -376,17 +376,14 @@
 
   // Aggressively remove any tilings that are not seen to save memory. Note
   // that this is at the expense of doing cause more frequent re-painting. A
-  // better scheme would be to maintain a tighter visible_content_rect for the
+  // better scheme would be to maintain a tighter visible_layer_rect for the
   // finer tilings.
   CleanUpTilingsOnActiveLayer(last_append_quads_tilings_);
 }
 
 bool PictureLayerImpl::UpdateTiles(bool resourceless_software_draw) {
-  DCHECK_EQ(1.f, contents_scale_x());
-  DCHECK_EQ(1.f, contents_scale_y());
-
   if (!resourceless_software_draw) {
-    visible_rect_for_tile_priority_ = visible_content_rect();
+    visible_rect_for_tile_priority_ = visible_layer_rect();
   }
 
   if (!CanHaveTilings()) {
@@ -1195,7 +1192,7 @@
   MathUtil::AddToTracedValue("tile_priority_rect",
                              viewport_rect_for_tile_priority_in_content_space_,
                              state);
-  MathUtil::AddToTracedValue("visible_rect", visible_content_rect(), state);
+  MathUtil::AddToTracedValue("visible_rect", visible_layer_rect(), state);
 
   state->BeginArray("pictures");
   raster_source_->AsValueInto(state);
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 18ca3a9d..83e37a0 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -223,7 +223,6 @@
     pending_root->SetHasRenderSurface(true);
     // The bounds() just mirror the pile size.
     pending_layer->SetBounds(raster_source->GetSize());
-    pending_layer->SetContentBounds(raster_source->GetSize());
     pending_layer->SetRasterSourceOnPending(raster_source, invalidation);
 
     pending_root->AddChild(pending_layer.Pass());
@@ -544,7 +543,7 @@
                                         viewport,
                                         transform,
                                         resourceless_software_draw);
-  active_layer_->draw_properties().visible_content_rect = viewport;
+  active_layer_->draw_properties().visible_layer_rect = viewport;
   active_layer_->draw_properties().screen_space_transform = transform;
   active_layer_->UpdateTiles(resourceless_software_draw);
 
@@ -560,7 +559,7 @@
   resourceless_software_draw = true;
   viewport = gfx::ScaleToEnclosingRect(viewport, 2);
   transform.Translate(1.f, 1.f);
-  active_layer_->draw_properties().visible_content_rect = viewport;
+  active_layer_->draw_properties().visible_layer_rect = viewport;
   active_layer_->draw_properties().screen_space_transform = transform;
   host_impl_.SetExternalDrawConstraints(transform,
                                         viewport,
@@ -1295,7 +1294,6 @@
       FakePictureLayerImpl::CreateMaskWithRasterSource(
           host_impl_.pending_tree(), 3, pending_pile);
   mask->SetBounds(layer_bounds);
-  mask->SetContentBounds(layer_bounds);
   mask->SetDrawsContent(true);
 
   SetupDrawPropertiesAndUpdateTiles(
@@ -1319,7 +1317,6 @@
       FakePictureLayerImpl::CreateMaskWithRasterSource(
           host_impl_.pending_tree(), 3, valid_pile);
   mask_ptr->SetBounds(layer_bounds);
-  mask_ptr->SetContentBounds(layer_bounds);
   mask_ptr->SetDrawsContent(true);
   pending_layer_->SetMaskLayer(mask_ptr.Pass());
   pending_layer_->SetHasRenderSurface(true);
@@ -1371,7 +1368,6 @@
 
   SetupPendingTree(huge_pile);
   pending_mask->SetBounds(huge_bounds);
-  pending_mask->SetContentBounds(huge_bounds);
   pending_mask->SetRasterSourceOnPending(huge_pile, Region());
 
   host_impl_.AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1));
@@ -1424,7 +1420,6 @@
 
   SetupPendingTree(extra_huge_pile);
   pending_mask->SetBounds(extra_huge_bounds);
-  pending_mask->SetContentBounds(extra_huge_bounds);
   pending_mask->SetRasterSourceOnPending(extra_huge_pile, Region());
 
   EXPECT_FALSE(pending_mask->CanHaveTilings());
@@ -1451,7 +1446,6 @@
       FakePictureLayerImpl::CreateMaskWithRasterSource(
           host_impl_.pending_tree(), 3, valid_pile);
   mask_ptr->SetBounds(layer_bounds);
-  mask_ptr->SetContentBounds(layer_bounds);
   mask_ptr->SetDrawsContent(true);
   pending_layer_->SetMaskLayer(mask_ptr.Pass());
   pending_layer_->SetHasRenderSurface(true);
@@ -1627,8 +1621,7 @@
   gfx::Rect layer_invalidation(150, 200, 30, 180);
   SetupTreesWithInvalidation(pending_pile, active_pile, layer_invalidation);
 
-  active_layer_->draw_properties().visible_content_rect =
-      gfx::Rect(layer_bounds);
+  active_layer_->draw_properties().visible_layer_rect = gfx::Rect(layer_bounds);
 
   AppendQuadsData data;
   active_layer_->WillDraw(DRAW_MODE_RESOURCELESS_SOFTWARE, nullptr);
@@ -1658,7 +1651,7 @@
 
   SetupTrees(pending_pile, active_pile);
 
-  active_layer_->draw_properties().visible_content_rect = visible_rect;
+  active_layer_->draw_properties().visible_layer_rect = visible_rect;
 
   AppendQuadsData data;
   active_layer_->WillDraw(DRAW_MODE_SOFTWARE, nullptr);
@@ -1750,7 +1743,7 @@
   gfx::Size tile_size(100, 100);
   gfx::Size layer_bounds(400, 400);
   gfx::Rect external_viewport_for_tile_priority(400, 200);
-  gfx::Rect visible_content_rect(200, 400);
+  gfx::Rect visible_layer_rect(200, 400);
 
   scoped_refptr<FakePicturePileImpl> active_pile =
       FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
@@ -1778,7 +1771,7 @@
 
   // Set visible content rect that is different from
   // external_viewport_for_tile_priority.
-  pending_layer_->draw_properties().visible_content_rect = visible_content_rect;
+  pending_layer_->draw_properties().visible_layer_rect = visible_layer_rect;
   host_impl_.AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(200));
   pending_layer_->UpdateTiles(resourceless_software_draw);
 
@@ -1786,7 +1779,7 @@
   // activation.
   gfx::Rect viewport_for_tile_priority =
       pending_layer_->viewport_rect_for_tile_priority_in_content_space();
-  viewport_for_tile_priority.Intersect(pending_layer_->visible_content_rect());
+  viewport_for_tile_priority.Intersect(pending_layer_->visible_layer_rect());
 
   EXPECT_TRUE(pending_layer_->HighResTiling()->AllTilesForTesting().empty());
 
@@ -1815,7 +1808,7 @@
   // Activate and draw active layer.
   host_impl_.ActivateSyncTree();
   host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
-  active_layer_->draw_properties().visible_content_rect = visible_content_rect;
+  active_layer_->draw_properties().visible_layer_rect = visible_layer_rect;
 
   scoped_ptr<RenderPass> render_pass = RenderPass::Create();
   AppendQuadsData data;
@@ -2921,7 +2914,7 @@
   // No NOW tiles.
   host_impl_.AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(200));
 
-  pending_layer_->draw_properties().visible_content_rect =
+  pending_layer_->draw_properties().visible_layer_rect =
       gfx::Rect(1100, 1100, 500, 500);
   bool resourceless_software_draw = false;
   pending_layer_->UpdateTiles(resourceless_software_draw);
@@ -2951,7 +2944,7 @@
 
   host_impl_.AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(200));
 
-  pending_layer_->draw_properties().visible_content_rect =
+  pending_layer_->draw_properties().visible_layer_rect =
       gfx::Rect(0, 0, 500, 500);
   pending_layer_->UpdateTiles(resourceless_software_draw);
 
@@ -3071,11 +3064,8 @@
   for (size_t i = 0; i < pending_layer_->num_tilings(); ++i) {
     PictureLayerTiling* tiling = pending_layer_->tilings()->tiling_at(i);
     for (PictureLayerTiling::CoverageIterator iter(
-             tiling,
-             pending_layer_->contents_scale_x(),
-             pending_layer_->visible_content_rect());
-         iter;
-         ++iter) {
+             tiling, 1.f, pending_layer_->visible_layer_rect());
+         iter; ++iter) {
       if (mark_required) {
         number_of_marked_tiles++;
         iter->set_required_for_activation(true);
@@ -3217,7 +3207,7 @@
 
   {
     SCOPED_TRACE("Full occlusion");
-    gfx::Rect occluded(active_layer_->visible_content_rect());
+    gfx::Rect occluded(active_layer_->visible_layer_rect());
     impl.AppendQuadsWithOcclusion(active_layer_, occluded);
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
@@ -3576,7 +3566,7 @@
                                         viewport,
                                         transform,
                                         resourceless_software_draw);
-  active_layer_->draw_properties().visible_content_rect = viewport;
+  active_layer_->draw_properties().visible_layer_rect = viewport;
   active_layer_->draw_properties().screen_space_transform = transform;
   active_layer_->UpdateTiles(resourceless_software_draw);
 
@@ -3592,7 +3582,7 @@
   resourceless_software_draw = true;
   viewport = gfx::ScaleToEnclosingRect(viewport, 2);
   transform.Translate(1.f, 1.f);
-  active_layer_->draw_properties().visible_content_rect = viewport;
+  active_layer_->draw_properties().visible_layer_rect = viewport;
   active_layer_->draw_properties().screen_space_transform = transform;
   host_impl_.SetExternalDrawConstraints(transform,
                                         viewport,
@@ -3807,21 +3797,21 @@
 
   // SharedQuadState should have be of size 1, as we are doing AppenQuad once.
   EXPECT_EQ(1u, render_pass->shared_quad_state_list.size());
-  // The content_to_target_transform should be scaled by the
+  // The quad_to_target_transform should be scaled by the
   // MaximumTilingContentsScale on the layer.
   EXPECT_EQ(scaled_draw_transform.ToString(),
             render_pass->shared_quad_state_list.front()
-                ->content_to_target_transform.ToString());
+                ->quad_to_target_transform.ToString());
   // The content_bounds should be scaled by the
   // MaximumTilingContentsScale on the layer.
-  EXPECT_EQ(
-      gfx::Size(2500u, 5000u).ToString(),
-      render_pass->shared_quad_state_list.front()->content_bounds.ToString());
-  // The visible_content_rect should be scaled by the
+  EXPECT_EQ(gfx::Size(2500u, 5000u).ToString(),
+            render_pass->shared_quad_state_list.front()
+                ->quad_layer_bounds.ToString());
+  // The visible_layer_rect should be scaled by the
   // MaximumTilingContentsScale on the layer.
   EXPECT_EQ(gfx::Rect(0u, 0u, 2500u, 5000u).ToString(),
             render_pass->shared_quad_state_list.front()
-                ->visible_content_rect.ToString());
+                ->visible_quad_layer_rect.ToString());
 }
 
 class PictureLayerImplTestWithDelegatingRenderer : public PictureLayerImplTest {
@@ -3965,7 +3955,7 @@
     // Some tiles may not be visible (i.e. outside the viewport). The rest are
     // visible and at least partially unoccluded, verified by the above expect.
     bool tile_is_visible =
-        tile->content_rect().Intersects(pending_layer_->visible_content_rect());
+        tile->content_rect().Intersects(pending_layer_->visible_layer_rect());
     if (tile_is_visible)
       unoccluded_tile_count++;
     queue->Pop();
@@ -3976,7 +3966,6 @@
   pending_layer_->AddChild(LayerImpl::Create(host_impl_.pending_tree(), 1));
   LayerImpl* layer1 = pending_layer_->children()[0];
   layer1->SetBounds(layer_bounds);
-  layer1->SetContentBounds(layer_bounds);
   layer1->SetDrawsContent(true);
   layer1->SetContentsOpaque(true);
   layer1->SetPosition(occluding_layer_position);
@@ -3995,7 +3984,7 @@
     EXPECT_FALSE(prioritized_tile.is_occluded());
 
     bool tile_is_visible =
-        tile->content_rect().Intersects(pending_layer_->visible_content_rect());
+        tile->content_rect().Intersects(pending_layer_->visible_layer_rect());
     if (tile_is_visible)
       unoccluded_tile_count++;
     queue->Pop();
@@ -4018,7 +4007,7 @@
     EXPECT_FALSE(prioritized_tile.is_occluded());
 
     bool tile_is_visible =
-        tile->content_rect().Intersects(pending_layer_->visible_content_rect());
+        tile->content_rect().Intersects(pending_layer_->visible_layer_rect());
     if (tile_is_visible)
       unoccluded_tile_count++;
     queue->Pop();
@@ -4049,12 +4038,9 @@
         tiling->UpdateAndGetAllPrioritizedTilesForTesting();
 
     occluded_tile_count = 0;
-    for (PictureLayerTiling::CoverageIterator iter(
-             tiling,
-             pending_layer_->contents_scale_x(),
-             gfx::Rect(layer_bounds));
-         iter;
-         ++iter) {
+    for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f,
+                                                   gfx::Rect(layer_bounds));
+         iter; ++iter) {
       if (!*iter)
         continue;
       const Tile* tile = *iter;
@@ -4072,7 +4058,6 @@
   pending_layer_->AddChild(LayerImpl::Create(host_impl_.pending_tree(), 1));
   LayerImpl* layer1 = pending_layer_->children()[0];
   layer1->SetBounds(layer_bounds);
-  layer1->SetContentBounds(layer_bounds);
   layer1->SetDrawsContent(true);
   layer1->SetContentsOpaque(true);
   layer1->SetPosition(occluding_layer_position);
@@ -4087,12 +4072,9 @@
         tiling->UpdateAndGetAllPrioritizedTilesForTesting();
 
     occluded_tile_count = 0;
-    for (PictureLayerTiling::CoverageIterator iter(
-             tiling,
-             pending_layer_->contents_scale_x(),
-             gfx::Rect(layer_bounds));
-         iter;
-         ++iter) {
+    for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f,
+                                                   gfx::Rect(layer_bounds));
+         iter; ++iter) {
       if (!*iter)
         continue;
       const Tile* tile = *iter;
@@ -4126,12 +4108,9 @@
         tiling->UpdateAndGetAllPrioritizedTilesForTesting();
 
     occluded_tile_count = 0;
-    for (PictureLayerTiling::CoverageIterator iter(
-             tiling,
-             pending_layer_->contents_scale_x(),
-             gfx::Rect(layer_bounds));
-         iter;
-         ++iter) {
+    for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f,
+                                                   gfx::Rect(layer_bounds));
+         iter; ++iter) {
       if (!*iter)
         continue;
       const Tile* tile = *iter;
@@ -4173,7 +4152,6 @@
   pending_layer_->AddChild(LayerImpl::Create(host_impl_.pending_tree(), 1));
   LayerImpl* layer1 = pending_layer_->children()[0];
   layer1->SetBounds(layer_bounds);
-  layer1->SetContentBounds(layer_bounds);
   layer1->SetDrawsContent(true);
   layer1->SetContentsOpaque(true);
   layer1->SetPosition(occluding_layer_position);
@@ -4249,7 +4227,6 @@
   pending_layer_->AddChild(LayerImpl::Create(host_impl_.pending_tree(), 2));
   LayerImpl* layer1 = pending_layer_->children()[0];
   layer1->SetBounds(layer_bounds);
-  layer1->SetContentBounds(layer_bounds);
   layer1->SetDrawsContent(true);
   layer1->SetContentsOpaque(true);
   layer1->SetPosition(occluding_layer_position);
@@ -4261,10 +4238,9 @@
     auto prioritized_tiles =
         tiling->UpdateAndGetAllPrioritizedTilesForTesting();
 
-    for (
-        PictureLayerTiling::CoverageIterator iter(
-            tiling, active_layer_->contents_scale_x(), gfx::Rect(layer_bounds));
-        iter; ++iter) {
+    for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f,
+                                                   gfx::Rect(layer_bounds));
+         iter; ++iter) {
       if (!*iter)
         continue;
       const Tile* tile = *iter;
@@ -4286,12 +4262,9 @@
     auto prioritized_tiles =
         tiling->UpdateAndGetAllPrioritizedTilesForTesting();
 
-    for (PictureLayerTiling::CoverageIterator iter(
-             tiling,
-             active_layer_->contents_scale_x(),
-             gfx::Rect(layer_bounds));
-         iter;
-         ++iter) {
+    for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f,
+                                                   gfx::Rect(layer_bounds));
+         iter; ++iter) {
       if (!*iter)
         continue;
       const Tile* tile = *iter;
@@ -4343,7 +4316,6 @@
   pending_layer_->AddChild(LayerImpl::Create(host_impl_.pending_tree(), 2));
   LayerImpl* active_occluding_layer = pending_layer_->children()[0];
   active_occluding_layer->SetBounds(layer_bounds);
-  active_occluding_layer->SetContentBounds(layer_bounds);
   active_occluding_layer->SetDrawsContent(true);
   active_occluding_layer->SetContentsOpaque(true);
   active_occluding_layer->SetPosition(active_occluding_layer_position);
@@ -4358,7 +4330,6 @@
   pending_layer_->AddChild(LayerImpl::Create(host_impl_.pending_tree(), 3));
   LayerImpl* pending_occluding_layer = pending_layer_->children()[0];
   pending_occluding_layer->SetBounds(layer_bounds);
-  pending_occluding_layer->SetContentBounds(layer_bounds);
   pending_occluding_layer->SetDrawsContent(true);
   pending_occluding_layer->SetContentsOpaque(true);
   pending_occluding_layer->SetPosition(pending_occluding_layer_position);
@@ -4411,12 +4382,9 @@
         tiling->UpdateAndGetAllPrioritizedTilesForTesting();
 
     size_t occluded_tile_count_on_active = 0u;
-    for (PictureLayerTiling::CoverageIterator iter(
-             tiling,
-             pending_layer_->contents_scale_x(),
-             gfx::Rect(layer_bounds));
-         iter;
-         ++iter) {
+    for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f,
+                                                   gfx::Rect(layer_bounds));
+         iter; ++iter) {
       Tile* tile = *iter;
 
       if (!tile)
@@ -4637,7 +4605,7 @@
   host_impl_.SetRequiresHighResToDraw();
 
   // Update tiles.
-  pending_layer_->draw_properties().visible_content_rect = viewport;
+  pending_layer_->draw_properties().visible_layer_rect = viewport;
   pending_layer_->draw_properties().screen_space_transform = transform;
   SetupDrawPropertiesAndUpdateTiles(pending_layer_, 1.f, 1.f, 1.f, 1.f, 0.f,
                                     false);
@@ -4651,7 +4619,7 @@
   viewport = gfx::Rect(0, 2000, 100, 100);
 
   // Update tiles.
-  pending_layer_->draw_properties().visible_content_rect = viewport;
+  pending_layer_->draw_properties().visible_layer_rect = viewport;
   pending_layer_->draw_properties().screen_space_transform = transform;
   SetupDrawPropertiesAndUpdateTiles(pending_layer_, 1.f, 1.f, 1.f, 1.f, 0.f,
                                     false);
diff --git a/cc/layers/picture_layer_unittest.cc b/cc/layers/picture_layer_unittest.cc
index 7bea6d8c..0133952 100644
--- a/cc/layers/picture_layer_unittest.cc
+++ b/cc/layers/picture_layer_unittest.cc
@@ -47,9 +47,7 @@
   host->SetRootLayer(layer);
   layer->SetIsDrawable(true);
   layer->SavePaintProperties();
-
-  scoped_ptr<ResourceUpdateQueue> queue(new ResourceUpdateQueue);
-  layer->Update(queue.get());
+  layer->Update();
 
   EXPECT_EQ(0, host->source_frame_number());
   host->CommitComplete();
diff --git a/cc/layers/render_surface.h b/cc/layers/render_surface.h
index ac58903..32276b6c 100644
--- a/cc/layers/render_surface.h
+++ b/cc/layers/render_surface.h
@@ -19,8 +19,6 @@
 namespace cc {
 
 class Layer;
-template <typename LayerType>
-class LayerIterator;
 
 class CC_EXPORT RenderSurface {
  public:
@@ -116,8 +114,6 @@
   void ClearLayerLists();
 
  private:
-  friend class LayerIterator<Layer>;
-
   Layer* owning_layer_;
 
   // Uses this surface's space.
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
index 22f311d..ebb3016 100644
--- a/cc/layers/render_surface_impl.cc
+++ b/cc/layers/render_surface_impl.cc
@@ -157,9 +157,9 @@
                                     LayerImpl* mask_layer,
                                     AppendQuadsData* append_quads_data,
                                     RenderPassId render_pass_id) {
-  gfx::Rect visible_content_rect =
+  gfx::Rect visible_layer_rect =
       occlusion_in_content_space.GetUnoccludedContentRect(content_rect_);
-  if (visible_content_rect.IsEmpty())
+  if (visible_layer_rect.IsEmpty())
     return;
 
   SharedQuadState* shared_quad_state =
@@ -173,7 +173,7 @@
     DebugBorderDrawQuad* debug_border_quad =
         render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
     debug_border_quad->SetNew(shared_quad_state, content_rect_,
-                              visible_content_rect, debug_border_color,
+                              visible_layer_rect, debug_border_color,
                               debug_border_width);
   }
 
@@ -197,19 +197,12 @@
   DCHECK(owning_layer_->draw_properties().target_space_transform.IsScale2d());
   gfx::Vector2dF owning_layer_to_target_scale =
       owning_layer_->draw_properties().target_space_transform.Scale2d();
-  owning_layer_to_target_scale.Scale(owning_layer_->contents_scale_x(),
-                                     owning_layer_->contents_scale_y());
 
   RenderPassDrawQuad* quad =
       render_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
-  quad->SetNew(shared_quad_state,
-               content_rect_,
-               visible_content_rect,
-               render_pass_id,
-               mask_resource_id,
-               mask_uv_scale,
-               mask_texture_size,
-               owning_layer_->filters(),
+  quad->SetNew(shared_quad_state, content_rect_, visible_layer_rect,
+               render_pass_id, mask_resource_id, mask_uv_scale,
+               mask_texture_size, owning_layer_->filters(),
                owning_layer_to_target_scale,
                owning_layer_->background_filters());
 }
diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h
index 84a574e..80a4f72 100644
--- a/cc/layers/render_surface_impl.h
+++ b/cc/layers/render_surface_impl.h
@@ -27,7 +27,6 @@
 class RenderPassId;
 class RenderPassSink;
 class LayerImpl;
-template <typename LayerType>
 class LayerIterator;
 
 struct AppendQuadsData;
@@ -194,7 +193,7 @@
   int target_render_surface_layer_index_history_;
   size_t current_layer_index_history_;
 
-  friend class LayerIterator<LayerImpl>;
+  friend class LayerIterator;
 
   DISALLOW_COPY_AND_ASSIGN(RenderSurfaceImpl);
 };
diff --git a/cc/layers/render_surface_impl_unittest.cc b/cc/layers/render_surface_impl_unittest.cc
index 9934cd6..ac406758 100644
--- a/cc/layers/render_surface_impl_unittest.cc
+++ b/cc/layers/render_surface_impl_unittest.cc
@@ -18,7 +18,6 @@
 
   LayerImpl* owning_layer_impl = impl.AddChildToRoot<LayerImpl>();
   owning_layer_impl->SetBounds(layer_size);
-  owning_layer_impl->SetContentBounds(layer_size);
   owning_layer_impl->SetDrawsContent(true);
   owning_layer_impl->SetHasRenderSurface(true);
 
@@ -39,7 +38,7 @@
 
   {
     SCOPED_TRACE("Full occlusion");
-    gfx::Rect occluded(owning_layer_impl->visible_content_rect());
+    gfx::Rect occluded(owning_layer_impl->visible_layer_rect());
     impl.AppendSurfaceQuadsWithOcclusion(render_surface_impl, occluded);
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
diff --git a/cc/layers/render_surface_unittest.cc b/cc/layers/render_surface_unittest.cc
index 96f8532f..5ecf2f7b 100644
--- a/cc/layers/render_surface_unittest.cc
+++ b/cc/layers/render_surface_unittest.cc
@@ -126,11 +126,12 @@
 
   EXPECT_EQ(
       30.0,
-      shared_quad_state->content_to_target_transform.matrix().getDouble(0, 3));
+      shared_quad_state->quad_to_target_transform.matrix().getDouble(0, 3));
   EXPECT_EQ(
       40.0,
-      shared_quad_state->content_to_target_transform.matrix().getDouble(1, 3));
-  EXPECT_EQ(content_rect, gfx::Rect(shared_quad_state->visible_content_rect));
+      shared_quad_state->quad_to_target_transform.matrix().getDouble(1, 3));
+  EXPECT_EQ(content_rect,
+            gfx::Rect(shared_quad_state->visible_quad_layer_rect));
   EXPECT_EQ(1.f, shared_quad_state->opacity);
   EXPECT_EQ(blend_mode, shared_quad_state->blend_mode);
 }
diff --git a/cc/layers/scrollbar_layer_impl_base.cc b/cc/layers/scrollbar_layer_impl_base.cc
index 35e716bd..1d07a83 100644
--- a/cc/layers/scrollbar_layer_impl_base.cc
+++ b/cc/layers/scrollbar_layer_impl_base.cc
@@ -94,16 +94,6 @@
   ScrollbarParametersDidChange(false);
 }
 
-gfx::Rect ScrollbarLayerImplBase::ScrollbarLayerRectToContentRect(
-    const gfx::RectF& layer_rect) const {
-  // Don't intersect with the bounds as in LayerRectToContentRect() because
-  // layer_rect here might be in coordinates of the containing layer.
-  gfx::RectF content_rect = gfx::ScaleRect(layer_rect,
-      contents_scale_x(),
-      contents_scale_y());
-  return gfx::ToEnclosingRect(content_rect);
-}
-
 bool ScrollbarLayerImplBase::SetCurrentPos(float current_pos) {
   if (current_pos_ == current_pos)
     return false;
@@ -249,7 +239,7 @@
         thumb_length);
   }
 
-  return ScrollbarLayerRectToContentRect(thumb_rect);
+  return gfx::ToEnclosingRect(thumb_rect);
 }
 
 void ScrollbarLayerImplBase::ScrollbarParametersDidChange(bool on_resize) {
diff --git a/cc/layers/scrollbar_layer_impl_base.h b/cc/layers/scrollbar_layer_impl_base.h
index d905c88..da7fa52 100644
--- a/cc/layers/scrollbar_layer_impl_base.h
+++ b/cc/layers/scrollbar_layer_impl_base.h
@@ -69,8 +69,6 @@
                          bool is_overlay);
   ~ScrollbarLayerImplBase() override;
 
-  gfx::Rect ScrollbarLayerRectToContentRect(const gfx::RectF& layer_rect) const;
-
   float visible_to_total_length_ratio() const {
     return visible_to_total_length_ratio_;
   }
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index fca8a8e..0042204 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -12,7 +12,6 @@
 #include "cc/layers/solid_color_scrollbar_layer.h"
 #include "cc/layers/solid_color_scrollbar_layer_impl.h"
 #include "cc/quads/solid_color_draw_quad.h"
-#include "cc/resources/resource_update_queue.h"
 #include "cc/test/fake_impl_proxy.h"
 #include "cc/test/fake_layer_tree_host.h"
 #include "cc/test/fake_layer_tree_host_client.h"
@@ -432,22 +431,6 @@
     EXPECT_EQ(gfx::Rect(6, 0, 39, 3), quads.front()->rect);
   }
 
-  // Contents scale should scale the draw quad.
-  scrollbar_layer_impl->draw_properties().contents_scale_x = 2.f;
-  scrollbar_layer_impl->draw_properties().contents_scale_y = 2.f;
-  {
-    scoped_ptr<RenderPass> render_pass = RenderPass::Create();
-    AppendQuadsData data;
-    scrollbar_layer_impl->AppendQuads(render_pass.get(), &data);
-
-    const QuadList& quads = render_pass->quad_list;
-    ASSERT_EQ(1u, quads.size());
-    EXPECT_EQ(DrawQuad::SOLID_COLOR, quads.front()->material);
-    EXPECT_EQ(gfx::Rect(12, 0, 78, 6), quads.front()->rect);
-  }
-  scrollbar_layer_impl->draw_properties().contents_scale_x = 1.f;
-  scrollbar_layer_impl->draw_properties().contents_scale_y = 1.f;
-
   // For solid color scrollbars, position and size should reflect the
   // current viewport state.
   scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.2f);
@@ -730,7 +713,7 @@
     layer_tree_root->SetScrollOffset(gfx::ScrollOffset(10, 20));
     layer_tree_root->SetBounds(gfx::Size(100, 200));
     content_layer->SetBounds(gfx::Size(100, 200));
-    scrollbar_layer->draw_properties().visible_content_rect =
+    scrollbar_layer->draw_properties().visible_layer_rect =
         gfx::Rect(0, 0, 100, 200);
     scrollbar_layer->CreateRenderSurface();
     scrollbar_layer->draw_properties().render_target = scrollbar_layer.get();
@@ -738,11 +721,9 @@
     testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
     EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
 
-    ResourceUpdateQueue queue;
-
     scrollbar_layer->SavePaintProperties();
     for (int update_counter = 0; update_counter < num_updates; update_counter++)
-      scrollbar_layer->Update(&queue);
+      scrollbar_layer->Update();
 
     // A non-solid-color scrollbar should have requested two textures.
     EXPECT_EQ(expected_resources, layer_tree_host_->UIResourceCount());
@@ -795,7 +776,7 @@
   layer_tree_root->SetBounds(gfx::Size(100, 200));
   content_layer->SetBounds(gfx::Size(100, 200));
 
-  scrollbar_layer->draw_properties().visible_content_rect =
+  scrollbar_layer->draw_properties().visible_layer_rect =
       gfx::Rect(0, 0, 100, 200);
 
   scrollbar_layer->CreateRenderSurface();
@@ -804,7 +785,6 @@
   testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
   EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
 
-  ResourceUpdateQueue queue;
   size_t resource_count;
   int expected_created, expected_deleted;
   scrollbar_layer->SavePaintProperties();
@@ -812,7 +792,7 @@
   resource_count = 2;
   expected_created = 2;
   expected_deleted = 0;
-  EXPECT_TRUE(scrollbar_layer->Update(&queue));
+  EXPECT_TRUE(scrollbar_layer->Update());
   EXPECT_NE(0, scrollbar_layer->track_resource_id());
   EXPECT_NE(0, scrollbar_layer->thumb_resource_id());
   EXPECT_EQ(resource_count, layer_tree_host_->UIResourceCount());
@@ -823,7 +803,7 @@
   expected_created = 2;
   expected_deleted = 2;
   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(0, 0, 0, 0));
-  EXPECT_TRUE(scrollbar_layer->Update(&queue));
+  EXPECT_TRUE(scrollbar_layer->Update());
   EXPECT_EQ(0, scrollbar_layer->track_resource_id());
   EXPECT_EQ(0, scrollbar_layer->thumb_resource_id());
   EXPECT_EQ(resource_count, layer_tree_host_->UIResourceCount());
@@ -834,7 +814,7 @@
   expected_created = 2;
   expected_deleted = 2;
   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(0, 0, 0, 0));
-  EXPECT_FALSE(scrollbar_layer->Update(&queue));
+  EXPECT_FALSE(scrollbar_layer->Update());
   EXPECT_EQ(0, scrollbar_layer->track_resource_id());
   EXPECT_EQ(0, scrollbar_layer->thumb_resource_id());
   EXPECT_EQ(resource_count, layer_tree_host_->UIResourceCount());
@@ -845,7 +825,7 @@
   expected_created = 4;
   expected_deleted = 2;
   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
-  EXPECT_TRUE(scrollbar_layer->Update(&queue));
+  EXPECT_TRUE(scrollbar_layer->Update());
   EXPECT_NE(0, scrollbar_layer->track_resource_id());
   EXPECT_NE(0, scrollbar_layer->thumb_resource_id());
   EXPECT_EQ(resource_count, layer_tree_host_->UIResourceCount());
@@ -856,7 +836,7 @@
   expected_created = 5;
   expected_deleted = 4;
   scrollbar_layer->fake_scrollbar()->set_has_thumb(false);
-  EXPECT_TRUE(scrollbar_layer->Update(&queue));
+  EXPECT_TRUE(scrollbar_layer->Update());
   EXPECT_NE(0, scrollbar_layer->track_resource_id());
   EXPECT_EQ(0, scrollbar_layer->thumb_resource_id());
   EXPECT_EQ(resource_count, layer_tree_host_->UIResourceCount());
@@ -867,7 +847,7 @@
   expected_created = 5;
   expected_deleted = 5;
   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(0, 0, 0, 0));
-  EXPECT_TRUE(scrollbar_layer->Update(&queue));
+  EXPECT_TRUE(scrollbar_layer->Update());
   EXPECT_EQ(0, scrollbar_layer->track_resource_id());
   EXPECT_EQ(0, scrollbar_layer->thumb_resource_id());
   EXPECT_EQ(resource_count, layer_tree_host_->UIResourceCount());
@@ -879,7 +859,7 @@
   expected_deleted = 5;
   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
   scrollbar_layer->fake_scrollbar()->set_has_thumb(true);
-  EXPECT_TRUE(scrollbar_layer->Update(&queue));
+  EXPECT_TRUE(scrollbar_layer->Update());
   EXPECT_NE(0, scrollbar_layer->track_resource_id());
   EXPECT_NE(0, scrollbar_layer->thumb_resource_id());
 
@@ -889,7 +869,7 @@
   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
   scrollbar_layer->fake_scrollbar()->set_has_thumb(false);
   scrollbar_layer->SetBounds(gfx::Size(90, 15));
-  EXPECT_TRUE(scrollbar_layer->Update(&queue));
+  EXPECT_TRUE(scrollbar_layer->Update());
   EXPECT_EQ(resource_count, layer_tree_host_->UIResourceCount());
   EXPECT_EQ(expected_created, layer_tree_host_->TotalUIResourceCreated());
   EXPECT_EQ(expected_deleted, layer_tree_host_->TotalUIResourceDeleted());
@@ -898,7 +878,7 @@
       layer_tree_host_->ui_resource_size(scrollbar_layer->track_resource_id()));
 
   scrollbar_layer->ResetNeedsDisplayForTesting();
-  EXPECT_FALSE(scrollbar_layer->Update(&queue));
+  EXPECT_FALSE(scrollbar_layer->Update());
   EXPECT_NE(0, scrollbar_layer->track_resource_id());
   EXPECT_EQ(0, scrollbar_layer->thumb_resource_id());
   EXPECT_EQ(resource_count, layer_tree_host_->UIResourceCount());
@@ -929,26 +909,18 @@
     scrollbar_layer->SetPosition(scrollbar_location);
     layer_tree_root->SetBounds(gfx::Size(100, 200));
     content_layer->SetBounds(gfx::Size(100, 200));
-    gfx::SizeF scaled_size =
-        gfx::ScaleSize(scrollbar_layer->bounds(), test_scale, test_scale);
-    gfx::PointF scaled_location =
-        gfx::ScalePoint(scrollbar_layer->position(), test_scale, test_scale);
-    scrollbar_layer->draw_properties().contents_scale_x = test_scale;
-    scrollbar_layer->draw_properties().contents_scale_y = test_scale;
-    scrollbar_layer->draw_properties().visible_content_rect =
-        gfx::Rect(scaled_location.x(),
-                  scaled_location.y(),
-                  scaled_size.width(),
-                  scaled_size.height());
+    scrollbar_layer->draw_properties().visible_layer_rect =
+        gfx::Rect(scrollbar_location, scrollbar_layer->bounds());
     scrollbar_layer->CreateRenderSurface();
     scrollbar_layer->draw_properties().render_target = scrollbar_layer.get();
 
     testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
     EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
 
-    ResourceUpdateQueue queue;
+    layer_tree_host_->SetDeviceScaleFactor(test_scale);
+
     scrollbar_layer->SavePaintProperties();
-    scrollbar_layer->Update(&queue);
+    scrollbar_layer->Update();
 
     // Verify that we have not generated any content uploads that are larger
     // than their destination textures.
@@ -999,23 +971,14 @@
     scrollbar_layer->SetPosition(scrollbar_rect.origin());
     scrollbar_layer->fake_scrollbar()->set_location(scrollbar_rect.origin());
     scrollbar_layer->fake_scrollbar()->set_track_rect(scrollbar_rect);
-    gfx::SizeF scaled_size =
-        gfx::ScaleSize(scrollbar_layer->bounds(), test_scale, test_scale);
-    gfx::PointF scaled_location =
-        gfx::ScalePoint(scrollbar_layer->position(), test_scale, test_scale);
-    scrollbar_layer->draw_properties().contents_scale_x = test_scale;
-    scrollbar_layer->draw_properties().contents_scale_y = test_scale;
-    scrollbar_layer->draw_properties().visible_content_rect =
-        gfx::Rect(scaled_location.x(),
-                  scaled_location.y(),
-                  scaled_size.width(),
-                  scaled_size.height());
+    scrollbar_layer->draw_properties().visible_layer_rect = scrollbar_rect;
 
-    ResourceUpdateQueue queue;
+    layer_tree_host_->SetDeviceScaleFactor(test_scale);
+
     gfx::Rect screen_space_clip_rect;
     scrollbar_layer->SavePaintProperties();
 
-    scrollbar_layer->Update(&queue);
+    scrollbar_layer->Update();
 
     UIResourceBitmap* bitmap = layer_tree_host_->ui_resource_bitmap(
         scrollbar_layer->track_resource_id());
diff --git a/cc/layers/solid_color_layer_impl.cc b/cc/layers/solid_color_layer_impl.cc
index b85012a..76cd3fc4 100644
--- a/cc/layers/solid_color_layer_impl.cc
+++ b/cc/layers/solid_color_layer_impl.cc
@@ -29,29 +29,29 @@
 
 void SolidColorLayerImpl::AppendSolidQuads(
     RenderPass* render_pass,
-    const Occlusion& occlusion_in_content_space,
+    const Occlusion& occlusion_in_layer_space,
     SharedQuadState* shared_quad_state,
-    const gfx::Rect& visible_content_rect,
+    const gfx::Rect& visible_layer_rect,
     SkColor color,
     AppendQuadsData* append_quads_data) {
   // We create a series of smaller quads instead of just one large one so that
   // the culler can reduce the total pixels drawn.
-  int right = visible_content_rect.right();
-  int bottom = visible_content_rect.bottom();
-  for (int x = visible_content_rect.x(); x < visible_content_rect.right();
+  int right = visible_layer_rect.right();
+  int bottom = visible_layer_rect.bottom();
+  for (int x = visible_layer_rect.x(); x < visible_layer_rect.right();
        x += kSolidQuadTileSize) {
-    for (int y = visible_content_rect.y(); y < visible_content_rect.bottom();
+    for (int y = visible_layer_rect.y(); y < visible_layer_rect.bottom();
          y += kSolidQuadTileSize) {
       gfx::Rect quad_rect(x,
                           y,
                           std::min(right - x, kSolidQuadTileSize),
                           std::min(bottom - y, kSolidQuadTileSize));
       gfx::Rect visible_quad_rect =
-          occlusion_in_content_space.GetUnoccludedContentRect(quad_rect);
+          occlusion_in_layer_space.GetUnoccludedContentRect(quad_rect);
       if (visible_quad_rect.IsEmpty())
         continue;
 
-      append_quads_data->visible_content_area +=
+      append_quads_data->visible_layer_area +=
           visible_quad_rect.width() * visible_quad_rect.height();
 
       SolidColorDrawQuad* quad =
diff --git a/cc/layers/solid_color_layer_impl.h b/cc/layers/solid_color_layer_impl.h
index 213e3f13..2068ac5d 100644
--- a/cc/layers/solid_color_layer_impl.h
+++ b/cc/layers/solid_color_layer_impl.h
@@ -19,9 +19,9 @@
   }
 
   static void AppendSolidQuads(RenderPass* render_pass,
-                               const Occlusion& occlusion_in_content_space,
+                               const Occlusion& occlusion_in_layer_space,
                                SharedQuadState* shared_quad_state,
-                               const gfx::Rect& visible_content_rect,
+                               const gfx::Rect& visible_layer_rect,
                                SkColor color,
                                AppendQuadsData* append_quads_data);
 
diff --git a/cc/layers/solid_color_layer_impl_unittest.cc b/cc/layers/solid_color_layer_impl_unittest.cc
index 8d32780..59ef6ba7 100644
--- a/cc/layers/solid_color_layer_impl_unittest.cc
+++ b/cc/layers/solid_color_layer_impl_unittest.cc
@@ -24,16 +24,15 @@
   scoped_ptr<RenderPass> render_pass = RenderPass::Create();
 
   gfx::Size layer_size = gfx::Size(800, 600);
-  gfx::Rect visible_content_rect = gfx::Rect(layer_size);
+  gfx::Rect visible_layer_rect = gfx::Rect(layer_size);
 
   FakeImplProxy proxy;
   TestTaskGraphRunner task_graph_runner;
   FakeLayerTreeHostImpl host_impl(&proxy, nullptr, &task_graph_runner);
   scoped_ptr<SolidColorLayerImpl> layer =
       SolidColorLayerImpl::Create(host_impl.active_tree(), 1);
-  layer->draw_properties().visible_content_rect = visible_content_rect;
+  layer->draw_properties().visible_layer_rect = visible_layer_rect;
   layer->SetBounds(layer_size);
-  layer->SetContentBounds(layer_size);
   layer->SetHasRenderSurface(true);
   layer->draw_properties().render_target = layer.get();
 
@@ -41,7 +40,7 @@
   layer->AppendQuads(render_pass.get(), &data);
 
   LayerTestCommon::VerifyQuadsExactlyCoverRect(render_pass->quad_list,
-                                               visible_content_rect);
+                                               visible_layer_rect);
 }
 
 TEST(SolidColorLayerImplTest, VerifyCorrectBackgroundColorInQuad) {
@@ -50,16 +49,15 @@
   scoped_ptr<RenderPass> render_pass = RenderPass::Create();
 
   gfx::Size layer_size = gfx::Size(100, 100);
-  gfx::Rect visible_content_rect = gfx::Rect(layer_size);
+  gfx::Rect visible_layer_rect = gfx::Rect(layer_size);
 
   FakeImplProxy proxy;
   TestTaskGraphRunner task_graph_runner;
   FakeLayerTreeHostImpl host_impl(&proxy, nullptr, &task_graph_runner);
   scoped_ptr<SolidColorLayerImpl> layer =
       SolidColorLayerImpl::Create(host_impl.active_tree(), 1);
-  layer->draw_properties().visible_content_rect = visible_content_rect;
+  layer->draw_properties().visible_layer_rect = visible_layer_rect;
   layer->SetBounds(layer_size);
-  layer->SetContentBounds(layer_size);
   layer->SetBackgroundColor(test_color);
   layer->SetHasRenderSurface(true);
   layer->draw_properties().render_target = layer.get();
@@ -79,16 +77,15 @@
   scoped_ptr<RenderPass> render_pass = RenderPass::Create();
 
   gfx::Size layer_size = gfx::Size(100, 100);
-  gfx::Rect visible_content_rect = gfx::Rect(layer_size);
+  gfx::Rect visible_layer_rect = gfx::Rect(layer_size);
 
   FakeImplProxy proxy;
   TestTaskGraphRunner task_graph_runner;
   FakeLayerTreeHostImpl host_impl(&proxy, nullptr, &task_graph_runner);
   scoped_ptr<SolidColorLayerImpl> layer =
       SolidColorLayerImpl::Create(host_impl.active_tree(), 1);
-  layer->draw_properties().visible_content_rect = visible_content_rect;
+  layer->draw_properties().visible_layer_rect = visible_layer_rect;
   layer->SetBounds(layer_size);
-  layer->SetContentBounds(layer_size);
   layer->draw_properties().opacity = opacity;
   layer->SetHasRenderSurface(true);
   layer->draw_properties().render_target = layer.get();
@@ -108,7 +105,7 @@
   scoped_ptr<RenderPass> render_pass = RenderPass::Create();
 
   gfx::Size layer_size = gfx::Size(100, 100);
-  gfx::Rect visible_content_rect = gfx::Rect(layer_size);
+  gfx::Rect visible_layer_rect = gfx::Rect(layer_size);
 
   FakeImplProxy proxy;
   TestTaskGraphRunner task_graph_runner;
@@ -116,7 +113,6 @@
   scoped_ptr<SolidColorLayerImpl> layer =
       SolidColorLayerImpl::Create(host_impl.active_tree(), 1);
   layer->SetBounds(layer_size);
-  layer->SetContentBounds(layer_size);
   layer->draw_properties().blend_mode = blend_mode;
 
   AppendQuadsData data;
@@ -129,7 +125,7 @@
 
 TEST(SolidColorLayerImplTest, VerifyOpaqueRect) {
   gfx::Size layer_size = gfx::Size(100, 100);
-  gfx::Rect visible_content_rect = gfx::Rect(layer_size);
+  gfx::Rect visible_layer_rect = gfx::Rect(layer_size);
 
   LayerSettings layer_settings;
 
@@ -174,7 +170,7 @@
     layer_impl->AppendQuads(render_pass.get(), &data);
 
     ASSERT_EQ(render_pass->quad_list.size(), 1U);
-    EXPECT_EQ(visible_content_rect.ToString(),
+    EXPECT_EQ(visible_layer_rect.ToString(),
               render_pass->quad_list.front()->opaque_rect.ToString());
   }
 
@@ -215,7 +211,6 @@
       impl.AddChildToRoot<SolidColorLayerImpl>();
   solid_color_layer_impl->SetBackgroundColor(SkColorSetARGB(255, 10, 20, 30));
   solid_color_layer_impl->SetBounds(layer_size);
-  solid_color_layer_impl->SetContentBounds(layer_size);
   solid_color_layer_impl->SetDrawsContent(true);
 
   impl.CalcDrawProps(viewport_size);
@@ -232,7 +227,7 @@
 
   {
     SCOPED_TRACE("Full occlusion");
-    gfx::Rect occluded(solid_color_layer_impl->visible_content_rect());
+    gfx::Rect occluded(solid_color_layer_impl->visible_layer_rect());
     impl.AppendQuadsWithOcclusion(solid_color_layer_impl, occluded);
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
diff --git a/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc b/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
index b27c84b..c758497 100644
--- a/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/solid_color_scrollbar_layer_impl_unittest.cc
@@ -30,7 +30,6 @@
           is_left_side_vertical_scrollbar,
           is_overlay);
   scrollbar_layer_impl->SetBounds(layer_size);
-  scrollbar_layer_impl->SetContentBounds(layer_size);
   scrollbar_layer_impl->SetDrawsContent(true);
   scrollbar_layer_impl->SetCurrentPos(100.f / 4);
   scrollbar_layer_impl->SetMaximum(100);
@@ -55,7 +54,7 @@
 
   {
     SCOPED_TRACE("Full occlusion");
-    gfx::Rect occluded(scrollbar_layer_impl->visible_content_rect());
+    gfx::Rect occluded(scrollbar_layer_impl->visible_layer_rect());
     impl.AppendQuadsWithOcclusion(scrollbar_layer_impl, occluded);
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
diff --git a/cc/layers/surface_layer_impl_unittest.cc b/cc/layers/surface_layer_impl_unittest.cc
index 39577a3..3ff8255 100644
--- a/cc/layers/surface_layer_impl_unittest.cc
+++ b/cc/layers/surface_layer_impl_unittest.cc
@@ -19,7 +19,6 @@
   SurfaceLayerImpl* surface_layer_impl =
       impl.AddChildToRoot<SurfaceLayerImpl>();
   surface_layer_impl->SetBounds(layer_size);
-  surface_layer_impl->SetContentBounds(layer_size);
   surface_layer_impl->SetDrawsContent(true);
   SurfaceId surface_id(9);
   surface_layer_impl->SetSurfaceId(surface_id);
@@ -40,7 +39,7 @@
 
   {
     SCOPED_TRACE("Full occlusion");
-    gfx::Rect occluded(surface_layer_impl->visible_content_rect());
+    gfx::Rect occluded(surface_layer_impl->visible_layer_rect());
     impl.AppendQuadsWithOcclusion(surface_layer_impl, occluded);
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
diff --git a/cc/layers/texture_layer.cc b/cc/layers/texture_layer.cc
index 965b2f39..20741fdd 100644
--- a/cc/layers/texture_layer.cc
+++ b/cc/layers/texture_layer.cc
@@ -216,8 +216,8 @@
   return (client_ || holder_ref_) && Layer::HasDrawableContent();
 }
 
-bool TextureLayer::Update(ResourceUpdateQueue* queue) {
-  bool updated = Layer::Update(queue);
+bool TextureLayer::Update() {
+  bool updated = Layer::Update();
   if (client_) {
     TextureMailbox mailbox;
     scoped_ptr<SingleReleaseCallback> release_callback;
@@ -267,16 +267,6 @@
   }
 }
 
-SimpleEnclosedRegion TextureLayer::VisibleContentOpaqueRegion() const {
-  if (contents_opaque())
-    return SimpleEnclosedRegion(visible_content_rect());
-
-  if (blend_background_color_ && (SkColorGetA(background_color()) == 0xFF))
-    return SimpleEnclosedRegion(visible_content_rect());
-
-  return SimpleEnclosedRegion();
-}
-
 TextureLayer::TextureMailboxHolder::MainThreadReference::MainThreadReference(
     TextureMailboxHolder* holder)
     : holder_(holder) {
diff --git a/cc/layers/texture_layer.h b/cc/layers/texture_layer.h
index e9276b3..53f2d8d 100644
--- a/cc/layers/texture_layer.h
+++ b/cc/layers/texture_layer.h
@@ -142,9 +142,8 @@
   void SetNeedsDisplayRect(const gfx::Rect& dirty_rect) override;
 
   void SetLayerTreeHost(LayerTreeHost* layer_tree_host) override;
-  bool Update(ResourceUpdateQueue* queue) override;
+  bool Update() override;
   void PushPropertiesTo(LayerImpl* layer) override;
-  SimpleEnclosedRegion VisibleContentOpaqueRegion() const override;
 
  protected:
   TextureLayer(const LayerSettings& settings, TextureLayerClient* client);
diff --git a/cc/layers/texture_layer_impl.cc b/cc/layers/texture_layer_impl.cc
index ba31c6d..08617dbe 100644
--- a/cc/layers/texture_layer_impl.cc
+++ b/cc/layers/texture_layer_impl.cc
@@ -184,12 +184,12 @@
   ValidateQuadResources(quad);
 }
 
-SimpleEnclosedRegion TextureLayerImpl::VisibleContentOpaqueRegion() const {
+SimpleEnclosedRegion TextureLayerImpl::VisibleOpaqueRegion() const {
   if (contents_opaque())
-    return SimpleEnclosedRegion(visible_content_rect());
+    return SimpleEnclosedRegion(visible_layer_rect());
 
   if (blend_background_color_ && (SkColorGetA(background_color()) == 0xFF))
-    return SimpleEnclosedRegion(visible_content_rect());
+    return SimpleEnclosedRegion(visible_layer_rect());
 
   return SimpleEnclosedRegion();
 }
diff --git a/cc/layers/texture_layer_impl.h b/cc/layers/texture_layer_impl.h
index acbfbce4..680aaf0a 100644
--- a/cc/layers/texture_layer_impl.h
+++ b/cc/layers/texture_layer_impl.h
@@ -30,7 +30,7 @@
                 ResourceProvider* resource_provider) override;
   void AppendQuads(RenderPass* render_pass,
                    AppendQuadsData* append_quads_data) override;
-  SimpleEnclosedRegion VisibleContentOpaqueRegion() const override;
+  SimpleEnclosedRegion VisibleOpaqueRegion() const override;
   void ReleaseResources() override;
 
   // These setter methods don't cause any implicit damage, so the texture client
diff --git a/cc/layers/texture_layer_impl_unittest.cc b/cc/layers/texture_layer_impl_unittest.cc
index cbe80e1..27efd97 100644
--- a/cc/layers/texture_layer_impl_unittest.cc
+++ b/cc/layers/texture_layer_impl_unittest.cc
@@ -17,6 +17,32 @@
                     BlockingTaskRunner* main_thread_task_runner) {
 }
 
+TEST(TextureLayerImplTest, VisibleOpaqueRegion) {
+  const gfx::Size layer_bounds(100, 100);
+  const gfx::Rect layer_rect(layer_bounds);
+  const Region layer_region(layer_rect);
+
+  LayerTestCommon::LayerImplTest impl;
+
+  TextureLayerImpl* layer = impl.AddChildToRoot<TextureLayerImpl>();
+  layer->SetBounds(layer_bounds);
+  layer->draw_properties().visible_layer_rect = layer_rect;
+  layer->SetBlendBackgroundColor(true);
+
+  // Verify initial conditions.
+  EXPECT_FALSE(layer->contents_opaque());
+  EXPECT_EQ(0u, layer->background_color());
+  EXPECT_EQ(Region().ToString(), layer->VisibleOpaqueRegion().ToString());
+
+  // Opaque background.
+  layer->SetBackgroundColor(SK_ColorWHITE);
+  EXPECT_EQ(layer_region.ToString(), layer->VisibleOpaqueRegion().ToString());
+
+  // Transparent background.
+  layer->SetBackgroundColor(SkColorSetARGB(100, 255, 255, 255));
+  EXPECT_EQ(Region().ToString(), layer->VisibleOpaqueRegion().ToString());
+}
+
 TEST(TextureLayerImplTest, Occlusion) {
   gfx::Size layer_size(1000, 1000);
   gfx::Size viewport_size(1000, 1000);
@@ -31,7 +57,6 @@
   TextureLayerImpl* texture_layer_impl =
       impl.AddChildToRoot<TextureLayerImpl>();
   texture_layer_impl->SetBounds(layer_size);
-  texture_layer_impl->SetContentBounds(layer_size);
   texture_layer_impl->SetDrawsContent(true);
   texture_layer_impl->SetTextureMailbox(
       texture_mailbox,
@@ -51,7 +76,7 @@
 
   {
     SCOPED_TRACE("Full occlusion");
-    gfx::Rect occluded(texture_layer_impl->visible_content_rect());
+    gfx::Rect occluded(texture_layer_impl->visible_layer_rect());
     impl.AppendQuadsWithOcclusion(texture_layer_impl, occluded);
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc
index 0a68d23..97bb7a1 100644
--- a/cc/layers/texture_layer_unittest.cc
+++ b/cc/layers/texture_layer_unittest.cc
@@ -235,34 +235,6 @@
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBlendBackgroundColor(true));
 }
 
-TEST_F(TextureLayerTest, VisibleContentOpaqueRegion) {
-  const gfx::Size layer_bounds(100, 100);
-  const gfx::Rect layer_rect(layer_bounds);
-  const Region layer_region(layer_rect);
-
-  scoped_refptr<TextureLayer> layer =
-      TextureLayer::CreateForMailbox(layer_settings_, nullptr);
-  layer->SetBounds(layer_bounds);
-  layer->draw_properties().visible_content_rect = layer_rect;
-  layer->SetBlendBackgroundColor(true);
-
-  // Verify initial conditions.
-  EXPECT_FALSE(layer->contents_opaque());
-  EXPECT_EQ(0u, layer->background_color());
-  EXPECT_EQ(Region().ToString(),
-            layer->VisibleContentOpaqueRegion().ToString());
-
-  // Opaque background.
-  layer->SetBackgroundColor(SK_ColorWHITE);
-  EXPECT_EQ(layer_region.ToString(),
-            layer->VisibleContentOpaqueRegion().ToString());
-
-  // Transparent background.
-  layer->SetBackgroundColor(SkColorSetARGB(100, 255, 255, 255));
-  EXPECT_EQ(Region().ToString(),
-            layer->VisibleContentOpaqueRegion().ToString());
-}
-
 TEST_F(TextureLayerTest, RateLimiter) {
   FakeTextureLayerClient client;
   scoped_refptr<TextureLayer> test_layer =
diff --git a/cc/layers/ui_resource_layer_impl_unittest.cc b/cc/layers/ui_resource_layer_impl_unittest.cc
index c093a86..e31d68a 100644
--- a/cc/layers/ui_resource_layer_impl_unittest.cc
+++ b/cc/layers/ui_resource_layer_impl_unittest.cc
@@ -28,12 +28,11 @@
     const gfx::Size& layer_size,
     bool opaque,
     UIResourceId uid) {
-  gfx::Rect visible_content_rect(layer_size);
+  gfx::Rect visible_layer_rect(layer_size);
   scoped_ptr<UIResourceLayerImpl> layer =
       UIResourceLayerImpl::Create(host_impl->active_tree(), 1);
-  layer->draw_properties().visible_content_rect = visible_content_rect;
+  layer->draw_properties().visible_layer_rect = visible_layer_rect;
   layer->SetBounds(layer_size);
-  layer->SetContentBounds(layer_size);
   layer->SetHasRenderSurface(true);
   layer->draw_properties().render_target = layer.get();
 
@@ -174,7 +173,6 @@
   UIResourceLayerImpl* ui_resource_layer_impl =
       impl.AddChildToRoot<UIResourceLayerImpl>();
   ui_resource_layer_impl->SetBounds(layer_size);
-  ui_resource_layer_impl->SetContentBounds(layer_size);
   ui_resource_layer_impl->SetDrawsContent(true);
   ui_resource_layer_impl->SetUIResourceId(uid);
 
@@ -192,7 +190,7 @@
 
   {
     SCOPED_TRACE("Full occlusion");
-    gfx::Rect occluded(ui_resource_layer_impl->visible_content_rect());
+    gfx::Rect occluded(ui_resource_layer_impl->visible_layer_rect());
     impl.AppendQuadsWithOcclusion(ui_resource_layer_impl, occluded);
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
diff --git a/cc/layers/ui_resource_layer_unittest.cc b/cc/layers/ui_resource_layer_unittest.cc
index d650687..3a45eaf3 100644
--- a/cc/layers/ui_resource_layer_unittest.cc
+++ b/cc/layers/ui_resource_layer_unittest.cc
@@ -7,7 +7,6 @@
 #include "base/thread_task_runner_handle.h"
 #include "cc/resources/prioritized_resource_manager.h"
 #include "cc/resources/resource_provider.h"
-#include "cc/resources/resource_update_queue.h"
 #include "cc/resources/scoped_ui_resource.h"
 #include "cc/test/fake_layer_tree_host.h"
 #include "cc/test/fake_layer_tree_host_client.h"
@@ -82,9 +81,8 @@
   Mock::VerifyAndClearExpectations(layer_tree_host_.get());
   EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host_.get());
 
-  ResourceUpdateQueue queue;
   test_layer->SavePaintProperties();
-  test_layer->Update(&queue);
+  test_layer->Update();
 
   EXPECT_FALSE(test_layer->DrawsContent());
 
@@ -93,7 +91,7 @@
   bitmap.setImmutable();
 
   test_layer->SetBitmap(bitmap);
-  test_layer->Update(&queue);
+  test_layer->Update();
 
   EXPECT_TRUE(test_layer->DrawsContent());
 }
@@ -108,9 +106,8 @@
   Mock::VerifyAndClearExpectations(layer_tree_host_.get());
   EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host_.get());
 
-  ResourceUpdateQueue queue;
   test_layer->SavePaintProperties();
-  test_layer->Update(&queue);
+  test_layer->Update();
 
   EXPECT_FALSE(test_layer->DrawsContent());
 
@@ -118,7 +115,7 @@
   scoped_ptr<ScopedUIResource> resource = ScopedUIResource::Create(
       layer_tree_host_.get(), UIResourceBitmap(gfx::Size(10, 10), is_opaque));
   test_layer->SetUIResourceId(resource->id());
-  test_layer->Update(&queue);
+  test_layer->Update();
 
   EXPECT_TRUE(test_layer->DrawsContent());
 
diff --git a/cc/layers/video_frame_provider_client_impl_unittest.cc b/cc/layers/video_frame_provider_client_impl_unittest.cc
index f2f2c0d..05385e4 100644
--- a/cc/layers/video_frame_provider_client_impl_unittest.cc
+++ b/cc/layers/video_frame_provider_client_impl_unittest.cc
@@ -76,7 +76,6 @@
     video_layer_impl_ = impl_.AddChildToRoot<VideoLayerImpl>(
         &provider_, media::VIDEO_ROTATION_0);
     video_layer_impl_->SetBounds(layer_size);
-    video_layer_impl_->SetContentBounds(layer_size);
     video_layer_impl_->SetDrawsContent(true);
     client_impl_->SetActiveVideoLayer(video_layer_impl_);
     ASSERT_TRUE(client_impl_->ActiveVideoLayer());
diff --git a/cc/layers/video_layer.cc b/cc/layers/video_layer.cc
index 8e0f17f..cae00aa 100644
--- a/cc/layers/video_layer.cc
+++ b/cc/layers/video_layer.cc
@@ -30,8 +30,8 @@
   return impl.Pass();
 }
 
-bool VideoLayer::Update(ResourceUpdateQueue* queue) {
-  bool updated = Layer::Update(queue);
+bool VideoLayer::Update() {
+  bool updated = Layer::Update();
 
   // Video layer doesn't update any resources from the main thread side,
   // but repaint rects need to be sent to the VideoLayerImpl via commit.
diff --git a/cc/layers/video_layer.h b/cc/layers/video_layer.h
index f8e257e..575de46 100644
--- a/cc/layers/video_layer.h
+++ b/cc/layers/video_layer.h
@@ -26,7 +26,7 @@
 
   scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
 
-  bool Update(ResourceUpdateQueue* queue) override;
+  bool Update() override;
 
  private:
   VideoLayer(const LayerSettings& settings,
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc
index 665541e..2233583 100644
--- a/cc/layers/video_layer_impl.cc
+++ b/cc/layers/video_layer_impl.cc
@@ -161,7 +161,7 @@
 
   SharedQuadState* shared_quad_state =
       render_pass->CreateAndAppendSharedQuadState();
-  shared_quad_state->SetAll(transform, rotated_size, visible_content_rect(),
+  shared_quad_state->SetAll(transform, rotated_size, visible_layer_rect(),
                             clip_rect(), is_clipped(), draw_opacity(),
                             draw_blend_mode(), sorting_context_id());
 
@@ -389,11 +389,11 @@
   provider_client_impl_->ReleaseLock();
 }
 
-SimpleEnclosedRegion VideoLayerImpl::VisibleContentOpaqueRegion() const {
+SimpleEnclosedRegion VideoLayerImpl::VisibleOpaqueRegion() const {
   // If we don't have a frame yet, then we don't have an opaque region.
   if (!provider_client_impl_->HasCurrentFrame())
     return SimpleEnclosedRegion();
-  return LayerImpl::VisibleContentOpaqueRegion();
+  return LayerImpl::VisibleOpaqueRegion();
 }
 
 void VideoLayerImpl::ReleaseResources() {
diff --git a/cc/layers/video_layer_impl.h b/cc/layers/video_layer_impl.h
index 58c0e12..6fab7f5 100644
--- a/cc/layers/video_layer_impl.h
+++ b/cc/layers/video_layer_impl.h
@@ -38,7 +38,7 @@
   void AppendQuads(RenderPass* render_pass,
                    AppendQuadsData* append_quads_data) override;
   void DidDraw(ResourceProvider* resource_provider) override;
-  SimpleEnclosedRegion VisibleContentOpaqueRegion() const override;
+  SimpleEnclosedRegion VisibleOpaqueRegion() const override;
   void DidBecomeActive() override;
   void ReleaseResources() override;
 
diff --git a/cc/layers/video_layer_impl_unittest.cc b/cc/layers/video_layer_impl_unittest.cc
index 35a07df..da2de85 100644
--- a/cc/layers/video_layer_impl_unittest.cc
+++ b/cc/layers/video_layer_impl_unittest.cc
@@ -47,7 +47,6 @@
   VideoLayerImpl* video_layer_impl =
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0);
   video_layer_impl->SetBounds(layer_size);
-  video_layer_impl->SetContentBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
 
   impl.CalcDrawProps(viewport_size);
@@ -64,7 +63,7 @@
 
   {
     SCOPED_TRACE("Full occlusion");
-    gfx::Rect occluded(video_layer_impl->visible_content_rect());
+    gfx::Rect occluded(video_layer_impl->visible_layer_rect());
     impl.AppendQuadsWithOcclusion(video_layer_impl, occluded);
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
@@ -98,7 +97,6 @@
   scoped_ptr<LayerImpl> layer_impl = LayerImpl::Create(active_tree, 3);
   layer_impl->SetHasRenderSurface(true);
   layer_impl->SetBounds(layer_size);
-  layer_impl->SetContentBounds(layer_size);
   layer_impl->SetDrawsContent(true);
   const auto& draw_properties = layer_impl->draw_properties();
 
@@ -106,7 +104,6 @@
   scoped_ptr<VideoLayerImpl> video_layer_impl = VideoLayerImpl::Create(
       active_tree, 4, &provider, media::VIDEO_ROTATION_0);
   video_layer_impl->SetBounds(layer_size);
-  video_layer_impl->SetContentBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
   video_layer_impl->SetContentsOpaque(true);
 
@@ -167,7 +164,6 @@
   VideoLayerImpl* video_layer_impl =
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0);
   video_layer_impl->SetBounds(layer_size);
-  video_layer_impl->SetContentBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
 
   impl.CalcDrawProps(viewport_size);
@@ -180,10 +176,10 @@
   gfx::Point3F p2(impl.quad_list().front()->rect.width(), 0, 0);
   impl.quad_list()
       .front()
-      ->shared_quad_state->content_to_target_transform.TransformPoint(&p1);
+      ->shared_quad_state->quad_to_target_transform.TransformPoint(&p1);
   impl.quad_list()
       .front()
-      ->shared_quad_state->content_to_target_transform.TransformPoint(&p2);
+      ->shared_quad_state->quad_to_target_transform.TransformPoint(&p2);
   EXPECT_EQ(gfx::Point3F(0, 50, 0), p1);
   EXPECT_EQ(gfx::Point3F(100, 0, 0), p2);
 }
@@ -207,7 +203,6 @@
   VideoLayerImpl* video_layer_impl =
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_90);
   video_layer_impl->SetBounds(layer_size);
-  video_layer_impl->SetContentBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
 
   impl.CalcDrawProps(viewport_size);
@@ -220,10 +215,10 @@
   gfx::Point3F p2(impl.quad_list().front()->rect.width(), 0, 0);
   impl.quad_list()
       .front()
-      ->shared_quad_state->content_to_target_transform.TransformPoint(&p1);
+      ->shared_quad_state->quad_to_target_transform.TransformPoint(&p1);
   impl.quad_list()
       .front()
-      ->shared_quad_state->content_to_target_transform.TransformPoint(&p2);
+      ->shared_quad_state->quad_to_target_transform.TransformPoint(&p2);
   EXPECT_EQ(gfx::Point3F(0, 0, 0), p1);
   EXPECT_EQ(gfx::Point3F(100, 50, 0), p2);
 }
@@ -247,7 +242,6 @@
   VideoLayerImpl* video_layer_impl =
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_180);
   video_layer_impl->SetBounds(layer_size);
-  video_layer_impl->SetContentBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
 
   impl.CalcDrawProps(viewport_size);
@@ -260,10 +254,10 @@
   gfx::Point3F p2(impl.quad_list().front()->rect.width(), 0, 0);
   impl.quad_list()
       .front()
-      ->shared_quad_state->content_to_target_transform.TransformPoint(&p1);
+      ->shared_quad_state->quad_to_target_transform.TransformPoint(&p1);
   impl.quad_list()
       .front()
-      ->shared_quad_state->content_to_target_transform.TransformPoint(&p2);
+      ->shared_quad_state->quad_to_target_transform.TransformPoint(&p2);
   EXPECT_EQ(gfx::Point3F(100, 0, 0), p1);
   EXPECT_EQ(gfx::Point3F(0, 50, 0), p2);
 }
@@ -287,7 +281,6 @@
   VideoLayerImpl* video_layer_impl =
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_270);
   video_layer_impl->SetBounds(layer_size);
-  video_layer_impl->SetContentBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
 
   impl.CalcDrawProps(viewport_size);
@@ -300,10 +293,10 @@
   gfx::Point3F p2(impl.quad_list().front()->rect.width(), 0, 0);
   impl.quad_list()
       .front()
-      ->shared_quad_state->content_to_target_transform.TransformPoint(&p1);
+      ->shared_quad_state->quad_to_target_transform.TransformPoint(&p1);
   impl.quad_list()
       .front()
-      ->shared_quad_state->content_to_target_transform.TransformPoint(&p2);
+      ->shared_quad_state->quad_to_target_transform.TransformPoint(&p2);
   EXPECT_EQ(gfx::Point3F(100, 50, 0), p1);
   EXPECT_EQ(gfx::Point3F(0, 0, 0), p2);
 }
@@ -331,7 +324,6 @@
   VideoLayerImpl* video_layer_impl =
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0);
   video_layer_impl->SetBounds(layer_size);
-  video_layer_impl->SetContentBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
 
   gfx::Rect occluded;
@@ -372,7 +364,6 @@
   VideoLayerImpl* video_layer_impl =
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0);
   video_layer_impl->SetBounds(layer_size);
-  video_layer_impl->SetContentBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
 
   gfx::Rect occluded;
diff --git a/cc/output/bsp_walk_action.cc b/cc/output/bsp_walk_action.cc
index ddf39f3b..8a336941 100644
--- a/cc/output/bsp_walk_action.cc
+++ b/cc/output/bsp_walk_action.cc
@@ -28,7 +28,7 @@
   gfx::Transform inverse_transform;
   bool invertible =
       item->original_ref()
-          ->shared_quad_state->content_to_target_transform.GetInverse(
+          ->shared_quad_state->quad_to_target_transform.GetInverse(
               &inverse_transform);
   DCHECK(invertible);
   item->TransformToLayerSpace(inverse_transform);
diff --git a/cc/output/direct_renderer.cc b/cc/output/direct_renderer.cc
index 058c53b..7ceaa00 100644
--- a/cc/output/direct_renderer.cc
+++ b/cc/output/direct_renderer.cc
@@ -461,10 +461,9 @@
     // This layer is in a 3D sorting context so we add it to the list of
     // polygons to go into the BSP tree.
     if (quad.shared_quad_state->sorting_context_id != 0) {
-      scoped_ptr<DrawPolygon> new_polygon(
-          new DrawPolygon(*it, quad.visible_rect,
-                          quad.shared_quad_state->content_to_target_transform,
-                          next_polygon_id++));
+      scoped_ptr<DrawPolygon> new_polygon(new DrawPolygon(
+          *it, quad.visible_rect,
+          quad.shared_quad_state->quad_to_target_transform, next_polygon_id++));
       if (new_polygon->points().size() > 2u) {
         poly_list.push_back(new_polygon.Pass());
       }
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index 473e164a..761123e 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -599,7 +599,7 @@
 
   SetShaderOpacity(quad->shared_quad_state->opacity,
                    program->fragment_shader().alpha_location());
-  DrawQuadGeometry(frame, quad->shared_quad_state->content_to_target_transform,
+  DrawQuadGeometry(frame, quad->shared_quad_state->quad_to_target_transform,
                    quad->rect, program->vertex_shader().matrix_location());
 }
 
@@ -620,7 +620,7 @@
   gfx::Rect layer_rect = quad->rect;
   gfx::Transform render_matrix;
   QuadRectTransform(&render_matrix,
-                    quad->shared_quad_state->content_to_target_transform,
+                    quad->shared_quad_state->quad_to_target_transform,
                     layer_rect);
   GLRenderer::ToGLMatrix(&gl_matrix[0],
                          frame->projection_matrix * render_matrix);
@@ -927,7 +927,7 @@
 
   gfx::Transform quad_rect_matrix;
   QuadRectTransform(&quad_rect_matrix,
-                    quad->shared_quad_state->content_to_target_transform,
+                    quad->shared_quad_state->quad_to_target_transform,
                     quad->rect);
   gfx::Transform contents_device_transform =
       frame->window_matrix * frame->projection_matrix * quad_rect_matrix;
@@ -1081,10 +1081,8 @@
   }
 
   TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
-      gl_,
-      &highp_threshold_cache_,
-      highp_threshold_min_,
-      quad->shared_quad_state->visible_content_rect.bottom_right());
+      gl_, &highp_threshold_cache_, highp_threshold_min_,
+      quad->shared_quad_state->visible_quad_layer_rect.bottom_right());
 
   ShaderLocations locations;
 
@@ -1258,7 +1256,7 @@
 
   SetShaderOpacity(quad->shared_quad_state->opacity, locations.alpha);
   SetShaderQuadF(surface_quad, locations.quad);
-  DrawQuadGeometry(frame, quad->shared_quad_state->content_to_target_transform,
+  DrawQuadGeometry(frame, quad->shared_quad_state->quad_to_target_transform,
                    quad->rect, locations.matrix);
 
   // Flush the compositor context before the filter bitmap goes out of
@@ -1310,10 +1308,10 @@
     return true;
 
   return std::abs(clip_region->p3().y() -
-                  quad->shared_quad_state->content_bounds.height()) <
+                  quad->shared_quad_state->quad_layer_bounds.height()) <
              kAntiAliasingEpsilon &&
          std::abs(clip_region->p4().y() -
-                  quad->shared_quad_state->content_bounds.height()) <
+                  quad->shared_quad_state->quad_layer_bounds.height()) <
              kAntiAliasingEpsilon;
 }
 
@@ -1334,10 +1332,10 @@
     return true;
 
   return std::abs(clip_region->p2().x() -
-                  quad->shared_quad_state->content_bounds.width()) <
+                  quad->shared_quad_state->quad_layer_bounds.width()) <
              kAntiAliasingEpsilon &&
          std::abs(clip_region->p3().x() -
-                  quad->shared_quad_state->content_bounds.width()) <
+                  quad->shared_quad_state->quad_layer_bounds.width()) <
              kAntiAliasingEpsilon;
 }
 }  // anonymous namespace
@@ -1584,7 +1582,7 @@
 
   gfx::Transform device_transform =
       frame->window_matrix * frame->projection_matrix *
-      quad->shared_quad_state->content_to_target_transform;
+      quad->shared_quad_state->quad_to_target_transform;
   device_transform.FlattenTo2d();
   if (!device_transform.IsInvertible())
     return;
@@ -1601,7 +1599,7 @@
     bool force_aa = false;
     device_layer_quad = MathUtil::MapQuad(
         device_transform,
-        gfx::QuadF(quad->shared_quad_state->visible_content_rect), &clipped);
+        gfx::QuadF(quad->shared_quad_state->visible_quad_layer_rect), &clipped);
     use_aa = ShouldAntialiasQuad(device_layer_quad, clipped, force_aa);
   }
 
@@ -1649,7 +1647,7 @@
   gfx::RectF centered_rect(
       gfx::PointF(-0.5f * tile_rect.width(), -0.5f * tile_rect.height()),
       tile_rect.size());
-  DrawQuadGeometry(frame, quad->shared_quad_state->content_to_target_transform,
+  DrawQuadGeometry(frame, quad->shared_quad_state->quad_to_target_transform,
                    centered_rect, uniforms.matrix_location);
 }
 
@@ -1693,7 +1691,7 @@
                                  const gfx::QuadF* clip_region) {
   gfx::Transform device_transform =
       frame->window_matrix * frame->projection_matrix *
-      quad->shared_quad_state->content_to_target_transform;
+      quad->shared_quad_state->quad_to_target_transform;
   device_transform.FlattenTo2d();
 
   gfx::QuadF device_layer_quad;
@@ -1704,7 +1702,7 @@
     bool force_aa = false;
     device_layer_quad = MathUtil::MapQuad(
         device_transform,
-        gfx::QuadF(quad->shared_quad_state->visible_content_rect), &clipped);
+        gfx::QuadF(quad->shared_quad_state->visible_quad_layer_rect), &clipped);
     use_aa = ShouldAntialiasQuad(device_layer_quad, clipped, force_aa);
   }
 
@@ -1835,7 +1833,7 @@
   gfx::RectF centered_rect(
       gfx::PointF(-0.5f * tile_rect.width(), -0.5f * tile_rect.height()),
       tile_rect.size());
-  DrawQuadGeometry(frame, quad->shared_quad_state->content_to_target_transform,
+  DrawQuadGeometry(frame, quad->shared_quad_state->quad_to_target_transform,
                    centered_rect, uniforms.matrix_location);
 }
 
@@ -1851,7 +1849,7 @@
 
   bool scaled = (tex_to_geom_scale_x != 1.f || tex_to_geom_scale_y != 1.f);
   GLenum filter = (scaled ||
-                   !quad->shared_quad_state->content_to_target_transform
+                   !quad->shared_quad_state->quad_to_target_transform
                         .IsIdentityOrIntegerTranslation()) &&
                           !quad->nearest_neighbor
                       ? GL_LINEAR
@@ -1951,7 +1949,7 @@
   static float gl_matrix[16];
   ToGLMatrix(&gl_matrix[0],
              frame->projection_matrix *
-                 quad->shared_quad_state->content_to_target_transform);
+                 quad->shared_quad_state->quad_to_target_transform);
   gl_->UniformMatrix4fv(uniforms.matrix_location, 1, false, &gl_matrix[0]);
 
   gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
@@ -1963,10 +1961,8 @@
   SetBlendEnabled(quad->ShouldDrawWithBlending());
 
   TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
-      gl_,
-      &highp_threshold_cache_,
-      highp_threshold_min_,
-      quad->shared_quad_state->visible_content_rect.bottom_right());
+      gl_, &highp_threshold_cache_, highp_threshold_min_,
+      quad->shared_quad_state->visible_quad_layer_rect.bottom_right());
 
   bool use_alpha_plane = quad->a_plane_resource_id() != 0;
 
@@ -2159,8 +2155,7 @@
 
   SetShaderOpacity(quad->shared_quad_state->opacity, alpha_location);
   if (!clip_region) {
-    DrawQuadGeometry(frame,
-                     quad->shared_quad_state->content_to_target_transform,
+    DrawQuadGeometry(frame, quad->shared_quad_state->quad_to_target_transform,
                      tile_rect, matrix_location);
   } else {
     float uvs[8] = {0};
@@ -2169,7 +2164,7 @@
     region_quad.Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height());
     region_quad -= gfx::Vector2dF(0.5f, 0.5f);
     DrawQuadGeometryClippedByQuadF(
-        frame, quad->shared_quad_state->content_to_target_transform, tile_rect,
+        frame, quad->shared_quad_state->quad_to_target_transform, tile_rect,
         region_quad, matrix_location, uvs);
   }
 }
@@ -2184,10 +2179,8 @@
   DCHECK(capabilities_.using_egl_image);
 
   TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
-      gl_,
-      &highp_threshold_cache_,
-      highp_threshold_min_,
-      quad->shared_quad_state->visible_content_rect.bottom_right());
+      gl_, &highp_threshold_cache_, highp_threshold_min_,
+      quad->shared_quad_state->visible_quad_layer_rect.bottom_right());
 
   const VideoStreamTextureProgram* program =
       GetVideoStreamTextureProgram(tex_coord_precision);
@@ -2207,8 +2200,7 @@
   SetShaderOpacity(quad->shared_quad_state->opacity,
                    program->fragment_shader().alpha_location());
   if (!clip_region) {
-    DrawQuadGeometry(frame,
-                     quad->shared_quad_state->content_to_target_transform,
+    DrawQuadGeometry(frame, quad->shared_quad_state->quad_to_target_transform,
                      quad->rect, program->vertex_shader().matrix_location());
   } else {
     gfx::QuadF region_quad(*clip_region);
@@ -2217,7 +2209,7 @@
     float uvs[8] = {0};
     GetScaledUVs(quad->visible_rect, clip_region, uvs);
     DrawQuadGeometryClippedByQuadF(
-        frame, quad->shared_quad_state->content_to_target_transform, quad->rect,
+        frame, quad->shared_quad_state->quad_to_target_transform, quad->rect,
         region_quad, program->vertex_shader().matrix_location(), uvs);
   }
 }
@@ -2333,10 +2325,8 @@
   }
 
   TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
-      gl_,
-      &highp_threshold_cache_,
-      highp_threshold_min_,
-      quad->shared_quad_state->visible_content_rect.bottom_right());
+      gl_, &highp_threshold_cache_, highp_threshold_min_,
+      quad->shared_quad_state->visible_quad_layer_rect.bottom_right());
 
   ResourceProvider::ScopedReadLockGL lock(resource_provider_,
                                           quad->resource_id());
@@ -2399,7 +2389,7 @@
   // Generate the transform matrix
   gfx::Transform quad_rect_matrix;
   QuadRectTransform(&quad_rect_matrix,
-                    quad->shared_quad_state->content_to_target_transform,
+                    quad->shared_quad_state->quad_to_target_transform,
                     quad->rect);
   quad_rect_matrix = frame->projection_matrix * quad_rect_matrix;
 
@@ -2435,10 +2425,8 @@
   SetBlendEnabled(quad->ShouldDrawWithBlending());
 
   TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
-      gl_,
-      &highp_threshold_cache_,
-      highp_threshold_min_,
-      quad->shared_quad_state->visible_content_rect.bottom_right());
+      gl_, &highp_threshold_cache_, highp_threshold_min_,
+      quad->shared_quad_state->visible_quad_layer_rect.bottom_right());
 
   TexTransformTextureProgramBinding binding;
   binding.Set(GetTextureIOSurfaceProgram(tex_coord_precision));
@@ -2467,14 +2455,13 @@
   gl_->BindTexture(GL_TEXTURE_RECTANGLE_ARB, lock.texture_id());
 
   if (!clip_region) {
-    DrawQuadGeometry(frame,
-                     quad->shared_quad_state->content_to_target_transform,
+    DrawQuadGeometry(frame, quad->shared_quad_state->quad_to_target_transform,
                      quad->rect, binding.matrix_location);
   } else {
     float uvs[8] = {0};
     GetScaledUVs(quad->visible_rect, clip_region, uvs);
     DrawQuadGeometryClippedByQuadF(
-        frame, quad->shared_quad_state->content_to_target_transform, quad->rect,
+        frame, quad->shared_quad_state->quad_to_target_transform, quad->rect,
         *clip_region, binding.matrix_location, uvs);
   }
 
diff --git a/cc/output/latency_info_swap_promise.cc b/cc/output/latency_info_swap_promise.cc
index afc0f2d..5e08470 100644
--- a/cc/output/latency_info_swap_promise.cc
+++ b/cc/output/latency_info_swap_promise.cc
@@ -5,6 +5,7 @@
 #include "cc/output/latency_info_swap_promise.h"
 
 #include "base/logging.h"
+#include "base/trace_event/trace_event.h"
 
 namespace {
 ui::LatencyComponentType DidNotSwapReasonToLatencyComponentType(
@@ -49,4 +50,11 @@
   return latency_.trace_id;
 }
 
+// Trace the original LatencyInfo of a LatencyInfoSwapPromise
+void LatencyInfoSwapPromise::OnCommit() {
+  TRACE_EVENT_FLOW_STEP0("input,benchmark", "LatencyInfo.Flow",
+                         TRACE_ID_DONT_MANGLE(TraceId()),
+                         "HanldeInputEventMainCommit");
+}
+
 }  // namespace cc
diff --git a/cc/output/latency_info_swap_promise.h b/cc/output/latency_info_swap_promise.h
index 74810b8..9a4deb6 100644
--- a/cc/output/latency_info_swap_promise.h
+++ b/cc/output/latency_info_swap_promise.h
@@ -19,6 +19,7 @@
   void DidActivate() override {}
   void DidSwap(CompositorFrameMetadata* metadata) override;
   void DidNotSwap(DidNotSwapReason reason) override;
+  void OnCommit() override;
 
   int64 TraceId() const override;
 
diff --git a/cc/output/overlay_strategy_common.cc b/cc/output/overlay_strategy_common.cc
index f95b373..9c3a2342 100644
--- a/cc/output/overlay_strategy_common.cc
+++ b/cc/output/overlay_strategy_common.cc
@@ -50,7 +50,7 @@
                                                OverlayCandidate* quad_info) {
   gfx::OverlayTransform overlay_transform =
       OverlayCandidate::GetOverlayTransform(
-          quad.shared_quad_state->content_to_target_transform, quad.y_flipped);
+          quad.shared_quad_state->quad_to_target_transform, quad.y_flipped);
   if (quad.background_color != SK_ColorTRANSPARENT ||
       quad.premultiplied_alpha ||
       overlay_transform == gfx::OVERLAY_TRANSFORM_INVALID)
@@ -66,7 +66,7 @@
                                              OverlayCandidate* quad_info) {
   gfx::OverlayTransform overlay_transform =
       OverlayCandidate::GetOverlayTransform(
-          quad.shared_quad_state->content_to_target_transform, false);
+          quad.shared_quad_state->quad_to_target_transform, false);
   if (overlay_transform == gfx::OVERLAY_TRANSFORM_INVALID)
     return false;
   if (!quad.matrix.IsScaleOrTranslation()) {
@@ -125,7 +125,7 @@
 
   quad_info->format = RGBA_8888;
   quad_info->display_rect = OverlayCandidate::GetOverlayRect(
-      draw_quad.shared_quad_state->content_to_target_transform, draw_quad.rect);
+      draw_quad.shared_quad_state->quad_to_target_transform, draw_quad.rect);
   return true;
 }
 
diff --git a/cc/output/overlay_strategy_single_on_top.cc b/cc/output/overlay_strategy_single_on_top.cc
index e188916..956aa54 100644
--- a/cc/output/overlay_strategy_single_on_top.cc
+++ b/cc/output/overlay_strategy_single_on_top.cc
@@ -35,13 +35,13 @@
       // Check that no prior quads overlap it.
       bool intersects = false;
       gfx::RectF rect = draw_quad->rect;
-      draw_quad->shared_quad_state->content_to_target_transform.TransformRect(
+      draw_quad->shared_quad_state->quad_to_target_transform.TransformRect(
           &rect);
       for (auto overlap_iter = quad_list.cbegin(); overlap_iter != it;
            ++overlap_iter) {
         gfx::RectF overlap_rect = overlap_iter->rect;
-        overlap_iter->shared_quad_state->content_to_target_transform
-            .TransformRect(&overlap_rect);
+        overlap_iter->shared_quad_state->quad_to_target_transform.TransformRect(
+            &overlap_rect);
         if (rect.Intersects(overlap_rect) && !IsInvisibleQuad(*overlap_iter)) {
           intersects = true;
           break;
diff --git a/cc/output/overlay_unittest.cc b/cc/output/overlay_unittest.cc
index 448d04d..6ac9b1c64 100644
--- a/cc/output/overlay_unittest.cc
+++ b/cc/output/overlay_unittest.cc
@@ -278,8 +278,8 @@
          exp_iter != expected->quad_list.cend();
          ++exp_iter, ++act_iter) {
       EXPECT_EQ(exp_iter->rect.ToString(), act_iter->rect.ToString());
-      EXPECT_EQ(exp_iter->shared_quad_state->content_bounds.ToString(),
-                act_iter->shared_quad_state->content_bounds.ToString());
+      EXPECT_EQ(exp_iter->shared_quad_state->quad_layer_bounds.ToString(),
+                act_iter->shared_quad_state->quad_layer_bounds.ToString());
     }
   }
 }
@@ -546,7 +546,7 @@
                                 pass->shared_quad_state_list.back(),
                                 pass.get());
   pass->shared_quad_state_list.back()
-      ->content_to_target_transform.RotateAboutXAxis(45.f);
+      ->quad_to_target_transform.RotateAboutXAxis(45.f);
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
@@ -563,8 +563,8 @@
   scoped_ptr<RenderPass> pass = CreateRenderPass();
   CreateCandidateQuadAt(resource_provider_.get(),
                         pass->shared_quad_state_list.back(), pass.get(), rect);
-  pass->shared_quad_state_list.back()->content_to_target_transform.Scale(2.0f,
-                                                                         -1.0f);
+  pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(2.0f,
+                                                                      -1.0f);
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
@@ -583,8 +583,8 @@
   scoped_ptr<RenderPass> pass = CreateRenderPass();
   CreateCandidateQuadAt(resource_provider_.get(),
                         pass->shared_quad_state_list.back(), pass.get(), rect);
-  pass->shared_quad_state_list.back()->content_to_target_transform.Scale(-1.0f,
-                                                                         2.0f);
+  pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(-1.0f,
+                                                                      2.0f);
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
@@ -602,8 +602,8 @@
   scoped_ptr<RenderPass> pass = CreateRenderPass();
   CreateCandidateQuadAt(resource_provider_.get(),
                         pass->shared_quad_state_list.back(), pass.get(), rect);
-  pass->shared_quad_state_list.back()->content_to_target_transform.Scale(2.0f,
-                                                                         1.0f);
+  pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(2.0f,
+                                                                      1.0f);
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
@@ -620,7 +620,7 @@
   CreateCandidateQuadAt(resource_provider_.get(),
                         pass->shared_quad_state_list.back(), pass.get(), rect);
   pass->shared_quad_state_list.back()
-      ->content_to_target_transform.RotateAboutZAxis(90.f);
+      ->quad_to_target_transform.RotateAboutZAxis(90.f);
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
@@ -638,7 +638,7 @@
   CreateCandidateQuadAt(resource_provider_.get(),
                         pass->shared_quad_state_list.back(), pass.get(), rect);
   pass->shared_quad_state_list.back()
-      ->content_to_target_transform.RotateAboutZAxis(180.f);
+      ->quad_to_target_transform.RotateAboutZAxis(180.f);
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
@@ -656,7 +656,7 @@
   CreateCandidateQuadAt(resource_provider_.get(),
                         pass->shared_quad_state_list.back(), pass.get(), rect);
   pass->shared_quad_state_list.back()
-      ->content_to_target_transform.RotateAboutZAxis(270.f);
+      ->quad_to_target_transform.RotateAboutZAxis(270.f);
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc
index 136550c..d6a8e5d 100644
--- a/cc/output/renderer_pixeltest.cc
+++ b/cc/output/renderer_pixeltest.cc
@@ -47,48 +47,38 @@
 }
 
 SharedQuadState* CreateTestSharedQuadState(
-    gfx::Transform content_to_target_transform,
+    gfx::Transform quad_to_target_transform,
     const gfx::Rect& rect,
     RenderPass* render_pass) {
-  const gfx::Size content_bounds = rect.size();
-  const gfx::Rect visible_content_rect = rect;
+  const gfx::Size layer_bounds = rect.size();
+  const gfx::Rect visible_layer_rect = rect;
   const gfx::Rect clip_rect = rect;
   const bool is_clipped = false;
   const float opacity = 1.0f;
   const SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode;
   int sorting_context_id = 0;
   SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(content_to_target_transform,
-                       content_bounds,
-                       visible_content_rect,
-                       clip_rect,
-                       is_clipped,
-                       opacity,
-                       blend_mode,
-                       sorting_context_id);
+  shared_state->SetAll(quad_to_target_transform, layer_bounds,
+                       visible_layer_rect, clip_rect, is_clipped, opacity,
+                       blend_mode, sorting_context_id);
   return shared_state;
 }
 
 SharedQuadState* CreateTestSharedQuadStateClipped(
-    gfx::Transform content_to_target_transform,
+    gfx::Transform quad_to_target_transform,
     const gfx::Rect& rect,
     const gfx::Rect& clip_rect,
     RenderPass* render_pass) {
-  const gfx::Size content_bounds = rect.size();
-  const gfx::Rect visible_content_rect = clip_rect;
+  const gfx::Size layer_bounds = rect.size();
+  const gfx::Rect visible_layer_rect = clip_rect;
   const bool is_clipped = true;
   const float opacity = 1.0f;
   const SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode;
   int sorting_context_id = 0;
   SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(content_to_target_transform,
-                       content_bounds,
-                       visible_content_rect,
-                       clip_rect,
-                       is_clipped,
-                       opacity,
-                       blend_mode,
-                       sorting_context_id);
+  shared_state->SetAll(quad_to_target_transform, layer_bounds,
+                       visible_layer_rect, clip_rect, is_clipped, opacity,
+                       blend_mode, sorting_context_id);
   return shared_state;
 }
 
@@ -1220,9 +1210,9 @@
   scoped_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
+  gfx::Transform quad_to_target_transform;
   SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
   shared_state->opacity = 0.5f;
 
   gfx::Rect blue_rect(0,
@@ -1241,7 +1231,7 @@
   yellow->SetNew(shared_state, yellow_rect, yellow_rect, SK_ColorYELLOW, false);
 
   SharedQuadState* blank_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
 
   SolidColorDrawQuad* white =
       child_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -1312,9 +1302,9 @@
   scoped_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
+  gfx::Transform quad_to_target_transform;
   SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
   shared_state->opacity = 0.5f;
 
   gfx::Rect blue_rect(0,
@@ -1333,7 +1323,7 @@
   yellow->SetNew(shared_state, yellow_rect, yellow_rect, SK_ColorYELLOW, false);
 
   SharedQuadState* blank_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
 
   SolidColorDrawQuad* white =
       child_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -1383,9 +1373,9 @@
   scoped_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
+  gfx::Transform quad_to_target_transform;
   SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
   shared_state->opacity = 0.5f;
 
   gfx::Rect blue_rect(0,
@@ -1404,7 +1394,7 @@
   yellow->SetNew(shared_state, yellow_rect, yellow_rect, SK_ColorYELLOW, false);
 
   SharedQuadState* blank_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
 
   SolidColorDrawQuad* white =
       child_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -1456,9 +1446,9 @@
   scoped_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
+  gfx::Transform quad_to_target_transform;
   SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
   shared_state->opacity = 0.5f;
 
   gfx::Rect blue_rect(0,
@@ -1477,7 +1467,7 @@
   yellow->SetNew(shared_state, yellow_rect, yellow_rect, SK_ColorYELLOW, false);
 
   SharedQuadState* blank_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
 
   SolidColorDrawQuad* white =
       child_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -1552,9 +1542,9 @@
   scoped_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
+  gfx::Transform quad_to_target_transform;
   SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
 
   gfx::Rect blue_rect(0,
                       0,
@@ -1601,9 +1591,9 @@
   scoped_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
+  gfx::Transform quad_to_target_transform;
   SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
 
   gfx::Rect blue_rect(0,
                       0,
@@ -1758,41 +1748,32 @@
         CreateTestRootRenderPass(root_id, device_viewport_rect);
     root_pass->has_transparent_background = false;
 
-    gfx::Transform identity_content_to_target_transform;
+    gfx::Transform identity_quad_to_target_transform;
 
     RenderPassId filter_pass_id(2, 1);
     gfx::Transform transform_to_root;
-    scoped_ptr<RenderPass> filter_pass =
-        CreateTestRenderPass(filter_pass_id,
-                             filter_pass_content_rect_,
-                             transform_to_root);
+    scoped_ptr<RenderPass> filter_pass = CreateTestRenderPass(
+        filter_pass_id, filter_pass_layer_rect_, transform_to_root);
 
     // A non-visible quad in the filtering render pass.
     {
       SharedQuadState* shared_state =
-          CreateTestSharedQuadState(identity_content_to_target_transform,
-                                    filter_pass_content_rect_,
-                                    filter_pass.get());
+          CreateTestSharedQuadState(identity_quad_to_target_transform,
+                                    filter_pass_layer_rect_, filter_pass.get());
       SolidColorDrawQuad* color_quad =
           filter_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
-      color_quad->SetNew(shared_state,
-                         filter_pass_content_rect_,
-                         filter_pass_content_rect_,
-                         SK_ColorTRANSPARENT,
-                         false);
+      color_quad->SetNew(shared_state, filter_pass_layer_rect_,
+                         filter_pass_layer_rect_, SK_ColorTRANSPARENT, false);
     }
 
     {
       SharedQuadState* shared_state =
           CreateTestSharedQuadState(filter_pass_to_target_transform_,
-                                    filter_pass_content_rect_,
-                                    filter_pass.get());
+                                    filter_pass_layer_rect_, filter_pass.get());
       RenderPassDrawQuad* filter_pass_quad =
           root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
-      filter_pass_quad->SetNew(shared_state,
-                               filter_pass_content_rect_,
-                               filter_pass_content_rect_,
-                               filter_pass_id,
+      filter_pass_quad->SetNew(shared_state, filter_pass_layer_rect_,
+                               filter_pass_layer_rect_, filter_pass_id,
                                0,                   // mask_resource_id
                                gfx::Vector2dF(),    // mask_uv_scale
                                gfx::Size(),         // mask_texture_size
@@ -1806,7 +1787,7 @@
     gfx::Rect left_rect = gfx::Rect(0, 0, kColumnWidth, 20);
     for (int i = 0; left_rect.y() < device_viewport_rect.height(); ++i) {
       SharedQuadState* shared_state = CreateTestSharedQuadState(
-          identity_content_to_target_transform, left_rect, root_pass.get());
+          identity_quad_to_target_transform, left_rect, root_pass.get());
       SolidColorDrawQuad* color_quad =
           root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
       color_quad->SetNew(
@@ -1817,7 +1798,7 @@
     gfx::Rect middle_rect = gfx::Rect(kColumnWidth+1, 0, kColumnWidth, 20);
     for (int i = 0; middle_rect.y() < device_viewport_rect.height(); ++i) {
       SharedQuadState* shared_state = CreateTestSharedQuadState(
-          identity_content_to_target_transform, middle_rect, root_pass.get());
+          identity_quad_to_target_transform, middle_rect, root_pass.get());
       SolidColorDrawQuad* color_quad =
           root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
       color_quad->SetNew(
@@ -1828,7 +1809,7 @@
     gfx::Rect right_rect = gfx::Rect((kColumnWidth+1)*2, 0, kColumnWidth, 20);
     for (int i = 0; right_rect.y() < device_viewport_rect.height(); ++i) {
       SharedQuadState* shared_state = CreateTestSharedQuadState(
-          identity_content_to_target_transform, right_rect, root_pass.get());
+          identity_quad_to_target_transform, right_rect, root_pass.get());
       SolidColorDrawQuad* color_quad =
           root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
       color_quad->SetNew(
@@ -1837,9 +1818,8 @@
     }
 
     SharedQuadState* shared_state =
-        CreateTestSharedQuadState(identity_content_to_target_transform,
-                                  device_viewport_rect,
-                                  root_pass.get());
+        CreateTestSharedQuadState(identity_quad_to_target_transform,
+                                  device_viewport_rect, root_pass.get());
     SolidColorDrawQuad* background_quad =
         root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
     background_quad->SetNew(shared_state,
@@ -1855,7 +1835,7 @@
   RenderPassList pass_list_;
   FilterOperations background_filters_;
   gfx::Transform filter_pass_to_target_transform_;
-  gfx::Rect filter_pass_content_rect_;
+  gfx::Rect filter_pass_layer_rect_;
 };
 
 typedef ::testing::Types<GLRenderer, SoftwareRenderer>
@@ -1871,8 +1851,8 @@
   this->background_filters_.Append(
       FilterOperation::CreateInvertFilter(1.f));
 
-  this->filter_pass_content_rect_ = gfx::Rect(this->device_viewport_size_);
-  this->filter_pass_content_rect_.Inset(12, 14, 16, 18);
+  this->filter_pass_layer_rect_ = gfx::Rect(this->device_viewport_size_);
+  this->filter_pass_layer_rect_.Inset(12, 14, 16, 18);
 
   this->SetUpRenderPassList();
   EXPECT_TRUE(this->RunPixelTest(
@@ -1979,9 +1959,9 @@
   scoped_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
+  gfx::Transform quad_to_target_transform;
   SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
 
   gfx::Rect blue_rect(0,
                       0,
@@ -2036,26 +2016,26 @@
   RenderPassId id(1, 1);
   scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
-  gfx::Transform red_content_to_target_transform;
-  red_content_to_target_transform.Rotate(10);
-  SharedQuadState* red_shared_state = CreateTestSharedQuadState(
-      red_content_to_target_transform, rect, pass.get());
+  gfx::Transform red_quad_to_target_transform;
+  red_quad_to_target_transform.Rotate(10);
+  SharedQuadState* red_shared_state =
+      CreateTestSharedQuadState(red_quad_to_target_transform, rect, pass.get());
 
   SolidColorDrawQuad* red = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   red->SetNew(red_shared_state, rect, rect, SK_ColorRED, false);
 
-  gfx::Transform yellow_content_to_target_transform;
-  yellow_content_to_target_transform.Rotate(5);
+  gfx::Transform yellow_quad_to_target_transform;
+  yellow_quad_to_target_transform.Rotate(5);
   SharedQuadState* yellow_shared_state = CreateTestSharedQuadState(
-      yellow_content_to_target_transform, rect, pass.get());
+      yellow_quad_to_target_transform, rect, pass.get());
 
   SolidColorDrawQuad* yellow =
       pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   yellow->SetNew(yellow_shared_state, rect, rect, SK_ColorYELLOW, false);
 
-  gfx::Transform blue_content_to_target_transform;
+  gfx::Transform blue_quad_to_target_transform;
   SharedQuadState* blue_shared_state = CreateTestSharedQuadState(
-      blue_content_to_target_transform, rect, pass.get());
+      blue_quad_to_target_transform, rect, pass.get());
 
   SolidColorDrawQuad* blue =
       pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -2080,30 +2060,29 @@
   scoped_ptr<RenderPass> pass =
       CreateTestRenderPass(id, rect, transform_to_root);
 
-  gfx::Transform red_content_to_target_transform;
-  red_content_to_target_transform.Translate(50, 50);
-  red_content_to_target_transform.Scale(
-      0.5f + 1.0f / (rect.width() * 2.0f),
-      0.5f + 1.0f / (rect.height() * 2.0f));
-  SharedQuadState* red_shared_state = CreateTestSharedQuadState(
-      red_content_to_target_transform, rect, pass.get());
+  gfx::Transform red_quad_to_target_transform;
+  red_quad_to_target_transform.Translate(50, 50);
+  red_quad_to_target_transform.Scale(0.5f + 1.0f / (rect.width() * 2.0f),
+                                     0.5f + 1.0f / (rect.height() * 2.0f));
+  SharedQuadState* red_shared_state =
+      CreateTestSharedQuadState(red_quad_to_target_transform, rect, pass.get());
 
   SolidColorDrawQuad* red = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   red->SetNew(red_shared_state, rect, rect, SK_ColorRED, false);
 
-  gfx::Transform yellow_content_to_target_transform;
-  yellow_content_to_target_transform.Translate(25.5f, 25.5f);
-  yellow_content_to_target_transform.Scale(0.5f, 0.5f);
+  gfx::Transform yellow_quad_to_target_transform;
+  yellow_quad_to_target_transform.Translate(25.5f, 25.5f);
+  yellow_quad_to_target_transform.Scale(0.5f, 0.5f);
   SharedQuadState* yellow_shared_state = CreateTestSharedQuadState(
-      yellow_content_to_target_transform, rect, pass.get());
+      yellow_quad_to_target_transform, rect, pass.get());
 
   SolidColorDrawQuad* yellow =
       pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   yellow->SetNew(yellow_shared_state, rect, rect, SK_ColorYELLOW, false);
 
-  gfx::Transform blue_content_to_target_transform;
+  gfx::Transform blue_quad_to_target_transform;
   SharedQuadState* blue_shared_state = CreateTestSharedQuadState(
-      blue_content_to_target_transform, rect, pass.get());
+      blue_quad_to_target_transform, rect, pass.get());
 
   SolidColorDrawQuad* blue =
       pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -2128,22 +2107,21 @@
   scoped_ptr<RenderPass> pass =
       CreateTestRenderPass(id, rect, transform_to_root);
 
-  gfx::Transform hole_content_to_target_transform;
-  hole_content_to_target_transform.Translate(50, 50);
-  hole_content_to_target_transform.Scale(
-      0.5f + 1.0f / (rect.width() * 2.0f),
-      0.5f + 1.0f / (rect.height() * 2.0f));
+  gfx::Transform hole_quad_to_target_transform;
+  hole_quad_to_target_transform.Translate(50, 50);
+  hole_quad_to_target_transform.Scale(0.5f + 1.0f / (rect.width() * 2.0f),
+                                      0.5f + 1.0f / (rect.height() * 2.0f));
   SharedQuadState* hole_shared_state = CreateTestSharedQuadState(
-      hole_content_to_target_transform, rect, pass.get());
+      hole_quad_to_target_transform, rect, pass.get());
 
   SolidColorDrawQuad* hole =
       pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   hole->SetAll(
       hole_shared_state, rect, rect, rect, false, SK_ColorTRANSPARENT, true);
 
-  gfx::Transform green_content_to_target_transform;
+  gfx::Transform green_quad_to_target_transform;
   SharedQuadState* green_shared_state = CreateTestSharedQuadState(
-      green_content_to_target_transform, rect, pass.get());
+      green_quad_to_target_transform, rect, pass.get());
 
   SolidColorDrawQuad* green =
       pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -2165,13 +2143,11 @@
       CreateTestRootRenderPass(RenderPassId(1, 1), rect);
 
   gfx::Rect red_rect(0, 0, 180, 500);
-  gfx::Transform red_content_to_target_transform(
-      1.0f,  2.4520f,  10.6206f, 19.0f,
-      0.0f,  0.3528f,  5.9737f,  9.5f,
-      0.0f, -0.2250f, -0.9744f,  0.0f,
-      0.0f,  0.0225f,  0.0974f,  1.0f);
+  gfx::Transform red_quad_to_target_transform(
+      1.0f, 2.4520f, 10.6206f, 19.0f, 0.0f, 0.3528f, 5.9737f, 9.5f, 0.0f,
+      -0.2250f, -0.9744f, 0.0f, 0.0f, 0.0225f, 0.0974f, 1.0f);
   SharedQuadState* red_shared_state = CreateTestSharedQuadState(
-      red_content_to_target_transform, red_rect, pass.get());
+      red_quad_to_target_transform, red_rect, pass.get());
   SolidColorDrawQuad* red = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   red->SetNew(red_shared_state, red_rect, red_rect, SK_ColorRED, false);
 
@@ -2228,16 +2204,14 @@
   scoped_refptr<FakePicturePileImpl> blue_pile =
       FakePicturePileImpl::CreateFromPile(blue_recording.get(), nullptr);
 
-  gfx::Transform blue_content_to_target_transform;
+  gfx::Transform blue_quad_to_target_transform;
   gfx::Vector2d offset(viewport.bottom_right() - blue_rect.bottom_right());
-  blue_content_to_target_transform.Translate(offset.x(), offset.y());
+  blue_quad_to_target_transform.Translate(offset.x(), offset.y());
   gfx::RectF blue_scissor_rect = blue_clip_rect;
-  blue_content_to_target_transform.TransformRect(&blue_scissor_rect);
-  SharedQuadState* blue_shared_state =
-      CreateTestSharedQuadStateClipped(blue_content_to_target_transform,
-                                       blue_rect,
-                                       gfx::ToEnclosingRect(blue_scissor_rect),
-                                       pass.get());
+  blue_quad_to_target_transform.TransformRect(&blue_scissor_rect);
+  SharedQuadState* blue_shared_state = CreateTestSharedQuadStateClipped(
+      blue_quad_to_target_transform, blue_rect,
+      gfx::ToEnclosingRect(blue_scissor_rect), pass.get());
 
   PictureDrawQuad* blue_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
 
@@ -2257,9 +2231,9 @@
   scoped_refptr<FakePicturePileImpl> green_pile =
       FakePicturePileImpl::CreateFromPile(green_recording.get(), nullptr);
 
-  gfx::Transform green_content_to_target_transform;
+  gfx::Transform green_quad_to_target_transform;
   SharedQuadState* green_shared_state = CreateTestSharedQuadState(
-      green_content_to_target_transform, viewport, pass.get());
+      green_quad_to_target_transform, viewport, pass.get());
 
   PictureDrawQuad* green_quad =
       pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
@@ -2299,9 +2273,9 @@
   scoped_refptr<FakePicturePileImpl> green_pile =
       FakePicturePileImpl::CreateFromPile(green_recording.get(), nullptr);
 
-  gfx::Transform green_content_to_target_transform;
+  gfx::Transform green_quad_to_target_transform;
   SharedQuadState* green_shared_state = CreateTestSharedQuadState(
-      green_content_to_target_transform, viewport, pass.get());
+      green_quad_to_target_transform, viewport, pass.get());
   green_shared_state->opacity = 0.5f;
 
   PictureDrawQuad* green_quad =
@@ -2320,9 +2294,9 @@
   scoped_refptr<FakePicturePileImpl> white_pile =
       FakePicturePileImpl::CreateFromPile(white_recording.get(), nullptr);
 
-  gfx::Transform white_content_to_target_transform;
+  gfx::Transform white_quad_to_target_transform;
   SharedQuadState* white_shared_state = CreateTestSharedQuadState(
-      white_content_to_target_transform, viewport, pass.get());
+      white_quad_to_target_transform, viewport, pass.get());
 
   PictureDrawQuad* white_quad =
       pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
@@ -2391,9 +2365,9 @@
   scoped_refptr<FakePicturePileImpl> pile =
       FakePicturePileImpl::CreateFromPile(recording.get(), nullptr);
 
-  gfx::Transform content_to_target_transform;
-  SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport, pass.get());
+  gfx::Transform quad_to_target_transform;
+  SharedQuadState* shared_state =
+      CreateTestSharedQuadState(quad_to_target_transform, viewport, pass.get());
 
   PictureDrawQuad* quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
   quad->SetNew(shared_state, viewport, gfx::Rect(), viewport,
@@ -2443,9 +2417,9 @@
   scoped_refptr<FakePicturePileImpl> pile =
       FakePicturePileImpl::CreateFromPile(recording.get(), nullptr);
 
-  gfx::Transform content_to_target_transform;
-  SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport, pass.get());
+  gfx::Transform quad_to_target_transform;
+  SharedQuadState* shared_state =
+      CreateTestSharedQuadState(quad_to_target_transform, viewport, pass.get());
 
   PictureDrawQuad* quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
   quad->SetNew(shared_state, viewport, gfx::Rect(), viewport,
@@ -2494,9 +2468,9 @@
   scoped_ptr<RenderPass> pass =
       CreateTestRenderPass(id, viewport, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
-  SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport, pass.get());
+  gfx::Transform quad_to_target_transform;
+  SharedQuadState* shared_state =
+      CreateTestSharedQuadState(quad_to_target_transform, viewport, pass.get());
 
   TileDrawQuad* quad = pass->CreateAndAppendDrawQuad<TileDrawQuad>();
   quad->SetNew(shared_state, viewport, gfx::Rect(), viewport, resource,
@@ -2545,9 +2519,9 @@
   scoped_ptr<RenderPass> pass =
       CreateTestRenderPass(id, viewport, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
-  SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport, pass.get());
+  gfx::Transform quad_to_target_transform;
+  SharedQuadState* shared_state =
+      CreateTestSharedQuadState(quad_to_target_transform, viewport, pass.get());
 
   float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
   TextureDrawQuad* quad = pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
@@ -2597,9 +2571,9 @@
   scoped_ptr<RenderPass> pass =
       CreateTestRenderPass(id, viewport, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
-  SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport, pass.get());
+  gfx::Transform quad_to_target_transform;
+  SharedQuadState* shared_state =
+      CreateTestSharedQuadState(quad_to_target_transform, viewport, pass.get());
 
   float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
   TextureDrawQuad* quad = pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
@@ -2634,7 +2608,7 @@
   // a few extra "cleanup rects" need to be added to clobber the blending
   // to make the output image more clean.  This will also test subrects
   // of the layer.
-  gfx::Transform green_content_to_target_transform;
+  gfx::Transform green_quad_to_target_transform;
   gfx::Rect green_rect1(gfx::Point(80, 0), gfx::Size(20, 100));
   gfx::Rect green_rect2(gfx::Point(0, 80), gfx::Size(100, 20));
 
@@ -2653,8 +2627,8 @@
       FakePicturePileImpl::CreateFromPile(green_recording.get(), nullptr);
 
   SharedQuadState* top_right_green_shared_quad_state =
-      CreateTestSharedQuadState(
-          green_content_to_target_transform, viewport, pass.get());
+      CreateTestSharedQuadState(green_quad_to_target_transform, viewport,
+                                pass.get());
 
   PictureDrawQuad* green_quad1 =
       pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
@@ -2676,10 +2650,8 @@
       gfx::Point(viewport.width() / 2, viewport.height() / 2),
       gfx::Size(viewport.width() / 2, viewport.height() / 2));
   SharedQuadState* bottom_right_green_shared_state =
-      CreateTestSharedQuadStateClipped(green_content_to_target_transform,
-                                       viewport,
-                                       bottom_right_rect,
-                                       pass.get());
+      CreateTestSharedQuadStateClipped(green_quad_to_target_transform, viewport,
+                                       bottom_right_rect, pass.get());
   SolidColorDrawQuad* bottom_right_color_quad =
       pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   bottom_right_color_quad->SetNew(bottom_right_green_shared_state,
@@ -2729,11 +2701,11 @@
 
   // At a scale of 4x the rectangles with a width of 2.5 will take up 10 pixels,
   // so scale an additional 10x to make them 100x100.
-  gfx::Transform content_to_target_transform;
-  content_to_target_transform.Scale(10.0, 10.0);
+  gfx::Transform quad_to_target_transform;
+  quad_to_target_transform.Scale(10.0, 10.0);
   gfx::Rect quad_content_rect(gfx::Size(20, 20));
   SharedQuadState* blue_shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, quad_content_rect, pass.get());
+      quad_to_target_transform, quad_content_rect, pass.get());
 
   PictureDrawQuad* blue_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
   blue_quad->SetNew(blue_shared_state, quad_content_rect, gfx::Rect(),
@@ -2742,10 +2714,10 @@
                     content_union_rect, contents_scale, pile.get());
 
   // Fill left half of viewport with green.
-  gfx::Transform half_green_content_to_target_transform;
+  gfx::Transform half_green_quad_to_target_transform;
   gfx::Rect half_green_rect(gfx::Size(viewport.width() / 2, viewport.height()));
   SharedQuadState* half_green_shared_state = CreateTestSharedQuadState(
-      half_green_content_to_target_transform, half_green_rect, pass.get());
+      half_green_quad_to_target_transform, half_green_rect, pass.get());
   SolidColorDrawQuad* half_color_quad =
       pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   half_color_quad->SetNew(half_green_shared_state,
@@ -2780,9 +2752,9 @@
   scoped_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
+  gfx::Transform quad_to_target_transform;
   SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
 
   gfx::Rect blue_rect(0,
                       0,
@@ -2828,9 +2800,9 @@
   scoped_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
+  gfx::Transform quad_to_target_transform;
   SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
 
   gfx::Rect blue_rect(0,
                       0,
@@ -2877,9 +2849,9 @@
   scoped_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
 
-  gfx::Transform content_to_target_transform;
+  gfx::Transform quad_to_target_transform;
   SharedQuadState* shared_state = CreateTestSharedQuadState(
-      content_to_target_transform, viewport_rect, child_pass.get());
+      quad_to_target_transform, viewport_rect, child_pass.get());
 
   // Draw a green quad full-size with a blue quad in the lower-right corner.
   gfx::Rect blue_rect(this->device_viewport_size_.width() * 3 / 4,
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc
index aa55a5b..a8110ef 100644
--- a/cc/output/software_renderer.cc
+++ b/cc/output/software_renderer.cc
@@ -251,7 +251,7 @@
   TRACE_EVENT0("cc", "SoftwareRenderer::DoDrawQuad");
   gfx::Transform quad_rect_matrix;
   QuadRectTransform(&quad_rect_matrix,
-                    quad->shared_quad_state->content_to_target_transform,
+                    quad->shared_quad_state->quad_to_target_transform,
                     quad->rect);
   gfx::Transform contents_device_transform =
       frame->window_matrix * frame->projection_matrix * quad_rect_matrix;
diff --git a/cc/output/swap_promise.h b/cc/output/swap_promise.h
index 45deee8..cf0b88d 100644
--- a/cc/output/swap_promise.h
+++ b/cc/output/swap_promise.h
@@ -54,6 +54,8 @@
   virtual void DidActivate() = 0;
   virtual void DidSwap(CompositorFrameMetadata* metadata) = 0;
   virtual void DidNotSwap(DidNotSwapReason reason) = 0;
+  // This is called when the main thread starts a (blocking) commit
+  virtual void OnCommit() {}
 
   // A non-zero trace id identifies a trace flow object that is embedded in the
   // swap promise. This can be used for registering additional flow steps to
diff --git a/cc/playback/display_item_list.cc b/cc/playback/display_item_list.cc
index cb3cde8..2afa2291 100644
--- a/cc/playback/display_item_list.cc
+++ b/cc/playback/display_item_list.cc
@@ -11,6 +11,7 @@
 #include "base/trace_event/trace_event_argument.h"
 #include "cc/base/math_util.h"
 #include "cc/debug/picture_debug_util.h"
+#include "cc/debug/traced_display_item_list.h"
 #include "cc/debug/traced_picture.h"
 #include "cc/debug/traced_value.h"
 #include "cc/playback/display_item_list_settings.h"
@@ -24,12 +25,10 @@
 
 namespace {
 
-bool PictureTracingEnabled() {
+bool DisplayItemsTracingEnabled() {
   bool tracing_enabled;
   TRACE_EVENT_CATEGORY_GROUP_ENABLED(
-    TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") ","
-    TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"),
-    &tracing_enabled);
+      TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items"), &tracing_enabled);
   return tracing_enabled;
 }
 
@@ -65,9 +64,10 @@
 
 DisplayItemList::DisplayItemList(gfx::Rect layer_rect,
                                  const DisplayItemListSettings& settings)
-    : DisplayItemList(layer_rect,
-                      settings,
-                      !settings.use_cached_picture || PictureTracingEnabled()) {
+    : DisplayItemList(
+          layer_rect,
+          settings,
+          !settings.use_cached_picture || DisplayItemsTracingEnabled()) {
 }
 
 scoped_refptr<DisplayItemList> DisplayItemList::CreateWithoutCachedPicture(
@@ -210,17 +210,19 @@
 }
 
 scoped_refptr<base::trace_event::ConvertableToTraceFormat>
-DisplayItemList::AsValue() const {
+DisplayItemList::AsValue(bool include_items) const {
   DCHECK(ProcessAppendedItemsCalled());
   scoped_refptr<base::trace_event::TracedValue> state =
       new base::trace_event::TracedValue();
 
-  state->SetInteger("length", base::saturated_cast<int>(items_.size()));
-  state->BeginArray("params.items");
-  for (const DisplayItem* item : items_) {
-    item->AsValueInto(state.get());
+  if (include_items) {
+    state->BeginArray("params.items");
+    for (const DisplayItem* item : items_) {
+      item->AsValueInto(state.get());
+    }
+    state->EndArray();
   }
-  state->EndArray();
+
   state->SetValue("params.layer_rect", MathUtil::AsValue(layer_rect_));
 
   if (!layer_rect_.IsEmpty()) {
@@ -244,9 +246,12 @@
 void DisplayItemList::EmitTraceSnapshot() const {
   DCHECK(ProcessAppendedItemsCalled());
   TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+      TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items") ","
       TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") ","
       TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"),
-      "cc::DisplayItemList", this, AsValue());
+      "cc::DisplayItemList", this,
+      TracedDisplayItemList::AsTraceableDisplayItemList(this,
+          DisplayItemsTracingEnabled()));
 }
 
 void DisplayItemList::GatherPixelRefs(const gfx::Size& grid_cell_size) {
diff --git a/cc/playback/display_item_list.h b/cc/playback/display_item_list.h
index 5212a60..843e0bd 100644
--- a/cc/playback/display_item_list.h
+++ b/cc/playback/display_item_list.h
@@ -67,7 +67,8 @@
   int ApproximateOpCount() const;
   size_t PictureMemoryUsage() const;
 
-  scoped_refptr<base::trace_event::ConvertableToTraceFormat> AsValue() const;
+  scoped_refptr<base::trace_event::ConvertableToTraceFormat> AsValue(
+      bool include_items) const;
 
   void EmitTraceSnapshot() const;
 
diff --git a/cc/quads/draw_polygon.cc b/cc/quads/draw_polygon.cc
index 6544b64..cdb94d0 100644
--- a/cc/quads/draw_polygon.cc
+++ b/cc/quads/draw_polygon.cc
@@ -52,7 +52,7 @@
 // a visible content rect to make the 4 corner points from, and a transformation
 // to move it and its normal into screen space.
 DrawPolygon::DrawPolygon(const DrawQuad* original_ref,
-                         const gfx::RectF& visible_content_rect,
+                         const gfx::RectF& visible_layer_rect,
                          const gfx::Transform& transform,
                          int draw_order_index)
     : normal_(0.0f, 0.0f, 1.0f),
@@ -61,7 +61,7 @@
       is_split_(false) {
   gfx::Point3F points[8];
   int num_vertices_in_clipped_quad;
-  gfx::QuadF send_quad(visible_content_rect);
+  gfx::QuadF send_quad(visible_layer_rect);
 
   // Doing this mapping here is very important, since we can't just transform
   // the points without clipping and not run into strange geometry issues when
diff --git a/cc/quads/draw_polygon.h b/cc/quads/draw_polygon.h
index b94ff1e..93c3602 100644
--- a/cc/quads/draw_polygon.h
+++ b/cc/quads/draw_polygon.h
@@ -29,7 +29,7 @@
               const gfx::Vector3dF& normal,
               int draw_order_index = 0);
   DrawPolygon(const DrawQuad* original_ref,
-              const gfx::RectF& visible_content_rect,
+              const gfx::RectF& visible_layer_rect,
               const gfx::Transform& transform,
               int draw_order_index = 0);
 
diff --git a/cc/quads/draw_quad.cc b/cc/quads/draw_quad.cc
index 1142cc10..606d7ab 100644
--- a/cc/quads/draw_quad.cc
+++ b/cc/quads/draw_quad.cc
@@ -62,10 +62,9 @@
   MathUtil::AddToTracedValue("content_space_rect", rect, value);
 
   bool rect_is_clipped;
-  gfx::QuadF rect_as_target_space_quad = MathUtil::MapQuad(
-      shared_quad_state->content_to_target_transform,
-      gfx::QuadF(rect),
-      &rect_is_clipped);
+  gfx::QuadF rect_as_target_space_quad =
+      MathUtil::MapQuad(shared_quad_state->quad_to_target_transform,
+                        gfx::QuadF(rect), &rect_is_clipped);
   MathUtil::AddToTracedValue("rect_as_target_space_quad",
                              rect_as_target_space_quad, value);
 
@@ -74,10 +73,9 @@
   MathUtil::AddToTracedValue("content_space_opaque_rect", opaque_rect, value);
 
   bool opaque_rect_is_clipped;
-  gfx::QuadF opaque_rect_as_target_space_quad = MathUtil::MapQuad(
-      shared_quad_state->content_to_target_transform,
-      gfx::QuadF(opaque_rect),
-      &opaque_rect_is_clipped);
+  gfx::QuadF opaque_rect_as_target_space_quad =
+      MathUtil::MapQuad(shared_quad_state->quad_to_target_transform,
+                        gfx::QuadF(opaque_rect), &opaque_rect_is_clipped);
   MathUtil::AddToTracedValue("opaque_rect_as_target_space_quad",
                              opaque_rect_as_target_space_quad, value);
 
@@ -86,10 +84,9 @@
   MathUtil::AddToTracedValue("content_space_visible_rect", visible_rect, value);
 
   bool visible_rect_is_clipped;
-  gfx::QuadF visible_rect_as_target_space_quad = MathUtil::MapQuad(
-      shared_quad_state->content_to_target_transform,
-      gfx::QuadF(visible_rect),
-      &visible_rect_is_clipped);
+  gfx::QuadF visible_rect_as_target_space_quad =
+      MathUtil::MapQuad(shared_quad_state->quad_to_target_transform,
+                        gfx::QuadF(visible_rect), &visible_rect_is_clipped);
 
   MathUtil::AddToTracedValue("visible_rect_as_target_space_quad",
                              visible_rect_as_target_space_quad, value);
diff --git a/cc/quads/draw_quad.h b/cc/quads/draw_quad.h
index d5d431e..df3bc12 100644
--- a/cc/quads/draw_quad.h
+++ b/cc/quads/draw_quad.h
@@ -96,13 +96,13 @@
   // Is the right edge of this tile aligned with the originating layer's
   // right edge?
   bool IsRightEdge() const {
-    return rect.right() == shared_quad_state->content_bounds.width();
+    return rect.right() == shared_quad_state->quad_layer_bounds.width();
   }
 
   // Is the bottom edge of this tile aligned with the originating layer's
   // bottom edge?
   bool IsBottomEdge() const {
-    return rect.bottom() == shared_quad_state->content_bounds.height();
+    return rect.bottom() == shared_quad_state->quad_layer_bounds.height();
   }
 
   // Is any edge of this tile aligned with the originating layer's
diff --git a/cc/quads/draw_quad_perftest.cc b/cc/quads/draw_quad_perftest.cc
index 0473238..08ce6d0 100644
--- a/cc/quads/draw_quad_perftest.cc
+++ b/cc/quads/draw_quad_perftest.cc
@@ -23,7 +23,7 @@
 SharedQuadState* CreateSharedQuadState(RenderPass* render_pass) {
   gfx::Transform quad_transform = gfx::Transform(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
   gfx::Size content_bounds(26, 28);
-  gfx::Rect visible_content_rect(10, 12, 14, 16);
+  gfx::Rect visible_layer_rect(10, 12, 14, 16);
   gfx::Rect clip_rect(19, 21, 23, 25);
   bool is_clipped = false;
   float opacity = 1.f;
@@ -31,7 +31,7 @@
   SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode;
 
   SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState();
-  state->SetAll(quad_transform, content_bounds, visible_content_rect, clip_rect,
+  state->SetAll(quad_transform, content_bounds, visible_layer_rect, clip_rect,
                 is_clipped, opacity, blend_mode, sorting_context_id);
   return state;
 }
diff --git a/cc/quads/draw_quad_unittest.cc b/cc/quads/draw_quad_unittest.cc
index 5bce42e..833f388 100644
--- a/cc/quads/draw_quad_unittest.cc
+++ b/cc/quads/draw_quad_unittest.cc
@@ -34,8 +34,8 @@
 
 TEST(DrawQuadTest, CopySharedQuadState) {
   gfx::Transform quad_transform = gfx::Transform(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
-  gfx::Size content_bounds(26, 28);
-  gfx::Rect visible_content_rect(10, 12, 14, 16);
+  gfx::Size layer_bounds(26, 28);
+  gfx::Rect visible_layer_rect(10, 12, 14, 16);
   gfx::Rect clip_rect(19, 21, 23, 25);
   bool is_clipped = true;
   float opacity = 0.25f;
@@ -43,19 +43,13 @@
   int sorting_context_id = 65536;
 
   scoped_ptr<SharedQuadState> state(new SharedQuadState);
-  state->SetAll(quad_transform,
-                content_bounds,
-                visible_content_rect,
-                clip_rect,
-                is_clipped,
-                opacity,
-                blend_mode,
-                sorting_context_id);
+  state->SetAll(quad_transform, layer_bounds, visible_layer_rect, clip_rect,
+                is_clipped, opacity, blend_mode, sorting_context_id);
 
   scoped_ptr<SharedQuadState> copy(new SharedQuadState);
   copy->CopyFrom(state.get());
-  EXPECT_EQ(quad_transform, copy->content_to_target_transform);
-  EXPECT_EQ(visible_content_rect, copy->visible_content_rect);
+  EXPECT_EQ(quad_transform, copy->quad_to_target_transform);
+  EXPECT_EQ(visible_layer_rect, copy->visible_quad_layer_rect);
   EXPECT_EQ(opacity, copy->opacity);
   EXPECT_EQ(clip_rect, copy->clip_rect);
   EXPECT_EQ(is_clipped, copy->is_clipped);
@@ -64,8 +58,8 @@
 
 SharedQuadState* CreateSharedQuadState(RenderPass* render_pass) {
   gfx::Transform quad_transform = gfx::Transform(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
-  gfx::Size content_bounds(26, 28);
-  gfx::Rect visible_content_rect(10, 12, 14, 16);
+  gfx::Size layer_bounds(26, 28);
+  gfx::Rect visible_layer_rect(10, 12, 14, 16);
   gfx::Rect clip_rect(19, 21, 23, 25);
   bool is_clipped = false;
   float opacity = 1.f;
@@ -73,14 +67,8 @@
   SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode;
 
   SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState();
-  state->SetAll(quad_transform,
-                content_bounds,
-                visible_content_rect,
-                clip_rect,
-                is_clipped,
-                opacity,
-                blend_mode,
-                sorting_context_id);
+  state->SetAll(quad_transform, layer_bounds, visible_layer_rect, clip_rect,
+                is_clipped, opacity, blend_mode, sorting_context_id);
   return state;
 }
 
diff --git a/cc/quads/render_pass_unittest.cc b/cc/quads/render_pass_unittest.cc
index e6f39bca..65183af 100644
--- a/cc/quads/render_pass_unittest.cc
+++ b/cc/quads/render_pass_unittest.cc
@@ -57,8 +57,8 @@
          exp_iter != expected->quad_list.cend();
          ++exp_iter, ++act_iter) {
       EXPECT_EQ(exp_iter->rect.ToString(), act_iter->rect.ToString());
-      EXPECT_EQ(exp_iter->shared_quad_state->content_bounds.ToString(),
-                act_iter->shared_quad_state->content_bounds.ToString());
+      EXPECT_EQ(exp_iter->shared_quad_state->quad_layer_bounds.ToString(),
+                act_iter->shared_quad_state->quad_layer_bounds.ToString());
     }
   }
 }
diff --git a/cc/quads/shared_quad_state.cc b/cc/quads/shared_quad_state.cc
index 5e46fcc..e09cdaf 100644
--- a/cc/quads/shared_quad_state.cc
+++ b/cc/quads/shared_quad_state.cc
@@ -28,17 +28,17 @@
   *this = *other;
 }
 
-void SharedQuadState::SetAll(const gfx::Transform& content_to_target_transform,
-                             const gfx::Size& content_bounds,
-                             const gfx::Rect& visible_content_rect,
+void SharedQuadState::SetAll(const gfx::Transform& quad_to_target_transform,
+                             const gfx::Size& quad_layer_bounds,
+                             const gfx::Rect& visible_quad_layer_rect,
                              const gfx::Rect& clip_rect,
                              bool is_clipped,
                              float opacity,
                              SkXfermode::Mode blend_mode,
                              int sorting_context_id) {
-  this->content_to_target_transform = content_to_target_transform;
-  this->content_bounds = content_bounds;
-  this->visible_content_rect = visible_content_rect;
+  this->quad_to_target_transform = quad_to_target_transform;
+  this->quad_layer_bounds = quad_layer_bounds;
+  this->visible_quad_layer_rect = visible_quad_layer_rect;
   this->clip_rect = clip_rect;
   this->is_clipped = is_clipped;
   this->opacity = opacity;
@@ -47,10 +47,10 @@
 }
 
 void SharedQuadState::AsValueInto(base::trace_event::TracedValue* value) const {
-  MathUtil::AddToTracedValue("transform", content_to_target_transform, value);
-  MathUtil::AddToTracedValue("layer_content_bounds", content_bounds, value);
-  MathUtil::AddToTracedValue("layer_visible_content_rect", visible_content_rect,
-                             value);
+  MathUtil::AddToTracedValue("transform", quad_to_target_transform, value);
+  MathUtil::AddToTracedValue("layer_content_bounds", quad_layer_bounds, value);
+  MathUtil::AddToTracedValue("layer_visible_content_rect",
+                             visible_quad_layer_rect, value);
 
   value->SetBoolean("is_clipped", is_clipped);
 
diff --git a/cc/quads/shared_quad_state.h b/cc/quads/shared_quad_state.h
index 6f0ef929..a66c7bc 100644
--- a/cc/quads/shared_quad_state.h
+++ b/cc/quads/shared_quad_state.h
@@ -32,9 +32,9 @@
 
   void CopyFrom(const SharedQuadState* other);
 
-  void SetAll(const gfx::Transform& content_to_target_transform,
-              const gfx::Size& content_bounds,
-              const gfx::Rect& visible_content_rect,
+  void SetAll(const gfx::Transform& quad_to_target_transform,
+              const gfx::Size& layer_bounds,
+              const gfx::Rect& visible_layer_rect,
               const gfx::Rect& clip_rect,
               bool is_clipped,
               float opacity,
@@ -42,12 +42,13 @@
               int sorting_context_id);
   void AsValueInto(base::trace_event::TracedValue* dict) const;
 
-  // Transforms from quad's original content space to its target content space.
-  gfx::Transform content_to_target_transform;
-  // This size lives in the content space for the quad's originating layer.
-  gfx::Size content_bounds;
-  // This rect lives in the content space for the quad's originating layer.
-  gfx::Rect visible_content_rect;
+  // Transforms quad rects into the target content space.
+  gfx::Transform quad_to_target_transform;
+  // The size of the quads' originating layer in the space of the quad rects.
+  gfx::Size quad_layer_bounds;
+  // The size of the visible area in the quads' originating layer, in the space
+  // of the quad rects.
+  gfx::Rect visible_quad_layer_rect;
   // This rect lives in the target content space.
   gfx::Rect clip_rect;
   bool is_clipped;
diff --git a/cc/surfaces/surface_aggregator.cc b/cc/surfaces/surface_aggregator.cc
index a6af98ea..e1ee987a 100644
--- a/cc/surfaces/surface_aggregator.cc
+++ b/cc/surfaces/surface_aggregator.cc
@@ -259,7 +259,7 @@
     // transform of the surface quad into account to update their transform to
     // the root surface.
     copy_pass->transform_to_root_target.ConcatTransform(
-        surface_quad->shared_quad_state->content_to_target_transform);
+        surface_quad->shared_quad_state->quad_to_target_transform);
     copy_pass->transform_to_root_target.ConcatTransform(target_transform);
     copy_pass->transform_to_root_target.ConcatTransform(
         dest_pass->transform_to_root_target);
@@ -280,14 +280,14 @@
     const QuadList& quads = last_pass.quad_list;
 
     gfx::Transform surface_transform =
-        surface_quad->shared_quad_state->content_to_target_transform;
+        surface_quad->shared_quad_state->quad_to_target_transform;
     surface_transform.ConcatTransform(target_transform);
 
     // Intersect the transformed visible rect and the clip rect to create a
     // smaller cliprect for the quad.
     ClipData surface_quad_clip_rect(
         true, MathUtil::MapEnclosingClippedRect(
-                  surface_quad->shared_quad_state->content_to_target_transform,
+                  surface_quad->shared_quad_state->quad_to_target_transform,
                   surface_quad->visible_rect));
     if (surface_quad->shared_quad_state->is_clipped) {
       surface_quad_clip_rect.rect.Intersect(
@@ -324,7 +324,7 @@
   dest_pass->damage_rect = gfx::UnionRects(
       dest_pass->damage_rect,
       MathUtil::MapEnclosingClippedRect(
-          surface_quad->shared_quad_state->content_to_target_transform,
+          surface_quad->shared_quad_state->quad_to_target_transform,
           surface_damage));
 
   referenced_surfaces_.erase(it);
@@ -344,7 +344,7 @@
   // target space of the pass. This will be identity except when copying the
   // root draw pass from a surface into a pass when the surface draw quad's
   // transform is not identity.
-  copy_shared_quad_state->content_to_target_transform.ConcatTransform(
+  copy_shared_quad_state->quad_to_target_transform.ConcatTransform(
       target_transform);
 
   ClipData new_clip_rect = CalculateClipRect(
@@ -404,7 +404,7 @@
         dest_pass->damage_rect = gfx::UnionRects(
             dest_pass->damage_rect,
             MathUtil::MapEnclosingClippedRect(
-                dest_quad->shared_quad_state->content_to_target_transform,
+                dest_quad->shared_quad_state->quad_to_target_transform,
                 pass_damage));
       } else {
         dest_quad = dest_pass->CopyFromAndAppendDrawQuad(
diff --git a/cc/surfaces/surface_aggregator_test_helpers.cc b/cc/surfaces/surface_aggregator_test_helpers.cc
index b21b37e..f7da1a2 100644
--- a/cc/surfaces/surface_aggregator_test_helpers.cc
+++ b/cc/surfaces/surface_aggregator_test_helpers.cc
@@ -26,22 +26,17 @@
                         const gfx::Size& surface_size,
                         float opacity,
                         SurfaceId surface_id) {
-  gfx::Transform content_to_target_transform;
-  gfx::Size content_bounds = surface_size;
-  gfx::Rect visible_content_rect = gfx::Rect(surface_size);
+  gfx::Transform layer_to_target_transform;
+  gfx::Size layer_bounds = surface_size;
+  gfx::Rect visible_layer_rect = gfx::Rect(surface_size);
   gfx::Rect clip_rect = gfx::Rect(surface_size);
   bool is_clipped = false;
   SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode;
 
   SharedQuadState* shared_quad_state = pass->CreateAndAppendSharedQuadState();
-  shared_quad_state->SetAll(content_to_target_transform,
-                            content_bounds,
-                            visible_content_rect,
-                            clip_rect,
-                            is_clipped,
-                            opacity,
-                            blend_mode,
-                            0);
+  shared_quad_state->SetAll(layer_to_target_transform, layer_bounds,
+                            visible_layer_rect, clip_rect, is_clipped, opacity,
+                            blend_mode, 0);
 
   SurfaceDrawQuad* surface_quad =
       pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
diff --git a/cc/surfaces/surface_aggregator_unittest.cc b/cc/surfaces/surface_aggregator_unittest.cc
index 981a573..b657efa 100644
--- a/cc/surfaces/surface_aggregator_unittest.cc
+++ b/cc/surfaces/surface_aggregator_unittest.cc
@@ -690,9 +690,9 @@
 void AddSolidColorQuadWithBlendMode(const gfx::Size& size,
                                     RenderPass* pass,
                                     const SkXfermode::Mode blend_mode) {
-  const gfx::Transform content_to_target_transform;
-  const gfx::Size content_bounds(size);
-  const gfx::Rect visible_content_rect(size);
+  const gfx::Transform layer_to_target_transform;
+  const gfx::Size layer_bounds(size);
+  const gfx::Rect visible_layer_rect(size);
   const gfx::Rect clip_rect(size);
 
   bool is_clipped = false;
@@ -700,21 +700,13 @@
 
   bool force_anti_aliasing_off = false;
   SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
-  sqs->SetAll(content_to_target_transform,
-              content_bounds,
-              visible_content_rect,
-              clip_rect,
-              is_clipped,
-              opacity,
-              blend_mode,
-              0);
+  sqs->SetAll(layer_to_target_transform, layer_bounds, visible_layer_rect,
+              clip_rect, is_clipped, opacity, blend_mode, 0);
 
   SolidColorDrawQuad* color_quad =
       pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
-  color_quad->SetNew(pass->shared_quad_state_list.back(),
-                     visible_content_rect,
-                     visible_content_rect,
-                     SK_ColorGREEN,
+  color_quad->SetNew(pass->shared_quad_state_list.back(), visible_layer_rect,
+                     visible_layer_rect, SK_ColorGREEN,
                      force_anti_aliasing_off);
 }
 
@@ -889,12 +881,12 @@
     child_nonroot_pass->transform_to_root_target.Translate(8, 0);
     SharedQuadState* child_nonroot_pass_sqs =
         child_nonroot_pass->shared_quad_state_list.front();
-    child_nonroot_pass_sqs->content_to_target_transform.Translate(5, 0);
+    child_nonroot_pass_sqs->quad_to_target_transform.Translate(5, 0);
 
     RenderPass* child_root_pass = child_pass_list.at(1u);
     SharedQuadState* child_root_pass_sqs =
         child_root_pass->shared_quad_state_list.front();
-    child_root_pass_sqs->content_to_target_transform.Translate(8, 0);
+    child_root_pass_sqs->quad_to_target_transform.Translate(8, 0);
     child_root_pass_sqs->is_clipped = true;
     child_root_pass_sqs->clip_rect = gfx::Rect(0, 0, 5, 5);
 
@@ -927,7 +919,7 @@
         gfx::Rect(0, 1, 100, 7);
     SharedQuadState* middle_root_pass_sqs =
         middle_root_pass->shared_quad_state_list.front();
-    middle_root_pass_sqs->content_to_target_transform.Scale(2, 3);
+    middle_root_pass_sqs->quad_to_target_transform.Scale(2, 3);
 
     scoped_ptr<DelegatedFrameData> middle_frame_data(new DelegatedFrameData);
     middle_pass_list.swap(middle_frame_data->render_pass_list);
@@ -956,10 +948,10 @@
 
   root_pass_list.at(0)
       ->shared_quad_state_list.front()
-      ->content_to_target_transform.Translate(0, 7);
+      ->quad_to_target_transform.Translate(0, 7);
   root_pass_list.at(0)
       ->shared_quad_state_list.ElementAt(1)
-      ->content_to_target_transform.Translate(0, 10);
+      ->quad_to_target_transform.Translate(0, 10);
   root_pass_list.at(0)->quad_list.ElementAt(1)->visible_rect =
       gfx::Rect(0, 0, 8, 100);
 
@@ -1005,7 +997,7 @@
   gfx::Transform expected_aggregated_first_pass_sqs_transform;
   expected_aggregated_first_pass_sqs_transform.Translate(5, 0);
   EXPECT_EQ(expected_aggregated_first_pass_sqs_transform.ToString(),
-            aggregated_first_pass_sqs->content_to_target_transform.ToString());
+            aggregated_first_pass_sqs->quad_to_target_transform.ToString());
 
   // The first pass's transform to the root target should include the aggregated
   // transform, including the transform from the child pass to the root.
@@ -1036,7 +1028,7 @@
        iter != aggregated_pass_list[1]->quad_list.cend();
        ++iter) {
     EXPECT_EQ(expected_root_pass_quad_transforms[iter.index()].ToString(),
-              iter->shared_quad_state->content_to_target_transform.ToString())
+              iter->shared_quad_state->quad_to_target_transform.ToString())
         << iter.index();
   }
 
@@ -1073,7 +1065,7 @@
   RenderPass* child_root_pass = child_pass_list.at(0u);
   SharedQuadState* child_root_pass_sqs =
       child_root_pass->shared_quad_state_list.front();
-  child_root_pass_sqs->content_to_target_transform.Translate(8, 0);
+  child_root_pass_sqs->quad_to_target_transform.Translate(8, 0);
 
   scoped_ptr<DelegatedFrameData> child_frame_data(new DelegatedFrameData);
   child_pass_list.swap(child_frame_data->render_pass_list);
@@ -1100,7 +1092,7 @@
 
   root_pass_list.at(0)
       ->shared_quad_state_list.front()
-      ->content_to_target_transform.Translate(0, 10);
+      ->quad_to_target_transform.Translate(0, 10);
   root_pass_list.at(0)->damage_rect = gfx::Rect(5, 5, 10, 10);
   root_pass_list.at(1)->damage_rect = gfx::Rect(5, 5, 100, 100);
 
@@ -1138,7 +1130,7 @@
     RenderPass* child_root_pass = child_pass_list.at(0u);
     SharedQuadState* child_root_pass_sqs =
         child_root_pass->shared_quad_state_list.front();
-    child_root_pass_sqs->content_to_target_transform.Translate(8, 0);
+    child_root_pass_sqs->quad_to_target_transform.Translate(8, 0);
     child_root_pass->damage_rect = gfx::Rect(10, 10, 10, 10);
 
     scoped_ptr<DelegatedFrameData> child_frame_data(new DelegatedFrameData);
@@ -1178,7 +1170,7 @@
 
     root_pass_list.at(0)
         ->shared_quad_state_list.front()
-        ->content_to_target_transform.Translate(0, 10);
+        ->quad_to_target_transform.Translate(0, 10);
     root_pass_list.at(0)->damage_rect = gfx::Rect(0, 0, 1, 1);
 
     scoped_ptr<DelegatedFrameData> root_frame_data(new DelegatedFrameData);
@@ -1200,7 +1192,7 @@
 
     root_pass_list.at(0)
         ->shared_quad_state_list.front()
-        ->content_to_target_transform.Translate(0, 10);
+        ->quad_to_target_transform.Translate(0, 10);
     root_pass_list.at(0)->damage_rect = gfx::Rect(1, 1, 1, 1);
 
     scoped_ptr<DelegatedFrameData> root_frame_data(new DelegatedFrameData);
diff --git a/cc/surfaces/surfaces_pixeltest.cc b/cc/surfaces/surfaces_pixeltest.cc
index 528684b..bf8ad57 100644
--- a/cc/surfaces/surfaces_pixeltest.cc
+++ b/cc/surfaces/surfaces_pixeltest.cc
@@ -41,21 +41,15 @@
     RenderPass* render_pass,
     const gfx::Transform& transform,
     const gfx::Size& size) {
-  const gfx::Size content_bounds = size;
-  const gfx::Rect visible_content_rect = gfx::Rect(size);
+  const gfx::Size layer_bounds = size;
+  const gfx::Rect visible_layer_rect = gfx::Rect(size);
   const gfx::Rect clip_rect = gfx::Rect(size);
   bool is_clipped = false;
   float opacity = 1.f;
   const SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode;
   SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(transform,
-                       content_bounds,
-                       visible_content_rect,
-                       clip_rect,
-                       is_clipped,
-                       opacity,
-                       blend_mode,
-                       0);
+  shared_state->SetAll(transform, layer_bounds, visible_layer_rect, clip_rect,
+                       is_clipped, opacity, blend_mode, 0);
   return shared_state;
 }
 
diff --git a/cc/test/fake_layer_tree_host.h b/cc/test/fake_layer_tree_host.h
index f1dbb63..180e144 100644
--- a/cc/test/fake_layer_tree_host.h
+++ b/cc/test/fake_layer_tree_host.h
@@ -45,9 +45,7 @@
   using LayerTreeHost::SetOutputSurfaceLostForTesting;
   using LayerTreeHost::InitializeSingleThreaded;
   using LayerTreeHost::InitializeForTesting;
-  void UpdateLayers(ResourceUpdateQueue* queue) {
-    LayerTreeHost::UpdateLayers(queue);
-  }
+  void UpdateLayers() { LayerTreeHost::UpdateLayers(); }
 
   MicroBenchmarkController* GetMicroBenchmarkController() {
     return &micro_benchmark_controller_;
diff --git a/cc/test/fake_layer_tree_host_impl_client.cc b/cc/test/fake_layer_tree_host_impl_client.cc
index c4d38ab..e8e382d 100644
--- a/cc/test/fake_layer_tree_host_impl_client.cc
+++ b/cc/test/fake_layer_tree_host_impl_client.cc
@@ -6,12 +6,6 @@
 
 namespace cc {
 
-bool FakeLayerTreeHostImplClient::ReduceContentsTextureMemoryOnImplThread(
-    size_t limit_butes,
-    int priority_cutoff) {
-  return false;
-}
-
 bool FakeLayerTreeHostImplClient::IsInsideDraw() {
   return false;
 }
diff --git a/cc/test/fake_layer_tree_host_impl_client.h b/cc/test/fake_layer_tree_host_impl_client.h
index 4085733f..1aafd93 100644
--- a/cc/test/fake_layer_tree_host_impl_client.h
+++ b/cc/test/fake_layer_tree_host_impl_client.h
@@ -33,8 +33,6 @@
   void SetVideoNeedsBeginFrames(bool needs_begin_frames) override {}
   void PostAnimationEventsToMainThreadOnImplThread(
       scoped_ptr<AnimationEventsVector> events) override {}
-  bool ReduceContentsTextureMemoryOnImplThread(size_t limit_bytes,
-                                               int priority_cutoff) override;
   bool IsInsideDraw() override;
   void RenewTreePriority() override {}
   void PostDelayedAnimationTaskOnImplThread(const base::Closure& task,
diff --git a/cc/test/fake_painted_scrollbar_layer.cc b/cc/test/fake_painted_scrollbar_layer.cc
index 9811c5f..97736a80 100644
--- a/cc/test/fake_painted_scrollbar_layer.cc
+++ b/cc/test/fake_painted_scrollbar_layer.cc
@@ -37,8 +37,8 @@
 
 FakePaintedScrollbarLayer::~FakePaintedScrollbarLayer() {}
 
-bool FakePaintedScrollbarLayer::Update(ResourceUpdateQueue* queue) {
-  bool updated = PaintedScrollbarLayer::Update(queue);
+bool FakePaintedScrollbarLayer::Update() {
+  bool updated = PaintedScrollbarLayer::Update();
   ++update_count_;
   return updated;
 }
diff --git a/cc/test/fake_painted_scrollbar_layer.h b/cc/test/fake_painted_scrollbar_layer.h
index 05d9165..72ea250d 100644
--- a/cc/test/fake_painted_scrollbar_layer.h
+++ b/cc/test/fake_painted_scrollbar_layer.h
@@ -23,7 +23,7 @@
   int update_count() const { return update_count_; }
   void reset_update_count() { update_count_ = 0; }
 
-  bool Update(ResourceUpdateQueue* queue) override;
+  bool Update() override;
 
   void PushPropertiesTo(LayerImpl* layer) override;
 
diff --git a/cc/test/fake_picture_layer.cc b/cc/test/fake_picture_layer.cc
index c3b5908..0732b30 100644
--- a/cc/test/fake_picture_layer.cc
+++ b/cc/test/fake_picture_layer.cc
@@ -42,10 +42,10 @@
   return FakePictureLayerImpl::Create(tree_impl, layer_id_);
 }
 
-bool FakePictureLayer::Update(ResourceUpdateQueue* queue) {
+bool FakePictureLayer::Update() {
   if (disable_lcd_text_)
     draw_properties().can_use_lcd_text = false;
-  bool updated = PictureLayer::Update(queue);
+  bool updated = PictureLayer::Update();
   update_count_++;
   return updated || always_update_resources_;
 }
diff --git a/cc/test/fake_picture_layer.h b/cc/test/fake_picture_layer.h
index 7c95fd1..70938161 100644
--- a/cc/test/fake_picture_layer.h
+++ b/cc/test/fake_picture_layer.h
@@ -40,7 +40,7 @@
 
   void disable_lcd_text() { disable_lcd_text_ = true; }
 
-  bool Update(ResourceUpdateQueue* queue) override;
+  bool Update() override;
 
   void PushPropertiesTo(LayerImpl* layer) override;
 
diff --git a/cc/test/fake_picture_layer_impl.cc b/cc/test/fake_picture_layer_impl.cc
index 11d13204..93cfbc2 100644
--- a/cc/test/fake_picture_layer_impl.cc
+++ b/cc/test/fake_picture_layer_impl.cc
@@ -25,7 +25,6 @@
       use_set_valid_tile_priorities_flag_(false),
       release_resources_count_(0) {
   SetBounds(raster_source->GetSize());
-  SetContentBounds(raster_source->GetSize());
   SetRasterSourceOnPending(raster_source, Region());
 }
 
@@ -45,7 +44,6 @@
       use_set_valid_tile_priorities_flag_(false),
       release_resources_count_(0) {
   SetBounds(layer_bounds);
-  SetContentBounds(layer_bounds);
   SetRasterSourceOnPending(raster_source, Region());
 }
 
diff --git a/cc/test/layer_test_common.cc b/cc/test/layer_test_common.cc
index 2bedaf8a..abc1eb0 100644
--- a/cc/test/layer_test_common.cc
+++ b/cc/test/layer_test_common.cc
@@ -42,7 +42,7 @@
 
   for (auto iter = quads.cbegin(); iter != quads.cend(); ++iter) {
     gfx::RectF quad_rectf = MathUtil::MapClippedRect(
-        iter->shared_quad_state->content_to_target_transform,
+        iter->shared_quad_state->quad_to_target_transform,
         gfx::RectF(iter->rect));
 
     // Before testing for exact coverage in the integer world, assert that
@@ -70,29 +70,27 @@
   // No quad should exist if it's fully occluded.
   for (const auto& quad : quads) {
     gfx::Rect target_visible_rect = MathUtil::MapEnclosingClippedRect(
-        quad->shared_quad_state->content_to_target_transform,
-        quad->visible_rect);
+        quad->shared_quad_state->quad_to_target_transform, quad->visible_rect);
     EXPECT_FALSE(occluded.Contains(target_visible_rect));
   }
 
   // Quads that are fully occluded on one axis only should be shrunken.
   for (const auto& quad : quads) {
     gfx::Rect target_rect = MathUtil::MapEnclosingClippedRect(
-        quad->shared_quad_state->content_to_target_transform, quad->rect);
-    if (!quad->shared_quad_state->content_to_target_transform
+        quad->shared_quad_state->quad_to_target_transform, quad->rect);
+    if (!quad->shared_quad_state->quad_to_target_transform
              .IsIdentityOrIntegerTranslation()) {
-      DCHECK(quad->shared_quad_state->content_to_target_transform
+      DCHECK(quad->shared_quad_state->quad_to_target_transform
                  .IsPositiveScaleOrTranslation())
-          << quad->shared_quad_state->content_to_target_transform.ToString();
+          << quad->shared_quad_state->quad_to_target_transform.ToString();
       gfx::RectF target_rectf = MathUtil::MapClippedRect(
-          quad->shared_quad_state->content_to_target_transform, quad->rect);
+          quad->shared_quad_state->quad_to_target_transform, quad->rect);
       // Scale transforms allowed, as long as the final transformed rect
       // ends up on integer boundaries for ease of testing.
       DCHECK_EQ(target_rectf.ToString(), gfx::RectF(target_rect).ToString());
     }
     gfx::Rect target_visible_rect = MathUtil::MapEnclosingClippedRect(
-        quad->shared_quad_state->content_to_target_transform,
-        quad->visible_rect);
+        quad->shared_quad_state->quad_to_target_transform, quad->visible_rect);
 
     bool fully_occluded_horizontal = target_rect.x() >= occluded.x() &&
                                      target_rect.right() <= occluded.right();
@@ -115,7 +113,8 @@
     : client_(FakeLayerTreeHostClient::DIRECT_3D),
       host_(FakeLayerTreeHost::Create(&client_, &task_graph_runner_)),
       root_layer_impl_(LayerImpl::Create(host_->host_impl()->active_tree(), 1)),
-      render_pass_(RenderPass::Create()) {
+      render_pass_(RenderPass::Create()),
+      layer_impl_id_(2) {
   root_layer_impl_->SetHasRenderSurface(true);
   scoped_ptr<FakeOutputSurface> output_surface = FakeOutputSurface::Create3d();
   host_->host_impl()->InitializeRenderer(FakeOutputSurface::Create3d());
diff --git a/cc/test/layer_test_common.h b/cc/test/layer_test_common.h
index 9815e80..22d6a16 100644
--- a/cc/test/layer_test_common.h
+++ b/cc/test/layer_test_common.h
@@ -53,15 +53,26 @@
 
     template <typename T>
     T* AddChildToRoot() {
-      scoped_ptr<T> layer = T::Create(host_->host_impl()->active_tree(), 2);
+      scoped_ptr<T> layer =
+          T::Create(host_->host_impl()->active_tree(), layer_impl_id_++);
       T* ptr = layer.get();
       root_layer_impl_->AddChild(layer.Pass());
       return ptr;
     }
 
+    template <typename T>
+    T* AddChild(LayerImpl* parent) {
+      scoped_ptr<T> layer =
+          T::Create(host_->host_impl()->active_tree(), layer_impl_id_++);
+      T* ptr = layer.get();
+      parent->AddChild(layer.Pass());
+      return ptr;
+    }
+
     template <typename T, typename A>
     T* AddChildToRoot(const A& a) {
-      scoped_ptr<T> layer = T::Create(host_->host_impl()->active_tree(), 2, a);
+      scoped_ptr<T> layer =
+          T::Create(host_->host_impl()->active_tree(), layer_impl_id_++, a);
       T* ptr = layer.get();
       root_layer_impl_->AddChild(layer.Pass());
       return ptr;
@@ -70,7 +81,7 @@
     template <typename T, typename A, typename B>
     T* AddChildToRoot(const A& a, const B& b) {
       scoped_ptr<T> layer =
-          T::Create(host_->host_impl()->active_tree(), 2, a, b);
+          T::Create(host_->host_impl()->active_tree(), layer_impl_id_++, a, b);
       T* ptr = layer.get();
       root_layer_impl_->AddChild(layer.Pass());
       return ptr;
@@ -78,8 +89,8 @@
 
     template <typename T, typename A, typename B, typename C, typename D>
     T* AddChildToRoot(const A& a, const B& b, const C& c, const D& d) {
-      scoped_ptr<T> layer =
-          T::Create(host_->host_impl()->active_tree(), 2, a, b, c, d);
+      scoped_ptr<T> layer = T::Create(host_->host_impl()->active_tree(),
+                                      layer_impl_id_++, a, b, c, d);
       T* ptr = layer.get();
       root_layer_impl_->AddChild(layer.Pass());
       return ptr;
@@ -96,8 +107,8 @@
                       const C& c,
                       const D& d,
                       const E& e) {
-      scoped_ptr<T> layer =
-          T::Create(host_->host_impl()->active_tree(), 2, a, b, c, d, e);
+      scoped_ptr<T> layer = T::Create(host_->host_impl()->active_tree(),
+                                      layer_impl_id_++, a, b, c, d, e);
       T* ptr = layer.get();
       root_layer_impl_->AddChild(layer.Pass());
       return ptr;
@@ -119,6 +130,7 @@
       return host_->host_impl()->resource_provider();
     }
     LayerImpl* root_layer() const { return root_layer_impl_.get(); }
+    FakeLayerTreeHost* host() { return host_.get(); }
     FakeLayerTreeHostImpl* host_impl() const { return host_->host_impl(); }
     Proxy* proxy() const { return host_->host_impl()->proxy(); }
     const QuadList& quad_list() const { return render_pass_->quad_list; }
@@ -129,6 +141,7 @@
     scoped_ptr<FakeLayerTreeHost> host_;
     scoped_ptr<LayerImpl> root_layer_impl_;
     scoped_ptr<RenderPass> render_pass_;
+    int layer_impl_id_;
   };
 };
 
diff --git a/cc/test/layer_tree_host_common_test.cc b/cc/test/layer_tree_host_common_test.cc
index 918e7c3..e21cc14c 100644
--- a/cc/test/layer_tree_host_common_test.cc
+++ b/cc/test/layer_tree_host_common_test.cc
@@ -13,8 +13,7 @@
 namespace cc {
 
 LayerTreeHostCommonTestBase::LayerTreeHostCommonTestBase()
-    : client_(FakeLayerTreeHostClient::DIRECT_3D),
-      render_surface_layer_list_count_(0) {
+    : render_surface_layer_list_count_(0) {
 }
 
 LayerTreeHostCommonTestBase::~LayerTreeHostCommonTestBase() {
@@ -48,8 +47,6 @@
   if (create_render_surface) {
     layer->SetHasRenderSurface(true);
   }
-
-  layer->SetContentBounds(bounds);
 }
 
 void LayerTreeHostCommonTestBase::ExecuteCalculateDrawProperties(
@@ -158,9 +155,4 @@
   LayerTreeHostCommon::CalculateDrawProperties(&inputs);
 }
 
-scoped_ptr<FakeLayerTreeHost>
-LayerTreeHostCommonTestBase::CreateFakeLayerTreeHost() {
-  return FakeLayerTreeHost::Create(&client_, &task_graph_runner_);
-}
-
 }  // namespace cc
diff --git a/cc/test/layer_tree_host_common_test.h b/cc/test/layer_tree_host_common_test.h
index f1181cb..89de841 100644
--- a/cc/test/layer_tree_host_common_test.h
+++ b/cc/test/layer_tree_host_common_test.h
@@ -10,6 +10,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "cc/layers/layer_lists.h"
 #include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/layer_test_common.h"
 #include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_settings.h"
 #include "cc/trees/property_tree.h"
@@ -29,7 +30,7 @@
 class LayerImpl;
 class RenderSurfaceLayerList;
 
-class LayerTreeHostCommonTestBase {
+class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest {
  protected:
   LayerTreeHostCommonTestBase();
   virtual ~LayerTreeHostCommonTestBase();
@@ -122,8 +123,6 @@
     return render_surface_layer_list_count_;
   }
 
-  scoped_ptr<FakeLayerTreeHost> CreateFakeLayerTreeHost();
-
   const LayerSettings& layer_settings() { return layer_settings_; }
 
  private:
@@ -131,8 +130,6 @@
   scoped_ptr<std::vector<LayerImpl*>> render_surface_layer_list_impl_;
   LayerSettings layer_settings_;
 
-  FakeLayerTreeHostClient client_;
-  TestTaskGraphRunner task_graph_runner_;
   int render_surface_layer_list_count_;
 };
 
diff --git a/cc/test/render_pass_test_common.cc b/cc/test/render_pass_test_common.cc
index b2242f7e..6fda53660 100644
--- a/cc/test/render_pass_test_common.cc
+++ b/cc/test/render_pass_test_common.cc
@@ -193,8 +193,8 @@
   transformed_state->CopyFrom(shared_state);
   gfx::Transform rotation;
   rotation.Rotate(45);
-  transformed_state->content_to_target_transform =
-      transformed_state->content_to_target_transform * rotation;
+  transformed_state->quad_to_target_transform =
+      transformed_state->quad_to_target_transform * rotation;
   TileDrawQuad* transformed_tile_quad =
       this->CreateAndAppendDrawQuad<TileDrawQuad>();
   transformed_tile_quad->SetNew(transformed_state,
diff --git a/cc/trees/damage_tracker.cc b/cc/trees/damage_tracker.cc
index 235dfcfd..de11b47c 100644
--- a/cc/trees/damage_tracker.cc
+++ b/cc/trees/damage_tracker.cc
@@ -288,6 +288,7 @@
 
   gfx::RectF damage_rect =
       gfx::UnionRects(layer->update_rect(), layer->damage_rect());
+  damage_rect.Intersect(gfx::RectF(layer->bounds()));
 
   if (layer_is_new || layer->LayerPropertyChanged()) {
     // If a layer is new or has changed, then its entire layer rect affects the
@@ -300,9 +301,8 @@
   } else if (!damage_rect.IsEmpty()) {
     // If the layer properties haven't changed, then the the target surface is
     // only affected by the layer's damaged area, which could be empty.
-    gfx::Rect damage_content_rect = layer->LayerRectToContentRect(damage_rect);
-    gfx::Rect damage_rect_in_target_space = MathUtil::MapEnclosingClippedRect(
-        layer->draw_transform(), damage_content_rect);
+    gfx::Rect damage_rect_in_target_space = gfx::ToEnclosingRect(
+        MathUtil::MapClippedRect(layer->draw_transform(), damage_rect));
     target_damage_rect->Union(damage_rect_in_target_space);
   }
 }
diff --git a/cc/trees/damage_tracker_unittest.cc b/cc/trees/damage_tracker_unittest.cc
index 9727759..831b1f7 100644
--- a/cc/trees/damage_tracker_unittest.cc
+++ b/cc/trees/damage_tracker_unittest.cc
@@ -89,14 +89,12 @@
 
     root->SetPosition(gfx::PointF());
     root->SetBounds(gfx::Size(500, 500));
-    root->SetContentBounds(gfx::Size(500, 500));
     root->SetDrawsContent(true);
     root->SetHasRenderSurface(true);
     root->render_surface()->SetContentRect(gfx::Rect(0, 0, 500, 500));
 
     child->SetPosition(gfx::PointF(100.f, 100.f));
     child->SetBounds(gfx::Size(30, 30));
-    child->SetContentBounds(gfx::Size(30, 30));
     child->SetDrawsContent(true);
     root->AddChild(child.Pass());
 
@@ -121,14 +119,12 @@
 
     root->SetPosition(gfx::PointF());
     root->SetBounds(gfx::Size(500, 500));
-    root->SetContentBounds(gfx::Size(500, 500));
     root->SetDrawsContent(true);
     root->SetHasRenderSurface(true);
     root->render_surface()->SetContentRect(gfx::Rect(0, 0, 500, 500));
 
     child1->SetPosition(gfx::PointF(100.f, 100.f));
     child1->SetBounds(gfx::Size(30, 30));
-    child1->SetContentBounds(gfx::Size(30, 30));
     // With a child that draws_content, opacity will cause the layer to create
     // its own RenderSurface. This layer does not draw, but is intended to
     // create its own RenderSurface.
@@ -137,17 +133,14 @@
 
     child2->SetPosition(gfx::PointF(11.f, 11.f));
     child2->SetBounds(gfx::Size(18, 18));
-    child2->SetContentBounds(gfx::Size(18, 18));
     child2->SetDrawsContent(true);
 
     grand_child1->SetPosition(gfx::PointF(200.f, 200.f));
     grand_child1->SetBounds(gfx::Size(6, 8));
-    grand_child1->SetContentBounds(gfx::Size(6, 8));
     grand_child1->SetDrawsContent(true);
 
     grand_child2->SetPosition(gfx::PointF(190.f, 190.f));
     grand_child2->SetBounds(gfx::Size(6, 8));
-    grand_child2->SetContentBounds(gfx::Size(6, 8));
     grand_child2->SetDrawsContent(true);
 
     child1->AddChild(grand_child1.Pass());
@@ -472,7 +465,6 @@
   // Set up the child
   child->SetPosition(gfx::PointF(0.f, 0.f));
   child->SetBounds(gfx::Size(100, 100));
-  child->SetContentBounds(gfx::Size(100, 100));
   child->SetTransform(transform);
   EmulateDrawingOneFrame(root.get());
 
@@ -710,7 +702,6 @@
             LayerImpl::Create(host_impl_.active_tree(), 3);
     child2->SetPosition(gfx::PointF(400.f, 380.f));
     child2->SetBounds(gfx::Size(6, 8));
-    child2->SetContentBounds(gfx::Size(6, 8));
     child2->SetDrawsContent(true);
     root->AddChild(child2.Pass());
   }
@@ -759,7 +750,6 @@
             LayerImpl::Create(host_impl_.active_tree(), 3);
     child2->SetPosition(gfx::PointF(400.f, 380.f));
     child2->SetBounds(gfx::Size(6, 8));
-    child2->SetContentBounds(gfx::Size(6, 8));
     child2->SetDrawsContent(true);
     child2->ResetAllChangeTrackingForSubtree();
     // Sanity check the initial conditions of the test, if these asserts
@@ -792,7 +782,6 @@
             LayerImpl::Create(host_impl_.active_tree(), 3);
     child2->SetPosition(gfx::PointF(400.f, 380.f));
     child2->SetBounds(gfx::Size(6, 8));
-    child2->SetContentBounds(gfx::Size(6, 8));
     child2->SetDrawsContent(true);
     root->AddChild(child2.Pass());
   }
@@ -1042,7 +1031,6 @@
             LayerImpl::Create(host_impl_.active_tree(), 6);
     grand_child3->SetPosition(gfx::PointF(240.f, 240.f));
     grand_child3->SetBounds(gfx::Size(10, 10));
-    grand_child3->SetContentBounds(gfx::Size(10, 10));
     grand_child3->SetDrawsContent(true);
     child1->AddChild(grand_child3.Pass());
   }
@@ -1144,7 +1132,6 @@
             LayerImpl::Create(host_impl_.active_tree(), 3);
     mask_layer->SetPosition(child->position());
     mask_layer->SetBounds(child->bounds());
-    mask_layer->SetContentBounds(child->bounds());
     child->SetMaskLayer(mask_layer.Pass());
     child->SetHasRenderSurface(true);
   }
@@ -1157,7 +1144,6 @@
             LayerImpl::Create(host_impl_.active_tree(), 4);
     grand_child->SetPosition(gfx::PointF(2.f, 2.f));
     grand_child->SetBounds(gfx::Size(2, 2));
-    grand_child->SetContentBounds(gfx::Size(2, 2));
     grand_child->SetDrawsContent(true);
     child->AddChild(grand_child.Pass());
   }
@@ -1246,7 +1232,6 @@
             LayerImpl::Create(host_impl_.active_tree(), 7);
     replica_mask_layer->SetPosition(gfx::PointF());
     replica_mask_layer->SetBounds(grand_child1->bounds());
-    replica_mask_layer->SetContentBounds(grand_child1->bounds());
     grand_child1_replica->SetMaskLayer(replica_mask_layer.Pass());
   }
   LayerImpl* replica_mask_layer = grand_child1_replica->mask_layer();
@@ -1325,7 +1310,6 @@
     replica_mask_layer->SetPosition(gfx::PointF());
     // Note: this is not the transform origin being tested.
     replica_mask_layer->SetBounds(grand_child1->bounds());
-    replica_mask_layer->SetContentBounds(grand_child1->bounds());
     grand_child1_replica->SetMaskLayer(replica_mask_layer.Pass());
   }
   LayerImpl* replica_mask_layer = grand_child1_replica->mask_layer();
@@ -1459,7 +1443,6 @@
     // but has a huge negative position.
     child->SetPosition(gfx::PointF());
     child->SetBounds(gfx::Size(kBigNumber + i, kBigNumber + i));
-    child->SetContentBounds(gfx::Size(kBigNumber + i, kBigNumber + i));
     child->SetTransform(transform);
     EmulateDrawingOneFrame(root.get());
 
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index b1b4de1..e2a7fd69 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -270,7 +270,7 @@
   // Some additional conditions need to be computed at a later point after the
   // recursion is finished.
   //   - the intersection of render_surface content and layer clip_rect is empty
-  //   - the visible_content_rect is empty
+  //   - the visible_layer_rect is empty
   //
   // Note, if the layer should not have been drawn due to being fully
   // transparent, we would have skipped the entire subtree and never made it
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 4c900dc..c213c64a 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -194,13 +194,6 @@
   proxy_->SetLayerTreeHostClientReady();
 }
 
-void LayerTreeHost::DeleteContentsTexturesOnImplThread(
-    ResourceProvider* resource_provider) {
-  DCHECK(proxy_->IsImplThread());
-  if (contents_texture_manager_)
-    contents_texture_manager_->ClearAllMemory(resource_provider);
-}
-
 void LayerTreeHost::WillBeginMainFrame() {
   devtools_instrumentation::WillBeginMainThreadFrame(id(),
                                                      source_frame_number());
@@ -242,32 +235,6 @@
 void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) {
   DCHECK(proxy_->IsImplThread());
 
-  // If there are linked evicted backings, these backings' resources may be put
-  // into the impl tree, so we can't draw yet. Determine this before clearing
-  // all evicted backings.
-  bool new_impl_tree_has_no_evicted_resources = false;
-  if (contents_texture_manager_) {
-    new_impl_tree_has_no_evicted_resources =
-        !contents_texture_manager_->LinkedEvictedBackingsExist();
-
-    // If the memory limit has been increased since this now-finishing
-    // commit began, and the extra now-available memory would have been used,
-    // then request another commit.
-    if (contents_texture_manager_->MaxMemoryLimitBytes() <
-            host_impl->memory_allocation_limit_bytes() &&
-        contents_texture_manager_->MaxMemoryLimitBytes() <
-            contents_texture_manager_->MaxMemoryNeededBytes()) {
-      host_impl->SetNeedsCommit();
-    }
-
-    host_impl->set_max_memory_needed_bytes(
-        contents_texture_manager_->MaxMemoryNeededBytes());
-
-    contents_texture_manager_->UpdateBackingsState(
-        host_impl->resource_provider());
-    contents_texture_manager_->ReduceMemory(host_impl->resource_provider());
-  }
-
   bool is_new_trace;
   TRACE_EVENT_IS_NEW_TRACE(&is_new_trace);
   if (is_new_trace &&
@@ -353,11 +320,6 @@
 
   DCHECK(!sync_tree->ViewportSizeInvalid());
 
-  if (new_impl_tree_has_no_evicted_resources) {
-    if (sync_tree->ContentsTexturesPurged())
-      sync_tree->ResetContentsTexturesPurged();
-  }
-
   sync_tree->set_has_ever_been_drawn(false);
 
   {
@@ -373,6 +335,7 @@
 }
 
 void LayerTreeHost::WillCommit() {
+  OnCommitForSwapPromises();
   client_->WillCommit();
 }
 
@@ -416,19 +379,10 @@
 
 void LayerTreeHost::DidInitializeOutputSurface() {
   output_surface_lost_ = false;
-
-  if (!contents_texture_manager_ && !settings_.impl_side_painting) {
-    contents_texture_manager_ =
-        PrioritizedResourceManager::Create(proxy_.get());
-    surface_memory_placeholder_ =
-        contents_texture_manager_->CreateTexture(gfx::Size(), RGBA_8888);
-  }
-
   if (root_layer()) {
     LayerTreeHostCommon::CallFunctionForSubtree(
         root_layer(), [](Layer* layer) { layer->OnOutputSurfaceCreated(); });
   }
-
   client_->DidInitializeOutputSurface();
 }
 
@@ -693,18 +647,13 @@
   proxy->CompositeImmediately(frame_begin_time);
 }
 
-bool LayerTreeHost::UpdateLayers(ResourceUpdateQueue* queue) {
+bool LayerTreeHost::UpdateLayers() {
   DCHECK(!output_surface_lost_);
-
   if (!root_layer())
     return false;
-
   DCHECK(!root_layer()->parent());
-
-  bool result = UpdateLayers(root_layer(), queue);
-
+  bool result = DoUpdateLayers(root_layer());
   micro_benchmark_controller_.DidUpdateLayers();
-
   return result || next_commit_forces_redraw_;
 }
 
@@ -757,10 +706,9 @@
   return GetRendererCapabilities().using_shared_memory_resources;
 }
 
-bool LayerTreeHost::UpdateLayers(Layer* root_layer,
-                                 ResourceUpdateQueue* queue) {
-  TRACE_EVENT1("cc", "LayerTreeHost::UpdateLayers",
-               "source_frame_number", source_frame_number());
+bool LayerTreeHost::DoUpdateLayers(Layer* root_layer) {
+  TRACE_EVENT1("cc", "LayerTreeHost::DoUpdateLayers", "source_frame_number",
+               source_frame_number());
 
   RenderSurfaceLayerList render_surface_layer_list;
 
@@ -819,9 +767,9 @@
   bool did_paint_content = false;
   for (const auto& layer : update_layer_list) {
     // TODO(enne): temporarily clobber draw properties visible rect.
-    layer->draw_properties().visible_content_rect =
+    layer->draw_properties().visible_layer_rect =
         layer->visible_rect_from_property_trees();
-    did_paint_content |= layer->Update(queue);
+    did_paint_content |= layer->Update();
     content_is_suitable_for_gpu_rasterization_ &=
         layer->IsSuitableForGpuRasterization();
   }
@@ -1093,6 +1041,11 @@
   swap_promise_list_.clear();
 }
 
+void LayerTreeHost::OnCommitForSwapPromises() {
+  for (auto* swap_promise : swap_promise_list_)
+    swap_promise->OnCommit();
+}
+
 void LayerTreeHost::set_surface_id_namespace(uint32_t id_namespace) {
   surface_id_namespace_ = id_namespace;
 }
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index e68035a..e6e91b8a 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -121,8 +121,7 @@
   bool output_surface_lost() const { return output_surface_lost_; }
   void DidCommitAndDrawFrame() { client_->DidCommitAndDrawFrame(); }
   void DidCompleteSwapBuffers() { client_->DidCompleteSwapBuffers(); }
-  void DeleteContentsTexturesOnImplThread(ResourceProvider* resource_provider);
-  bool UpdateLayers(ResourceUpdateQueue* queue);
+  bool UpdateLayers();
 
   // Called when the compositor completed page scale animation.
   void DidCompletePageScaleAnimation();
@@ -227,10 +226,6 @@
     has_transparent_background_ = transparent;
   }
 
-  PrioritizedResourceManager* contents_texture_manager() const {
-    return contents_texture_manager_.get();
-  }
-
   void SetVisible(bool visible);
   bool visible() const { return visible_; }
 
@@ -356,10 +351,12 @@
 
   MicroBenchmarkController micro_benchmark_controller_;
 
+  void OnCommitForSwapPromises();
+
  private:
   void InitializeProxy(scoped_ptr<Proxy> proxy);
 
-  bool UpdateLayers(Layer* root_layer, ResourceUpdateQueue* queue);
+  bool DoUpdateLayers(Layer* root_layer);
   void UpdateHudLayer();
 
   void ReduceMemoryUsage();
@@ -400,9 +397,6 @@
   scoped_refptr<Layer> root_layer_;
   scoped_refptr<HeadsUpDisplayLayer> hud_layer_;
 
-  scoped_ptr<PrioritizedResourceManager> contents_texture_manager_;
-  scoped_ptr<PrioritizedResource> surface_memory_placeholder_;
-
   base::WeakPtr<InputHandler> input_handler_weak_ptr_;
   base::WeakPtr<TopControlsManager> top_controls_manager_weak_ptr_;
 
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc
index b57177b..ce268de 100644
--- a/cc/trees/layer_tree_host_common.cc
+++ b/cc/trees/layer_tree_host_common.cc
@@ -370,7 +370,7 @@
 }
 
 template <typename LayerType>
-static gfx::Rect CalculateVisibleContentRect(
+static gfx::Rect CalculateVisibleLayerRect(
     LayerType* layer,
     const gfx::Rect& clip_rect_of_target_surface_in_target_space,
     const gfx::Rect& layer_rect_in_target_space) {
@@ -432,7 +432,7 @@
   // Some additional conditions need to be computed at a later point after the
   // recursion is finished.
   //   - the intersection of render_surface content and layer clip_rect is empty
-  //   - the visible_content_rect is empty
+  //   - the visible_layer_rect is empty
   //
   // Note, if the layer should not have been drawn due to being fully
   // transparent, we would have skipped the entire subtree and never made it
@@ -710,9 +710,6 @@
   // Calculate step 1b
   gfx::Transform container_layer_space_to_container_target_surface_space =
       container->draw_transform();
-  container_layer_space_to_container_target_surface_space.Scale(
-      container->contents_scale_x(), container->contents_scale_y());
-
   gfx::Transform container_target_surface_space_to_container_layer_space;
   if (container_layer_space_to_container_target_surface_space.GetInverse(
       &container_target_surface_space_to_container_layer_space)) {
@@ -1794,10 +1791,6 @@
   // draw_transform, unless the layer itself creates a render_surface. In that
   // case, the render_surface re-parents the transforms.
   layer_draw_properties.target_space_transform = combined_transform;
-  // M[draw] = M[parent] * LT * S[layer2content]
-  layer_draw_properties.target_space_transform.Scale(
-      SK_MScalar1 / layer->contents_scale_x(),
-      SK_MScalar1 / layer->contents_scale_y());
 
   // The layer's screen_space_transform represents the transform between root
   // layer's "screen space" and local content space.
@@ -1872,8 +1865,7 @@
       // space.
       layer_draw_properties.target_space_transform.MakeIdentity();
       layer_draw_properties.target_space_transform.Scale(
-          combined_transform_scales.x() / layer->contents_scale_x(),
-          combined_transform_scales.y() / layer->contents_scale_y());
+          combined_transform_scales.x(), combined_transform_scales.y());
 
       // Inside the surface's subtree, we scale everything to the owning layer's
       // scale.  The sublayer matrix transforms layer rects into target surface
@@ -1925,7 +1917,7 @@
       DrawProperties<LayerType>& mask_layer_draw_properties =
           layer->mask_layer()->draw_properties();
       mask_layer_draw_properties.render_target = layer;
-      mask_layer_draw_properties.visible_content_rect =
+      mask_layer_draw_properties.visible_layer_rect =
           gfx::Rect(layer->bounds());
     }
 
@@ -1933,7 +1925,7 @@
       DrawProperties<LayerType>& replica_mask_draw_properties =
           layer->replica_layer()->mask_layer()->draw_properties();
       replica_mask_draw_properties.render_target = layer;
-      replica_mask_draw_properties.visible_content_rect =
+      replica_mask_draw_properties.visible_layer_rect =
           gfx::Rect(layer->bounds());
     }
 
@@ -2230,7 +2222,7 @@
   }
 
   // Compute the layer's visible content rect (the rect is in content space).
-  layer_draw_properties.visible_content_rect = CalculateVisibleContentRect(
+  layer_draw_properties.visible_layer_rect = CalculateVisibleLayerRect(
       layer, clip_rect_of_target_surface_in_target_space, rect_in_target_space);
 
   // Compute the remaining properties for the render surface, if the layer has
@@ -2290,9 +2282,8 @@
     // layer space which we need to undo and replace with a scale from the
     // surface's subtree into layer space.
     gfx::Transform screen_space_transform = layer->screen_space_transform();
-    screen_space_transform.Scale(
-        layer->contents_scale_x() / combined_transform_scales.x(),
-        layer->contents_scale_y() / combined_transform_scales.y());
+    screen_space_transform.Scale(1.0 / combined_transform_scales.x(),
+                                 1.0 / combined_transform_scales.y());
     render_surface->SetScreenSpaceTransform(screen_space_transform);
 
     if (layer->replica_layer()) {
@@ -2490,10 +2481,10 @@
 void VerifyPropertyTreeValuesForLayer(LayerType* current_layer,
                                       PropertyTrees* property_trees) {
   const bool visible_rects_match =
-      ApproximatelyEqual(current_layer->visible_content_rect(),
+      ApproximatelyEqual(current_layer->visible_layer_rect(),
                          current_layer->visible_rect_from_property_trees());
   CHECK(visible_rects_match)
-      << "expected: " << current_layer->visible_content_rect().ToString()
+      << "expected: " << current_layer->visible_layer_rect().ToString()
       << " actual: "
       << current_layer->visible_rect_from_property_trees().ToString();
 
@@ -2518,22 +2509,13 @@
 
 void VerifyPropertyTreeValues(
     LayerTreeHostCommon::CalcDrawPropsMainInputs* inputs) {
-  LayerIterator<Layer> it, end;
-  for (it = LayerIterator<Layer>::Begin(inputs->render_surface_layer_list),
-      end = LayerIterator<Layer>::End(inputs->render_surface_layer_list);
-       it != end; ++it) {
-    Layer* current_layer = *it;
-    if (!it.represents_itself() || !current_layer->DrawsContent())
-      continue;
-    VerifyPropertyTreeValuesForLayer(current_layer, inputs->property_trees);
-  }
 }
 
 void VerifyPropertyTreeValues(
     LayerTreeHostCommon::CalcDrawPropsImplInputs* inputs) {
-  LayerIterator<LayerImpl> it, end;
-  for (it = LayerIterator<LayerImpl>::Begin(inputs->render_surface_layer_list),
-      end = LayerIterator<LayerImpl>::End(inputs->render_surface_layer_list);
+  LayerIterator it, end;
+  for (it = LayerIterator::Begin(inputs->render_surface_layer_list),
+      end = LayerIterator::End(inputs->render_surface_layer_list);
        it != end; ++it) {
     LayerImpl* current_layer = *it;
     if (!it.represents_itself() || !current_layer->DrawsContent())
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 2fc2f6ee..abb687c 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -102,8 +102,7 @@
   parent->AddChild(child);
   child->AddChild(grand_child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   gfx::Transform identity_matrix;
   SetLayerPropertiesForTesting(parent.get(),
@@ -146,8 +145,7 @@
   parent->AddChild(child);
   child->AddChild(grand_child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   gfx::Transform identity_matrix;
   SetLayerPropertiesForTesting(parent.get(),
@@ -199,8 +197,7 @@
                                false);
   root->AddChild(layer);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   // Case 2: Setting the bounds of the layer should not affect either the draw
   // transform or the screenspace transform.
@@ -319,8 +316,6 @@
   scoped_ptr<LayerImpl> sublayer_scoped_ptr(
       LayerImpl::Create(host_impl.active_tree(), 1));
   LayerImpl* sublayer = sublayer_scoped_ptr.get();
-  sublayer->SetContentsScale(kPageScale * kDeviceScale,
-                             kPageScale * kDeviceScale);
   SetLayerPropertiesForTesting(sublayer, identity_matrix, gfx::Point3F(),
                                gfx::PointF(), gfx::Size(500, 500), true, false,
                                false);
@@ -358,9 +353,12 @@
       root.get(), kDeviceScale, kPageScale, scroll_layer->parent());
   gfx::Transform expected_transform = identity_matrix;
   gfx::PointF sub_layer_screen_position = kScrollLayerPosition - kScrollDelta;
-  sub_layer_screen_position.Scale(kPageScale * kDeviceScale);
-  expected_transform.Translate(MathUtil::Round(sub_layer_screen_position.x()),
-                               MathUtil::Round(sub_layer_screen_position.y()));
+  expected_transform.Translate(MathUtil::Round(sub_layer_screen_position.x() *
+                                               kPageScale * kDeviceScale),
+                               MathUtil::Round(sub_layer_screen_position.y() *
+                                               kPageScale * kDeviceScale));
+  expected_transform.Scale(kPageScale * kDeviceScale,
+                           kPageScale * kDeviceScale);
   EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform,
                                   sublayer->draw_transform());
   EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform,
@@ -378,9 +376,13 @@
   expected_transform.MakeIdentity();
   expected_transform.Translate(
       MathUtil::Round(kTranslateX * kPageScale * kDeviceScale +
-                      sub_layer_screen_position.x()),
+                      sub_layer_screen_position.x() * kPageScale *
+                          kDeviceScale),
       MathUtil::Round(kTranslateY * kPageScale * kDeviceScale +
-                      sub_layer_screen_position.y()));
+                      sub_layer_screen_position.y() * kPageScale *
+                          kDeviceScale));
+  expected_transform.Scale(kPageScale * kDeviceScale,
+                           kPageScale * kDeviceScale);
   EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform,
                                   sublayer->draw_transform());
 }
@@ -395,8 +397,7 @@
   parent->AddChild(child);
   child->AddChild(grand_child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   // One-time setup of root layer
   SetLayerPropertiesForTesting(root.get(),
@@ -522,8 +523,7 @@
   parent->AddChild(child);
   child->AddChild(grand_child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   // One-time setup of root layer
   gfx::Transform identity_matrix;
@@ -619,8 +619,7 @@
   child->AddChild(grand_child);
   child->SetReplicaLayer(child_replica.get());
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   // One-time setup of root layer
   gfx::Transform identity_matrix;
@@ -740,8 +739,7 @@
   render_surface1->SetReplicaLayer(replica_of_rs1.get());
   render_surface2->SetReplicaLayer(replica_of_rs2.get());
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   // In combination with descendant draws content, opacity != 1 forces the layer
   // to have a new render surface.
@@ -1041,8 +1039,7 @@
   grand_child->AddChild(great_grand_child);
   child->SetForceRenderSurface(true);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   // No layers in this test should preserve 3d.
   ASSERT_TRUE(root->should_flatten_transform());
@@ -1130,8 +1127,7 @@
   child->AddChild(grand_child);
   child->SetForceRenderSurface(true);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
 
@@ -1155,8 +1151,7 @@
   child->SetScrollClipLayerId(root->id());
   root->AddChild(child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   SetLayerPropertiesForTesting(root.get(),
                                identity_matrix,
@@ -1309,8 +1304,7 @@
   scoped_refptr<LayerWithForcedDrawsContent> child =
       make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   const gfx::Transform identity_matrix;
   SetLayerPropertiesForTesting(parent.get(),
@@ -1358,38 +1352,23 @@
 }
 
 TEST_F(LayerTreeHostCommonTest, RenderSurfaceListForTransparentChild) {
-  scoped_refptr<Layer> parent = Layer::Create(layer_settings());
-  scoped_refptr<Layer> render_surface1 = Layer::Create(layer_settings());
-  scoped_refptr<LayerWithForcedDrawsContent> child =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
-
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  LayerImpl* parent = root_layer();
+  LayerImpl* render_surface1 = AddChild<LayerImpl>(parent);
+  LayerImpl* child = AddChild<LayerImpl>(render_surface1);
+  child->SetDrawsContent(true);
 
   const gfx::Transform identity_matrix;
-  SetLayerPropertiesForTesting(render_surface1.get(),
-                               identity_matrix,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(10, 10),
-                               true,
+  SetLayerPropertiesForTesting(render_surface1, identity_matrix, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(10, 10), true, false,
+                               true);
+  SetLayerPropertiesForTesting(child, identity_matrix, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(10, 10), true, false,
                                false);
-  SetLayerPropertiesForTesting(child.get(),
-                               identity_matrix,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(10, 10),
-                               true,
-                               false);
-
-  parent->AddChild(render_surface1);
-  render_surface1->AddChild(child);
-  render_surface1->SetForceRenderSurface(true);
   render_surface1->SetOpacity(0.f);
 
-  RenderSurfaceLayerList render_surface_layer_list;
-  LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
-      parent.get(), parent->bounds(), &render_surface_layer_list);
+  LayerImplList render_surface_layer_list;
+  LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
+      parent, parent->bounds(), &render_surface_layer_list);
   inputs.can_adjust_raster_scales = true;
   LayerTreeHostCommon::CalculateDrawProperties(&inputs);
 
@@ -1408,8 +1387,7 @@
   scoped_refptr<LayerWithForcedDrawsContent> child =
       make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   const gfx::Transform identity_matrix;
   const SkXfermode::Mode blend_mode = SkXfermode::kMultiply_Mode;
@@ -1440,8 +1418,7 @@
       make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
   render_surface1->SetForceRenderSurface(true);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   const gfx::Transform identity_matrix;
   SetLayerPropertiesForTesting(parent.get(),
@@ -1533,8 +1510,7 @@
 
   grand_child->SetShouldFlattenTransform(false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   // Only grand_child should preserve 3d.
   EXPECT_TRUE(root->should_flatten_transform());
@@ -1597,8 +1573,7 @@
   child->AddChild(grand_child);
   grand_child->AddChild(great_grand_child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   // leaf_node1 ensures that parent and child are kept on the
   // render_surface_layer_list, even though grand_child and great_grand_child
@@ -1695,8 +1670,7 @@
   child->AddChild(grand_child);
   grand_child->AddChild(leaf_node);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   SetLayerPropertiesForTesting(parent.get(),
                                identity_matrix,
@@ -1797,8 +1771,7 @@
   child2->AddChild(leaf_node2);
   grand_child->AddChild(leaf_node1);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   child2->SetForceRenderSurface(true);
 
@@ -1955,8 +1928,7 @@
   child->AddChild(grand_child3);
   child->AddChild(grand_child4);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   SetLayerPropertiesForTesting(parent.get(),
                                identity_matrix,
@@ -2053,8 +2025,7 @@
   child->AddChild(grand_child3);
   child->AddChild(grand_child4);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   // the leaf nodes ensure that these grand_children become render surfaces for
   // this test.
@@ -2190,8 +2161,7 @@
   child_of_rs1->AddChild(grand_child_of_rs1);
   child_of_rs2->AddChild(grand_child_of_rs2);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   // Make our render surfaces.
   render_surface1->SetForceRenderSurface(true);
@@ -2670,8 +2640,7 @@
        VisibleRectsForPositionedRootLayerClippedByViewport) {
   scoped_refptr<Layer> root =
       make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   // Root layer is positioned at (60, 70). The default device viewport size
@@ -2687,7 +2656,7 @@
   // In target space, not clipped.
   EXPECT_EQ(gfx::Rect(60, 70, 100, 100), root->drawable_content_rect());
   // In layer space, clipped.
-  EXPECT_EQ(gfx::Rect(0, 0, 40, 30), root->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 40, 30), root->visible_layer_rect());
 }
 
 TEST_F(LayerTreeHostCommonTest, DrawableAndVisibleContentRectsForSimpleLayers) {
@@ -2702,8 +2671,7 @@
   root->AddChild(child2);
   root->AddChild(child3);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   SetLayerPropertiesForTesting(root.get(),
@@ -2741,13 +2709,13 @@
             root->render_surface()->DrawableContentRect());
   EXPECT_EQ(gfx::Rect(0, 0, 100, 100), root->drawable_content_rect());
 
-  // Layers that do not draw content should have empty visible_content_rects.
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_content_rect());
+  // Layers that do not draw content should have empty visible_layer_rects.
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect());
 
-  // layer visible_content_rects are clipped by their target surface.
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 25, 25), child2->visible_content_rect());
-  EXPECT_TRUE(child3->visible_content_rect().IsEmpty());
+  // layer visible_layer_rects are clipped by their target surface.
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 25, 25), child2->visible_layer_rect());
+  EXPECT_TRUE(child3->visible_layer_rect().IsEmpty());
 
   // layer drawable_content_rects are not clipped.
   EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->drawable_content_rect());
@@ -2770,8 +2738,7 @@
   child->AddChild(grand_child2);
   child->AddChild(grand_child3);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   SetLayerPropertiesForTesting(root.get(),
@@ -2820,13 +2787,13 @@
   EXPECT_EQ(gfx::Rect(0, 0, 100, 100), root->drawable_content_rect());
 
   // Layers that do not draw content should have empty visible content rects.
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), child->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), child->visible_layer_rect());
 
   // All grandchild visible content rects should be clipped by child.
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), grand_child1->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 25, 25), grand_child2->visible_content_rect());
-  EXPECT_TRUE(grand_child3->visible_content_rect().IsEmpty());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), grand_child1->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 25, 25), grand_child2->visible_layer_rect());
+  EXPECT_TRUE(grand_child3->visible_layer_rect().IsEmpty());
 
   // All grandchild DrawableContentRects should also be clipped by child.
   EXPECT_EQ(gfx::Rect(5, 5, 50, 50), grand_child1->drawable_content_rect());
@@ -2842,8 +2809,7 @@
   root->AddChild(child);
   child->AddChild(grand_child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   gfx::Transform child_scale_matrix;
@@ -2882,8 +2848,7 @@
   render_surface1->AddChild(child2);
   render_surface1->AddChild(child3);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   SetLayerPropertiesForTesting(root.get(),
@@ -2932,8 +2897,8 @@
   EXPECT_EQ(gfx::Rect(0, 0, 100, 100), root->drawable_content_rect());
 
   // Layers that do not draw content should have empty visible content rects.
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), render_surface1->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), render_surface1->visible_layer_rect());
 
   // An unclipped surface grows its DrawableContentRect to include all drawable
   // regions of the subtree.
@@ -2941,9 +2906,9 @@
             render_surface1->render_surface()->DrawableContentRect());
 
   // All layers that draw content into the unclipped surface are also unclipped.
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child2->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child3->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child2->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child3->visible_layer_rect());
 
   EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->drawable_content_rect());
   EXPECT_EQ(gfx::Rect(75, 75, 50, 50), child2->drawable_content_rect());
@@ -2963,8 +2928,7 @@
   root->AddChild(child2);
   root->AddChild(child3);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   SetLayerPropertiesForTesting(root.get(), identity_matrix, gfx::Point3F(),
@@ -2995,9 +2959,9 @@
   // Visible content rect calculation will check if the target surface is
   // clipped or not. An empty clip rect does not indicate the render surface
   // is unclipped.
-  EXPECT_EQ(empty, child1->visible_content_rect());
-  EXPECT_EQ(empty, child2->visible_content_rect());
-  EXPECT_EQ(empty, child3->visible_content_rect());
+  EXPECT_EQ(empty, child1->visible_layer_rect());
+  EXPECT_EQ(empty, child2->visible_layer_rect());
+  EXPECT_EQ(empty, child3->visible_layer_rect());
 }
 
 TEST_F(LayerTreeHostCommonTest,
@@ -3007,8 +2971,7 @@
       make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
   root->AddChild(child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   // Case 1: a truly degenerate matrix
   gfx::Transform identity_matrix;
@@ -3032,7 +2995,7 @@
 
   ExecuteCalculateDrawProperties(root.get());
 
-  EXPECT_TRUE(child->visible_content_rect().IsEmpty());
+  EXPECT_TRUE(child->visible_layer_rect().IsEmpty());
   EXPECT_TRUE(child->drawable_content_rect().IsEmpty());
 
   // Case 2: a matrix with flattened z, uninvertible and not visible according
@@ -3051,7 +3014,7 @@
 
   ExecuteCalculateDrawProperties(root.get());
 
-  EXPECT_TRUE(child->visible_content_rect().IsEmpty());
+  EXPECT_TRUE(child->visible_layer_rect().IsEmpty());
   EXPECT_TRUE(child->drawable_content_rect().IsEmpty());
 
   // Case 3: a matrix with flattened z, also uninvertible and not visible.
@@ -3070,7 +3033,7 @@
 
   ExecuteCalculateDrawProperties(root.get());
 
-  EXPECT_TRUE(child->visible_content_rect().IsEmpty());
+  EXPECT_TRUE(child->visible_layer_rect().IsEmpty());
   EXPECT_TRUE(child->drawable_content_rect().IsEmpty());
 }
 
@@ -3081,8 +3044,7 @@
       make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
   root->AddChild(child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   gfx::Transform uninvertible_matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
@@ -3125,8 +3087,7 @@
        SingularNonAnimatingTransformDoesNotPreventClearingDrawProperties) {
   scoped_refptr<Layer> root = Layer::Create(layer_settings());
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   gfx::Transform uninvertible_matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
@@ -3164,8 +3125,7 @@
   render_surface1->AddChild(child2);
   render_surface1->AddChild(child3);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   SetLayerPropertiesForTesting(root.get(),
@@ -3215,8 +3175,8 @@
   EXPECT_EQ(gfx::Rect(0, 0, 100, 100), root->drawable_content_rect());
 
   // Layers that do not draw content should have empty visible content rects.
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), render_surface1->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), render_surface1->visible_layer_rect());
 
   // A clipped surface grows its DrawableContentRect to include all drawable
   // regions of the subtree, but also gets clamped by the ancestor's clip.
@@ -3225,9 +3185,9 @@
 
   // All layers that draw content into the surface have their visible content
   // rect clipped by the surface clip rect.
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 25, 25), child2->visible_content_rect());
-  EXPECT_TRUE(child3->visible_content_rect().IsEmpty());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 25, 25), child2->visible_layer_rect());
+  EXPECT_TRUE(child3->visible_layer_rect().IsEmpty());
 
   // But the DrawableContentRects are unclipped.
   EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->drawable_content_rect());
@@ -3253,8 +3213,7 @@
   render_surface2->AddChild(child2);
   render_surface2->AddChild(child3);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   SetLayerPropertiesForTesting(root.get(),
@@ -3313,9 +3272,9 @@
   EXPECT_EQ(gfx::Rect(0, 0, 100, 100), root->drawable_content_rect());
 
   // Layers that do not draw content should have empty visible content rects.
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), render_surface1->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), render_surface2->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), render_surface1->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), render_surface2->visible_layer_rect());
 
   // A clipped surface grows its DrawableContentRect to include all drawable
   // regions of the subtree, but also gets clamped by the ancestor's clip.
@@ -3329,9 +3288,9 @@
             render_surface2->render_surface()->DrawableContentRect());
 
   // All layers that draw content into render_surface2 think they are unclipped.
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child2->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child3->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child2->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child3->visible_layer_rect());
 
   // DrawableContentRects are also unclipped.
   EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->drawable_content_rect());
@@ -3351,8 +3310,7 @@
   root->AddChild(render_surface1);
   render_surface1->AddChild(child1);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   gfx::Transform child_rotation;
@@ -3389,8 +3347,8 @@
   EXPECT_EQ(gfx::Rect(0, 0, 100, 100), root->drawable_content_rect());
 
   // Layers that do not draw content should have empty visible content rects.
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), render_surface1->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), render_surface1->visible_layer_rect());
 
   // The unclipped surface grows its DrawableContentRect to include all drawable
   // regions of the subtree.
@@ -3404,7 +3362,7 @@
             render_surface1->render_surface()->DrawableContentRect());
 
   // All layers that draw content into the unclipped surface are also unclipped.
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_layer_rect());
   EXPECT_EQ(expected_surface_drawable_content, child1->drawable_content_rect());
 }
 
@@ -3420,8 +3378,7 @@
   root->AddChild(render_surface1);
   render_surface1->AddChild(child1);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   gfx::Transform child_rotation;
@@ -3472,7 +3429,7 @@
   // up covering the full left half of child1.
   //
   // Given the floating point math, this number is a little bit fuzzy.
-  EXPECT_EQ(gfx::Rect(0, 0, 26, 50), child1->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 26, 50), child1->visible_layer_rect());
 
   // The child's DrawableContentRect is unclipped.
   EXPECT_EQ(unclipped_surface_content, child1->drawable_content_rect());
@@ -3498,8 +3455,7 @@
   render_surface2->AddChild(child2);
   render_surface2->AddChild(child3);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   SetLayerPropertiesForTesting(root.get(),
@@ -3573,15 +3529,15 @@
   EXPECT_EQ(gfx::Rect(250, 250, 100, 100), child3->drawable_content_rect());
 
   // The root layer does not actually draw content of its own.
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect());
 
   // All layer visible content rects are not expressed in content space of each
   // layer, so they are not scaled by the device_scale_factor.
-  EXPECT_EQ(gfx::Rect(0, 0, 3, 4), render_surface1->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 7, 13), render_surface2->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child2->visible_content_rect());
-  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child3->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 3, 4), render_surface1->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 7, 13), render_surface2->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child2->visible_layer_rect());
+  EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child3->visible_layer_rect());
 }
 
 TEST_F(LayerTreeHostCommonTest, BackFaceCullingWithoutPreserves3d) {
@@ -3621,8 +3577,7 @@
   back_facing_surface->AddChild(front_facing_child_of_back_facing_surface);
   back_facing_surface->AddChild(back_facing_child_of_back_facing_surface);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   // Nothing is double-sided
   front_facing_child->SetDoubleSided(false);
@@ -3827,8 +3782,7 @@
   back_facing_surface->AddChild(front_facing_child_of_back_facing_surface);
   back_facing_surface->AddChild(back_facing_child_of_back_facing_surface);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   // Nothing is double-sided
   front_facing_child->SetDoubleSided(false);
@@ -3995,8 +3949,7 @@
   parent->AddChild(animating_child);
   parent->AddChild(child2);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   // Nothing is double-sided
   child->SetDoubleSided(false);
@@ -4106,17 +4059,17 @@
             render_surface_layer_list.at(1)
                 ->render_surface()->layer_list().at(1)->id());
 
-  EXPECT_FALSE(child2->visible_content_rect().IsEmpty());
+  EXPECT_FALSE(child2->visible_layer_rect().IsEmpty());
 
   // The animating layers should have a visible content rect that represents the
   // area of the front face that is within the viewport.
-  EXPECT_EQ(animating_child->visible_content_rect(),
+  EXPECT_EQ(animating_child->visible_layer_rect(),
             gfx::Rect(animating_child->bounds()));
-  EXPECT_EQ(animating_surface->visible_content_rect(),
+  EXPECT_EQ(animating_surface->visible_layer_rect(),
             gfx::Rect(animating_surface->bounds()));
   // And layers in the subtree of the animating layer should have valid visible
   // content rects also.
-  EXPECT_EQ(child_of_animating_surface->visible_content_rect(),
+  EXPECT_EQ(child_of_animating_surface->visible_layer_rect(),
             gfx::Rect(child_of_animating_surface->bounds()));
 }
 
@@ -4141,8 +4094,7 @@
   front_facing_surface->AddChild(child1);
   back_facing_surface->AddChild(child2);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   // RenderSurfaces are not double-sided
   front_facing_surface->SetDoubleSided(false);
@@ -4272,8 +4224,7 @@
   parent->AddChild(child);
   parent->AddChild(child_empty);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   float device_scale_factor = 2.5f;
   float page_scale_factor = 1.f;
@@ -4400,8 +4351,7 @@
   parent->AddChild(scale_surface);
   root->AddChild(parent);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   float device_scale_factor = 2.5f;
   float page_scale_factor = 3.f;
@@ -4509,8 +4459,7 @@
 
   parent->AddChild(child_scale);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   float device_scale_factor = 2.5f;
   float page_scale_factor = 0.01f;
@@ -4577,8 +4526,7 @@
 
   parent->AddChild(child_scale);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   {
     RenderSurfaceLayerList render_surface_layer_list;
@@ -4647,8 +4595,7 @@
   child->AddChild(duplicate_child_non_owner);
   child->SetReplicaLayer(replica.get());
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   RenderSurfaceLayerList render_surface_layer_list;
 
@@ -4774,8 +4721,7 @@
   parent->AddChild(child);
   child->SetReplicaLayer(replica.get());
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(parent);
+  host()->SetRootLayer(parent);
 
   float device_scale_factor = 1.7f;
 
@@ -4823,8 +4769,7 @@
   child->SetMaskLayer(mask_layer.get());
   root->AddChild(child.get());
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   int nonexistent_id = -1;
   EXPECT_EQ(root.get(),
@@ -4877,8 +4822,7 @@
   child->AddChild(grand_child);
   child->SetOpacity(0.5f);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
 
@@ -5181,8 +5125,7 @@
   child->AddChild(grand_child);
   root->AddChild(child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   RenderSurfaceLayerList render_surface_layer_list;
   LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
@@ -5288,8 +5231,7 @@
   child->AddChild(grand_child);
   root->AddChild(child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   RenderSurfaceLayerList render_surface_layer_list;
   LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
@@ -5440,8 +5382,7 @@
   root->AddChild(copy_grand_parent);
   root->AddChild(copy_grand_parent_sibling_after);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   // Hide the copy_grand_parent and its subtree. But make a copy request in that
   // hidden subtree on copy_layer.
@@ -5556,8 +5497,7 @@
   copy_parent->AddChild(copy_layer);
   root->AddChild(copy_parent);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   copy_layer->RequestCopyOfOutput(CopyOutputRequest::CreateRequest(
       base::Bind(&EmptyCopyOutputCallback)));
@@ -5622,8 +5562,7 @@
   surface->AddChild(surface_child);
   root->AddChild(surface);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   RenderSurfaceLayerList render_surface_layer_list;
   LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
@@ -5631,10 +5570,10 @@
   inputs.can_adjust_raster_scales = true;
   LayerTreeHostCommon::CalculateDrawProperties(&inputs);
 
-  // The visible_content_rect for the |surface_child| should not be clipped by
+  // The visible_layer_rect for the |surface_child| should not be clipped by
   // the viewport.
   EXPECT_EQ(gfx::Rect(50, 50).ToString(),
-            surface_child->visible_content_rect().ToString());
+            surface_child->visible_layer_rect().ToString());
 }
 
 TEST_F(LayerTreeHostCommonTest, TransformedClipParent) {
@@ -5709,8 +5648,7 @@
                                true,
                                false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
 
@@ -5820,8 +5758,7 @@
                                true,
                                false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
 
@@ -5862,7 +5799,7 @@
   EXPECT_EQ(gfx::Rect(-1, -1, 40, 40).ToString(),
             clip_child->clip_rect().ToString());
   EXPECT_EQ(gfx::Rect(9, 9, 40, 40).ToString(),
-            clip_child->visible_content_rect().ToString());
+            clip_child->visible_layer_rect().ToString());
   EXPECT_TRUE(clip_child->is_clipped());
 }
 
@@ -5949,8 +5886,7 @@
                                true,
                                false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
 
@@ -5991,7 +5927,7 @@
   EXPECT_EQ(gfx::Rect(2, 2, 40, 40).ToString(),
             clip_child->clip_rect().ToString());
   EXPECT_EQ(gfx::Rect(12, 12, 40, 40).ToString(),
-            clip_child->visible_content_rect().ToString());
+            clip_child->visible_layer_rect().ToString());
   EXPECT_TRUE(clip_child->is_clipped());
 }
 
@@ -6058,8 +5994,7 @@
                                true,
                                false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
 
@@ -6071,7 +6006,7 @@
             clip_child->clip_rect().ToString());
   EXPECT_TRUE(clip_child->is_clipped());
   EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
-            child->visible_content_rect().ToString());
+            child->visible_layer_rect().ToString());
   EXPECT_TRUE(child->is_clipped());
 }
 
@@ -6155,8 +6090,7 @@
   render_surface1->SetForceRenderSurface(true);
   render_surface2->SetForceRenderSurface(true);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
 
@@ -6247,8 +6181,8 @@
     int count_represents_target_render_surface = 0;
     int count_represents_contributing_render_surface = 0;
     int count_represents_itself = 0;
-    auto end = LayerIterator<LayerImpl>::End(&render_surface_layer_list);
-    for (auto it = LayerIterator<LayerImpl>::Begin(&render_surface_layer_list);
+    LayerIterator end = LayerIterator::End(&render_surface_layer_list);
+    for (LayerIterator it = LayerIterator::Begin(&render_surface_layer_list);
          it != end; ++it) {
       if (it.represents_target_render_surface())
         count_represents_target_render_surface++;
@@ -6278,8 +6212,8 @@
     int count_represents_target_render_surface = 0;
     int count_represents_contributing_render_surface = 0;
     int count_represents_itself = 0;
-    auto end = LayerIterator<LayerImpl>::End(&render_surface_layer_list);
-    for (auto it = LayerIterator<LayerImpl>::Begin(&render_surface_layer_list);
+    LayerIterator end = LayerIterator::End(&render_surface_layer_list);
+    for (LayerIterator it = LayerIterator::Begin(&render_surface_layer_list);
          it != end; ++it) {
       if (it.represents_target_render_surface())
         count_represents_target_render_surface++;
@@ -6299,66 +6233,53 @@
 }
 
 TEST_F(LayerTreeHostCommonTest, DoNotIncludeBackfaceInvisibleSurfaces) {
-  scoped_refptr<Layer> root = Layer::Create(layer_settings());
-  scoped_refptr<Layer> render_surface = Layer::Create(layer_settings());
-  scoped_refptr<LayerWithForcedDrawsContent> child =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
-
-  root->AddChild(render_surface);
-  render_surface->AddChild(child);
+  LayerImpl* root = root_layer();
+  LayerImpl* render_surface = AddChild<LayerImpl>(root);
+  LayerImpl* child = AddChild<LayerImpl>(render_surface);
+  child->SetDrawsContent(true);
 
   gfx::Transform identity_transform;
-  SetLayerPropertiesForTesting(root.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(render_surface.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(30, 30),
-                               false,
+  SetLayerPropertiesForTesting(root, identity_transform, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(50, 50), true, false,
                                true);
-  SetLayerPropertiesForTesting(child.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(20, 20),
-                               true,
+  SetLayerPropertiesForTesting(render_surface, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(30, 30),
+                               false, true, true);
+  SetLayerPropertiesForTesting(child, identity_transform, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(20, 20), true, false,
                                false);
 
   root->SetShouldFlattenTransform(false);
   root->Set3dSortingContextId(1);
   render_surface->SetDoubleSided(false);
-  render_surface->SetForceRenderSurface(true);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  ExecuteCalculateDrawProperties(root);
 
-  ExecuteCalculateDrawProperties(root.get());
-
-  EXPECT_EQ(2u, render_surface_layer_list()->size());
-  EXPECT_EQ(1u,
-            render_surface_layer_list()->at(0)
-                ->render_surface()->layer_list().size());
-  EXPECT_EQ(1u,
-            render_surface_layer_list()->at(1)
-                ->render_surface()->layer_list().size());
+  EXPECT_EQ(2u, render_surface_layer_list_impl()->size());
+  EXPECT_EQ(1u, render_surface_layer_list_impl()
+                    ->at(0)
+                    ->render_surface()
+                    ->layer_list()
+                    .size());
+  EXPECT_EQ(1u, render_surface_layer_list_impl()
+                    ->at(1)
+                    ->render_surface()
+                    ->layer_list()
+                    .size());
 
   gfx::Transform rotation_transform = identity_transform;
   rotation_transform.RotateAboutXAxis(180.0);
 
   render_surface->SetTransform(rotation_transform);
 
-  ExecuteCalculateDrawProperties(root.get());
+  ExecuteCalculateDrawProperties(root);
 
-  EXPECT_EQ(1u, render_surface_layer_list()->size());
-  EXPECT_EQ(0u,
-            render_surface_layer_list()->at(0)
-                ->render_surface()->layer_list().size());
+  EXPECT_EQ(1u, render_surface_layer_list_impl()->size());
+  EXPECT_EQ(0u, render_surface_layer_list_impl()
+                    ->at(0)
+                    ->render_surface()
+                    ->layer_list()
+                    .size());
 }
 
 TEST_F(LayerTreeHostCommonTest, ClippedByScrollParent) {
@@ -6426,8 +6347,7 @@
                                true,
                                false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
 
@@ -6475,8 +6395,7 @@
                                true);
   child->SetForceRenderSurface(true);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
 
@@ -6507,70 +6426,42 @@
   // still results in correct clipping.
   //
   // + root
-  //   + scroll_child
   //   + scroll_parent_border
   //     + scroll_parent_clip
   //       + scroll_parent
+  //   + scroll_child
   //
-  scoped_refptr<Layer> root = Layer::Create(layer_settings());
-  scoped_refptr<Layer> scroll_parent_border = Layer::Create(layer_settings());
-  scoped_refptr<Layer> scroll_parent_clip = Layer::Create(layer_settings());
-  scoped_refptr<LayerWithForcedDrawsContent> scroll_parent =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
-  scoped_refptr<LayerWithForcedDrawsContent> scroll_child =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
+  LayerImpl* root = root_layer();
+  LayerImpl* scroll_parent_border = AddChild<LayerImpl>(root);
+  LayerImpl* scroll_parent_clip = AddChild<LayerImpl>(scroll_parent_border);
+  LayerImpl* scroll_parent = AddChild<LayerImpl>(scroll_parent_clip);
+  LayerImpl* scroll_child = AddChild<LayerImpl>(root);
 
-  root->AddChild(scroll_parent_border);
-  scroll_parent_border->AddChild(scroll_parent_clip);
-  scroll_parent_clip->AddChild(scroll_parent);
-
-  root->AddChild(scroll_child);
+  scroll_parent->SetDrawsContent(true);
+  scroll_child->SetDrawsContent(true);
 
   scroll_parent_clip->SetMasksToBounds(true);
 
-  scroll_child->SetScrollParent(scroll_parent.get());
+  scroll_child->SetScrollParent(scroll_parent);
 
   gfx::Transform identity_transform;
-  SetLayerPropertiesForTesting(root.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_parent_border.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(40, 40),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_parent_clip.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(30, 30),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_parent.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_child.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
+  SetLayerPropertiesForTesting(root, identity_transform, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(50, 50), true, false,
+                               true);
+  SetLayerPropertiesForTesting(scroll_parent_border, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(40, 40),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_parent_clip, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(30, 30),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_parent, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(50, 50),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_child, identity_transform, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(50, 50), true, false,
                                false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
-
-  ExecuteCalculateDrawProperties(root.get());
+  ExecuteCalculateDrawProperties(root);
 
   EXPECT_TRUE(root->render_surface());
 
@@ -6592,100 +6483,53 @@
   //     + scroll_grandparent_clip
   //       + scroll_grandparent
   //
-  scoped_refptr<Layer> root = Layer::Create(layer_settings());
-  scoped_refptr<Layer> scroll_parent_border = Layer::Create(layer_settings());
-  scoped_refptr<Layer> scroll_parent_clip = Layer::Create(layer_settings());
-  scoped_refptr<LayerWithForcedDrawsContent> scroll_parent =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
+  LayerImpl* root = root_layer();
+  LayerImpl* scroll_child = AddChild<LayerImpl>(root);
+  LayerImpl* scroll_parent_border = AddChild<LayerImpl>(root);
+  LayerImpl* scroll_parent_clip = AddChild<LayerImpl>(scroll_parent_border);
+  LayerImpl* scroll_parent = AddChild<LayerImpl>(scroll_parent_clip);
+  LayerImpl* scroll_grandparent_border = AddChild<LayerImpl>(root);
+  LayerImpl* scroll_grandparent_clip =
+      AddChild<LayerImpl>(scroll_grandparent_border);
+  LayerImpl* scroll_grandparent = AddChild<LayerImpl>(scroll_grandparent_clip);
 
-  scoped_refptr<Layer> scroll_grandparent_border =
-      Layer::Create(layer_settings());
-  scoped_refptr<Layer> scroll_grandparent_clip =
-      Layer::Create(layer_settings());
-  scoped_refptr<LayerWithForcedDrawsContent> scroll_grandparent =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
-
-  scoped_refptr<LayerWithForcedDrawsContent> scroll_child =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
-
-  root->AddChild(scroll_child);
-
-  root->AddChild(scroll_parent_border);
-  scroll_parent_border->AddChild(scroll_parent_clip);
-  scroll_parent_clip->AddChild(scroll_parent);
-
-  root->AddChild(scroll_grandparent_border);
-  scroll_grandparent_border->AddChild(scroll_grandparent_clip);
-  scroll_grandparent_clip->AddChild(scroll_grandparent);
+  scroll_parent->SetDrawsContent(true);
+  scroll_grandparent->SetDrawsContent(true);
+  scroll_child->SetDrawsContent(true);
 
   scroll_parent_clip->SetMasksToBounds(true);
   scroll_grandparent_clip->SetMasksToBounds(true);
 
-  scroll_child->SetScrollParent(scroll_parent.get());
-  scroll_parent_border->SetScrollParent(scroll_grandparent.get());
+  scroll_child->SetScrollParent(scroll_parent);
+  scroll_parent_border->SetScrollParent(scroll_grandparent);
 
   gfx::Transform identity_transform;
-  SetLayerPropertiesForTesting(root.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_grandparent_border.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(40, 40),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_grandparent_clip.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(20, 20),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_grandparent.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_parent_border.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(40, 40),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_parent_clip.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(30, 30),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_parent.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_child.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
+  SetLayerPropertiesForTesting(root, identity_transform, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(50, 50), true, false,
+                               true);
+  SetLayerPropertiesForTesting(scroll_grandparent_border, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(40, 40),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_grandparent_clip, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(20, 20),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_grandparent, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(50, 50),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_parent_border, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(40, 40),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_parent_clip, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(30, 30),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_parent, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(50, 50),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_child, identity_transform, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(50, 50), true, false,
                                false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
-
-  ExecuteCalculateDrawProperties(root.get());
+  ExecuteCalculateDrawProperties(root);
 
   EXPECT_TRUE(root->render_surface());
 
@@ -6696,12 +6540,9 @@
   // Despite the fact that we visited the above layers out of order to get the
   // correct clip, the layer lists should be unaffected.
   EXPECT_EQ(3u, root->render_surface()->layer_list().size());
-  EXPECT_EQ(scroll_child.get(),
-            root->render_surface()->layer_list().at(0).get());
-  EXPECT_EQ(scroll_parent.get(),
-            root->render_surface()->layer_list().at(1).get());
-  EXPECT_EQ(scroll_grandparent.get(),
-            root->render_surface()->layer_list().at(2).get());
+  EXPECT_EQ(scroll_child, root->render_surface()->layer_list().at(0));
+  EXPECT_EQ(scroll_parent, root->render_surface()->layer_list().at(1));
+  EXPECT_EQ(scroll_grandparent, root->render_surface()->layer_list().at(2));
 }
 
 TEST_F(LayerTreeHostCommonTest, OutOfOrderClippingRequiresRSLLSorting) {
@@ -6712,138 +6553,72 @@
   //   + scroll_parent_border
   //     + scroll_parent_clip
   //       + scroll_parent
-  //         + render_surface1
+  //         + render_surface2
   //   + scroll_grandparent_border
   //     + scroll_grandparent_clip
   //       + scroll_grandparent
-  //         + render_surface2
+  //         + render_surface1
   //
-  scoped_refptr<LayerWithForcedDrawsContent> root =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
+  LayerImpl* root = root_layer();
+  root->SetDrawsContent(true);
 
-  scoped_refptr<Layer> scroll_parent_border = Layer::Create(layer_settings());
-  scoped_refptr<Layer> scroll_parent_clip = Layer::Create(layer_settings());
-  scoped_refptr<LayerWithForcedDrawsContent> scroll_parent =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
-  scoped_refptr<LayerWithForcedDrawsContent> render_surface1 =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
+  LayerImpl* scroll_child = AddChild<LayerImpl>(root);
+  scroll_child->SetDrawsContent(true);
 
-  scoped_refptr<Layer> scroll_grandparent_border =
-      Layer::Create(layer_settings());
-  scoped_refptr<Layer> scroll_grandparent_clip =
-      Layer::Create(layer_settings());
-  scoped_refptr<LayerWithForcedDrawsContent> scroll_grandparent =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
-  scoped_refptr<LayerWithForcedDrawsContent> render_surface2 =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
+  LayerImpl* scroll_parent_border = AddChild<LayerImpl>(root);
+  LayerImpl* scroll_parent_clip = AddChild<LayerImpl>(scroll_parent_border);
+  LayerImpl* scroll_parent = AddChild<LayerImpl>(scroll_parent_clip);
+  LayerImpl* render_surface2 = AddChild<LayerImpl>(scroll_parent);
+  LayerImpl* scroll_grandparent_border = AddChild<LayerImpl>(root);
+  LayerImpl* scroll_grandparent_clip =
+      AddChild<LayerImpl>(scroll_grandparent_border);
+  LayerImpl* scroll_grandparent = AddChild<LayerImpl>(scroll_grandparent_clip);
+  LayerImpl* render_surface1 = AddChild<LayerImpl>(scroll_grandparent);
 
-  scoped_refptr<LayerWithForcedDrawsContent> scroll_child =
-      make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
-
-  root->AddChild(scroll_child);
-
-  root->AddChild(scroll_parent_border);
-  scroll_parent_border->AddChild(scroll_parent_clip);
-  scroll_parent_clip->AddChild(scroll_parent);
-  scroll_parent->AddChild(render_surface2);
-
-  root->AddChild(scroll_grandparent_border);
-  scroll_grandparent_border->AddChild(scroll_grandparent_clip);
-  scroll_grandparent_clip->AddChild(scroll_grandparent);
-  scroll_grandparent->AddChild(render_surface1);
+  scroll_parent->SetDrawsContent(true);
+  render_surface1->SetDrawsContent(true);
+  scroll_grandparent->SetDrawsContent(true);
+  render_surface2->SetDrawsContent(true);
 
   scroll_parent_clip->SetMasksToBounds(true);
   scroll_grandparent_clip->SetMasksToBounds(true);
 
-  scroll_child->SetScrollParent(scroll_parent.get());
-  scroll_parent_border->SetScrollParent(scroll_grandparent.get());
-
-  render_surface1->SetForceRenderSurface(true);
-  render_surface2->SetForceRenderSurface(true);
+  scroll_child->SetScrollParent(scroll_parent);
+  scroll_parent_border->SetScrollParent(scroll_grandparent);
 
   gfx::Transform identity_transform;
-  SetLayerPropertiesForTesting(root.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_grandparent_border.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(40, 40),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_grandparent_clip.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(20, 20),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_grandparent.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(render_surface1.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_parent_border.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(40, 40),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_parent_clip.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(30, 30),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_parent.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(render_surface2.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
-                               false);
-  SetLayerPropertiesForTesting(scroll_child.get(),
-                               identity_transform,
-                               gfx::Point3F(),
-                               gfx::PointF(),
-                               gfx::Size(50, 50),
-                               true,
+  SetLayerPropertiesForTesting(root, identity_transform, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(50, 50), true, false,
+                               true);
+  SetLayerPropertiesForTesting(scroll_grandparent_border, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(40, 40),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_grandparent_clip, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(20, 20),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_grandparent, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(50, 50),
+                               true, false, false);
+  SetLayerPropertiesForTesting(render_surface1, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(50, 50),
+                               true, false, true);
+  SetLayerPropertiesForTesting(scroll_parent_border, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(40, 40),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_parent_clip, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(30, 30),
+                               true, false, false);
+  SetLayerPropertiesForTesting(scroll_parent, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(50, 50),
+                               true, false, false);
+  SetLayerPropertiesForTesting(render_surface2, identity_transform,
+                               gfx::Point3F(), gfx::PointF(), gfx::Size(50, 50),
+                               true, false, true);
+  SetLayerPropertiesForTesting(scroll_child, identity_transform, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(50, 50), true, false,
                                false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
-
-  RenderSurfaceLayerList render_surface_layer_list;
-  LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
-      root.get(),
-      root->bounds(),
-      identity_transform,
-      &render_surface_layer_list);
-
-  LayerTreeHostCommon::CalculateDrawProperties(&inputs);
+  ExecuteCalculateDrawProperties(root);
 
   EXPECT_TRUE(root->render_surface());
 
@@ -6853,13 +6628,13 @@
 
   // Despite the fact that we had to process the layers out of order to get the
   // right clip, our render_surface_layer_list's order should be unaffected.
-  EXPECT_EQ(3u, render_surface_layer_list.size());
-  EXPECT_EQ(root.get(), render_surface_layer_list.at(0));
-  EXPECT_EQ(render_surface2.get(), render_surface_layer_list.at(1));
-  EXPECT_EQ(render_surface1.get(), render_surface_layer_list.at(2));
-  EXPECT_TRUE(render_surface_layer_list.at(0)->render_surface());
-  EXPECT_TRUE(render_surface_layer_list.at(1)->render_surface());
-  EXPECT_TRUE(render_surface_layer_list.at(2)->render_surface());
+  EXPECT_EQ(3u, render_surface_layer_list_impl()->size());
+  EXPECT_EQ(root, render_surface_layer_list_impl()->at(0));
+  EXPECT_EQ(render_surface2, render_surface_layer_list_impl()->at(1));
+  EXPECT_EQ(render_surface1, render_surface_layer_list_impl()->at(2));
+  EXPECT_TRUE(render_surface_layer_list_impl()->at(0)->render_surface());
+  EXPECT_TRUE(render_surface_layer_list_impl()->at(1)->render_surface());
+  EXPECT_TRUE(render_surface_layer_list_impl()->at(2)->render_surface());
 }
 
 TEST_F(LayerTreeHostCommonTest, FixedPositionWithInterveningRenderSurface) {
@@ -6904,8 +6679,7 @@
                                gfx::PointF(1.f, 2.f), gfx::Size(50, 50), true,
                                false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
 
@@ -7383,10 +7157,9 @@
 
 static void GatherDrawnLayers(LayerImplList* rsll,
                               std::set<LayerImpl*>* drawn_layers) {
-  for (LayerIterator<LayerImpl> it = LayerIterator<LayerImpl>::Begin(rsll),
-                                end = LayerIterator<LayerImpl>::End(rsll);
-       it != end;
-       ++it) {
+  for (LayerIterator it = LayerIterator::Begin(rsll),
+                     end = LayerIterator::End(rsll);
+       it != end; ++it) {
     LayerImpl* layer = *it;
     if (it.represents_itself())
       drawn_layers->insert(layer);
@@ -7906,16 +7679,12 @@
   root->AddChild(clip);
   clip->AddChild(content);
 
-  FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D);
-  TestTaskGraphRunner task_graph_runner;
-  scoped_ptr<FakeLayerTreeHost> host =
-      FakeLayerTreeHost::Create(&client, &task_graph_runner);
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Size device_viewport_size(768, 582);
   RenderSurfaceLayerList render_surface_layer_list;
   LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
-      host->root_layer(), device_viewport_size, &render_surface_layer_list);
+      host()->root_layer(), device_viewport_size, &render_surface_layer_list);
   inputs.device_scale_factor = 2.f;
   inputs.page_scale_factor = 1.f;
   inputs.page_scale_layer = NULL;
@@ -7923,11 +7692,11 @@
 
   // Layers in the root render surface have their visible content rect clipped
   // by the viewport.
-  EXPECT_EQ(gfx::Rect(768 / 2, 582 / 2), root->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(768 / 2, 582 / 2), root->visible_layer_rect());
 
   // Layers drawing to a child render surface should still have their visible
   // content rect clipped by the viewport.
-  EXPECT_EQ(gfx::Rect(768 / 2, 582 / 2), content->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(768 / 2, 582 / 2), content->visible_layer_rect());
 }
 
 TEST_F(LayerTreeHostCommonTest, BoundsDeltaAffectVisibleContentRect) {
@@ -7962,8 +7731,6 @@
                                false,
                                false,
                                true);
-
-  root->SetContentBounds(root_size);
   root->SetMasksToBounds(true);
 
   root->AddChild(LayerImpl::Create(host_impl.active_tree(), 2));
@@ -7977,8 +7744,6 @@
                                false,
                                false,
                                false);
-
-  sublayer->SetContentBounds(sublayer_size);
   sublayer->SetDrawsContent(true);
 
   LayerImplList layer_impl_list;
@@ -7987,7 +7752,7 @@
 
   LayerTreeHostCommon::CalculateDrawProperties(&inputs);
 
-  EXPECT_EQ(gfx::Rect(root_size), sublayer->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(root_size), sublayer->visible_layer_rect());
 
   root->SetBoundsDelta(gfx::Vector2dF(0.0, 50.0));
 
@@ -7995,7 +7760,7 @@
 
   gfx::Rect affected_by_delta(0, 0, root_size.width(),
                               root_size.height() + 50);
-  EXPECT_EQ(affected_by_delta, sublayer->visible_content_rect());
+  EXPECT_EQ(affected_by_delta, sublayer->visible_layer_rect());
 }
 
 TEST_F(LayerTreeHostCommonTest, VisibleContentRectForAnimatedLayer) {
@@ -8006,8 +7771,7 @@
 
   root->AddChild(animated);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   SetLayerPropertiesForTesting(root.get(), identity_matrix, gfx::Point3F(),
                                gfx::PointF(), gfx::Size(100, 100), true, false);
@@ -8046,8 +7810,7 @@
   clip->SetMasksToBounds(true);
   surface->SetForceRenderSurface(true);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform uninvertible_matrix;
   uninvertible_matrix.Scale3d(6.f, 6.f, 0.f);
@@ -8120,8 +7883,7 @@
   SetLayerPropertiesForTesting(grandchild.get(), identity_transform,
                                gfx::Point3F(), gfx::PointF(), gfx::Size(50, 50),
                                true, false);
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   AddAnimatedFilterToLayer(child.get(), 10.0, 0.1f, 0.2f);
 
@@ -8169,8 +7931,7 @@
 
   root->SetIsContainerForFixedPositionLayers(true);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawPropertiesWithPropertyTrees(root.get());
 
@@ -8226,8 +7987,7 @@
   surface->AddChild(container);
   surface->AddChild(box);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
 }
@@ -8261,8 +8021,7 @@
   root->AddChild(frame_clip);
   frame_clip->AddChild(fixed);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawPropertiesWithPropertyTrees(root.get());
 
@@ -8318,8 +8077,7 @@
   scroller->AddChild(fixed);
   fixed->AddChild(fixed_child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawPropertiesWithPropertyTrees(root.get());
 
@@ -8369,8 +8127,7 @@
   frame_clip->AddChild(scroller);
   scroller->AddChild(fixed);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawPropertiesWithPropertyTrees(root.get());
 
@@ -8391,17 +8148,16 @@
                                gfx::PointF(), gfx::Size(800, 800), true, false);
   root->SetIsContainerForFixedPositionLayers(true);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawPropertiesWithPropertyTrees(root.get());
-  EXPECT_FALSE(host->property_trees()->needs_rebuild);
+  EXPECT_FALSE(host()->property_trees()->needs_rebuild);
 
   root->SetTransform(translate);
-  EXPECT_FALSE(host->property_trees()->needs_rebuild);
+  EXPECT_FALSE(host()->property_trees()->needs_rebuild);
 
   root->SetTransform(rotate);
-  EXPECT_TRUE(host->property_trees()->needs_rebuild);
+  EXPECT_TRUE(host()->property_trees()->needs_rebuild);
 }
 
 TEST_F(LayerTreeHostCommonTest, ChangeTransformOrigin) {
@@ -8410,8 +8166,7 @@
       make_scoped_refptr(new LayerWithForcedDrawsContent(layer_settings()));
   root->AddChild(child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_matrix;
   gfx::Transform scale_matrix;
@@ -8442,8 +8197,7 @@
   scroll_child->SetScrollParent(scroll_parent.get());
   scroll_parent->SetScrollClipLayerId(root->id());
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   gfx::Transform identity_transform;
   gfx::Transform scale;
@@ -8493,8 +8247,7 @@
   child->AddChild(grandchild);
   grandchild->AddChild(greatgrandchild);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   // Check the non-skipped case.
   ExecuteCalculateDrawPropertiesWithPropertyTrees(root.get());
@@ -8646,8 +8399,7 @@
                                gfx::PointF(), gfx::Size(10, 10), true, false);
   root->AddChild(child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawPropertiesWithPropertyTrees(root.get());
   EXPECT_EQ(gfx::Rect(10, 10), child->visible_rect_from_property_trees());
@@ -8698,8 +8450,7 @@
   SetLayerPropertiesForTesting(child.get(), identity, gfx::Point3F(),
                                gfx::PointF(), gfx::Size(100, 100), true, false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawProperties(root.get());
   EXPECT_EQ(parent->draw_properties().num_unclipped_descendants, 1u);
@@ -8739,8 +8490,7 @@
   SetLayerPropertiesForTesting(child.get(), identity, gfx::Point3F(),
                                gfx::PointF(), gfx::Size(100, 100), true, false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   EXPECT_EQ(root->num_layer_or_descendants_with_input_handler(), 0);
   ExecuteCalculateDrawProperties(root.get());
@@ -8764,8 +8514,7 @@
 
   root->AddChild(child);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   ExecuteCalculateDrawPropertiesWithPropertyTrees(root.get());
   EXPECT_NE(-1, child->transform_tree_index());
@@ -8788,8 +8537,7 @@
   SetLayerPropertiesForTesting(child.get(), identity, gfx::Point3F(),
                                gfx::PointF(), gfx::Size(100, 100), true, false);
 
-  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
-  host->SetRootLayer(root);
+  host()->SetRootLayer(root);
 
   EXPECT_FALSE(root->layer_or_descendant_is_drawn());
   EXPECT_FALSE(root->visited());
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 22609255..412e736 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -289,10 +289,8 @@
   // If the begin frame data was handled, then scroll and scale set was applied
   // by the main thread, so the active tree needs to be updated as if these sent
   // values were applied and committed.
-  if (CommitEarlyOutHandledCommit(reason)) {
+  if (CommitEarlyOutHandledCommit(reason))
     active_tree_->ApplySentScrollAndScaleDeltasFromAbortedCommit();
-    active_tree_->ResetContentsTexturesPurged();
-  }
 }
 
 void LayerTreeHostImpl::BeginCommit() {
@@ -386,12 +384,6 @@
         TRACE_EVENT_SCOPE_THREAD);
     return false;
   }
-  if (active_tree_->ContentsTexturesPurged()) {
-    TRACE_EVENT_INSTANT0(
-        "cc", "LayerTreeHostImpl::CanDraw contents textures purged",
-        TRACE_EVENT_SCOPE_THREAD);
-    return false;
-  }
   if (EvictedUIResourcesExist()) {
     TRACE_EVENT_INSTANT0(
         "cc", "LayerTreeHostImpl::CanDraw UI resources evicted not recreated",
@@ -823,9 +815,9 @@
   bool have_copy_request = false;
   bool have_missing_animated_tiles = false;
 
-  auto end = LayerIterator<LayerImpl>::End(frame->render_surface_layer_list);
-  for (auto it =
-           LayerIterator<LayerImpl>::Begin(frame->render_surface_layer_list);
+  LayerIterator end = LayerIterator::End(frame->render_surface_layer_list);
+  for (LayerIterator it =
+           LayerIterator::Begin(frame->render_surface_layer_list);
        it != end; ++it) {
     RenderPassId target_render_pass_id =
         it.target_render_surface_layer()->render_surface()->GetRenderPassId();
@@ -850,11 +842,10 @@
                                        *it,
                                        contributing_render_pass,
                                        &append_quads_data);
-    } else if (it.represents_itself() &&
-               !it->visible_content_rect().IsEmpty()) {
+    } else if (it.represents_itself() && !it->visible_layer_rect().IsEmpty()) {
       bool occluded =
           it->draw_properties().occlusion_in_content_space.IsOccluded(
-              it->visible_content_rect());
+              it->visible_layer_rect());
       if (!occluded && it->WillDraw(draw_mode, resource_provider_.get())) {
         DCHECK_EQ(active_tree_, it->layer_tree_impl());
 
@@ -880,9 +871,7 @@
         // For layers that represent themselves, add composite frame timing
         // requests if the visible rect intersects the requested rect.
         for (const auto& request : it->frame_timing_requests()) {
-          const gfx::Rect& request_content_rect =
-              it->LayerRectToContentRect(request.rect());
-          if (request_content_rect.Intersects(it->visible_content_rect())) {
+          if (request.rect().Intersects(it->visible_layer_rect())) {
             frame->composite_events.push_back(
                 FrameTimingTracker::FrameAndRectIds(
                     active_tree_->source_frame_number(), request.id()));
@@ -894,7 +883,7 @@
     }
 
     rendering_stats_instrumentation_->AddVisibleContentArea(
-        append_quads_data.visible_content_area);
+        append_quads_data.visible_layer_area);
     rendering_stats_instrumentation_->AddApproximatedVisibleContentArea(
         append_quads_data.approximated_visible_content_area);
     rendering_stats_instrumentation_->AddCheckerboardedVisibleContentArea(
@@ -1152,7 +1141,7 @@
 }
 
 void LayerTreeHostImpl::EvictTexturesForTesting() {
-  EnforceManagedMemoryPolicy(ManagedMemoryPolicy(0));
+  UpdateTileManagerMemoryPolicy(ManagedMemoryPolicy(0));
 }
 
 void LayerTreeHostImpl::BlockNotifyReadyToActivateForTesting(bool block) {
@@ -1178,26 +1167,6 @@
   return fps_counter_->current_frame_number();
 }
 
-void LayerTreeHostImpl::EnforceManagedMemoryPolicy(
-    const ManagedMemoryPolicy& policy) {
-
-  bool evicted_resources = client_->ReduceContentsTextureMemoryOnImplThread(
-      visible_ ? policy.bytes_limit_when_visible : 0,
-      ManagedMemoryPolicy::PriorityCutoffToValue(
-          visible_ ? policy.priority_cutoff_when_visible
-                   : gpu::MemoryAllocation::CUTOFF_ALLOW_NOTHING));
-  if (evicted_resources) {
-    active_tree_->SetContentsTexturesPurged();
-    if (pending_tree_)
-      pending_tree_->SetContentsTexturesPurged();
-    client_->SetNeedsCommitOnImplThread();
-    client_->OnCanDrawStateChanged(CanDraw());
-    client_->RenewTreePriority();
-  }
-
-  UpdateTileManagerMemoryPolicy(policy);
-}
-
 void LayerTreeHostImpl::UpdateTileManagerMemoryPolicy(
     const ManagedMemoryPolicy& policy) {
   if (!tile_manager_)
@@ -1370,10 +1339,10 @@
     // In single-thread mode, this can be called on the main thread by
     // GLRenderer::OnMemoryAllocationChanged.
     DebugScopedSetImplThread impl_thread(proxy_);
-    EnforceManagedMemoryPolicy(actual_policy);
+    UpdateTileManagerMemoryPolicy(actual_policy);
   } else {
     DCHECK(proxy_->IsImplThread());
-    EnforceManagedMemoryPolicy(actual_policy);
+    UpdateTileManagerMemoryPolicy(actual_policy);
   }
 
   // If there is already enough memory to draw everything imaginable and the
@@ -1977,7 +1946,7 @@
     return;
   visible_ = visible;
   DidVisibilityChange(this, visible_);
-  EnforceManagedMemoryPolicy(ActualManagedMemoryPolicy());
+  UpdateTileManagerMemoryPolicy(ActualManagedMemoryPolicy());
 
   // If we just became visible, we have to ensure that we draw high res tiles,
   // to prevent checkerboard/low res flashes.
@@ -2596,13 +2565,6 @@
   if (start_clipped || end_clipped)
     return gfx::Vector2dF();
 
-  // local_start_point and local_end_point are in content space but we want to
-  // move them to layer space for scrolling.
-  float width_scale = 1.f / layer_impl->contents_scale_x();
-  float height_scale = 1.f / layer_impl->contents_scale_y();
-  local_start_point.Scale(width_scale, height_scale);
-  local_end_point.Scale(width_scale, height_scale);
-
   // Apply the scroll delta.
   gfx::ScrollOffset previous_offset = layer_impl->CurrentScrollOffset();
   layer_impl->ScrollBy(local_end_point - local_start_point);
@@ -2613,16 +2575,11 @@
   // ScreenSpaceTransform.
   gfx::PointF actual_local_end_point =
       local_start_point + gfx::Vector2dF(scrolled.x(), scrolled.y());
-  gfx::PointF actual_local_content_end_point =
-      gfx::ScalePoint(actual_local_end_point,
-                      1.f / width_scale,
-                      1.f / height_scale);
 
   // Calculate the applied scroll delta in viewport space coordinates.
   gfx::PointF actual_screen_space_end_point =
       MathUtil::MapPoint(layer_impl->screen_space_transform(),
-                         actual_local_content_end_point,
-                         &end_clipped);
+                         actual_local_end_point, &end_clipped);
   DCHECK(!end_clipped);
   if (end_clipped)
     return gfx::Vector2dF();
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index bce1a81..518760f 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -110,10 +110,6 @@
   virtual void SetVideoNeedsBeginFrames(bool needs_begin_frames) = 0;
   virtual void PostAnimationEventsToMainThreadOnImplThread(
       scoped_ptr<AnimationEventsVector> events) = 0;
-  // Returns true if resources were deleted by this call.
-  virtual bool ReduceContentsTextureMemoryOnImplThread(
-      size_t limit_bytes,
-      int priority_cutoff) = 0;
   virtual bool IsInsideDraw() = 0;
   virtual void RenewTreePriority() = 0;
   virtual void PostDelayedAnimationTaskOnImplThread(const base::Closure& task,
@@ -631,7 +627,6 @@
                                    LayerImpl* layer_impl);
   void StartScrollbarFadeRecursive(LayerImpl* layer);
   void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy);
-  void EnforceManagedMemoryPolicy(const ManagedMemoryPolicy& policy);
 
   void MarkUIResourceNotEvicted(UIResourceId uid);
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 0dfcd01..530c49d 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -93,9 +93,7 @@
         did_request_animate_(false),
         did_request_prepare_tiles_(false),
         did_complete_page_scale_animation_(false),
-        reduce_memory_result_(true),
-        current_limit_bytes_(0),
-        current_priority_cutoff_value_(0) {
+        reduce_memory_result_(true) {
     media::InitializeMediaLibrary();
   }
 
@@ -142,12 +140,6 @@
   void SetVideoNeedsBeginFrames(bool needs_begin_frames) override {}
   void PostAnimationEventsToMainThreadOnImplThread(
       scoped_ptr<AnimationEventsVector> events) override {}
-  bool ReduceContentsTextureMemoryOnImplThread(size_t limit_bytes,
-                                               int priority_cutoff) override {
-    current_limit_bytes_ = limit_bytes;
-    current_priority_cutoff_value_ = priority_cutoff;
-    return reduce_memory_result_;
-  }
   bool IsInsideDraw() override { return false; }
   void RenewTreePriority() override {}
   void PostDelayedAnimationTaskOnImplThread(const base::Closure& task,
@@ -187,9 +179,8 @@
   void SetupRootLayerImpl(scoped_ptr<LayerImpl> root) {
     root->SetPosition(gfx::PointF());
     root->SetBounds(gfx::Size(10, 10));
-    root->SetContentBounds(gfx::Size(10, 10));
     root->SetDrawsContent(true);
-    root->draw_properties().visible_content_rect = gfx::Rect(0, 0, 10, 10);
+    root->draw_properties().visible_layer_rect = gfx::Rect(0, 0, 10, 10);
     root->SetHasRenderSurface(true);
     host_impl_->active_tree()->SetRootLayer(root.Pass());
   }
@@ -242,7 +233,6 @@
     scoped_ptr<LayerImpl> root =
         LayerImpl::Create(layer_tree_impl, 1);
     root->SetBounds(content_size);
-    root->SetContentBounds(content_size);
     root->SetPosition(gfx::PointF());
     root->SetHasRenderSurface(true);
 
@@ -261,7 +251,6 @@
 
     inner_scroll->SetScrollClipLayer(inner_clip->id());
     inner_scroll->SetBounds(content_size);
-    inner_scroll->SetContentBounds(content_size);
     inner_scroll->SetPosition(gfx::PointF());
 
     scoped_ptr<LayerImpl> outer_clip =
@@ -274,14 +263,12 @@
     outer_scroll->SetScrollClipLayer(outer_clip->id());
     outer_scroll->PushScrollOffsetFromMainThread(gfx::ScrollOffset());
     outer_scroll->SetBounds(content_size);
-    outer_scroll->SetContentBounds(content_size);
     outer_scroll->SetPosition(gfx::PointF());
 
     scoped_ptr<LayerImpl> contents =
         LayerImpl::Create(layer_tree_impl, kContentLayerId);
     contents->SetDrawsContent(true);
     contents->SetBounds(content_size);
-    contents->SetContentBounds(content_size);
     contents->SetPosition(gfx::PointF());
 
     outer_scroll->AddChild(contents.Pass());
@@ -318,7 +305,6 @@
     layer->SetScrollClipLayer(clip_layer->id());
     layer->SetDrawsContent(true);
     layer->SetBounds(size);
-    layer->SetContentBounds(size);
     clip_layer->SetBounds(gfx::Size(size.width() / 2, size.height() / 2));
     return layer.Pass();
   }
@@ -374,32 +360,6 @@
     EXPECT_TRUE(host_impl_->CanDraw());
     EXPECT_TRUE(on_can_draw_state_changed_called_);
     on_can_draw_state_changed_called_ = false;
-
-    // Toggle contents textures purged without causing any evictions,
-    // and make sure that it does not change can_draw.
-    set_reduce_memory_result(false);
-    host_impl_->SetMemoryPolicy(ManagedMemoryPolicy(
-        host_impl_->memory_allocation_limit_bytes() - 1));
-    EXPECT_TRUE(host_impl_->CanDraw());
-    EXPECT_FALSE(on_can_draw_state_changed_called_);
-    on_can_draw_state_changed_called_ = false;
-
-    // Toggle contents textures purged to make sure it toggles can_draw.
-    set_reduce_memory_result(true);
-    host_impl_->SetMemoryPolicy(ManagedMemoryPolicy(
-        host_impl_->memory_allocation_limit_bytes() - 1));
-    if (always_draw) {
-      EXPECT_TRUE(host_impl_->CanDraw());
-    } else {
-      EXPECT_FALSE(host_impl_->CanDraw());
-    }
-    EXPECT_TRUE(on_can_draw_state_changed_called_);
-    on_can_draw_state_changed_called_ = false;
-
-    host_impl_->active_tree()->ResetContentsTexturesPurged();
-    EXPECT_TRUE(host_impl_->CanDraw());
-    EXPECT_TRUE(on_can_draw_state_changed_called_);
-    on_can_draw_state_changed_called_ = false;
   }
 
   void SetupMouseMoveAtWithDeviceScale(float device_scale_factor);
@@ -434,8 +394,6 @@
   bool reduce_memory_result_;
   base::Closure animation_task_;
   base::TimeDelta requested_animation_delay_;
-  size_t current_limit_bytes_;
-  int current_priority_cutoff_value_;
 };
 
 TEST_F(LayerTreeHostImplTest, NotifyIfCanDrawChanged) {
@@ -646,7 +604,6 @@
     child_layer->SetDrawsContent(true);
     child_layer->SetPosition(gfx::PointF(0, 20));
     child_layer->SetBounds(gfx::Size(50, 50));
-    child_layer->SetContentBounds(gfx::Size(50, 50));
     scroll->AddChild(child_layer.Pass());
   }
 
@@ -846,7 +803,6 @@
   host_impl_->SetViewportSize(gfx::Size(100, 100));
 
   LayerImpl* root = host_impl_->active_tree()->root_layer();
-  root->SetContentsScale(2.f, 2.f);
   root->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 50));
 
   DrawFrame();
@@ -887,7 +843,6 @@
   host_impl_->SetViewportSize(gfx::Size(100, 100));
 
   LayerImpl* root = host_impl_->active_tree()->root_layer();
-  root->SetContentsScale(2.f, 2.f);
   root->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 50));
   root->SetPosition(gfx::PointF(-25.f, 0.f));
 
@@ -1020,7 +975,6 @@
   ASSERT_EQ(1u, scroll_layer->children().size());
   LayerImpl* overflow = scroll_layer->children()[0];
   overflow->SetBounds(overflow_size);
-  overflow->SetContentBounds(overflow_size);
   overflow->SetScrollClipLayer(scroll_layer->parent()->id());
   overflow->PushScrollOffsetFromMainThread(gfx::ScrollOffset());
   overflow->SetPosition(gfx::PointF());
@@ -1148,11 +1102,8 @@
   LayerImpl* inner_clip_layer =
       host_impl_->InnerViewportScrollLayer()->parent()->parent();
   inner_clip_layer->SetBounds(gfx::Size(100, 100));
-  inner_clip_layer->SetContentBounds(gfx::Size(100, 100));
   outer_scroll_layer->SetBounds(gfx::Size(200, 200));
-  outer_scroll_layer->SetContentBounds(gfx::Size(200, 200));
   content_layer->SetBounds(gfx::Size(200, 200));
-  content_layer->SetContentBounds(gfx::Size(200, 200));
 
   host_impl_->SetViewportSize(gfx::Size(100, 100));
 
@@ -1187,11 +1138,8 @@
   LayerImpl* inner_clip_layer =
       host_impl_->InnerViewportScrollLayer()->parent()->parent();
   inner_clip_layer->SetBounds(gfx::Size(100, 100));
-  inner_clip_layer->SetContentBounds(gfx::Size(100, 100));
   outer_scroll_layer->SetBounds(gfx::Size(200, 200));
-  outer_scroll_layer->SetContentBounds(gfx::Size(200, 200));
   content_layer->SetBounds(gfx::Size(200, 200));
-  content_layer->SetContentBounds(gfx::Size(200, 200));
 
   host_impl_->SetViewportSize(gfx::Size(100, 100));
 
@@ -1807,14 +1755,12 @@
     scroll->PushScrollOffsetFromMainThread(gfx::ScrollOffset());
     root->SetBounds(viewport_size);
     scroll->SetBounds(content_size);
-    scroll->SetContentBounds(content_size);
     scroll->SetIsContainerForFixedPositionLayers(true);
 
     scoped_ptr<LayerImpl> contents =
         LayerImpl::Create(host_impl_->active_tree(), 3);
     contents->SetDrawsContent(true);
     contents->SetBounds(content_size);
-    contents->SetContentBounds(content_size);
 
     scoped_ptr<SolidColorScrollbarLayerImpl> scrollbar =
         SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(), 4,
@@ -1965,21 +1911,18 @@
   scroll->SetScrollClipLayer(root->id());
   scroll->PushScrollOffsetFromMainThread(gfx::ScrollOffset());
   scroll->SetBounds(content_size);
-  scroll->SetContentBounds(content_size);
   scroll->SetIsContainerForFixedPositionLayers(true);
 
   scoped_ptr<LayerImpl> contents =
       LayerImpl::Create(host_impl_->active_tree(), 3);
   contents->SetDrawsContent(true);
   contents->SetBounds(content_size);
-  contents->SetContentBounds(content_size);
 
   // The scrollbar is on the right side.
   scoped_ptr<PaintedScrollbarLayerImpl> scrollbar =
       PaintedScrollbarLayerImpl::Create(host_impl_->active_tree(), 5, VERTICAL);
   scrollbar->SetDrawsContent(true);
   scrollbar->SetBounds(gfx::Size(15, viewport_size.height()));
-  scrollbar->SetContentBounds(gfx::Size(15, viewport_size.height()));
   scrollbar->SetPosition(gfx::Point(285, 0));
 
   scroll->AddChild(contents.Pass());
@@ -2172,9 +2115,8 @@
         append_quads_called_(false),
         did_draw_called_(false) {
     SetBounds(gfx::Size(10, 10));
-    SetContentBounds(gfx::Size(10, 10));
     SetDrawsContent(true);
-    draw_properties().visible_content_rect = gfx::Rect(0, 0, 10, 10);
+    draw_properties().visible_layer_rect = gfx::Rect(0, 0, 10, 10);
   }
 
  private:
@@ -2238,10 +2180,9 @@
   root->AddChild(DidDrawCheckLayer::Create(host_impl_->active_tree(), 2));
   DidDrawCheckLayer* layer =
       static_cast<DidDrawCheckLayer*>(root->children()[0]);
-  // Ensure visible_content_rect for layer is empty.
+  // Ensure visible_layer_rect for layer is empty.
   layer->SetPosition(gfx::PointF(100.f, 100.f));
   layer->SetBounds(gfx::Size(10, 10));
-  layer->SetContentBounds(gfx::Size(10, 10));
 
   LayerTreeHostImpl::FrameData frame;
 
@@ -2255,9 +2196,9 @@
   EXPECT_FALSE(layer->will_draw_called());
   EXPECT_FALSE(layer->did_draw_called());
 
-  EXPECT_TRUE(layer->visible_content_rect().IsEmpty());
+  EXPECT_TRUE(layer->visible_layer_rect().IsEmpty());
 
-  // Ensure visible_content_rect for layer is not empty
+  // Ensure visible_layer_rect for layer is not empty
   layer->SetPosition(gfx::PointF());
 
   EXPECT_FALSE(layer->will_draw_called());
@@ -2270,7 +2211,7 @@
   EXPECT_TRUE(layer->will_draw_called());
   EXPECT_TRUE(layer->did_draw_called());
 
-  EXPECT_FALSE(layer->visible_content_rect().IsEmpty());
+  EXPECT_FALSE(layer->visible_layer_rect().IsEmpty());
 }
 
 TEST_F(LayerTreeHostImplTest, WillDrawNotCalledOnOccludedLayer) {
@@ -2293,7 +2234,6 @@
   // This layer covers the occluded_layer above. Make this layer large so it can
   // occlude.
   top_layer->SetBounds(big_size);
-  top_layer->SetContentBounds(big_size);
   top_layer->SetContentsOpaque(true);
 
   LayerTreeHostImpl::FrameData frame;
@@ -2676,7 +2616,6 @@
     root_clip->SetBounds(clip_size_);
     root->SetScrollClipLayer(root_clip->id());
     root->SetBounds(layer_size_);
-    root->SetContentBounds(layer_size_);
     root->SetPosition(gfx::PointF());
     root->SetDrawsContent(false);
     root->SetIsContainerForFixedPositionLayers(true);
@@ -2705,7 +2644,6 @@
     root_clip->SetBounds(clip_size_);
     root->SetScrollClipLayer(root_clip->id());
     root->SetBounds(layer_size_);
-    root->SetContentBounds(layer_size_);
     root->SetPosition(gfx::PointF());
     root->SetDrawsContent(false);
     root->SetIsContainerForFixedPositionLayers(true);
@@ -2747,7 +2685,6 @@
     root_clip->SetBounds(inner_viewport_size);
     root->SetScrollClipLayer(root_clip->id());
     root->SetBounds(outer_viewport_size);
-    root->SetContentBounds(outer_viewport_size);
     root->SetPosition(gfx::PointF());
     root->SetDrawsContent(false);
     root->SetIsContainerForFixedPositionLayers(true);
@@ -2755,7 +2692,6 @@
     outer_clip->SetBounds(outer_viewport_size);
     outer_scroll->SetScrollClipLayer(outer_clip->id());
     outer_scroll->SetBounds(scroll_layer_size);
-    outer_scroll->SetContentBounds(scroll_layer_size);
     outer_scroll->SetPosition(gfx::PointF());
     outer_scroll->SetDrawsContent(false);
     outer_scroll->SetIsContainerForFixedPositionLayers(true);
@@ -2960,7 +2896,6 @@
   child_clip->SetBounds(sub_content_layer_size);
   child->SetScrollClipLayer(child_clip->id());
   child->SetBounds(sub_content_size);
-  child->SetContentBounds(sub_content_size);
   child->SetPosition(gfx::PointF());
   child->SetDrawsContent(true);
   child->SetIsContainerForFixedPositionLayers(true);
@@ -3316,8 +3251,6 @@
   content_layer->SetDrawsContent(true);
   content_layer->SetPosition(gfx::PointF());
   content_layer->SetBounds(contents_size);
-  content_layer->SetContentBounds(contents_size);
-  content_layer->SetContentsScale(2.f, 2.f);
 
   scoped_ptr<LayerImpl> scroll_clip_layer =
       LayerImpl::Create(host_impl_->active_tree(), 3);
@@ -3327,7 +3260,6 @@
       LayerImpl::Create(host_impl_->active_tree(), 2);
   scroll_layer->SetScrollClipLayer(3);
   scroll_layer->SetBounds(contents_size);
-  scroll_layer->SetContentBounds(contents_size);
   scroll_layer->SetPosition(gfx::PointF());
   scroll_layer->AddChild(content_layer.Pass());
   scroll_clip_layer->AddChild(scroll_layer.Pass());
@@ -3350,7 +3282,6 @@
   gfx::Size contents_size(20, 20);
   scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
   root->SetBounds(surface_size);
-  root->SetContentBounds(contents_size);
   root->AddChild(CreateScrollableLayer(2, contents_size, root.get()));
   root->SetHasRenderSurface(true);
   host_impl_->active_tree()->SetRootLayer(root.Pass());
@@ -3570,15 +3501,6 @@
   host_impl_->ScrollEnd();
   DrawOneFrame();
 
-  EXPECT_EQ(1.f, root->contents_scale_x());
-  EXPECT_EQ(1.f, root->contents_scale_y());
-  EXPECT_EQ(1.f, scroll->contents_scale_x());
-  EXPECT_EQ(1.f, scroll->contents_scale_y());
-  EXPECT_EQ(1.f, child->contents_scale_x());
-  EXPECT_EQ(1.f, child->contents_scale_y());
-  EXPECT_EQ(1.f, grand_child->contents_scale_x());
-  EXPECT_EQ(1.f, grand_child->contents_scale_y());
-
   // Make sure all the layers are drawn with the page scale delta applied, i.e.,
   // the page scale delta on the root layer is applied hierarchically.
   LayerTreeHostImpl::FrameData frame;
@@ -3606,7 +3528,6 @@
   scoped_ptr<LayerImpl> root_scrolling =
       LayerImpl::Create(host_impl_->active_tree(), 2);
   root_scrolling->SetBounds(surface_size);
-  root_scrolling->SetContentBounds(surface_size);
   root_scrolling->SetScrollClipLayer(root->id());
   root_scrolling->SetIsContainerForFixedPositionLayers(true);
   LayerImpl* root_scrolling_ptr = root_scrolling.get();
@@ -4689,7 +4610,6 @@
             RGBA_8888)) {
     resource_provider->AllocateForTesting(resource_id_);
     SetBounds(gfx::Size(10, 10));
-    SetContentBounds(gfx::Size(10, 10));
     SetDrawsContent(true);
   }
 
@@ -4707,7 +4627,6 @@
     scoped_ptr<LayerImpl> root =
         LayerImpl::Create(host_impl_->active_tree(), 1);
     root->SetBounds(gfx::Size(10, 10));
-    root->SetContentBounds(root->bounds());
     root->SetDrawsContent(false);
     root->SetHasRenderSurface(true);
     host_impl_->active_tree()->SetRootLayer(root.Pass());
@@ -4964,7 +4883,6 @@
     gfx::Rect layer_rect(viewport_size_);
     child_->SetPosition(layer_rect.origin());
     child_->SetBounds(layer_rect.size());
-    child_->SetContentBounds(layer_rect.size());
     child_->SetQuadRect(gfx::Rect(layer_rect.size()));
     child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size()));
 
@@ -4985,7 +4903,6 @@
     gfx::Rect layer_rect(0, 0, 0, 0);
     child_->SetPosition(layer_rect.origin());
     child_->SetBounds(layer_rect.size());
-    child_->SetContentBounds(layer_rect.size());
     child_->SetQuadRect(gfx::Rect(layer_rect.size()));
     child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size()));
 
@@ -5006,7 +4923,6 @@
     gfx::Rect layer_rect(500, 500, 200, 200);
     child_->SetPosition(layer_rect.origin());
     child_->SetBounds(layer_rect.size());
-    child_->SetContentBounds(layer_rect.size());
     child_->SetQuadRect(gfx::Rect(layer_rect.size()));
     child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size()));
 
@@ -5028,7 +4944,6 @@
                          viewport_size_.height() + 10);
     child_->SetPosition(layer_rect.origin());
     child_->SetBounds(layer_rect.size());
-    child_->SetContentBounds(layer_rect.size());
     child_->SetQuadRect(gfx::Rect(layer_rect.size()));
     child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size()));
 
@@ -5194,7 +5109,6 @@
   scoped_ptr<LayerImpl> root =
       FakeDrawableLayerImpl::Create(host_impl_->active_tree(), 1);
   root->SetBounds(gfx::Size(10, 10));
-  root->SetContentBounds(gfx::Size(10, 10));
   root->SetDrawsContent(true);
   root->SetHasRenderSurface(true);
   host_impl_->active_tree()->SetRootLayer(root.Pass());
@@ -5266,10 +5180,8 @@
       FakeDrawableLayerImpl::Create(layer_tree_host_impl->active_tree(), 2);
   child->SetPosition(gfx::PointF(12.f, 13.f));
   child->SetBounds(gfx::Size(14, 15));
-  child->SetContentBounds(gfx::Size(14, 15));
   child->SetDrawsContent(true);
   root->SetBounds(gfx::Size(500, 500));
-  root->SetContentBounds(gfx::Size(500, 500));
   root->SetDrawsContent(true);
   root->AddChild(child.Pass());
   layer_tree_host_impl->active_tree()->SetRootLayer(root.Pass());
@@ -5323,10 +5235,8 @@
   scoped_ptr<LayerImpl> child =
       FakeDrawableLayerImpl::Create(host_impl_->active_tree(), 2);
   child->SetBounds(gfx::Size(10, 10));
-  child->SetContentBounds(gfx::Size(10, 10));
   child->SetDrawsContent(true);
   root->SetBounds(gfx::Size(10, 10));
-  root->SetContentBounds(gfx::Size(10, 10));
   root->SetDrawsContent(true);
   root->SetHasRenderSurface(true);
   root->AddChild(child.Pass());
@@ -5583,23 +5493,20 @@
   root->SetHasRenderSurface(true);
   root->SetPosition(root_rect.origin());
   root->SetBounds(root_rect.size());
-  root->SetContentBounds(root->bounds());
-  root->draw_properties().visible_content_rect = root_rect;
+  root->draw_properties().visible_layer_rect = root_rect;
   root->SetDrawsContent(false);
   root->render_surface()->SetContentRect(gfx::Rect(root_rect.size()));
 
   child->SetPosition(gfx::PointF(child_rect.x(), child_rect.y()));
   child->SetOpacity(0.5f);
   child->SetBounds(gfx::Size(child_rect.width(), child_rect.height()));
-  child->SetContentBounds(child->bounds());
-  child->draw_properties().visible_content_rect = child_rect;
+  child->draw_properties().visible_layer_rect = child_rect;
   child->SetDrawsContent(false);
   child->SetHasRenderSurface(true);
 
   grand_child->SetPosition(grand_child_rect.origin());
   grand_child->SetBounds(grand_child_rect.size());
-  grand_child->SetContentBounds(grand_child->bounds());
-  grand_child->draw_properties().visible_content_rect = grand_child_rect;
+  grand_child->draw_properties().visible_layer_rect = grand_child_rect;
   grand_child->SetDrawsContent(true);
 
   child->AddChild(grand_child.Pass());
@@ -5678,14 +5585,12 @@
   scoped_ptr<VideoLayerImpl> video_layer = VideoLayerImpl::Create(
       host_impl_->active_tree(), 4, &provider, media::VIDEO_ROTATION_0);
   video_layer->SetBounds(gfx::Size(10, 10));
-  video_layer->SetContentBounds(gfx::Size(10, 10));
   video_layer->SetDrawsContent(true);
   root_layer->AddChild(video_layer.Pass());
 
   scoped_ptr<IOSurfaceLayerImpl> io_surface_layer =
       IOSurfaceLayerImpl::Create(host_impl_->active_tree(), 5);
   io_surface_layer->SetBounds(gfx::Size(10, 10));
-  io_surface_layer->SetContentBounds(gfx::Size(10, 10));
   io_surface_layer->SetDrawsContent(true);
   io_surface_layer->SetIOSurfaceProperties(1, gfx::Size(10, 10));
   root_layer->AddChild(io_surface_layer.Pass());
@@ -5752,46 +5657,6 @@
   Mock::VerifyAndClearExpectations(&mock_context);
 }
 
-TEST_F(LayerTreeHostImplTest, ReleaseContentsTextureShouldTriggerCommit) {
-  set_reduce_memory_result(false);
-
-  // If changing the memory limit wouldn't result in changing what was
-  // committed, then no commit should be requested.
-  set_reduce_memory_result(false);
-  host_impl_->set_max_memory_needed_bytes(
-      host_impl_->memory_allocation_limit_bytes() - 1);
-  host_impl_->SetMemoryPolicy(ManagedMemoryPolicy(
-      host_impl_->memory_allocation_limit_bytes() - 1));
-  EXPECT_FALSE(did_request_commit_);
-  did_request_commit_ = false;
-
-  // If changing the memory limit would result in changing what was
-  // committed, then a commit should be requested, even though nothing was
-  // evicted.
-  set_reduce_memory_result(false);
-  host_impl_->set_max_memory_needed_bytes(
-      host_impl_->memory_allocation_limit_bytes());
-  host_impl_->SetMemoryPolicy(ManagedMemoryPolicy(
-      host_impl_->memory_allocation_limit_bytes() - 1));
-  EXPECT_TRUE(did_request_commit_);
-  did_request_commit_ = false;
-
-  // Especially if changing the memory limit caused evictions, we need
-  // to re-commit.
-  set_reduce_memory_result(true);
-  host_impl_->set_max_memory_needed_bytes(1);
-  host_impl_->SetMemoryPolicy(ManagedMemoryPolicy(
-      host_impl_->memory_allocation_limit_bytes() - 1));
-  EXPECT_TRUE(did_request_commit_);
-  did_request_commit_ = false;
-
-  // But if we set it to the same value that it was before, we shouldn't
-  // re-commit.
-  host_impl_->SetMemoryPolicy(ManagedMemoryPolicy(
-      host_impl_->memory_allocation_limit_bytes()));
-  EXPECT_FALSE(did_request_commit_);
-}
-
 class LayerTreeHostImplTestWithDelegatingRenderer
     : public LayerTreeHostImplTest {
  protected:
@@ -5841,7 +5706,6 @@
       SolidColorLayerImpl::Create(host_impl_->active_tree(), 1);
   root->SetPosition(gfx::PointF());
   root->SetBounds(gfx::Size(10, 10));
-  root->SetContentBounds(gfx::Size(10, 10));
   root->SetDrawsContent(true);
   root->SetHasRenderSurface(true);
 
@@ -5850,7 +5714,6 @@
       SolidColorLayerImpl::Create(host_impl_->active_tree(), 2);
   child->SetPosition(gfx::PointF(9.f, 9.f));
   child->SetBounds(gfx::Size(1, 1));
-  child->SetContentBounds(gfx::Size(1, 1));
   child->SetDrawsContent(true);
   root->AddChild(child.Pass());
 
@@ -5961,8 +5824,8 @@
 
   bool clipped = false, force_aa = false;
   gfx::QuadF device_layer_quad = MathUtil::MapQuad(
-      quad->shared_quad_state->content_to_target_transform,
-      gfx::QuadF(quad->shared_quad_state->visible_content_rect), &clipped);
+      quad->shared_quad_state->quad_to_target_transform,
+      gfx::QuadF(quad->shared_quad_state->visible_quad_layer_rect), &clipped);
   EXPECT_FALSE(clipped);
   bool antialiased =
       GLRendererWithSetupQuadForAntialiasing::ShouldAntialiasQuad(
@@ -6079,7 +5942,6 @@
   scoped_ptr<VideoLayerImpl> video_layer = VideoLayerImpl::Create(
       host_impl_->active_tree(), 2, &provider, media::VIDEO_ROTATION_0);
   video_layer->SetBounds(gfx::Size(10, 10));
-  video_layer->SetContentBounds(gfx::Size(10, 10));
   video_layer->SetDrawsContent(true);
   root_layer->AddChild(video_layer.Pass());
   SetupRootLayerImpl(root_layer.Pass());
@@ -6107,51 +5969,6 @@
   EXPECT_LT(0ul, host_impl_->memory_allocation_limit_bytes());
 }
 
-TEST_F(LayerTreeHostImplTest, MemoryPolicy) {
-  ManagedMemoryPolicy policy1(
-      456, gpu::MemoryAllocation::CUTOFF_ALLOW_EVERYTHING, 1000);
-  int everything_cutoff_value = ManagedMemoryPolicy::PriorityCutoffToValue(
-      gpu::MemoryAllocation::CUTOFF_ALLOW_EVERYTHING);
-  int allow_nice_to_have_cutoff_value =
-      ManagedMemoryPolicy::PriorityCutoffToValue(
-          gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE);
-  int nothing_cutoff_value = ManagedMemoryPolicy::PriorityCutoffToValue(
-      gpu::MemoryAllocation::CUTOFF_ALLOW_NOTHING);
-
-  // GPU rasterization should be disabled by default on the tree(s)
-  EXPECT_FALSE(host_impl_->active_tree()->use_gpu_rasterization());
-  EXPECT_TRUE(host_impl_->pending_tree() == NULL);
-
-  host_impl_->SetVisible(true);
-  host_impl_->SetMemoryPolicy(policy1);
-  EXPECT_EQ(policy1.bytes_limit_when_visible, current_limit_bytes_);
-  EXPECT_EQ(everything_cutoff_value, current_priority_cutoff_value_);
-
-  host_impl_->SetVisible(false);
-  EXPECT_EQ(0u, current_limit_bytes_);
-  EXPECT_EQ(nothing_cutoff_value, current_priority_cutoff_value_);
-
-  host_impl_->SetVisible(true);
-  EXPECT_EQ(policy1.bytes_limit_when_visible, current_limit_bytes_);
-  EXPECT_EQ(everything_cutoff_value, current_priority_cutoff_value_);
-
-  // Now enable GPU rasterization and test if we get nice to have cutoff,
-  // when visible.
-  LayerTreeSettings settings;
-  settings.gpu_rasterization_enabled = true;
-  CreateHostImpl(settings, CreateOutputSurface());
-  host_impl_->SetContentIsSuitableForGpuRasterization(true);
-  host_impl_->SetHasGpuRasterizationTrigger(true);
-  host_impl_->SetVisible(true);
-  host_impl_->SetMemoryPolicy(policy1);
-  EXPECT_EQ(policy1.bytes_limit_when_visible, current_limit_bytes_);
-  EXPECT_EQ(allow_nice_to_have_cutoff_value, current_priority_cutoff_value_);
-
-  host_impl_->SetVisible(false);
-  EXPECT_EQ(0u, current_limit_bytes_);
-  EXPECT_EQ(nothing_cutoff_value, current_priority_cutoff_value_);
-}
-
 TEST_F(LayerTreeHostImplTest, RequireHighResWhenVisible) {
   ASSERT_TRUE(host_impl_->active_tree());
 
@@ -6504,7 +6321,6 @@
       LayerImpl::Create(host_impl_->active_tree(), occluder_layer_id);
   occluder_layer->SetDrawsContent(true);
   occluder_layer->SetBounds(content_size);
-  occluder_layer->SetContentBounds(content_size);
   occluder_layer->SetPosition(gfx::PointF());
 
   // The parent of the occluder is *above* the scroller.
@@ -6533,7 +6349,6 @@
       LayerImpl::Create(host_impl_->active_tree(), occluder_layer_id);
   occluder_layer->SetDrawsContent(true);
   occluder_layer->SetBounds(content_size);
-  occluder_layer->SetContentBounds(content_size);
   occluder_layer->SetPosition(gfx::PointF(-10.f, -10.f));
 
   int child_scroll_clip_layer_id = 7;
@@ -6605,7 +6420,6 @@
       LayerImpl::Create(host_impl_->active_tree(), 9);
   grand_child_layer->SetDrawsContent(true);
   grand_child_layer->SetBounds(content_size);
-  grand_child_layer->SetContentBounds(content_size);
   // Move the grand child so it's not hit by our test point.
   grand_child_layer->SetPosition(gfx::PointF(10.f, 10.f));
 
@@ -6642,7 +6456,6 @@
       LayerImpl::Create(host_impl_->active_tree(), scroll_child_id);
   scroll_child->SetDrawsContent(true);
   scroll_child->SetBounds(content_size);
-  scroll_child->SetContentBounds(content_size);
   // Move the scroll child so it's not hit by our test point.
   scroll_child->SetPosition(gfx::PointF(10.f, 10.f));
 
@@ -6684,7 +6497,6 @@
       SolidColorLayerImpl::Create(host_impl_->active_tree(), 1);
   root->SetPosition(gfx::PointF());
   root->SetBounds(gfx::Size(10, 10));
-  root->SetContentBounds(gfx::Size(10, 10));
   root->SetDrawsContent(true);
   root->SetHasRenderSurface(true);
 
@@ -6725,7 +6537,6 @@
       SolidColorLayerImpl::Create(host_impl_->active_tree(), root_layer_id);
   root->SetPosition(gfx::PointF());
   root->SetBounds(gfx::Size(10, 10));
-  root->SetContentBounds(gfx::Size(10, 10));
   root->SetDrawsContent(true);
   root->SetHasRenderSurface(true);
 
@@ -7286,7 +7097,6 @@
 
     inner_scroll->SetScrollClipLayer(inner_clip->id());
     inner_scroll->SetBounds(outer_viewport);
-    inner_scroll->SetContentBounds(outer_viewport);
     inner_scroll->SetPosition(gfx::PointF());
 
     scoped_ptr<LayerImpl> outer_clip =
@@ -7299,14 +7109,12 @@
     outer_scroll->SetScrollClipLayer(outer_clip->id());
     outer_scroll->PushScrollOffsetFromMainThread(gfx::ScrollOffset());
     outer_scroll->SetBounds(content_size);
-    outer_scroll->SetContentBounds(content_size);
     outer_scroll->SetPosition(gfx::PointF());
 
     scoped_ptr<LayerImpl> contents =
         LayerImpl::Create(layer_tree_impl, 8);
     contents->SetDrawsContent(true);
     contents->SetBounds(content_size);
-    contents->SetContentBounds(content_size);
     contents->SetPosition(gfx::PointF());
 
     outer_scroll->AddChild(contents.Pass());
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index f181e3c..956cf41d 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -69,17 +69,7 @@
 namespace cc {
 namespace {
 
-class LayerTreeHostTest : public LayerTreeTest {
- public:
-  LayerTreeHostTest() : contents_texture_manager_(nullptr) {}
-
-  void DidInitializeOutputSurface() override {
-    contents_texture_manager_ = layer_tree_host()->contents_texture_manager();
-  }
-
- protected:
-  PrioritizedResourceManager* contents_texture_manager_;
-};
+class LayerTreeHostTest : public LayerTreeTest {};
 
 class LayerTreeHostTestHasImplThreadTest : public LayerTreeHostTest {
  public:
@@ -3533,69 +3523,6 @@
 
 MULTI_THREAD_TEST_F(LayerTreeHostTestUpdateLayerInEmptyViewport);
 
-class LayerTreeHostTestAbortEvictedTextures : public LayerTreeHostTest {
- public:
-  LayerTreeHostTestAbortEvictedTextures()
-      : num_will_begin_main_frames_(0), num_impl_commits_(0) {}
-
- protected:
-  void SetupTree() override {
-    scoped_refptr<SolidColorLayer> root_layer =
-        SolidColorLayer::Create(layer_settings());
-    root_layer->SetBounds(gfx::Size(200, 200));
-    root_layer->SetIsDrawable(true);
-    root_layer->CreateRenderSurface();
-
-    layer_tree_host()->SetRootLayer(root_layer);
-    LayerTreeHostTest::SetupTree();
-  }
-
-  void BeginTest() override { PostSetNeedsCommitToMainThread(); }
-
-  void WillBeginMainFrame() override {
-    num_will_begin_main_frames_++;
-    switch (num_will_begin_main_frames_) {
-      case 2:
-        // Send a redraw to the compositor thread.  This will (wrongly) be
-        // ignored unless aborting resets the texture state.
-        layer_tree_host()->SetNeedsRedraw();
-        break;
-    }
-  }
-
-  void BeginCommitOnThread(LayerTreeHostImpl* impl) override {
-    num_impl_commits_++;
-  }
-
-  void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
-    switch (impl->SourceAnimationFrameNumberForTesting()) {
-      case 1:
-        // Prevent draws until commit.
-        impl->active_tree()->SetContentsTexturesPurged();
-        EXPECT_FALSE(impl->CanDraw());
-        // Trigger an abortable commit.
-        impl->SetNeedsCommit();
-        break;
-      case 2:
-        EndTest();
-        break;
-    }
-  }
-
-  void AfterTest() override {
-    // Ensure that the commit was truly aborted.
-    EXPECT_EQ(2, num_will_begin_main_frames_);
-    EXPECT_EQ(1, num_impl_commits_);
-  }
-
- private:
-  int num_will_begin_main_frames_;
-  int num_impl_commits_;
-};
-
-// Commits can only be aborted when using the thread proxy.
-MULTI_THREAD_TEST_F(LayerTreeHostTestAbortEvictedTextures);
-
 class LayerTreeHostTestMaxTransferBufferUsageBytes : public LayerTreeHostTest {
  protected:
   void InitializeSettings(LayerTreeSettings* settings) override {
@@ -4804,8 +4731,7 @@
       float quad_scale =
           quad->tex_coord_rect.width() / static_cast<float>(quad->rect.width());
       float transform_scale = SkMScalarToFloat(
-          quad->shared_quad_state->content_to_target_transform.matrix().get(0,
-                                                                            0));
+          quad->shared_quad_state->quad_to_target_transform.matrix().get(0, 0));
       float scale = quad_scale / transform_scale;
       if (frame_scale != 0.f && frame_scale != scale)
         return 0.f;
@@ -5106,8 +5032,7 @@
       float quad_scale =
           quad->tex_coord_rect.width() / static_cast<float>(quad->rect.width());
       float transform_scale = SkMScalarToFloat(
-          quad->shared_quad_state->content_to_target_transform.matrix().get(0,
-                                                                            0));
+          quad->shared_quad_state->quad_to_target_transform.matrix().get(0, 0));
       float scale = quad_scale / transform_scale;
       if (frame_scale != 0.f && frame_scale != scale)
         return 0.f;
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index b1287d3..45e3456 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -1035,5 +1035,55 @@
 SINGLE_AND_MULTI_THREAD_TEST_F(
     LayerTreeHostAnimationTestAddAnimationAfterAnimating);
 
+class LayerTreeHostAnimationTestNotifyAnimationFinished
+    : public LayerTreeHostAnimationTest {
+ public:
+  LayerTreeHostAnimationTestNotifyAnimationFinished()
+      : called_animation_started_(false), called_animation_finished_(false) {}
+
+  void SetupTree() override {
+    LayerTreeHostAnimationTest::SetupTree();
+    picture_ = FakePictureLayer::Create(layer_settings(), &client_);
+    picture_->SetBounds(gfx::Size(4, 4));
+    picture_->set_layer_animation_delegate(this);
+    layer_tree_host()->root_layer()->AddChild(picture_);
+  }
+
+  void BeginTest() override {
+    layer_tree_host()->SetViewportSize(gfx::Size());
+    PostAddLongAnimationToMainThread(picture_.get());
+  }
+
+  void NotifyAnimationStarted(base::TimeTicks monotonic_time,
+                              Animation::TargetProperty target_property,
+                              int group) override {
+    called_animation_started_ = true;
+    layer_tree_host()->AnimateLayers(
+        base::TimeTicks::FromInternalValue(std::numeric_limits<int64>::max()));
+    PostSetNeedsCommitToMainThread();
+  }
+
+  void NotifyAnimationFinished(base::TimeTicks monotonic_time,
+                               Animation::TargetProperty target_property,
+                               int group) override {
+    called_animation_finished_ = true;
+    EndTest();
+  }
+
+  void AfterTest() override {
+    EXPECT_TRUE(called_animation_started_);
+    EXPECT_TRUE(called_animation_finished_);
+  }
+
+ private:
+  bool called_animation_started_;
+  bool called_animation_finished_;
+  FakeContentLayerClient client_;
+  scoped_refptr<FakePictureLayer> picture_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+    LayerTreeHostAnimationTestNotifyAnimationFinished);
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_unittest_copyrequest.cc b/cc/trees/layer_tree_host_unittest_copyrequest.cc
index d92bd9c..b474507 100644
--- a/cc/trees/layer_tree_host_unittest_copyrequest.cc
+++ b/cc/trees/layer_tree_host_unittest_copyrequest.cc
@@ -1025,10 +1025,9 @@
 
     bool saw_root = false;
     bool saw_child = false;
-    for (LayerIterator<LayerImpl> it = LayerIterator<LayerImpl>::Begin(
-             frame_data->render_surface_layer_list);
-         it != LayerIterator<LayerImpl>::End(
-                   frame_data->render_surface_layer_list);
+    for (LayerIterator it =
+             LayerIterator::Begin(frame_data->render_surface_layer_list);
+         it != LayerIterator::End(frame_data->render_surface_layer_list);
          ++it) {
       if (it.represents_itself()) {
         if (*it == root)
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index ce3c3e06..fe2e958 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -57,7 +57,6 @@
       max_page_scale_factor_(0),
       elastic_overscroll_(elastic_overscroll),
       scrolling_layer_id_from_previous_tree_(0),
-      contents_textures_purged_(false),
       viewport_size_invalid_(false),
       needs_update_draw_properties_(true),
       needs_full_tree_sync_(true),
@@ -235,11 +234,6 @@
   target_tree->set_background_color(background_color());
   target_tree->set_has_transparent_background(has_transparent_background());
 
-  if (ContentsTexturesPurged())
-    target_tree->SetContentsTexturesPurged();
-  else
-    target_tree->ResetContentsTexturesPurged();
-
   if (ViewportSizeInvalid())
     target_tree->SetViewportSizeInvalid();
   else
@@ -608,8 +602,8 @@
     // LayerIterator is used here instead of CallFunctionForSubtree to only
     // UpdateTilePriorities on layers that will be visible (and thus have valid
     // draw properties) and not because any ordering is required.
-    auto end = LayerIterator<LayerImpl>::End(&render_surface_layer_list_);
-    for (auto it = LayerIterator<LayerImpl>::Begin(&render_surface_layer_list_);
+    LayerIterator end = LayerIterator::End(&render_surface_layer_list_);
+    for (LayerIterator it = LayerIterator::Begin(&render_surface_layer_list_);
          it != end; ++it) {
       occlusion_tracker.EnterLayer(it);
 
@@ -797,24 +791,6 @@
                                                  source_frame_number_);
 }
 
-bool LayerTreeImpl::ContentsTexturesPurged() const {
-  return contents_textures_purged_;
-}
-
-void LayerTreeImpl::SetContentsTexturesPurged() {
-  if (contents_textures_purged_)
-    return;
-  contents_textures_purged_ = true;
-  layer_tree_host_impl_->OnCanDrawStateChangedForTree();
-}
-
-void LayerTreeImpl::ResetContentsTexturesPurged() {
-  if (!contents_textures_purged_)
-    return;
-  contents_textures_purged_ = false;
-  layer_tree_host_impl_->OnCanDrawStateChangedForTree();
-}
-
 bool LayerTreeImpl::RequiresHighResToDraw() const {
   return layer_tree_host_impl_->RequiresHighResToDraw();
 }
@@ -1006,12 +982,9 @@
 
 void LayerTreeImpl::GetAllPrioritizedTilesForTracing(
     std::vector<PrioritizedTile>* prioritized_tiles) const {
-  typedef LayerIterator<LayerImpl> LayerIteratorType;
-  LayerIteratorType end = LayerIteratorType::End(&render_surface_layer_list_);
-  for (LayerIteratorType it =
-           LayerIteratorType::Begin(&render_surface_layer_list_);
-       it != end;
-       ++it) {
+  LayerIterator end = LayerIterator::End(&render_surface_layer_list_);
+  for (LayerIterator it = LayerIterator::Begin(&render_surface_layer_list_);
+       it != end; ++it) {
     if (!it.represents_itself())
       continue;
     LayerImpl* layer_impl = *it;
@@ -1028,10 +1001,9 @@
   state->EndDictionary();
 
   state->BeginArray("render_surface_layer_list");
-  typedef LayerIterator<LayerImpl> LayerIteratorType;
-  LayerIteratorType end = LayerIteratorType::End(&render_surface_layer_list_);
-  for (LayerIteratorType it = LayerIteratorType::Begin(
-           &render_surface_layer_list_); it != end; ++it) {
+  LayerIterator end = LayerIterator::End(&render_surface_layer_list_);
+  for (LayerIterator it = LayerIterator::Begin(&render_surface_layer_list_);
+       it != end; ++it) {
     if (!it.represents_itself())
       continue;
     TracedValue::AppendIDRef(*it, state);
@@ -1283,9 +1255,7 @@
 
 static bool PointHitsRegion(const gfx::PointF& screen_space_point,
                             const gfx::Transform& screen_space_transform,
-                            const Region& layer_space_region,
-                            float layer_content_scale_x,
-                            float layer_content_scale_y) {
+                            const Region& layer_space_region) {
   // If the transform is not invertible, then assume that this point doesn't hit
   // this region.
   gfx::Transform inverse_screen_space_transform(
@@ -1296,12 +1266,8 @@
   // Transform the hit test point from screen space to the local space of the
   // given region.
   bool clipped = false;
-  gfx::PointF hit_test_point_in_content_space = MathUtil::ProjectPoint(
+  gfx::PointF hit_test_point_in_layer_space = MathUtil::ProjectPoint(
       inverse_screen_space_transform, screen_space_point, &clipped);
-  gfx::PointF hit_test_point_in_layer_space =
-      gfx::ScalePoint(hit_test_point_in_content_space,
-                      1.f / layer_content_scale_x,
-                      1.f / layer_content_scale_y);
 
   // If ProjectPoint could not project to a valid value, then we assume that
   // this point doesn't hit this region.
@@ -1463,11 +1429,8 @@
   if (layer_impl->touch_event_handler_region().IsEmpty())
     return false;
 
-  if (!PointHitsRegion(screen_space_point,
-                       layer_impl->screen_space_transform(),
-                       layer_impl->touch_event_handler_region(),
-                       layer_impl->contents_scale_x(),
-                       layer_impl->contents_scale_y()))
+  if (!PointHitsRegion(screen_space_point, layer_impl->screen_space_transform(),
+                       layer_impl->touch_event_handler_region()))
     return false;
 
   // At this point, we think the point does hit the touch event handler region
@@ -1535,18 +1498,14 @@
   if (!layer || layer_bound.type == SELECTION_BOUND_EMPTY)
     return viewport_bound;
 
-  gfx::PointF layer_scaled_top = gfx::ScalePoint(layer_bound.edge_top,
-                                                 layer->contents_scale_x(),
-                                                 layer->contents_scale_y());
-  gfx::PointF layer_scaled_bottom = gfx::ScalePoint(layer_bound.edge_bottom,
-                                                    layer->contents_scale_x(),
-                                                    layer->contents_scale_y());
+  gfx::PointF layer_top = layer_bound.edge_top;
+  gfx::PointF layer_bottom = layer_bound.edge_bottom;
 
   bool clipped = false;
-  gfx::PointF screen_top = MathUtil::MapPoint(
-      layer->screen_space_transform(), layer_scaled_top, &clipped);
+  gfx::PointF screen_top =
+      MathUtil::MapPoint(layer->screen_space_transform(), layer_top, &clipped);
   gfx::PointF screen_bottom = MathUtil::MapPoint(
-      layer->screen_space_transform(), layer_scaled_bottom, &clipped);
+      layer->screen_space_transform(), layer_bottom, &clipped);
 
   const float inv_scale = 1.f / device_scale_factor;
   viewport_bound.edge_top = gfx::ScalePoint(screen_top, inv_scale);
@@ -1557,9 +1516,9 @@
   // Shifting the visibility point fractionally inward ensures that neighboring
   // or logically coincident layers aligned to integral DPI coordinates will not
   // spuriously occlude the bound.
-  gfx::Vector2dF visibility_offset = layer_scaled_top - layer_scaled_bottom;
+  gfx::Vector2dF visibility_offset = layer_top - layer_bottom;
   visibility_offset.Scale(device_scale_factor / visibility_offset.Length());
-  gfx::PointF visibility_point = layer_scaled_bottom + visibility_offset;
+  gfx::PointF visibility_point = layer_bottom + visibility_offset;
   if (visibility_point.x() <= 0)
     visibility_point.set_x(visibility_point.x() + device_scale_factor);
   visibility_point = MathUtil::MapPoint(
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index fb4440b..e58506e8 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -259,10 +259,6 @@
 
   void DidBecomeActive();
 
-  bool ContentsTexturesPurged() const;
-  void SetContentsTexturesPurged();
-  void ResetContentsTexturesPurged();
-
   // Set on the active tree when the viewport size recently changed
   // and the active tree's size is now out of date.
   bool ViewportSizeInvalid() const;
@@ -400,7 +396,6 @@
   // would not be fully covered by opaque content.
   Region unoccluded_screen_space_region_;
 
-  bool contents_textures_purged_;
   bool viewport_size_invalid_;
   bool needs_update_draw_properties_;
 
diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc
index 4811893..81a566f 100644
--- a/cc/trees/layer_tree_impl_unittest.cc
+++ b/cc/trees/layer_tree_impl_unittest.cc
@@ -1470,8 +1470,7 @@
   ASSERT_EQ(1u, root_layer()->render_surface()->layer_list().size());
 
   // Check whether the child layer fits into the root after scaled.
-  EXPECT_EQ(gfx::Rect(test_layer->bounds()),
-            test_layer->visible_content_rect());
+  EXPECT_EQ(gfx::Rect(test_layer->bounds()), test_layer->visible_layer_rect());
 
   // Hit checking for a point outside the layer should return a null pointer
   // (the root layer does not draw content, so it will not be tested either).
diff --git a/cc/trees/occlusion_tracker.cc b/cc/trees/occlusion_tracker.cc
index 002df50..996adc89 100644
--- a/cc/trees/occlusion_tracker.cc
+++ b/cc/trees/occlusion_tracker.cc
@@ -46,8 +46,7 @@
                    second_last.occlusion_from_inside_target);
 }
 
-void OcclusionTracker::EnterLayer(
-    const LayerIteratorPosition<LayerImpl>& layer_iterator) {
+void OcclusionTracker::EnterLayer(const LayerIteratorPosition& layer_iterator) {
   LayerImpl* render_target = layer_iterator.target_render_surface_layer;
 
   if (layer_iterator.represents_itself)
@@ -56,8 +55,7 @@
     FinishedRenderTarget(render_target);
 }
 
-void OcclusionTracker::LeaveLayer(
-    const LayerIteratorPosition<LayerImpl>& layer_iterator) {
+void OcclusionTracker::LeaveLayer(const LayerIteratorPosition& layer_iterator) {
   LayerImpl* render_target = layer_iterator.target_render_surface_layer;
 
   if (layer_iterator.represents_itself)
@@ -358,11 +356,11 @@
   if (layer->Is3dSorted())
     return;
 
-  SimpleEnclosedRegion opaque_contents = layer->VisibleContentOpaqueRegion();
-  if (opaque_contents.IsEmpty())
+  SimpleEnclosedRegion opaque_layer_region = layer->VisibleOpaqueRegion();
+  if (opaque_layer_region.IsEmpty())
     return;
 
-  DCHECK(layer->visible_content_rect().Contains(opaque_contents.bounds()));
+  DCHECK(layer->visible_layer_rect().Contains(opaque_layer_region.bounds()));
 
   // TODO(danakj): Find a rect interior to each transformed quad.
   if (!layer->draw_transform().Preserves2dAxisAlignment())
@@ -377,10 +375,10 @@
         layer->render_target()->render_surface()->content_rect());
   }
 
-  for (size_t i = 0; i < opaque_contents.GetRegionComplexity(); ++i) {
+  for (size_t i = 0; i < opaque_layer_region.GetRegionComplexity(); ++i) {
     gfx::Rect transformed_rect =
         MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
-            layer->draw_transform(), opaque_contents.GetRect(i));
+            layer->draw_transform(), opaque_layer_region.GetRect(i));
     transformed_rect.Intersect(clip_rect_in_target);
     if (transformed_rect.width() < minimum_tracking_size_.width() &&
         transformed_rect.height() < minimum_tracking_size_.height())
diff --git a/cc/trees/occlusion_tracker.h b/cc/trees/occlusion_tracker.h
index 7a6e494..9a9294c 100644
--- a/cc/trees/occlusion_tracker.h
+++ b/cc/trees/occlusion_tracker.h
@@ -42,10 +42,10 @@
 
   // Called at the beginning of each step in the LayerIterator's front-to-back
   // traversal.
-  void EnterLayer(const LayerIteratorPosition<LayerImpl>& layer_iterator);
+  void EnterLayer(const LayerIteratorPosition& layer_iterator);
   // Called at the end of each step in the LayerIterator's front-to-back
   // traversal.
-  void LeaveLayer(const LayerIteratorPosition<LayerImpl>& layer_iterator);
+  void LeaveLayer(const LayerIteratorPosition& layer_iterator);
 
   // Gives the region of the screen that is not occluded by something opaque.
   Region ComputeVisibleRegionInScreen() const;
diff --git a/cc/trees/occlusion_tracker_perftest.cc b/cc/trees/occlusion_tracker_perftest.cc
index b4303e5..209399d 100644
--- a/cc/trees/occlusion_tracker_perftest.cc
+++ b/cc/trees/occlusion_tracker_perftest.cc
@@ -4,6 +4,7 @@
 
 #include "cc/trees/occlusion_tracker.h"
 
+#include "base/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "cc/debug/lap_timer.h"
 #include "cc/layers/layer_iterator.h"
@@ -13,6 +14,7 @@
 #include "cc/test/fake_proxy.h"
 #include "cc/test/fake_rendering_stats_instrumentation.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_host_impl.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "cc/trees/single_thread_proxy.h"
@@ -32,13 +34,13 @@
       : timer_(kWarmupRuns,
                base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
                kTimeCheckInterval),
+        proxy_(base::ThreadTaskRunnerHandle::Get(), nullptr),
         impl_(&proxy_) {}
   void CreateHost() {
     LayerTreeSettings settings;
-    shared_bitmap_manager_.reset(new TestSharedBitmapManager());
-    host_impl_ =
-        LayerTreeHostImpl::Create(settings, &client_, &proxy_, &stats_,
-                                  shared_bitmap_manager_.get(), NULL, NULL, 1);
+    host_impl_ = LayerTreeHostImpl::Create(settings, &client_, &proxy_, &stats_,
+                                           &shared_bitmap_manager_, nullptr,
+                                           &task_graph_runner_, 1);
     host_impl_->InitializeRenderer(FakeOutputSurface::Create3d());
 
     scoped_ptr<LayerImpl> root_layer = LayerImpl::Create(active_tree(), 1);
@@ -67,7 +69,8 @@
   FakeProxy proxy_;
   DebugScopedSetImplThread impl_;
   FakeRenderingStatsInstrumentation stats_;
-  scoped_ptr<SharedBitmapManager> shared_bitmap_manager_;
+  TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   scoped_ptr<LayerTreeHostImpl> host_impl_;
 };
 
@@ -86,7 +89,6 @@
   opaque_layer->SetContentsOpaque(true);
   opaque_layer->SetDrawsContent(true);
   opaque_layer->SetBounds(viewport_rect.size());
-  opaque_layer->SetContentBounds(viewport_rect.size());
   active_tree()->root_layer()->AddChild(opaque_layer.Pass());
 
   bool update_lcd_text = false;
@@ -95,10 +97,10 @@
   ASSERT_EQ(1u, rsll.size());
   EXPECT_EQ(1u, rsll[0]->render_surface()->layer_list().size());
 
-  LayerIterator<LayerImpl> begin = LayerIterator<LayerImpl>::Begin(&rsll);
-  LayerIterator<LayerImpl> end = LayerIterator<LayerImpl>::End(&rsll);
+  LayerIterator begin = LayerIterator::Begin(&rsll);
+  LayerIterator end = LayerIterator::End(&rsll);
 
-  LayerIteratorPosition<LayerImpl> pos = begin;
+  LayerIteratorPosition pos = begin;
 
   // The opaque_layer adds occlusion over the whole viewport.
   tracker.EnterLayer(pos);
@@ -128,7 +130,7 @@
   } while (!timer_.HasTimeLimitExpired());
 
   ++begin;
-  LayerIteratorPosition<LayerImpl> next = begin;
+  LayerIteratorPosition next = begin;
   EXPECT_EQ(active_tree()->root_layer(), next.current_layer);
 
   ++begin;
@@ -155,8 +157,6 @@
     opaque_layer->SetDrawsContent(true);
     opaque_layer->SetBounds(
         gfx::Size(viewport_rect.width() / 2, viewport_rect.height() / 2));
-    opaque_layer->SetContentBounds(
-        gfx::Size(viewport_rect.width() / 2, viewport_rect.height() / 2));
     opaque_layer->SetPosition(gfx::Point(i, i));
     active_tree()->root_layer()->AddChild(opaque_layer.Pass());
   }
@@ -168,17 +168,17 @@
   EXPECT_EQ(static_cast<size_t>(kNumOpaqueLayers),
             rsll[0]->render_surface()->layer_list().size());
 
-  LayerIterator<LayerImpl> begin = LayerIterator<LayerImpl>::Begin(&rsll);
-  LayerIterator<LayerImpl> end = LayerIterator<LayerImpl>::End(&rsll);
+  LayerIterator begin = LayerIterator::Begin(&rsll);
+  LayerIterator end = LayerIterator::End(&rsll);
 
   // The opaque_layers add occlusion.
   for (int i = 0; i < kNumOpaqueLayers - 1; ++i) {
-    LayerIteratorPosition<LayerImpl> pos = begin;
+    LayerIteratorPosition pos = begin;
     tracker.EnterLayer(pos);
     tracker.LeaveLayer(pos);
     ++begin;
   }
-  LayerIteratorPosition<LayerImpl> pos = begin;
+  LayerIteratorPosition pos = begin;
   tracker.EnterLayer(pos);
   tracker.LeaveLayer(pos);
 
@@ -200,7 +200,7 @@
   } while (!timer_.HasTimeLimitExpired());
 
   ++begin;
-  LayerIteratorPosition<LayerImpl> next = begin;
+  LayerIteratorPosition next = begin;
   EXPECT_EQ(active_tree()->root_layer(), next.current_layer);
 
   ++begin;
diff --git a/cc/trees/occlusion_tracker_unittest.cc b/cc/trees/occlusion_tracker_unittest.cc
index 6550d42..08cfcf7 100644
--- a/cc/trees/occlusion_tracker_unittest.cc
+++ b/cc/trees/occlusion_tracker_unittest.cc
@@ -35,12 +35,12 @@
     SetDrawsContent(true);
   }
 
-  SimpleEnclosedRegion VisibleContentOpaqueRegion() const override {
+  SimpleEnclosedRegion VisibleOpaqueRegion() const override {
     if (override_opaque_contents_rect_) {
       return SimpleEnclosedRegion(
-          gfx::IntersectRects(opaque_contents_rect_, visible_content_rect()));
+          gfx::IntersectRects(opaque_contents_rect_, visible_layer_rect()));
     }
-    return LayerImpl::VisibleContentOpaqueRegion();
+    return LayerImpl::VisibleOpaqueRegion();
   }
   void SetOpaqueContentsRect(const gfx::Rect& opaque_contents_rect) {
     override_opaque_contents_rect_ = true;
@@ -59,7 +59,7 @@
 
   bool OccludedLayer(const LayerImpl* layer,
                      const gfx::Rect& content_rect) const {
-    DCHECK(layer->visible_content_rect().Contains(content_rect));
+    DCHECK(layer->visible_layer_rect().Contains(content_rect));
     return this->GetCurrentOcclusionForLayer(layer->draw_transform())
         .IsOccluded(content_rect);
   }
@@ -68,7 +68,7 @@
   // layer. Simple wrapper around GetUnoccludedContentRect.
   gfx::Rect UnoccludedLayerContentRect(const LayerImpl* layer,
                                        const gfx::Rect& content_rect) const {
-    DCHECK(layer->visible_content_rect().Contains(content_rect));
+    DCHECK(layer->visible_layer_rect().Contains(content_rect));
     return this->GetCurrentOcclusionForLayer(layer->draw_transform())
         .GetUnoccludedContentRect(content_rect);
   }
@@ -233,7 +233,7 @@
     LayerTreeHostCommon::CalculateDrawProperties(&inputs);
 
     layer_iterator_ = layer_iterator_begin_ =
-        LayerIterator<LayerImpl>::Begin(&render_surface_layer_list_impl_);
+        LayerIterator::Begin(&render_surface_layer_list_impl_);
   }
 
   void EnterLayer(LayerImpl* layer, OcclusionTracker* occlusion) {
@@ -287,22 +287,13 @@
 
   void SetRootLayerOnMainThread(LayerImpl* root) {}
 
-  void SetBaseProperties(LayerImpl* layer,
-                         const gfx::Transform& transform,
-                         const gfx::PointF& position,
-                         const gfx::Size& bounds) {
-    layer->SetTransform(transform);
-    layer->SetPosition(position);
-    layer->SetBounds(bounds);
-  }
-
   void SetProperties(LayerImpl* layer,
                      const gfx::Transform& transform,
                      const gfx::PointF& position,
                      const gfx::Size& bounds) {
-    SetBaseProperties(layer, transform, position, bounds);
-
-    layer->SetContentBounds(layer->bounds());
+    layer->SetTransform(transform);
+    layer->SetPosition(position);
+    layer->SetBounds(bounds);
   }
 
   void SetReplica(LayerImpl* owning_layer, scoped_ptr<LayerImpl> layer) {
@@ -321,8 +312,8 @@
   scoped_ptr<LayerImpl> root_;
   scoped_ptr<RenderSurfaceLayerList> render_surface_layer_list_;
   LayerImplList render_surface_layer_list_impl_;
-  LayerIterator<LayerImpl> layer_iterator_begin_;
-  LayerIterator<LayerImpl> layer_iterator_;
+  LayerIterator layer_iterator_begin_;
+  LayerIterator layer_iterator_;
   LayerList replica_layers_;
   LayerList mask_layers_;
   int next_layer_impl_id_;
@@ -653,7 +644,7 @@
     TestOcclusionTrackerWithClip occlusion(gfx::Rect(0, 0, 1000, 1000));
 
     gfx::Rect clipped_layer_in_child = MathUtil::MapEnclosingClippedRect(
-        layer_transform, layer->visible_content_rect());
+        layer_transform, layer->visible_layer_rect());
 
     this->VisitLayer(layer, &occlusion);
     this->EnterContributingSurface(child, &occlusion);
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 91816b9c..848cba3 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -152,8 +152,6 @@
   {
     DebugScopedSetMainThreadBlocked main_thread_blocked(this);
     DebugScopedSetImplThread impl(this);
-    layer_tree_host_->DeleteContentsTexturesOnImplThread(
-        layer_tree_host_impl_->resource_provider());
     success = layer_tree_host_impl_->InitializeRenderer(output_surface.Pass());
   }
 
@@ -242,16 +240,6 @@
         blocking_main_thread_task_runner()));
 
     layer_tree_host_impl_->BeginCommit();
-
-    if (PrioritizedResourceManager* contents_texture_manager =
-        layer_tree_host_->contents_texture_manager()) {
-      // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
-      // is fixed.
-      tracked_objects::ScopedTracker tracking_profile3(
-          FROM_HERE_WITH_EXPLICIT_FUNCTION(
-              "461509 SingleThreadProxy::DoCommit3"));
-      contents_texture_manager->PushTexturePrioritiesToBackings();
-    }
     layer_tree_host_->BeginCommitOnImplThread(layer_tree_host_impl_.get());
 
     // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
@@ -406,8 +394,6 @@
 
     BlockingTaskRunner::CapturePostTasks blocked(
         blocking_main_thread_task_runner());
-    layer_tree_host_->DeleteContentsTexturesOnImplThread(
-        layer_tree_host_impl_->resource_provider());
     scheduler_on_impl_thread_ = nullptr;
     layer_tree_host_impl_ = nullptr;
   }
@@ -483,23 +469,6 @@
   layer_tree_host_->SetAnimationEvents(events.Pass());
 }
 
-bool SingleThreadProxy::ReduceContentsTextureMemoryOnImplThread(
-    size_t limit_bytes,
-    int priority_cutoff) {
-  DCHECK(IsImplThread());
-  PrioritizedResourceManager* contents_texture_manager =
-      layer_tree_host_->contents_texture_manager();
-
-  ResourceProvider* resource_provider =
-      layer_tree_host_impl_->resource_provider();
-
-  if (!contents_texture_manager || !resource_provider)
-    return false;
-
-  return contents_texture_manager->ReduceMemoryOnImplThread(
-      limit_bytes, priority_cutoff, resource_provider);
-}
-
 bool SingleThreadProxy::IsInsideDraw() { return inside_draw_; }
 
 void SingleThreadProxy::DidActivateSyncTree() {
@@ -889,22 +858,13 @@
   layer_tree_host_->AnimateLayers(begin_frame_args.frame_time);
   layer_tree_host_->Layout();
 
-  if (PrioritizedResourceManager* contents_texture_manager =
-          layer_tree_host_->contents_texture_manager()) {
-    contents_texture_manager->UnlinkAndClearEvictedBackings();
-    contents_texture_manager->SetMaxMemoryLimitBytes(
-        layer_tree_host_impl_->memory_allocation_limit_bytes());
-    contents_texture_manager->SetExternalPriorityCutoff(
-        layer_tree_host_impl_->memory_allocation_priority_cutoff());
-  }
-
   DCHECK(!queue_for_commit_);
   queue_for_commit_ = make_scoped_ptr(new ResourceUpdateQueue);
 
   // New commits requested inside UpdateLayers should be respected.
   commit_requested_ = false;
 
-  layer_tree_host_->UpdateLayers(queue_for_commit_.get());
+  layer_tree_host_->UpdateLayers();
 
   timing_history_.DidBeginMainFrame();
 
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index 8cb5f665..46762a8 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -103,8 +103,6 @@
   void SetVideoNeedsBeginFrames(bool needs_begin_frames) override;
   void PostAnimationEventsToMainThreadOnImplThread(
       scoped_ptr<AnimationEventsVector> events) override;
-  bool ReduceContentsTextureMemoryOnImplThread(size_t limit_bytes,
-                                               int priority_cutoff) override;
   bool IsInsideDraw() override;
   void RenewTreePriority() override {}
   void PostDelayedAnimationTaskOnImplThread(const base::Closure& task,
diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc
index 94e8d89..8ce9c37 100644
--- a/cc/trees/thread_proxy.cc
+++ b/cc/trees/thread_proxy.cc
@@ -97,18 +97,12 @@
 
 ThreadProxy::MainThreadOrBlockedMainThread::~MainThreadOrBlockedMainThread() {}
 
-PrioritizedResourceManager*
-ThreadProxy::MainThreadOrBlockedMainThread::contents_texture_manager() {
-  return layer_tree_host->contents_texture_manager();
-}
-
 ThreadProxy::CompositorThreadOnly::CompositorThreadOnly(
     ThreadProxy* proxy,
     int layer_tree_host_id,
     RenderingStatsInstrumentation* rendering_stats_instrumentation,
     scoped_ptr<BeginFrameSource> external_begin_frame_source)
     : layer_tree_host_id(layer_tree_host_id),
-      contents_texture_manager(NULL),
       commit_completion_event(NULL),
       completion_event_for_commit_held_on_tree_activation(NULL),
       next_frame_is_newly_committed_frame(false),
@@ -212,22 +206,6 @@
   TRACE_EVENT0("cc", "ThreadProxy::DidLoseOutputSurface");
   DCHECK(IsMainThread());
   layer_tree_host()->DidLoseOutputSurface();
-
-  {
-    DebugScopedSetMainThreadBlocked main_thread_blocked(this);
-
-    // Return lost resources to their owners immediately.
-    BlockingTaskRunner::CapturePostTasks blocked(
-        blocking_main_thread_task_runner());
-
-    CompletionEvent completion;
-    Proxy::ImplThreadTaskRunner()->PostTask(
-        FROM_HERE,
-        base::Bind(&ThreadProxy::DeleteContentsTexturesOnImplThread,
-                   impl_thread_weak_ptr_,
-                   &completion));
-    completion.Wait();
-  }
 }
 
 void ThreadProxy::RequestNewOutputSurface() {
@@ -421,32 +399,6 @@
                  base::Passed(&events)));
 }
 
-bool ThreadProxy::ReduceContentsTextureMemoryOnImplThread(size_t limit_bytes,
-                                                          int priority_cutoff) {
-  DCHECK(IsImplThread());
-
-  if (!impl().contents_texture_manager)
-    return false;
-  if (!impl().layer_tree_host_impl->resource_provider())
-    return false;
-
-  bool reduce_result =
-      impl().contents_texture_manager->ReduceMemoryOnImplThread(
-          limit_bytes,
-          priority_cutoff,
-          impl().layer_tree_host_impl->resource_provider());
-  if (!reduce_result)
-    return false;
-
-  // The texture upload queue may reference textures that were just purged,
-  // clear them from the queue.
-  if (impl().current_resource_update_controller) {
-    impl()
-        .current_resource_update_controller->DiscardUploadsToEvictedResources();
-  }
-  return true;
-}
-
 bool ThreadProxy::IsInsideDraw() { return impl().inside_draw; }
 
 void ThreadProxy::SetNeedsRedraw(const gfx::Rect& damage_rect) {
@@ -796,17 +748,6 @@
   layer_tree_host()->AnimateLayers(
       begin_main_frame_state->begin_frame_args.frame_time);
 
-  // Unlink any backings that the impl thread has evicted, so that we know to
-  // re-paint them in UpdateLayers.
-  if (blocked_main().contents_texture_manager()) {
-    blocked_main().contents_texture_manager()->UnlinkAndClearEvictedBackings();
-
-    blocked_main().contents_texture_manager()->SetMaxMemoryLimitBytes(
-        begin_main_frame_state->memory_allocation_limit_bytes);
-    blocked_main().contents_texture_manager()->SetExternalPriorityCutoff(
-        begin_main_frame_state->memory_allocation_priority_cutoff);
-  }
-
   // Recreate all UI resources if there were evicted UI resources when the impl
   // thread initiated the commit.
   if (begin_main_frame_state->evicted_ui_resources)
@@ -827,7 +768,7 @@
   scoped_ptr<ResourceUpdateQueue> queue =
       make_scoped_ptr(new ResourceUpdateQueue);
 
-  bool updated = layer_tree_host()->UpdateLayers(queue.get());
+  bool updated = layer_tree_host()->UpdateLayers();
 
   layer_tree_host()->WillCommit();
   devtools_instrumentation::ScopedCommitTrace commit_task(
@@ -916,28 +857,6 @@
 
   scoped_ptr<ResourceUpdateQueue> queue(raw_queue);
 
-  if (impl().contents_texture_manager) {
-    DCHECK_EQ(impl().contents_texture_manager,
-              blocked_main().contents_texture_manager());
-  } else {
-    // Cache this pointer that was created on the main thread side to avoid a
-    // data race between creating it and using it on the compositor thread.
-    impl().contents_texture_manager = blocked_main().contents_texture_manager();
-  }
-
-  if (impl().contents_texture_manager) {
-    if (impl().contents_texture_manager->LinkedEvictedBackingsExist()) {
-      // Clear any uploads we were making to textures linked to evicted
-      // resources
-      queue->ClearUploadsToEvictedResources();
-      // Some textures in the layer tree are invalid. Kick off another commit
-      // to fill them again.
-      SetNeedsCommitOnImplThread();
-    }
-
-    impl().contents_texture_manager->PushTexturePrioritiesToBackings();
-  }
-
   impl().commit_completion_event = completion;
   impl().current_resource_update_controller = ResourceUpdateController::Create(
       this,
@@ -1213,16 +1132,6 @@
   completion->Signal();
 }
 
-void ThreadProxy::DeleteContentsTexturesOnImplThread(
-    CompletionEvent* completion) {
-  TRACE_EVENT0("cc", "ThreadProxy::DeleteContentsTexturesOnImplThread");
-  DCHECK(IsImplThread());
-  DCHECK(IsMainThreadBlocked());
-  layer_tree_host()->DeleteContentsTexturesOnImplThread(
-      impl().layer_tree_host_impl->resource_provider());
-  completion->Signal();
-}
-
 void ThreadProxy::InitializeOutputSurfaceOnImplThread(
     scoped_ptr<OutputSurface> output_surface) {
   TRACE_EVENT0("cc", "ThreadProxy::InitializeOutputSurfaceOnImplThread");
@@ -1263,8 +1172,6 @@
   TRACE_EVENT0("cc", "ThreadProxy::LayerTreeHostClosedOnImplThread");
   DCHECK(IsImplThread());
   DCHECK(IsMainThreadBlocked());
-  layer_tree_host()->DeleteContentsTexturesOnImplThread(
-      impl().layer_tree_host_impl->resource_provider());
   impl().current_resource_update_controller = nullptr;
   impl().scheduler = nullptr;
   impl().layer_tree_host_impl = nullptr;
@@ -1273,7 +1180,6 @@
   // holding while still on the compositor thread. This also ensures any
   // callbacks holding a ThreadProxy pointer are cancelled.
   impl().smoothness_priority_expiration_notifier.Shutdown();
-  impl().contents_texture_manager = NULL;
   completion->Signal();
 }
 
@@ -1339,10 +1245,9 @@
   if (impl().smoothness_priority_expiration_notifier.HasPendingNotification())
     priority = SMOOTHNESS_TAKES_PRIORITY;
 
-  // New content always takes priority when the active tree has
-  // evicted resources or there is an invalid viewport size.
-  if (impl().layer_tree_host_impl->active_tree()->ContentsTexturesPurged() ||
-      impl().layer_tree_host_impl->active_tree()->ViewportSizeInvalid() ||
+  // New content always takes priority when there is an invalid viewport size or
+  // ui resources have been evicted.
+  if (impl().layer_tree_host_impl->active_tree()->ViewportSizeInvalid() ||
       impl().layer_tree_host_impl->EvictedUIResourcesExist() ||
       impl().input_throttled_until_commit) {
     // Once we enter NEW_CONTENTS_TAKES_PRIORITY mode, visible tiles on active
diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h
index 48aa661..e8a791b9 100644
--- a/cc/trees/thread_proxy.h
+++ b/cc/trees/thread_proxy.h
@@ -88,8 +88,6 @@
     explicit MainThreadOrBlockedMainThread(LayerTreeHost* host);
     ~MainThreadOrBlockedMainThread();
 
-    PrioritizedResourceManager* contents_texture_manager();
-
     LayerTreeHost* layer_tree_host;
     bool commit_waits_for_activation;
     bool main_thread_inside_commit;
@@ -105,10 +103,6 @@
 
     const int layer_tree_host_id;
 
-    // Copy of the main thread side contents texture manager for work
-    // that needs to be done on the compositor thread.
-    PrioritizedResourceManager* contents_texture_manager;
-
     scoped_ptr<Scheduler> scheduler;
 
     // Set when the main thread is waiting on a
@@ -207,8 +201,6 @@
   void SetVideoNeedsBeginFrames(bool needs_begin_frames) override;
   void PostAnimationEventsToMainThreadOnImplThread(
       scoped_ptr<AnimationEventsVector> queue) override;
-  bool ReduceContentsTextureMemoryOnImplThread(size_t limit_bytes,
-                                               int priority_cutoff) override;
   bool IsInsideDraw() override;
   void RenewTreePriority() override;
   void PostDelayedAnimationTaskOnImplThread(const base::Closure& task,
diff --git a/chrome/VERSION b/chrome/VERSION
index ff052a5..3e4127d 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=45
 MINOR=0
-BUILD=2435
+BUILD=2437
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 9316bdc..52e65d50 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -751,7 +751,7 @@
 
 # GYP: //chrome/android/chrome_apk.gyp:chrome_apk_manifest
 jinja_template("chrome_public_apk_manifest") {
-  input = "java_staging/AndroidManifest.xml"
+  input = "java/AndroidManifest.xml"
   output = "$root_gen_dir/chrome_public_apk_manifest/AndroidManifest.xml"
   variables = jinja_variables
   variables += [
diff --git a/chrome/android/chrome_apk.gyp b/chrome/android/chrome_apk.gyp
index 239236d..87078c9 100644
--- a/chrome/android/chrome_apk.gyp
+++ b/chrome/android/chrome_apk.gyp
@@ -270,7 +270,7 @@
       'target_name': 'chrome_public_manifest',
       'type': 'none',
       'variables': {
-        'jinja_inputs': ['<(chrome_java_dir)/AndroidManifest.xml'],
+        'jinja_inputs': ['java/AndroidManifest.xml'],
         'jinja_output': '<(chrome_public_apk_manifest)',
         'jinja_variables': [
           'channel=<(android_channel)',
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
new file mode 100644
index 0000000..46a755f
--- /dev/null
+++ b/chrome/android/java/AndroidManifest.xml
@@ -0,0 +1,592 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<!--
+Note: This is a jinja2 template, processed at build time into the final manifest.
+
+Blocks denoted with { % block some_name % }foo{ % endblock % } can be overridden
+by a child template that "extends" this file.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="{{ manifest_package }}"
+    tools:ignore="MissingVersion">
+    <!-- android:versionCode and android:versionName is set through gyp. See build/common.gypi -->
+
+    <uses-sdk android:minSdkVersion="{{min_sdk_version}}" android:targetSdkVersion="{{target_sdk_version}}" />
+    <uses-feature android:glEsVersion="0x00020000" />
+
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+    <uses-permission android:name="android.permission.NFC"/>
+    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
+    <uses-permission android:name="android.permission.READ_SYNC_STATS"/>
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <uses-permission android:name="android.permission.USE_CREDENTIALS"/>
+    <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
+
+    <permission android:name="{{ manifest_package }}.permission.CHILD_SERVICE" android:protectionLevel="signature" />
+    <permission android:name="{{ manifest_package }}.permission.READ_WRITE_BOOKMARK_FOLDERS" android:protectionLevel="signatureOrSystem" />
+    <permission android:name="{{ manifest_package }}.TOS_ACKED" android:protectionLevel="signatureOrSystem" />
+    <!-- Only chrome can receive the messages and registration result -->
+    <permission android:name="{{ manifest_package }}.permission.C2D_MESSAGE"
+        android:protectionLevel="signature" />
+    <permission android:name="{{ manifest_package }}.permission.DEBUG"
+                android:label="Debug web pages"
+                android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
+                android:protectionLevel="signature" />
+
+    <uses-permission android:name="{{ manifest_package }}.permission.C2D_MESSAGE" />
+    <uses-permission android:name="{{ manifest_package }}.permission.READ_WRITE_BOOKMARK_FOLDERS" />
+    <uses-permission android:name="{{ manifest_package }}.TOS_ACKED" />
+
+    <uses-permission android:name="com.chrome.permission.DEVICE_EXTRAS" />
+    <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
+    <uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"/>
+    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
+    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
+
+    {% block extra_uses_permissions %}
+    {% endblock %}
+
+    <!-- We may use GPS but it's not required -->
+    <uses-feature android:name="android.hardware.location.gps" android:required="false" />
+    <uses-feature android:name="android.hardware.camera" android:required="false" />
+
+    <!--
+      android.permission.RECORD_AUDIO makes this implied, however we don't
+      require a microphone.
+    -->
+    <uses-feature android:name="android.hardware.microphone" android:required="false" />
+    <!--
+      The app is usable with keyboard/mouse. This feature is implicitly true for
+      all applications and needs to be disabled manually.
+    -->
+    <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
+
+    <!-- Set android:largeHeap to "true" to allow more than the default
+         Java heap limit (32Mb on Nexus S, 48Mb on Xoom). -->
+    <application android:name="{% block application_name %}org.chromium.chrome.browser.ChromeMobileApplication{% endblock %}"
+        android:icon="@mipmap/app_icon"
+        android:label="@string/app_name"
+        android:largeHeap="false"
+        android:allowBackup="false"
+        android:supportsRtl="true"
+        {% block extra_application_attributes %}{% endblock %}>
+
+        <!-- Samsung MultiWindow Support -->
+        <meta-data android:name="com.samsung.android.sdk.multiwindow.enable"
+            android:value="true" />
+        <meta-data android:name="com.samsung.android.sdk.multiwindow.multiinstance.enable"
+            android:value="true" />
+        <meta-data android:name="com.samsung.android.sdk.multiwindow.multiinstance.launchmode"
+            android:value="singleTask" />
+        <meta-data android:name="com.samsung.android.sdk.multiwindow.penwindow.enable"
+            android:value="true"/>
+
+        {% if channel in ['dev', 'canary', 'default'] %}
+        <meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
+        {% endif %}
+        {% if configuration_policy == '1' %}
+        <meta-data android:name="android.content.APP_RESTRICTIONS"
+            android:resource="@xml/app_restrictions"/>
+        {% endif %}
+
+        <!-- Note: Set android:hardwareAccelerated to "false" in the ".Main"
+             activity below to go into software, browser tiling mode.
+
+             Since this activity (shown in the launcher) and the application
+             (shown in Android's Settings/Apps list) share the same label, we
+             do not specify one here to allow it to inherit from the app. -->
+        <activity android:name="org.chromium.chrome.browser.document.ChromeLauncherActivity"
+            android:theme="@android:style/Theme.Translucent.NoTitleBar">
+        </activity>
+        <activity-alias android:name="com.google.android.apps.chrome.Main"
+            android:targetActivity="org.chromium.chrome.browser.document.ChromeLauncherActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <category android:name="android.intent.category.APP_BROWSER" />
+                <category android:name="android.intent.category.NOTIFICATION_PREFERENCES" />
+                {% if channel in ['dev', 'canary', 'default'] %}
+                <category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
+                {% endif %}
+            </intent-filter>
+            <!-- Matches the common case of intents with no MIME type.
+                 Make sure to keep in sync with the next filter.  -->
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                {% if channel in ['stable', 'default'] %}<data android:scheme="googlechrome" />{% endif %}
+                <data android:scheme="http" />
+                <data android:scheme="https" />
+                <data android:scheme="about" />
+                <data android:scheme="javascript" />
+            </intent-filter>
+            <!-- Same filter as above but with MIME types.  Intents that
+                 do not specify a MIME type won't match. -->
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                {% if channel in ['stable', 'default'] %}<data android:scheme="googlechrome" />{% endif %}
+                <data android:scheme="http" />
+                <data android:scheme="https" />
+                <data android:scheme="about" />
+                <data android:scheme="content" />
+                <data android:scheme="javascript" />
+                <data android:mimeType="text/html"/>
+                <data android:mimeType="text/plain"/>
+                <data android:mimeType="application/xhtml+xml"/>
+            </intent-filter>
+            <!-- MHTML support, used for snapshots -->
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="file" android:mimeType="multipart/related"/>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MEDIA_SEARCH" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.speech.action.VOICE_SEARCH_RESULTS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="http" />
+                <data android:scheme="https" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.SEARCH" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="com.sec.android.airview.HOVER" />
+            </intent-filter>
+            <meta-data android:name="android.app.searchable"
+                android:resource="@xml/searchable" />
+        </activity-alias>
+        <activity android:name="org.chromium.chrome.browser.document.DocumentActivity"
+            android:exported="false"
+            android:theme="@style/MainTheme"
+            android:windowSoftInputMode="adjustResize"
+            android:taskAffinity=""
+            android:persistableMode="persistAcrossReboots"
+            android:autoRemoveFromRecents="false"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc"
+            android:hardwareAccelerated="true">
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.document.IncognitoDocumentActivity"
+            android:icon="@mipmap/incognito_app_icon"
+            android:exported="false"
+            android:theme="@style/IncognitoTheme"
+            android:windowSoftInputMode="adjustResize"
+            android:taskAffinity=""
+            android:persistableMode="persistNever"
+            android:autoRemoveFromRecents="false"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc"
+            android:hardwareAccelerated="true">
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.customtabs.CustomTabActivity"
+            android:theme="@style/MainTheme"
+            android:exported="false"
+            android:windowSoftInputMode="adjustResize"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc"
+            android:hardwareAccelerated="true">
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.ChromeTabbedActivity"
+             android:theme="@style/MainTheme"
+             android:exported="false"
+             android:windowBackground="@drawable/window_background"
+             android:windowSoftInputMode="adjustResize"
+             android:launchMode="singleTask"
+             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc"
+             android:hardwareAccelerated="true">
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.multiwindow.MultiInstanceChromeTabbedActivity"
+            android:theme="@style/MainTheme"
+            android:exported="false"
+            android:windowBackground="@drawable/window_background"
+            android:windowSoftInputMode="adjustResize"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc"
+            android:hardwareAccelerated="true">
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.sync.ui.PassphraseActivity"
+            android:theme="@style/MainTheme"
+            android:autoRemoveFromRecents="true">
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.bookmark.ManageBookmarkActivity"
+            android:theme="@style/DialogWhenLargeHolo"
+            android:label="@string/save_bookmark"
+            android:windowSoftInputMode="stateHidden|adjustPan"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="chrome" android:host="editbookmark" />
+            </intent-filter>
+            <intent-filter>
+                <!-- This is sent by the BrowserProviderProxy as a result of
+                     calls to android.provider.Browser.saveBookmark(). -->
+                <action android:name="{{ manifest_package }}.ADDBOOKMARK" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.firstrun.FirstRunActivityStaging"
+            android:theme="@style/DialogWhenLarge"
+            android:label="@string/fre_label"
+            android:launchMode="singleTop"
+            android:windowSoftInputMode="stateHidden|adjustPan"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc">
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.preferences.PreferencesStaging"
+            android:theme="@style/PreferencesTheme"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc"
+            android:label="@string/preferences"
+            android:exported="false">
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.EmbedContentViewActivity"
+            android:theme="@style/ThemeWithActionBar"
+            android:hardwareAccelerated="true"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc">
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.prerender.PrerenderAPITestActivity"
+            android:theme="@style/MainTheme"
+            android:exported="true">
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkActivity"
+            android:theme="@style/EnhancedBookmarkDialogWhite"
+            android:windowSoftInputMode="stateHidden"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc" >
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkDetailActivity"
+            android:theme="@style/EnhancedBookmarkDialogWhite"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc" >
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkAddEditFolderActivity"
+            android:theme="@style/EnhancedBookmarkDialogWhite"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc" >
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkFolderSelectActivity"
+            android:theme="@style/EnhancedBookmarkDialogWhite"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc" >
+        </activity>
+        <activity android:name="org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkSigninActivity"
+            android:theme="@style/EnhancedBookmarkDialogWhite"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc" >
+        </activity>
+
+        <!--
+            Activities for webapps.
+            TODO(dfalcantara): Remove the aliases for the WebappActivities once we're pretty sure
+                               that users don't have any instances of the original Activity still
+                               running.
+        -->
+        <activity-alias android:name="com.google.android.apps.chrome.webapps.WebappManager"
+            android:targetActivity="org.chromium.chrome.browser.document.ChromeLauncherActivity">
+            <intent-filter>
+                <action android:name="com.google.android.apps.chrome.webapps.WebappManager.ACTION_START_WEBAPP" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity-alias>
+        <activity android:name="org.chromium.chrome.browser.webapps.WebappActivity"
+            android:theme="@style/MainTheme"
+            android:label="@string/webapp_activity_title"
+            android:documentLaunchMode="intoExisting"
+            android:persistableMode="persistNever"
+            android:hardwareAccelerated="true"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc">
+        </activity>
+        <activity-alias android:name="com.google.android.apps.chrome.webapps.WebappActivity"
+            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity"
+            android:label="@string/webapp_activity_title">
+        </activity-alias>
+        {% for i in range(10) %}
+        <activity android:name="org.chromium.chrome.browser.webapps.WebappActivity{{ i }}"
+            android:theme="@style/MainTheme"
+            android:icon="@mipmap/app_shortcut_icon"
+            android:label="@string/webapp_activity_title"
+            android:launchMode="singleTask"
+            android:windowSoftInputMode="adjustResize"
+            android:persistableMode="persistNever"
+            android:hardwareAccelerated="true"
+            android:taskAffinity="{{ manifest_package }}.webapps.WebappActivity{{ i }}"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc">
+        </activity>
+        <activity-alias android:name="com.google.android.apps.chrome.webapps.WebappActivity{{ i }}"
+            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity{{ i }}"
+            android:icon="@mipmap/app_shortcut_icon"
+            android:label="@string/webapp_activity_title">
+        </activity-alias>
+        {% endfor %}
+
+        <activity android:name="org.chromium.chrome.browser.media.remote.ExpandedControllerActivity"
+            android:theme="@style/MainTheme"
+            android:label="Chrome.ExpandedControllerActivity"
+            android:hardwareAccelerated="true"
+            android:launchMode="singleTask"
+            android:noHistory="true"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc"
+            android:excludeFromRecents="true">
+        </activity>
+
+        <activity android:name="org.chromium.chrome.browser.document.CipherKeyActivity"
+            android:launchMode="singleInstance"
+            android:theme="@android:style/Theme.Translucent.NoTitleBar"
+            android:excludeFromRecents="true">
+        </activity>
+
+        <!-- Providers for chrome data. -->
+        <provider android:name="org.chromium.chrome.browser.ChromeBrowserProviderStaging"
+            android:authorities="{{ manifest_package }}.ChromeBrowserProvider;{{ manifest_package }}.browser;{{ manifest_package }}"
+            android:exported="true">
+            <path-permission android:path="/bookmarks/search_suggest_query"
+                    android:readPermission="android.permission.GLOBAL_SEARCH" />
+        </provider>
+
+        <!-- Provider for FileProvider. -->
+        <provider android:name="org.chromium.chrome.browser.util.CompatibilityFileProvider"
+            android:authorities="{{ manifest_package }}.FileProvider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/file_paths" />
+        </provider>
+
+        <!-- Sync adapter for browser invalidation. -->
+        <service android:name="org.chromium.chrome.browser.invalidation.ChromeBrowserSyncAdapterService"
+            android:exported="false">
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter" />
+            </intent-filter>
+            <meta-data android:name="android.content.SyncAdapter"
+                       android:resource="@xml/syncadapter" />
+        </service>
+
+        <!-- Broadcast receiver that will be notified of account changes -->
+        <receiver android:name="org.chromium.chrome.browser.services.AccountsChangedReceiver">
+            <intent-filter>
+                <action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
+            </intent-filter>
+        </receiver>
+
+        <!-- Precache service. -->
+        <service android:name="org.chromium.chrome.browser.precache.PrecacheService"
+            android:exported="false" />
+        <receiver android:name="org.chromium.chrome.browser.precache.PrecacheServiceLauncher">
+            <intent-filter>
+                <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
+                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
+                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="org.chromium.chrome.browser.firstrun.ToSAckedReceiver"
+            android:permission="{{ manifest_package }}.TOS_ACKED">
+            <intent-filter>
+                <action android:name="{{ manifest_package }}.TOS_ACKED" />
+            </intent-filter>
+        </receiver>
+
+        <activity android:name="org.chromium.chrome.browser.bookmark.ShortcutActivity"
+            android:enabled="{{ 'true' if channel in ['stable', 'default'] else 'false' }}"
+            android:label="@string/bookmark_shortcut_name"
+            android:icon="@mipmap/bookmark_shortcut_icon"
+            android:theme="@style/ThemeWithActionBar"
+            android:hardwareAccelerated="true">
+
+            <intent-filter>
+                <action android:name="android.intent.action.CREATE_SHORTCUT" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <receiver android:name="com.google.android.apps.chrome.appwidget.bookmarks.BookmarkThumbnailWidgetProvider"
+            android:enabled="{{ 'true' if channel in ['stable', 'default'] else 'false' }}"
+            android:label="@string/bookmarks">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name=".BOOKMARK_APPWIDGET_UPDATE" />
+            </intent-filter>
+            <meta-data
+                android:name="android.appwidget.provider"
+                android:resource="@xml/bookmark_thumbnail_widget_info" />
+        </receiver>
+        <service android:name="org.chromium.chrome.browser.bookmarkswidget.BookmarkThumbnailWidgetService"
+            android:permission="android.permission.BIND_REMOTEVIEWS"
+            android:exported="false" />
+        <receiver android:name="org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProxy"
+            android:exported="false" />
+
+        <!-- Receiver for GCM messages. Rebroadcasts them locally for sync and cloudprint. -->
+        <receiver android:name="com.google.ipc.invalidation.external.client.contrib.MultiplexingGcmListener$GCMReceiver"
+            android:exported="true"
+            android:permission="com.google.android.c2dm.permission.SEND">
+            <intent-filter>
+                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
+                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
+               <category android:name="{{ manifest_package }}"/>
+            </intent-filter>
+        </receiver>
+        <service android:name="com.google.ipc.invalidation.external.client.contrib.MultiplexingGcmListener"
+            android:exported="false">
+            <meta-data android:name="sender_ids"
+                android:value="cloudprint.c2dm@gmail.com,ipc.invalidation@gmail.com"/>
+        </service>
+
+        <!-- Notification service for sync. -->
+        <meta-data android:name="ipc.invalidation.ticl.listener_service_class"
+            android:value="org.chromium.components.invalidation.InvalidationClientService"/>
+        <service android:name="org.chromium.components.invalidation.InvalidationClientService"
+            android:exported="false">
+            <intent-filter>
+              <action android:name="com.google.ipc.invalidation.AUTH_TOKEN_REQUEST"/>
+            </intent-filter>
+        </service>
+        <service android:name="com.google.ipc.invalidation.ticl.android2.TiclService"
+            android:exported="false"/>
+        <service android:name="com.google.ipc.invalidation.ticl.android2.channel.AndroidMessageSenderService"
+            android:exported="false"/>
+        <receiver android:name="com.google.ipc.invalidation.ticl.android2.AndroidInternalScheduler$AlarmReceiver"
+            android:exported="false"/>
+        <receiver android:name="com.google.ipc.invalidation.external.client2.contrib.AndroidListener$AlarmReceiver"
+            android:exported="false"/>
+
+        <!-- Notification service multiplexed GCM receiver -->
+        <service android:name="com.google.ipc.invalidation.ticl.android2.channel.AndroidMessageReceiverService"
+            android:exported="false"
+            android:enabled="true"/>
+        <receiver android:name="com.google.ipc.invalidation.ticl.android2.channel.AndroidMessageReceiverService$Receiver"
+            android:exported="false">
+            <intent-filter>
+                <action android:name="com.google.ipc.invalidation.gcmmplex.EVENT" />
+            </intent-filter>
+        </receiver>
+
+        <!-- GCMDriver multiplexed GCM receiver -->
+        <service android:name="org.chromium.chrome.browser.services.gcm.GCMListener"
+            android:exported="false"/>
+        <receiver android:name="org.chromium.chrome.browser.services.gcm.GCMListener$Receiver"
+            android:exported="false">
+            <intent-filter>
+                <action android:name="com.google.ipc.invalidation.gcmmplex.EVENT" />
+            </intent-filter>
+        </receiver>
+
+        <!-- Android Notification service listener -->
+        <service android:name="org.chromium.chrome.browser.notifications.NotificationService"
+            android:exported="false"/>
+        <receiver android:name="org.chromium.chrome.browser.notifications.NotificationService$Receiver"
+            android:exported="false">
+            <intent-filter>
+                <action android:name="org.chromium.chrome.browser.notifications.CLICK_NOTIFICATION" />
+                <action android:name="org.chromium.chrome.browser.notifications.CLOSE_NOTIFICATION" />
+            </intent-filter>
+        </receiver>
+
+        <!-- Service Worker Background Sync service listener -->
+        <service android:name="org.chromium.content.browser.BackgroundSyncLauncherService"
+            android:exported="false" />
+        <receiver android:name="org.chromium.content.browser.BackgroundSyncLauncherService$Receiver">
+            <intent-filter>
+                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+            </intent-filter>
+        </receiver>
+
+        <service android:name="org.chromium.chrome.browser.prerender.ChromePrerenderService"
+            android:exported="true"
+            tools:ignore="ExportedService" />
+        <service android:name="org.chromium.chrome.browser.customtabs.CustomTabsConnectionService"
+             android:exported="{{ 'true' if channel in ['dev', 'canary', 'default'] else 'false' }}"
+             tools:ignore="ExportedService">
+            <intent-filter>
+              <action android:name="android.intent.action.MAIN" />
+              <category android:name="android.intent.category.CUSTOM_TABS" />
+            </intent-filter>
+        </service>
+        <service android:name="org.chromium.chrome.browser.crash.MinidumpUploadService"
+            android:exported="false"/>
+
+        <service android:name="org.chromium.chrome.browser.omaha.OmahaClient"
+            android:exported="false"/>
+
+        <!-- The following service entries exist in order to allow us to
+             start more than one sandboxed process. -->
+
+        <!-- NOTE: If you change the value of "android:process" for the below services,
+             you also need to update kHelperProcessExecutableName in chrome_constants.cc. -->
+        {% set num_sandboxed_services = 20 %}
+        <meta-data android:name="org.chromium.content.browser.NUM_SANDBOXED_SERVICES"
+            android:value="{{ num_sandboxed_services }}"/>
+        {% for i in range(num_sandboxed_services) %}
+        <service android:name="org.chromium.content.app.SandboxedProcessService{{ i }}"
+            android:process=":sandboxed_process{{ i }}"
+            android:permission="{{ manifest_package }}.permission.CHILD_SERVICE"
+            android:isolatedProcess="true"
+            android:exported="false" />
+        {% endfor %}
+
+        {% set num_privileged_services = 3 %}
+        <meta-data android:name="org.chromium.content.browser.NUM_PRIVILEGED_SERVICES"
+            android:value="{{ num_privileged_services }}"/>
+        {% for i in range(num_privileged_services) %}
+        <service android:name="org.chromium.content.app.PrivilegedProcessService{{ i }}"
+            android:process=":privileged_process{{ i }}"
+            android:permission="{{ manifest_package }}.permission.CHILD_SERVICE"
+            android:isolatedProcess="false"
+            android:exported="false" />
+        {% endfor %}
+
+        <receiver android:name="org.chromium.chrome.browser.download.OpenDownloadReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"/>
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="org.chromium.base.PowerStatusReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
+                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
+            </intent-filter>
+        </receiver>
+
+        <service android:name="org.chromium.chrome.browser.media.remote.NotificationTransportControl$ListenerService" />
+
+        <!--  Receiver for lock screen input when casting -->
+        <receiver android:name="org.chromium.chrome.browser.media.remote.LockScreenTransportControl$MediaButtonIntentReceiver"/>
+
+        <service android:name="org.chromium.chrome.browser.media.MediaNotificationService"
+            android:exported="false"/>
+
+        <meta-data android:name="com.google.android.gms.version"
+            android:value="@integer/google_play_services_version" />
+
+        <meta-data android:name="org.chromium.content.browser.SMART_CLIP_PROVIDER"
+            android:value="org.chromium.content.browser.SmartClipProvider"/>
+        <meta-data android:name="org.chromium.components.service_tab_launcher.SERVICE_TAB_LAUNCHER"
+                   android:value="org.chromium.chrome.browser.ChromeServiceTabLauncher" />
+
+        {% block extra_application_definitions %}
+        {% endblock %}
+    </application>
+</manifest>
diff --git a/chrome/android/java/res/layout/website_settings_permission_row.xml b/chrome/android/java/res/layout/website_settings_permission_row.xml
index 868a9a7..4205e20 100644
--- a/chrome/android/java/res/layout/website_settings_permission_row.xml
+++ b/chrome/android/java/res/layout/website_settings_permission_row.xml
@@ -4,6 +4,7 @@
      found in the LICENSE file. -->
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/website_settings_permission_row"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:paddingTop="12dp" >
@@ -22,4 +23,16 @@
         android:layout_toEndOf="@id/website_settings_permission_icon"
         android:textColor="@color/website_settings_popup_text"
         android:textSize="14sp" />
+
+    <TextView
+        android:id="@+id/website_settings_permission_unavailable_message"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/website_settings_permission_status"
+        android:layout_toEndOf="@id/website_settings_permission_icon"
+        android:lineSpacingExtra="6dp"
+        android:text="@string/page_info_android_permission_blocked"
+        android:textColor="@color/website_settings_popup_text_link"
+        android:textSize="14sp"
+        android:visibility="gone" />
 </RelativeLayout>
diff --git a/chrome/android/java/res/menu/chrome_context_menu.xml b/chrome/android/java/res/menu/chrome_context_menu.xml
index cea196a3..f4c108ae 100644
--- a/chrome/android/java/res/menu/chrome_context_menu.xml
+++ b/chrome/android/java/res/menu/chrome_context_menu.xml
@@ -6,6 +6,8 @@
 -->
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
     <group android:id="@+id/contextmenu_group_anchor">
+        <item android:id="@+id/contextmenu_load_images"
+            android:title="@string/contextmenu_load_images"/>
         <item android:id="@+id/contextmenu_open_in_new_tab"
             android:title="@string/contextmenu_open_in_new_tab"/>
         <item android:id="@+id/contextmenu_open_in_incognito_tab"
diff --git a/chrome/android/java/res/values/attrs.xml b/chrome/android/java/res/values/attrs.xml
index 3a46c71c..dff3030 100644
--- a/chrome/android/java/res/values/attrs.xml
+++ b/chrome/android/java/res/values/attrs.xml
@@ -38,6 +38,10 @@
         <attr name="floatLabelHint" format="reference|string" />
     </declare-styleable>
 
+    <declare-styleable name="EmptyAlertEditText">
+        <attr name="alertMessage" format="string" />
+    </declare-styleable>
+
     <declare-styleable name="ButtonCompat">
         <attr name="buttonColor" format="reference|color"/>
     </declare-styleable>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BookmarksBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/BookmarksBridge.java
index 068a5e1..47b41dc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/BookmarksBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/BookmarksBridge.java
@@ -205,6 +205,7 @@
 
     /**
      * @return A BookmarkItem instance for the given BookmarkId.
+     *         <code>null</code> if it doesn't exist.
      */
     public BookmarkItem getBookmarkById(BookmarkId id) {
         assert mIsNativeBookmarkModelLoaded;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java
index 4c2459c..a0c71d5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java
@@ -78,8 +78,8 @@
             "enable-contextual-search-for-testing";
 
     /** Enable new Website Settings UI, which does not have controls for editing settings */
-    public static final String ENABLE_READ_ONLY_WEBSITE_SETTINGS_POPUP =
-            "enable-read-only-website-settings-popup";
+    public static final String DISABLE_READ_ONLY_WEBSITE_SETTINGS_POPUP =
+            "disable-read-only-website-settings-popup";
 
     // How many thumbnails should we allow in the cache (per tab stack)?
     public static final String THUMBNAILS = "thumbnails";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
index b6fcc26..0909a23b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -14,6 +14,7 @@
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.provider.Browser;
 import android.provider.MediaStore;
 import android.speech.RecognizerResultsIntent;
@@ -112,6 +113,11 @@
     public static final String EXTRA_REFERRER_ID = "org.chromium.chrome.browser.referrer_id";
 
     /**
+     * Key to associate a timestamp with an intent.
+     */
+    private static final String EXTRA_TIMESTAMP_MS = "org.chromium.chrome.browser.timestamp";
+
+    /**
      * Fake ComponentName used in constructing TRUSTED_APPLICATION_CODE_EXTRA.
      */
     private static ComponentName sFakeComponentName = null;
@@ -512,6 +518,23 @@
     }
 
     /**
+     * Adds a timestamp to an intent, as returned by {@link SystemClock#elapsedRealtime()}.
+     *
+     * To track page load time, this needs to be called as close as possible to
+     * the entry point (in {@link Activity#onCreate()} for instance).
+     */
+    public static void addTimestampToIntent(Intent intent) {
+        intent.putExtra(EXTRA_TIMESTAMP_MS, SystemClock.elapsedRealtime());
+    }
+
+    /**
+     * @return the timestamp associated with an intent, or -1.
+     */
+    public static long getTimestampFromIntent(Intent intent) {
+        return intent.getLongExtra(EXTRA_TIMESTAMP_MS, -1);
+    }
+
+    /**
      * Returns true if the app should ignore a given intent.
      *
      * @param context Android Context.
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 fa2d798..9aaeba5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/Tab.java
@@ -347,6 +347,11 @@
         }
 
         @Override
+        public void onReloadIgnoringCache() {
+            reloadIgnoringCache();
+        }
+
+        @Override
         public void onLoadOriginalImage() {
             if (mNativeTabAndroid != 0) nativeLoadOriginalImage(mNativeTabAndroid);
         }
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 77eab819..b506784 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
@@ -12,9 +12,13 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
 import android.os.Bundle;
+import android.os.Process;
+import android.provider.Settings;
 import android.support.v7.widget.AppCompatTextView;
 import android.text.Layout;
 import android.text.Spannable;
@@ -552,6 +556,26 @@
         return messageBuilder;
     }
 
+    private String getAndroidPermissionForContentSetting(int contentSettingType) {
+        switch(contentSettingType) {
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_GEOLOCATION:
+                return android.Manifest.permission.ACCESS_FINE_LOCATION;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
+                return android.Manifest.permission.RECORD_AUDIO;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
+                return android.Manifest.permission.CAMERA;
+            default:
+                return null;
+        }
+    }
+
+    private boolean hasAndroidPermission(int contentSettingType) {
+        String androidPermission = getAndroidPermissionForContentSetting(contentSettingType);
+        return androidPermission == null
+                || (mContext.checkPermission(androidPermission, Process.myPid(), Process.myUid())
+                           == PackageManager.PERMISSION_GRANTED);
+    }
+
     /**
      * Adds a new row for the given permission.
      *
@@ -619,6 +643,18 @@
                 R.id.website_settings_permission_icon);
         permissionIcon.setImageResource(getImageResourceForPermission(type));
 
+        if (!hasAndroidPermission(type) && currentSetting == ContentSetting.ALLOW) {
+            View permissionUnavailable = permissionRow.findViewById(
+                    R.id.website_settings_permission_unavailable_message);
+            permissionUnavailable.setVisibility(View.VISIBLE);
+
+            permissionIcon.setImageResource(R.drawable.deprecation_warning);
+            permissionIcon.setColorFilter(
+                    mContext.getResources().getColor(R.color.website_settings_popup_text_link));
+
+            permissionRow.setOnClickListener(this);
+        }
+
         TextView permissionStatus = (TextView) permissionRow.findViewById(
                 R.id.website_settings_permission_status);
         SpannableStringBuilder builder = new SpannableStringBuilder();
@@ -741,6 +777,16 @@
                     }
                 }
             });
+        } else if (view.getId() == R.id.website_settings_permission_row) {
+            runAfterDismiss(new Runnable() {
+                @Override
+                public void run() {
+                    Intent settingsIntent =
+                            new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+                    settingsIntent.setData(Uri.parse("package:" + mContext.getPackageName()));
+                    mContext.startActivity(settingsIntent);
+                }
+            });
         }
     }
 
@@ -842,8 +888,8 @@
     }
 
     private static boolean enableReadOnlyPopup() {
-        return CommandLine.getInstance().hasSwitch(
-                ChromeSwitches.ENABLE_READ_ONLY_WEBSITE_SETTINGS_POPUP);
+        return !CommandLine.getInstance().hasSwitch(
+                ChromeSwitches.DISABLE_READ_ONLY_WEBSITE_SETTINGS_POPUP);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelStateHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelStateHandler.java
index 55d10ee4..517a007 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelStateHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelStateHandler.java
@@ -113,6 +113,9 @@
         boolean isFirstExitFromMaximized = fromState == PanelState.MAXIMIZED && !mHasExitedMaximized
                 && !isSameState;
         boolean isFirstSearchView = isFirstExitFromPeeking && toState != PanelState.CLOSED;
+        // This variable is needed for logging and gets reset in an isStartingSearch block below,
+        // so a local copy is created before the reset.
+        boolean isSearchPanelFullyPreloaded = mIsSearchPanelFullyPreloaded;
 
         if (isEndingSearch) {
             if (!mDidSearchInvolvePromo) {
@@ -178,6 +181,9 @@
         }
 
         if (isEndingSearch) {
+            if (mHasMaximized || mHasExpanded) {
+                ContextualSearchUma.logSerpLoadedOnClose(isSearchPanelFullyPreloaded);
+            }
             mDidSearchInvolvePromo = false;
             mWasSearchContentViewSeen = false;
             mHasExpanded = false;
@@ -248,10 +254,10 @@
             assert mSearchViewStartTimeNs != 0;
             long durationMs = (System.nanoTime() - mSearchViewStartTimeNs) / 1000000;
             logSearchPanelLoadDuration(wasPrefetch, durationMs);
-        } else {
-            // Not yet opened, wait till an open to log.
-            mIsSearchPanelFullyPreloaded = true;
         }
+
+        // Not yet opened, wait till an open to log.
+        mIsSearchPanelFullyPreloaded = true;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItemDelegate.java
index d706104..fb15d293 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItemDelegate.java
@@ -63,6 +63,11 @@
     void onOpenImageInNewTab(String url, Referrer referrer);
 
     /**
+     * Called when the page should be reloaded ignoring the cache.
+     */
+    void onReloadIgnoringCache();
+
+    /**
      * Called when the original image should be loaded.
      */
     void onLoadOriginalImage();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
index fada09a..82e96a4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
@@ -73,6 +73,20 @@
         menu.findItem(R.id.contextmenu_save_link_as).setVisible(
                 UrlUtilities.isDownloadableScheme(params.getLinkUrl()));
 
+        if (params.imageWasFetchedLoFi()
+                || !DataReductionProxySettings.getInstance().wasLoFiModeActiveOnMainFrame()
+                || !DataReductionProxySettings.getInstance().canUseDataReductionProxy(
+                        params.getPageUrl())) {
+            menu.findItem(R.id.contextmenu_load_images).setVisible(false);
+        } else {
+            // Links can have images as backgrounds that aren't recognized here as images. CSS
+            // properties can also prevent an image underlying a link from being clickable.
+            // When Lo-Fi is active, provide the user with a "Load images" option on links
+            // to get the images in these cases.
+            DataReductionProxyUma.dataReductionProxyLoFiUIAction(
+                    DataReductionProxyUma.ACTION_LOAD_IMAGES_CONTEXT_MENU_SHOWN);
+        }
+
         if (params.isVideo()) {
             menu.findItem(R.id.contextmenu_save_video).setVisible(
                     UrlUtilities.isDownloadableScheme(params.getSrcUrl()));
@@ -131,6 +145,10 @@
         } else if (itemId == R.id.contextmenu_open_image_in_new_tab
                 || itemId == R.id.contextmenu_open_original_image_in_new_tab) {
             mDelegate.onOpenImageInNewTab(params.getSrcUrl(), params.getReferrer());
+        } else if (itemId == R.id.contextmenu_load_images) {
+            DataReductionProxyUma.dataReductionProxyLoFiUIAction(
+                    DataReductionProxyUma.ACTION_LOAD_IMAGES_CONTEXT_MENU_CLICKED);
+            mDelegate.onReloadIgnoringCache();
         } else if (itemId == R.id.contextmenu_load_original_image) {
             DataReductionProxyUma.dataReductionProxyLoFiUIAction(
                     DataReductionProxyUma.ACTION_LOAD_IMAGE_CONTEXT_MENU_CLICKED);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/EmptyChromeContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/EmptyChromeContextMenuItemDelegate.java
index 334826d..39afb9d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/EmptyChromeContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/EmptyChromeContextMenuItemDelegate.java
@@ -36,6 +36,10 @@
     }
 
     @Override
+    public void onReloadIgnoringCache() {
+    }
+
+    @Override
     public void onLoadOriginalImage() {
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java
index cf0867b..e46c963e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java
@@ -170,6 +170,11 @@
     private static final int RESOLVED_MULTI_WORD = 1;
     private static final int RESOLVED_BOUNDARY = 2;
 
+    // Constants used to log UMA "enum" histograms for paritally / fully loaded.
+    private static final int PARTIALLY_LOADED = 0;
+    private static final int FULLY_LOADED = 1;
+    private static final int LOADED_BOUNDARY = 2;
+
 
     /**
      * Key used in maps from {state, reason} to state entry (exit) logging code.
@@ -640,6 +645,15 @@
     }
 
     /**
+     * Logs whether the SERP was fully loaded when an opened panel was closed.
+     * @param fullyLoaded Whether the SERP had finished loading before the panel was closed.
+     */
+    public static void logSerpLoadedOnClose(boolean fullyLoaded) {
+        RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchSerpLoadedOnClose",
+                fullyLoaded ? FULLY_LOADED : PARTIALLY_LOADED, LOADED_BOUNDARY);
+    }
+
+    /**
      * Logs how a state was entered for the first time within a Contextual Search.
      * @param fromState The state to transition from.
      * @param toState The state to transition to.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index ae8338f..636b53f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -540,9 +540,7 @@
                         mPendingAutoOpenDownloads.remove(downloadId);
                         if (OMADownloadHandler.OMA_DOWNLOAD_DESCRIPTOR_MIME.equalsIgnoreCase(
                                 info.getMimeType())) {
-                            mOMADownloadHandler.handleOMADownload(
-                                    info, downloadId);
-                            manager.remove(downloadId);
+                            mOMADownloadHandler.handleOMADownload(info, downloadId);
                             break;
                         }
                         Uri uri = manager.getUriForDownloadedFile(downloadId);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
index bbe3b439..fbb736ba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
@@ -225,14 +225,16 @@
                     (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
             try {
                 ParcelFileDescriptor fd = manager.openDownloadedFile(mDownloadId);
-                if (fd == null) return null;
-                omaInfo = parseDownloadDescriptor(new FileInputStream(fd.getFileDescriptor()));
-                fd.close();
+                if (fd != null) {
+                    omaInfo = parseDownloadDescriptor(new FileInputStream(fd.getFileDescriptor()));
+                    fd.close();
+                }
             } catch (FileNotFoundException e) {
                 Log.w(TAG, "File not found.", e);
             } catch (IOException e) {
                 Log.w(TAG, "Cannot read file.", e);
             }
+            manager.remove(mDownloadId);
             return omaInfo;
         }
 
@@ -386,8 +388,7 @@
                 if (which == AlertDialog.BUTTON_POSITIVE) {
                     downloadOMAContent(downloadId, downloadInfo, omaInfo);
                 } else {
-                    sendInstallNotificationAndNextStep(
-                            omaInfo, downloadInfo, DOWNLOAD_STATUS_USER_CANCELLED);
+                    sendNotification(omaInfo, downloadInfo, DOWNLOAD_STATUS_USER_CANCELLED);
                 }
             }
         };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/bandwidth/DataReductionProxyUma.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/bandwidth/DataReductionProxyUma.java
index dc2d0f9..d591561 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/bandwidth/DataReductionProxyUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/bandwidth/DataReductionProxyUma.java
@@ -30,7 +30,9 @@
     public static final int ACTION_LOAD_IMAGE_CONTEXT_MENU_SHOWN = 2;
     public static final int ACTION_LOAD_IMAGE_CONTEXT_MENU_CLICKED = 3;
     public static final int ACTION_LOAD_IMAGE_CONTEXT_MENU_CLICKED_ON_PAGE = 4;
-    public static final int LOFI_ACTION_INDEX_BOUNDARY = 5;
+    public static final int ACTION_LOAD_IMAGES_CONTEXT_MENU_SHOWN = 5;
+    public static final int ACTION_LOAD_IMAGES_CONTEXT_MENU_CLICKED = 6;
+    public static final int LOFI_ACTION_INDEX_BOUNDARY = 7;
 
     /**
      * Record the DataReductionProxy.UIAction histogram.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/EmptyAlertEditText.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/EmptyAlertEditText.java
new file mode 100644
index 0000000..aa13cfd
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/EmptyAlertEditText.java
@@ -0,0 +1,69 @@
+// 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.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.v7.widget.AppCompatEditText;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.R;
+
+/**
+ * An EditText that shows an alert message when the content is empty.
+ */
+public class EmptyAlertEditText extends AppCompatEditText {
+
+    private String mAlertMessage;
+
+    /**
+     * Constructor for inflating from XML.
+     */
+    public EmptyAlertEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.EmptyAlertEditText);
+        int resId = a.getResourceId(R.styleable.EmptyAlertEditText_alertMessage, 0);
+        if (resId != 0) mAlertMessage = context.getResources().getString(resId);
+        a.recycle();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                if (s.length() != 0 && getError() != null) setError(null);
+            }
+        });
+    }
+
+    /**
+     * Sets the alert message to be shown when the text content is empty.
+     */
+    public void setAlertMessage(String message) {
+        mAlertMessage = message;
+    }
+
+    /**
+     * Checks whether the content is empty. If empty, an alert message will be shown.
+     * @return Whether the content is empty.
+     */
+    public boolean validate() {
+        if (getText().length() == 0) {
+            setError(mAlertMessage);
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index d586b56..84a60f9 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -1189,6 +1189,9 @@
       <message name="IDS_CONTEXTMENU_OPEN_ORIGINAL_IMAGE_IN_NEW_TAB" desc="Context sensitive menu item for opening/viewing the original version of the selected image in a new tab. [CHAR-LIMIT=30]">
         Open original image in new tab
       </message>
+      <message name="IDS_CONTEXTMENU_LOAD_IMAGES" desc="Context sensitive menu item for Data Saver low fidelity placeholder images that reloads the page with the original images. [CHAR-LIMIT=30]">
+        Load images
+      </message>
       <message name="IDS_CONTEXTMENU_LOAD_ORIGINAL_IMAGE" desc="Context sensitive menu item for Data Saver low fidelity placeholder images that loads the original version in place. [CHAR-LIMIT=30]">
         Load image
       </message>
@@ -1313,6 +1316,9 @@
       <message name="IDS_PAGE_INFO_PERMISSION_BLOCKED" desc="The label used in the Page Info dialog to describe a blocked permission. Eg: Location - Blocked">
         Blocked
       </message>
+      <message name="IDS_PAGE_INFO_ANDROID_PERMISSION_BLOCKED" desc="The label used in the Page Info dialog to indicate a permission has been blocked within Android settings">
+        Turned off in Android Settings
+      </message>
       <message name="IDS_PAGE_INFO_DETAILS_LINK" desc="The label of the link to open the detailed connection information popup from the page info bubble.">
         Details
       </message>
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeActivity.java
index 4723797b..f41f3fe 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -11,68 +11,127 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Looper;
 import android.os.MessageQueue;
+import android.os.Process;
 import android.os.StrictMode;
+import android.os.SystemClock;
 import android.preference.PreferenceManager;
+import android.support.v7.app.AlertDialog;
 import android.util.DisplayMetrics;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnPreDrawListener;
 import android.view.Window;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
 import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.BaseSwitches;
 import org.chromium.base.CommandLine;
+import org.chromium.base.Log;
+import org.chromium.base.TraceEvent;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.BookmarksBridge.BookmarkModelObserver;
 import org.chromium.chrome.browser.IntentHandler.IntentHandlerDelegate;
 import org.chromium.chrome.browser.IntentHandler.TabOpenType;
+import org.chromium.chrome.browser.appmenu.AppMenuHandler;
+import org.chromium.chrome.browser.bookmark.ManageBookmarkActivity;
+import org.chromium.chrome.browser.compositor.CompositorViewHolder;
+import org.chromium.chrome.browser.compositor.layouts.Layout;
+import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
+import org.chromium.chrome.browser.compositor.layouts.LayoutManagerDocument;
+import org.chromium.chrome.browser.compositor.layouts.SceneChangeObserver;
 import org.chromium.chrome.browser.compositor.layouts.content.ContentOffsetProvider;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
+import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
+import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager.ContextualSearchTabPromotionDelegate;
+import org.chromium.chrome.browser.customtabs.CustomTab;
+import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.device.DeviceClassManager;
-import org.chromium.chrome.browser.fullscreen.FullscreenManager;
+import org.chromium.chrome.browser.dom_distiller.DistilledPagePrefsView;
+import org.chromium.chrome.browser.dom_distiller.ReaderModeActivityDelegate;
+import org.chromium.chrome.browser.dom_distiller.ReaderModeManager;
+import org.chromium.chrome.browser.enhanced_bookmarks.EnhancedBookmarksModel;
+import org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkUtils;
+import org.chromium.chrome.browser.feedback.FeedbackCollector;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.gsa.ContextReporter;
 import org.chromium.chrome.browser.gsa.GSAServiceClient;
 import org.chromium.chrome.browser.gsa.GSAState;
+import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
 import org.chromium.chrome.browser.metrics.LaunchMetrics;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
+import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.nfc.BeamController;
 import org.chromium.chrome.browser.nfc.BeamProvider;
 import org.chromium.chrome.browser.omaha.OmahaClient;
 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
 import org.chromium.chrome.browser.policy.PolicyManager.PolicyChangeListener;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.preferences.PreferencesLauncher;
+import org.chromium.chrome.browser.printing.TabPrinter;
 import org.chromium.chrome.browser.share.ShareHelper;
+import org.chromium.chrome.browser.snackbar.LoFiBarPopupController;
+import org.chromium.chrome.browser.snackbar.SnackbarManager;
+import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarManageable;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.tabmodel.ChromeTabCreator;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModel;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.tabmodel.TabWindowManager;
 import org.chromium.chrome.browser.util.FeatureUtilities;
+import org.chromium.chrome.browser.webapps.AddToHomescreenDialog;
+import org.chromium.chrome.browser.widget.ControlContainer;
+import org.chromium.components.bookmarks.BookmarkId;
+import org.chromium.components.bookmarks.BookmarkType;
 import org.chromium.content.browser.ContentReadbackHandler;
+import org.chromium.content.browser.ContentReadbackHandler.GetBitmapCallback;
 import org.chromium.content.browser.ContentViewCore;
 import org.chromium.content.common.ContentSwitches;
+import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.readback_types.ReadbackResponse;
+import org.chromium.printing.PrintManagerDelegateImpl;
+import org.chromium.printing.PrintingController;
+import org.chromium.ui.base.ActivityWindowAndroid;
+import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.base.WindowAndroid;
 
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
 /**
- * The main Chrome activity.  This exposes extra methods that relate to the {@link TabModelSelector}
- * and lists of tabs.
+ * A {@link AsyncInitializationActivity} that builds and manages a {@link CompositorViewHolder}
+ * and associated classes.
  */
-public abstract class ChromeActivity extends AsyncInitializationActivity implements
-        TabCreatorManager, AccessibilityStateChangeListener, PolicyChangeListener {
+public abstract class ChromeActivity extends AsyncInitializationActivity
+        implements TabCreatorManager, AccessibilityStateChangeListener, PolicyChangeListener,
+        ContextualSearchTabPromotionDelegate, SnackbarManageable, SceneChangeObserver {
+    /**
+     * No control container to inflate during initialization.
+     */
+    private static final int NO_CONTROL_CONTAINER = -1;
 
     private static final String SNAPSHOT_DATABASE_REMOVED = "snapshot_database_removed";
     private static final String SNAPSHOT_DATABASE_NAME = "snapshots.db";
@@ -84,6 +143,7 @@
      * Timeout in ms for reading PartnerBrowserCustomizations provider.
      */
     private static final int PARTNER_BROWSER_CUSTOMIZATIONS_TIMEOUT_MS = 10000;
+    private static final String TAG = "cr.ChromeActivity";
 
     private TabModelSelector mTabModelSelector;
     private TabModelSelectorTabObserver mTabModelSelectorTabObserver;
@@ -109,30 +169,171 @@
     // Observes when sync becomes ready to create the mContextReporter.
     private ProfileSyncService.SyncStateChangedListener mSyncStateChangedListener;
 
+    private ActivityWindowAndroid mWindowAndroid;
+    private ChromeFullscreenManager mFullscreenManager;
+    private CompositorViewHolder mCompositorViewHolder;
+    private ContextualSearchManager mContextualSearchManager;
+    private ReaderModeActivityDelegate mReaderModeActivityDelegate;
+    private SnackbarManager mSnackbarManager;
+    private LoFiBarPopupController mLoFiBarPopupController;
+
+    // Time in ms that it took took us to inflate the initial layout
+    private long mInflateInitialLayoutDurationMs;
+
+    private OnPreDrawListener mFirstDrawListener;
+
+    private final Locale mCurrentLocale = Locale.getDefault();
+
+    @Override
+    public void preInflationStartup() {
+        super.preInflationStartup();
+        ApplicationInitialization.enableFullscreenFlags(
+                getResources(), this, getControlContainerHeightResource());
+        getWindow().setBackgroundDrawableResource(R.color.light_background_color);
+    }
+
     @SuppressLint("NewApi")
     @Override
-    protected void onDestroy() {
-        getChromeApplication().removePolicyChangeListener(this);
-        if (mTabContentManager != null) mTabContentManager.destroy();
-        if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy();
+    public void postInflationStartup() {
+        super.postInflationStartup();
+
+        mWindowAndroid = new ChromeWindow(this);
+        mWindowAndroid.restoreInstanceState(getSavedInstanceState());
+        mSnackbarManager = new SnackbarManager(findViewById(android.R.id.content));
+        mLoFiBarPopupController = new LoFiBarPopupController(this, getSnackbarManager());
+
+        // Low end device UI should be allowed only after a fresh install or when the data has
+        // been cleared. This must happen before anyone calls SysUtils.isLowEndDevice() or
+        // SysUtils.isLowEndDevice() will always return the wrong value.
+        // Temporarily allowing disk access. TODO: Fix. See http://crbug.com/473352
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            if (OmahaClient.isFreshInstallOrDataHasBeenCleared(this)) {
+                ChromePreferenceManager.getInstance(this).setAllowLowEndDeviceUi();
+            }
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+
+        if (!ChromePreferenceManager.getInstance(this).getAllowLowEndDeviceUi()) {
+            CommandLine.getInstance().appendSwitch(
+                    BaseSwitches.DISABLE_LOW_END_DEVICE_MODE);
+        }
 
         AccessibilityManager manager = (AccessibilityManager)
                 getBaseContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        manager.removeAccessibilityStateChangeListener(this);
+        manager.addAccessibilityStateChangeListener(this);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            manager.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+            mTouchExplorationStateChangeListener = new TouchExplorationStateChangeListener() {
+                @Override
+                public void onTouchExplorationStateChanged(boolean enabled) {
+                    checkAccessibility();
+                }
+            };
+            manager.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
         }
 
-        super.onDestroy();
+        // Make the activity listen to policy change events
+        getChromeApplication().addPolicyChangeListener(this);
+
+        // Set up the animation placeholder to be the SurfaceView. This disables the
+        // SurfaceView's 'hole' clipping during animations that are notified to the window.
+        mWindowAndroid.setAnimationPlaceholderView(mCompositorViewHolder.getSurfaceView());
+
+        // Inform the WindowAndroid of the keyboard accessory view.
+        mWindowAndroid.setKeyboardAccessoryView((ViewGroup) findViewById(R.id.keyboard_accessory));
+        final View controlContainer = findViewById(R.id.control_container);
+        if (controlContainer != null) {
+            mFirstDrawListener = new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    controlContainer.getViewTreeObserver()
+                            .removeOnPreDrawListener(mFirstDrawListener);
+                    mFirstDrawListener = null;
+                    onFirstDrawComplete();
+                    return true;
+                }
+            };
+            controlContainer.getViewTreeObserver().addOnPreDrawListener(mFirstDrawListener);
+        }
     }
 
     /**
-     * {@link TabModelSelector} no longer implements TabModel.  Use getTabModelSelector() or
-     * getCurrentTabModel() depending on your needs.
-     * @return The {@link TabModelSelector}, possibly null.
+     * This function builds the {@link CompositorViewHolder}.  Subclasses *must* call
+     * super.setContentView() before using {@link #getTabModelSelector()} or
+     * {@link #getCompositorViewHolder()}.
      */
-    public TabModelSelector getTabModelSelector() {
-        return mTabModelSelector;
+    @Override
+    protected final void setContentView() {
+        final long begin = SystemClock.elapsedRealtime();
+        TraceEvent.begin("onCreate->setContentView()");
+        if (WarmupManager.getInstance().hasBuiltViewHierarchy()) {
+            View placeHolderView = new View(this);
+            setContentView(placeHolderView);
+            ViewGroup contentParent = (ViewGroup) placeHolderView.getParent();
+            WarmupManager.getInstance().transferViewHierarchyTo(contentParent);
+            contentParent.removeView(placeHolderView);
+        } else {
+            setContentView(R.layout.main);
+            if (getControlContainerLayoutId() != NO_CONTROL_CONTAINER) {
+                ViewStub toolbarContainerStub =
+                        ((ViewStub) findViewById(R.id.control_container_stub));
+                toolbarContainerStub.setLayoutResource(getControlContainerLayoutId());
+                toolbarContainerStub.inflate();
+            }
+        }
+        TraceEvent.end("onCreate->setContentView()");
+        mInflateInitialLayoutDurationMs = SystemClock.elapsedRealtime() - begin;
+
+        // Set the status bar color to black by default. This is an optimization for
+        // Chrome not to draw under status and navigation bars when we use the default
+        // black status bar
+        ApiCompatibilityUtils.setStatusBarColor(getWindow(), Color.BLACK);
+
+        mCompositorViewHolder = (CompositorViewHolder) findViewById(R.id.compositor_view_holder);
+        mCompositorViewHolder.setRootView(getWindow().getDecorView().getRootView());
+    }
+
+    /**
+     * @return The resource id for the layout to use for {@link ControlContainer}. 0 by default.
+     */
+    protected int getControlContainerLayoutId() {
+        return NO_CONTROL_CONTAINER;
+    }
+
+    /**
+     * @return Whether contextual search is allowed for this activity or not.
+     */
+    protected boolean isContextualSearchAllowed() {
+        return true;
+    }
+
+    @Override
+    public void initializeState() {
+        super.initializeState();
+        IntentHandler.setTestIntentsEnabled(
+                CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_TEST_INTENTS));
+        mIntentHandler = new IntentHandler(createIntentHandlerDelegate(), getPackageName());
+    }
+
+    @Override
+    public void initializeCompositor() {
+        TraceEvent.begin("ChromeActivity:CompositorInitialization");
+        super.initializeCompositor();
+
+        setTabContentManager(new TabContentManager(this, getContentOffsetProvider(),
+                DeviceClassManager.enableSnapshots()));
+        mCompositorViewHolder.onNativeLibraryReady(mWindowAndroid, getTabContentManager());
+
+        if (isContextualSearchAllowed() && ContextualSearchFieldTrial.isEnabled(this)) {
+            mContextualSearchManager = new ContextualSearchManager(this, mWindowAndroid, this);
+        }
+
+        if (ReaderModeManager.isEnabled(this)) {
+            mReaderModeActivityDelegate = new ReaderModeActivityDelegate(this);
+        }
+
+        TraceEvent.end("ChromeActivity:CompositorInitialization");
     }
 
     /**
@@ -145,6 +346,39 @@
         if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy();
         mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(tabModelSelector) {
             @Override
+            public void onDidNavigateMainFrame(Tab tab, String url, String baseUrl,
+                    boolean isNavigationToDifferentPage, boolean isFragmentNavigation,
+                    int statusCode) {
+                if (!tab.isNativePage() && !tab.isIncognito()
+                        && DataReductionProxySettings.getInstance().wasLoFiModeActiveOnMainFrame()
+                        && DataReductionProxySettings.getInstance().canUseDataReductionProxy(
+                                url)) {
+                    if (tab.isHidden()) {
+                        TabObserver tabObserver = new EmptyTabObserver() {
+                            @Override
+                            public void onShown(Tab tab) {
+                                mLoFiBarPopupController.showLoFiBar(tab);
+                                tab.removeObserver(this);
+                            }
+                        };
+                        tab.addObserver(tabObserver);
+                        return;
+                    }
+                    mLoFiBarPopupController.showLoFiBar(tab);
+                }
+            }
+
+            @Override
+            public void onHidden(Tab tab) {
+                mLoFiBarPopupController.dismissLoFiBar();
+            }
+
+            @Override
+            public void onDestroyed(Tab tab) {
+                mLoFiBarPopupController.dismissLoFiBar();
+            }
+
+            @Override
             public void onLoadStopped(Tab tab) {
                 postDeferredStartupIfNeeded();
                 showUpdateInfoBarIfNecessary();
@@ -164,6 +398,456 @@
     }
 
     @Override
+    public void onStartWithNative() {
+        super.onStartWithNative();
+        getChromeApplication().onStartWithNative();
+        Tab tab = getActivityTab();
+        if (tab != null) tab.onActivityStart();
+        FeatureUtilities.setDocumentModeEnabled(FeatureUtilities.isDocumentMode(this));
+        WarmupManager.getInstance().clearWebContentsIfNecessary();
+
+        if (GSAState.getInstance(this).isGsaAvailable()) {
+            mGSAServiceClient = new GSAServiceClient(this);
+            mGSAServiceClient.connect();
+            createContextReporterIfNeeded();
+        } else {
+            ContextReporter.reportStatus(ContextReporter.STATUS_GSA_NOT_AVAILABLE);
+        }
+        mCompositorViewHolder.resetFlags();
+    }
+
+    private void createContextReporterIfNeeded() {
+        if (mContextReporter != null || getActivityTab() == null) return;
+
+        final ProfileSyncService syncService = ProfileSyncService.get(this);
+
+        if (syncService.isSyncingUrlsWithKeystorePassphrase()) {
+            mContextReporter = ((ChromeMobileApplication) getApplicationContext()).createGsaHelper()
+                    .getContextReporter(this);
+
+            if (mSyncStateChangedListener != null) {
+                syncService.removeSyncStateChangedListener(mSyncStateChangedListener);
+                mSyncStateChangedListener = null;
+            }
+
+            return;
+        } else {
+            ContextReporter.reportSyncStatus(syncService);
+        }
+
+        if (mSyncStateChangedListener == null) {
+            mSyncStateChangedListener = new ProfileSyncService.SyncStateChangedListener() {
+                @Override
+                public void syncStateChanged() {
+                    createContextReporterIfNeeded();
+                }
+            };
+            syncService.addSyncStateChangedListener(mSyncStateChangedListener);
+        }
+    }
+
+    @Override
+    public void onResumeWithNative() {
+        super.onResumeWithNative();
+        markSessionResume();
+
+        if (getActivityTab() != null) {
+            LaunchMetrics.commitLaunchMetrics(getActivityTab().getWebContents());
+        }
+        FeatureUtilities.setCustomTabVisible(isCustomTab());
+    }
+
+    @Override
+    public void onPauseWithNative() {
+        markSessionEnd();
+        super.onPauseWithNative();
+    }
+
+    @Override
+    public void onStopWithNative() {
+        if (mGSAServiceClient != null) {
+            mGSAServiceClient.disconnect();
+            mGSAServiceClient = null;
+            if (mSyncStateChangedListener != null) {
+                ProfileSyncService syncService = ProfileSyncService.get(this);
+                syncService.removeSyncStateChangedListener(mSyncStateChangedListener);
+                mSyncStateChangedListener = null;
+            }
+        }
+        super.onStopWithNative();
+    }
+
+    @Override
+    public void onNewIntentWithNative(Intent intent) {
+        super.onNewIntentWithNative(intent);
+        if (mIntentHandler.shouldIgnoreIntent(this, intent)) return;
+
+        mIntentHandler.onNewIntent(this, intent);
+    }
+
+    /**
+     * @return Whether the given activity contains a {@link CustomTab}.
+     */
+    private boolean isCustomTab() {
+        return this instanceof CustomTabActivity;
+    }
+
+    @Override
+    protected void onDeferredStartup() {
+        super.onDeferredStartup();
+        boolean crashDumpUploadingDisabled = getIntent() != null
+                && getIntent().hasExtra(
+                        ChromeTabbedActivity.INTENT_EXTRA_DISABLE_CRASH_DUMP_UPLOADING);
+        DeferredStartupHandler.getInstance()
+                .onDeferredStartup(getChromeApplication(), crashDumpUploadingDisabled);
+
+        BeamController.registerForBeam(this, new BeamProvider() {
+            @Override
+            public String getTabUrlForBeam() {
+                if (isOverlayVisible()) return null;
+                if (getActivityTab() == null) return null;
+                return getActivityTab().getUrl();
+            }
+        });
+
+        getChromeApplication().getUpdateInfoBarHelper().checkForUpdateOnBackgroundThread(this);
+
+        removeSnapshotDatabase();
+        RecordHistogram.recordTimesHistogram("MobileStartup.ToolbarInflationTime",
+                mInflateInitialLayoutDurationMs, TimeUnit.MILLISECONDS);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mContextReporter != null) mContextReporter.enable();
+
+        if (mPartnerBrowserRefreshNeeded) {
+            mPartnerBrowserRefreshNeeded = false;
+            PartnerBrowserCustomizations.initializeAsync(getApplicationContext(),
+                    PARTNER_BROWSER_CUSTOMIZATIONS_TIMEOUT_MS);
+            PartnerBrowserCustomizations.setOnInitializeAsyncFinished(new Runnable() {
+                @Override
+                public void run() {
+                    if (PartnerBrowserCustomizations.isIncognitoDisabled()) {
+                        terminateIncognitoSession();
+                    }
+                }
+            });
+        }
+        if (mCompositorViewHolder != null) mCompositorViewHolder.onStart();
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mContextReporter != null) mContextReporter.disable();
+
+        // We want to refresh partner browser provider every onStart().
+        mPartnerBrowserRefreshNeeded = true;
+        if (mCompositorViewHolder != null) mCompositorViewHolder.onStop();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mSnackbarManager != null) mSnackbarManager.dismissSnackbar(false);
+    }
+
+    @Override
+    public long getOnCreateTimestampMs() {
+        return super.getOnCreateTimestampMs();
+    }
+
+    /**
+     * This cannot be overridden in order to preserve destruction order.  Override
+     * {@link #onDestroyInternal()} instead to perform clean up tasks.
+     */
+    @SuppressLint("NewApi")
+    @Override
+    protected final void onDestroy() {
+        if (mReaderModeActivityDelegate != null) mReaderModeActivityDelegate.destroy();
+        if (mContextualSearchManager != null) mContextualSearchManager.destroy();
+        if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy();
+        if (mCompositorViewHolder != null) {
+            if (mCompositorViewHolder.getLayoutManager() != null) {
+                mCompositorViewHolder.getLayoutManager().removeSceneChangeObserver(this);
+            }
+            mCompositorViewHolder.shutDown();
+        }
+        onDestroyInternal();
+
+        TabModelSelector selector = getTabModelSelector();
+        if (selector != null) selector.destroy();
+
+        if (mWindowAndroid != null) mWindowAndroid.destroy();
+        if (!Locale.getDefault().equals(mCurrentLocale)) {
+            // This is a hack to relaunch renderer processes. Killing the main process also kills
+            // its dependent (renderer) processes, and Android's activity manager service seems to
+            // still relaunch the activity even when process dies in onDestroy().
+            // This shouldn't be moved to ChromeActivity since it may cause a crash if
+            // you kill the process from EmbedContentViewActivity since Preferences looks up
+            // ChildAccountManager#hasChildAccount() when it is not set.
+            // TODO(changwan): Implement a more generic and safe relaunch mechanism such as
+            // killing dependent processes on onDestroy and launching them at onCreate().
+            Log.w(TAG, "Forcefully killing process...");
+            Process.killProcess(Process.myPid());
+        }
+        getChromeApplication().removePolicyChangeListener(this);
+        if (mTabContentManager != null) mTabContentManager.destroy();
+        if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy();
+
+        AccessibilityManager manager = (AccessibilityManager)
+                getBaseContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+        manager.removeAccessibilityStateChangeListener(this);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            manager.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+        }
+        super.onDestroy();
+    }
+
+    /**
+     * Override this to perform destruction tasks.  Note that by the time this is called, the
+     * {@link CompositorViewHolder} will be destroyed, but the {@link WindowAndroid} and
+     * {@link TabModelSelector} will not.
+     * <p>
+     * After returning from this, the {@link TabModelSelector} will be destroyed followed
+     * by the {@link WindowAndroid}.
+     */
+    protected void onDestroyInternal() {
+    }
+
+    /**
+     * This will handle passing {@link Intent} results back to the {@link WindowAndroid}.  It will
+     * return whether or not the {@link WindowAndroid} has consumed the event or not.
+     */
+    @Override
+    public boolean onActivityResultWithNative(int requestCode, int resultCode, Intent intent) {
+        if (super.onActivityResultWithNative(requestCode, resultCode, intent)) return true;
+        return mWindowAndroid.onActivityResult(requestCode, resultCode, intent);
+    }
+
+    // @Override[ANDROID-M]
+    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            int[] grantResults) {
+        if (mWindowAndroid != null) {
+            mWindowAndroid.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        }
+        //super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        mWindowAndroid.saveInstanceState(outState);
+    }
+
+    /**
+     * @return The unified manager for all snackbar related operations.
+     */
+    @Override
+    public SnackbarManager getSnackbarManager() {
+        return mSnackbarManager;
+    }
+
+    /**
+     * Called when the accessibility status of this device changes.  This might be triggered by
+     * touch exploration or general accessibility status updates.  It is an aggregate of two other
+     * accessibility update methods.
+     * @see #onAccessibilityModeChanged(boolean)
+     * @see #onTouchExplorationStateChanged(boolean)
+     * @param enabled Whether or not accessibility and touch exploration are currently enabled.
+     */
+    protected void onAccessibilityModeChanged(boolean enabled) {
+        InfoBarContainer.setIsAllowedToAutoHide(!enabled);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item != null && onMenuOrKeyboardAction(item.getItemId(), true)) {
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /**
+     * Triggered when the share menu item is selected.
+     * This creates and shows a share intent picker dialog or starts a share intent directly.
+     *
+     * @param currentTab The {@link Tab} a user is watching.
+     * @param windowAndroid The {@link WindowAndroid} currentTab is linked to.
+     * @param shareDirectly Whether it should share directly with the activity that was most
+     *                      recently used to share.
+     * @param isIncognito Whether currentTab is incognito.
+     */
+    public void onShareMenuItemSelected(final Tab currentTab,
+            final WindowAndroid windowAndroid, final boolean shareDirectly, boolean isIncognito) {
+        if (currentTab == null) return;
+
+        final Activity mainActivity = this;
+        ContentReadbackHandler.GetBitmapCallback bitmapCallback =
+                new ContentReadbackHandler.GetBitmapCallback() {
+                    @Override
+                    public void onFinishGetBitmap(Bitmap bitmap, int reponse) {
+                        ShareHelper.share(shareDirectly, mainActivity, currentTab.getTitle(),
+                                currentTab.getUrl(), bitmap);
+                        if (shareDirectly) {
+                            RecordUserAction.record("MobileMenuDirectShare");
+                        } else {
+                            RecordUserAction.record("MobileMenuShare");
+                        }
+                    }
+                };
+        ContentReadbackHandler readbackHandler = getContentReadbackHandler();
+        if (isIncognito || readbackHandler == null || windowAndroid == null
+                || currentTab.getContentViewCore() == null) {
+            bitmapCallback.onFinishGetBitmap(null, ReadbackResponse.SURFACE_UNAVAILABLE);
+        } else {
+            readbackHandler.getContentBitmapAsync(1, new Rect(), currentTab.getContentViewCore(),
+                    Bitmap.Config.ARGB_8888, bitmapCallback);
+        }
+    }
+
+    /**
+     * @return Whether the activity is in overview mode.
+     */
+    public boolean isInOverviewMode() {
+        return false;
+    }
+
+    /**
+     * @return Whether the app menu should be shown.
+     */
+    public boolean shouldShowAppMenu() {
+        // Do not show the menu if Contextual Search Panel is opened.
+        if (mContextualSearchManager != null && mContextualSearchManager.isSearchPanelOpened()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Allows Activities that extend ChromeActivity to do additional hiding/showing of menu items.
+     * @param menu Menu that is going to be shown when the menu button is pressed.
+     */
+    public void prepareMenu(Menu menu) {
+    }
+
+    protected IntentHandlerDelegate createIntentHandlerDelegate() {
+        return new IntentHandlerDelegate() {
+            @Override
+            public void processWebSearchIntent(String query) {
+                Intent searchIntent = new Intent(Intent.ACTION_WEB_SEARCH);
+                searchIntent.putExtra(SearchManager.QUERY, query);
+                startActivity(searchIntent);
+            }
+
+            @Override
+            public void processUrlViewIntent(String url, String referer, String headers,
+                    TabOpenType tabOpenType, String externalAppId, int tabIdToBringToFront,
+                    Intent intent) {
+            }
+        };
+    }
+
+    /**
+     * @return The resource id that contains how large the top controls are.
+     */
+    protected int getControlContainerHeightResource() {
+        return R.dimen.control_container_height;
+    }
+
+    @Override
+    public final void onAccessibilityStateChanged(boolean enabled) {
+        checkAccessibility();
+    }
+
+    private void checkAccessibility() {
+        onAccessibilityModeChanged(DeviceClassManager.isAccessibilityModeEnabled(this));
+    }
+
+    /**
+     * @return A casted version of {@link #getApplication()}.
+     */
+    public ChromeMobileApplication getChromeApplication() {
+        return (ChromeMobileApplication) getApplication();
+    }
+
+    /**
+     * @return Whether the update infobar may be shown.
+     */
+    public boolean mayShowUpdateInfoBar() {
+        return true;
+    }
+
+    /**
+     * Add the specified tab to bookmarks or allows to edit the bookmark if the specified tab is
+     * already bookmarked. If a new bookmark is added, a snackbar will be shown.
+     * @param tabToBookmark The tab that needs to be bookmarked.
+     */
+    public void addOrEditBookmark(final Tab tabToBookmark) {
+        if (tabToBookmark == null || tabToBookmark.isFrozen()) {
+            return;
+        }
+
+        // Managed bookmarks can't be edited. If the current URL is only bookmarked by managed
+        // bookmarks then fall back on adding a new bookmark instead.
+        final long bookmarkId = tabToBookmark.getUserBookmarkId();
+
+        if (EnhancedBookmarkUtils.isEnhancedBookmarkEnabled(tabToBookmark.getProfile())) {
+            final EnhancedBookmarksModel bookmarkModel = new EnhancedBookmarksModel();
+
+            BookmarkModelObserver modelObserver = new BookmarkModelObserver() {
+                @Override
+                public void bookmarkModelChanged() {}
+
+                @Override
+                public void bookmarkModelLoaded() {
+                    if (bookmarkId == ChromeBrowserProviderClient.INVALID_BOOKMARK_ID) {
+                        EnhancedBookmarkUtils.addBookmarkAndShowSnackbar(bookmarkModel,
+                                tabToBookmark, getSnackbarManager(), ChromeActivity.this);
+                    } else {
+                        EnhancedBookmarkUtils.startDetailActivity(ChromeActivity.this,
+                                new BookmarkId(bookmarkId, BookmarkType.NORMAL));
+                    }
+                    bookmarkModel.removeModelObserver(this);
+                }
+            };
+
+            if (bookmarkModel.isBookmarkModelLoaded()) {
+                modelObserver.bookmarkModelLoaded();
+            } else {
+                bookmarkModel.addModelObserver(modelObserver);
+            }
+        } else {
+            Intent intent = new Intent(this, ManageBookmarkActivity.class);
+            // Managed bookmarks can't be edited. Fallback on adding a new bookmark if the current
+            // URL is bookmarked by a managed bookmark.
+
+            if (bookmarkId == ChromeBrowserProviderClient.INVALID_BOOKMARK_ID) {
+                intent.putExtra(ManageBookmarkActivity.BOOKMARK_INTENT_IS_FOLDER, false);
+                intent.putExtra(ManageBookmarkActivity.BOOKMARK_INTENT_TITLE,
+                        tabToBookmark.getTitle());
+                intent.putExtra(ManageBookmarkActivity.BOOKMARK_INTENT_URL, tabToBookmark.getUrl());
+            } else {
+                intent.putExtra(ManageBookmarkActivity.BOOKMARK_INTENT_IS_FOLDER, false);
+                intent.putExtra(ManageBookmarkActivity.BOOKMARK_INTENT_ID, bookmarkId);
+            }
+            startActivity(intent);
+        }
+    }
+
+    /**
+     * {@link TabModelSelector} no longer implements TabModel.  Use getTabModelSelector() or
+     * getCurrentTabModel() depending on your needs.
+     * @return The {@link TabModelSelector}, possibly null.
+     */
+    public TabModelSelector getTabModelSelector() {
+        return mTabModelSelector;
+    }
+
+    @Override
     public TabCreatorManager.TabCreator getTabCreator(boolean incognito) {
         return incognito ? mIncognitoTabCreator : mRegularTabCreator;
     }
@@ -235,216 +919,184 @@
     }
 
     /**
+     * @return A {@link WindowAndroid} instance.
+     */
+    public WindowAndroid getWindowAndroid() {
+        return mWindowAndroid;
+    }
+
+    /**
+     * @return A {@link CompositorViewHolder} instance.
+     */
+    public CompositorViewHolder getCompositorViewHolder() {
+        return mCompositorViewHolder;
+    }
+
+    /**
      * Gets the full screen manager.
      * @return The fullscreen manager, possibly null
      */
-    public FullscreenManager getFullscreenManager() {
-        return null;
+    public ChromeFullscreenManager getFullscreenManager() {
+        return mFullscreenManager;
     }
 
     /**
      * @return The content offset provider, may be null.
      */
     public ContentOffsetProvider getContentOffsetProvider() {
-        return null;
+        return mCompositorViewHolder.getContentOffsetProvider();
     }
 
     /**
      * @return The content readback handler, may be null.
      */
     public ContentReadbackHandler getContentReadbackHandler() {
-        return null;
-    }
-
-    @Override
-    public void preInflationStartup() {
-        super.preInflationStartup();
-        ApplicationInitialization.enableFullscreenFlags(
-                getResources(), this, getControlContainerHeightResource());
-        getWindow().setBackgroundDrawableResource(R.color.light_background_color);
-    }
-
-    @SuppressLint("NewApi")
-    @Override
-    public void postInflationStartup() {
-        super.postInflationStartup();
-        // Low end device UI should be allowed only after a fresh install or when the data has
-        // been cleared. This must happen before anyone calls SysUtils.isLowEndDevice() or
-        // SysUtils.isLowEndDevice() will always return the wrong value.
-        // Temporarily allowing disk access. TODO: Fix. See http://crbug.com/473352
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-        try {
-            if (OmahaClient.isFreshInstallOrDataHasBeenCleared(this)) {
-                ChromePreferenceManager.getInstance(this).setAllowLowEndDeviceUi();
-            }
-        } finally {
-            StrictMode.setThreadPolicy(oldPolicy);
-        }
-
-        if (!ChromePreferenceManager.getInstance(this).getAllowLowEndDeviceUi()) {
-            CommandLine.getInstance().appendSwitch(
-                    BaseSwitches.DISABLE_LOW_END_DEVICE_MODE);
-        }
-
-        AccessibilityManager manager = (AccessibilityManager)
-                getBaseContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        manager.addAccessibilityStateChangeListener(this);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            mTouchExplorationStateChangeListener = new TouchExplorationStateChangeListener() {
-                @Override
-                public void onTouchExplorationStateChanged(boolean enabled) {
-                    checkAccessibility();
-                }
-            };
-            manager.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
-        }
-
-        // Make the activity listen to policy change events
-        getChromeApplication().addPolicyChangeListener(this);
-    }
-
-    @Override
-    public void initializeState() {
-        super.initializeState();
-        IntentHandler.setTestIntentsEnabled(
-                CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_TEST_INTENTS));
-        mIntentHandler = new IntentHandler(createIntentHandlerDelegate(), getPackageName());
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        if (mContextReporter != null) mContextReporter.enable();
-
-        if (mPartnerBrowserRefreshNeeded) {
-            mPartnerBrowserRefreshNeeded = false;
-            PartnerBrowserCustomizations.initializeAsync(getApplicationContext(),
-                    PARTNER_BROWSER_CUSTOMIZATIONS_TIMEOUT_MS);
-            PartnerBrowserCustomizations.setOnInitializeAsyncFinished(new Runnable() {
-                @Override
-                public void run() {
-                    if (PartnerBrowserCustomizations.isIncognitoDisabled()) {
-                        terminateIncognitoSession();
-                    }
-                }
-            });
-        }
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        if (mContextReporter != null) mContextReporter.disable();
-
-        // We want to refresh partner browser provider every onStart().
-        mPartnerBrowserRefreshNeeded = true;
-    }
-
-    @Override
-    public void onStartWithNative() {
-        super.onStartWithNative();
-        getChromeApplication().onStartWithNative();
-        Tab tab = getActivityTab();
-        if (tab != null) tab.onActivityStart();
-        FeatureUtilities.setDocumentModeEnabled(FeatureUtilities.isDocumentMode(this));
-        WarmupManager.getInstance().clearWebContentsIfNecessary();
-
-        if (GSAState.getInstance(this).isGsaAvailable()) {
-            mGSAServiceClient = new GSAServiceClient(this);
-            mGSAServiceClient.connect();
-            createContextReporterIfNeeded();
-        } else {
-            ContextReporter.reportStatus(ContextReporter.STATUS_GSA_NOT_AVAILABLE);
-        }
-    }
-
-    private void createContextReporterIfNeeded() {
-        if (mContextReporter != null || getActivityTab() == null) return;
-
-        final ProfileSyncService syncService = ProfileSyncService.get(this);
-
-        if (syncService.isSyncingUrlsWithKeystorePassphrase()) {
-            mContextReporter = ((ChromeMobileApplication) getApplicationContext()).createGsaHelper()
-                    .getContextReporter(this);
-
-            if (mSyncStateChangedListener != null) {
-                syncService.removeSyncStateChangedListener(mSyncStateChangedListener);
-                mSyncStateChangedListener = null;
-            }
-
-            return;
-        } else {
-            ContextReporter.reportSyncStatus(syncService);
-        }
-
-        if (mSyncStateChangedListener == null) {
-            mSyncStateChangedListener = new ProfileSyncService.SyncStateChangedListener() {
-                @Override
-                public void syncStateChanged() {
-                    createContextReporterIfNeeded();
-                }
-            };
-            syncService.addSyncStateChangedListener(mSyncStateChangedListener);
-        }
-    }
-
-    @Override
-    public void onResumeWithNative() {
-        super.onResumeWithNative();
-        markSessionResume();
-
-        if (getActivityTab() != null) {
-            LaunchMetrics.commitLaunchMetrics(getActivityTab().getWebContents());
-        }
-    }
-
-    @Override
-    public void onPauseWithNative() {
-        markSessionEnd();
-        super.onPauseWithNative();
-    }
-
-    @Override
-    public void onStopWithNative() {
-        if (mGSAServiceClient != null) {
-            mGSAServiceClient.disconnect();
-            mGSAServiceClient = null;
-            if (mSyncStateChangedListener != null) {
-                ProfileSyncService syncService = ProfileSyncService.get(this);
-                syncService.removeSyncStateChangedListener(mSyncStateChangedListener);
-                mSyncStateChangedListener = null;
-            }
-        }
-
-        super.onStopWithNative();
-    }
-
-    @Override
-    public void onNewIntentWithNative(Intent intent) {
-        super.onNewIntentWithNative(intent);
-        if (mIntentHandler.shouldIgnoreIntent(this, intent)) return;
-
-        mIntentHandler.onNewIntent(this, intent);
+        return mCompositorViewHolder.getContentReadbackHandler();
     }
 
     /**
-     * Called when the accessibility status of this device changes.  This might be triggered by
-     * touch exploration or general accessibility status updates.  It is an aggregate of two other
-     * accessibility update methods.
-     * @see #onAccessibilityModeChanged(boolean)
-     * @see #onTouchExplorationStateChanged(boolean)
-     * @param enabled Whether or not accessibility and touch exploration are currently enabled.
+     * Starts asynchronously taking the compositor activity screenshot.
+     * @param getBitmapCallback The callback to call once the screenshot is taken, or when failed.
      */
-    protected void onAccessibilityModeChanged(boolean enabled) {
-        InfoBarContainer.setIsAllowedToAutoHide(!enabled);
+    public void startTakingCompositorActivityScreenshot(GetBitmapCallback getBitmapCallback) {
+        ContentReadbackHandler readbackHandler = getContentReadbackHandler();
+        if (readbackHandler == null || getWindowAndroid() == null) {
+            getBitmapCallback.onFinishGetBitmap(null, ReadbackResponse.SURFACE_UNAVAILABLE);
+        } else {
+            readbackHandler.getCompositorBitmapAsync(getWindowAndroid(), getBitmapCallback);
+        }
+    }
+
+    /**
+     * @return The {@code ContextualSearchManager} or {@code null} if none;
+     */
+    public ContextualSearchManager getContextualSearchManager() {
+        return mContextualSearchManager;
+    }
+
+    /**
+     * @return A {@link ReaderModeActivityDelegate} instance or {@code null} if reader mode is
+     *         not enabled.
+     */
+    public ReaderModeActivityDelegate getReaderModeActivityDelegate() {
+        return mReaderModeActivityDelegate;
+    }
+
+    /**
+     * Create a full-screen manager to be used by this activity.
+     * @param controlContainer The control container that will be controlled by the full-screen
+     *                         manager.
+     * @return A {@link ChromeFullscreenManager} instance that's been created.
+     */
+    protected ChromeFullscreenManager createFullscreenManager(View controlContainer) {
+        return new ChromeFullscreenManager(this, controlContainer, getTabModelSelector(),
+                getControlContainerHeightResource(), true);
+    }
+
+    /**
+     * Initializes the {@link CompositorViewHolder} with the relevant content it needs to properly
+     * show content on the screen.
+     * @param layoutManager             A {@link LayoutManagerDocument} instance.  This class is
+     *                                  responsible for driving all high level screen content and
+     *                                  determines which {@link Layout} is shown when.
+     * @param urlBar                    The {@link View} representing the URL bar (must be
+     *                                  focusable) or {@code null} if none exists.
+     * @param contentContainer          A {@link ViewGroup} that can have content attached by
+     *                                  {@link Layout}s.
+     * @param controlContainer          A {@link ControlContainer} instance to draw.
+     */
+    protected void initializeCompositorContent(
+            LayoutManagerDocument layoutManager, View urlBar, ViewGroup contentContainer,
+            ControlContainer controlContainer) {
+        CommandLine commandLine = CommandLine.getInstance();
+        boolean enableFullscreen = !commandLine.hasSwitch(ChromeSwitches.DISABLE_FULLSCREEN);
+
+        if (enableFullscreen && controlContainer != null) {
+            mFullscreenManager = createFullscreenManager((View) controlContainer);
+        }
+
+        if (mContextualSearchManager != null) {
+            mContextualSearchManager.initialize(contentContainer);
+            mContextualSearchManager.setSearchContentViewDelegate(layoutManager);
+        }
+
+        if (mReaderModeActivityDelegate != null) {
+            mReaderModeActivityDelegate.initialize(contentContainer);
+            mReaderModeActivityDelegate.setDynamicResourceLoader(
+                    mCompositorViewHolder.getDynamicResourceLoader());
+        }
+
+        layoutManager.addSceneChangeObserver(this);
+        mCompositorViewHolder.setLayoutManager(layoutManager);
+        mCompositorViewHolder.setFocusable(false);
+        mCompositorViewHolder.setControlContainer(controlContainer);
+        mCompositorViewHolder.setFullscreenHandler(mFullscreenManager);
+        mCompositorViewHolder.setUrlBar(urlBar);
+        mCompositorViewHolder.onFinishNativeInitialization(getTabModelSelector(), this,
+                getTabContentManager(), contentContainer, mContextualSearchManager);
+
+        if (controlContainer != null
+                && DeviceClassManager.enableToolbarSwipe(FeatureUtilities.isDocumentMode(this))) {
+            controlContainer.setSwipeHandler(
+                    getCompositorViewHolder().getLayoutManager().getTopSwipeHandler());
+        }
+    }
+
+    /**
+     * Called when the back button is pressed.
+     * @return Whether or not the back button was handled.
+     */
+    protected abstract boolean handleBackPressed();
+
+    @Override
+    public void onOrientationChange(int orientation) {
+        if (mContextualSearchManager != null) {
+            mContextualSearchManager.onOrientationChange();
+        }
     }
 
     @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (item != null && onMenuOrKeyboardAction(item.getItemId(), true)) {
-            return true;
+    public final void onBackPressed() {
+        if (mCompositorViewHolder != null) {
+            LayoutManager layoutManager = mCompositorViewHolder.getLayoutManager();
+            boolean layoutConsumed = layoutManager != null && layoutManager.onBackPressed();
+            if (layoutConsumed || mContextualSearchManager != null
+                    && mContextualSearchManager.onBackPressed()) {
+                RecordUserAction.record("SystemBack");
+                return;
+            }
         }
-        return super.onOptionsItemSelected(item);
+        if (!isSelectActionBarShowing() && handleBackPressed()) {
+            return;
+        }
+        // This will close the select action bar if it is showing, otherwise close the activity.
+        super.onBackPressed();
+    }
+
+    private boolean isSelectActionBarShowing() {
+        Tab tab = getActivityTab();
+        if (tab == null) return false;
+        ContentViewCore contentViewCore = tab.getContentViewCore();
+        if (contentViewCore == null) return false;
+        return contentViewCore.isSelectActionBarShowing();
+    }
+
+    @Override
+    public boolean createContextualSearchTab(ContentViewCore searchContentViewCore) {
+        Tab currentTab = getActivityTab();
+        if (currentTab == null) return false;
+
+        TabCreator tabCreator = getTabCreator(currentTab.isIncognito());
+        if (tabCreator == null) return false;
+
+        tabCreator.createTabWithWebContents(searchContentViewCore.getWebContents(),
+                currentTab.getId(), TabLaunchType.FROM_LONGPRESS_FOREGROUND);
+        return true;
+    }
+
+    @VisibleForTesting
+    public AppMenuHandler getAppMenuHandler() {
+        return null;
     }
 
     /**
@@ -456,90 +1108,85 @@
      * @return Whether the action was handled.
      */
     public boolean onMenuOrKeyboardAction(int id, boolean fromMenu) {
-        return false;
-    }
-
-    /**
-     * Triggered when the share menu item is selected.
-     * This creates and shows a share intent picker dialog or starts a share intent directly.
-     *
-     * @param currentTab The {@link Tab} a user is watching.
-     * @param windowAndroid The {@link WindowAndroid} currentTab is linked to.
-     * @param shareDirectly Whether it should share directly with the activity that was most
-     *                      recently used to share.
-     * @param isIncognito Whether currentTab is incognito.
-     */
-    public void onShareMenuItemSelected(final Tab currentTab,
-            final WindowAndroid windowAndroid, final boolean shareDirectly, boolean isIncognito) {
-        if (currentTab == null) return;
-
-        final Activity mainActivity = this;
-        ContentReadbackHandler.GetBitmapCallback bitmapCallback =
-                new ContentReadbackHandler.GetBitmapCallback() {
-                    @Override
-                    public void onFinishGetBitmap(Bitmap bitmap, int reponse) {
-                        ShareHelper.share(shareDirectly, mainActivity, currentTab.getTitle(),
-                                currentTab.getUrl(), bitmap);
-                        if (shareDirectly) {
-                            RecordUserAction.record("MobileMenuDirectShare");
-                        } else {
-                            RecordUserAction.record("MobileMenuShare");
-                        }
-                    }
-                };
-        ContentReadbackHandler readbackHandler = getContentReadbackHandler();
-        if (isIncognito || readbackHandler == null || windowAndroid == null
-                || currentTab.getContentViewCore() == null) {
-            bitmapCallback.onFinishGetBitmap(null, ReadbackResponse.SURFACE_UNAVAILABLE);
-        } else {
-            readbackHandler.getContentBitmapAsync(1, new Rect(), currentTab.getContentViewCore(),
-                    Bitmap.Config.ARGB_8888, bitmapCallback);
+        if (id == R.id.preferences_id) {
+            PreferencesLauncher.launchSettingsPage(this, null);
+            RecordUserAction.record("MobileMenuSettings");
         }
-    }
 
-    /**
-     * @return Whether the activity is in overview mode.
-     */
-    public boolean isInOverviewMode() {
-        return false;
-    }
-
-    /**
-     * @return Whether the app menu should be shown.
-     */
-    public boolean shouldShowAppMenu() {
-        return false;
-    }
-
-    /**
-     * Allows Activities that extend ChromeActivity to do additional hiding/showing of menu items.
-     * @param menu Menu that is going to be shown when the menu button is pressed.
-     */
-    public void prepareMenu(Menu menu) {
-    }
-
-    protected IntentHandlerDelegate createIntentHandlerDelegate() {
-        return new IntentHandlerDelegate() {
-            @Override
-            public void processWebSearchIntent(String query) {
-                Intent searchIntent = new Intent(Intent.ACTION_WEB_SEARCH);
-                searchIntent.putExtra(SearchManager.QUERY, query);
-                startActivity(searchIntent);
+        // All the code below assumes currentTab is not null, so return early if it is null.
+        final Tab currentTab = getActivityTab();
+        if (currentTab == null) {
+            return false;
+        } else if (id == R.id.forward_menu_id) {
+            if (currentTab.canGoForward()) {
+                currentTab.goForward();
+                RecordUserAction.record("MobileMenuForward");
+                RecordUserAction.record("MobileTabClobbered");
             }
-
-            @Override
-            public void processUrlViewIntent(String url, String referer, String headers,
-                    TabOpenType tabOpenType, String externalAppId, int tabIdToBringToFront,
-                    Intent intent) {
+        } else if (id == R.id.bookmark_this_page_id) {
+            addOrEditBookmark(currentTab);
+            RecordUserAction.record("MobileMenuAddToBookmarks");
+        } else if (id == R.id.reload_menu_id) {
+            if (currentTab.isLoading()) {
+                currentTab.stopLoading();
+            } else {
+                currentTab.reload();
+                RecordUserAction.record("MobileToolbarReload");
             }
-        };
-    }
-
-    /**
-     * @return The resource id that contains how large the top controls are.
-     */
-    protected int getControlContainerHeightResource() {
-        return R.dimen.control_container_height;
+        } else if (id == R.id.info_menu_id) {
+            WebsiteSettingsPopup.show(this, currentTab.getProfile(), currentTab.getWebContents());
+        } else if (id == R.id.open_history_menu_id) {
+            currentTab.loadUrl(
+                    new LoadUrlParams(UrlConstants.HISTORY_URL, PageTransition.AUTO_TOPLEVEL));
+            RecordUserAction.record("MobileMenuHistory");
+        } else if (id == R.id.share_menu_id || id == R.id.direct_share_menu_id) {
+            onShareMenuItemSelected(currentTab, getWindowAndroid(),
+                    id == R.id.direct_share_menu_id, getCurrentTabModel().isIncognito());
+        } else if (id == R.id.print_id) {
+            PrintingController printingController = getChromeApplication().getPrintingController();
+            if (printingController != null && !printingController.isBusy()
+                    && PrefServiceBridge.getInstance().isPrintingEnabled()) {
+                printingController.startPrint(new TabPrinter(currentTab),
+                        new PrintManagerDelegateImpl(this));
+                RecordUserAction.record("MobileMenuPrint");
+            }
+        } else if (id == R.id.add_to_homescreen_id) {
+            AddToHomescreenDialog.show(this, currentTab);
+            RecordUserAction.record("MobileMenuAddToHomescreen");
+        } else if (id == R.id.request_desktop_site_id) {
+            final boolean reloadOnChange = !currentTab.isNativePage();
+            final boolean usingDesktopUserAgent = currentTab.getUseDesktopUserAgent();
+            currentTab.setUseDesktopUserAgent(!usingDesktopUserAgent, reloadOnChange);
+            RecordUserAction.record("MobileMenuRequestDesktopSite");
+        } else if (id == R.id.reader_mode_prefs_id) {
+            if (currentTab.getWebContents() != null) {
+                RecordUserAction.record("DomDistiller_DistilledPagePrefsOpened");
+                AlertDialog.Builder builder =
+                        new AlertDialog.Builder(this, R.style.AlertDialogTheme);
+                builder.setView(DistilledPagePrefsView.create(this));
+                builder.show();
+            }
+        } else if (id == R.id.help_id) {
+            // Since reading back the compositor is asynchronous, we need to do the readback
+            // before starting the GoogleHelp.
+            final String helpContextId = HelpAndFeedback.getHelpContextIdFromUrl(
+                    this, currentTab.getUrl(), getCurrentTabModel().isIncognito());
+            final Activity mainActivity = this;
+            final FeedbackCollector collector =
+                    FeedbackCollector.create(currentTab.getProfile(), currentTab.getUrl());
+            startTakingCompositorActivityScreenshot(new GetBitmapCallback() {
+                @Override
+                public void onFinishGetBitmap(Bitmap bitmap, int response) {
+                    collector.setScreenshot(bitmap);
+                    HelpAndFeedback.getInstance(mainActivity)
+                            .show(mainActivity, helpContextId, collector);
+                    RecordUserAction.record("MobileMenuFeedback");
+                }
+            });
+        } else {
+            return false;
+        }
+        return true;
     }
 
     private void markSessionResume() {
@@ -591,52 +1238,6 @@
         return -1;
     }
 
-    @Override
-    public final void onAccessibilityStateChanged(boolean enabled) {
-        checkAccessibility();
-    }
-
-    private void checkAccessibility() {
-        onAccessibilityModeChanged(DeviceClassManager.isAccessibilityModeEnabled(this));
-    }
-
-    /**
-     * @return A casted version of {@link #getApplication()}.
-     */
-    public ChromeMobileApplication getChromeApplication() {
-        return (ChromeMobileApplication) getApplication();
-    }
-
-    /**
-     * @return Whether the update infobar may be shown.
-     */
-    public boolean mayShowUpdateInfoBar() {
-        return true;
-    }
-
-    @Override
-    protected void onDeferredStartup() {
-        super.onDeferredStartup();
-        boolean crashDumpUploadingDisabled = getIntent() != null
-                && getIntent().hasExtra(
-                        ChromeTabbedActivity.INTENT_EXTRA_DISABLE_CRASH_DUMP_UPLOADING);
-        DeferredStartupHandler.getInstance()
-                .onDeferredStartup(getChromeApplication(), crashDumpUploadingDisabled);
-
-        BeamController.registerForBeam(this, new BeamProvider() {
-            @Override
-            public String getTabUrlForBeam() {
-                if (isOverlayVisible()) return null;
-                if (getActivityTab() == null) return null;
-                return getActivityTab().getUrl();
-            }
-        });
-
-        getChromeApplication().getUpdateInfoBarHelper().checkForUpdateOnBackgroundThread(this);
-
-        removeSnapshotDatabase();
-    }
-
     private final void postDeferredStartupIfNeeded() {
         if (!mDeferredStartupNotified) {
             // We want to perform deferred startup tasks a short time after the first page
@@ -693,10 +1294,9 @@
     @Override
     public void terminateIncognitoSession() {}
 
-    /**
-     * @return The {@code ContextualSearchManager} or {@code null} if none;
-     */
-    public ContextualSearchManager getContextualSearchManager() {
-        return null;
-    }
+    @Override
+    public void onTabSelectionHinted(int tabId) { }
+
+    @Override
+    public void onSceneChange(Layout layout) { }
 }
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java
index 083cee4c..55b6184 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java
@@ -126,10 +126,8 @@
         }
     }
 
-    private static final String[] CHROME_MANDATORY_PAKS = {
-        "en-US.pak", "resources.pak", "chrome_100_percent.pak", "icudtl.dat",
-        "natives_blob.bin", "snapshot_blob.bin"
-    };
+    private static final String[] CHROME_MANDATORY_PAKS = {"en-US.pak", "resources.pak",
+            "chrome_100_percent.pak", "natives_blob.bin", "snapshot_blob.bin"};
     private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
     private static final String DEV_TOOLS_SERVER_SOCKET_PREFIX = "chrome";
     private static final String SESSIONS_UUID_PREF_KEY = "chromium.sync.sessions.id";
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeServiceTabLauncher.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeServiceTabLauncher.java
index ef7b6d7..d036574 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeServiceTabLauncher.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeServiceTabLauncher.java
@@ -46,7 +46,7 @@
 
             ChromeLauncherActivity.launchDocumentInstance(null /* activity */, incognito,
                     ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND, url, intentSource,
-                    PageTransition.LINK, false /* useDesktopUserAgent */, data);
+                    PageTransition.LINK, data);
             return;
         }
 
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 517031f..bc214608 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -39,6 +39,7 @@
 import org.chromium.chrome.browser.appmenu.AppMenuObserver;
 import org.chromium.chrome.browser.appmenu.ChromeAppMenuPropertiesDelegate;
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
+import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel.StateChangeReason;
 import org.chromium.chrome.browser.compositor.layouts.Layout;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome.OverviewLayoutFactoryDelegate;
@@ -100,7 +101,7 @@
  * This is the main activity for ChromeMobile when not running in document mode.  All the tabs
  * are accessible via a chrome specific tab switching UI.
  */
-public class ChromeTabbedActivity extends CompositorChromeActivity implements ActionBarDelegate,
+public class ChromeTabbedActivity extends ChromeActivity implements ActionBarDelegate,
         OverviewModeObserver {
 
     private static final int FIRST_RUN_EXPERIENCE_RESULT = 101;
@@ -428,10 +429,6 @@
 
             mFindToolbarManager = new FindToolbarManager(this, getTabModelSelector(),
                     mToolbarHelper.getContextualMenuBar().getCustomSelectionActionModeCallback());
-            mControlContainer.setFindToolbarManager(mFindToolbarManager);
-            if (getContextualSearchManager() != null) {
-                getContextualSearchManager().setFindToolbarManager(mFindToolbarManager);
-            }
 
             OnClickListener tabSwitcherClickHandler = new OnClickListener() {
                 @Override
@@ -969,6 +966,7 @@
             RecordUserAction.record("MobileMenuCloseAllTabs");
         } else if (id == R.id.find_in_page_id) {
             mFindToolbarManager.showToolbar();
+            getContextualSearchManager().hideContextualSearch(StateChangeReason.UNKNOWN);
             if (fromMenu) {
                 RecordUserAction.record("MobileMenuFindInPage");
             } else {
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/CompositorChromeActivity.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/CompositorChromeActivity.java
deleted file mode 100644
index 11d9bd4a..0000000
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/CompositorChromeActivity.java
+++ /dev/null
@@ -1,708 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.SystemClock;
-import android.support.v7.app.AlertDialog;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnPreDrawListener;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.CommandLine;
-import org.chromium.base.TraceEvent;
-import org.chromium.base.VisibleForTesting;
-import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.BookmarksBridge.BookmarkModelObserver;
-import org.chromium.chrome.browser.appmenu.AppMenuHandler;
-import org.chromium.chrome.browser.bookmark.ManageBookmarkActivity;
-import org.chromium.chrome.browser.compositor.CompositorViewHolder;
-import org.chromium.chrome.browser.compositor.layouts.Layout;
-import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
-import org.chromium.chrome.browser.compositor.layouts.LayoutManagerDocument;
-import org.chromium.chrome.browser.compositor.layouts.SceneChangeObserver;
-import org.chromium.chrome.browser.compositor.layouts.content.ContentOffsetProvider;
-import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
-import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial;
-import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
-import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager.ContextualSearchTabPromotionDelegate;
-import org.chromium.chrome.browser.customtabs.CustomTab;
-import org.chromium.chrome.browser.customtabs.CustomTabActivity;
-import org.chromium.chrome.browser.device.DeviceClassManager;
-import org.chromium.chrome.browser.dom_distiller.DistilledPagePrefsView;
-import org.chromium.chrome.browser.dom_distiller.ReaderModeActivityDelegate;
-import org.chromium.chrome.browser.dom_distiller.ReaderModeManager;
-import org.chromium.chrome.browser.enhanced_bookmarks.EnhancedBookmarksModel;
-import org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkUtils;
-import org.chromium.chrome.browser.feedback.FeedbackCollector;
-import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
-import org.chromium.chrome.browser.help.HelpAndFeedback;
-import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
-import org.chromium.chrome.browser.preferences.PrefServiceBridge;
-import org.chromium.chrome.browser.preferences.PreferencesLauncher;
-import org.chromium.chrome.browser.printing.TabPrinter;
-import org.chromium.chrome.browser.snackbar.LoFiBarPopupController;
-import org.chromium.chrome.browser.snackbar.SnackbarManager;
-import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarManageable;
-import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
-import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
-import org.chromium.chrome.browser.util.FeatureUtilities;
-import org.chromium.chrome.browser.webapps.AddToHomescreenDialog;
-import org.chromium.chrome.browser.widget.ControlContainer;
-import org.chromium.components.bookmarks.BookmarkId;
-import org.chromium.components.bookmarks.BookmarkType;
-import org.chromium.content.browser.ContentReadbackHandler;
-import org.chromium.content.browser.ContentReadbackHandler.GetBitmapCallback;
-import org.chromium.content.browser.ContentViewCore;
-import org.chromium.content_public.browser.LoadUrlParams;
-import org.chromium.content_public.browser.readback_types.ReadbackResponse;
-import org.chromium.printing.PrintManagerDelegateImpl;
-import org.chromium.printing.PrintingController;
-import org.chromium.ui.base.ActivityWindowAndroid;
-import org.chromium.ui.base.PageTransition;
-import org.chromium.ui.base.WindowAndroid;
-
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A {@link ChromeActivity} that builds and manages a {@link CompositorViewHolder} and associated
- * classes.
- */
-public abstract class CompositorChromeActivity extends ChromeActivity
-        implements ContextualSearchTabPromotionDelegate, SnackbarManageable, SceneChangeObserver {
-    /**
-     * No control container to inflate during initialization.
-     */
-    private static final int NO_CONTROL_CONTAINER = -1;
-
-    private static final String TAG = "CompositorChromeActivity";
-
-    private ActivityWindowAndroid mWindowAndroid;
-    private ChromeFullscreenManager mFullscreenManager;
-    private CompositorViewHolder mCompositorViewHolder;
-    private ContextualSearchManager mContextualSearchManager;
-    private ReaderModeActivityDelegate mReaderModeActivityDelegate;
-    private TabModelSelectorTabObserver mTabModelSelectorTabObserver;
-    private SnackbarManager mSnackbarManager;
-    private LoFiBarPopupController mLoFiBarPopupController;
-
-    // Time in ms that it took took us to inflate the initial layout
-    private long mInflateInitialLayoutDurationMs;
-
-    private OnPreDrawListener mFirstDrawListener;
-
-    private final Locale mCurrentLocale = Locale.getDefault();
-
-    @Override
-    public void postInflationStartup() {
-        mWindowAndroid = new ChromeWindow(this);
-        mWindowAndroid.restoreInstanceState(getSavedInstanceState());
-        mSnackbarManager = new SnackbarManager(findViewById(android.R.id.content));
-        mLoFiBarPopupController = new LoFiBarPopupController(this, getSnackbarManager());
-        super.postInflationStartup();
-
-        // Set up the animation placeholder to be the SurfaceView. This disables the
-        // SurfaceView's 'hole' clipping during animations that are notified to the window.
-        mWindowAndroid.setAnimationPlaceholderView(mCompositorViewHolder.getSurfaceView());
-
-        // Inform the WindowAndroid of the keyboard accessory view.
-        mWindowAndroid.setKeyboardAccessoryView((ViewGroup) findViewById(R.id.keyboard_accessory));
-        final View controlContainer = findViewById(R.id.control_container);
-        if (controlContainer != null) {
-            mFirstDrawListener = new ViewTreeObserver.OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    controlContainer.getViewTreeObserver()
-                            .removeOnPreDrawListener(mFirstDrawListener);
-                    mFirstDrawListener = null;
-                    onFirstDrawComplete();
-                    return true;
-                }
-            };
-            controlContainer.getViewTreeObserver().addOnPreDrawListener(mFirstDrawListener);
-        }
-    }
-
-    /**
-     * This function builds the {@link CompositorViewHolder}.  Subclasses *must* call
-     * super.setContentView() before using {@link #getTabModelSelector()} or
-     * {@link #getCompositorViewHolder()}.
-     */
-    @Override
-    protected final void setContentView() {
-        final long begin = SystemClock.elapsedRealtime();
-        TraceEvent.begin("onCreate->setContentView()");
-        if (WarmupManager.getInstance().hasBuiltViewHierarchy()) {
-            View placeHolderView = new View(this);
-            setContentView(placeHolderView);
-            ViewGroup contentParent = (ViewGroup) placeHolderView.getParent();
-            WarmupManager.getInstance().transferViewHierarchyTo(contentParent);
-            contentParent.removeView(placeHolderView);
-        } else {
-            setContentView(R.layout.main);
-            if (getControlContainerLayoutId() != NO_CONTROL_CONTAINER) {
-                ViewStub toolbarContainerStub =
-                        ((ViewStub) findViewById(R.id.control_container_stub));
-                toolbarContainerStub.setLayoutResource(getControlContainerLayoutId());
-                toolbarContainerStub.inflate();
-            }
-        }
-        TraceEvent.end("onCreate->setContentView()");
-        mInflateInitialLayoutDurationMs = SystemClock.elapsedRealtime() - begin;
-
-        // Set the status bar color to black by default. This is an optimization for
-        // Chrome not to draw under status and navigation bars when we use the default
-        // black status bar
-        ApiCompatibilityUtils.setStatusBarColor(getWindow(), Color.BLACK);
-
-        mCompositorViewHolder = (CompositorViewHolder) findViewById(R.id.compositor_view_holder);
-        mCompositorViewHolder.setRootView(getWindow().getDecorView().getRootView());
-    }
-
-    /**
-     * @return The resource id for the layout to use for {@link ControlContainer}. 0 by default.
-     */
-    protected int getControlContainerLayoutId() {
-        return NO_CONTROL_CONTAINER;
-    }
-
-    /**
-     * @return Whether contextual search is allowed for this activity or not.
-     */
-    protected boolean isContextualSearchAllowed() {
-        return true;
-    }
-
-    @Override
-    public void initializeCompositor() {
-        TraceEvent.begin("CompositorChromeActivity:CompositorInitialization");
-        super.initializeCompositor();
-
-        setTabContentManager(new TabContentManager(this, getContentOffsetProvider(),
-                DeviceClassManager.enableSnapshots()));
-        mCompositorViewHolder.onNativeLibraryReady(mWindowAndroid, getTabContentManager());
-
-        if (isContextualSearchAllowed() && ContextualSearchFieldTrial.isEnabled(this)) {
-            mContextualSearchManager = new ContextualSearchManager(this, mWindowAndroid, this);
-        }
-
-        if (ReaderModeManager.isEnabled(this)) {
-            mReaderModeActivityDelegate = new ReaderModeActivityDelegate(this);
-        }
-
-        TraceEvent.end("CompositorChromeActivity:CompositorInitialization");
-    }
-
-    @Override
-    protected void setTabModelSelector(TabModelSelector tabModelSelector) {
-        super.setTabModelSelector(tabModelSelector);
-
-        if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy();
-        mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(tabModelSelector) {
-            @Override
-            public void onDidNavigateMainFrame(Tab tab, String url, String baseUrl,
-                    boolean isNavigationToDifferentPage, boolean isFragmentNavigation,
-                    int statusCode) {
-                if (!tab.isNativePage() && !tab.isIncognito()
-                        && DataReductionProxySettings.getInstance().wasLoFiModeActiveOnMainFrame()
-                        && DataReductionProxySettings.getInstance().canUseDataReductionProxy(
-                                url)) {
-                    if (tab.isHidden()) {
-                        TabObserver tabObserver = new EmptyTabObserver() {
-                            @Override
-                            public void onShown(Tab tab) {
-                                mLoFiBarPopupController.showLoFiBar(tab);
-                                tab.removeObserver(this);
-                            }
-                        };
-                        tab.addObserver(tabObserver);
-                        return;
-                    }
-                    mLoFiBarPopupController.showLoFiBar(tab);
-                }
-            }
-
-            @Override
-            public void onHidden(Tab tab) {
-                mLoFiBarPopupController.dismissLoFiBar();
-            }
-
-            @Override
-            public void onDestroyed(Tab tab) {
-                mLoFiBarPopupController.dismissLoFiBar();
-            }
-        };
-    }
-
-    @Override
-    public void onStartWithNative() {
-        super.onStartWithNative();
-        mCompositorViewHolder.resetFlags();
-    }
-
-    @Override
-    public void onResumeWithNative() {
-        super.onResumeWithNative();
-        FeatureUtilities.setCustomTabVisible(isCustomTab());
-    }
-
-    /**
-     * @return Whether the given activity contains a {@link CustomTab}.
-     */
-    private boolean isCustomTab() {
-        return this instanceof CustomTabActivity;
-    }
-
-    @Override
-    protected void onDeferredStartup() {
-        super.onDeferredStartup();
-        RecordHistogram.recordTimesHistogram("MobileStartup.ToolbarInflationTime",
-                mInflateInitialLayoutDurationMs, TimeUnit.MILLISECONDS);
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        if (mCompositorViewHolder != null) mCompositorViewHolder.onStart();
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        if (mCompositorViewHolder != null) mCompositorViewHolder.onStop();
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        if (mSnackbarManager != null) mSnackbarManager.dismissSnackbar(false);
-    }
-
-    @Override
-    public long getOnCreateTimestampMs() {
-        return super.getOnCreateTimestampMs();
-    }
-
-    /**
-     * This cannot be overridden in order to preserve destruction order.  Override
-     * {@link #onDestroyInternal()} instead to perform clean up tasks.
-     */
-    @Override
-    protected final void onDestroy() {
-        if (mReaderModeActivityDelegate != null) mReaderModeActivityDelegate.destroy();
-        if (mContextualSearchManager != null) mContextualSearchManager.destroy();
-        if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy();
-        if (mCompositorViewHolder != null) {
-            if (mCompositorViewHolder.getLayoutManager() != null) {
-                mCompositorViewHolder.getLayoutManager().removeSceneChangeObserver(this);
-            }
-            mCompositorViewHolder.shutDown();
-        }
-        onDestroyInternal();
-
-        TabModelSelector selector = getTabModelSelector();
-        if (selector != null) selector.destroy();
-
-        if (mWindowAndroid != null) mWindowAndroid.destroy();
-        if (!Locale.getDefault().equals(mCurrentLocale)) {
-            // This is a hack to relaunch renderer processes. Killing the main process also kills
-            // its dependent (renderer) processes, and Android's activity manager service seems to
-            // still relaunch the activity even when process dies in onDestroy().
-            // This shouldn't be moved to ChromeActivity since it may cause a crash if
-            // you kill the process from EmbedContentViewActivity since Preferences looks up
-            // ChildAccountManager#hasChildAccount() when it is not set.
-            // TODO(changwan): Implement a more generic and safe relaunch mechanism such as
-            // killing dependent processes on onDestroy and launching them at onCreate().
-            Log.w(TAG, "Forcefully killing process...");
-            Process.killProcess(Process.myPid());
-        }
-        super.onDestroy();
-    }
-
-    /**
-     * Override this to perform destruction tasks.  Note that by the time this is called, the
-     * {@link CompositorViewHolder} will be destroyed, but the {@link WindowAndroid} and
-     * {@link TabModelSelector} will not.
-     * <p>
-     * After returning from this, the {@link TabModelSelector} will be destroyed followed
-     * by the {@link WindowAndroid}.
-     */
-    protected void onDestroyInternal() {
-    }
-
-    /**
-     * This will handle passing {@link Intent} results back to the {@link WindowAndroid}.  It will
-     * return whether or not the {@link WindowAndroid} has consumed the event or not.
-     */
-    @Override
-    public boolean onActivityResultWithNative(int requestCode, int resultCode, Intent intent) {
-        if (super.onActivityResultWithNative(requestCode, resultCode, intent)) return true;
-        return mWindowAndroid.onActivityResult(requestCode, resultCode, intent);
-    }
-
-    // @Override[ANDROID-M]
-    public void onRequestPermissionsResult(int requestCode, String[] permissions,
-            int[] grantResults) {
-        if (mWindowAndroid != null) {
-            mWindowAndroid.onRequestPermissionsResult(requestCode, permissions, grantResults);
-        }
-        //super.onRequestPermissionsResult(requestCode, permissions, grantResults);
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        mWindowAndroid.saveInstanceState(outState);
-    }
-
-    /**
-     * @return The unified manager for all snackbar related operations.
-     */
-    @Override
-    public SnackbarManager getSnackbarManager() {
-        return mSnackbarManager;
-    }
-
-    /**
-     * Add the specified tab to bookmarks or allows to edit the bookmark if the specified tab is
-     * already bookmarked. If a new bookmark is added, a snackbar will be shown.
-     * @param tabToBookmark The tab that needs to be bookmarked.
-     */
-    public void addOrEditBookmark(final Tab tabToBookmark) {
-        if (tabToBookmark == null || tabToBookmark.isFrozen()) {
-            return;
-        }
-
-        // Managed bookmarks can't be edited. If the current URL is only bookmarked by managed
-        // bookmarks then fall back on adding a new bookmark instead.
-        final long bookmarkId = tabToBookmark.getUserBookmarkId();
-
-        if (EnhancedBookmarkUtils.isEnhancedBookmarkEnabled(tabToBookmark.getProfile())) {
-            final EnhancedBookmarksModel bookmarkModel = new EnhancedBookmarksModel();
-
-            BookmarkModelObserver modelObserver = new BookmarkModelObserver() {
-                @Override
-                public void bookmarkModelChanged() {}
-
-                @Override
-                public void bookmarkModelLoaded() {
-                    if (bookmarkId == ChromeBrowserProviderClient.INVALID_BOOKMARK_ID) {
-                        EnhancedBookmarkUtils.addBookmarkAndShowSnackbar(bookmarkModel,
-                                tabToBookmark, getSnackbarManager(), CompositorChromeActivity.this);
-                    } else {
-                        EnhancedBookmarkUtils.startDetailActivity(CompositorChromeActivity.this,
-                                new BookmarkId(bookmarkId, BookmarkType.NORMAL));
-                    }
-                    bookmarkModel.removeModelObserver(this);
-                }
-            };
-
-            if (bookmarkModel.isBookmarkModelLoaded()) {
-                modelObserver.bookmarkModelLoaded();
-            } else {
-                bookmarkModel.addModelObserver(modelObserver);
-            }
-        } else {
-            Intent intent = new Intent(this, ManageBookmarkActivity.class);
-            // Managed bookmarks can't be edited. Fallback on adding a new bookmark if the current
-            // URL is bookmarked by a managed bookmark.
-
-            if (bookmarkId == ChromeBrowserProviderClient.INVALID_BOOKMARK_ID) {
-                intent.putExtra(ManageBookmarkActivity.BOOKMARK_INTENT_IS_FOLDER, false);
-                intent.putExtra(ManageBookmarkActivity.BOOKMARK_INTENT_TITLE,
-                        tabToBookmark.getTitle());
-                intent.putExtra(ManageBookmarkActivity.BOOKMARK_INTENT_URL, tabToBookmark.getUrl());
-            } else {
-                intent.putExtra(ManageBookmarkActivity.BOOKMARK_INTENT_IS_FOLDER, false);
-                intent.putExtra(ManageBookmarkActivity.BOOKMARK_INTENT_ID, bookmarkId);
-            }
-            startActivity(intent);
-        }
-    }
-
-    /**
-     * @return A {@link WindowAndroid} instance.
-     */
-    public WindowAndroid getWindowAndroid() {
-        return mWindowAndroid;
-    }
-
-    /**
-     * @return A {@link CompositorViewHolder} instance.
-     */
-    public CompositorViewHolder getCompositorViewHolder() {
-        return mCompositorViewHolder;
-    }
-
-    @Override
-    public ChromeFullscreenManager getFullscreenManager() {
-        return mFullscreenManager;
-    }
-
-    @Override
-    public ContentOffsetProvider getContentOffsetProvider() {
-        return mCompositorViewHolder.getContentOffsetProvider();
-    }
-
-    @Override
-    public ContentReadbackHandler getContentReadbackHandler() {
-        return mCompositorViewHolder.getContentReadbackHandler();
-    }
-
-    /**
-     * Starts asynchronously taking the compositor activity screenshot.
-     * @param getBitmapCallback The callback to call once the screenshot is taken, or when failed.
-     */
-    public void startTakingCompositorActivityScreenshot(GetBitmapCallback getBitmapCallback) {
-        ContentReadbackHandler readbackHandler = getContentReadbackHandler();
-        if (readbackHandler == null || getWindowAndroid() == null) {
-            getBitmapCallback.onFinishGetBitmap(null, ReadbackResponse.SURFACE_UNAVAILABLE);
-        } else {
-            readbackHandler.getCompositorBitmapAsync(getWindowAndroid(), getBitmapCallback);
-        }
-    }
-
-    @Override
-    public ContextualSearchManager getContextualSearchManager() {
-        return mContextualSearchManager;
-    }
-
-    /**
-     * @return A {@link ReaderModeActivityDelegate} instance or {@code null} if reader mode is
-     *         not enabled.
-     */
-    public ReaderModeActivityDelegate getReaderModeActivityDelegate() {
-        return mReaderModeActivityDelegate;
-    }
-
-    /**
-     * Create a full-screen manager to be used by this activity.
-     * @param controlContainer The control container that will be controlled by the full-screen
-     *                         manager.
-     * @return A {@link ChromeFullscreenManager} instance that's been created.
-     */
-    protected ChromeFullscreenManager createFullscreenManager(View controlContainer) {
-        return new ChromeFullscreenManager(this, controlContainer, getTabModelSelector(),
-                getControlContainerHeightResource(), true);
-    }
-
-    /**
-     * Initializes the {@link CompositorViewHolder} with the relevant content it needs to properly
-     * show content on the screen.
-     * @param layoutManager             A {@link LayoutManagerDocument} instance.  This class is
-     *                                  responsible for driving all high level screen content and
-     *                                  determines which {@link Layout} is shown when.
-     * @param urlBar                    The {@link View} representing the URL bar (must be
-     *                                  focusable) or {@code null} if none exists.
-     * @param contentContainer          A {@link ViewGroup} that can have content attached by
-     *                                  {@link Layout}s.
-     * @param controlContainer          A {@link ControlContainer} instance to draw.
-     */
-    protected void initializeCompositorContent(
-            LayoutManagerDocument layoutManager, View urlBar, ViewGroup contentContainer,
-            ControlContainer controlContainer) {
-        CommandLine commandLine = CommandLine.getInstance();
-        boolean enableFullscreen = !commandLine.hasSwitch(ChromeSwitches.DISABLE_FULLSCREEN);
-
-        if (enableFullscreen && controlContainer != null) {
-            mFullscreenManager = createFullscreenManager((View) controlContainer);
-        }
-
-        if (mContextualSearchManager != null) {
-            mContextualSearchManager.initialize(contentContainer);
-            mContextualSearchManager.setSearchContentViewDelegate(layoutManager);
-        }
-
-        if (mReaderModeActivityDelegate != null) {
-            mReaderModeActivityDelegate.initialize(contentContainer);
-            mReaderModeActivityDelegate.setDynamicResourceLoader(
-                    mCompositorViewHolder.getDynamicResourceLoader());
-        }
-
-        layoutManager.addSceneChangeObserver(this);
-        mCompositorViewHolder.setLayoutManager(layoutManager);
-        mCompositorViewHolder.setFocusable(false);
-        mCompositorViewHolder.setControlContainer(controlContainer);
-        mCompositorViewHolder.setFullscreenHandler(mFullscreenManager);
-        mCompositorViewHolder.setUrlBar(urlBar);
-        mCompositorViewHolder.onFinishNativeInitialization(getTabModelSelector(), this,
-                getTabContentManager(), contentContainer, mContextualSearchManager);
-
-        if (controlContainer != null
-                && DeviceClassManager.enableToolbarSwipe(FeatureUtilities.isDocumentMode(this))) {
-            controlContainer.setSwipeHandler(
-                    getCompositorViewHolder().getLayoutManager().getTopSwipeHandler());
-        }
-    }
-
-    /**
-     * Called when the back button is pressed.
-     * @return Whether or not the back button was handled.
-     */
-    protected abstract boolean handleBackPressed();
-
-    @Override
-    public void onOrientationChange(int orientation) {
-        if (mContextualSearchManager != null) {
-            mContextualSearchManager.onOrientationChange();
-        }
-    }
-
-    @Override
-    public final void onBackPressed() {
-        if (mCompositorViewHolder != null) {
-            LayoutManager layoutManager = mCompositorViewHolder.getLayoutManager();
-            boolean layoutConsumed = layoutManager != null && layoutManager.onBackPressed();
-            if (layoutConsumed || mContextualSearchManager != null
-                    && mContextualSearchManager.onBackPressed()) {
-                RecordUserAction.record("SystemBack");
-                return;
-            }
-        }
-        if (!isSelectActionBarShowing() && handleBackPressed()) {
-            return;
-        }
-        // This will close the select action bar if it is showing, otherwise close the activity.
-        super.onBackPressed();
-    }
-
-    private boolean isSelectActionBarShowing() {
-        Tab tab = getActivityTab();
-        if (tab == null) return false;
-        ContentViewCore contentViewCore = tab.getContentViewCore();
-        if (contentViewCore == null) return false;
-        return contentViewCore.isSelectActionBarShowing();
-    }
-
-    @Override
-    public boolean createContextualSearchTab(ContentViewCore searchContentViewCore) {
-        Tab currentTab = getActivityTab();
-        if (currentTab == null) return false;
-
-        TabCreator tabCreator = getTabCreator(currentTab.isIncognito());
-        if (tabCreator == null) return false;
-
-        tabCreator.createTabWithWebContents(searchContentViewCore.getWebContents(),
-                currentTab.getId(), TabLaunchType.FROM_LONGPRESS_FOREGROUND);
-        return true;
-    }
-
-    @VisibleForTesting
-    public AppMenuHandler getAppMenuHandler() {
-        return null;
-    }
-
-    @Override
-    public boolean shouldShowAppMenu() {
-        // Do not show the menu if Contextual Search Panel is opened.
-        if (mContextualSearchManager != null && mContextualSearchManager.isSearchPanelOpened()) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public boolean onMenuOrKeyboardAction(int id, boolean fromMenu) {
-        if (id == R.id.preferences_id) {
-            PreferencesLauncher.launchSettingsPage(this, null);
-            RecordUserAction.record("MobileMenuSettings");
-        }
-
-        // All the code below assumes currentTab is not null, so return early if it is null.
-        final Tab currentTab = getActivityTab();
-        if (currentTab == null) {
-            return false;
-        } else if (id == R.id.forward_menu_id) {
-            if (currentTab.canGoForward()) {
-                currentTab.goForward();
-                RecordUserAction.record("MobileMenuForward");
-                RecordUserAction.record("MobileTabClobbered");
-            }
-        } else if (id == R.id.bookmark_this_page_id) {
-            addOrEditBookmark(currentTab);
-            RecordUserAction.record("MobileMenuAddToBookmarks");
-        } else if (id == R.id.reload_menu_id) {
-            if (currentTab.isLoading()) {
-                currentTab.stopLoading();
-            } else {
-                currentTab.reload();
-                RecordUserAction.record("MobileToolbarReload");
-            }
-        } else if (id == R.id.info_menu_id) {
-            WebsiteSettingsPopup.show(this, currentTab.getProfile(), currentTab.getWebContents());
-        } else if (id == R.id.open_history_menu_id) {
-            currentTab.loadUrl(
-                    new LoadUrlParams(UrlConstants.HISTORY_URL, PageTransition.AUTO_TOPLEVEL));
-            RecordUserAction.record("MobileMenuHistory");
-        } else if (id == R.id.share_menu_id || id == R.id.direct_share_menu_id) {
-            onShareMenuItemSelected(currentTab, getWindowAndroid(),
-                    id == R.id.direct_share_menu_id, getCurrentTabModel().isIncognito());
-        } else if (id == R.id.print_id) {
-            PrintingController printingController = getChromeApplication().getPrintingController();
-            if (printingController != null && !printingController.isBusy()
-                    && PrefServiceBridge.getInstance().isPrintingEnabled()) {
-                printingController.startPrint(new TabPrinter(currentTab),
-                        new PrintManagerDelegateImpl(this));
-                RecordUserAction.record("MobileMenuPrint");
-            }
-        } else if (id == R.id.add_to_homescreen_id) {
-            AddToHomescreenDialog.show(this, currentTab);
-            RecordUserAction.record("MobileMenuAddToHomescreen");
-        } else if (id == R.id.request_desktop_site_id) {
-            final boolean reloadOnChange = !currentTab.isNativePage();
-            final boolean usingDesktopUserAgent = currentTab.getUseDesktopUserAgent();
-            currentTab.setUseDesktopUserAgent(!usingDesktopUserAgent, reloadOnChange);
-            RecordUserAction.record("MobileMenuRequestDesktopSite");
-        } else if (id == R.id.reader_mode_prefs_id) {
-            if (currentTab.getWebContents() != null) {
-                RecordUserAction.record("DomDistiller_DistilledPagePrefsOpened");
-                AlertDialog.Builder builder =
-                        new AlertDialog.Builder(this, R.style.AlertDialogTheme);
-                builder.setView(DistilledPagePrefsView.create(this));
-                builder.show();
-            }
-        } else if (id == R.id.help_id) {
-            // Since reading back the compositor is asynchronous, we need to do the readback
-            // before starting the GoogleHelp.
-            final String helpContextId = HelpAndFeedback.getHelpContextIdFromUrl(
-                    this, currentTab.getUrl(), getCurrentTabModel().isIncognito());
-            final Activity mainActivity = this;
-            final FeedbackCollector collector =
-                    FeedbackCollector.create(currentTab.getProfile(), currentTab.getUrl());
-            startTakingCompositorActivityScreenshot(new GetBitmapCallback() {
-                @Override
-                public void onFinishGetBitmap(Bitmap bitmap, int response) {
-                    collector.setScreenshot(bitmap);
-                    HelpAndFeedback.getInstance(mainActivity)
-                            .show(mainActivity, helpContextId, collector);
-                    RecordUserAction.record("MobileMenuFeedback");
-                }
-            });
-        } else {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public void onTabSelectionHinted(int tabId) { }
-
-    @Override
-    public void onSceneChange(Layout layout) { }
-}
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index bdc793e..157640b 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -41,8 +41,6 @@
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
-import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager;
-import org.chromium.chrome.browser.widget.findinpage.FindToolbarObserver;
 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
 import org.chromium.components.navigation_interception.NavigationParams;
 import org.chromium.components.web_contents_delegate_android.WebContentsDelegateAndroid;
@@ -113,8 +111,6 @@
     private final WebContentsDelegateAndroid mWebContentsDelegate;
     private ContextualSearchContentViewDelegate mSearchContentViewDelegate;
     private final ContextualSearchTabPromotionDelegate mTabPromotionDelegate;
-    private final FindToolbarObserver mFindToolbarObserver;
-    private FindToolbarManager mFindToolbarManager;
     private TabModelSelectorTabObserver mTabModelSelectorTabObserver;
     private TabModelObserver mTabModelObserver;
     private boolean mIsSearchContentViewShowing;
@@ -215,13 +211,6 @@
             }
         };
 
-        mFindToolbarObserver = new FindToolbarObserver() {
-            @Override
-            public void onFindToolbarShown() {
-                hideContextualSearch(StateChangeReason.UNKNOWN);
-            }
-        };
-
         final View controlContainer = mActivity.findViewById(R.id.control_container);
         mOnFocusChangeListener = new OnGlobalFocusChangeListener() {
             @Override
@@ -244,16 +233,6 @@
     }
 
     /**
-     * Sets the manager in charge of find in page.
-     * @param manager A {@link FindToolbarManager} instance.
-     */
-    public void setFindToolbarManager(FindToolbarManager manager) {
-        assert manager != null;
-        mFindToolbarManager = manager;
-        mFindToolbarManager.addObserver(mFindToolbarObserver);
-    }
-
-    /**
      * Initializes this manager.  Must be called before {@link #getContextualSearchControl()}.
      * @param parentView The parent view to attach Contextual Search UX to.
      */
@@ -633,7 +612,6 @@
      */
     private void stopListeningForHideNotifications() {
         if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy();
-        if (mFindToolbarManager != null) mFindToolbarManager.removeObserver(mFindToolbarObserver);
 
         TabModelSelector selector = mActivity.getTabModelSelector();
         if (selector != null) {
@@ -1337,11 +1315,6 @@
     }
 
     @Override
-    public void handleSelectionDismissal() {
-        hideContextualSearch(StateChangeReason.INVALID_SELECTION);
-    }
-
-    @Override
     public void handleSelectionModification(String selection, float x, float y) {
         if (mSearchPanelDelegate.isShowing()) {
             getContextualSearchControl().setCentralText(selection);
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
index 40dee706..4ed4ea7 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
@@ -170,7 +170,7 @@
                 shouldHandleSelection = true;
                 break;
             case SelectionEventType.SELECTION_CLEARED:
-                mHandler.handleSelectionDismissal();
+                mHandler.onClearSelection();
                 resetAllStates();
                 break;
             case SelectionEventType.SELECTION_DRAG_STARTED:
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java
index b1c44ca7..7bf5bee4 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java
@@ -40,11 +40,6 @@
     public void handleSelectionModification(String selection, float x, float y);
 
     /**
-     * Handle a dismissal of the selection on the base page.
-     */
-    public void handleSelectionDismissal();
-
-    /**
      * Called when the selection is cleared.
      */
     public void onClearSelection();
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
index d438eb0..04b7eb38 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.contextualsearch;
 
 import org.chromium.base.CalledByNative;
-import org.chromium.chrome.browser.CompositorChromeActivity;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.EmptyTabObserver;
 import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
@@ -168,8 +168,8 @@
      */
     private static ContextualSearchManager getContextualSearchManager(ContentViewCore cvc) {
         // TODO(yfriedman): Decouple this from the activity.
-        if (cvc.getContext() instanceof CompositorChromeActivity) {
-            return ((CompositorChromeActivity) cvc.getContext()).getContextualSearchManager();
+        if (cvc.getContext() instanceof ChromeActivity) {
+            return ((ChromeActivity) cvc.getContext()).getContextualSearchManager();
         }
         return null;
     }
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTab.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTab.java
index cffb37ff..ebb4965 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTab.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTab.java
@@ -8,6 +8,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
+import android.os.SystemClock;
 import android.os.TransactionTooLargeException;
 import android.text.TextUtils;
 import android.view.ContextMenu;
@@ -15,8 +16,9 @@
 
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.CompositorChromeActivity;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.EmptyTabObserver;
 import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.UrlUtilities;
@@ -33,6 +35,8 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * A chrome tab that is only used as a custom tab.
  */
@@ -40,10 +44,26 @@
     private static class CustomTabObserver extends EmptyTabObserver {
         private CustomTabsConnection mCustomTabsConnection;
         private long mSessionId;
+        private long mIntentReceivedTimestamp;
+        private long mPageLoadStartedTimestamp;
+
+        private static final int STATE_RESET = 0;
+        private static final int STATE_WAITING_LOAD_START = 1;
+        private static final int STATE_WAITING_LOAD_FINISH = 2;
+        private int mCurrentState;
 
         public CustomTabObserver(CustomTabsConnection customTabsConnection, long sessionId) {
             mCustomTabsConnection = customTabsConnection;
             mSessionId = sessionId;
+            resetPageLoadTracking();
+        }
+
+        /**
+         * Tracks the next page load, with timestamp as the origin of time.
+         */
+        public void trackNextPageLoadFromTimestamp(long timestamp) {
+            mIntentReceivedTimestamp = timestamp;
+            mCurrentState = STATE_WAITING_LOAD_START;
         }
 
         @Override
@@ -53,12 +73,44 @@
 
         @Override
         public void onPageLoadStarted(Tab tab, String url) {
+            if (mCurrentState == STATE_WAITING_LOAD_START) {
+                mPageLoadStartedTimestamp = SystemClock.elapsedRealtime();
+                mCurrentState = STATE_WAITING_LOAD_FINISH;
+            }
             mCustomTabsConnection.notifyPageLoadStarted(mSessionId, url);
         }
 
         @Override
         public void onPageLoadFinished(Tab tab) {
+            long pageLoadFinishedTimestamp = SystemClock.elapsedRealtime();
             mCustomTabsConnection.notifyPageLoadFinished(mSessionId, tab.getUrl());
+            // Both histograms (commit and PLT) are reported here, to make sure
+            // that they are always recorded together, and that we only record
+            // commits for successful navigations.
+            if (mCurrentState == STATE_WAITING_LOAD_FINISH && mIntentReceivedTimestamp > 0) {
+                long timeToPageLoadStartedMs = mPageLoadStartedTimestamp - mIntentReceivedTimestamp;
+                long timeToPageLoadFinishedMs =
+                        pageLoadFinishedTimestamp - mIntentReceivedTimestamp;
+                // Same bounds and bucket count as "Startup.FirstCommitNavigationTime"
+                RecordHistogram.recordCustomTimesHistogram(
+                        "CustomTabs.IntentToFirstCommitNavigationTime", timeToPageLoadStartedMs,
+                        1, TimeUnit.MINUTES.toMillis(1), TimeUnit.MILLISECONDS, 225);
+                // Same bounds and bucket count as PLT histograms.
+                RecordHistogram.recordCustomTimesHistogram("CustomTabs.IntentToPageLoadedTime",
+                        timeToPageLoadFinishedMs, 10, TimeUnit.MINUTES.toMillis(10),
+                        TimeUnit.MILLISECONDS, 100);
+            }
+            resetPageLoadTracking();
+        }
+
+        @Override
+        public void onPageLoadFailed(Tab tab, int errorCode) {
+            resetPageLoadTracking();
+        }
+
+        private void resetPageLoadTracking() {
+            mCurrentState = STATE_RESET;
+            mIntentReceivedTimestamp = -1;
         }
     }
 
@@ -73,11 +125,13 @@
                 }
             };
 
+    private CustomTabObserver mTabObserver;
+
     /**
      * Construct an CustomTab. It might load a prerendered {@link WebContents} for the URL, if
      * {@link CustomTabsConnectionService} has successfully warmed up for the url.
      */
-    public CustomTab(CompositorChromeActivity activity, WindowAndroid windowAndroid,
+    public CustomTab(ChromeActivity activity, WindowAndroid windowAndroid,
             long sessionId, String url, int parentTabId) {
         super(Tab.generateValidId(Tab.INVALID_TAB_ID), activity, false, windowAndroid,
                 TabLaunchType.FROM_EXTERNAL_APP, parentTabId, null, null);
@@ -89,7 +143,20 @@
         }
         initialize(webContents, activity.getTabContentManager(), false);
         getView().requestFocus();
-        addObserver(new CustomTabObserver(customTabsConnection, sessionId));
+        mTabObserver = new CustomTabObserver(customTabsConnection, sessionId);
+        addObserver(mTabObserver);
+    }
+
+    /**
+     * Loads a URL and tracks its load time, from the timestamp of the intent arrival.
+     *
+     * @param params As in {@link Tab#loadUrl(LoadUrlParams)}.
+     * @param timestamp Timestamp of the intent arrival, as returned by
+     *                  {@link SystemClock#elapsedRealtime()}.
+     */
+    void loadUrlAndTrackFromTimestamp(LoadUrlParams params, long timestamp) {
+        mTabObserver.trackNextPageLoadFromTimestamp(timestamp);
+        loadUrl(params);
     }
 
     @Override
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index e337fbb9..e1bbb99 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -18,13 +18,14 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.CompositorChromeActivity;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.IntentHandler.ExternalAppId;
 import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.appmenu.AppMenuHandler;
 import org.chromium.chrome.browser.appmenu.AppMenuObserver;
 import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate;
+import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel.StateChangeReason;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerDocument;
 import org.chromium.chrome.browser.document.BrandColorUtils;
 import org.chromium.chrome.browser.tabmodel.SingleTabModelSelector;
@@ -37,7 +38,7 @@
 /**
  * The activity for custom tabs. It will be launched on top of a client's task.
  */
-public class CustomTabActivity extends CompositorChromeActivity {
+public class CustomTabActivity extends ChromeActivity {
     private static CustomTabContentHandler sActiveContentHandler;
 
     private CustomTab mTab;
@@ -81,7 +82,8 @@
         if (sActiveContentHandler.getSessionId() != intentSessionId) return false;
         String url = IntentHandler.getUrlFromIntent(intent);
         if (TextUtils.isEmpty(url)) return false;
-        sActiveContentHandler.loadUrl(new LoadUrlParams(url));
+        sActiveContentHandler.loadUrlAndTrackFromTimestamp(
+                new LoadUrlParams(url), IntentHandler.getTimestampFromIntent(intent));
         return true;
     }
 
@@ -165,7 +167,6 @@
                 (ViewGroup) findViewById(android.R.id.content), controlContainer);
         mFindToolbarManager = new FindToolbarManager(this, getTabModelSelector(),
                 mToolbarHelper.getContextualMenuBar().getCustomSelectionActionModeCallback());
-        controlContainer.setFindToolbarManager(mFindToolbarManager);
         mToolbarHelper.initializeControls(
                 mFindToolbarManager, null, layoutDriver, null, null, null,
                 new OnClickListener() {
@@ -176,11 +177,12 @@
                 });
 
         mTab.setFullscreenManager(getFullscreenManager());
-        mTab.loadUrl(new LoadUrlParams(url));
+        mTab.loadUrlAndTrackFromTimestamp(
+                new LoadUrlParams(url), IntentHandler.getTimestampFromIntent(getIntent()));
         mCustomTabContentHandler = new CustomTabContentHandler() {
             @Override
-            public void loadUrl(LoadUrlParams params) {
-                mTab.loadUrl(params);
+            public void loadUrlAndTrackFromTimestamp(LoadUrlParams params, long timestamp) {
+                mTab.loadUrlAndTrackFromTimestamp(params, timestamp);
             }
 
             @Override
@@ -317,7 +319,12 @@
             return true;
         } else if (id == R.id.find_in_page_id) {
             mFindToolbarManager.showToolbar();
-            RecordUserAction.record("CustomTabsMenuFindInPage");
+            getContextualSearchManager().hideContextualSearch(StateChangeReason.UNKNOWN);
+            if (fromMenu) {
+                RecordUserAction.record("MobileMenuFindInPage");
+            } else {
+                RecordUserAction.record("MobileShortcutFindInPage");
+            }
             return true;
         }
         return super.onMenuOrKeyboardAction(id, fromMenu);
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTabContentHandler.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTabContentHandler.java
index 2207b14..3ea598e 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTabContentHandler.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/customtabs/CustomTabContentHandler.java
@@ -12,10 +12,14 @@
  */
 public interface CustomTabContentHandler {
     /**
-     * Load a new url inside the {@link CustomTabContentHandler}.
+     * Loads a new url inside the {@link CustomTabContentHandler}, and tracks
+     * its load time.
+     *
      * @param params The params to use while loading the url.
+     * @param timestamp The intent arrival timestamp, as returned by
+     *                  {@link SystemClock#elapsedRealtime()}.
      */
-    void loadUrl(LoadUrlParams params);
+    void loadUrlAndTrackFromTimestamp(LoadUrlParams params, long timestamp);
 
     /**
      * @return The session id this {@link CustomTabContentHandler} is associated with.
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java
index 0a19e65..0279df4 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java
@@ -142,6 +142,9 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        // Needs to be called as early as possible, to accurately capture the
+        // time at which the intent was received.
+        IntentHandler.addTimestampToIntent(getIntent());
         // Initialize the command line in case we've disabled document mode from there.
         ((ChromeMobileApplication) getApplication()).initCommandLine();
 
@@ -361,8 +364,7 @@
 
                 int mode = mIsInMultiInstanceMode ? LAUNCH_MODE_FOREGROUND : LAUNCH_MODE_RETARGET;
                 launchDocumentInstance(ChromeLauncherActivity.this, false, mode, url,
-                        DocumentMetricIds.STARTED_BY_LAUNCHER, PageTransition.AUTO_TOPLEVEL, false,
-                        null);
+                        DocumentMetricIds.STARTED_BY_LAUNCHER, PageTransition.AUTO_TOPLEVEL, null);
 
                 if (mIsFinishNeeded) finish();
             }
@@ -439,14 +441,13 @@
      * @param intentSource What is causing the Intent to be fired.
      *         See DocumentUma.DOCUMENT_ACTIVITY_STARTED_BY_
      * @param pageTransitionType The page transition we will do on loading the given URL.
-     * @param useDesktopUserAgent Whether to use a desktop user agent.
      * @param pendingUrlParams PendingUrlParams to store internally and use later once an intent is
      *                         received to launch the URL. May be null.
      */
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public static void launchDocumentInstance(Activity activity, boolean incognito, int launchMode,
             String url, int intentSource, int pageTransitionType,
-            boolean useDesktopUserAgent, PendingDocumentData pendingUrlParams) {
+            PendingDocumentData pendingUrlParams) {
         // If we weren't given an initial URL, check the pending parameters.
         if (url == null && pendingUrlParams != null) {
             if (pendingUrlParams.url != null) {
@@ -477,7 +478,6 @@
         intent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, incognito);
         intent.putExtra(IntentHandler.EXTRA_PAGE_TRANSITION_TYPE, pageTransitionType);
         intent.putExtra(IntentHandler.EXTRA_STARTED_BY, intentSource);
-        intent.putExtra(IntentHandler.EXTRA_USE_DESKTOP_USER_AGENT, useDesktopUserAgent);
         intent.putExtra(EXTRA_LAUNCH_MODE, launchMode);
         IntentHandler.addTrustedIntentExtras(intent, context);
 
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.java
index 8878ad50..60d75ee 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.java
@@ -25,8 +25,8 @@
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeMobileApplication;
-import org.chromium.chrome.browser.CompositorChromeActivity;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.KeyboardShortcuts;
 import org.chromium.chrome.browser.Tab;
@@ -36,6 +36,7 @@
 import org.chromium.chrome.browser.appmenu.AppMenuHandler;
 import org.chromium.chrome.browser.appmenu.AppMenuObserver;
 import org.chromium.chrome.browser.appmenu.ChromeAppMenuPropertiesDelegate;
+import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel.StateChangeReason;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerDocument;
 import org.chromium.chrome.browser.document.DocumentTab.DocumentTabObserver;
 import org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkUtils;
@@ -81,7 +82,7 @@
  * Tab switching is handled via the system wide Android task management system.
  */
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class DocumentActivity extends CompositorChromeActivity {
+public class DocumentActivity extends ChromeActivity {
     protected static final String KEY_INITIAL_URL = "DocumentActivity.KEY_INITIAL_URL";
 
     private static final String TAG = "DocumentActivity";
@@ -596,10 +597,6 @@
         mFindToolbarManager = new FindToolbarManager(this, getTabModelSelector(),
                 mToolbarHelper.getContextualMenuBar()
                         .getCustomSelectionActionModeCallback());
-        controlContainer.setFindToolbarManager(mFindToolbarManager);
-        if (getContextualSearchManager() != null) {
-            getContextualSearchManager().setFindToolbarManager(mFindToolbarManager);
-        }
 
         mToolbarHelper.initializeControls(
                 mFindToolbarManager, null, layoutDriver, null, null, null, null);
@@ -790,7 +787,7 @@
         ChromeLauncherActivity.launchDocumentInstance(this, false,
                 ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND, url,
                 DocumentMetricIds.STARTED_BY_CONTEXTUAL_SEARCH,
-                PageTransition.LINK, false, documentData);
+                PageTransition.LINK, documentData);
         return false;
     }
 
@@ -826,6 +823,7 @@
             RecordUserAction.record("MobileMenuOpenTabs");
         } else if (id == R.id.find_in_page_id) {
             mFindToolbarManager.showToolbar();
+            getContextualSearchManager().hideContextualSearch(StateChangeReason.UNKNOWN);
             if (fromMenu) {
                 RecordUserAction.record("MobileMenuFindInPage");
             } else {
@@ -974,7 +972,6 @@
         if (incognito && !PrefServiceBridge.getInstance().isIncognitoModeEnabled()) return;
         ChromeLauncherActivity.launchDocumentInstance(this, incognito,
                 ChromeLauncherActivity.LAUNCH_MODE_RETARGET, UrlConstants.NTP_URL,
-                DocumentMetricIds.STARTED_BY_OPTIONS_MENU, PageTransition.AUTO_TOPLEVEL, false,
-                null);
+                DocumentMetricIds.STARTED_BY_OPTIONS_MENU, PageTransition.AUTO_TOPLEVEL, null);
     }
 }
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentTab.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentTab.java
index 05bb9cf..b7cfce3 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentTab.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentTab.java
@@ -190,7 +190,7 @@
                     getWindowAndroid().getActivity().get(), isIncognito(),
                     ChromeLauncherActivity.LAUNCH_MODE_AFFILIATED, url,
                     DocumentMetricIds.STARTED_BY_CONTEXT_MENU,
-                    PageTransition.AUTO_TOPLEVEL, false, params);
+                    PageTransition.AUTO_TOPLEVEL, params);
         }
 
         @Override
@@ -199,7 +199,7 @@
                     getWindowAndroid().getActivity().get(), true,
                     ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND,
                     url, DocumentMetricIds.STARTED_BY_CONTEXT_MENU,
-                    PageTransition.AUTO_TOPLEVEL, false, null);
+                    PageTransition.AUTO_TOPLEVEL, null);
         }
 
         @Override
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/document/TabDelegateImpl.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/TabDelegateImpl.java
index 2e18030..2b54a905 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/document/TabDelegateImpl.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/TabDelegateImpl.java
@@ -83,7 +83,7 @@
                 ? PageTransition.RELOAD : PageTransition.AUTO_TOPLEVEL;
         ChromeLauncherActivity.launchDocumentInstance(activity, mIsIncognito,
                 ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND, url, startedBy, pageTransition,
-                false, data);
+                data);
 
         return null;
     }
@@ -118,7 +118,7 @@
         ChromeLauncherActivity.launchDocumentInstance(
                 parentActivity, mIsIncognito, launchMode, loadUrlParams.getUrl(),
                 DocumentMetricIds.STARTED_BY_CHROME_HOME_RECENT_TABS,
-                PageTransition.RELOAD, false, params);
+                PageTransition.RELOAD, params);
         return null;
     }
 
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index 2ef0f3bc..ee038ef9 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -12,9 +12,9 @@
 import org.chromium.base.ObserverList;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeVersionInfo;
-import org.chromium.chrome.browser.CompositorChromeActivity;
 import org.chromium.chrome.browser.EmptyTabObserver;
 import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
@@ -328,8 +328,8 @@
     private ContextualSearchManager getContextualSearchManager(Tab tab) {
         if (tab == null || tab.getWindowAndroid() == null) return null;
         Activity activity = tab.getWindowAndroid().getActivity().get();
-        if (!(activity instanceof CompositorChromeActivity)) return null;
-        return ((CompositorChromeActivity) activity).getContextualSearchManager();
+        if (!(activity instanceof ChromeActivity)) return null;
+        return ((ChromeActivity) activity).getContextualSearchManager();
     }
 
     /**
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/feedback/ScreenshotTask.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/feedback/ScreenshotTask.java
index 82d3908..7940005b 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/feedback/ScreenshotTask.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/feedback/ScreenshotTask.java
@@ -7,7 +7,7 @@
 import android.app.Activity;
 import android.graphics.Bitmap;
 
-import org.chromium.chrome.browser.CompositorChromeActivity;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.content.browser.ContentReadbackHandler;
 import org.chromium.content_public.browser.readback_types.ReadbackResponse;
 import org.chromium.ui.UiUtils;
@@ -39,11 +39,11 @@
     /**
      * Prepares screenshot (possibly asynchronously) and invokes the callback when the screenshot
      * is available, or collection has failed. The asynchronous path is only taken when the activity
-     * that is passed in is a {@link CompositorChromeActivity}.
+     * that is passed in is a {@link ChromeActivity}.
      */
     public static void create(Activity activity, ScreenshotTaskCallback callback) {
-        if (activity instanceof CompositorChromeActivity) {
-            createCompositorActivityScreenshot((CompositorChromeActivity) activity, callback);
+        if (activity instanceof ChromeActivity) {
+            createCompositorActivityScreenshot((ChromeActivity) activity, callback);
             return;
         }
 
@@ -51,7 +51,7 @@
         callback.onGotBitmap(bitmap, bitmap != null);
     }
 
-    private static void createCompositorActivityScreenshot(CompositorChromeActivity activity,
+    private static void createCompositorActivityScreenshot(ChromeActivity activity,
             final ScreenshotTaskCallback callback) {
         ContentReadbackHandler.GetBitmapCallback getBitmapCallback =
                 new ContentReadbackHandler.GetBitmapCallback() {
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/snackbar/LoFiBarPopupController.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/snackbar/LoFiBarPopupController.java
index eb755aa..3160bd9 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/snackbar/LoFiBarPopupController.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/snackbar/LoFiBarPopupController.java
@@ -60,7 +60,6 @@
     @Override
     public void onAction(Object actionData) {
         mSnackbarManager.dismissSnackbar(false);
-        mTab.stopLoading();
         mTab.reloadIgnoringCache();
         DataReductionProxySettings.getInstance().incrementLoFiUserRequestsForImages();
         DataReductionProxyUma.dataReductionProxyLoFiUIAction(
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/tab/ChromeTab.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/tab/ChromeTab.java
index b103581..a811b72 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/tab/ChromeTab.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/tab/ChromeTab.java
@@ -25,7 +25,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeMobileApplication;
-import org.chromium.chrome.browser.CompositorChromeActivity;
 import org.chromium.chrome.browser.EmptyTabObserver;
 import org.chromium.chrome.browser.FrozenNativePage;
 import org.chromium.chrome.browser.IntentHandler.TabOpenType;
@@ -1352,8 +1351,7 @@
     }
 
     public ReaderModeActivityDelegate getReaderModeActivityDelegate() {
-        if (!(mActivity instanceof CompositorChromeActivity)) return null;
-        return ((CompositorChromeActivity) mActivity).getReaderModeActivityDelegate();
+        return mActivity.getReaderModeActivityDelegate();
     }
 
     /**
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java
index cfbdd04..eb6c5ef 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java
@@ -23,7 +23,6 @@
 import org.chromium.chrome.browser.widget.SmoothProgressBar;
 import org.chromium.chrome.browser.widget.SmoothProgressBar.ProgressChangeListener;
 import org.chromium.chrome.browser.widget.ViewResourceFrameLayout;
-import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
 
@@ -40,8 +39,6 @@
     private final SwipeRecognizer mSwipeRecognizer;
     private EdgeSwipeHandler mSwipeHandler;
 
-    private FindToolbarManager mFindToolbarManager;
-
     private ViewResourceAdapter mProgressResourceAdapter;
 
     /**
@@ -74,13 +71,6 @@
         mSwipeRecognizer.setSwipeHandler(handler);
     }
 
-    /**
-     * Sets the manager in charge of find in page.
-     */
-    public void setFindToolbarManager(FindToolbarManager manager) {
-        mFindToolbarManager = manager;
-    }
-
     @Override
     public void onFinishInflate() {
         mToolbar = (Toolbar) findViewById(R.id.toolbar);
@@ -343,7 +333,6 @@
             if (isOnTabStrip(e1)) return false;
             if (mToolbar.shouldIgnoreSwipeGesture()) return false;
             if (UiUtils.isKeyboardShowing(getContext(), ToolbarControlContainer.this)) return false;
-            if (mFindToolbarManager == null || mFindToolbarManager.isShowing()) return false;
             return true;
         }
     }
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarHelper.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarHelper.java
index 77879a8..87b6054 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarHelper.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarHelper.java
@@ -15,7 +15,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.CompositorChromeActivity;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ContextualMenuBar;
 import org.chromium.chrome.browser.CustomSelectionActionModeCallback;
 import org.chromium.chrome.browser.Tab;
@@ -47,7 +47,7 @@
     private static final int MIN_FOCUS_TIME_FOR_UMA_HISTOGRAM_MS = 1000;
     private static final int MAX_FOCUS_TIME_FOR_UMA_HISTOGRAM_MS = 30000;
 
-    protected final CompositorChromeActivity mActivity;
+    protected final ChromeActivity mActivity;
     private final ToolbarControlContainer mControlContainer;
     private TabObserver mTabObserver;
 
@@ -68,7 +68,7 @@
      * @param appMenuPropertiesDelegate Controller for app menu item visibility.
      * @param invalidator Notifier to toolbar to force view invalidations.
      */
-    public ToolbarHelper(CompositorChromeActivity activity,
+    public ToolbarHelper(ChromeActivity activity,
             ToolbarControlContainer controlContainer,
             final AppMenuHandler appMenuHandler,
             final ChromeAppMenuPropertiesDelegate appMenuPropertiesDelegate,
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java
index 17ef088..d8b9541 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java
@@ -58,6 +58,8 @@
 
     protected final int mToolbarHeightWithoutShadow;
 
+    private boolean mFindInPageToolbarShowing;
+
     /**
      * Basic constructor for {@link ToolbarLayout}.
      */
@@ -244,6 +246,15 @@
         }
     }
 
+    /**
+     * Gives inheriting classes the chance to respond to
+     * {@link org.chromium.chrome.browser.widget.findinpage.FindToolbar} state changes.
+     * @param showing Whether or not the {@code FindToolbar} will be showing.
+     */
+    protected void handleFindToolbarStateChange(boolean showing) {
+        mFindInPageToolbarShowing = showing;
+    }
+
     @Override
     public void setOnTabSwitcherClickHandler(OnClickListener listener) { }
 
@@ -285,13 +296,6 @@
     protected void updateBookmarkButtonVisibility(boolean isBookmarked) { }
 
     /**
-     * Gives inheriting classes the chance to respond to
-     * {@link org.chromium.chrome.browser.widget.findinpage.FindToolbar} state changes.
-     * @param showing Whether or not the {@code FindToolbar} will be showing.
-     */
-    protected void handleFindToolbarStateChange(boolean showing) { }
-
-    /**
      * Gives inheriting classes the chance to respond to accessibility state changes.
      * @param enabled Whether or not accessibility is enabled.
      */
@@ -398,7 +402,8 @@
     @Override
     public boolean shouldIgnoreSwipeGesture() {
         return mUrlHasFocus
-                || (mAppMenuButtonHelper != null && mAppMenuButtonHelper.isAppMenuActive());
+                || (mAppMenuButtonHelper != null && mAppMenuButtonHelper.isAppMenuActive())
+                || mFindInPageToolbarShowing;
     }
 
     /**
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java
index e1d9dc03..6011816 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java
@@ -9,11 +9,17 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.CompositorChromeActivity;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeMobileApplication;
+import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerDocument;
 import org.chromium.chrome.browser.tabmodel.SingleTabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
+import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelSelector;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.widget.ControlContainer;
+import org.chromium.content_public.browser.LoadUrlParams;
 
 import java.io.File;
 
@@ -25,11 +31,8 @@
  * Activity would be webapps and streaming media activities - anything where user interaction with
  * the regular browser's UI is either unnecessary or undesirable.
  * Subclasses can override {@link #createUI()} if they need something more exotic.
- *
- * TODO(dfalcantara, andrewhayden): Consider not using a Tab here once all initialization is cleaned
- *                                  up: crbug.com/270973
  */
-public abstract class FullScreenActivity extends CompositorChromeActivity
+public abstract class FullScreenActivity extends ChromeActivity
         implements FullScreenActivityTab.TopControlsVisibilityDelegate {
     private FullScreenActivityTab mTab;
 
@@ -42,7 +45,26 @@
     @Override
     public void preInflationStartup() {
         super.preInflationStartup();
-        setTabModelSelector(new SingleTabModelSelector(this, false, true));
+
+        final boolean isDocumentMode = FeatureUtilities.isDocumentMode(this);
+        if (isDocumentMode) {
+            DocumentTabModelSelector selector =
+                    ChromeMobileApplication.getDocumentTabModelSelector();
+            setTabCreators(selector.getTabCreator(false), selector.getTabCreator(true));
+        }
+
+        setTabModelSelector(new SingleTabModelSelector(this, false, !isDocumentMode) {
+            @Override
+            public Tab openNewTab(LoadUrlParams loadUrlParams, TabLaunchType type, Tab parent,
+                    boolean incognito) {
+                if (isDocumentMode) {
+                    return ChromeMobileApplication.getDocumentTabModelSelector().openNewTab(
+                            loadUrlParams, type, parent, incognito);
+                } else {
+                    return super.openNewTab(loadUrlParams, type, parent, incognito);
+                }
+            }
+        });
     }
 
     @Override
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/webapps/FullScreenActivityTab.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/webapps/FullScreenActivityTab.java
index 9896aee..259f7d4 100644
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/webapps/FullScreenActivityTab.java
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/webapps/FullScreenActivityTab.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Bundle;
 import android.text.TextUtils;
@@ -27,8 +28,10 @@
 import org.chromium.chrome.browser.contextmenu.ContextMenuParams;
 import org.chromium.chrome.browser.contextmenu.ContextMenuPopulator;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
+import org.chromium.chrome.browser.document.DocumentWebContentsDelegate;
 import org.chromium.chrome.browser.tab.ChromeTab;
 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.StreamUtil;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.WebContents;
@@ -336,5 +339,27 @@
 
             getApplicationContext().startActivity(intent);
         }
+
+        @Override
+        public void webContentsCreated(WebContents sourceWebContents, long openerRenderFrameId,
+                String frameName, String targetUrl, WebContents newWebContents) {
+            super.webContentsCreated(sourceWebContents, openerRenderFrameId, frameName,
+                    targetUrl, newWebContents);
+            if (FeatureUtilities.isDocumentMode(mActivity)) {
+                DocumentWebContentsDelegate.getInstance().attachDelegate(newWebContents);
+            }
+        }
+
+        @Override
+        public boolean addNewContents(WebContents sourceWebContents, WebContents webContents,
+                int disposition, Rect initialPosition, boolean userGesture) {
+            if (isClosing() || !FeatureUtilities.isDocumentMode(mActivity)) return false;
+
+            mActivity.getTabCreator(isIncognito()).createTabWithWebContents(
+                    webContents, getId(), TabLaunchType.FROM_LONGPRESS_FOREGROUND);
+
+            // Returns true because Tabs are created asynchronously.
+            return true;
+        }
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java
index 1d529a97..7922a1ac 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java
@@ -136,8 +136,8 @@
 
         // Wait for the new tab animations on phones to finish.
         if (!DeviceFormFactor.isTablet(getActivity())
-                && getActivity() instanceof CompositorChromeActivity) {
-            final CompositorChromeActivity activity = (CompositorChromeActivity) getActivity();
+                && getActivity() instanceof ChromeActivity) {
+            final ChromeActivity activity = (ChromeActivity) getActivity();
             assertTrue("Did not finish animation",
                     CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
                         @Override
@@ -208,8 +208,8 @@
 
         // Wait for the new tab animations on phones to finish.
         if (!DeviceFormFactor.isTablet(getActivity())
-                && getActivity() instanceof CompositorChromeActivity) {
-            final CompositorChromeActivity activity = (CompositorChromeActivity) getActivity();
+                && getActivity() instanceof ChromeActivity) {
+            final ChromeActivity activity = (ChromeActivity) getActivity();
             assertTrue("Did not finish animation",
                     CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
                         @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index 9c0a6ac..2d3c536 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -25,7 +25,6 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
-import org.chromium.chrome.browser.CompositorChromeActivity;
 import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel.PanelState;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanelDelegate;
@@ -78,10 +77,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
-        ChromeActivity activity = getActivity();
-        if (activity instanceof CompositorChromeActivity) {
-            mManager = ((CompositorChromeActivity) activity).getContextualSearchManager();
-        }
+        mManager = getActivity().getContextualSearchManager();
 
         if (mManager != null) {
             mFakeServer = new ContextualSearchFakeServer(mManager);
@@ -1493,7 +1489,7 @@
         assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
             @Override
             public boolean isSatisfied() {
-                if (((CompositorChromeActivity) getActivity())
+                if (((ChromeActivity) getActivity())
                         .getAppMenuHandler().isAppMenuShowing() == isVisible) return true;
                 return false;
             }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 7b5a756..e3bc80b8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -23,8 +23,8 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
-import org.chromium.chrome.browser.CompositorChromeActivity;
 import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.document.BrandColorUtils;
 import org.chromium.chrome.browser.document.DocumentActivity;
@@ -255,7 +255,7 @@
             }
         });
 
-        final CompositorChromeActivity chromeActivity = (CompositorChromeActivity) monitor
+        final ChromeActivity chromeActivity = (ChromeActivity) monitor
                 .waitForActivityWithTimeout(ACTIVITY_START_TIMEOUT_MS);
         assertNotNull("A normal chrome activity did not start.", chromeActivity);
         assertTrue("The normal tab was not initiated correctly.",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeTestBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeTestBase.java
index c2f1b07..8fb0671 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeTestBase.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeTestBase.java
@@ -8,16 +8,13 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Build;
-import android.text.TextUtils;
 import android.view.ContextMenu;
 import android.view.View;
 
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
-import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeMobileApplication;
 import org.chromium.chrome.browser.EmptyTabObserver;
 import org.chromium.chrome.browser.IntentHandler;
@@ -47,51 +44,6 @@
 public class DocumentModeTestBase extends MultiActivityTestBase {
     protected static final String TAG = "cr.document";
 
-    protected static final String URL_1 = createTestUrl(1);
-    protected static final String URL_2 = createTestUrl(2);
-    protected static final String URL_3 = createTestUrl(3);
-    protected static final String URL_4 = createTestUrl(4);
-
-    // Defines one gigantic link spanning the whole page that creates a new window.
-    protected static final String HREF_LINK = UrlUtils.encodeHtmlDataUri(
-            "<html>"
-            + "  <head>"
-            + "    <title>href link page</title>"
-            + "    <meta name='viewport'"
-            + "        content='width=device-width initial-scale=0.5, maximum-scale=0.5'>"
-            + "    <style>"
-            + "      body {margin: 0em;} div {width: 100%; height: 100%; background: #011684;}"
-            + "    </style>"
-            + "  </head>"
-            + "  <body>"
-            + "    <a href='" + URL_4 + "' target='_blank'><div></div></a>"
-            + "  </body>"
-            + "</html>");
-
-    // Clicking the body triggers a window.open() call.
-    protected static final String SUCCESS_URL = UrlUtils.encodeHtmlDataUri("opened!");
-    protected static final String ONCLICK_LINK = UrlUtils.encodeHtmlDataUri(
-            "<html>"
-            + "  <head>"
-            + "    <title>window.open page</title>"
-            + "    <meta name='viewport'"
-            + "        content='width=device-width initial-scale=0.5, maximum-scale=0.5'>"
-            + "    <style>"
-            + "      body {margin: 0em;} div {width: 100%; height: 100%; background: #011684;}"
-            + "    </style>"
-            + "    <script>"
-            + "      function openNewWindow() {"
-            + "        if (window.open('" + URL_4 + "')) location.href = '" + SUCCESS_URL + "';"
-            + "      }"
-            + "    </script>"
-            + "  </head>"
-            + "  <body id='body'>"
-            + "    <div onclick='openNewWindow()'></div></a>"
-            + "  </body>"
-            + "</html>");
-
-    private static final float FLOAT_EPSILON = 0.001f;
-
     private static class TestTabObserver extends EmptyTabObserver {
         private ContextMenu mContextMenu;
 
@@ -197,7 +149,7 @@
             public void run() {
                 ChromeLauncherActivity.launchDocumentInstance(null, incognito,
                         ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND, url,
-                        DocumentMetricIds.STARTED_BY_UNKNOWN, PageTransition.LINK, false, null);
+                        DocumentMetricIds.STARTED_BY_UNKNOWN, PageTransition.LINK, null);
             }
         };
         return launchUrlViaRunnable(incognito, runnable, expectedTitle);
@@ -251,44 +203,6 @@
     }
 
     /**
-     * Approximates when a DocumentActivity is fully ready and loaded, which is hard to gauge
-     * because Android's Activity transition animations are not monitorable.
-     */
-    protected void waitForFullLoad(final DocumentActivity activity, final String expectedTitle)
-            throws Exception {
-        assertWaitForPageScaleFactorMatch(activity, 0.5f);
-        final Tab tab = activity.getActivityTab();
-        assert tab != null;
-
-        assertTrue(CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                if (!tab.isLoadingAndRenderingDone()) return false;
-                if (!TextUtils.equals(expectedTitle, tab.getTitle())) return false;
-                return true;
-            }
-        }));
-    }
-
-    /**
-     * Proper use of this function requires waiting for a page scale factor that isn't 1.0f because
-     * the default seems to be 1.0f.
-     * TODO(dfalcantara): Combine this one and ChromeActivityTestCaseBase's (crbug.com/498973)
-     */
-    private void assertWaitForPageScaleFactorMatch(
-            final ChromeActivity activity, final float expectedScale) throws Exception {
-        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                if (activity.getCurrentContentViewCore() == null) return false;
-
-                return Math.abs(activity.getCurrentContentViewCore().getScale() - expectedScale)
-                        < FLOAT_EPSILON;
-            }
-        }));
-    }
-
-    /**
      * Long presses at the center of the page, selects "Open In New Tab" option from the menu.
      */
     protected void openLinkInBackgroundTab() throws Exception {
@@ -361,17 +275,4 @@
         );
         waitForFullLoad(newActivity, "Page 4");
     }
-
-    private static final String createTestUrl(int index) {
-        String[] colors = {"#000000", "#ff0000", "#00ff00", "#0000ff", "#ffff00"};
-        return UrlUtils.encodeHtmlDataUri(
-            "<html>"
-            + "  <head>"
-            + "    <title>Page " + index + "</title>"
-            + "    <meta name='viewport' content='width=device-width "
-            + "        initial-scale=0.5 maximum-scale=0.5'>"
-            + "  </head>"
-            + "  <body style='margin: 0em; background: " + colors[index] + ";'></body>"
-            + "</html>");
-    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ContextMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ContextMenuTest.java
index 87da99e..9be286f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ContextMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ContextMenuTest.java
@@ -8,8 +8,8 @@
 
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.CompositorChromeActivity;
 import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
 import org.chromium.chrome.test.util.ChromeTabUtils;
@@ -95,8 +95,8 @@
                 nOpenedTabs , getActivity().getCurrentTabModel().getCount());
 
         // Wait for any new tab animation to finish if we're being driven by the compositor.
-        if (getActivity() instanceof CompositorChromeActivity) {
-            final LayoutManager layoutDriver = ((CompositorChromeActivity) getActivity())
+        if (getActivity() instanceof ChromeActivity) {
+            final LayoutManager layoutDriver = ((ChromeActivity) getActivity())
                     .getCompositorViewHolder().getLayoutManager();
             assertTrue("Background tab animation not finished.",
                     CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java
index 026974f..3d6c396 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java
@@ -15,13 +15,18 @@
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.UrlUtils;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.ShortcutHelper;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.document.DocumentActivity;
 import org.chromium.chrome.test.MultiActivityTestBase;
+import org.chromium.chrome.test.util.ActivityUtils;
+import org.chromium.chrome.test.util.DisableInTabbedMode;
+import org.chromium.chrome.test.util.browser.TabLoadObserver;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.content.browser.test.util.DOMUtils;
 import org.chromium.content_public.common.ScreenOrientationValues;
 
 import java.lang.ref.WeakReference;
@@ -210,4 +215,57 @@
             }
         }));
     }
+
+    /**
+     * Tests that WebappActivities handle window.open() properly in document mode.
+     */
+    @DisableInTabbedMode
+    @MediumTest
+    public void testWebappHandlesWindowOpenInDocumentMode() throws Exception {
+        // Start the WebappActivity.  We CAN use ActivityUtils.waitForActivity() because
+        // document mode only runs on L devices.
+        Runnable webappRunnable = new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    fireWebappIntent(WEBAPP_1_ID, WEBAPP_1_URL, WEBAPP_1_TITLE, WEBAPP_ICON, true);
+                } catch (Exception e) {
+                    fail();
+                }
+            }
+        };
+        final WebappActivity webappActivity = ActivityUtils.waitForActivity(
+                getInstrumentation(), WebappActivity.class, webappRunnable);
+
+        // Load up the test page.
+        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return webappActivity.getActivityTab() != null;
+            }
+        }));
+        assertTrue(CriteriaHelper.pollForCriteria(
+                new TabLoadObserver(webappActivity.getActivityTab(), ONCLICK_LINK)));
+
+        // Do a plain click to make the link open in a new foreground Document via a window.open().
+        // If the window is opened successfully, javascript on the first page triggers and changes
+        // its URL as a signal for this test.
+        Runnable fgTrigger = new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    DOMUtils.clickNode(null, webappActivity.getCurrentContentViewCore(), "body");
+                } catch (Exception e) {
+
+                }
+            }
+        };
+        ChromeActivity secondActivity = ActivityUtils.waitForActivity(
+                getInstrumentation(), DocumentActivity.class, fgTrigger);
+        waitForFullLoad(secondActivity, "Page 4");
+        assertEquals("New WebContents was not created",
+                SUCCESS_URL, webappActivity.getActivityTab().getUrl());
+        assertNotSame("Wrong Activity in foreground",
+                webappActivity, ApplicationStatus.getLastTrackedFocusedActivity());
+    }
 }
diff --git a/chrome/android/javatests_shell/src/org/chromium/chrome/browser/IntentHandlerTest.java b/chrome/android/javatests_shell/src/org/chromium/chrome/browser/IntentHandlerTest.java
index d3ff52b4..97ecdd7 100644
--- a/chrome/android/javatests_shell/src/org/chromium/chrome/browser/IntentHandlerTest.java
+++ b/chrome/android/javatests_shell/src/org/chromium/chrome/browser/IntentHandlerTest.java
@@ -8,6 +8,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.provider.Browser;
 import android.speech.RecognizerResultsIntent;
 import android.test.UiThreadTest;
@@ -278,4 +279,20 @@
         assertEquals("Referer: " + validReferer,
                 IntentHandler.getExtraHeadersFromIntent(headersIntent, false));
     }
+
+    @SmallTest
+    @Feature({"Android-AppBase"})
+    public void testAddTimestampToIntent() {
+        Intent intent = new Intent();
+        assertEquals(-1, IntentHandler.getTimestampFromIntent(intent));
+        // Check both before and after to make sure that the returned value is
+        // really from {@link SystemClock#elapsedRealtime()}.
+        long before = SystemClock.elapsedRealtime();
+        IntentHandler.addTimestampToIntent(intent);
+        long after = SystemClock.elapsedRealtime();
+        assertTrue("Time should be increasing",
+                before <= IntentHandler.getTimestampFromIntent(intent));
+        assertTrue("Time should be increasing",
+                IntentHandler.getTimestampFromIntent(intent) <= after);
+    }
 }
diff --git a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellApplication.java b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellApplication.java
index 1dca3dd..587d3be 100644
--- a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellApplication.java
+++ b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellApplication.java
@@ -42,7 +42,6 @@
         "en-US.pak",
         "resources.pak",
         "chrome_100_percent.pak",
-        "icudtl.dat",
         "natives_blob.bin",
         "snapshot_blob.bin"
     };
diff --git a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java
index 0cbbe9c..7435ca8 100644
--- a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java
+++ b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java
@@ -64,7 +64,7 @@
     @LargeTest
     @Feature({"Sync"})
     public void testDownloadAutofill() throws Exception {
-        addServerAutofillProfile(STREET, CITY, STATE, ZIP);
+        addServerAutofillData(STREET, CITY, STATE, ZIP);
         assertServerAutofillCountWithName(1, STREET);
         SyncTestUtil.triggerSyncAndWaitForCompletion(mContext);
 
@@ -84,7 +84,7 @@
     @Feature({"Sync"})
     public void testDownloadDeletedAutofill() throws Exception {
         // Add the entity to test deleting.
-        addServerAutofillProfile(STREET, CITY, STATE, ZIP);
+        addServerAutofillData(STREET, CITY, STATE, ZIP);
         SyncTestUtil.triggerSyncAndWaitForCompletion(mContext);
         assertServerAutofillCountWithName(1, STREET);
         assertClientAutofillCount(1);
@@ -97,7 +97,19 @@
         waitForClientAutofillCount(0);
     }
 
-    private void addServerAutofillProfile(String street, String city, String state, String zip) {
+    // Test that autofill entries don't get synced if the data type is disabled.
+    @LargeTest
+    @Feature({"Sync"})
+    public void testDisabledNoDownloadAutofill() throws Exception {
+        disableDataType(ModelType.AUTOFILL);
+        addServerAutofillData(STREET, CITY, STATE, ZIP);
+        assertServerAutofillCountWithName(1, STREET);
+        SyncTestUtil.triggerSyncAndWaitForCompletion(mContext);
+        assertClientAutofillCount(0);
+    }
+
+    // TODO(maxbogue): Switch to using specifics.autofill_profile instead.
+    private void addServerAutofillData(String street, String city, String state, String zip) {
         EntitySpecifics specifics = new EntitySpecifics();
         specifics.autofill = new AutofillSpecifics();
         AutofillProfileSpecifics profile = new AutofillProfileSpecifics();
diff --git a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java
index 90e8875..f3b0fc1 100644
--- a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java
+++ b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java
@@ -137,6 +137,27 @@
         assertServerBookmarkCountWithName(0, MODIFIED_TITLE);
     }
 
+    // Test that bookmarks don't get downloaded if the data type is disabled.
+    @LargeTest
+    @Feature({"Sync"})
+    public void testDisabledNoDownloadBookmark() throws Exception {
+        disableDataType(ModelType.BOOKMARK);
+        addServerBookmarkAndSync(TITLE, URL);
+        waitForServerBookmarkCountWithName(1, TITLE);
+        SyncTestUtil.triggerSyncAndWaitForCompletion(mContext);
+        assertClientBookmarkCount(0);
+    }
+
+    // Test that bookmarks don't get uploaded if the data type is disabled.
+    @LargeTest
+    @Feature({"Sync"})
+    public void testDisabledNoUploadBookmark() throws Exception {
+        disableDataType(ModelType.BOOKMARK);
+        addClientBookmark(TITLE, URL);
+        SyncTestUtil.triggerSyncAndWaitForCompletion(mContext);
+        assertServerBookmarkCountWithName(0, TITLE);
+    }
+
     private BookmarkId addClientBookmark(final String title, final String url) {
         final AtomicReference<BookmarkId> id = new AtomicReference<BookmarkId>();
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
diff --git a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java
index 05a3970..f967b31 100644
--- a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java
+++ b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java
@@ -16,11 +16,14 @@
 import org.chromium.chrome.shell.ChromeShellTestBase;
 import org.chromium.chrome.test.util.browser.sync.SyncTestUtil;
 import org.chromium.sync.AndroidSyncSettings;
+import org.chromium.sync.internal_api.pub.base.ModelType;
 import org.chromium.sync.signin.AccountManagerHelper;
 import org.chromium.sync.signin.ChromeSigninController;
 import org.chromium.sync.test.util.MockAccountManager;
 import org.chromium.sync.test.util.MockSyncContentResolverDelegate;
 
+import java.util.Set;
+
 /**
  * Base class for common functionality between sync tests.
  */
@@ -183,4 +186,15 @@
             }
         });
     }
+
+    protected void disableDataType(final ModelType modelType) {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                Set<ModelType> preferredTypes = mProfileSyncService.getPreferredDataTypes();
+                preferredTypes.remove(modelType);
+                mProfileSyncService.setPreferredDataTypes(false, preferredTypes);
+            }
+        });
+    }
 }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index fe443dd..528f13f 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4304,6 +4304,33 @@
       <message name="IDS_EXTENSION_REPAIR_PROMPT_TITLE" desc="Titlebar of the extension or app installation prompt. Asks the user if they want to update the extension/app.">
         Repair "<ph name="EXTENSION_NAME">$1<ex>Gmail Checker</ex></ph>"?
       </message>
+      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_APP" desc="Titlebar of the prompt window for an externally installed app">
+        Another program on your computer added an app that may change the way Chrome works.
+
+<ph name="EXTENSION_NAME">$1<ex>Babylon Toolbar</ex></ph>
+      </message>
+      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_EXTENSION" desc="Titlebar of the prompt window for an externally installed extension">
+        Another program on your computer added an extension that may change the way Chrome works.
+
+<ph name="EXTENSION_NAME">$1<ex>Babylon Toolbar</ex></ph>
+      </message>
+      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_THEME" desc="Titlebar of the prompt window for an externally installed theme">
+        Another program on your computer added a theme that may change the way Chrome works.
+
+<ph name="EXTENSION_NAME">$1<ex>Babylon Toolbar</ex></ph>
+      </message>
+      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION" desc="Button on the external install prompt to enable an extension installed by a third party.">
+        Enable extension
+      </message>
+      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP" desc="Button on the external install prompt to enable an extension installed by a third party.">
+        Enable app
+      </message>
+      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME" desc="Button on the external install prompt to enable an extension installed by a third party.">
+        Enable theme
+      </message>
+      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON" desc="Button on the external install prompt to remove an extension installed by a third party.">
+        Remove from Chrome
+      </message>
 
       <!-- Extension alerts. -->
       <message name="IDS_EXTENSION_ALERT_TITLE" desc="Titlebar of the extension notification alert">
@@ -5155,32 +5182,17 @@
       <message name="IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_THEME" desc="The wrench menu item indicating that a new external extension was installed.">
         New theme added (<ph name="EXTENSION_NAME">$1<ex>Babylon Toolbar</ex></ph>)
       </message>
-      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_APP" desc="Titlebar of the prompt window for an externally installed app">
+      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_TITLE" desc="The title for the bubble that alerts the user that a new external extension was installed.">
+        "<ph name="EXTENSION_NAME">$1<ex>Babylon Toolbar</ex></ph>" added
+      </message>
+      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_HEADING_APP" desc="The heading text (below the title) for the bubble that alerts the user that a new external app was installed.">
         Another program on your computer added an app that may change the way Chrome works.
-
-<ph name="EXTENSION_NAME">$1<ex>Babylon Toolbar</ex></ph>
       </message>
-      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_EXTENSION" desc="Titlebar of the prompt window for an externally installed extension">
+      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_HEADING_EXTENSION" desc="The heading text (below the title) for the bubble that alerts the user that a new external extension was installed.">
         Another program on your computer added an extension that may change the way Chrome works.
-
-<ph name="EXTENSION_NAME">$1<ex>Babylon Toolbar</ex></ph>
       </message>
-      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_THEME" desc="Titlebar of the prompt window for an externally installed theme">
+      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_HEADING_THEME" desc="The heading text (below the title) for the bubble that alerts the user that a new external theme was installed.">
         Another program on your computer added a theme that may change the way Chrome works.
-
-<ph name="EXTENSION_NAME">$1<ex>Babylon Toolbar</ex></ph>
-      </message>
-      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION" desc="Button on the external install prompt to enable an extension installed by a third party.">
-        Enable extension
-      </message>
-      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP" desc="Button on the external install prompt to enable an extension installed by a third party.">
-        Enable app
-      </message>
-      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME" desc="Button on the external install prompt to enable an extension installed by a third party.">
-        Enable theme
-      </message>
-      <message name="IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON" desc="Button on the external install prompt to remove an extension installed by a third party.">
-        Remove from Chrome
       </message>
 
       <if expr="use_titlecase">
@@ -5950,6 +5962,12 @@
       <message name="IDS_FLAGS_ENABLE_WEBRTC_STUN_ORIGIN_DESCRIPTION" desc="Description of chrome:flags option to turn on Origin header for WebRTC STUN messages">
         When enabled, Stun messages generated by WebRTC will contain the Origin header.
       </message>
+      <message name="IDS_FLAGS_ENABLE_WEBVR_NAME" desc="Name of the 'Enable WebVR' flag.">
+        Enable WebVR
+      </message>
+      <message name="IDS_FLAGS_ENABLE_WEBVR_DESCRIPTION" desc="Description for the flag to enable WebVR APIs.">
+        Enabling this option allows web applications to access experimental Virtual Reality APIs.
+      </message>
       <if expr="is_android">
         <message name="IDS_FLAGS_DISABLE_WEBAUDIO_NAME" desc="Name of the 'Disable WebAudio' lab.">
           Disable WebAudio
@@ -15003,16 +15021,16 @@
           Determines what pages the Reader Mode button is shown on.
         </message>
         <message name="IDS_FLAGS_READER_MODE_HEURISTICS_MARKUP" desc="A choice in dropdown dialog on about:flags page to show the reader mode button with article structured markup">
-          Show the Reader Mode button on pages with article structured markup
+          With article structured markup
         </message>
         <message name="IDS_FLAGS_READER_MODE_HEURISTICS_ADABOOST" desc="A choice in dropdown dialog on about:flags page to show the reader mode button on pages that appear to be long form content">
-          Show the Reader Mode button on pages that appear to be long form content
+          Appears to be an article
         </message>
         <message name="IDS_FLAGS_READER_MODE_HEURISTICS_ALWAYS_OFF" desc="A choice in dropdown dialog on about:flags page to never show the reader mode button">
-          Never show Reader Mode button
+          Never
         </message>
         <message name="IDS_FLAGS_READER_MODE_HEURISTICS_ALWAYS_ON" desc="A choice in dropdown dialog on about:flags page to show the reader mode button on all pages">
-          Show Reader Mode button on all pages
+          Always
         </message>
         <message name="IDS_FLAGS_READER_MODE_EXPERIMENT_NAME" desc="An about:flags experiment for reading mode UI">
           Enable Reader Mode Toolbar Icon
diff --git a/chrome/app/kasko_client.cc b/chrome/app/kasko_client.cc
index 97b0def6..16a165a5 100644
--- a/chrome/app/kasko_client.cc
+++ b/chrome/app/kasko_client.cc
@@ -10,16 +10,12 @@
 
 #include <string>
 
-#include "base/debug/crash_logging.h"
-#include "base/guid.h"
 #include "base/logging.h"
 #include "base/process/process_handle.h"
-#include "base/win/wrapped_window_proc.h"
 #include "breakpad/src/client/windows/common/ipc_protocol.h"
 #include "chrome/app/chrome_watcher_client_win.h"
 #include "chrome/chrome_watcher/chrome_watcher_main_api.h"
 #include "chrome/common/chrome_constants.h"
-#include "chrome/common/crash_keys.h"
 #include "components/crash/app/crash_keys_win.h"
 #include "syzygy/kasko/api/client.h"
 
@@ -28,6 +24,32 @@
 ChromeWatcherClient* g_chrome_watcher_client = nullptr;
 kasko::api::MinidumpType g_minidump_type = kasko::api::SMALL_DUMP_TYPE;
 
+void GetKaskoCrashKeys(const kasko::api::CrashKey** crash_keys,
+                       size_t* crash_key_count) {
+  static_assert(
+      sizeof(kasko::api::CrashKey) == sizeof(google_breakpad::CustomInfoEntry),
+      "CrashKey and CustomInfoEntry structs are not compatible.");
+  static_assert(offsetof(kasko::api::CrashKey, name) ==
+                    offsetof(google_breakpad::CustomInfoEntry, name),
+                "CrashKey and CustomInfoEntry structs are not compatible.");
+  static_assert(offsetof(kasko::api::CrashKey, value) ==
+                    offsetof(google_breakpad::CustomInfoEntry, value),
+                "CrashKey and CustomInfoEntry structs are not compatible.");
+  static_assert(
+      sizeof(reinterpret_cast<kasko::api::CrashKey*>(0)->name) ==
+          sizeof(reinterpret_cast<google_breakpad::CustomInfoEntry*>(0)->name),
+      "CrashKey and CustomInfoEntry structs are not compatible.");
+  static_assert(
+      sizeof(reinterpret_cast<kasko::api::CrashKey*>(0)->value) ==
+          sizeof(reinterpret_cast<google_breakpad::CustomInfoEntry*>(0)->value),
+      "CrashKey and CustomInfoEntry structs are not compatible.");
+
+  *crash_key_count =
+      breakpad::CrashKeysWin::keeper()->custom_info_entries().size();
+  *crash_keys = reinterpret_cast<const kasko::api::CrashKey*>(
+      breakpad::CrashKeysWin::keeper()->custom_info_entries().data());
+}
+
 }  // namespace
 
 KaskoClient::KaskoClient(ChromeWatcherClient* chrome_watcher_client,
@@ -48,52 +70,24 @@
 
 extern "C" void __declspec(dllexport) ReportCrashWithProtobuf(
     EXCEPTION_POINTERS* info, const char* protobuf, size_t protobuf_length) {
-  static_assert(
-      sizeof(kasko::api::CrashKey) == sizeof(google_breakpad::CustomInfoEntry),
-      "CrashKey and CustomInfoEntry structs are not compatible.");
-  static_assert(offsetof(kasko::api::CrashKey, name) ==
-                    offsetof(google_breakpad::CustomInfoEntry, name),
-                "CrashKey and CustomInfoEntry structs are not compatible.");
-  static_assert(offsetof(kasko::api::CrashKey, value) ==
-                    offsetof(google_breakpad::CustomInfoEntry, value),
-                "CrashKey and CustomInfoEntry structs are not compatible.");
-  static_assert(
-      sizeof(reinterpret_cast<kasko::api::CrashKey*>(0)->name) ==
-          sizeof(reinterpret_cast<google_breakpad::CustomInfoEntry*>(0)->name),
-      "CrashKey and CustomInfoEntry structs are not compatible.");
-  static_assert(
-      sizeof(reinterpret_cast<kasko::api::CrashKey*>(0)->value) ==
-          sizeof(reinterpret_cast<google_breakpad::CustomInfoEntry*>(0)->value),
-      "CrashKey and CustomInfoEntry structs are not compatible.");
-
-  // Assign a GUID that can be used to correlate the Kasko report to the
-  // Breakpad report, to verify data consistency.
-  std::string guid = base::GenerateGUID();
-  {
-    base::debug::ScopedCrashKey kasko_guid(crash_keys::kKaskoGuid, guid);
-    size_t crash_key_count =
-        breakpad::CrashKeysWin::keeper()->custom_info_entries().size();
-    const kasko::api::CrashKey* crash_keys =
-        reinterpret_cast<const kasko::api::CrashKey*>(
-            breakpad::CrashKeysWin::keeper()->custom_info_entries().data());
-
-    if (g_chrome_watcher_client &&
-        g_chrome_watcher_client->EnsureInitialized()) {
-      kasko::api::SendReport(info, g_minidump_type, protobuf, protobuf_length,
-                             crash_keys, crash_key_count);
-    }
+  if (g_chrome_watcher_client && g_chrome_watcher_client->EnsureInitialized()) {
+    size_t crash_key_count = 0;
+    const kasko::api::CrashKey* crash_keys = nullptr;
+    GetKaskoCrashKeys(&crash_keys, &crash_key_count);
+    kasko::api::SendReport(info, g_minidump_type, protobuf, protobuf_length,
+                           crash_keys, crash_key_count);
   }
 
-  {
-    base::debug::ScopedCrashKey kasko_equivalent_guid(
-        crash_keys::kKaskoEquivalentGuid, guid);
-    // While Kasko remains experimental, also report via Breakpad.
-    base::win::WinProcExceptionFilter crash_for_exception =
-        reinterpret_cast<base::win::WinProcExceptionFilter>(::GetProcAddress(
-            ::GetModuleHandle(chrome::kBrowserProcessExecutableName),
-            "CrashForException"));
-    crash_for_exception(info);
-  }
+  // The Breakpad integration hooks TerminateProcess. Sidestep it to avoid a
+  // secondary report.
+
+  using TerminateProcessWithoutDumpProc = void(__cdecl*)();
+  TerminateProcessWithoutDumpProc terminate_process_without_dump =
+      reinterpret_cast<TerminateProcessWithoutDumpProc>(::GetProcAddress(
+          ::GetModuleHandle(chrome::kBrowserProcessExecutableName),
+          "TerminateProcessWithoutDump"));
+  CHECK(terminate_process_without_dump);
+  terminate_process_without_dump();
 }
 
 #endif  // defined(KASKO)
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5312a68..e870a50 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -81,7 +81,6 @@
     "//chrome/app:generated_resources_map",
     "//chrome/app/resources:platform_locale_settings",
     "//chrome/app/theme:theme_resources",
-    "//chrome/browser/autocomplete:in_memory_url_index_cache_proto",
     "//chrome/browser/net:encrypted_cert_logger_proto",
     "//chrome/browser/net:probe_message_proto",
     "//chrome/browser/offline_pages:offline_pages_proto",
@@ -276,6 +275,7 @@
       "//components/web_modal",
       "//content/app/resources",
       "//media",
+      "//mojo/application/public/cpp",
       "//mojo/common",
       "//mojo/environment:chromium",
       "//net:extras",
@@ -626,7 +626,8 @@
     deps += [
       "//components/feedback",
       "//device/core",
-      "//device/devices_app:lib",
+      "//device/devices_app/public/cpp",
+      "//device/devices_app/public/cpp:factory",
       "//device/usb",
     ]
   }
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 5375c12..0b247bc 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -104,7 +104,7 @@
   "+courgette",
   "+device/bluetooth",
   "+device/core",
-  "+device/devices_app",
+  "+device/devices_app/public",
   "+device/hid",
   "+device/usb",
   "+device/media_transfer_protocol",
@@ -122,6 +122,7 @@
   "+jni",
   "+media/audio", # For media audio hang monitor.
   "+media/base",  # For media switches
+  "+mojo/application/public/cpp",
   "+policy",  # For generated headers and source
   "+ppapi/c",  # For various types.
   "+ppapi/host",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6b26f7f..1f94e3b8 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -827,7 +827,7 @@
     {"enable-download-notification",
      IDS_FLAGS_ENABLE_DOWNLOAD_NOTIFICATION_NAME,
      IDS_FLAGS_ENABLE_DOWNLOAD_NOTIFICATION_DESCRIPTION,
-     kOsCrOS,
+     kOsDesktop,
      SINGLE_VALUE_TYPE(switches::kEnableDownloadNotification)},
 #if defined(ENABLE_PLUGINS)
     {"allow-nacl-socket-api",
@@ -1992,6 +1992,13 @@
      ENABLE_DISABLE_VALUE_TYPE(switches::kEnableMacViewsNativeAppWindows,
                                switches::kDisableMacViewsNativeAppWindows)},
 #endif
+#if defined(ENABLE_WEBVR)
+    {"enable-webvr",
+     IDS_FLAGS_ENABLE_WEBVR_NAME,
+     IDS_FLAGS_ENABLE_WEBVR_DESCRIPTION,
+     kOsAll,
+     SINGLE_VALUE_TYPE(switches::kEnableWebVR)},
+#endif
     // NOTE: Adding new command-line switches requires adding corresponding
     // entries to enum "LoginCustomFlags" in histograms.xml. See note in
     // histograms.xml and don't forget to run AboutFlagsHistogramTest unit test.
@@ -2176,6 +2183,27 @@
   result->swap(new_enabled_experiments);
 }
 
+// Returns true if none of this experiment's options have been enabled.
+bool IsDefaultValue(
+    const Experiment& experiment,
+    const std::set<std::string>& enabled_experiments) {
+  switch (experiment.type) {
+    case Experiment::SINGLE_VALUE:
+    case Experiment::SINGLE_DISABLE_VALUE:
+      return enabled_experiments.count(experiment.internal_name) == 0;
+    case Experiment::MULTI_VALUE:
+    case Experiment::ENABLE_DISABLE_VALUE:
+      for (int i = 0; i < experiment.num_choices; ++i) {
+        if (enabled_experiments.count(experiment.NameForChoice(i)) > 0)
+          return false;
+      }
+      break;
+    default:
+      NOTREACHED();
+  }
+  return true;
+}
+
 // Returns the Value representing the choice data in the specified experiment.
 base::Value* CreateChoiceData(
     const Experiment& experiment,
@@ -2284,14 +2312,12 @@
     AddOsStrings(experiment.supported_platforms, supported_platforms);
     data->Set("supported_platforms", supported_platforms);
     // True if the switch is not currently passed.
-    bool is_default_value =
-        enabled_experiments.count(experiment.internal_name) == 0;
+    bool is_default_value = IsDefaultValue(experiment, enabled_experiments);
+    data->SetBoolean("is_default", is_default_value);
 
     switch (experiment.type) {
       case Experiment::SINGLE_VALUE:
       case Experiment::SINGLE_DISABLE_VALUE:
-        data->SetBoolean("is_default", is_default_value);
-
         data->SetBoolean(
             "enabled",
             (!is_default_value &&
diff --git a/chrome/browser/android/bookmarks/bookmarks_bridge.cc b/chrome/browser/android/bookmarks/bookmarks_bridge.cc
index f3a6669..d642899 100644
--- a/chrome/browser/android/bookmarks/bookmarks_bridge.cc
+++ b/chrome/browser/android/bookmarks/bookmarks_bridge.cc
@@ -170,7 +170,8 @@
                                                              jlong id,
                                                              jint type) {
   DCHECK(IsLoaded());
-  return CreateJavaBookmark(GetNodeByID(id, type));
+  const BookmarkNode* node = GetNodeByID(id, type);
+  return node ? CreateJavaBookmark(node) : ScopedJavaLocalRef<jobject>();
 }
 
 bool BookmarksBridge::IsDoingExtensiveChanges(JNIEnv* env, jobject obj) {
diff --git a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
index 0cd6280..872e66d 100644
--- a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
+++ b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
@@ -14,11 +14,14 @@
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
+#include "chrome/browser/ui/user_manager.h"
 #include "chrome/browser/web_applications/web_app_mac.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/extensions/extension_metrics.h"
@@ -175,6 +178,13 @@
       base::string16(), base::string16(), std::string());
 }
 
+bool ExtensionAppShimHandler::Delegate::IsProfileLockedForPath(
+    const base::FilePath& path) {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  base::FilePath full_path = profile_manager->user_data_dir().Append(path);
+  return profiles::IsProfileLocked(full_path);
+}
+
 AppWindowList ExtensionAppShimHandler::Delegate::GetWindows(
     Profile* profile,
     const std::string& extension_id) {
@@ -223,6 +233,12 @@
       web_app::ShortcutInfoForExtensionAndProfile(extension, profile));
 }
 
+void ExtensionAppShimHandler::Delegate::LaunchUserManager() {
+  UserManager::Show(base::FilePath(),
+                    profiles::USER_MANAGER_NO_TUTORIAL,
+                    profiles::USER_MANAGER_SELECT_PROFILE_APP_LAUNCHER);
+}
+
 void ExtensionAppShimHandler::Delegate::MaybeTerminate() {
   AppShimHandler::MaybeTerminate();
 }
@@ -416,6 +432,13 @@
     return;
   }
 
+  if (delegate_->IsProfileLockedForPath(profile_path)) {
+    LOG(WARNING) << "Requested profile is locked.  Showing User Manager.";
+    host->OnAppLaunchComplete(APP_SHIM_LAUNCH_PROFILE_LOCKED);
+    delegate_->LaunchUserManager();
+    return;
+  }
+
   Profile* profile = delegate_->ProfileForPath(profile_path);
 
   if (profile) {
diff --git a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h
index f498a485..5e2f9028 100644
--- a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h
+++ b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h
@@ -52,6 +52,7 @@
     virtual Profile* ProfileForPath(const base::FilePath& path);
     virtual void LoadProfileAsync(const base::FilePath& path,
                                   base::Callback<void(Profile*)> callback);
+    virtual bool IsProfileLockedForPath(const base::FilePath& path);
 
     virtual extensions::AppWindowRegistry::AppWindowList GetWindows(
         Profile* profile,
@@ -68,6 +69,7 @@
                            const std::vector<base::FilePath>& files);
     virtual void LaunchShim(Profile* profile,
                             const extensions::Extension* extension);
+    virtual void LaunchUserManager();
 
     virtual void MaybeTerminate();
   };
diff --git a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
index 1df7cd98..31fe992 100644
--- a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
+++ b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
@@ -35,6 +35,7 @@
   MOCK_METHOD2(LoadProfileAsync,
                void(const base::FilePath&,
                     base::Callback<void(Profile*)>));
+  MOCK_METHOD1(IsProfileLockedForPath, bool(const base::FilePath&));
 
   MOCK_METHOD2(GetWindows, AppWindowList(Profile*, const std::string&));
 
@@ -48,6 +49,7 @@
                     const Extension*,
                     const std::vector<base::FilePath>&));
   MOCK_METHOD2(LaunchShim, void(Profile*, const Extension*));
+  MOCK_METHOD0(LaunchUserManager, void());
 
   MOCK_METHOD0(MaybeTerminate, void());
 
@@ -169,10 +171,14 @@
 
     EXPECT_CALL(*delegate_, ProfileExistsForPath(profile_path_a_))
         .WillRepeatedly(Return(true));
+    EXPECT_CALL(*delegate_, IsProfileLockedForPath(profile_path_a_))
+        .WillRepeatedly(Return(false));
     EXPECT_CALL(*delegate_, ProfileForPath(profile_path_a_))
         .WillRepeatedly(Return(&profile_a_));
     EXPECT_CALL(*delegate_, ProfileExistsForPath(profile_path_b_))
         .WillRepeatedly(Return(true));
+    EXPECT_CALL(*delegate_, IsProfileLockedForPath(profile_path_b_))
+        .WillRepeatedly(Return(false));
     EXPECT_CALL(*delegate_, ProfileForPath(profile_path_b_))
         .WillRepeatedly(Return(&profile_b_));
 
@@ -254,6 +260,15 @@
   NormalLaunch(&host_aa_);
 }
 
+TEST_F(ExtensionAppShimHandlerTest, LaunchProfileIsLocked) {
+  // Profile is locked.
+  EXPECT_CALL(*delegate_, IsProfileLockedForPath(profile_path_a_))
+      .WillOnce(Return(true));
+  EXPECT_CALL(host_aa_, OnAppLaunchComplete(APP_SHIM_LAUNCH_PROFILE_LOCKED));
+  EXPECT_CALL(*delegate_, LaunchUserManager());
+  NormalLaunch(&host_aa_);
+}
+
 TEST_F(ExtensionAppShimHandlerTest, LaunchAppNotFound) {
   // App not found.
   EXPECT_CALL(*delegate_, MaybeGetAppExtension(&profile_a_, kTestAppIdA))
diff --git a/chrome/browser/autocomplete/BUILD.gn b/chrome/browser/autocomplete/BUILD.gn
deleted file mode 100644
index 3f092810..0000000
--- a/chrome/browser/autocomplete/BUILD.gn
+++ /dev/null
@@ -1,12 +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.
-
-import("//third_party/protobuf/proto_library.gni")
-
-# GYP version: chrome/chrome_browser.gypi:in_memory_url_index_cache_proto
-proto_library("in_memory_url_index_cache_proto") {
-  sources = [
-    "in_memory_url_index_cache.proto",
-  ]
-}
diff --git a/chrome/browser/autocomplete/autocomplete_controller.cc b/chrome/browser/autocomplete/autocomplete_controller.cc
index 81878abe6..db6d56aa 100644
--- a/chrome/browser/autocomplete/autocomplete_controller.cc
+++ b/chrome/browser/autocomplete/autocomplete_controller.cc
@@ -15,19 +15,17 @@
 #include "base/time/time.h"
 #include "chrome/browser/autocomplete/autocomplete_controller_delegate.h"
 #include "chrome/browser/autocomplete/builtin_provider.h"
-#include "chrome/browser/autocomplete/history_quick_provider.h"
-#include "chrome/browser/autocomplete/history_url_provider.h"
 #include "chrome/browser/autocomplete/in_memory_url_index_factory.h"
 #include "chrome/browser/autocomplete/shortcuts_provider.h"
 #include "chrome/browser/autocomplete/zero_suggest_provider.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "components/omnibox/bookmark_provider.h"
+#include "components/omnibox/history_quick_provider.h"
+#include "components/omnibox/history_url_provider.h"
 #include "components/omnibox/keyword_provider.h"
 #include "components/omnibox/omnibox_field_trial.h"
 #include "components/omnibox/search_provider.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
-#include "content/public/browser/notification_service.h"
 #include "grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -609,12 +607,8 @@
 void AutocompleteController::NotifyChanged(bool notify_default_match) {
   if (delegate_)
     delegate_->OnResultChanged(notify_default_match);
-  if (done_) {
-    content::NotificationService::current()->Notify(
-        chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
-        content::Source<AutocompleteController>(this),
-        content::NotificationService::NoDetails());
-  }
+  if (done_)
+    provider_client_->OnAutocompleteControllerResultReady(this);
 }
 
 void AutocompleteController::CheckIfDone() {
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index 25f97819..f70ad5f9 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service.h"
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -17,6 +18,7 @@
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/common/pref_names.h"
 #include "components/history/core/browser/history_service.h"
+#include "content/public/browser/notification_service.h"
 
 ChromeAutocompleteProviderClient::ChromeAutocompleteProviderClient(
     Profile* profile)
@@ -116,3 +118,11 @@
   DCHECK(image_service);
   image_service->Prefetch(url);
 }
+
+void ChromeAutocompleteProviderClient::OnAutocompleteControllerResultReady(
+    AutocompleteController* controller) {
+  content::NotificationService::current()->Notify(
+      chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
+      content::Source<AutocompleteController>(controller),
+      content::NotificationService::NoDetails());
+}
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
index 00952b89..5cda994 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
@@ -40,6 +40,8 @@
       history::KeywordID keyword_id,
       const base::string16& term) override;
   void PrefetchImage(const GURL& url) override;
+  void OnAutocompleteControllerResultReady(
+      AutocompleteController* controller) override;
 
  private:
   Profile* profile_;
diff --git a/chrome/browser/autocomplete/history_quick_provider_unittest.cc b/chrome/browser/autocomplete/history_quick_provider_unittest.cc
index 9f359eb..4652043 100644
--- a/chrome/browser/autocomplete/history_quick_provider_unittest.cc
+++ b/chrome/browser/autocomplete/history_quick_provider_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 "chrome/browser/autocomplete/history_quick_provider.h"
+#include "components/omnibox/history_quick_provider.h"
 
 #include <algorithm>
 #include <functional>
@@ -18,10 +18,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
-#include "chrome/browser/autocomplete/history_url_provider.h"
-#include "chrome/browser/autocomplete/in_memory_url_index.h"
 #include "chrome/browser/autocomplete/in_memory_url_index_factory.h"
-#include "chrome/browser/autocomplete/url_index_private_data.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/search_engines/chrome_template_url_service_client.h"
@@ -37,6 +34,9 @@
 #include "components/metrics/proto/omnibox_event.pb.h"
 #include "components/omnibox/autocomplete_match.h"
 #include "components/omnibox/autocomplete_result.h"
+#include "components/omnibox/history_url_provider.h"
+#include "components/omnibox/in_memory_url_index.h"
+#include "components/omnibox/url_index_private_data.h"
 #include "components/search_engines/search_terms_data.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
diff --git a/chrome/browser/autocomplete/history_url_provider_unittest.cc b/chrome/browser/autocomplete/history_url_provider_unittest.cc
index cd2ff56d..e87e7e20 100644
--- a/chrome/browser/autocomplete/history_url_provider_unittest.cc
+++ b/chrome/browser/autocomplete/history_url_provider_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 "chrome/browser/autocomplete/history_url_provider.h"
+#include "components/omnibox/history_url_provider.h"
 
 #include <algorithm>
 
@@ -14,7 +14,6 @@
 #include "base/time/time.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
-#include "chrome/browser/autocomplete/history_quick_provider.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/search_engines/chrome_template_url_service_client.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -29,6 +28,7 @@
 #include "components/omnibox/autocomplete_provider.h"
 #include "components/omnibox/autocomplete_provider_listener.h"
 #include "components/omnibox/autocomplete_result.h"
+#include "components/omnibox/history_quick_provider.h"
 #include "components/search_engines/default_search_manager.h"
 #include "components/search_engines/search_terms_data.h"
 #include "components/search_engines/template_url.h"
diff --git a/chrome/browser/autocomplete/in_memory_url_index_factory.cc b/chrome/browser/autocomplete/in_memory_url_index_factory.cc
index e193c74..69a2fdc 100644
--- a/chrome/browser/autocomplete/in_memory_url_index_factory.cc
+++ b/chrome/browser/autocomplete/in_memory_url_index_factory.cc
@@ -6,7 +6,6 @@
 
 #include "base/memory/singleton.h"
 #include "base/prefs/pref_service.h"
-#include "chrome/browser/autocomplete/in_memory_url_index.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
@@ -14,6 +13,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/core/service_access_type.h"
+#include "components/omnibox/in_memory_url_index.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/url_constants.h"
 
diff --git a/chrome/browser/autocomplete/in_memory_url_index_unittest.cc b/chrome/browser/autocomplete/in_memory_url_index_unittest.cc
index 6c2c0e8..dc2e2dc 100644
--- a/chrome/browser/autocomplete/in_memory_url_index_unittest.cc
+++ b/chrome/browser/autocomplete/in_memory_url_index_unittest.cc
@@ -16,8 +16,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/autocomplete/in_memory_url_index.h"
-#include "chrome/browser/autocomplete/url_index_private_data.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/common/chrome_paths.h"
@@ -27,7 +25,9 @@
 #include "components/history/core/browser/history_backend.h"
 #include "components/history/core/browser/history_database.h"
 #include "components/history/core/browser/history_service.h"
+#include "components/omnibox/in_memory_url_index.h"
 #include "components/omnibox/in_memory_url_index_types.h"
+#include "components/omnibox/url_index_private_data.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "sql/transaction.h"
diff --git a/chrome/browser/autocomplete/scored_history_match_unittest.cc b/chrome/browser/autocomplete/scored_history_match_unittest.cc
index cb1e5781..5714bcc 100644
--- a/chrome/browser/autocomplete/scored_history_match_unittest.cc
+++ b/chrome/browser/autocomplete/scored_history_match_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 "chrome/browser/autocomplete/scored_history_match.h"
+#include "components/omnibox/scored_history_match.h"
 
 #include <algorithm>
 
diff --git a/chrome/browser/autocomplete/search_provider_unittest.cc b/chrome/browser/autocomplete/search_provider_unittest.cc
index 2d7f228..e659637 100644
--- a/chrome/browser/autocomplete/search_provider_unittest.cc
+++ b/chrome/browser/autocomplete/search_provider_unittest.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/autocomplete/autocomplete_controller.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
-#include "chrome/browser/autocomplete/history_url_provider.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
@@ -38,6 +37,7 @@
 #include "components/omnibox/autocomplete_match.h"
 #include "components/omnibox/autocomplete_provider.h"
 #include "components/omnibox/autocomplete_provider_listener.h"
+#include "components/omnibox/history_url_provider.h"
 #include "components/omnibox/omnibox_field_trial.h"
 #include "components/omnibox/omnibox_switches.h"
 #include "components/omnibox/suggestion_answer.h"
diff --git a/chrome/browser/autocomplete/shortcuts_backend_factory.cc b/chrome/browser/autocomplete/shortcuts_backend_factory.cc
index 3b8bdfa..fd20c30f 100644
--- a/chrome/browser/autocomplete/shortcuts_backend_factory.cc
+++ b/chrome/browser/autocomplete/shortcuts_backend_factory.cc
@@ -4,11 +4,23 @@
 
 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
 
+#include "base/memory/scoped_ptr.h"
 #include "base/prefs/pref_service.h"
-#include "chrome/browser/autocomplete/shortcuts_backend.h"
+#include "chrome/browser/autocomplete/shortcuts_extensions_manager.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
+#include "chrome/common/chrome_constants.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/omnibox/shortcuts_backend.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace {
+#if defined(ENABLE_EXTENSIONS)
+const char kShortcutsExtensionsManagerKey[] = "ShortcutsExtensionsManager";
+#endif
+}
 
 // static
 scoped_refptr<ShortcutsBackend> ShortcutsBackendFactory::GetForProfile(
@@ -33,22 +45,14 @@
 scoped_refptr<RefcountedKeyedService>
 ShortcutsBackendFactory::BuildProfileForTesting(
     content::BrowserContext* profile) {
-  scoped_refptr<ShortcutsBackend> backend(
-      new ShortcutsBackend(static_cast<Profile*>(profile), false));
-  if (backend->Init())
-    return backend;
-  return NULL;
+  return CreateShortcutsBackend(Profile::FromBrowserContext(profile), false);
 }
 
 // static
 scoped_refptr<RefcountedKeyedService>
 ShortcutsBackendFactory::BuildProfileNoDatabaseForTesting(
     content::BrowserContext* profile) {
-  scoped_refptr<ShortcutsBackend> backend(
-      new ShortcutsBackend(static_cast<Profile*>(profile), true));
-  if (backend->Init())
-    return backend;
-  return NULL;
+  return CreateShortcutsBackend(Profile::FromBrowserContext(profile), true);
 }
 
 ShortcutsBackendFactory::ShortcutsBackendFactory()
@@ -56,6 +60,7 @@
         "ShortcutsBackend",
         BrowserContextDependencyManager::GetInstance()) {
   DependsOn(HistoryServiceFactory::GetInstance());
+  DependsOn(TemplateURLServiceFactory::GetInstance());
 }
 
 ShortcutsBackendFactory::~ShortcutsBackendFactory() {}
@@ -63,13 +68,38 @@
 scoped_refptr<RefcountedKeyedService>
 ShortcutsBackendFactory::BuildServiceInstanceFor(
     content::BrowserContext* profile) const {
-  scoped_refptr<ShortcutsBackend> backend(
-      new ShortcutsBackend(static_cast<Profile*>(profile), false));
-  if (backend->Init())
-    return backend;
-  return NULL;
+  return CreateShortcutsBackend(Profile::FromBrowserContext(profile), false);
 }
 
 bool ShortcutsBackendFactory::ServiceIsNULLWhileTesting() const {
   return true;
 }
+
+void ShortcutsBackendFactory::BrowserContextShutdown(
+    content::BrowserContext* context) {
+#if defined(ENABLE_EXTENSIONS)
+  context->RemoveUserData(kShortcutsExtensionsManagerKey);
+#endif
+
+  RefcountedBrowserContextKeyedServiceFactory::BrowserContextShutdown(context);
+}
+
+// static
+scoped_refptr<ShortcutsBackend> ShortcutsBackendFactory::CreateShortcutsBackend(
+    Profile* profile,
+    bool suppress_db) {
+  scoped_refptr<ShortcutsBackend> backend(new ShortcutsBackend(
+      TemplateURLServiceFactory::GetForProfile(profile),
+      make_scoped_ptr(new UIThreadSearchTermsData(profile)),
+      HistoryServiceFactory::GetForProfile(profile,
+                                           ServiceAccessType::EXPLICIT_ACCESS),
+      content::BrowserThread::GetMessageLoopProxyForThread(
+          content::BrowserThread::DB),
+      profile->GetPath().Append(chrome::kShortcutsDatabaseName), suppress_db));
+#if defined(ENABLE_EXTENSIONS)
+  ShortcutsExtensionsManager* extensions_manager =
+      new ShortcutsExtensionsManager(profile);
+  profile->SetUserData(kShortcutsExtensionsManagerKey, extensions_manager);
+#endif
+  return backend->Init() ? backend : nullptr;
+}
diff --git a/chrome/browser/autocomplete/shortcuts_backend_factory.h b/chrome/browser/autocomplete/shortcuts_backend_factory.h
index 318b070..bad596c0 100644
--- a/chrome/browser/autocomplete/shortcuts_backend_factory.h
+++ b/chrome/browser/autocomplete/shortcuts_backend_factory.h
@@ -44,6 +44,11 @@
   scoped_refptr<RefcountedKeyedService> BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
   bool ServiceIsNULLWhileTesting() const override;
+  void BrowserContextShutdown(content::BrowserContext* context) override;
+
+  static scoped_refptr<ShortcutsBackend> CreateShortcutsBackend(
+      Profile* profile,
+      bool suppress_db);
 };
 
 #endif  // CHROME_BROWSER_AUTOCOMPLETE_SHORTCUTS_BACKEND_FACTORY_H_
diff --git a/chrome/browser/autocomplete/shortcuts_backend_unittest.cc b/chrome/browser/autocomplete/shortcuts_backend_unittest.cc
index 0adbab4..433de776 100644
--- a/chrome/browser/autocomplete/shortcuts_backend_unittest.cc
+++ b/chrome/browser/autocomplete/shortcuts_backend_unittest.cc
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/autocomplete/shortcuts_backend.h"
+#include "components/omnibox/shortcuts_backend.h"
 
 #include "base/files/scoped_temp_dir.h"
 #include "base/message_loop/message_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
-#include "chrome/browser/autocomplete/shortcuts_database.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/omnibox/shortcuts_database.h"
 #include "components/search_engines/template_url_service.h"
 #include "content/public/test/test_browser_thread.h"
 
@@ -61,6 +62,7 @@
 
  protected:
   TestingProfile profile_;
+  UIThreadSearchTermsData search_terms_data_;
 
  private:
   scoped_refptr<ShortcutsBackend> backend_;
@@ -74,6 +76,7 @@
 ShortcutsBackendTest::ShortcutsBackendTest()
     : ui_thread_(content::BrowserThread::UI, &ui_message_loop_),
       db_thread_(content::BrowserThread::DB),
+      search_terms_data_(&profile_),
       load_notified_(false),
       changed_notified_(false) {
 }
@@ -92,7 +95,9 @@
       AutocompleteMatch::ClassificationsFromString(description_class);
   match.search_terms_args.reset(
       new TemplateURLRef::SearchTermsArgs(match.contents));
-  return ShortcutsBackend::MatchToMatchCore(match, &profile_);
+  return ShortcutsBackend::MatchToMatchCore(
+      match, TemplateURLServiceFactory::GetForProfile(&profile_),
+      &search_terms_data_);
 }
 
 void ShortcutsBackendTest::SetSearchProvider() {
@@ -231,8 +236,9 @@
       new TemplateURLRef::SearchTermsArgs(match.fill_into_edit));
 
   ShortcutsDatabase::Shortcut::MatchCore match_core =
-      ShortcutsBackend::MatchToMatchCore(match, &profile_);
-
+      ShortcutsBackend::MatchToMatchCore(
+          match, TemplateURLServiceFactory::GetForProfile(&profile_),
+          &search_terms_data_);
   EXPECT_EQ("http://foo.com/search?bar=franklin+d+roosevelt",
             match_core.destination_url.spec());
   EXPECT_EQ(match.fill_into_edit, match_core.contents);
diff --git a/chrome/browser/autocomplete/shortcuts_database_unittest.cc b/chrome/browser/autocomplete/shortcuts_database_unittest.cc
index 756752a..cc68fdce 100644
--- a/chrome/browser/autocomplete/shortcuts_database_unittest.cc
+++ b/chrome/browser/autocomplete/shortcuts_database_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 "chrome/browser/autocomplete/shortcuts_database.h"
+#include "components/omnibox/shortcuts_database.h"
 
 #include "base/files/scoped_temp_dir.h"
 #include "base/format_macros.h"
diff --git a/chrome/browser/autocomplete/shortcuts_extensions_manager.cc b/chrome/browser/autocomplete/shortcuts_extensions_manager.cc
new file mode 100644
index 0000000..e48d725
--- /dev/null
+++ b/chrome/browser/autocomplete/shortcuts_extensions_manager.cc
@@ -0,0 +1,48 @@
+// 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/autocomplete/shortcuts_extensions_manager.h"
+
+#include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/omnibox/shortcuts_backend.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_source.h"
+
+#if defined(ENABLE_EXTENSIONS)
+#include "extensions/browser/notification_types.h"
+#include "extensions/common/extension.h"
+#endif
+
+ShortcutsExtensionsManager::ShortcutsExtensionsManager(Profile* profile)
+    : profile_(profile) {
+  DCHECK(profile_);
+#if defined(ENABLE_EXTENSIONS)
+  notification_registrar_.Add(
+      this, extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
+      content::Source<Profile>(profile_));
+#endif
+}
+
+ShortcutsExtensionsManager::~ShortcutsExtensionsManager() {}
+
+void ShortcutsExtensionsManager::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+#if defined(ENABLE_EXTENSIONS)
+  DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, type);
+  scoped_refptr<ShortcutsBackend> shortcuts_backend =
+      ShortcutsBackendFactory::GetForProfileIfExists(profile_);
+  if (!shortcuts_backend)
+    return;
+
+  // When an extension is unloaded, we want to remove any Shortcuts associated
+  // with it.
+  shortcuts_backend->DeleteShortcutsBeginningWithURL(
+      content::Details<extensions::UnloadedExtensionInfo>(details)
+          ->extension->url());
+#endif
+}
diff --git a/chrome/browser/autocomplete/shortcuts_extensions_manager.h b/chrome/browser/autocomplete/shortcuts_extensions_manager.h
new file mode 100644
index 0000000..e29c7656
--- /dev/null
+++ b/chrome/browser/autocomplete/shortcuts_extensions_manager.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 CHROME_BROWSER_AUTOCOMPLETE_SHORTCUTS_EXTENSIONS_MANAGER_H_
+#define CHROME_BROWSER_AUTOCOMPLETE_SHORTCUTS_EXTENSIONS_MANAGER_H_
+
+#include "base/supports_user_data.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+class Profile;
+
+// This class manages the removal of shortcuts associated with an extension when
+// that extension is unloaded.
+class ShortcutsExtensionsManager : public base::SupportsUserData::Data,
+                                   public content::NotificationObserver {
+ public:
+  explicit ShortcutsExtensionsManager(Profile* profile);
+  ~ShortcutsExtensionsManager() override;
+
+ private:
+  // content::NotificationObserver:
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override;
+
+  Profile* profile_;
+  content::NotificationRegistrar notification_registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShortcutsExtensionsManager);
+};
+
+#endif  // CHROME_BROWSER_AUTOCOMPLETE_SHORTCUTS_EXTENSIONS_MANAGER_H_
diff --git a/chrome/browser/autocomplete/shortcuts_provider.h b/chrome/browser/autocomplete/shortcuts_provider.h
index d50bdeb82..121b6c0 100644
--- a/chrome/browser/autocomplete/shortcuts_provider.h
+++ b/chrome/browser/autocomplete/shortcuts_provider.h
@@ -10,8 +10,8 @@
 #include <string>
 
 #include "base/gtest_prod_util.h"
-#include "chrome/browser/autocomplete/shortcuts_backend.h"
 #include "components/omnibox/autocomplete_provider.h"
+#include "components/omnibox/shortcuts_backend.h"
 
 class Profile;
 class ShortcutsProviderTest;
diff --git a/chrome/browser/autocomplete/shortcuts_provider_unittest.cc b/chrome/browser/autocomplete/shortcuts_provider_unittest.cc
index 10fbac5bd..0c4b428 100644
--- a/chrome/browser/autocomplete/shortcuts_provider_unittest.cc
+++ b/chrome/browser/autocomplete/shortcuts_provider_unittest.cc
@@ -18,8 +18,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
-#include "chrome/browser/autocomplete/in_memory_url_index.h"
-#include "chrome/browser/autocomplete/shortcuts_backend.h"
 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_service.h"
@@ -29,6 +27,8 @@
 #include "components/omnibox/autocomplete_match.h"
 #include "components/omnibox/autocomplete_provider.h"
 #include "components/omnibox/autocomplete_result.h"
+#include "components/omnibox/in_memory_url_index.h"
+#include "components/omnibox/shortcuts_backend.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_browser_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/autocomplete/zero_suggest_provider.cc b/chrome/browser/autocomplete/zero_suggest_provider.cc
index 5e425a94..4e12588 100644
--- a/chrome/browser/autocomplete/zero_suggest_provider.cc
+++ b/chrome/browser/autocomplete/zero_suggest_provider.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
-#include "chrome/browser/autocomplete/history_url_provider.h"
 #include "chrome/browser/history/top_sites_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -29,6 +28,7 @@
 #include "components/omnibox/autocomplete_input.h"
 #include "components/omnibox/autocomplete_match.h"
 #include "components/omnibox/autocomplete_provider_listener.h"
+#include "components/omnibox/history_url_provider.h"
 #include "components/omnibox/omnibox_field_trial.h"
 #include "components/omnibox/search_provider.h"
 #include "components/pref_registry/pref_registry_syncable.h"
diff --git a/chrome/browser/browser_process_platform_part_chromeos.cc b/chrome/browser/browser_process_platform_part_chromeos.cc
index 12d6ce6a..3fe495e 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.cc
+++ b/chrome/browser/browser_process_platform_part_chromeos.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/login/session/chrome_session_manager.h"
 #include "chrome/browser/chromeos/login/users/chrome_user_manager_impl.h"
-#include "chrome/browser/chromeos/memory/oom_priority_manager.h"
 #include "chrome/browser/chromeos/net/delay_network_call.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -20,6 +19,7 @@
 #include "chrome/browser/chromeos/system/device_disabling_manager.h"
 #include "chrome/browser/chromeos/system/device_disabling_manager_default_delegate.h"
 #include "chrome/browser/chromeos/system/timezone_util.h"
+#include "chrome/browser/memory/oom_priority_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
 #include "chromeos/geolocation/simple_geolocation_provider.h"
@@ -92,11 +92,10 @@
   return session_manager_.get();
 }
 
-chromeos::OomPriorityManager*
-    BrowserProcessPlatformPart::oom_priority_manager() {
+memory::OomPriorityManager* BrowserProcessPlatformPart::oom_priority_manager() {
   DCHECK(CalledOnValidThread());
   if (!oom_priority_manager_.get())
-    oom_priority_manager_.reset(new chromeos::OomPriorityManager());
+    oom_priority_manager_.reset(new memory::OomPriorityManager());
   return oom_priority_manager_.get();
 }
 
diff --git a/chrome/browser/browser_process_platform_part_chromeos.h b/chrome/browser/browser_process_platform_part_chromeos.h
index 6012af9..d764ec2e 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.h
+++ b/chrome/browser/browser_process_platform_part_chromeos.h
@@ -16,7 +16,6 @@
 
 namespace chromeos {
 class ChromeUserManager;
-class OomPriorityManager;
 class ProfileHelper;
 class TimeZoneResolver;
 }
@@ -29,6 +28,10 @@
 }
 }
 
+namespace memory {
+class OomPriorityManager;
+}
+
 namespace policy {
 class BrowserPolicyConnector;
 class BrowserPolicyConnectorChromeOS;
@@ -71,7 +74,7 @@
 
   // Returns the out-of-memory priority manager.
   // Virtual for testing (see TestingBrowserProcessPlatformPart).
-  virtual chromeos::OomPriorityManager* oom_priority_manager();
+  virtual memory::OomPriorityManager* oom_priority_manager();
 
   // Returns the ProfileHelper instance that is used to identify
   // users and their profiles in Chrome OS multi user session.
@@ -107,7 +110,7 @@
   bool created_profile_helper_;
   scoped_ptr<chromeos::ProfileHelper> profile_helper_;
 
-  scoped_ptr<chromeos::OomPriorityManager> oom_priority_manager_;
+  scoped_ptr<memory::OomPriorityManager> oom_priority_manager_;
 
   scoped_ptr<chromeos::system::AutomaticRebootManager>
       automatic_reboot_manager_;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 6bc3356d..1c41f8c 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -12,7 +12,6 @@
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/files/scoped_file.h"
-#include "base/i18n/icu_util.h"
 #include "base/lazy_instance.h"
 #include "base/path_service.h"
 #include "base/prefs/pref_service.h"
@@ -124,8 +123,10 @@
 #include "content/public/common/service_registry.h"
 #include "content/public/common/url_utils.h"
 #include "content/public/common/web_preferences.h"
-#include "device/devices_app/devices_app.h"
+#include "device/devices_app/public/cpp/constants.h"
+#include "device/devices_app/public/cpp/devices_app_factory.h"
 #include "gin/v8_initializer.h"
+#include "mojo/application/public/cpp/application_delegate.h"
 #include "net/base/mime_util.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_options.h"
@@ -610,10 +611,6 @@
 
 ChromeContentBrowserClient::ChromeContentBrowserClient()
     :
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-      v8_natives_fd_(-1),
-      v8_snapshot_fd_(-1),
-#endif  // OS_POSIX && !OS_MACOSX
       weak_factory_(this) {
 #if defined(ENABLE_PLUGINS)
   for (size_t i = 0; i < arraysize(kPredefinedAllowedDevChannelOrigins); ++i)
@@ -1175,32 +1172,6 @@
 
 }  // namespace
 
-// When Chrome is updated on non-Windows platforms, the new files (like
-// V8 natives and snapshot) can have the same names as the previous
-// versions. Since the renderers for an existing Chrome browser process
-// are likely not compatible with the new files, the browser keeps hold
-// of the old files using an open fd. This fd is passed to subprocesses
-// like renderers.  Here we add the flag to tell the subprocesses where
-// to find these file descriptors.
-void ChromeContentBrowserClient::AppendMappedFileCommandLineSwitches(
-    base::CommandLine* command_line) {
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
-  std::string process_type =
-      command_line->GetSwitchValueASCII(switches::kProcessType);
-  if (process_type != switches::kZygoteProcess) {
-    // We want to pass the natives by fd because after an update the file may
-    // be updated, but we want the newly launched renderers to get the old one,
-    // opened by the browser when it started.
-    DCHECK(natives_fd_exists());
-    command_line->AppendSwitch(::switches::kV8NativesPassedByFD);
-    if (snapshot_fd_exists())
-      command_line->AppendSwitch(::switches::kV8SnapshotPassedByFD);
-  }
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // OS_POSIX && !OS_MACOSX
-}
-
 void ChromeContentBrowserClient::AppendExtraCommandLineSwitches(
     base::CommandLine* command_line,
     int child_process_id) {
@@ -2246,24 +2217,6 @@
     const base::CommandLine& command_line,
     int child_process_id,
     FileDescriptorInfo* mappings) {
-#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
-  if (!natives_fd_exists()) {
-    int v8_natives_fd = -1;
-    int v8_snapshot_fd = -1;
-    if (gin::V8Initializer::OpenV8FilesForChildProcesses(&v8_natives_fd,
-                                                         &v8_snapshot_fd)) {
-      v8_natives_fd_.reset(v8_natives_fd);
-      v8_snapshot_fd_.reset(v8_snapshot_fd);
-    }
-  }
-  // V8 can't start up without the source of the natives, but it can
-  // start up (slower) without the snapshot.
-  DCHECK(natives_fd_exists());
-  mappings->Share(kV8NativesDataDescriptor, v8_natives_fd_.get());
-  if (snapshot_fd_exists())
-    mappings->Share(kV8SnapshotDataDescriptor, v8_snapshot_fd_.get());
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-
 #if defined(OS_ANDROID)
   base::FilePath data_path;
   PathService::Get(ui::DIR_RESOURCE_PAKS_ANDROID, &data_path);
@@ -2307,14 +2260,6 @@
   base::FilePath app_data_path;
   PathService::Get(base::DIR_ANDROID_APP_DATA, &app_data_path);
   DCHECK(!app_data_path.empty());
-
-  flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
-  base::FilePath icudata_path =
-      app_data_path.AppendASCII(base::i18n::kIcuDataFileName);
-  base::File icudata_file(icudata_path, flags);
-  DCHECK(icudata_file.IsValid());
-  mappings->Transfer(kAndroidICUDataDescriptor,
-                     base::ScopedFD(icudata_file.TakePlatformFile()));
 #else
   int crash_signal_fd = GetCrashSignalFD(command_line);
   if (crash_signal_fd >= 0) {
@@ -2371,7 +2316,7 @@
     StaticMojoApplicationMap* apps) {
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
   apps->insert(std::make_pair(GURL(device::kDevicesMojoAppUrl),
-                              base::Bind(&device::DevicesApp::CreateDelegate,
+                              base::Bind(&device::DevicesAppFactory::CreateApp,
                                          base::ThreadTaskRunnerHandle::Get())));
 #endif
 }
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 9edd155..d618af528 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -112,8 +112,6 @@
       const std::string& alias_name) override;
   void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                       int child_process_id) override;
-  void AppendMappedFileCommandLineSwitches(
-      base::CommandLine* command_line) override;
   std::string GetApplicationLocale() override;
   std::string GetAcceptLangs(content::BrowserContext* context) override;
   const gfx::ImageSkia* GetDefaultFavicon() override;
@@ -307,13 +305,6 @@
   std::set<std::string> allowed_dev_channel_origins_;
 #endif
 
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-  base::ScopedFD v8_natives_fd_;
-  base::ScopedFD v8_snapshot_fd_;
-  bool natives_fd_exists() { return v8_natives_fd_ != -1; }
-  bool snapshot_fd_exists() { return v8_snapshot_fd_ != -1; }
-#endif  // OS_POSIX && !OS_MACOSX
-
   // Vector of additional ChromeContentBrowserClientParts.
   // Parts are deleted in the reverse order they are added.
   std::vector<ChromeContentBrowserClientParts*> extra_parts_;
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index c6225f0..58e2dbd 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -50,7 +50,6 @@
 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
-#include "chrome/browser/chromeos/memory/oom_priority_manager.h"
 #include "chrome/browser/chromeos/net/network_portal_detector_impl.h"
 #include "chrome/browser/chromeos/net/wake_on_wifi_manager.h"
 #include "chrome/browser/chromeos/options/cert_library.h"
@@ -72,6 +71,7 @@
 #include "chrome/browser/chromeos/upgrade_detector_chromeos.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/memory/oom_priority_manager.h"
 #include "chrome/browser/net/chrome_network_delegate.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index a13ac8e..158b6dc5 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -1584,5 +1584,44 @@
   StartTest();
 }
 
+template <GuestMode M>
+class AudioPlayerBrowserTestBase : public FileManagerBrowserTestBase {
+ public:
+  GuestMode GetGuestModeParam() const override { return M; }
+  const char* GetTestCaseNameParam() const override {
+    return test_case_name_.c_str();
+  }
+
+ protected:
+  const char* GetTestManifestName() const override {
+    return "audio_player_test_manifest.json";
+  }
+
+  void set_test_case_name(const std::string& name) { test_case_name_ = name; }
+
+ private:
+  std::string test_case_name_;
+};
+
+typedef AudioPlayerBrowserTestBase<NOT_IN_GUEST_MODE> AudioPlayerBrowserTest;
+typedef AudioPlayerBrowserTestBase<IN_GUEST_MODE>
+    AudioPlayerBrowserTestInGuestMode;
+
+IN_PROC_BROWSER_TEST_F(AudioPlayerBrowserTest, OpenAudioOnDownloads) {
+  set_test_case_name("openAudioOnDownloads");
+  StartTest();
+}
+
+IN_PROC_BROWSER_TEST_F(AudioPlayerBrowserTestInGuestMode,
+                       OpenAudioOnDownloads) {
+  set_test_case_name("openAudioOnDownloads");
+  StartTest();
+}
+
+IN_PROC_BROWSER_TEST_F(AudioPlayerBrowserTest, OpenAudioOnDrive) {
+  set_test_case_name("openAudioOnDrive");
+  StartTest();
+}
+
 }  // namespace
 }  // namespace file_manager
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index 16b433d..9e77998 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -158,6 +158,7 @@
     ::switches::kVModule,
     ::switches::kEnableWebGLDraftExtensions,
     ::switches::kEnableWebGLImageChromium,
+    ::switches::kEnableWebVR,
 #if defined(ENABLE_WEBRTC)
     ::switches::kDisableWebRtcHWDecoding,
     ::switches::kDisableWebRtcHWEncoding,
diff --git a/chrome/browser/chromeos/platform_keys/key_permissions.cc b/chrome/browser/chromeos/platform_keys/key_permissions.cc
index 7f184a7..e080a63 100644
--- a/chrome/browser/chromeos/platform_keys/key_permissions.cc
+++ b/chrome/browser/chromeos/platform_keys/key_permissions.cc
@@ -8,8 +8,16 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/logging.h"
+#include "base/prefs/pref_service.h"
+#include "base/prefs/scoped_user_pref_update.h"
 #include "base/values.h"
+#include "chrome/common/pref_names.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/pref_registry/pref_registry_syncable.h"
 #include "extensions/browser/state_store.h"
+#include "policy/policy_constants.h"
 
 namespace chromeos {
 
@@ -35,6 +43,25 @@
 const char kStateStoreSignOnce[] = "signOnce";
 const char kStateStoreSignUnlimited[] = "signUnlimited";
 
+// The profile pref prefs::kPlatformKeys stores a dictionary mapping from
+// public key (base64 encoding of an DER-encoded SPKI) to key properties. The
+// currently only key property is the key usage, which can either be undefined
+// or "corporate". If a key is not present in the pref, the default for the key
+// usage is undefined, which in particular means "not for corporate usage".
+// E.g. the entry in the profile pref might look like:
+// "platform_keys" : {
+//   "ABCDEF123" : {
+//     "keyUsage" : "corporate"
+//   },
+//   "abcdef567" : {
+//     "keyUsage" : "corporate"
+//   }
+// }
+const char kPrefKeyUsage[] = "keyUsage";
+const char kPrefKeyUsageCorporate[] = "corporate";
+
+const char kPolicyAllowCorporateKeyUsage[] = "allowCorporateKeyUsage";
+
 }  // namespace
 
 struct KeyPermissions::PermissionsForExtension::KeyEntry {
@@ -51,9 +78,8 @@
   bool sign_once = false;
 
   // True if the key can be used for signing an unlimited number of times.
-  // This permission is granted by the user or by policy to allow the extension
-  // to use the key for signing through the enterprise.platformKeys or
-  // platformKeys API.
+  // This permission is granted by the user to allow the extension to use the
+  // key for signing through the enterprise.platformKeys or platformKeys API.
   // This permission is granted until revoked by the user or the policy.
   bool sign_unlimited = false;
 };
@@ -61,8 +87,15 @@
 KeyPermissions::PermissionsForExtension::PermissionsForExtension(
     const std::string& extension_id,
     scoped_ptr<base::Value> state_store_value,
+    PrefService* profile_prefs,
+    policy::PolicyService* profile_policies,
     KeyPermissions* key_permissions)
-    : extension_id_(extension_id), key_permissions_(key_permissions) {
+    : extension_id_(extension_id),
+      profile_prefs_(profile_prefs),
+      profile_policies_(profile_policies),
+      key_permissions_(key_permissions) {
+  DCHECK(profile_prefs_);
+  DCHECK(profile_policies_);
   DCHECK(key_permissions_);
   if (state_store_value)
     KeyEntriesFromState(*state_store_value);
@@ -78,7 +111,24 @@
 
   KeyEntry* matching_entry = GetStateStoreEntry(public_key_spki_der_b64);
 
-  return matching_entry->sign_once || matching_entry->sign_unlimited;
+  // In any case, we allow the generating extension to use the generated key a
+  // single time for signing arbitrary data. The reason is, that the extension
+  // usually has to sign a certification request containing the public key in
+  // order to obtain a certificate for the key.
+  // That means, once a certificate authority generated a certificate for the
+  // key, the generating extension doesn't have access to the key anymore,
+  // except if explicitly permitted by the administrator.
+  if (matching_entry->sign_once)
+    return true;
+
+  // Usage of corporate keys is solely determined by policy. The user must not
+  // circumvent this decision.
+  if (key_permissions_->IsCorporateKey(public_key_spki_der_b64))
+    return PolicyAllowsCorporateKeyUsage();
+
+  // Only permissions for keys that are not designated for corporate usage are
+  // determined by user decisions.
+  return matching_entry->sign_unlimited;
 }
 
 void KeyPermissions::PermissionsForExtension::SetKeyUsedForSigning(
@@ -114,13 +164,28 @@
 
   matching_entry->sign_once = true;
   WriteToStateStore();
+
+  DictionaryPrefUpdate update(profile_prefs_, prefs::kPlatformKeys);
+
+  scoped_ptr<base::DictionaryValue> new_pref_entry(new base::DictionaryValue);
+  new_pref_entry->SetStringWithoutPathExpansion(kPrefKeyUsage,
+                                                kPrefKeyUsageCorporate);
+
+  update->SetWithoutPathExpansion(public_key_spki_der_b64,
+                                  new_pref_entry.release());
 }
 
 void KeyPermissions::PermissionsForExtension::SetUserGrantedPermission(
     const std::string& public_key_spki_der) {
+  if (!key_permissions_->CanUserGrantPermissionFor(public_key_spki_der)) {
+    LOG(WARNING) << "Tried to grant permission for a key although prohibited "
+                    "(either key is a corporate key or this account is "
+                    "managed).";
+    return;
+  }
+
   std::string public_key_spki_der_b64;
   base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
-
   KeyEntry* matching_entry = GetStateStoreEntry(public_key_spki_der_b64);
 
   if (matching_entry->sign_unlimited) {
@@ -132,6 +197,37 @@
   WriteToStateStore();
 }
 
+bool KeyPermissions::PermissionsForExtension::PolicyAllowsCorporateKeyUsage()
+    const {
+  const policy::PolicyMap& policies = profile_policies_->GetPolicies(
+      policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string()));
+  const base::Value* policy_value =
+      policies.GetValue(policy::key::kKeyPermissions);
+  if (!policy_value)
+    return false;
+
+  const base::DictionaryValue* key_permissions_map = nullptr;
+  policy_value->GetAsDictionary(&key_permissions_map);
+  if (!key_permissions_map) {
+    LOG(ERROR) << "Expected policy to be a dictionary.";
+    return false;
+  }
+
+  const base::DictionaryValue* key_permissions_for_ext = nullptr;
+  key_permissions_map->GetDictionaryWithoutPathExpansion(
+      extension_id_, &key_permissions_for_ext);
+  if (!key_permissions_for_ext)
+    return false;
+
+  bool allow_corporate_key_usage = false;
+  key_permissions_for_ext->GetBooleanWithoutPathExpansion(
+      kPolicyAllowCorporateKeyUsage, &allow_corporate_key_usage);
+
+  VLOG_IF(allow_corporate_key_usage, 2)
+      << "Policy allows usage of corporate keys by extension " << extension_id_;
+  return allow_corporate_key_usage;
+}
+
 void KeyPermissions::PermissionsForExtension::WriteToStateStore() {
   key_permissions_->SetPlatformKeysOfExtension(extension_id_,
                                                KeyEntriesToState());
@@ -215,10 +311,17 @@
 }
 
 KeyPermissions::KeyPermissions(bool profile_is_managed,
+                               PrefService* profile_prefs,
+                               policy::PolicyService* profile_policies,
                                extensions::StateStore* extensions_state_store)
     : profile_is_managed_(profile_is_managed),
+      profile_prefs_(profile_prefs),
+      profile_policies_(profile_policies),
       extensions_state_store_(extensions_state_store),
       weak_factory_(this) {
+  DCHECK(profile_prefs_);
+  DCHECK(extensions_state_store_);
+  DCHECK(!profile_is_managed_ || profile_policies_);
 }
 
 KeyPermissions::~KeyPermissions() {
@@ -234,10 +337,44 @@
 }
 
 bool KeyPermissions::CanUserGrantPermissionFor(
-    const std::string& public_key_spki_der) {
+    const std::string& public_key_spki_der) const {
   // As keys cannot be tagged for non-corporate usage, the user can currently
   // not grant any permissions if the profile is managed.
-  return !profile_is_managed_;
+  if (profile_is_managed_)
+    return false;
+
+  std::string public_key_spki_der_b64;
+  base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
+
+  // If this profile is not managed but we find a corporate key, don't allow
+  // the user to grant permissions.
+  return !IsCorporateKey(public_key_spki_der_b64);
+}
+
+bool KeyPermissions::IsCorporateKey(
+    const std::string& public_key_spki_der_b64) const {
+  const base::DictionaryValue* prefs_entry =
+      GetPrefsEntry(public_key_spki_der_b64);
+  if (prefs_entry) {
+    std::string key_usage;
+    prefs_entry->GetStringWithoutPathExpansion(kPrefKeyUsage, &key_usage);
+    return key_usage == kPrefKeyUsageCorporate;
+  }
+  return false;
+}
+
+void KeyPermissions::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  // For the format of the dictionary see the documentation at kPrefKeyUsage.
+  registry->RegisterDictionaryPref(prefs::kPlatformKeys);
+}
+
+void KeyPermissions::CreatePermissionObjectAndPassToCallback(
+    const std::string& extension_id,
+    const PermissionsCallback& callback,
+    scoped_ptr<base::Value> value) {
+  callback.Run(make_scoped_ptr(new PermissionsForExtension(
+      extension_id, value.Pass(), profile_prefs_, profile_policies_, this)));
 }
 
 void KeyPermissions::SetPlatformKeysOfExtension(const std::string& extension_id,
@@ -246,12 +383,15 @@
       extension_id, kStateStorePlatformKeys, value.Pass());
 }
 
-void KeyPermissions::CreatePermissionObjectAndPassToCallback(
-    const std::string& extension_id,
-    const PermissionsCallback& callback,
-    scoped_ptr<base::Value> value) {
-  callback.Run(make_scoped_ptr(
-      new PermissionsForExtension(extension_id, value.Pass(), this)));
+const base::DictionaryValue* KeyPermissions::GetPrefsEntry(
+    const std::string& public_key_spki_der_b64) const {
+  const base::DictionaryValue* platform_keys =
+      profile_prefs_->GetDictionary(prefs::kPlatformKeys);
+
+  const base::DictionaryValue* key_entry = nullptr;
+  platform_keys->GetDictionaryWithoutPathExpansion(public_key_spki_der_b64,
+                                                   &key_entry);
+  return key_entry;
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/platform_keys/key_permissions.h b/chrome/browser/chromeos/platform_keys/key_permissions.h
index af584e2..69eb403 100644
--- a/chrome/browser/chromeos/platform_keys/key_permissions.h
+++ b/chrome/browser/chromeos/platform_keys/key_permissions.h
@@ -12,7 +12,10 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 
+class PrefService;
+
 namespace base {
+class DictionaryValue;
 class Value;
 }
 
@@ -20,15 +23,53 @@
 class StateStore;
 }
 
+namespace policy {
+class PolicyService;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
 namespace chromeos {
 
 // This class manages permissions for extensions to use private keys through
-// chrome.platformKeys .
-// It handles the following permissions:
-//  * The extension that generated a key has the permission to sign arbitrary
-//    data with that key at most once.
-//  * The user can explicitly grant an extension the permission to sign
-//    arbitrary data with a key an unlimited number of times.
+// chrome.platformKeys or chrome.enterprise.platformKeys .
+// The permission model depends on whether the user account is managed or not.
+//
+// ** If the user account is not managed **
+// The user is under full control of the keys that are generated or imported
+// while the device is not managed.  For that, a user can grant a specific
+// extension the permission to sign arbitrary data with a specific key for an
+// unlimited number of times.
+//
+// ** If the user account is managed **
+// The administrator is in charge of granting access to keys that are meant for
+// corporate usage.
+//
+// As not every key is meant for corporate usage but probably for the user's
+// private usage, this class introduces the concept of tagging keys with the
+// intended purpose of the key. Currently, the only usage that can be assigned
+// to a key is "corporate".
+//
+// Every key that is generated by the chrome.enterprise.platformKeys API (which
+// requires the user account to be managed), is marked for corporate usage.
+// Any key that is generated or imported by other means is currently not marked
+// for corporate usage.
+//
+// The KeyPermissions policy allows the administrator to list exactly the
+// extensions that are allowed to use such corporate keys. Non-corporate keys
+// are not affected. This policy is the only means to grant this permission.
+//
+// ** One-off Permission for the Certification Requests **
+// Independent of the above, the extension that generates a key using the
+// chrome.enterprise.platformKeys API is allowed to sign arbitrary data with the
+// private key for a single time in order to create a certification request.
+// The assumption is that certification requests usually require a signature of
+// data including the public key. So the one-off permission implies that once a
+// certificate authority creates the certificate of the generated key, the
+// generating extension isn't able to use the key anymore except if explicitly
+// permitted by the administrator.
 class KeyPermissions {
  public:
   // Allows querying and modifying permissions and registering keys for a
@@ -41,6 +82,8 @@
     // |KeyPermissions::GetPermissionsForExtension| instead.
     PermissionsForExtension(const std::string& extension_id,
                             scoped_ptr<base::Value> state_store_value,
+                            PrefService* profile_prefs,
+                            policy::PolicyService* profile_policies,
                             KeyPermissions* key_permissions);
 
     ~PermissionsForExtension();
@@ -92,17 +135,26 @@
     KeyPermissions::PermissionsForExtension::KeyEntry* GetStateStoreEntry(
         const std::string& public_key_spki_der_b64);
 
+    bool PolicyAllowsCorporateKeyUsage() const;
+
     const std::string extension_id_;
     std::vector<KeyEntry> state_store_entries_;
+    PrefService* const profile_prefs_;
+    policy::PolicyService* const profile_policies_;
     KeyPermissions* const key_permissions_;
 
     DISALLOW_COPY_AND_ASSIGN(PermissionsForExtension);
   };
 
-  // |extensions_state_store| must not be null and outlive this object.
+  // |profile_prefs| and |extensions_state_store| must not be null and must
+  // outlive this object.
+  // If |profile_is_managed| is false, |profile_policies| is ignored. Otherwise,
+  // |profile_policies| must not be null and must outlive this object.
   // |profile_is_managed| determines the default usage and permissions for
   // keys without explicitly assigned usage.
   KeyPermissions(bool profile_is_managed,
+                 PrefService* profile_prefs,
+                 policy::PolicyService* profile_policies,
                  extensions::StateStore* extensions_state_store);
 
   ~KeyPermissions();
@@ -119,9 +171,13 @@
   // Returns true if the user can grant any permission for |public_key_spki_der|
   // to extensions. |public_key_spki_der| must be the DER of a Subject Public
   // Key Info.
-  bool CanUserGrantPermissionFor(const std::string& public_key_spki_der);
+  bool CanUserGrantPermissionFor(const std::string& public_key_spki_der) const;
+
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
  private:
+  bool IsCorporateKey(const std::string& public_key_spki_der_b64) const;
+
   // Creates a PermissionsForExtension object from |extension_id| and |value|
   // and passes the object to |callback|.
   void CreatePermissionObjectAndPassToCallback(
@@ -133,7 +189,12 @@
   void SetPlatformKeysOfExtension(const std::string& extension_id,
                                   scoped_ptr<base::Value> value);
 
+  const base::DictionaryValue* GetPrefsEntry(
+      const std::string& public_key_spki_der_b64) const;
+
   const bool profile_is_managed_;
+  PrefService* const profile_prefs_;
+  policy::PolicyService* const profile_policies_;
   extensions::StateStore* const extensions_state_store_;
   base::WeakPtrFactory<KeyPermissions> weak_factory_;
 
diff --git a/chrome/browser/chromeos/platform_keys/key_permissions_policy_handler.cc b/chrome/browser/chromeos/platform_keys/key_permissions_policy_handler.cc
new file mode 100644
index 0000000..b28fb7ba
--- /dev/null
+++ b/chrome/browser/chromeos/platform_keys/key_permissions_policy_handler.cc
@@ -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.
+
+#include "chrome/browser/chromeos/platform_keys/key_permissions_policy_handler.h"
+
+#include "components/policy/core/common/schema.h"
+#include "policy/policy_constants.h"
+
+namespace chromeos {
+
+KeyPermissionsPolicyHandler::KeyPermissionsPolicyHandler(
+    const policy::Schema& chrome_schema)
+    : policy::SchemaValidatingPolicyHandler(
+          policy::key::kKeyPermissions,
+          chrome_schema.GetKnownProperty(policy::key::kKeyPermissions),
+          policy::SCHEMA_ALLOW_UNKNOWN) {
+}
+
+void KeyPermissionsPolicyHandler::ApplyPolicySettings(
+    const policy::PolicyMap& /* policies */,
+    PrefValueMap* /* prefs */) {
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/platform_keys/key_permissions_policy_handler.h b/chrome/browser/chromeos/platform_keys/key_permissions_policy_handler.h
new file mode 100644
index 0000000..67d6aac
--- /dev/null
+++ b/chrome/browser/chromeos/platform_keys/key_permissions_policy_handler.h
@@ -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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_KEY_PERMISSIONS_POLICY_HANDLER_H_
+#define CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_KEY_PERMISSIONS_POLICY_HANDLER_H_
+
+#include "base/macros.h"
+#include "components/policy/core/browser/configuration_policy_handler.h"
+
+namespace policy {
+class Schema;
+}
+
+namespace chromeos {
+
+class KeyPermissionsPolicyHandler
+    : public policy::SchemaValidatingPolicyHandler {
+ public:
+  explicit KeyPermissionsPolicyHandler(const policy::Schema& chrome_schema);
+
+  // policy::ConfigurationPolicyHandler:
+  void ApplyPolicySettings(const policy::PolicyMap& policies,
+                           PrefValueMap* prefs) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(KeyPermissionsPolicyHandler);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_KEY_PERMISSIONS_POLICY_HANDLER_H_
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
index 386186b..4b81281 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
@@ -512,10 +512,15 @@
 
 PlatformKeysService::PlatformKeysService(
     bool profile_is_managed,
+    PrefService* profile_prefs,
+    policy::PolicyService* profile_policies,
     content::BrowserContext* browser_context,
     extensions::StateStore* state_store)
     : browser_context_(browser_context),
-      key_permissions_(profile_is_managed, state_store),
+      key_permissions_(profile_is_managed,
+                       profile_prefs,
+                       profile_policies,
+                       state_store),
       weak_factory_(this) {
   DCHECK(browser_context);
   DCHECK(state_store);
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service.h b/chrome/browser/chromeos/platform_keys/platform_keys_service.h
index 6cbae2c..7cd75923 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service.h
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service.h
@@ -18,16 +18,18 @@
 #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "components/keyed_service/core/keyed_service.h"
 
-namespace content {
-class BrowserContext;
-class WebContents;
-}
+class PrefService;
 
 namespace base {
 class ListValue;
 class Value;
 }
 
+namespace content {
+class BrowserContext;
+class WebContents;
+}
+
 namespace extensions {
 class StateStore;
 }
@@ -37,6 +39,10 @@
 typedef std::vector<scoped_refptr<X509Certificate>> CertificateList;
 }
 
+namespace policy {
+class PolicyService;
+}
+
 namespace chromeos {
 
 class PlatformKeysService : public KeyedService {
@@ -77,6 +83,8 @@
   // |browser_context| and |state_store| must not be null and outlive this
   // object.
   explicit PlatformKeysService(bool profile_is_managed,
+                               PrefService* profile_prefs,
+                               policy::PolicyService* profile_policies,
                                content::BrowserContext* browser_context,
                                extensions::StateStore* state_store);
 
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc
index f72a586b..2f529b6f 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/platform_keys_certificate_selector_chromeos.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "extensions/browser/extension_registry.h"
@@ -103,8 +104,12 @@
 
   policy::ProfilePolicyConnector* const policy_connector =
       policy::ProfilePolicyConnectorFactory::GetForBrowserContext(context);
-  PlatformKeysService* const service =
-      new PlatformKeysService(policy_connector->IsManaged(), context, store);
+
+  Profile* const profile = Profile::FromBrowserContext(context);
+
+  PlatformKeysService* const service = new PlatformKeysService(
+      policy_connector->IsManaged(), profile->GetPrefs(),
+      policy_connector->policy_service(), context, store);
 
   service->SetSelectDelegate(make_scoped_ptr(new DefaultSelectDelegate()));
   return service;
diff --git a/chrome/browser/download/download_ui_controller.cc b/chrome/browser/download/download_ui_controller.cc
index eec0f38..f41d2b3 100644
--- a/chrome/browser/download/download_ui_controller.cc
+++ b/chrome/browser/download/download_ui_controller.cc
@@ -8,7 +8,6 @@
 #include "base/command_line.h"
 #include "base/stl_util.h"
 #include "chrome/browser/download/download_item_model.h"
-#include "chrome/browser/download/notification/download_notification_manager.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/common/chrome_switches.h"
@@ -19,6 +18,7 @@
 #if defined(OS_ANDROID)
 #include "content/public/browser/android/download_controller_android.h"
 #else
+#include "chrome/browser/download/notification/download_notification_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/host_desktop.h"
diff --git a/chrome/browser/download/notification/download_notification_browsertest.cc b/chrome/browser/download/notification/download_notification_browsertest.cc
index 54176eb..1556bfd 100644
--- a/chrome/browser/download/notification/download_notification_browsertest.cc
+++ b/chrome/browser/download/notification/download_notification_browsertest.cc
@@ -510,6 +510,26 @@
   EXPECT_FALSE(base::PathExists(GetDownloadPath().Append(filename.BaseName())));
 }
 
+IN_PROC_BROWSER_TEST_F(DownloadNotificationTest, DownloadImageFile) {
+  GURL download_url(embedded_test_server()->GetURL(
+      "/downloads/image-octet-stream.png"));
+
+  content::DownloadTestObserverTerminal download_terminal_observer(
+      GetDownloadManager(browser()), 1u, /* wait_count */
+      content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_IGNORE);
+
+  CreateDownloadForBrowserAndURL(browser(), download_url);
+
+  // Wait for the download completion.
+  download_terminal_observer.WaitForFinished();
+
+  // Waits for download completion.
+  while (GetNotification(notification_id())->image().IsEmpty()) {
+    NotificationUpdateObserver download_change_notification_observer;
+    download_change_notification_observer.Wait();
+  }
+}
+
 IN_PROC_BROWSER_TEST_F(DownloadNotificationTest,
                        CloseNotificationAfterDownload) {
   CreateDownload();
diff --git a/chrome/browser/download/notification/download_notification_item.cc b/chrome/browser/download/notification/download_notification_item.cc
index fd2bad5..714de6f0 100644
--- a/chrome/browser/download/notification/download_notification_item.cc
+++ b/chrome/browser/download/notification/download_notification_item.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/download/notification/download_notification_item.h"
 
+#include "base/files/file_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/download/download_crx_util.h"
 #include "chrome/browser/download/download_item_model.h"
@@ -15,6 +16,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/mime_util/mime_util.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_interrupt_reasons.h"
@@ -22,8 +24,11 @@
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/web_contents.h"
 #include "grit/theme_resources.h"
+#include "net/base/mime_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/image/image.h"
 #include "ui/message_center/message_center.h"
 
 namespace {
@@ -31,12 +36,30 @@
 const char kDownloadNotificationNotifierId[] =
     "chrome://downloads/notification/id-notifier";
 
+// Maximum size of preview image. If the image exceeds this size, don't show the
+// preview image.
+const int64 kMaxImagePreviewSize = 10 * 1024 * 1024;  // 10 MB
+
+std::string ReadNotificationImage(const base::FilePath& file_path) {
+  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
+
+  std::string data;
+  bool ret = base::ReadFileToString(file_path, &data);
+  if (!ret)
+    return std::string();
+
+  DCHECK_LE(data.size(), static_cast<size_t>(kMaxImagePreviewSize));
+
+  return data;
+}
+
 }  // anonymous namespace
 
 DownloadNotificationItem::DownloadNotificationItem(
     content::DownloadItem* item,
     DownloadNotificationManagerForProfile* manager)
-    : item_(item) {
+    : item_(item),
+      weak_factory_(this) {
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
 
   message_center::RichNotificationData data;
@@ -59,10 +82,17 @@
 }
 
 DownloadNotificationItem::~DownloadNotificationItem() {
+  if (image_decode_status_ == IN_PROGRESS)
+    ImageDecoder::Cancel(this);
 }
 
 void DownloadNotificationItem::OnNotificationClose() {
   visible_ = false;
+
+  if (image_decode_status_ == IN_PROGRESS) {
+    image_decode_status_ = NOT_STARTED;
+    ImageDecoder::Cancel(this);
+  }
 }
 
 void DownloadNotificationItem::OnNotificationClick() {
@@ -186,6 +216,8 @@
 
 void DownloadNotificationItem::UpdateNotificationData(
     NotificationUpdateType type) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
   DownloadItemModel model(item_);
   DownloadCommands command(item_);
 
@@ -195,7 +227,7 @@
     notification_->set_message(GetWarningText());
 
     // Show icon.
-    SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_MALICIOUS);
+    SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_MALICIOUS);
   } else {
     notification_->set_title(GetTitle());
     notification_->set_message(model.GetStatusText());
@@ -209,9 +241,9 @@
         notification_->set_progress(item_->PercentComplete());
         if (is_off_the_record) {
           // TODO(yoshiki): Replace the tentative image.
-          SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
+          SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
         } else {
-          SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
+          SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
         }
         break;
       case content::DownloadItem::COMPLETE:
@@ -232,9 +264,9 @@
 
         if (is_off_the_record) {
           // TODO(yoshiki): Replace the tentative image.
-          SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
+          SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_INCOGNITO);
         } else {
-          SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
+          SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING);
         }
         break;
       case content::DownloadItem::CANCELLED:
@@ -257,7 +289,7 @@
         }
 
         notification_->set_progress(0);
-        SetNotificationImage(IDR_DOWNLOAD_NOTIFICATION_WARNING);
+        SetNotificationIcon(IDR_DOWNLOAD_NOTIFICATION_WARNING);
         break;
       case content::DownloadItem::MAX_DOWNLOAD_STATE:  // sentinel
         NOTREACHED();
@@ -278,10 +310,6 @@
   }
   notification_->set_buttons(notification_actions);
 
-  if (item_->IsDone()) {
-    // TODO(yoshiki): If the downloaded file is an image, show the thumbnail.
-  }
-
   if (type == ADD) {
     g_browser_process->notification_ui_manager()->
         Add(*notification_, profile());
@@ -299,6 +327,41 @@
   } else {
     NOTREACHED();
   }
+
+  if (item_->IsDone() && image_decode_status_ == NOT_STARTED) {
+    // TODO(yoshiki): Add an UMA to collect statistics of image file sizes.
+
+    if (item_->GetReceivedBytes() > kMaxImagePreviewSize)
+      return;
+
+    DCHECK(notification_->image().IsEmpty());
+
+    image_decode_status_ = IN_PROGRESS;
+
+    bool maybe_image = false;
+    if (mime_util::IsSupportedImageMimeType(item_->GetMimeType())) {
+      maybe_image = true;
+    } else {
+      std::string mime;
+      base::FilePath::StringType extension_with_dot =
+          item_->GetTargetFilePath().FinalExtension();
+      if (!extension_with_dot.empty() &&
+          net::GetWellKnownMimeTypeFromExtension(extension_with_dot.substr(1),
+                                                 &mime) &&
+          mime_util::IsSupportedImageMimeType(mime)) {
+        maybe_image = true;
+      }
+    }
+
+    if (maybe_image) {
+      base::FilePath file_path = item_->GetFullPath();
+      base::PostTaskAndReplyWithResult(
+          content::BrowserThread::GetBlockingPool(), FROM_HERE,
+          base::Bind(&ReadNotificationImage, file_path),
+          base::Bind(&DownloadNotificationItem::OnImageLoaded,
+                     weak_factory_.GetWeakPtr()));
+    }
+  }
 }
 
 void DownloadNotificationItem::OnDownloadRemoved(content::DownloadItem* item) {
@@ -314,7 +377,7 @@
   item_ = nullptr;
 }
 
-void DownloadNotificationItem::SetNotificationImage(int resource_id) {
+void DownloadNotificationItem::SetNotificationIcon(int resource_id) {
   if (image_resource_id_ == resource_id)
     return;
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
@@ -322,6 +385,28 @@
   notification_->set_icon(bundle.GetImageNamed(image_resource_id_));
 }
 
+void DownloadNotificationItem::OnImageLoaded(const std::string& image_data) {
+  if (image_data.empty())
+    return;
+
+  // TODO(yoshiki): Set option to reduce the image size to supress memory usage.
+  ImageDecoder::Start(this, image_data);
+}
+
+void DownloadNotificationItem::OnImageDecoded(const SkBitmap& decoded_image) {
+  gfx::Image image = gfx::Image::CreateFrom1xBitmap(decoded_image);
+  notification_->set_image(image);
+  image_decode_status_ = DONE;
+  UpdateNotificationData(UPDATE);
+}
+
+void DownloadNotificationItem::OnDecodeImageFailed() {
+  DCHECK(notification_->image().IsEmpty());
+
+  image_decode_status_ = FAILED;
+  UpdateNotificationData(UPDATE);
+}
+
 scoped_ptr<std::vector<DownloadCommands::Command>>
 DownloadNotificationItem::GetExtraActions() const {
   scoped_ptr<std::vector<DownloadCommands::Command>> actions(
diff --git a/chrome/browser/download/notification/download_notification_item.h b/chrome/browser/download/notification/download_notification_item.h
index ad9a7f6..51d710f 100644
--- a/chrome/browser/download/notification/download_notification_item.h
+++ b/chrome/browser/download/notification/download_notification_item.h
@@ -8,11 +8,13 @@
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/download/download_commands.h"
 #include "chrome/browser/download/notification/download_notification.h"
+#include "chrome/browser/image_decoder.h"
 #include "chrome/browser/notifications/notification.h"
 #include "chrome/browser/notifications/notification_delegate.h"
 #include "chrome/browser/notifications/notification_test_util.h"
 #include "content/public/browser/download_item.h"
 #include "grit/theme_resources.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/message_center_observer.h"
 
@@ -20,7 +22,8 @@
 class DownloadNotificationItemTest;
 }
 
-class DownloadNotificationItem : public DownloadNotification {
+class DownloadNotificationItem : public DownloadNotification,
+                                 public ImageDecoder::ImageRequest {
  public:
   DownloadNotificationItem(content::DownloadItem* item,
                            DownloadNotificationManagerForProfile* manager);
@@ -38,6 +41,8 @@
  private:
   friend class test::DownloadNotificationItemTest;
 
+  enum ImageDecodeStatus { NOT_STARTED, IN_PROGRESS, DONE, FAILED, NOT_IMAGE };
+
   enum NotificationUpdateType {
     ADD,
     UPDATE,
@@ -48,7 +53,16 @@
   void CloseNotificationByNonUser();
   void Update();
   void UpdateNotificationData(NotificationUpdateType type);
-  void SetNotificationImage(int resource_id);
+
+  // Set icon of the notification.
+  void SetNotificationIcon(int resource_id);
+
+  // Set preview image of the notification. Must be called on IO thread.
+  void OnImageLoaded(const std::string& image_data);
+
+  // ImageDecoder::ImageRequest overrides:
+  void OnImageDecoded(const SkBitmap& decoded_image) override;
+  void OnDecodeImageFailed() override;
 
   // Returns a short one-line status string for the download.
   base::string16 GetTitle() const;
@@ -79,6 +93,11 @@
   content::DownloadItem* item_;
   scoped_ptr<std::vector<DownloadCommands::Command>> button_actions_;
 
+  // Status of the preview image decode.
+  ImageDecodeStatus image_decode_status_ = NOT_STARTED;
+
+  base::WeakPtrFactory<DownloadNotificationItem> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(DownloadNotificationItem);
 };
 
diff --git a/chrome/browser/download/notification/download_notification_item_unittest.cc b/chrome/browser/download/notification/download_notification_item_unittest.cc
index e59dbeee..464c1ad 100644
--- a/chrome/browser/download/notification/download_notification_item_unittest.cc
+++ b/chrome/browser/download/notification/download_notification_item_unittest.cc
@@ -23,8 +23,16 @@
 
 using testing::NiceMock;
 using testing::Return;
+using testing::ReturnRefOfCopy;
 using testing::_;
 
+namespace {
+
+const base::FilePath::CharType kDownloadItemTargetPathString[] =
+    FILE_PATH_LITERAL("/tmp/TITLE.bin");
+
+}  // anonymouse namespace
+
 namespace test {
 
 class DownloadNotificationItemTest : public testing::Test {
@@ -49,6 +57,7 @@
     download_notification_manager_.reset(
         new DownloadNotificationManagerForProfile(profile_, nullptr));
 
+    base::FilePath download_item_target_path(kDownloadItemTargetPathString);
     download_item_.reset(new NiceMock<content::MockDownloadItem>());
     ON_CALL(*download_item_, GetId()).WillByDefault(Return(12345));
     ON_CALL(*download_item_, GetState())
@@ -56,6 +65,8 @@
     ON_CALL(*download_item_, IsDangerous()).WillByDefault(Return(false));
     ON_CALL(*download_item_, GetFileNameToReportUser())
         .WillByDefault(Return(base::FilePath("TITLE.bin")));
+    ON_CALL(*download_item_, GetTargetFilePath())
+        .WillByDefault(ReturnRefOfCopy(download_item_target_path));
     ON_CALL(*download_item_, GetDangerType())
         .WillByDefault(Return(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
     ON_CALL(*download_item_, IsDone()).WillByDefault(Return(false));
diff --git a/chrome/browser/extensions/api/automation/automation_apitest.cc b/chrome/browser/extensions/api/automation/automation_apitest.cc
index e3c1aff..5f1adcf 100644
--- a/chrome/browser/extensions/api/automation/automation_apitest.cc
+++ b/chrome/browser/extensions/api/automation/automation_apitest.cc
@@ -8,13 +8,15 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/thread_task_runner_handle.h"
-#include "chrome/browser/extensions/api/automation_internal/automation_util.h"
+#include "chrome/browser/accessibility/ax_tree_id_registry.h"
+#include "chrome/browser/extensions/api/automation_internal/automation_event_router.h"
 #include "chrome/browser/extensions/chrome_extension_function.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/api/automation_internal.h"
+#include "chrome/common/extensions/chrome_extension_messages.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/ax_event_notification_details.h"
 #include "content/public/browser/render_widget_host.h"
@@ -99,11 +101,6 @@
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(AutomationApiTest, Unit) {
-  ASSERT_TRUE(RunExtensionSubtest("automation/tests/unit", "unit.html"))
-      << message_;
-}
-
 IN_PROC_BROWSER_TEST_F(AutomationApiTest, GetTreeByTabId) {
   StartEmbeddedTestServer();
   ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "tab_id.html"))
@@ -203,10 +200,9 @@
       << message_;
 }
 
-// Flaky. http://crbug.com/467921
-IN_PROC_BROWSER_TEST_F(AutomationApiTest, DISABLED_Mixins) {
+IN_PROC_BROWSER_TEST_F(AutomationApiTest, Attributes) {
   StartEmbeddedTestServer();
-  ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "mixins.html"))
+  ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "attributes.html"))
       << message_;
 }
 
@@ -216,327 +212,4 @@
       << message_;
 }
 
-
-static const int kPid = 1;
-static const int kTab0Rid = 1;
-static const int kTab1Rid = 2;
-
-using content::BrowserContext;
-
-typedef ui::AXTreeSerializer<const ui::AXNode*> TreeSerializer;
-typedef ui::AXTreeSource<const ui::AXNode*> TreeSource;
-
-#define AX_EVENT_ASSERT_EQUAL ui::AX_EVENT_LOAD_COMPLETE
-#define AX_EVENT_ASSERT_NOT_EQUAL ui::AX_EVENT_ACTIVEDESCENDANTCHANGED
-#define AX_EVENT_IGNORE ui::AX_EVENT_CHILDREN_CHANGED
-#define AX_EVENT_TEST_COMPLETE ui::AX_EVENT_BLUR
-
-// This test is based on ui/accessibility/ax_generated_tree_unittest.cc
-// However, because the tree updates need to be sent to the extension, we can't
-// use a straightforward set of nested loops as that test does, so this class
-// keeps track of where we're up to in our imaginary loops, while the extension
-// function classes below do the work of actually incrementing the state when
-// appropriate.
-// The actual deserialization and comparison happens in the API bindings and the
-// test extension respectively: see
-// c/t/data/extensions/api_test/automation/tests/generated/generated_trees.js
-class TreeSerializationState {
- public:
-  TreeSerializationState()
-#ifdef NDEBUG
-      : tree_size(3),
-#else
-      : tree_size(2),
-#endif
-        generator(tree_size, true),
-        num_trees(generator.UniqueTreeCount()),
-        tree0_version(0),
-        tree1_version(0) {
-  }
-
-  // Serializes tree and sends it as an accessibility event to the extension.
-  void SendDataForTree(const ui::AXTree* tree,
-                       TreeSerializer* serializer,
-                       int routing_id,
-                       BrowserContext* browser_context) {
-    ui::AXTreeUpdate update;
-    serializer->SerializeChanges(tree->root(), &update);
-    SendUpdate(update,
-               ui::AX_EVENT_LAYOUT_COMPLETE,
-               tree->root()->id(),
-               routing_id,
-               browser_context);
-  }
-
-  // Sends the given AXTreeUpdate to the extension as an accessibility event.
-  void SendUpdate(ui::AXTreeUpdate update,
-                  ui::AXEvent event,
-                  int node_id,
-                  int routing_id,
-                  BrowserContext* browser_context) {
-    content::AXEventNotificationDetails detail(update.node_id_to_clear,
-                                               update.nodes,
-                                               event,
-                                               node_id,
-                                               kPid,
-                                               routing_id);
-    std::vector<content::AXEventNotificationDetails> details;
-    details.push_back(detail);
-    automation_util::DispatchAccessibilityEventsToAutomation(
-        details, browser_context, gfx::Vector2d());
-  }
-
-  // Notify the extension bindings to destroy the tree for the given tab
-  // (identified by routing_id)
-  void SendTreeDestroyedEvent(int routing_id, BrowserContext* browser_context) {
-    automation_util::DispatchTreeDestroyedEventToAutomation(
-        kPid, routing_id, browser_context);
-  }
-
-  // Reset tree0 to a new generated tree based on tree0_version, reset
-  // tree0_source accordingly.
-  void ResetTree0() {
-    tree0.reset(new ui::AXSerializableTree);
-    tree0_source.reset(tree0->CreateTreeSource());
-    generator.BuildUniqueTree(tree0_version, tree0.get());
-    if (!serializer0.get())
-      serializer0.reset(new TreeSerializer(tree0_source.get()));
-  }
-
-  // Reset tree0, set up serializer0, send down the initial tree data to create
-  // the tree in the extension
-  void InitializeTree0(BrowserContext* browser_context) {
-    ResetTree0();
-    serializer0->ChangeTreeSourceForTesting(tree0_source.get());
-    serializer0->Reset();
-    SendDataForTree(tree0.get(), serializer0.get(), kTab0Rid, browser_context);
-  }
-
-  // Reset tree1 to a new generated tree based on tree1_version, reset
-  // tree1_source accordingly.
-  void ResetTree1() {
-    tree1.reset(new ui::AXSerializableTree);
-    tree1_source.reset(tree1->CreateTreeSource());
-    generator.BuildUniqueTree(tree1_version, tree1.get());
-    if (!serializer1.get())
-      serializer1.reset(new TreeSerializer(tree1_source.get()));
-  }
-
-  // Reset tree1, set up serializer1, send down the initial tree data to create
-  // the tree in the extension
-  void InitializeTree1(BrowserContext* browser_context) {
-    ResetTree1();
-    serializer1->ChangeTreeSourceForTesting(tree1_source.get());
-    serializer1->Reset();
-    SendDataForTree(tree1.get(), serializer1.get(), kTab1Rid, browser_context);
-  }
-
-  const int tree_size;
-  const ui::TreeGenerator generator;
-
-  // The loop variables: comments indicate which variables in
-  // ax_generated_tree_unittest they correspond to.
-  const int num_trees; // n
-  int tree0_version;   // i
-  int tree1_version;   // j
-  int starting_node;   // k
-
-  // Tree infrastructure; tree0 and tree1 need to be regenerated whenever
-  // tree0_version and tree1_version change, respectively; tree0_source and
-  // tree1_source need to be reset whenever that happens.
-  scoped_ptr<ui::AXSerializableTree> tree0, tree1;
-  scoped_ptr<TreeSource> tree0_source, tree1_source;
-  scoped_ptr<TreeSerializer> serializer0, serializer1;
-
-  // Whether tree0 needs to be destroyed after the extension has performed its
-  // checks
-  bool destroy_tree0;
-};
-
-static TreeSerializationState state;
-
-// Override for chrome.automationInternal.enableTab
-// This fakes out the process and routing IDs for two "tabs", which contain the
-// source and target trees, respectively, and sends down the current tree for
-// the requested tab - tab 1 always has tree1, and tab 0 starts with tree0
-// and then has a series of updates intended to translate tree0 to tree1.
-// Once all the updates have been sent, the extension asserts that both trees
-// are equivalent, and then one or both of the trees are reset to a new version.
-class FakeAutomationInternalEnableTabFunction
-    : public UIThreadExtensionFunction {
- public:
-  FakeAutomationInternalEnableTabFunction() {}
-
-  ExtensionFunction::ResponseAction Run() override {
-    using api::automation_internal::EnableTab::Params;
-    scoped_ptr<Params> params(Params::Create(*args_));
-    EXTENSION_FUNCTION_VALIDATE(params.get());
-    if (!params->args.tab_id.get())
-      return RespondNow(Error("tab_id not specified"));
-    int tab_id = *params->args.tab_id;
-    if (tab_id == 0) {
-      // tab 0 <--> tree0
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(&TreeSerializationState::InitializeTree0,
-                                base::Unretained(&state),
-                                base::Unretained(browser_context())));
-      // TODO(aboxhall): Need to rewrite this test in terms of tree ids.
-      return RespondNow(ArgumentList(
-          api::automation_internal::EnableTab::Results::Create(0)));
-    }
-    if (tab_id == 1) {
-      // tab 1 <--> tree1
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(&TreeSerializationState::InitializeTree1,
-                                base::Unretained(&state),
-                                base::Unretained(browser_context())));
-      return RespondNow(ArgumentList(
-          api::automation_internal::EnableTab::Results::Create(0)));
-    }
-    return RespondNow(Error("Unrecognised tab_id"));
-  }
-};
-
-// Factory method for use in OverrideFunction()
-ExtensionFunction* FakeAutomationInternalEnableTabFunctionFactory() {
-  return new FakeAutomationInternalEnableTabFunction();
-}
-
-// Helper method to serialize a series of updates via source_serializer to
-// transform the tree which source_serializer was initialized from into
-// target_tree, and then trigger the test code to assert the two tabs contain
-// the same tree.
-void TransformTree(TreeSerializer* source_serializer,
-                   ui::AXTree* target_tree,
-                   TreeSource* target_tree_source,
-                   content::BrowserContext* browser_context) {
-  source_serializer->ChangeTreeSourceForTesting(target_tree_source);
-  for (int node_delta = 0; node_delta < state.tree_size; ++node_delta) {
-    int id = 1 + (state.starting_node + node_delta) % state.tree_size;
-    ui::AXTreeUpdate update;
-    source_serializer->SerializeChanges(target_tree->GetFromId(id), &update);
-    bool is_last_update = node_delta == state.tree_size - 1;
-    ui::AXEvent event =
-        is_last_update ? AX_EVENT_ASSERT_EQUAL : AX_EVENT_IGNORE;
-    state.SendUpdate(
-        update, event, target_tree->root()->id(), kTab0Rid, browser_context);
-  }
-}
-
-// Helper method to send a no-op tree update to tab 0 with the given event.
-void SendEvent(ui::AXEvent event, content::BrowserContext* browser_context) {
-  ui::AXTreeUpdate update;
-  ui::AXNode* root = state.tree0->root();
-  state.serializer0->SerializeChanges(root, &update);
-  state.SendUpdate(update, event, root->id(), kTab0Rid, browser_context);
-}
-
-// Override for chrome.automationInternal.performAction
-// This is used as a synchronization mechanism; the general flow is:
-// 1. The extension requests tree0 and tree1 (on tab 0 and tab 1 respectively)
-// 2. FakeAutomationInternalEnableTabFunction sends down the trees
-// 3. When the callback for getTree(0) fires, the extension calls doDefault() on
-//    the root node of tree0, which calls into this class's Run() method.
-// 4. In the normal case, we're in the "inner loop" (iterating over
-//    starting_node). For each value of starting_node, we do the following:
-//    a. Serialize a sequence of updates which should transform tree0 into
-//       tree1. Each of these updates is sent as a childrenChanged event,
-//       except for the last which is sent as a loadComplete event.
-//    b. state.destroy_tree0 is set to true
-//    c. state.starting_node gets incremented
-//    d. The loadComplete event triggers an assertion in the extension.
-//    e. The extension performs another doDefault() on the root node of the
-//       tree.
-//    f. This time, we send a destroy event to tab0, so that the tree can be
-//       reset.
-//    g. The extension is notified of the tree's destruction and requests the
-//       tree for tab 0 again, returning to step 2.
-// 5. When starting_node exceeds state.tree_size, we increment tree0_version if
-//    it would not exceed state.num_trees, or increment tree1_version and reset
-//    tree0_version to 0 otherwise, and reset starting_node to 0.
-//    Then we reset one or both trees as appropriate, and send down destroyed
-//    events similarly, causing the extension to re-request the tree and going
-//    back to step 2 again.
-// 6. When tree1_version has gone through all possible values, we send a blur
-//    event, signaling the extension to call chrome.test.succeed() and finish
-//    the test.
-class FakeAutomationInternalPerformActionFunction
-    : public UIThreadExtensionFunction {
- public:
-  FakeAutomationInternalPerformActionFunction() {}
-
-  ExtensionFunction::ResponseAction Run() override {
-    if (state.destroy_tree0) {
-      // Step 4.f: tell the extension to destroy the tree and re-request it.
-      state.SendTreeDestroyedEvent(kTab0Rid, browser_context());
-      state.destroy_tree0 = false;
-      return RespondNow(NoArguments());
-    }
-
-    TreeSerializer* serializer0 = state.serializer0.get();
-    if (state.starting_node < state.tree_size) {
-      // As a sanity check, if the trees are not equal, assert that they are not
-      // equal before serializing changes.
-      if (state.tree0_version != state.tree1_version)
-        SendEvent(AX_EVENT_ASSERT_NOT_EQUAL, browser_context());
-
-      // Step 4.a: pretend that tree0 turned into tree1, and serialize
-      // a sequence of updates to tab 0 to match.
-      TransformTree(serializer0,
-                    state.tree1.get(),
-                    state.tree1_source.get(),
-                    browser_context());
-
-      // Step 4.b: remember that we need to tell the extension to destroy and
-      // re-request the tree on the next action.
-      state.destroy_tree0 = true;
-
-      // Step 4.c: increment starting_node.
-      state.starting_node++;
-    } else if (state.tree0_version < state.num_trees - 1) {
-      // Step 5: Increment tree0_version and reset starting_node
-      state.tree0_version++;
-      state.starting_node = 0;
-
-      // Step 5: Reset tree0 and tell the extension to destroy and re-request it
-      state.SendTreeDestroyedEvent(kTab0Rid, browser_context());
-    } else if (state.tree1_version < state.num_trees - 1) {
-      // Step 5: Increment tree1_version and reset tree0_version and
-      // starting_node
-      state.tree1_version++;
-      state.tree0_version = 0;
-      state.starting_node = 0;
-
-      // Step 5: Reset tree0 and tell the extension to destroy and re-request it
-      state.SendTreeDestroyedEvent(kTab0Rid, browser_context());
-
-      // Step 5: Reset tree1 and tell the extension to destroy and re-request it
-      state.SendTreeDestroyedEvent(kTab1Rid, browser_context());
-    } else {
-      // Step 6: Send a TEST_COMPLETE (blur) event to signal the extension to
-      // call chrome.test.succeed().
-      SendEvent(AX_EVENT_TEST_COMPLETE, browser_context());
-    }
-
-    return RespondNow(NoArguments());
-  }
-};
-
-// Factory method for use in OverrideFunction()
-ExtensionFunction* FakeAutomationInternalPerformActionFunctionFactory() {
-  return new FakeAutomationInternalPerformActionFunction();
-}
-
-// http://crbug.com/396353
-IN_PROC_BROWSER_TEST_F(AutomationApiTest, DISABLED_GeneratedTree) {
-  ASSERT_TRUE(extensions::ExtensionFunctionDispatcher::OverrideFunction(
-      "automationInternal.enableTab",
-      FakeAutomationInternalEnableTabFunctionFactory));
-  ASSERT_TRUE(extensions::ExtensionFunctionDispatcher::OverrideFunction(
-      "automationInternal.performAction",
-      FakeAutomationInternalPerformActionFunctionFactory));
-  ASSERT_TRUE(RunExtensionSubtest("automation/tests/generated",
-                                  "generated_trees.html")) << message_;
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/automation_internal/automation_event_router.cc b/chrome/browser/extensions/api/automation_internal/automation_event_router.cc
index 5dee32f..bb093f9 100644
--- a/chrome/browser/extensions/api/automation_internal/automation_event_router.cc
+++ b/chrome/browser/extensions/api/automation_internal/automation_event_router.cc
@@ -62,7 +62,8 @@
 
     content::RenderProcessHost* rph =
         content::RenderProcessHost::FromID(listener.process_id);
-    rph->Send(new ExtensionMsg_AccessibilityEvent(listener.routing_id, params));
+    rph->Send(new ExtensionMsg_AccessibilityEvent(listener.routing_id,
+                                                  params));
   }
 }
 
@@ -92,7 +93,8 @@
   auto iter = std::find_if(
       listeners_.begin(),
       listeners_.end(),
-      [listener_process_id, listener_routing_id](AutomationListener& item) {
+      [listener_process_id, listener_routing_id](
+          const AutomationListener& item) {
         return (item.process_id == listener_process_id &&
                 item.routing_id == listener_routing_id);
       });
@@ -131,8 +133,8 @@
   std::remove_if(
       listeners_.begin(),
       listeners_.end(),
-      [process_id](AutomationListener& item) {
-        return item.process_id = process_id;
+      [process_id](const AutomationListener& item) {
+        return item.process_id == process_id;
       });
 }
 
diff --git a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
index 5f2e8c8..5c2ebe85 100644
--- a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
+++ b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/accessibility/ax_tree_id_registry.h"
 #include "chrome/browser/extensions/api/automation_internal/automation_action_adapter.h"
 #include "chrome/browser/extensions/api/automation_internal/automation_event_router.h"
-#include "chrome/browser/extensions/api/automation_internal/automation_util.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -29,6 +28,8 @@
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/permissions/permissions_data.h"
 
@@ -45,6 +46,7 @@
 namespace extensions {
 
 namespace {
+
 const int kDesktopTreeID = 0;
 const char kCannotRequestAutomationOnPage[] =
     "Cannot request automation tree on url \"*\". "
@@ -193,24 +195,41 @@
   void AccessibilityEventReceived(
       const std::vector<content::AXEventNotificationDetails>& details)
       override {
-    automation_util::DispatchAccessibilityEventsToAutomation(
-        details, browser_context_,
-        web_contents()->GetContainerBounds().OffsetFromOrigin());
+    std::vector<content::AXEventNotificationDetails>::const_iterator iter =
+        details.begin();
+    for (; iter != details.end(); ++iter) {
+      const content::AXEventNotificationDetails& event = *iter;
+      int tree_id = AXTreeIDRegistry::GetInstance()->GetOrCreateAXTreeID(
+          event.process_id, event.routing_id);
+      ExtensionMsg_AccessibilityEventParams params;
+      params.tree_id = tree_id;
+      params.id = event.id;
+      params.event_type = event.event_type;
+      params.update.node_id_to_clear = event.node_id_to_clear;
+      params.update.nodes = event.nodes;
+      params.location_offset =
+          web_contents()->GetContainerBounds().OffsetFromOrigin();
+
+      AutomationEventRouter* router = AutomationEventRouter::GetInstance();
+      router->DispatchAccessibilityEvent(params);
+    }
   }
 
   void RenderFrameDeleted(
       content::RenderFrameHost* render_frame_host) override {
-    automation_util::DispatchTreeDestroyedEventToAutomation(
+    int tree_id = AXTreeIDRegistry::GetInstance()->GetOrCreateAXTreeID(
         render_frame_host->GetProcess()->GetID(),
-        render_frame_host->GetRoutingID(),
+        render_frame_host->GetRoutingID());
+    AXTreeIDRegistry::GetInstance()->RemoveAXTreeID(tree_id);
+    AutomationEventRouter::GetInstance()->DispatchTreeDestroyedEvent(
+        tree_id,
         browser_context_);
   }
 
  private:
   friend class content::WebContentsUserData<AutomationWebContentsObserver>;
 
-  AutomationWebContentsObserver(
-      content::WebContents* web_contents)
+  explicit AutomationWebContentsObserver(content::WebContents* web_contents)
       : content::WebContentsObserver(web_contents),
         browser_context_(web_contents->GetBrowserContext()) {}
 
@@ -245,6 +264,7 @@
     if (!contents)
       return RespondNow(Error("No active tab"));
   }
+
   content::RenderFrameHost* rfh = contents->GetMainFrame();
   if (!rfh)
     return RespondNow(Error("Could not enable accessibility for active tab"));
@@ -256,6 +276,7 @@
 
   AutomationWebContentsObserver::CreateForWebContents(contents);
   contents->EnableTreeOnlyAccessibilityMode();
+
   int ax_tree_id = AXTreeIDRegistry::GetInstance()->GetOrCreateAXTreeID(
       rfh->GetProcess()->GetID(), rfh->GetRoutingID());
 
@@ -270,11 +291,12 @@
 }
 
 ExtensionFunction::ResponseAction AutomationInternalEnableFrameFunction::Run() {
-// TODO(dtseng): Limited to desktop tree for now pending out of proc iframes.
+  // TODO(dtseng): Limited to desktop tree for now pending out of proc iframes.
   using api::automation_internal::EnableFrame::Params;
 
   scoped_ptr<Params> params(Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
+
   AXTreeIDRegistry::FrameID frame_id =
       AXTreeIDRegistry::GetInstance()->GetFrameID(params->tree_id);
   content::RenderFrameHost* rfh =
diff --git a/chrome/browser/extensions/api/automation_internal/automation_util.cc b/chrome/browser/extensions/api/automation_internal/automation_util.cc
deleted file mode 100644
index 6af78f1..0000000
--- a/chrome/browser/extensions/api/automation_internal/automation_util.cc
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/api/automation_internal/automation_util.h"
-
-#include <string>
-#include <utility>
-
-#include "base/values.h"
-#include "chrome/browser/accessibility/ax_tree_id_registry.h"
-#include "chrome/common/extensions/api/automation_internal.h"
-#include "extensions/browser/event_router.h"
-#include "ui/accessibility/ax_enums.h"
-#include "ui/accessibility/ax_node_data.h"
-
-namespace extensions {
-
-namespace {
-
-void PopulateNodeData(const ui::AXNodeData& node_data,
-    linked_ptr< api::automation_internal::AXNodeData>& out_node_data) {
-  out_node_data->id = node_data.id;
-  out_node_data->role = ToString(node_data.role);
-
-  uint32 state_pos = 0, state_shifter = node_data.state;
-  while (state_shifter) {
-    if (state_shifter & 1) {
-      out_node_data->state.additional_properties.SetBoolean(
-          ToString(static_cast<ui::AXState>(state_pos)), true);
-    }
-    state_shifter = state_shifter >> 1;
-    state_pos++;
-  }
-
-  out_node_data->location.left = node_data.location.x();
-  out_node_data->location.top = node_data.location.y();
-  out_node_data->location.width = node_data.location.width();
-  out_node_data->location.height = node_data.location.height();
-
-  if (!node_data.bool_attributes.empty()) {
-    out_node_data->bool_attributes.reset(
-        new api::automation_internal::AXNodeData::BoolAttributes());
-    for (size_t i = 0; i < node_data.bool_attributes.size(); ++i) {
-      std::pair<ui::AXBoolAttribute, bool> attr =
-          node_data.bool_attributes[i];
-      out_node_data->bool_attributes->additional_properties.SetBoolean(
-          ToString(attr.first), attr.second);
-    }
-  }
-
-  if (!node_data.float_attributes.empty()) {
-    out_node_data->float_attributes.reset(
-        new api::automation_internal::AXNodeData::FloatAttributes());
-    for (size_t i = 0; i < node_data.float_attributes.size(); ++i) {
-      std::pair<ui::AXFloatAttribute, float> attr =
-          node_data.float_attributes[i];
-      out_node_data->float_attributes->additional_properties.SetDouble(
-          ToString(attr.first), attr.second);
-    }
-  }
-
-  if (!node_data.html_attributes.empty()) {
-    out_node_data->html_attributes.reset(
-        new api::automation_internal::AXNodeData::HtmlAttributes());
-    for (size_t i = 0; i < node_data.html_attributes.size(); ++i) {
-      std::pair<std::string, std::string> attr = node_data.html_attributes[i];
-      out_node_data->html_attributes->additional_properties.SetString(
-          attr.first, attr.second);
-    }
-  }
-
-  if (!node_data.int_attributes.empty()) {
-    out_node_data->int_attributes.reset(
-        new api::automation_internal::AXNodeData::IntAttributes());
-    for (size_t i = 0; i < node_data.int_attributes.size(); ++i) {
-      std::pair<ui::AXIntAttribute, int> attr = node_data.int_attributes[i];
-      out_node_data->int_attributes->additional_properties.SetInteger(
-          ToString(attr.first), attr.second);
-    }
-  }
-
-  if (!node_data.intlist_attributes.empty()) {
-    out_node_data->intlist_attributes.reset(
-        new api::automation_internal::AXNodeData::IntlistAttributes());
-    for (size_t i = 0; i < node_data.intlist_attributes.size(); ++i) {
-      std::pair<ui::AXIntListAttribute, std::vector<int32> > attr =
-          node_data.intlist_attributes[i];
-      base::ListValue* intlist = new base::ListValue();
-      for (size_t j = 0; j < attr.second.size(); ++j)
-        intlist->AppendInteger(attr.second[j]);
-      out_node_data->intlist_attributes->additional_properties.Set(
-          ToString(attr.first), intlist);
-    }
-  }
-
-  if (!node_data.string_attributes.empty()) {
-    out_node_data->string_attributes.reset(
-        new api::automation_internal::AXNodeData::StringAttributes());
-    for (size_t i = 0; i < node_data.string_attributes.size(); ++i) {
-      std::pair<ui::AXStringAttribute, std::string> attr =
-          node_data.string_attributes[i];
-      out_node_data->string_attributes->additional_properties.SetString(
-          ToString(attr.first), attr.second);
-    }
-  }
-
-  for (size_t i = 0; i < node_data.child_ids.size(); ++i) {
-    out_node_data->child_ids.push_back(node_data.child_ids[i]);
-  }
-}
-
-void DispatchEventInternal(content::BrowserContext* context,
-                           const std::string& event_name,
-                           scoped_ptr<base::ListValue> args) {
-  if (context && EventRouter::Get(context)) {
-    scoped_ptr<Event> event(new Event(event_name, args.Pass()));
-    event->restrict_to_browser_context = context;
-    EventRouter::Get(context)->BroadcastEvent(event.Pass());
-  }
-}
-
-}  // namespace
-
-namespace automation_util {
-
-void DispatchAccessibilityEventsToAutomation(
-    const std::vector<content::AXEventNotificationDetails>& details,
-    content::BrowserContext* browser_context,
-    const gfx::Vector2d& location_offset) {
-  using api::automation_internal::AXEventParams;
-  using api::automation_internal::AXTreeUpdate;
-
-  std::vector<content::AXEventNotificationDetails>::const_iterator iter =
-      details.begin();
-  for (; iter != details.end(); ++iter) {
-    const content::AXEventNotificationDetails& event = *iter;
-
-    AXEventParams ax_event_params;
-    ax_event_params.tree_id =
-        AXTreeIDRegistry::GetInstance()->GetOrCreateAXTreeID(event.process_id,
-                                                             event.routing_id);
-    ax_event_params.event_type = ToString(iter->event_type);
-    ax_event_params.target_id = event.id;
-
-    AXTreeUpdate& ax_tree_update = ax_event_params.update;
-    ax_tree_update.node_id_to_clear = event.node_id_to_clear;
-    for (size_t i = 0; i < event.nodes.size(); ++i) {
-      ui::AXNodeData src = event.nodes[i];
-      src.location.Offset(location_offset);
-      linked_ptr<api::automation_internal::AXNodeData> out_node(
-          new api::automation_internal::AXNodeData());
-      PopulateNodeData(src, out_node);
-      ax_tree_update.nodes.push_back(out_node);
-    }
-
-    // TODO(dtseng/aboxhall): Why are we sending only one update at a time? We
-    // should match the behavior from renderer -> browser and send a
-    // collection of tree updates over (to the extension); see
-    // |AccessibilityHostMsg_EventParams| and |AccessibilityHostMsg_Events|.
-    DispatchEventInternal(
-        browser_context,
-        api::automation_internal::OnAccessibilityEvent::kEventName,
-        api::automation_internal::OnAccessibilityEvent::Create(
-            ax_event_params));
-  }
-}
-
-void DispatchTreeDestroyedEventToAutomation(
-    int process_id,
-    int routing_id,
-    content::BrowserContext* browser_context) {
-  int tree_id = AXTreeIDRegistry::GetInstance()->GetOrCreateAXTreeID(
-      process_id, routing_id);
-  DispatchEventInternal(
-      browser_context,
-      api::automation_internal::OnAccessibilityTreeDestroyed::kEventName,
-      api::automation_internal::OnAccessibilityTreeDestroyed::Create(tree_id));
-  AXTreeIDRegistry::GetInstance()->RemoveAXTreeID(tree_id);
-}
-
-}  // namespace automation_util
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/api/automation_internal/automation_util.h b/chrome/browser/extensions/api/automation_internal/automation_util.h
deleted file mode 100644
index 218038b..0000000
--- a/chrome/browser/extensions/api/automation_internal/automation_util.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_API_AUTOMATION_INTERNAL_AUTOMATION_UTIL_H_
-#define CHROME_BROWSER_EXTENSIONS_API_AUTOMATION_INTERNAL_AUTOMATION_UTIL_H_
-
-#include <vector>
-
-#include "chrome/common/extensions/api/automation_internal.h"
-#include "content/public/browser/ax_event_notification_details.h"
-
-namespace content {
-class BrowserContext;
-}  // namespace content
-
-namespace ui {
-struct AXNodeData;
-}  // namespace ui
-
-namespace extensions {
-
-// Shared utility functions for the Automation API.
-namespace automation_util {
-
-// Dispatch events through the Automation API making any necessary conversions
-// from accessibility to Automation types.
-void DispatchAccessibilityEventsToAutomation(
-    const std::vector<content::AXEventNotificationDetails>& details,
-    content::BrowserContext* browser_context,
-    const gfx::Vector2d& location_offset);
-
-void DispatchTreeDestroyedEventToAutomation(
-    int process_id,
-    int routing_id,
-    content::BrowserContext* browser_context);
-}  // namespace automation_util
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_API_AUTOMATION_INTERNAL_AUTOMATION_UTIL_H_
diff --git a/chrome/browser/extensions/api/media_galleries/OWNERS b/chrome/browser/extensions/api/media_galleries/OWNERS
index 3ddd92e0..857971d 100644
--- a/chrome/browser/extensions/api/media_galleries/OWNERS
+++ b/chrome/browser/extensions/api/media_galleries/OWNERS
@@ -1,3 +1,3 @@
-orenb@chromium.org
+reillyg@chromium.org
 thestig@chromium.org
 tommycli@chromium.org
diff --git a/chrome/browser/extensions/api/media_galleries/media_galleries_api.cc b/chrome/browser/extensions/api/media_galleries/media_galleries_api.cc
index 32fd2f1..d01627e 100644
--- a/chrome/browser/extensions/api/media_galleries/media_galleries_api.cc
+++ b/chrome/browser/extensions/api/media_galleries/media_galleries_api.cc
@@ -413,7 +413,12 @@
     const std::string& event_name,
     scoped_ptr<base::ListValue> event_args) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // TODO(tommycli): Remove these CHECKs after fixing https://crbug.com/467627.
+  CHECK(profile_);
   EventRouter* router = EventRouter::Get(profile_);
+  CHECK(router);
+
   if (!router->ExtensionHasEventListener(extension_id, event_name))
     return;
 
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
index 330ebe9d..8d086862 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
@@ -4,6 +4,7 @@
 
 #include <cryptohi.h>
 
+#include "base/json/json_writer.h"
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/stringprintf.h"
@@ -13,6 +14,9 @@
 #include "chrome/browser/chromeos/policy/user_policy_test_helper.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/net/nss_context.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/login/user_names.h"
 #include "content/public/browser/notification_service.h"
@@ -26,6 +30,7 @@
 #include "net/cert/nss_cert_database.h"
 #include "net/cert/test_root_certs.h"
 #include "net/test/cert_test_util.h"
+#include "policy/policy_constants.h"
 
 namespace {
 
@@ -39,8 +44,12 @@
 
 class PlatformKeysTest : public ExtensionApiTest {
  public:
-  PlatformKeysTest(DeviceStatus device_status, UserStatus user_status)
-      : device_status_(device_status), user_status_(user_status) {
+  PlatformKeysTest(DeviceStatus device_status,
+                   UserStatus user_status,
+                   bool key_permission_policy)
+      : device_status_(device_status),
+        user_status_(user_status),
+        key_permission_policy_(key_permission_policy) {
     if (user_status_ != USER_STATUS_UNMANAGED)
       SetupInitialEmptyPolicy();
   }
@@ -97,6 +106,9 @@
 
     base::FilePath extension_path = test_data_dir_.AppendASCII("platform_keys");
     extension_ = LoadExtension(extension_path);
+
+    if (policy_helper_ && key_permission_policy_)
+      SetupKeyPermissionPolicy();
   }
 
   void TearDownOnMainThread() override {
@@ -132,6 +144,28 @@
     return RunExtensionSubtest("", url.spec());
   }
 
+  void RegisterClient1AsCorporateKey() {
+    const extensions::Extension* const fake_gen_extension =
+        LoadExtension(test_data_dir_.AppendASCII("platform_keys_genkey"));
+
+    policy::ProfilePolicyConnector* const policy_connector =
+        policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile());
+
+    extensions::StateStore* const state_store =
+        extensions::ExtensionSystem::Get(profile())->state_store();
+
+    chromeos::KeyPermissions permissions(
+        policy_connector->IsManaged(), profile()->GetPrefs(),
+        policy_connector->policy_service(), state_store);
+
+    base::RunLoop run_loop;
+    permissions.GetPermissionsForExtension(
+        fake_gen_extension->id(),
+        base::Bind(&PlatformKeysTest::GotPermissionsForExtension,
+                   base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
  protected:
   const DeviceStatus device_status_;
   const UserStatus user_status_;
@@ -149,6 +183,43 @@
         base::DictionaryValue() /* empty recommended policy */);
   }
 
+  void SetupKeyPermissionPolicy() {
+    // Set up the test policy that gives |extension_| the permission to access
+    // corporate keys.
+    base::DictionaryValue key_permissions_policy;
+    {
+      scoped_ptr<base::DictionaryValue> cert1_key_permission(
+          new base::DictionaryValue);
+      cert1_key_permission->SetBooleanWithoutPathExpansion(
+          "allowCorporateKeyUsage", true);
+      key_permissions_policy.SetWithoutPathExpansion(
+          extension_->id(), cert1_key_permission.release());
+    }
+
+    std::string key_permissions_policy_str;
+    base::JSONWriter::WriteWithOptions(key_permissions_policy,
+                                       base::JSONWriter::OPTIONS_PRETTY_PRINT,
+                                       &key_permissions_policy_str);
+
+    base::DictionaryValue user_policy;
+    user_policy.SetStringWithoutPathExpansion(policy::key::kKeyPermissions,
+                                              key_permissions_policy_str);
+
+    policy_helper_->UpdatePolicy(
+        user_policy, base::DictionaryValue() /* empty recommended policy */,
+        browser()->profile());
+  }
+
+  void GotPermissionsForExtension(
+      const base::Closure& done_callback,
+      scoped_ptr<chromeos::KeyPermissions::PermissionsForExtension>
+          permissions_for_ext) {
+    std::string client_cert1_spki =
+        chromeos::platform_keys::GetSubjectPublicKeyInfo(client_cert1_);
+    permissions_for_ext->RegisterKeyForCorporateUsage(client_cert1_spki);
+    done_callback.Run();
+  }
+
   void SetupTestCerts(const base::Closure& done_callback,
                       net::NSSCertDatabase* cert_db) {
     SetupTestClientCerts(cert_db);
@@ -197,6 +268,7 @@
                                      done_callback);
   }
 
+  const bool key_permission_policy_;
   scoped_ptr<policy::UserPolicyTestHelper> policy_helper_;
   policy::DevicePolicyCrosTestHelper device_policy_test_helper_;
   scoped_ptr<crypto::ScopedTestSystemNSSKeySlot> test_system_slot_;
@@ -247,7 +319,9 @@
       public ::testing::WithParamInterface<DeviceStatus> {
  public:
   UnmanagedPlatformKeysTest()
-      : PlatformKeysTest(GetParam(), USER_STATUS_UNMANAGED) {}
+      : PlatformKeysTest(GetParam(),
+                         USER_STATUS_UNMANAGED,
+                         false /* unused */) {}
 };
 
 struct Params {
@@ -258,12 +332,24 @@
   UserStatus user_status_;
 };
 
+class ManagedWithPermissionPlatformKeysTest
+    : public PlatformKeysTest,
+      public ::testing::WithParamInterface<Params> {
+ public:
+  ManagedWithPermissionPlatformKeysTest()
+      : PlatformKeysTest(GetParam().device_status_,
+                         GetParam().user_status_,
+                         true /* grant the extension key permission */) {}
+};
+
 class ManagedWithoutPermissionPlatformKeysTest
     : public PlatformKeysTest,
       public ::testing::WithParamInterface<Params> {
  public:
   ManagedWithoutPermissionPlatformKeysTest()
-      : PlatformKeysTest(GetParam().device_status_, GetParam().user_status_) {}
+      : PlatformKeysTest(GetParam().device_status_,
+                         GetParam().user_status_,
+                         false /* do not grant key permission */) {}
 };
 
 }  // namespace
@@ -311,6 +397,20 @@
   ASSERT_TRUE(RunExtensionTest("managedProfile")) << message_;
 }
 
+// A corporate key must not be useable if there is no policy permitting it.
+IN_PROC_BROWSER_TEST_P(ManagedWithoutPermissionPlatformKeysTest,
+                       CorporateKeyAccessBlocked) {
+  RegisterClient1AsCorporateKey();
+
+  // To verify that the user is not prompted for any certificate selection,
+  // set up a delegate that fails on any invocation.
+  GetPlatformKeysService()->SetSelectDelegate(
+      make_scoped_ptr(new TestSelectDelegate(net::CertificateList())));
+
+  ASSERT_TRUE(RunExtensionTest("corporateKeyWithoutPermissionTests"))
+      << message_;
+}
+
 INSTANTIATE_TEST_CASE_P(
     ManagedWithoutPermission,
     ManagedWithoutPermissionPlatformKeysTest,
@@ -318,3 +418,38 @@
         Params(DEVICE_STATUS_ENROLLED, USER_STATUS_MANAGED_AFFILIATED_DOMAIN),
         Params(DEVICE_STATUS_ENROLLED, USER_STATUS_MANAGED_OTHER_DOMAIN),
         Params(DEVICE_STATUS_NOT_ENROLLED, USER_STATUS_MANAGED_OTHER_DOMAIN)));
+
+IN_PROC_BROWSER_TEST_P(ManagedWithPermissionPlatformKeysTest,
+                       PolicyGrantsAccessToCorporateKey) {
+  RegisterClient1AsCorporateKey();
+
+  // Set up the test SelectDelegate to select |client_cert1_| if available for
+  // selection.
+  net::CertificateList certs;
+  certs.push_back(client_cert1_);
+
+  GetPlatformKeysService()->SetSelectDelegate(
+      make_scoped_ptr(new TestSelectDelegate(certs)));
+
+  ASSERT_TRUE(RunExtensionTest("corporateKeyWithPermissionTests")) << message_;
+}
+
+IN_PROC_BROWSER_TEST_P(ManagedWithPermissionPlatformKeysTest,
+                       PolicyDoesGrantAccessToNonCorporateKey) {
+  // The policy grants access to corporate keys but none are available.
+  // As the profile is managed, the user must not be able to grant any
+  // certificate permission. Set up a delegate that fails on any invocation.
+  GetPlatformKeysService()->SetSelectDelegate(
+      make_scoped_ptr(new TestSelectDelegate(net::CertificateList())));
+
+  ASSERT_TRUE(RunExtensionTest("policyDoesGrantAccessToNonCorporateKey"))
+      << message_;
+}
+
+INSTANTIATE_TEST_CASE_P(
+    ManagedWithPermission,
+    ManagedWithPermissionPlatformKeysTest,
+    ::testing::Values(
+        Params(DEVICE_STATUS_ENROLLED, USER_STATUS_MANAGED_AFFILIATED_DOMAIN),
+        Params(DEVICE_STATUS_ENROLLED, USER_STATUS_MANAGED_OTHER_DOMAIN),
+        Params(DEVICE_STATUS_NOT_ENROLLED, USER_STATUS_MANAGED_OTHER_DOMAIN)));
diff --git a/chrome/browser/extensions/extension_request_limiting_throttle_browsertest.cc b/chrome/browser/extensions/extension_request_limiting_throttle_browsertest.cc
index 4cc8efc..be0b0137 100644
--- a/chrome/browser/extensions/extension_request_limiting_throttle_browsertest.cc
+++ b/chrome/browser/extensions/extension_request_limiting_throttle_browsertest.cc
@@ -12,6 +12,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "extensions/browser/extension_throttle_manager.h"
 #include "extensions/test/result_catcher.h"
+#include "net/base/backoff_entry.h"
 #include "net/base/url_util.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -70,6 +71,33 @@
       // checking of the net::LOAD_MAYBE_USER_GESTURE load flag in the manager
       // in order to test the throttling logic.
       manager->SetIgnoreUserGestureLoadFlagForTests(true);
+      scoped_ptr<
+          net::BackoffEntry::Policy> policy(new net::BackoffEntry::Policy{
+          // Number of initial errors (in sequence) to ignore before applying
+          // exponential back-off rules.
+          1,
+
+          // Initial delay for exponential back-off in ms.
+          10 * 60 * 1000,
+
+          // Factor by which the waiting time will be multiplied.
+          10,
+
+          // Fuzzing percentage. ex: 10% will spread requests randomly
+          // between 90%-100% of the calculated time.
+          0.1,
+
+          // Maximum amount of time we are willing to delay our request in ms.
+          15 * 60 * 1000,
+
+          // Time to keep an entry from being discarded even when it
+          // has no significant state, -1 to never discard.
+          -1,
+
+          // Don't use initial delay unless the last request was an error.
+          false,
+      });
+      manager->SetBackoffPolicyForTests(policy.Pass());
     }
     // Requests to 127.0.0.1 bypass throttling, so set up a host resolver rule
     // to use a fake domain.
diff --git a/chrome/browser/extensions/external_install_error.cc b/chrome/browser/extensions/external_install_error.cc
index 306fa30..e94fb5b 100644
--- a/chrome/browser/extensions/external_install_error.cc
+++ b/chrome/browser/extensions/external_install_error.cc
@@ -199,7 +199,9 @@
 }
 
 base::string16 ExternalInstallBubbleAlert::GetBubbleViewTitle() {
-  return prompt_->GetDialogTitle();
+  return l10n_util::GetStringFUTF16(
+      IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_TITLE,
+      base::UTF8ToUTF16(prompt_->extension()->name()));
 }
 
 std::vector<base::string16>
@@ -210,6 +212,14 @@
       ExtensionInstallPrompt::PermissionsType::WITHHELD_PERMISSIONS;
 
   std::vector<base::string16> messages;
+  int heading_id =
+      IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_HEADING_EXTENSION;
+  if (prompt_->extension()->is_app())
+    heading_id = IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_HEADING_APP;
+  else if (prompt_->extension()->is_theme())
+    heading_id = IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_HEADING_THEME;
+  messages.push_back(l10n_util::GetStringUTF16(heading_id));
+
   if (prompt_->GetPermissionCount(regular_permissions)) {
     messages.push_back(prompt_->GetPermissionsHeading(regular_permissions));
     for (size_t i = 0; i < prompt_->GetPermissionCount(regular_permissions);
diff --git a/chrome/browser/external_extension_browsertest.cc b/chrome/browser/external_extension_browsertest.cc
index a2f18de..8b05ec5c 100644
--- a/chrome/browser/external_extension_browsertest.cc
+++ b/chrome/browser/external_extension_browsertest.cc
@@ -3,13 +3,17 @@
 // found in the LICENSE file.
 
 #include "base/command_line.h"
+#include "base/prefs/pref_service.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/google/core/browser/google_switches.h"
+#include "components/search_engines/search_engines_pref_names.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
@@ -28,7 +32,7 @@
   GURL test_url;
 };
 
-}
+}  // namespace
 
 class SearchProviderTest : public InProcessBrowserTest {
  protected:
@@ -45,11 +49,23 @@
     // proxy configured.
     command_line->AppendSwitch(switches::kNoProxyServer);
 
+    // Always point google search to a known, non-secure URL. Normally, this
+    // varies based on locale and is a HTTPS host.
+    command_line->AppendSwitchASCII(
+        switches::kGoogleBaseURL, "http://www.google.com");
+
     // Get the url for the test page.
     search_provider_test_url_ =
         test_server()->GetURL("files/is_search_provider_installed.html");
   }
 
+  void SetUpOnMainThread() override {
+    // Force the country to Canada, which has an installed search provider
+    // that's HTTP.
+    browser()->profile()->GetPrefs()->SetInteger(
+        prefs::kCountryIDAtInstall, ('C' << 8) | 'A');
+  }
+
   IsSearchProviderTestData StartIsSearchProviderInstalledTest(
       Browser* browser,
       const char* host,
@@ -83,11 +99,7 @@
   DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
 };
 
-#if 1
-// Disabled - http://crbug.com/359727 (js has syntax errors which v8 hates)
-#define MAYBE_TestIsSearchProviderInstalled \
-    DISABLED_TestIsSearchProviderInstalled
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
 // This is flaking on XP. See http://crbug.com/159530
 #define MAYBE_TestIsSearchProviderInstalled \
     DISABLED_TestIsSearchProviderInstalled
@@ -97,11 +109,9 @@
 IN_PROC_BROWSER_TEST_F(SearchProviderTest,
                        MAYBE_TestIsSearchProviderInstalled) {
   // Use the default search provider, other installed search provider, and
-  // one not installed as well. (Note that yahoo isn't tested because the
-  // its host name varies a lot for different locales unlike Google and Bing,
-  // which would make the test fail depending on the machine's locale.)
+  // one not installed as well. Note, Ask is used here because it's a HTTP host.
   const char* test_hosts[] = { "www.google.com",
-                               "www.bing.com",
+                               "www.ask.com",
                                "localhost" };
   const char* expected_results[] = { "2",
                                      "1",
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index d2fcf39..240a3fcb 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -139,9 +139,6 @@
 const char kSpdyFieldTrialSpdy4GroupNamePrefix[] = "Spdy4Enabled";
 const char kSpdyFieldTrialParametrizedPrefix[] = "Parametrized";
 
-// Field trial for Cache-Control: stale-while-revalidate directive.
-const char kStaleWhileRevalidateFieldTrialName[] = "StaleWhileRevalidate";
-
 #if defined(OS_MACOSX) && !defined(OS_IOS)
 void ObserveKeychainEvents() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -294,15 +291,6 @@
   return it->second;
 }
 
-// Return true if stale-while-revalidate support should be enabled.
-bool IsStaleWhileRevalidateEnabled(const base::CommandLine& command_line) {
-  if (command_line.HasSwitch(switches::kEnableStaleWhileRevalidate))
-    return true;
-  const std::string group_name =
-      base::FieldTrialList::FindFullName(kStaleWhileRevalidateFieldTrialName);
-  return group_name == "Enabled";
-}
-
 // Parse kUseSpdy command line flag options, which may contain the following:
 //
 //   "off"                      : Disables SPDY support entirely.
@@ -481,7 +469,6 @@
 IOThread::Globals::Globals()
     : system_request_context_leak_checker(this),
       ignore_certificate_errors(false),
-      use_stale_while_revalidate(false),
       testing_fixed_http_port(0),
       testing_fixed_https_port(0),
       enable_user_alternate_protocol_ports(false) {
@@ -799,8 +786,6 @@
   }
   if (command_line.HasSwitch(switches::kIgnoreCertificateErrors))
     globals_->ignore_certificate_errors = true;
-  globals_->use_stale_while_revalidate =
-      IsStaleWhileRevalidateEnabled(command_line);
   if (command_line.HasSwitch(switches::kTestingFixedHttpPort)) {
     globals_->testing_fixed_http_port =
         GetSwitchValueAsInt(command_line, switches::kTestingFixedHttpPort);
@@ -1117,7 +1102,6 @@
   params->network_delegate = globals.system_network_delegate.get();
   params->host_mapping_rules = globals.host_mapping_rules.get();
   params->ignore_certificate_errors = globals.ignore_certificate_errors;
-  params->use_stale_while_revalidate = globals.use_stale_while_revalidate;
   params->testing_fixed_http_port = globals.testing_fixed_http_port;
   params->testing_fixed_https_port = globals.testing_fixed_https_port;
   globals.enable_tcp_fast_open_for_ssl.CopyToIfSet(
@@ -1386,8 +1370,7 @@
   if (command_line.HasSwitch(switches::kDisableQuic))
     return false;
 
-  return data_reduction_proxy::DataReductionProxyParams::
-      IsIncludedInQuicFieldTrial();
+  return data_reduction_proxy::params::IsIncludedInQuicFieldTrial();
 }
 
 // static
diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h
index b165d4a6..2406fe87 100644
--- a/chrome/browser/io_thread.h
+++ b/chrome/browser/io_thread.h
@@ -164,7 +164,6 @@
     scoped_ptr<net::HttpUserAgentSettings> http_user_agent_settings;
     scoped_ptr<net::NetworkQualityEstimator> network_quality_estimator;
     bool ignore_certificate_errors;
-    bool use_stale_while_revalidate;
     uint16 testing_fixed_http_port;
     uint16 testing_fixed_https_port;
     Optional<bool> enable_tcp_fast_open_for_ssl;
diff --git a/chrome/browser/io_thread_unittest.cc b/chrome/browser/io_thread_unittest.cc
index 6f460bd..c7effe12 100644
--- a/chrome/browser/io_thread_unittest.cc
+++ b/chrome/browser/io_thread_unittest.cc
@@ -197,8 +197,7 @@
 TEST_F(IOThreadTest, EnableQuicFromQuicProxyFieldTrialGroup) {
   base::FieldTrialList field_trial_list(new base::MockEntropyProvider());
   base::FieldTrialList::CreateFieldTrial(
-      data_reduction_proxy::DataReductionProxyParams::GetQuicFieldTrialName(),
-      "Enabled");
+      data_reduction_proxy::params::GetQuicFieldTrialName(), "Enabled");
 
   ConfigureQuicGlobals();
   net::HttpNetworkSession::Params params;
diff --git a/chrome/browser/media/router/create_session_request.cc b/chrome/browser/media/router/create_session_request.cc
index af83491..faed210b 100644
--- a/chrome/browser/media/router/create_session_request.cc
+++ b/chrome/browser/media/router/create_session_request.cc
@@ -28,9 +28,10 @@
 CreateSessionRequest::~CreateSessionRequest() {
 }
 
-void CreateSessionRequest::MaybeInvokeSuccessCallback() {
+void CreateSessionRequest::MaybeInvokeSuccessCallback(
+    const MediaRoute::Id& route_id) {
   if (!cb_invoked_) {
-    success_cb_.Run(presentation_info_);
+    success_cb_.Run(presentation_info_, route_id);
     cb_invoked_ = true;
   }
 }
diff --git a/chrome/browser/media/router/create_session_request.h b/chrome/browser/media/router/create_session_request.h
index e698e6e..5bcb71f6 100644
--- a/chrome/browser/media/router/create_session_request.h
+++ b/chrome/browser/media/router/create_session_request.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "chrome/browser/media/router/media_route.h"
 #include "chrome/browser/media/router/media_source.h"
 #include "content/public/browser/presentation_service_delegate.h"
 #include "url/gurl.h"
@@ -27,7 +28,8 @@
 class CreateSessionRequest {
  public:
   using PresentationSessionSuccessCallback =
-      content::PresentationServiceDelegate::PresentationSessionSuccessCallback;
+      base::Callback<void(const content::PresentationSessionInfo&,
+                          const MediaRoute::Id&)>;
   using PresentationSessionErrorCallback =
       content::PresentationServiceDelegate::PresentationSessionErrorCallback;
 
@@ -50,7 +52,7 @@
   // Invokes |success_cb_| or |error_cb_| with the given arguments.
   // These functions can only be invoked once per instance. Further invocations
   // are no-op.
-  void MaybeInvokeSuccessCallback();
+  void MaybeInvokeSuccessCallback(const MediaRoute::Id& route_id);
   void MaybeInvokeErrorCallback(const content::PresentationError& error);
 
  private:
diff --git a/chrome/browser/media/router/create_session_request_unittest.cc b/chrome/browser/media/router/create_session_request_unittest.cc
index 76c74d0ec..8e8590be 100644
--- a/chrome/browser/media/router/create_session_request_unittest.cc
+++ b/chrome/browser/media/router/create_session_request_unittest.cc
@@ -23,7 +23,8 @@
   ~CreateSessionRequestTest() override {}
 
   void OnSuccess(const content::PresentationSessionInfo& expected_info,
-                 const content::PresentationSessionInfo& actual_info) {
+                 const content::PresentationSessionInfo& actual_info,
+                 const MediaRoute::Id& route_id) {
     cb_invoked_ = true;
     EXPECT_EQ(expected_info.presentation_url, actual_info.presentation_url);
     EXPECT_EQ(expected_info.presentation_id, actual_info.presentation_id);
@@ -36,7 +37,8 @@
     EXPECT_EQ(expected_error.message, actual_error.message);
   }
 
-  void FailOnSuccess(const content::PresentationSessionInfo& info) {
+  void FailOnSuccess(const content::PresentationSessionInfo& info,
+    const MediaRoute::Id& route_id) {
     FAIL() << "Success callback should not have been called.";
   }
 
@@ -77,7 +79,7 @@
                  session_info),
       base::Bind(&CreateSessionRequestTest::FailOnError,
                  base::Unretained(this)));
-  context.MaybeInvokeSuccessCallback();
+  context.MaybeInvokeSuccessCallback("routeid");
   // No-op since success callback is already invoked.
   context.MaybeInvokeErrorCallback(content::PresentationError(
       content::PRESENTATION_ERROR_NO_AVAILABLE_SCREENS, "Error message"));
@@ -99,7 +101,7 @@
                  error));
   context.MaybeInvokeErrorCallback(error);
   // No-op since error callback is already invoked.
-  context.MaybeInvokeSuccessCallback();
+  context.MaybeInvokeSuccessCallback("routeid");
   EXPECT_TRUE(cb_invoked_);
 }
 
diff --git a/chrome/browser/media/router/presentation_service_delegate_impl.cc b/chrome/browser/media/router/presentation_service_delegate_impl.cc
index 31784ff..07917ea 100644
--- a/chrome/browser/media/router/presentation_service_delegate_impl.cc
+++ b/chrome/browser/media/router/presentation_service_delegate_impl.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/containers/scoped_ptr_hash_map.h"
+#include "base/containers/small_map.h"
 #include "base/guid.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -41,27 +42,6 @@
     content::PresentationServiceDelegate::PresentationSessionSuccessCallback;
 using RenderFrameHostId = std::pair<int, int>;
 
-// TODO(imcheng): Presentation URL and ID should be obtained from |route|, not
-// from bound values taken from the request.
-void OnJoinRouteResponse(const std::string& presentation_url,
-                         const std::string& presentation_id,
-                         const PresentationSessionSuccessCallback& success_cb,
-                         const PresentationSessionErrorCallback& error_cb,
-                         scoped_ptr<MediaRoute> route,
-                         const std::string& error_text) {
-  if (!route.get()) {
-    error_cb.Run(content::PresentationError(
-        content::PRESENTATION_ERROR_NO_PRESENTATION_FOUND, error_text));
-  } else {
-    DVLOG(1) << "OnJoinRouteResponse: "
-             << "route_id: " << route->media_route_id()
-             << ", presentation URL: " << presentation_url
-             << ", presentation ID: " << presentation_id;
-    success_cb.Run(
-        content::PresentationSessionInfo(presentation_url, presentation_id));
-  }
-}
-
 // Returns the unique identifier for the supplied RenderFrameHost.
 RenderFrameHostId GetRenderFrameHostId(RenderFrameHost* render_frame_host) {
   int render_process_id = render_frame_host->GetProcess()->GetID();
@@ -103,8 +83,14 @@
   std::string GetDefaultPresentationId() const;
   void Reset();
 
-  void OnDefaultPresentationStarted(
-      const content::PresentationSessionInfo& session) const;
+  const MediaRoute::Id GetRouteId(const std::string& presentation_id) const;
+  const std::vector<MediaRoute::Id> GetRouteIds() const;
+  void OnPresentationSessionClosed(const std::string& presentation_id);
+
+  void OnPresentationSessionStarted(
+      bool is_default_presentation,
+      const content::PresentationSessionInfo& session,
+      const MediaRoute::Id& route_id);
   void OnPresentationServiceDelegateDestroyed() const;
 
   void set_delegate_observer(DelegateObserver* observer) {
@@ -115,6 +101,8 @@
   MediaSource GetMediaSourceFromListener(
       content::PresentationScreenAvailabilityListener* listener) const;
 
+  base::SmallMap<std::map<std::string, MediaRoute::Id>>
+      presentation_id_to_route_id_;
   scoped_ptr<content::PresentationSessionInfo> default_presentation_info_;
   scoped_ptr<PresentationMediaSinksObserver> sinks_observer_;
 
@@ -142,12 +130,33 @@
     delegate_observer_->OnDelegateDestroyed();
 }
 
-void PresentationFrame::OnDefaultPresentationStarted(
-    const content::PresentationSessionInfo& session) const {
-  if (delegate_observer_)
+void PresentationFrame::OnPresentationSessionStarted(
+    bool is_default_presentation,
+    const content::PresentationSessionInfo& session,
+    const MediaRoute::Id& route_id) {
+  presentation_id_to_route_id_[session.presentation_id] = route_id;
+  if (is_default_presentation && delegate_observer_)
     delegate_observer_->OnDefaultPresentationStarted(session);
 }
 
+void PresentationFrame::OnPresentationSessionClosed(
+    const std::string& presentation_id) {
+  presentation_id_to_route_id_.erase(presentation_id);
+}
+
+const MediaRoute::Id PresentationFrame::GetRouteId(
+    const std::string& presentation_id) const {
+  auto it = presentation_id_to_route_id_.find(presentation_id);
+  return it != presentation_id_to_route_id_.end() ? it->second : "";
+}
+
+const std::vector<MediaRoute::Id> PresentationFrame::GetRouteIds() const {
+  std::vector<MediaRoute::Id> route_ids;
+  for (const auto& e : presentation_id_to_route_id_)
+    route_ids.push_back(e.second);
+  return route_ids;
+}
+
 bool PresentationFrame::SetScreenAvailabilityListener(
     content::PresentationScreenAvailabilityListener* listener) {
   if (sinks_observer_ && sinks_observer_->listener() == listener) {
@@ -174,6 +183,7 @@
 }
 
 void PresentationFrame::Reset() {
+  presentation_id_to_route_id_.clear();
   sinks_observer_.reset();
   default_presentation_info_.reset();
 }
@@ -235,9 +245,19 @@
   std::string GetDefaultPresentationId(
       const RenderFrameHostId& render_frame_host_id) const;
 
-  void OnDefaultPresentationStarted(
+  void OnPresentationSessionStarted(
       const RenderFrameHostId& render_frame_host_id,
-      const content::PresentationSessionInfo& session) const;
+      bool is_default_presentation,
+      const content::PresentationSessionInfo& session,
+      const MediaRoute::Id& route_id);
+
+  void OnPresentationSessionClosed(
+      const RenderFrameHostId& render_frame_host_id,
+      const std::string& presentation_id);
+  const MediaRoute::Id GetRouteId(const RenderFrameHostId& render_frame_host_id,
+                                  const std::string& presentation_id) const;
+  const std::vector<MediaRoute::Id> GetRouteIds(
+      const RenderFrameHostId& render_frame_host_id) const;
 
  private:
   PresentationFrame* GetOrAddPresentationFrame(
@@ -266,12 +286,38 @@
     frame.second->OnPresentationServiceDelegateDestroyed();
 }
 
-void PresentationFrameManager::OnDefaultPresentationStarted(
+void PresentationFrameManager::OnPresentationSessionStarted(
     const RenderFrameHostId& render_frame_host_id,
-    const content::PresentationSessionInfo& session) const {
+    bool is_default_presentation,
+    const content::PresentationSessionInfo& session,
+    const MediaRoute::Id& route_id) {
   auto presentation_frame = presentation_frames_.get(render_frame_host_id);
   if (presentation_frame)
-    presentation_frame->OnDefaultPresentationStarted(session);
+    presentation_frame->OnPresentationSessionStarted(is_default_presentation,
+                                                     session, route_id);
+}
+
+void PresentationFrameManager::OnPresentationSessionClosed(
+    const RenderFrameHostId& render_frame_host_id,
+    const std::string& presentation_id) {
+  auto presentation_frame = presentation_frames_.get(render_frame_host_id);
+  if (presentation_frame)
+    presentation_frame->OnPresentationSessionClosed(presentation_id);
+}
+
+const MediaRoute::Id PresentationFrameManager::GetRouteId(
+    const RenderFrameHostId& render_frame_host_id,
+    const std::string& presentation_id) const {
+  auto presentation_frame = presentation_frames_.get(render_frame_host_id);
+  return presentation_frame ? presentation_frame->GetRouteId(presentation_id)
+                            : "";
+}
+
+const std::vector<MediaRoute::Id> PresentationFrameManager::GetRouteIds(
+    const RenderFrameHostId& render_frame_host_id) const {
+  auto presentation_frame = presentation_frames_.get(render_frame_host_id);
+  return presentation_frame ? presentation_frame->GetRouteIds()
+                            : std::vector<MediaRoute::Id>();
 }
 
 bool PresentationFrameManager::SetScreenAvailabilityListener(
@@ -459,6 +505,45 @@
   }
 }
 
+void PresentationServiceDelegateImpl::OnJoinRouteResponse(
+    int render_process_id,
+    int render_frame_id,
+    const content::PresentationSessionInfo& session,
+    const PresentationSessionSuccessCallback& success_cb,
+    const PresentationSessionErrorCallback& error_cb,
+    scoped_ptr<MediaRoute> route,
+    const std::string& error_text) {
+  if (!route.get()) {
+    error_cb.Run(content::PresentationError(
+        content::PRESENTATION_ERROR_NO_PRESENTATION_FOUND, error_text));
+  } else {
+    DVLOG(1) << "OnJoinRouteResponse: "
+             << "route_id: " << route->media_route_id()
+             << ", presentation URL: " << session.presentation_url
+             << ", presentation ID: " << session.presentation_id;
+    frame_manager_->OnPresentationSessionStarted(
+        RenderFrameHostId(render_process_id, render_frame_id), false, session,
+        route->media_route_id());
+    success_cb.Run(session);
+  }
+}
+
+void PresentationServiceDelegateImpl::OnStartSessionSucceeded(
+    int render_process_id,
+    int render_frame_id,
+    const PresentationSessionSuccessCallback& success_cb,
+    const content::PresentationSessionInfo& new_session,
+    const MediaRoute::Id& route_id) {
+  DVLOG(1) << "OnStartSessionSucceeded: "
+           << "route_id: " << route_id
+           << ", presentation URL: " << new_session.presentation_url
+           << ", presentation ID: " << new_session.presentation_id;
+  frame_manager_->OnPresentationSessionStarted(
+      RenderFrameHostId(render_process_id, render_frame_id), false, new_session,
+      route_id);
+  success_cb.Run(new_session);
+}
+
 void PresentationServiceDelegateImpl::StartSession(
     int render_process_id,
     int render_frame_id,
@@ -482,7 +567,11 @@
     final_presentation_id = base::GenerateGUID();
   scoped_ptr<CreateSessionRequest> context(new CreateSessionRequest(
       presentation_url, final_presentation_id,
-      GetLastCommittedURLForFrame(render_frame_host_id), success_cb, error_cb));
+      GetLastCommittedURLForFrame(render_frame_host_id),
+      base::Bind(&PresentationServiceDelegateImpl::OnStartSessionSucceeded,
+                 weak_factory_.GetWeakPtr(), render_process_id, render_frame_id,
+                 success_cb),
+      error_cb));
   // NOTE: Currently this request is ignored if a dialog is already open, e.g.
   // via browser action. In practice, this should rarely happen, but log
   // an error message in case it does.
@@ -510,8 +599,11 @@
       GetLastCommittedURLForFrame(
           RenderFrameHostId(render_process_id, render_frame_id)).GetOrigin(),
       SessionTabHelper::IdForTab(web_contents_),
-      base::Bind(&OnJoinRouteResponse, presentation_url, presentation_id,
-                 success_cb, error_cb));
+      base::Bind(
+          &PresentationServiceDelegateImpl::OnJoinRouteResponse,
+          weak_factory_.GetWeakPtr(), render_process_id, render_frame_id,
+          content::PresentationSessionInfo(presentation_url, presentation_id),
+          success_cb, error_cb));
 }
 
 void PresentationServiceDelegateImpl::ListenForSessionMessages(
@@ -542,10 +634,12 @@
   RenderFrameHostId render_frame_host_id(GetRenderFrameHostId(main_frame));
   // TODO(imcheng): Pass in valid default presentation ID once it is
   // available from MediaRoute URN. BUG=493365
-  frame_manager_->OnDefaultPresentationStarted(
-      render_frame_host_id,
+  std::string presentation_id;
+  frame_manager_->OnPresentationSessionStarted(
+      render_frame_host_id, true,
       content::PresentationSessionInfo(PresentationUrlFromMediaSource(source),
-                                       std::string()));
+                                       presentation_id),
+      route.media_route_id());
 }
 
 void PresentationServiceDelegateImpl::AddDefaultMediaSourceObserver(
diff --git a/chrome/browser/media/router/presentation_service_delegate_impl.h b/chrome/browser/media/router/presentation_service_delegate_impl.h
index b8a6c810..82af0d9 100644
--- a/chrome/browser/media/router/presentation_service_delegate_impl.h
+++ b/chrome/browser/media/router/presentation_service_delegate_impl.h
@@ -148,6 +148,21 @@
   MediaSource GetMediaSourceFromListener(
       content::PresentationScreenAvailabilityListener* listener);
 
+  void OnJoinRouteResponse(int render_process_id,
+                           int render_frame_id,
+                           const content::PresentationSessionInfo& session,
+                           const PresentationSessionSuccessCallback& success_cb,
+                           const PresentationSessionErrorCallback& error_cb,
+                           scoped_ptr<MediaRoute> route,
+                           const std::string& error_text);
+
+  void OnStartSessionSucceeded(
+      int render_process_id,
+      int render_frame_id,
+      const PresentationSessionSuccessCallback& success_cb,
+      const content::PresentationSessionInfo& new_session,
+      const MediaRoute::Id& route_id);
+
   // Returns |true| if the frame is the main frame of |web_contents_|.
   bool IsMainFrame(int render_process_id, int render_frame_id) const;
 
diff --git a/chrome/browser/media_galleries/OWNERS b/chrome/browser/media_galleries/OWNERS
index e250c3d..857971d 100644
--- a/chrome/browser/media_galleries/OWNERS
+++ b/chrome/browser/media_galleries/OWNERS
@@ -1,4 +1,3 @@
-estade@chromium.org
-orenb@chromium.org
+reillyg@chromium.org
 thestig@chromium.org
 tommycli@chromium.org
diff --git a/chrome/browser/chromeos/memory/OWNERS b/chrome/browser/memory/OWNERS
similarity index 100%
rename from chrome/browser/chromeos/memory/OWNERS
rename to chrome/browser/memory/OWNERS
diff --git a/chrome/browser/chromeos/memory/low_memory_observer.cc b/chrome/browser/memory/low_memory_observer_chromeos.cc
similarity index 89%
rename from chrome/browser/chromeos/memory/low_memory_observer.cc
rename to chrome/browser/memory/low_memory_observer_chromeos.cc
index fbbb3fd..7dbc0f2 100644
--- a/chrome/browser/chromeos/memory/low_memory_observer.cc
+++ b/chrome/browser/memory/low_memory_observer_chromeos.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/chromeos/memory/low_memory_observer.h"
+#include "chrome/browser/memory/low_memory_observer_chromeos.h"
 
 #include <fcntl.h>
 
@@ -14,13 +14,11 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
-#include "chrome/browser/chromeos/memory/oom_priority_manager.h"
+#include "chrome/browser/memory/oom_priority_manager.h"
 #include "content/public/browser/browser_thread.h"
 
 using content::BrowserThread;
 
-namespace chromeos {
-
 namespace {
 // This is the file that will exist if low memory notification is available
 // on the device.  Whenever it becomes readable, it signals a low memory
@@ -32,6 +30,8 @@
 const int kLowMemoryCheckTimeoutMs = 750;
 }  // namespace
 
+namespace memory {
+
 ////////////////////////////////////////////////////////////////////////////////
 // LowMemoryObserverImpl
 //
@@ -62,9 +62,7 @@
  private:
   friend class base::RefCountedThreadSafe<LowMemoryObserverImpl>;
 
-  ~LowMemoryObserverImpl() {
-    StopObservingOnFileThread();
-  }
+  ~LowMemoryObserverImpl() { StopObservingOnFileThread(); }
 
   // Start a timer to resume watching the low memory file descriptor.
   void ScheduleNextObservation();
@@ -95,8 +93,9 @@
       CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
       if (g_browser_process &&
           g_browser_process->platform_part()->oom_priority_manager()) {
-        g_browser_process->platform_part()->
-            oom_priority_manager()->LogMemoryAndDiscardTab();
+        g_browser_process->platform_part()
+            ->oom_priority_manager()
+            ->LogMemoryAndDiscardTab();
       }
     }
 
@@ -145,8 +144,7 @@
 void LowMemoryObserverImpl::ScheduleNextObservation() {
   timer_.Start(FROM_HERE,
                base::TimeDelta::FromMilliseconds(kLowMemoryCheckTimeoutMs),
-               this,
-               &LowMemoryObserverImpl::StartWatchingDescriptor);
+               this, &LowMemoryObserverImpl::StartWatchingDescriptor);
 }
 
 void LowMemoryObserverImpl::StartWatchingDescriptor() {
@@ -158,8 +156,7 @@
   if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
           file_descriptor_,
           false,  // persistent=false: We want it to fire once and reschedule.
-          base::MessageLoopForIO::WATCH_READ,
-          watcher_.get(),
+          base::MessageLoopForIO::WATCH_READ, watcher_.get(),
           &watcher_delegate_)) {
     LOG(ERROR) << "Unable to watch " << kLowMemFile;
   }
@@ -168,24 +165,25 @@
 ////////////////////////////////////////////////////////////////////////////////
 // LowMemoryObserver
 
-LowMemoryObserver::LowMemoryObserver() : observer_(new LowMemoryObserverImpl) {}
+LowMemoryObserver::LowMemoryObserver() : observer_(new LowMemoryObserverImpl) {
+}
 
-LowMemoryObserver::~LowMemoryObserver() { Stop(); }
+LowMemoryObserver::~LowMemoryObserver() {
+  Stop();
+}
 
 void LowMemoryObserver::Start() {
   BrowserThread::PostTask(
-      BrowserThread::FILE,
-      FROM_HERE,
+      BrowserThread::FILE, FROM_HERE,
       base::Bind(&LowMemoryObserverImpl::StartObservingOnFileThread,
                  observer_.get()));
 }
 
 void LowMemoryObserver::Stop() {
   BrowserThread::PostTask(
-      BrowserThread::FILE,
-      FROM_HERE,
+      BrowserThread::FILE, FROM_HERE,
       base::Bind(&LowMemoryObserverImpl::StopObservingOnFileThread,
                  observer_.get()));
 }
 
-}  // namespace chromeos
+}  // namespace memory
diff --git a/chrome/browser/chromeos/memory/low_memory_observer.h b/chrome/browser/memory/low_memory_observer_chromeos.h
similarity index 81%
rename from chrome/browser/chromeos/memory/low_memory_observer.h
rename to chrome/browser/memory/low_memory_observer_chromeos.h
index 69d5f10..21e0fc430 100644
--- a/chrome/browser/chromeos/memory/low_memory_observer.h
+++ b/chrome/browser/memory/low_memory_observer_chromeos.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_MEMORY_LOW_MEMORY_OBSERVER_H_
-#define CHROME_BROWSER_CHROMEOS_MEMORY_LOW_MEMORY_OBSERVER_H_
+#ifndef CHROME_BROWSER_MEMORY_LOW_MEMORY_OBSERVER_CHROMEOS_H_
+#define CHROME_BROWSER_MEMORY_LOW_MEMORY_OBSERVER_CHROMEOS_H_
 
 #include "base/memory/ref_counted.h"
 
-namespace chromeos {
+namespace memory {
 
 class LowMemoryObserverImpl;
 
@@ -36,6 +36,6 @@
   DISALLOW_COPY_AND_ASSIGN(LowMemoryObserver);
 };
 
-}  // namespace chromeos
+}  // namespace memory
 
-#endif  // CHROME_BROWSER_CHROMEOS_MEMORY_LOW_MEMORY_OBSERVER_H_
+#endif  // CHROME_BROWSER_MEMORY_LOW_MEMORY_OBSERVER_CHROMEOS_H_
diff --git a/chrome/browser/chromeos/memory/oom_memory_details.h b/chrome/browser/memory/oom_memory_details.h
similarity index 82%
rename from chrome/browser/chromeos/memory/oom_memory_details.h
rename to chrome/browser/memory/oom_memory_details.h
index fa781a2..99f9829a 100644
--- a/chrome/browser/chromeos/memory/oom_memory_details.h
+++ b/chrome/browser/memory/oom_memory_details.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_CHROMEOS_MEMORY_OOM_MEMORY_DETAILS_H_
-#define CHROME_BROWSER_CHROMEOS_MEMORY_OOM_MEMORY_DETAILS_H_
+#ifndef CHROME_BROWSER_MEMORY_OOM_MEMORY_DETAILS_H_
+#define CHROME_BROWSER_MEMORY_OOM_MEMORY_DETAILS_H_
 
 #include <string>
 
@@ -12,7 +12,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/memory_details.h"
 
-namespace chromeos {
+namespace memory {
 
 ////////////////////////////////////////////////////////////////////////////////
 // OomMemoryDetails logs details about all Chrome processes during an out-of-
@@ -37,6 +37,6 @@
   DISALLOW_COPY_AND_ASSIGN(OomMemoryDetails);
 };
 
-}  // namespace chromeos
+}  // namespace memory
 
-#endif  // CHROME_BROWSER_CHROMEOS_MEMORY_OOM_MEMORY_DETAILS_H_
+#endif  // CHROME_BROWSER_MEMORY_OOM_MEMORY_DETAILS_H_
diff --git a/chrome/browser/chromeos/memory/oom_memory_details.cc b/chrome/browser/memory/oom_memory_details_chromeos.cc
similarity index 90%
rename from chrome/browser/chromeos/memory/oom_memory_details.cc
rename to chrome/browser/memory/oom_memory_details_chromeos.cc
index effd3178..9269a302 100644
--- a/chrome/browser/chromeos/memory/oom_memory_details.cc
+++ b/chrome/browser/memory/oom_memory_details_chromeos.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/memory/oom_memory_details.h"
+#include "chrome/browser/memory/oom_memory_details.h"
 
 #include "base/logging.h"
 #include "base/process/process_metrics.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/chromeos/memory/oom_memory_details.h"
+#include "chrome/browser/memory/oom_memory_details.h"
 #include "ui/base/text/bytes_formatting.h"
 
-namespace chromeos {
+namespace memory {
 
 // static
 void OomMemoryDetails::Log(const std::string& title,
@@ -49,4 +49,4 @@
   Release();
 }
 
-}  // namespace chromeos
+}  // namespace memory
diff --git a/chrome/browser/chromeos/memory/oom_priority_manager.h b/chrome/browser/memory/oom_priority_manager.h
similarity index 96%
rename from chrome/browser/chromeos/memory/oom_priority_manager.h
rename to chrome/browser/memory/oom_priority_manager.h
index fbb4430..b5c17573 100644
--- a/chrome/browser/chromeos/memory/oom_priority_manager.h
+++ b/chrome/browser/memory/oom_priority_manager.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_CHROMEOS_MEMORY_OOM_PRIORITY_MANAGER_H_
-#define CHROME_BROWSER_CHROMEOS_MEMORY_OOM_PRIORITY_MANAGER_H_
+#ifndef CHROME_BROWSER_MEMORY_OOM_PRIORITY_MANAGER_H_
+#define CHROME_BROWSER_MEMORY_OOM_PRIORITY_MANAGER_H_
 
 #include <utility>
 #include <vector>
@@ -23,7 +23,7 @@
 
 class GURL;
 
-namespace chromeos {
+namespace memory {
 
 class LowMemoryObserver;
 
@@ -192,6 +192,6 @@
   DISALLOW_COPY_AND_ASSIGN(OomPriorityManager);
 };
 
-}  // namespace chromeos
+}  // namespace memory
 
-#endif  // CHROME_BROWSER_CHROMEOS_MEMORY_OOM_PRIORITY_MANAGER_H_
+#endif  // CHROME_BROWSER_MEMORY_OOM_PRIORITY_MANAGER_H_
diff --git a/chrome/browser/chromeos/memory/oom_priority_manager_browsertest.cc b/chrome/browser/memory/oom_priority_manager_browsertest_chromeos.cc
similarity index 94%
rename from chrome/browser/chromeos/memory/oom_priority_manager_browsertest.cc
rename to chrome/browser/memory/oom_priority_manager_browsertest_chromeos.cc
index d5458ee..a6e8b24 100644
--- a/chrome/browser/chromeos/memory/oom_priority_manager_browsertest.cc
+++ b/chrome/browser/memory/oom_priority_manager_browsertest_chromeos.cc
@@ -6,7 +6,7 @@
 #include "base/memory/memory_pressure_listener.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
-#include "chrome/browser/chromeos/memory/oom_priority_manager.h"
+#include "chrome/browser/memory/oom_priority_manager.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
@@ -21,6 +21,7 @@
 
 using content::OpenURLParams;
 
+namespace memory {
 namespace {
 
 typedef InProcessBrowserTest OomPriorityManagerTest;
@@ -28,7 +29,7 @@
 IN_PROC_BROWSER_TEST_F(OomPriorityManagerTest, OomPriorityManagerBasics) {
   using content::WindowedNotificationObserver;
 
-  chromeos::OomPriorityManager* oom_priority_manager =
+  OomPriorityManager* oom_priority_manager =
       g_browser_process->platform_part()->oom_priority_manager();
   EXPECT_FALSE(oom_priority_manager->recent_tab_discard());
 
@@ -45,8 +46,7 @@
       content::NOTIFICATION_NAV_ENTRY_COMMITTED,
       content::NotificationService::AllSources());
   OpenURLParams open2(GURL(chrome::kChromeUICreditsURL), content::Referrer(),
-                      NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_TYPED,
-                      false);
+                      NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_TYPED, false);
   browser()->OpenURL(open2);
   load2.Wait();
 
@@ -54,8 +54,7 @@
       content::NOTIFICATION_NAV_ENTRY_COMMITTED,
       content::NotificationService::AllSources());
   OpenURLParams open3(GURL(chrome::kChromeUITermsURL), content::Referrer(),
-                      NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_TYPED,
-                      false);
+                      NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_TYPED, false);
   browser()->OpenURL(open3);
   load3.Wait();
 
@@ -67,8 +66,7 @@
       content::NOTIFICATION_NAV_ENTRY_COMMITTED,
       content::NotificationService::AllSources());
   OpenURLParams open4(GURL(chrome::kChromeUIVersionURL), content::Referrer(),
-                      CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
-                      false);
+                      CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
   browser()->OpenURL(open4);
   load4.Wait();
 
@@ -76,9 +74,8 @@
   WindowedNotificationObserver load5(
       content::NOTIFICATION_NAV_ENTRY_COMMITTED,
       content::NotificationService::AllSources());
-  OpenURLParams open5(GURL("chrome://dns"), content::Referrer(),
-                      CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
-                      false);
+  OpenURLParams open5(GURL("chrome://dns"), content::Referrer(), CURRENT_TAB,
+                      ui::PAGE_TRANSITION_TYPED, false);
   browser()->OpenURL(open5);
   load5.Wait();
 
@@ -165,7 +162,7 @@
 // Test that the MemoryPressureListener event is properly triggering a tab
 // discard upon |MEMORY_PRESSURE_LEVEL_CRITICAL| event.
 IN_PROC_BROWSER_TEST_F(OomPriorityManagerTest, OomPressureListener) {
-  chromeos::OomPriorityManager* oom_priority_manager =
+  OomPriorityManager* oom_priority_manager =
       g_browser_process->platform_part()->oom_priority_manager();
   // Get three tabs open.
   content::WindowedNotificationObserver load1(
@@ -180,8 +177,7 @@
       content::NOTIFICATION_NAV_ENTRY_COMMITTED,
       content::NotificationService::AllSources());
   OpenURLParams open2(GURL(chrome::kChromeUICreditsURL), content::Referrer(),
-                      NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_TYPED,
-                      false);
+                      NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_TYPED, false);
   browser()->OpenURL(open2);
   load2.Wait();
   EXPECT_FALSE(oom_priority_manager->recent_tab_discard());
@@ -209,3 +205,4 @@
 }
 
 }  // namespace
+}  // namespace memory
diff --git a/chrome/browser/chromeos/memory/oom_priority_manager.cc b/chrome/browser/memory/oom_priority_manager_chromeos.cc
similarity index 85%
rename from chrome/browser/chromeos/memory/oom_priority_manager.cc
rename to chrome/browser/memory/oom_priority_manager_chromeos.cc
index 093cec3..7940d05 100644
--- a/chrome/browser/chromeos/memory/oom_priority_manager.cc
+++ b/chrome/browser/memory/oom_priority_manager_chromeos.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/chromeos/memory/oom_priority_manager.h"
+#include "chrome/browser/memory/oom_priority_manager.h"
 
 #include <algorithm>
 #include <set>
@@ -27,9 +27,9 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
-#include "chrome/browser/chromeos/memory/low_memory_observer.h"
-#include "chrome/browser/chromeos/memory/oom_memory_details.h"
-#include "chrome/browser/chromeos/memory/system_memory_stats_recorder.h"
+#include "chrome/browser/memory/low_memory_observer_chromeos.h"
+#include "chrome/browser/memory/oom_memory_details.h"
+#include "chrome/browser/memory/system_memory_stats_recorder.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_iterator.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -53,8 +53,7 @@
 using content::BrowserThread;
 using content::WebContents;
 
-namespace chromeos {
-
+namespace memory {
 namespace {
 
 // The default interval in seconds after which to adjust the oom_score_adj
@@ -88,14 +87,14 @@
 // OomPriorityManager
 
 OomPriorityManager::TabStats::TabStats()
-  : is_app(false),
-    is_reloadable_ui(false),
-    is_playing_audio(false),
-    is_pinned(false),
-    is_selected(false),
-    is_discarded(false),
-    renderer_handle(0),
-    tab_contents_id(0) {
+    : is_app(false),
+      is_reloadable_ui(false),
+      is_playing_audio(false),
+      is_pinned(false),
+      is_selected(false),
+      is_discarded(false),
+      renderer_handle(0),
+      tab_contents_id(0) {
 }
 
 OomPriorityManager::TabStats::~TabStats() {
@@ -109,15 +108,12 @@
   if (!base::MemoryPressureMonitor::Get())
     low_memory_observer_.reset(new LowMemoryObserver);
 
-  registrar_.Add(this,
-      content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
-      content::NotificationService::AllBrowserContextsAndSources());
-  registrar_.Add(this,
-      content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
-      content::NotificationService::AllBrowserContextsAndSources());
-  registrar_.Add(this,
-      content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
-      content::NotificationService::AllBrowserContextsAndSources());
+  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
+                 content::NotificationService::AllBrowserContextsAndSources());
+  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
+                 content::NotificationService::AllBrowserContextsAndSources());
+  registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
+                 content::NotificationService::AllBrowserContextsAndSources());
 }
 
 OomPriorityManager::~OomPriorityManager() {
@@ -126,17 +122,13 @@
 
 void OomPriorityManager::Start() {
   if (!timer_.IsRunning()) {
-    timer_.Start(FROM_HERE,
-                 TimeDelta::FromSeconds(kAdjustmentIntervalSeconds),
-                 this,
-                 &OomPriorityManager::AdjustOomPriorities);
+    timer_.Start(FROM_HERE, TimeDelta::FromSeconds(kAdjustmentIntervalSeconds),
+                 this, &OomPriorityManager::AdjustOomPriorities);
   }
   if (!recent_tab_discard_timer_.IsRunning()) {
     recent_tab_discard_timer_.Start(
-        FROM_HERE,
-        TimeDelta::FromSeconds(kRecentTabDiscardIntervalSeconds),
-        this,
-        &OomPriorityManager::RecordRecentTabDiscard);
+        FROM_HERE, TimeDelta::FromSeconds(kRecentTabDiscardIntervalSeconds),
+        this, &OomPriorityManager::RecordRecentTabDiscard);
   }
   start_time_ = TimeTicks::Now();
   // If a |LowMemoryObserver| exists we use the old system, otherwise we create
@@ -146,9 +138,9 @@
   } else {
     base::MemoryPressureMonitor* monitor = base::MemoryPressureMonitor::Get();
     if (monitor) {
-      memory_pressure_listener_.reset(new base::MemoryPressureListener(
-          base::Bind(&OomPriorityManager::OnMemoryPressure,
-                     base::Unretained(this))));
+      memory_pressure_listener_.reset(
+          new base::MemoryPressureListener(base::Bind(
+              &OomPriorityManager::OnMemoryPressure, base::Unretained(this))));
       base::MemoryPressureListener::MemoryPressureLevel level =
           monitor->GetCurrentPressureLevel();
       if (level ==
@@ -174,7 +166,7 @@
   std::vector<base::string16> titles;
   titles.reserve(stats.size());
   TabStatsList::iterator it = stats.begin();
-  for ( ; it != stats.end(); ++it) {
+  for (; it != stats.end(); ++it) {
     base::string16 str;
     str.reserve(4096);
     int score = oom_score_map_[it->child_process_host_id];
@@ -201,8 +193,7 @@
     return false;
   // Loop until we find a non-discarded tab to kill.
   for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin();
-       stats_rit != stats.rend();
-       ++stats_rit) {
+       stats_rit != stats.rend(); ++stats_rit) {
     int64 least_important_tab_id = stats_rit->tab_contents_id;
     if (DiscardTabById(least_important_tab_id))
       return true;
@@ -248,8 +239,7 @@
   // Prefix-match against the table above. Use strncmp to avoid allocating
   // memory to convert the URL prefix constants into std::strings.
   for (size_t i = 0; i < arraysize(kReloadableUrlPrefixes); ++i) {
-    if (!strncmp(url.spec().c_str(),
-                 kReloadableUrlPrefixes[i],
+    if (!strncmp(url.spec().c_str(), kReloadableUrlPrefixes[i],
                  strlen(kReloadableUrlPrefixes[i])))
       return true;
   }
@@ -267,8 +257,8 @@
       WebContents* web_contents = model->GetWebContentsAt(idx);
       int64 web_contents_id = IdFromWebContents(web_contents);
       if (web_contents_id == target_web_contents_id) {
-        LOG(WARNING) << "Discarding tab " << idx
-                     << " id " << target_web_contents_id;
+        LOG(WARNING) << "Discarding tab " << idx << " id "
+                     << target_web_contents_id;
         // Record statistics before discarding because we want to capture the
         // memory state that lead to the discard.
         RecordDiscardStatistics();
@@ -284,17 +274,17 @@
 void OomPriorityManager::RecordDiscardStatistics() {
   // Record a raw count so we can compare to discard reloads.
   discard_count_++;
-  UMA_HISTOGRAM_CUSTOM_COUNTS(
-      "Tabs.Discard.DiscardCount", discard_count_, 1, 1000, 50);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.DiscardCount", discard_count_, 1,
+                              1000, 50);
 
   // TODO(jamescook): Maybe incorporate extension count?
-  UMA_HISTOGRAM_CUSTOM_COUNTS(
-      "Tabs.Discard.TabCount", GetTabCount(), 1, 100, 50);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.TabCount", GetTabCount(), 1, 100,
+                              50);
   // Record the discarded tab in relation to the amount of simultaneously
   // logged in users.
-  ash::MultiProfileUMA::RecordDiscardedTab(
-      ash::Shell::GetInstance()->session_state_delegate()->
-          NumberOfLoggedInUsers());
+  ash::MultiProfileUMA::RecordDiscardedTab(ash::Shell::GetInstance()
+                                               ->session_state_delegate()
+                                               ->NumberOfLoggedInUsers());
 
   // TODO(jamescook): If the time stats prove too noisy, then divide up users
   // based on how heavily they use Chrome using tab count as a proxy.
@@ -304,16 +294,16 @@
     TimeDelta interval = TimeTicks::Now() - start_time_;
     int interval_seconds = static_cast<int>(interval.InSeconds());
     // Record time in seconds over an interval of approximately 1 day.
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "Tabs.Discard.InitialTime2", interval_seconds, 1, 100000, 50);
+    UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.InitialTime2", interval_seconds,
+                                1, 100000, 50);
   } else {
     // Not the first discard, so compute time since last discard.
     TimeDelta interval = TimeTicks::Now() - last_discard_time_;
     int interval_ms = static_cast<int>(interval.InMilliseconds());
     // Record time in milliseconds over an interval of approximately 1 day.
     // Start at 100 ms to get extra resolution in the target 750 ms range.
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "Tabs.Discard.IntervalTime2", interval_ms, 100, 100000 * 1000, 50);
+    UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.IntervalTime2", interval_ms, 100,
+                                100000 * 1000, 50);
   }
   // Record chromeos's concept of system memory usage at the time of the
   // discard.
@@ -356,8 +346,7 @@
 
 // Returns true if |first| is considered less desirable to be killed
 // than |second|.
-bool OomPriorityManager::CompareTabStats(TabStats first,
-                                         TabStats second) {
+bool OomPriorityManager::CompareTabStats(TabStats first, TabStats second) {
   // Being currently selected is most important to protect.
   if (first.is_selected != second.is_selected)
     return first.is_selected;
@@ -439,27 +428,29 @@
       bool visible = *content::Details<bool>(details).ptr();
       if (visible) {
         content::RenderProcessHost* render_host =
-            content::Source<content::RenderWidgetHost>(source).ptr()->
-            GetProcess();
-        focused_tab_process_info_ = std::make_pair(render_host->GetID(),
-                                                   render_host->GetHandle());
+            content::Source<content::RenderWidgetHost>(source)
+                .ptr()
+                ->GetProcess();
+        focused_tab_process_info_ =
+            std::make_pair(render_host->GetID(), render_host->GetHandle());
 
         // If the currently focused tab already has a lower score, do not
         // set it. This can happen in case the newly focused tab is script
         // connected to the previous tab.
         ProcessScoreMap::iterator it;
         it = oom_score_map_.find(focused_tab_process_info_.first);
-        if (it == oom_score_map_.end()
-            || it->second != chrome::kLowestRendererOomScore) {
+        if (it == oom_score_map_.end() ||
+            it->second != chrome::kLowestRendererOomScore) {
           // By starting a timer we guarantee that the tab is focused for
           // certain amount of time. Secondly, it also does not add overhead
           // to the tab switching time.
           if (focus_tab_score_adjust_timer_.IsRunning())
             focus_tab_score_adjust_timer_.Reset();
           else
-            focus_tab_score_adjust_timer_.Start(FROM_HERE,
-              TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs),
-              this, &OomPriorityManager::OnFocusTabScoreAdjustmentTimeout);
+            focus_tab_score_adjust_timer_.Start(
+                FROM_HERE,
+                TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs),
+                this, &OomPriorityManager::OnFocusTabScoreAdjustmentTimeout);
         }
       }
       break;
@@ -547,8 +538,7 @@
 
 // static
 std::vector<OomPriorityManager::ProcessInfo>
-    OomPriorityManager::GetChildProcessInfos(
-    const TabStatsList& stats_list) {
+OomPriorityManager::GetChildProcessInfos(const TabStatsList& stats_list) {
   std::vector<ProcessInfo> process_infos;
   std::set<base::ProcessHandle> already_seen;
   for (TabStatsList::const_iterator iterator = stats_list.begin();
@@ -564,8 +554,8 @@
       continue;
     }
 
-    process_infos.push_back(std::make_pair(
-        iterator->child_process_host_id, iterator->renderer_handle));
+    process_infos.push_back(std::make_pair(iterator->child_process_host_id,
+                                           iterator->renderer_handle));
   }
   return process_infos;
 }
@@ -591,14 +581,13 @@
   // the end, however, it's a pretty arbitrary range to use.  Higher
   // values are more likely to be killed by the OOM killer.
   float priority = chrome::kLowestRendererOomScore;
-  const int kPriorityRange = chrome::kHighestRendererOomScore -
-                             chrome::kLowestRendererOomScore;
+  const int kPriorityRange =
+      chrome::kHighestRendererOomScore - chrome::kLowestRendererOomScore;
   float priority_increment =
       static_cast<float>(kPriorityRange) / process_infos.size();
   for (const auto& process_info : process_infos) {
     int score = static_cast<int>(priority + 0.5f);
-    ProcessScoreMap::iterator it =
-        oom_score_map_.find(process_info.first);
+    ProcessScoreMap::iterator it = oom_score_map_.find(process_info.first);
     // If a process has the same score as the newly calculated value,
     // do not set it.
     if (it == oom_score_map_.end() || it->second != score) {
@@ -621,4 +610,4 @@
   // consider to call PurgeBrowserMemory() before CRITICAL is reached.
 }
 
-}  // namespace chromeos
+}  // namespace memory
diff --git a/chrome/browser/chromeos/memory/oom_priority_manager_unittest.cc b/chrome/browser/memory/oom_priority_manager_chromeos_unittest.cc
similarity index 87%
rename from chrome/browser/chromeos/memory/oom_priority_manager_unittest.cc
rename to chrome/browser/memory/oom_priority_manager_chromeos_unittest.cc
index 9280b9f..58a48bd 100644
--- a/chrome/browser/chromeos/memory/oom_priority_manager_unittest.cc
+++ b/chrome/browser/memory/oom_priority_manager_chromeos_unittest.cc
@@ -8,12 +8,12 @@
 #include "base/logging.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
-#include "chrome/browser/chromeos/memory/oom_priority_manager.h"
+#include "chrome/browser/memory/oom_priority_manager.h"
 #include "chrome/common/url_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-namespace chromeos {
+namespace memory {
 
 typedef testing::Test OomPriorityManagerTest;
 
@@ -34,7 +34,7 @@
 // Tests the sorting comparator so that we know it's producing the
 // desired order.
 TEST_F(OomPriorityManagerTest, Comparator) {
-  chromeos::OomPriorityManager::TabStatsList test_list;
+  OomPriorityManager::TabStatsList test_list;
   const base::TimeTicks now = base::TimeTicks::Now();
 
   // Add kSelected last to verify we are sorting the array.
@@ -114,8 +114,7 @@
     test_list.push_back(stats);
   }
 
-  std::sort(test_list.begin(),
-            test_list.end(),
+  std::sort(test_list.begin(), test_list.end(),
             OomPriorityManager::CompareTabStats);
 
   int index = 0;
@@ -142,18 +141,18 @@
 }
 
 TEST_F(OomPriorityManagerTest, IsReloadableUI) {
-  EXPECT_TRUE(OomPriorityManager::IsReloadableUI(
-      GURL(chrome::kChromeUIDownloadsURL)));
-  EXPECT_TRUE(OomPriorityManager::IsReloadableUI(
-      GURL(chrome::kChromeUIHistoryURL)));
-  EXPECT_TRUE(OomPriorityManager::IsReloadableUI(
-      GURL(chrome::kChromeUINewTabURL)));
-  EXPECT_TRUE(OomPriorityManager::IsReloadableUI(
-      GURL(chrome::kChromeUISettingsURL)));
+  EXPECT_TRUE(
+      OomPriorityManager::IsReloadableUI(GURL(chrome::kChromeUIDownloadsURL)));
+  EXPECT_TRUE(
+      OomPriorityManager::IsReloadableUI(GURL(chrome::kChromeUIHistoryURL)));
+  EXPECT_TRUE(
+      OomPriorityManager::IsReloadableUI(GURL(chrome::kChromeUINewTabURL)));
+  EXPECT_TRUE(
+      OomPriorityManager::IsReloadableUI(GURL(chrome::kChromeUISettingsURL)));
 
   // Debugging URLs are not included.
-  EXPECT_FALSE(OomPriorityManager::IsReloadableUI(
-      GURL(chrome::kChromeUIDiscardsURL)));
+  EXPECT_FALSE(
+      OomPriorityManager::IsReloadableUI(GURL(chrome::kChromeUIDiscardsURL)));
   EXPECT_FALSE(OomPriorityManager::IsReloadableUI(
       GURL(chrome::kChromeUINetInternalsURL)));
 
@@ -168,8 +167,7 @@
 
   // Empty stats list gives empty |process_id_pairs| list.
   OomPriorityManager::TabStatsList empty_list;
-  process_id_pairs =
-      OomPriorityManager::GetChildProcessInfos(empty_list);
+  process_id_pairs = OomPriorityManager::GetChildProcessInfos(empty_list);
   EXPECT_EQ(0u, process_id_pairs.size());
 
   // Two tabs in two different processes generates two
@@ -193,8 +191,7 @@
   stats.child_process_host_id = 100;
   stats.renderer_handle = 0;
   zero_handle_list.push_back(stats);
-  process_id_pairs =
-      OomPriorityManager::GetChildProcessInfos(zero_handle_list);
+  process_id_pairs = OomPriorityManager::GetChildProcessInfos(zero_handle_list);
   EXPECT_EQ(0u, process_id_pairs.size());
 
   // Two tabs in the same process generates one handle out. When a duplicate
@@ -218,4 +215,4 @@
   EXPECT_EQ(201, process_id_pairs[1].second);
 }
 
-}  // namespace chromeos
+}  // namespace memory
diff --git a/chrome/browser/chromeos/memory/system_memory_stats_recorder.h b/chrome/browser/memory/system_memory_stats_recorder.h
similarity index 69%
rename from chrome/browser/chromeos/memory/system_memory_stats_recorder.h
rename to chrome/browser/memory/system_memory_stats_recorder.h
index decab265..ce406bf 100644
--- a/chrome/browser/chromeos/memory/system_memory_stats_recorder.h
+++ b/chrome/browser/memory/system_memory_stats_recorder.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_MEMORY_SYSTEM_MEMORY_STATS_RECORDER_H_
-#define CHROME_BROWSER_CHROMEOS_MEMORY_SYSTEM_MEMORY_STATS_RECORDER_H_
+#ifndef CHROME_BROWSER_MEMORY_SYSTEM_MEMORY_STATS_RECORDER_H_
+#define CHROME_BROWSER_MEMORY_SYSTEM_MEMORY_STATS_RECORDER_H_
 
-namespace chromeos {
+namespace memory {
 
 // The type of memory UMA stats to be recorded in RecordMemoryStats.
 enum RecordMemoryStatsType {
@@ -21,6 +21,6 @@
 
 void RecordMemoryStats(RecordMemoryStatsType type);
 
-}  // namespace chromeos
+}  // namespace memory
 
-#endif  // CHROME_BROWSER_CHROMEOS_MEMORY_SYSTEM_MEMORY_STATS_RECORDER_H_
+#endif  // CHROME_BROWSER_MEMORY_SYSTEM_MEMORY_STATS_RECORDER_H_
diff --git a/chrome/browser/chromeos/memory/system_memory_stats_recorder.cc b/chrome/browser/memory/system_memory_stats_recorder_chromeos.cc
similarity index 96%
rename from chrome/browser/chromeos/memory/system_memory_stats_recorder.cc
rename to chrome/browser/memory/system_memory_stats_recorder_chromeos.cc
index 20a605cb..063f189 100644
--- a/chrome/browser/chromeos/memory/system_memory_stats_recorder.cc
+++ b/chrome/browser/memory/system_memory_stats_recorder_chromeos.cc
@@ -2,11 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/memory/system_memory_stats_recorder.h"
+#include "chrome/browser/memory/system_memory_stats_recorder.h"
 
 #include "base/metrics/histogram_macros.h"
 #include "base/process/process_metrics.h"
 
+namespace memory {
+
 // Record a size in megabytes, over a potential interval up to 32 GB.
 #define UMA_HISTOGRAM_AVAILABLE_MEGABYTES(name, sample) \
   UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 32768, 50)
@@ -31,8 +33,6 @@
 #define UMA_HISTOGRAM_MEGABYTES_LINEAR(name, sample) \
   UMA_HISTOGRAM_LINEAR(name, sample, 2500, 50)
 
-namespace chromeos {
-
 void RecordMemoryStats(RecordMemoryStatsType type) {
   base::SystemMemoryInfoKB memory;
   if (!base::GetSystemMemoryInfo(&memory))
@@ -91,4 +91,4 @@
   }
 }
 
-}  // namespace chromeos
+}  // namespace memory
diff --git a/chrome/browser/metrics/chrome_stability_metrics_provider.cc b/chrome/browser/metrics/chrome_stability_metrics_provider.cc
index 6968d40..a26d263 100644
--- a/chrome/browser/metrics/chrome_stability_metrics_provider.cc
+++ b/chrome/browser/metrics/chrome_stability_metrics_provider.cc
@@ -37,7 +37,7 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/memory/system_memory_stats_recorder.h"
+#include "chrome/browser/memory/system_memory_stats_recorder.h"
 #endif
 
 namespace {
@@ -302,10 +302,10 @@
     UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.ChildKills.OOM",
                               was_extension_process ? 2 : 1,
                               3);
-    chromeos::RecordMemoryStats(
-        was_extension_process ?
-        chromeos::RECORD_MEMORY_STATS_EXTENSIONS_OOM_KILLED :
-        chromeos::RECORD_MEMORY_STATS_CONTENTS_OOM_KILLED);
+    memory::RecordMemoryStats(
+        was_extension_process
+            ? memory::RECORD_MEMORY_STATS_EXTENSIONS_OOM_KILLED
+            : memory::RECORD_MEMORY_STATS_CONTENTS_OOM_KILLED);
 #endif
   } else if (status == base::TERMINATION_STATUS_STILL_RUNNING) {
     UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.DisconnectedAlive",
diff --git a/chrome/browser/metrics/metrics_services_manager.cc b/chrome/browser/metrics/metrics_services_manager.cc
index 8c68d045..705def0 100644
--- a/chrome/browser/metrics/metrics_services_manager.cc
+++ b/chrome/browser/metrics/metrics_services_manager.cc
@@ -14,6 +14,8 @@
 #include "chrome/browser/metrics/chrome_metrics_service_client.h"
 #include "chrome/browser/metrics/metrics_reporting_state.h"
 #include "chrome/browser/metrics/variations/variations_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser_otr_state.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
@@ -39,7 +41,6 @@
       may_upload_(false),
       may_record_(false) {
   DCHECK(local_state);
-  pref_change_registrar_.Init(local_state);
 }
 
 MetricsServicesManager::~MetricsServicesManager() {
@@ -98,15 +99,36 @@
   return metrics_state_manager_.get();
 }
 
+bool MetricsServicesManager::GetSafeBrowsingState() {
+  // Start listening for updates to SB service state. This is done here instead
+  // of in the constructor to avoid errors from trying to instantiate SB
+  // service before the IO thread exists.
+  SafeBrowsingService* sb_service = g_browser_process->safe_browsing_service();
+  if (!sb_state_subscription_ && sb_service) {
+    // base::Unretained(this) is safe here since this object owns the
+    // sb_state_subscription_ which owns the pointer.
+    sb_state_subscription_ = sb_service->RegisterStateCallback(
+        base::Bind(&MetricsServicesManager::UpdateRunningServices,
+                   base::Unretained(this)));
+  }
+
+  return sb_service && sb_service->enabled_by_prefs();
+}
+
 void MetricsServicesManager::UpdatePermissions(bool may_record,
                                                bool may_upload) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   // Stash the current permissions so that we can update the RapporService
   // correctly when the Rappor preference changes.  The metrics recording
   // preference partially determines the initial rappor setting, and also
   // controls whether FINE metrics are sent.
   may_record_ = may_record;
   may_upload_ = may_upload;
+  UpdateRunningServices();
+}
 
+void MetricsServicesManager::UpdateRunningServices() {
+  DCHECK(thread_checker_.CalledOnValidThread());
   metrics::MetricsService* metrics = GetMetricsService();
 
   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
@@ -117,15 +139,17 @@
 
   if (only_do_metrics_recording) {
     metrics->StartRecordingForTests();
-    GetRapporService()->Update(rappor::FINE_LEVEL, false);
+    GetRapporService()->Update(
+        rappor::UMA_RAPPOR_GROUP | rappor::SAFEBROWSING_RAPPOR_GROUP,
+        false);
     return;
   }
 
-  if (may_record) {
+  if (may_record_) {
     if (!metrics->recording_active())
       metrics->Start();
 
-    if (may_upload)
+    if (may_upload_)
       metrics->EnableReporting();
     else
       metrics->DisableReporting();
@@ -133,12 +157,14 @@
     metrics->Stop();
   }
 
-  rappor::RecordingLevel recording_level = rappor::RECORDING_DISABLED;
+  int recording_groups = 0;
 #if defined(GOOGLE_CHROME_BUILD)
-  if (may_record)
-    recording_level = rappor::FINE_LEVEL;
+  if (may_record_)
+    recording_groups |= rappor::UMA_RAPPOR_GROUP;
+  if (GetSafeBrowsingState())
+    recording_groups |= rappor::SAFEBROWSING_RAPPOR_GROUP;
 #endif  // defined(GOOGLE_CHROME_BUILD)
-  GetRapporService()->Update(recording_level, may_upload);
+  GetRapporService()->Update(recording_groups, may_upload_);
 }
 
 void MetricsServicesManager::UpdateUploadPermissions(bool may_upload) {
diff --git a/chrome/browser/metrics/metrics_services_manager.h b/chrome/browser/metrics/metrics_services_manager.h
index 7a05c80..5158d10 100644
--- a/chrome/browser/metrics/metrics_services_manager.h
+++ b/chrome/browser/metrics/metrics_services_manager.h
@@ -7,8 +7,8 @@
 
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/prefs/pref_change_registrar.h"
 #include "base/threading/thread_checker.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
 
 class ChromeMetricsServiceClient;
 class PrefService;
@@ -70,14 +70,20 @@
 
   metrics::MetricsStateManager* GetMetricsStateManager();
 
+  // Retrieve the latest SafeBrowsing preferences state.
+  bool GetSafeBrowsingState();
+
+  // Update which services are running to match current permissions.
+  void UpdateRunningServices();
+
   // Ensures that all functions are called from the same thread.
   base::ThreadChecker thread_checker_;
 
   // Weak pointer to the local state prefs store.
   PrefService* local_state_;
 
-  // A change registrar for local_state_;
-  PrefChangeRegistrar pref_change_registrar_;
+  // Subscription to SafeBrowsing service state changes.
+  scoped_ptr<SafeBrowsingService::StateSubscription> sb_state_subscription_;
 
   // The current metrics reporting setting.
   bool may_upload_;
diff --git a/chrome/browser/metrics/variations/variations_request_scheduler_unittest.cc b/chrome/browser/metrics/variations/variations_request_scheduler_unittest.cc
index 493275b..e485602 100644
--- a/chrome/browser/metrics/variations/variations_request_scheduler_unittest.cc
+++ b/chrome/browser/metrics/variations/variations_request_scheduler_unittest.cc
@@ -5,22 +5,16 @@
 #include "chrome/browser/metrics/variations/variations_request_scheduler.h"
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/message_loop/message_loop.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chrome_variations {
 
-namespace {
-
-void DoNothing() {
-}
-
-}  // namespace
-
 TEST(VariationsRequestSchedulerTest, ScheduleFetchShortly) {
   base::MessageLoopForUI message_loop_;
 
-  const base::Closure task = base::Bind(&DoNothing);
+  const base::Closure task = base::Bind(&base::DoNothing);
   VariationsRequestScheduler scheduler(task);
   EXPECT_FALSE(scheduler.one_shot_timer_.IsRunning());
 
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
index 08a6344..1b47bd8 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
@@ -42,21 +42,21 @@
   int flags = DataReductionProxyParams::kAllowed |
       DataReductionProxyParams::kFallbackAllowed |
       DataReductionProxyParams::kAlternativeAllowed;
-  if (DataReductionProxyParams::IsIncludedInPromoFieldTrial())
+  if (data_reduction_proxy::params::IsIncludedInPromoFieldTrial())
     flags |= DataReductionProxyParams::kPromoAllowed;
-  if (DataReductionProxyParams::IsIncludedInHoldbackFieldTrial())
+  if (data_reduction_proxy::params::IsIncludedInHoldbackFieldTrial())
     flags |= DataReductionProxyParams::kHoldback;
 #if defined(OS_ANDROID)
-  if (DataReductionProxyParams::IsIncludedInAndroidOnePromoFieldTrial(
+  if (data_reduction_proxy::params::IsIncludedInAndroidOnePromoFieldTrial(
           base::android::BuildInfo::GetInstance()->android_build_fp())) {
     flags |= DataReductionProxyParams::kPromoAllowed;
   }
 #endif
 
-  bool enabled = prefs->GetBoolean(
-                     data_reduction_proxy::prefs::kDataReductionProxyEnabled) ||
-                 data_reduction_proxy::DataReductionProxyParams::
-                     ShouldForceEnableDataReductionProxy();
+  bool enabled =
+      prefs->GetBoolean(
+          data_reduction_proxy::prefs::kDataReductionProxyEnabled) ||
+      data_reduction_proxy::params::ShouldForceEnableDataReductionProxy();
   scoped_ptr<data_reduction_proxy::DataReductionProxyIOData>
       data_reduction_proxy_io_data(
           new data_reduction_proxy::DataReductionProxyIOData(
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc
index aaebedf4..0c43b511 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc
@@ -205,8 +205,7 @@
           base::Bind(
               &ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial));
   SetDataReductionProxyAlternativeEnabled(
-      data_reduction_proxy::DataReductionProxyParams::
-          IsIncludedInAlternativeFieldTrial());
+      data_reduction_proxy::params::IsIncludedInAlternativeFieldTrial());
   // TODO(bengr): Remove after M46. See http://crbug.com/445599.
   MigrateDataReductionProxyOffProxyPrefs(profile_prefs);
 }
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc
index 23dacc3..4cfea03 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc
@@ -21,7 +21,6 @@
 
 using base::android::ConvertUTF8ToJavaString;
 using base::android::ScopedJavaLocalRef;
-using data_reduction_proxy::DataReductionProxyParams;
 using data_reduction_proxy::DataReductionProxySettings;
 
 DataReductionProxySettingsAndroid::DataReductionProxySettingsAndroid() {
@@ -42,7 +41,7 @@
 
 jboolean DataReductionProxySettingsAndroid::IsIncludedInAltFieldTrial(
     JNIEnv* env, jobject obj) {
-  return DataReductionProxyParams::IsIncludedInAlternativeFieldTrial();
+  return data_reduction_proxy::params::IsIncludedInAlternativeFieldTrial();
 }
 
 jboolean DataReductionProxySettingsAndroid::IsDataReductionProxyEnabled(
diff --git a/chrome/browser/notifications/notification_browsertest.cc b/chrome/browser/notifications/notification_browsertest.cc
index b29d6bd3..314ef82 100644
--- a/chrome/browser/notifications/notification_browsertest.cc
+++ b/chrome/browser/notifications/notification_browsertest.cc
@@ -17,6 +17,7 @@
 #include "base/time/clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/infobars/infobar_responder.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/notifications/desktop_notification_profile_util.h"
 #include "chrome/browser/notifications/notification.h"
@@ -25,6 +26,7 @@
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -112,6 +114,57 @@
   DISALLOW_COPY_AND_ASSIGN(MessageCenterChangeObserver);
 };
 
+// Used to observe the creation of permission prompt without responding.
+class PermissionRequestObserver : public infobars::InfoBarManager::Observer,
+                                  public PermissionBubbleManager::Observer {
+ public:
+  explicit PermissionRequestObserver(content::WebContents* web_contents)
+      : infobar_service_(InfoBarService::FromWebContents(web_contents)),
+        bubble_manager_(PermissionBubbleManager::FromWebContents(web_contents)),
+        request_shown_(false),
+        message_loop_runner_(new content::MessageLoopRunner) {
+    if (PermissionBubbleManager::Enabled())
+      bubble_manager_->AddObserver(this);
+    else
+      infobar_service_->AddObserver(this);
+  }
+  ~PermissionRequestObserver() override {
+    // Safe to remove twice if it happens.
+    if (PermissionBubbleManager::Enabled())
+      bubble_manager_->RemoveObserver(this);
+    else
+      infobar_service_->RemoveObserver(this);
+  }
+
+  void Wait() { message_loop_runner_->Run(); }
+
+  bool request_shown() { return request_shown_; }
+
+ private:
+  // infobars::InfoBarManager::Observer:
+  void OnInfoBarAdded(infobars::InfoBar* infobar) override {
+    DCHECK(!PermissionBubbleManager::Enabled());
+    request_shown_ = true;
+    infobar_service_->RemoveObserver(this);
+    message_loop_runner_->Quit();
+  }
+
+  // PermissionBubbleManager::Observer
+  void OnBubbleAdded() override {
+    DCHECK(PermissionBubbleManager::Enabled());
+    request_shown_ = true;
+    bubble_manager_->RemoveObserver(this);
+    message_loop_runner_->Quit();
+  }
+
+  InfoBarService* infobar_service_;
+  PermissionBubbleManager* bubble_manager_;
+  bool request_shown_;
+  scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(PermissionRequestObserver);
+};
+
 }  // namespace
 
 class NotificationsTest : public InProcessBrowserTest {
@@ -139,12 +192,11 @@
                                  const char* replace_id);
   std::string CreateSimpleNotification(Browser* browser,
                                        bool wait_for_new_balloon);
+  bool RequestAndAcceptPermission(Browser* browser);
+  bool RequestAndDenyPermission(Browser* browser);
+  bool RequestAndDismissPermission(Browser* browser);
   bool RequestPermissionAndWait(Browser* browser);
   bool CancelNotification(const char* notification_id, Browser* browser);
-  bool PerformActionOnInfoBar(Browser* browser,
-                              InfobarAction action,
-                              size_t infobar_index,
-                              int tab_index);
   void GetPrefsByContentSetting(ContentSetting setting,
                                 ContentSettingsForOneType* settings);
   bool CheckOriginInSetting(const ContentSettingsForOneType& settings,
@@ -159,8 +211,16 @@
     return GetTestPageURLForFile("notification_tester.html");
   }
 
+  content::WebContents* GetActiveWebContents(Browser* browser) {
+    return browser->tab_strip_model()->GetActiveWebContents();
+  }
+
  private:
   void DropOriginPreference(const GURL& origin);
+  std::string RequestAndRespondToPermission(
+      Browser* browser,
+      PermissionBubbleManager::AutoResponseType bubble_response,
+      InfoBarResponder::AutoResponseType infobar_response);
 };
 
 int NotificationsTest::GetNotificationCount() {
@@ -232,9 +292,7 @@
   MessageCenterChangeObserver observer;
   std::string result;
   bool success = content::ExecuteScriptAndExtractString(
-      browser->tab_strip_model()->GetActiveWebContents(),
-      script,
-      &result);
+      GetActiveWebContents(browser), script, &result);
   if (success && result != "-1" && wait_for_new_balloon)
     success = observer.Wait();
   EXPECT_TRUE(success);
@@ -250,21 +308,53 @@
       "no_such_file.png", "My Title", "My Body", "");
 }
 
-bool NotificationsTest::RequestPermissionAndWait(Browser* browser) {
-  InfoBarService* infobar_service = InfoBarService::FromWebContents(
-      browser->tab_strip_model()->GetActiveWebContents());
-  content::WindowedNotificationObserver observer(
-      chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
-      content::Source<InfoBarService>(infobar_service));
+std::string NotificationsTest::RequestAndRespondToPermission(
+    Browser* browser,
+    PermissionBubbleManager::AutoResponseType bubble_response,
+    InfoBarResponder::AutoResponseType infobar_response) {
   std::string result;
-  bool success = content::ExecuteScriptAndExtractString(
-      browser->tab_strip_model()->GetActiveWebContents(),
-      "requestPermission();",
-      &result);
-  if (!success || result != "1")
-    return false;
+  content::WebContents* web_contents = GetActiveWebContents(browser);
+  scoped_ptr<InfoBarResponder> infobar_responder;
+  if (PermissionBubbleManager::Enabled()) {
+    PermissionBubbleManager::FromWebContents(web_contents)
+        ->set_auto_response_for_test(bubble_response);
+  } else {
+    infobar_responder.reset(new InfoBarResponder(
+        InfoBarService::FromWebContents(web_contents), infobar_response));
+  }
+  EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+      web_contents, "requestPermission();", &result));
+  return result;
+}
+
+bool NotificationsTest::RequestAndAcceptPermission(Browser* browser) {
+  std::string result = RequestAndRespondToPermission(
+      browser, PermissionBubbleManager::ACCEPT_ALL, InfoBarResponder::ACCEPT);
+  return "request-callback-granted" == result;
+}
+
+bool NotificationsTest::RequestAndDenyPermission(Browser* browser) {
+  std::string result = RequestAndRespondToPermission(
+      browser, PermissionBubbleManager::DENY_ALL, InfoBarResponder::DENY);
+  return "request-callback-denied" == result;
+}
+
+bool NotificationsTest::RequestAndDismissPermission(Browser* browser) {
+  std::string result = RequestAndRespondToPermission(
+      browser, PermissionBubbleManager::DISMISS, InfoBarResponder::DISMISS);
+  return "request-callback-default" == result;
+}
+
+bool NotificationsTest::RequestPermissionAndWait(Browser* browser) {
+  content::WebContents* web_contents = GetActiveWebContents(browser);
+  ui_test_utils::NavigateToURL(browser, GetTestPageURL());
+  PermissionRequestObserver observer(web_contents);
+  std::string result;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+      web_contents, "requestPermissionAndRespond();", &result));
+  EXPECT_EQ("requested", result);
   observer.Wait();
-  return true;
+  return observer.request_shown();
 }
 
 bool NotificationsTest::CancelNotification(
@@ -277,60 +367,12 @@
   MessageCenterChangeObserver observer;
   std::string result;
   bool success = content::ExecuteScriptAndExtractString(
-      browser->tab_strip_model()->GetActiveWebContents(),
-      script,
-      &result);
+      GetActiveWebContents(browser), script, &result);
   if (!success || result != "1")
     return false;
   return observer.Wait();
 }
 
-bool NotificationsTest::PerformActionOnInfoBar(
-    Browser* browser,
-    InfobarAction action,
-    size_t infobar_index,
-    int tab_index) {
-  InfoBarService* infobar_service = InfoBarService::FromWebContents(
-      browser->tab_strip_model()->GetWebContentsAt(tab_index));
-  if (infobar_index >= infobar_service->infobar_count()) {
-    ADD_FAILURE();
-    return false;
-  }
-
-  infobars::InfoBar* infobar = infobar_service->infobar_at(infobar_index);
-  infobars::InfoBarDelegate* infobar_delegate = infobar->delegate();
-  switch (action) {
-    case DISMISS:
-      infobar_delegate->InfoBarDismissed();
-      infobar_service->RemoveInfoBar(infobar);
-      return true;
-
-    case ALLOW: {
-      ConfirmInfoBarDelegate* confirm_infobar_delegate =
-          infobar_delegate->AsConfirmInfoBarDelegate();
-      if (!confirm_infobar_delegate) {
-        ADD_FAILURE();
-      } else if (confirm_infobar_delegate->Accept()) {
-        infobar_service->RemoveInfoBar(infobar);
-        return true;
-      }
-    }
-
-    case DENY: {
-      ConfirmInfoBarDelegate* confirm_infobar_delegate =
-          infobar_delegate->AsConfirmInfoBarDelegate();
-      if (!confirm_infobar_delegate) {
-        ADD_FAILURE();
-      } else if (confirm_infobar_delegate->Cancel()) {
-        infobar_service->RemoveInfoBar(infobar);
-        return true;
-      }
-    }
-  }
-
-  return false;
-}
-
 void NotificationsTest::GetPrefsByContentSetting(
     ContentSetting setting,
     ContentSettingsForOneType* settings) {
@@ -376,9 +418,8 @@
   // That's considered a user gesture to webkit, and should produce an infobar.
   bool result;
   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
-      browser()->tab_strip_model()->GetActiveWebContents(),
-      "window.domAutomationController.send(request());",
-      &result));
+      GetActiveWebContents(browser()),
+      "window.domAutomationController.send(request());", &result));
   EXPECT_TRUE(result);
 
   InfoBarService* infobar_service = InfoBarService::FromWebContents(
@@ -441,41 +482,36 @@
   ASSERT_EQ(0, GetNotificationCount());
 }
 
-IN_PROC_BROWSER_TEST_F(NotificationsTest, TestPermissionInfobarAppears) {
+// Requests notification privileges and verifies the prompt appears.
+IN_PROC_BROWSER_TEST_F(NotificationsTest, TestPermissionRequestUIAppears) {
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
-  // Requests notification privileges and verifies the infobar appears.
   ui_test_utils::NavigateToURL(browser(), GetTestPageURL());
-  ASSERT_TRUE(RequestPermissionAndWait(browser()));
-
+  EXPECT_TRUE(RequestPermissionAndWait(browser()));
   ASSERT_EQ(0, GetNotificationCount());
-  ASSERT_NO_FATAL_FAILURE(VerifyInfoBar(browser(), 0));
 }
 
-IN_PROC_BROWSER_TEST_F(NotificationsTest, TestAllowOnPermissionInfobar) {
+IN_PROC_BROWSER_TEST_F(NotificationsTest, TestAllowOnPermissionRequestUI) {
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
-  // Tries to create a notification and clicks allow on the infobar.
+  // Tries to create a notification & clicks 'allow' on the prompt.
   ui_test_utils::NavigateToURL(browser(), GetTestPageURL());
   // This notification should not be shown because we do not have permission.
   CreateSimpleNotification(browser(), false);
   ASSERT_EQ(0, GetNotificationCount());
 
-  ASSERT_TRUE(RequestPermissionAndWait(browser()));
-  ASSERT_TRUE(PerformActionOnInfoBar(browser(), ALLOW, 0, 0));
+  ASSERT_TRUE(RequestAndAcceptPermission(browser()));
 
   CreateSimpleNotification(browser(), true);
   EXPECT_EQ(1, GetNotificationCount());
 }
 
-IN_PROC_BROWSER_TEST_F(NotificationsTest, TestDenyOnPermissionInfobar) {
+IN_PROC_BROWSER_TEST_F(NotificationsTest, TestDenyOnPermissionRequestUI) {
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
-  // Test that no notification is created
-  // when Deny is chosen from permission infobar.
+  // Test that no notification is created when Deny is chosen from prompt.
   ui_test_utils::NavigateToURL(browser(), GetTestPageURL());
-  ASSERT_TRUE(RequestPermissionAndWait(browser()));
-  PerformActionOnInfoBar(browser(), DENY, 0, 0);
+  ASSERT_TRUE(RequestAndDenyPermission(browser()));
   CreateSimpleNotification(browser(), false);
   ASSERT_EQ(0, GetNotificationCount());
   ContentSettingsForOneType settings;
@@ -483,13 +519,12 @@
   EXPECT_TRUE(CheckOriginInSetting(settings, GetTestPageURL()));
 }
 
-IN_PROC_BROWSER_TEST_F(NotificationsTest, TestClosePermissionInfobar) {
+IN_PROC_BROWSER_TEST_F(NotificationsTest, TestClosePermissionRequestUI) {
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
-  // Test that no notification is created when permission infobar is dismissed.
+  // Test that no notification is created when prompt is dismissed.
   ui_test_utils::NavigateToURL(browser(), GetTestPageURL());
-  ASSERT_TRUE(RequestPermissionAndWait(browser()));
-  PerformActionOnInfoBar(browser(), DISMISS, 0, 0);
+  ASSERT_TRUE(RequestAndDismissPermission(browser()));
   CreateSimpleNotification(browser(), false);
   ASSERT_EQ(0, GetNotificationCount());
   ContentSettingsForOneType settings;
@@ -613,14 +648,12 @@
   // Verify that allow/deny origin preferences are not saved in incognito.
   Browser* incognito = CreateIncognitoBrowser();
   ui_test_utils::NavigateToURL(incognito, GetTestPageURL());
-  ASSERT_TRUE(RequestPermissionAndWait(incognito));
-  PerformActionOnInfoBar(incognito, DENY, 0, 0);
+  ASSERT_TRUE(RequestAndDenyPermission(incognito));
   CloseBrowserWindow(incognito);
 
   incognito = CreateIncognitoBrowser();
   ui_test_utils::NavigateToURL(incognito, GetTestPageURL());
-  ASSERT_TRUE(RequestPermissionAndWait(incognito));
-  PerformActionOnInfoBar(incognito, ALLOW, 0, 0);
+  ASSERT_TRUE(RequestAndAcceptPermission(incognito));
   CreateSimpleNotification(incognito, true);
   ASSERT_EQ(1, GetNotificationCount());
   CloseBrowserWindow(incognito);
@@ -643,24 +676,17 @@
   Browser* browser = CreateIncognitoBrowser();
   ui_test_utils::NavigateToURL(browser, GetTestPageURL());
   browser->tab_strip_model()->ActivateTabAt(0, true);
-  ASSERT_TRUE(RequestPermissionAndWait(browser));
-  PerformActionOnInfoBar(browser, ALLOW, 0, 0);
+  ASSERT_TRUE(RequestAndAcceptPermission(browser));
   CreateSimpleNotification(browser, true);
   ASSERT_EQ(1, GetNotificationCount());
 }
 
-IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCloseTabWithPermissionInfobar) {
+IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCloseTabWithPermissionRequestUI) {
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
-  // Test that user can close tab when infobar present.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(),
-      GURL("about:blank"),
-      NEW_BACKGROUND_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
-  browser()->tab_strip_model()->ActivateTabAt(0, true);
+  // Test that user can close tab when infobar or bubble present.
   ui_test_utils::NavigateToURL(browser(), GetTestPageURL());
-  ASSERT_TRUE(RequestPermissionAndWait(browser()));
+  EXPECT_TRUE(RequestPermissionAndWait(browser()));
   content::WebContentsDestroyedWatcher destroyed_watcher(
       browser()->tab_strip_model()->GetWebContentsAt(0));
   browser()->tab_strip_model()->CloseWebContentsAt(0,
@@ -668,9 +694,13 @@
   destroyed_watcher.Wait();
 }
 
-IN_PROC_BROWSER_TEST_F(
-    NotificationsTest,
-    TestNavigateAwayWithPermissionInfobar) {
+IN_PROC_BROWSER_TEST_F(NotificationsTest, TestNavigateAwayWithInfobar) {
+  // This test currently fails for permission bubbles because the same bubble
+  // will span the reload, unlike infobars where the bubble is dismissed during
+  // navigation. See crbug.com/493468.
+  if (PermissionBubbleManager::Enabled())
+    return;
+
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Test navigating away when an infobar is present,
@@ -684,8 +714,7 @@
   ui_test_utils::NavigateToURL(browser(), GetTestPageURL());
   ASSERT_TRUE(RequestPermissionAndWait(browser()));
   ui_test_utils::NavigateToURL(browser(), GetTestPageURL());
-  ASSERT_TRUE(RequestPermissionAndWait(browser()));
-  PerformActionOnInfoBar(browser(), ALLOW, 0, 0);
+  ASSERT_TRUE(RequestAndAcceptPermission(browser()));
   CreateSimpleNotification(browser(), true);
   ASSERT_EQ(1, GetNotificationCount());
 }
diff --git a/chrome/browser/password_manager/password_store_mac.cc b/chrome/browser/password_manager/password_store_mac.cc
index 09319f8..8b0d7be 100644
--- a/chrome/browser/password_manager/password_store_mac.cc
+++ b/chrome/browser/password_manager/password_store_mac.cc
@@ -924,6 +924,7 @@
       login_metadata_db_(login_db.Pass()) {
   DCHECK(keychain_.get());
   DCHECK(login_metadata_db_.get());
+  login_metadata_db_->set_clear_password_values(true);
 }
 
 PasswordStoreMac::~PasswordStoreMac() {}
diff --git a/chrome/browser/password_manager/password_store_mac_unittest.cc b/chrome/browser/password_manager/password_store_mac_unittest.cc
index ccd0cee..73443cc 100644
--- a/chrome/browser/password_manager/password_store_mac_unittest.cc
+++ b/chrome/browser/password_manager/password_store_mac_unittest.cc
@@ -11,9 +11,11 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/test/histogram_tester.h"
 #include "base/thread_task_runner_handle.h"
 #include "chrome/browser/password_manager/password_store_mac_internal.h"
 #include "chrome/common/chrome_paths.h"
+#include "components/os_crypt/os_crypt.h"
 #include "components/password_manager/core/browser/login_database.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
@@ -1205,6 +1207,9 @@
   void SetUp() override {
     ASSERT_TRUE(db_dir_.CreateUniqueTempDir());
 
+    // Ensure that LoginDatabase will use the mock keychain if it needs to
+    // encrypt/decrypt a password.
+    OSCrypt::UseMockKeychain(true);
     scoped_ptr<password_manager::LoginDatabase> login_db(
         new password_manager::LoginDatabase(test_login_db_file_path()));
     CreateAndInitPasswordStore(login_db.Pass());
@@ -1213,7 +1218,15 @@
     FinishAsyncProcessing();
   }
 
-  void TearDown() override { ClosePasswordStore(); }
+  void TearDown() override {
+    ClosePasswordStore();
+    // Whatever a test did, PasswordStoreMac stores only empty password values
+    // in LoginDatabase. The empty valus do not require encryption and therefore
+    // OSCrypt shouldn't call the Keychain. The histogram doesn't cover the
+    // internet passwords.
+    EXPECT_FALSE(histogram_tester_.GetHistogramSamplesSinceCreation(
+        "OSX.Keychain.Access"));
+  }
 
   void CreateAndInitPasswordStore(
       scoped_ptr<password_manager::LoginDatabase> login_db) {
@@ -1314,6 +1327,7 @@
 
   base::ScopedTempDir db_dir_;
   scoped_refptr<TestPasswordStoreMac> store_;
+  base::HistogramTester histogram_tester_;
 };
 
 TEST_F(PasswordStoreMacTest, TestStoreUpdate) {
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 14812ac..d0acd58 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -43,6 +43,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/drive/drive_pref_names.h"
+#include "chrome/browser/chromeos/platform_keys/key_permissions_policy_handler.h"
 #include "chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.h"
 #include "chromeos/chromeos_pref_names.h"
 #include "chromeos/dbus/power_policy_controller.h"
@@ -776,6 +777,8 @@
       key::kSessionLocales, NULL, chrome_schema, SCHEMA_STRICT,
       SimpleSchemaValidatingPolicyHandler::RECOMMENDED_ALLOWED,
       SimpleSchemaValidatingPolicyHandler::MANDATORY_PROHIBITED)));
+  handlers->AddHandler(make_scoped_ptr(
+      new chromeos::KeyPermissionsPolicyHandler(chrome_schema)));
 #endif  // defined(OS_CHROMEOS)
 
   return handlers.Pass();
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index c81efd44..9e3e8a0d 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -173,6 +173,7 @@
 #include "chrome/browser/chromeos/login/users/multi_profile_user_controller.h"
 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
 #include "chrome/browser/chromeos/net/proxy_config_handler.h"
+#include "chrome/browser/chromeos/platform_keys/key_permissions.h"
 #include "chrome/browser/chromeos/policy/auto_enrollment_client.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/policy/consumer_management_service.h"
@@ -490,6 +491,7 @@
 #if defined(OS_CHROMEOS)
   chromeos::first_run::RegisterProfilePrefs(registry);
   chromeos::file_system_provider::RegisterProfilePrefs(registry);
+  chromeos::KeyPermissions::RegisterProfilePrefs(registry);
   chromeos::MultiProfileUserController::RegisterProfilePrefs(registry);
   chromeos::Preferences::RegisterProfilePrefs(registry);
   chromeos::proxy_config::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 7d999ef..a519469 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -29,7 +29,6 @@
 #include "base/trace_event/trace_event.h"
 #include "base/version.h"
 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
-#include "chrome/browser/autocomplete/shortcuts_backend.h"
 #include "chrome/browser/background/background_contents_service_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
@@ -87,6 +86,7 @@
 #include "components/domain_reliability/service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/metrics/metrics_service.h"
+#include "components/omnibox/shortcuts_backend.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/ui/zoom/zoom_event_manager.h"
diff --git a/chrome/browser/profiles/profile_info_cache.cc b/chrome/browser/profiles/profile_info_cache.cc
index e80e030..2c1d80f 100644
--- a/chrome/browser/profiles/profile_info_cache.cc
+++ b/chrome/browser/profiles/profile_info_cache.cc
@@ -50,6 +50,7 @@
 const char kIsUsingDefaultAvatarKey[] = "is_using_default_avatar";
 const char kAvatarIconKey[] = "avatar_icon";
 const char kAuthCredentialsKey[] = "local_auth_credentials";
+const char kPasswordTokenKey[] = "gaia_password_token";
 const char kUseGAIAPictureKey[] = "use_gaia_picture";
 const char kBackgroundAppsKey[] = "background_apps";
 const char kGAIAPictureFileNameKey[] = "gaia_picture_file_name";
@@ -348,6 +349,13 @@
   return credentials;
 }
 
+std::string ProfileInfoCache::GetPasswordChangeDetectionTokenAtIndex(
+    size_t index) const {
+  std::string token;
+  GetInfoForProfileAtIndex(index)->GetString(kPasswordTokenKey, &token);
+  return token;
+}
+
 bool ProfileInfoCache::GetBackgroundStatusOfProfileAtIndex(
     size_t index) const {
   bool background_app_status;
@@ -627,6 +635,16 @@
   SetInfoForProfileAtIndex(index, info.release());
 }
 
+void ProfileInfoCache::SetPasswordChangeDetectionTokenAtIndex(
+    size_t index,
+    const std::string& token) {
+  scoped_ptr<base::DictionaryValue> info(
+      GetInfoForProfileAtIndex(index)->DeepCopy());
+  info->SetString(kPasswordTokenKey, token);
+  // This takes ownership of |info|.
+  SetInfoForProfileAtIndex(index, info.release());
+}
+
 void ProfileInfoCache::SetBackgroundStatusOfProfileAtIndex(
     size_t index,
     bool running_background_apps) {
diff --git a/chrome/browser/profiles/profile_info_cache.h b/chrome/browser/profiles/profile_info_cache.h
index 2214d6d8..38edaa2 100644
--- a/chrome/browser/profiles/profile_info_cache.h
+++ b/chrome/browser/profiles/profile_info_cache.h
@@ -66,6 +66,8 @@
   const gfx::Image& GetAvatarIconOfProfileAtIndex(size_t index) override;
   std::string GetLocalAuthCredentialsOfProfileAtIndex(
       size_t index) const override;
+  std::string GetPasswordChangeDetectionTokenAtIndex(
+      size_t index) const override;
   // Note that a return value of false could mean an error in collection or
   // that there are currently no background apps running. However, the action
   // which results is the same in both cases (thus far).
@@ -105,6 +107,8 @@
   void SetSupervisedUserIdOfProfileAtIndex(size_t index, const std::string& id);
   void SetLocalAuthCredentialsOfProfileAtIndex(size_t index,
                                                const std::string& auth);
+  void SetPasswordChangeDetectionTokenAtIndex(size_t index,
+                                              const std::string& token);
   void SetBackgroundStatusOfProfileAtIndex(size_t index,
                                            bool running_background_apps);
   // Warning: This will re-sort profiles and thus may change indices!
diff --git a/chrome/browser/profiles/profile_info_interface.h b/chrome/browser/profiles/profile_info_interface.h
index aa8d11e..2534a6b 100644
--- a/chrome/browser/profiles/profile_info_interface.h
+++ b/chrome/browser/profiles/profile_info_interface.h
@@ -41,6 +41,8 @@
 
   virtual std::string GetLocalAuthCredentialsOfProfileAtIndex(
       size_t index) const = 0;
+  virtual std::string GetPasswordChangeDetectionTokenAtIndex(
+      size_t index) const = 0;
 
   // Returns true if the profile at the given index is currently running any
   // background apps.
diff --git a/chrome/browser/profiles/profiles_state.cc b/chrome/browser/profiles/profiles_state.cc
index 7b63e08..930bc83 100644
--- a/chrome/browser/profiles/profiles_state.cc
+++ b/chrome/browser/profiles/profiles_state.cc
@@ -157,10 +157,10 @@
   return profile->IsGuestSession() || !profile->IsOffTheRecord();
 }
 
-bool IsProfileLocked(Profile* profile) {
+bool IsProfileLocked(const base::FilePath& path) {
   const ProfileInfoCache& cache =
       g_browser_process->profile_manager()->GetProfileInfoCache();
-  size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
+  size_t profile_index = cache.GetIndexOfProfileWithPath(path);
 
   if (profile_index == std::string::npos)
     return false;
diff --git a/chrome/browser/profiles/profiles_state.h b/chrome/browser/profiles/profiles_state.h
index 9e54a0a..3b5f8b0 100644
--- a/chrome/browser/profiles/profiles_state.h
+++ b/chrome/browser/profiles/profiles_state.h
@@ -64,10 +64,11 @@
 // incognito profiles.
 bool IsRegularOrGuestSession(Browser* browser);
 
-// Returns true if sign in is required to browse as this profile.
+// Returns true if sign in is required to browse as this profile.  Call with
+// profile->GetPath() if you have a profile pointer.
 // TODO(mlerman): Refactor appropriate calls to
 // ProfileInfoCache::ProfileIsSigninRequiredAtIndex to call here instead.
-bool IsProfileLocked(Profile* profile);
+bool IsProfileLocked(const base::FilePath& path);
 
 // If the lock-enabled information for this profile is not up to date, starts
 // an update for the Gaia profile info.
diff --git a/chrome/browser/resources/about_version.css b/chrome/browser/resources/about_version.css
index 343dedf..6ac70ed 100644
--- a/chrome/browser/resources/about_version.css
+++ b/chrome/browser/resources/about_version.css
@@ -18,7 +18,7 @@
 
 #inner {
   padding-top: 10px;
-  width: 550px;
+  word-break: break-word;
 }
 
 .label {
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
index a4e9576..eabbb5b 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -440,7 +440,7 @@
     },
     textField: {
       speak: '$name $value $if(' +
-          '$type, @input_type_+$type, @input_type_text)',
+          '$inputType, @input_type_+$inputType, @input_type_text)',
       braille: ''
     },
     toolbar: {
diff --git a/chrome/browser/resources/net_internals/source_entry.js b/chrome/browser/resources/net_internals/source_entry.js
index bf4259d70..e1e217b18 100644
--- a/chrome/browser/resources/net_internals/source_entry.js
+++ b/chrome/browser/resources/net_internals/source_entry.js
@@ -82,7 +82,6 @@
         // TODO(ricea): Remove SOCKET_STREAM after M41 is released.
         case EventSourceType.SOCKET_STREAM:
         case EventSourceType.HTTP_STREAM_JOB:
-        case EventSourceType.ASYNC_REVALIDATION:
           this.description_ = e.params.url;
           break;
         case EventSourceType.CONNECT_JOB:
diff --git a/chrome/browser/resources/pdf/browser_api.js b/chrome/browser/resources/pdf/browser_api.js
index 52ce4c6..c1a599c73 100644
--- a/chrome/browser/resources/pdf/browser_api.js
+++ b/chrome/browser/resources/pdf/browser_api.js
@@ -4,16 +4,23 @@
 
 'use strict';
 
-let defaultZoomPromise = (function() {
+/**
+ * Returns a promise that will resolve to the default zoom factor.
+ * @param {!Object} streamInfo The stream object pointing to the data contained
+ *     in the PDF.
+ * @return {Promise<number>} A promise that will resolve to the default zoom
+ *     factor.
+ */
+function lookupDefaultZoom(streamInfo) {
   if (!chrome.tabs)
     return Promise.resolve(1);
 
   return new Promise(function(resolve, reject) {
-    chrome.tabs.getZoomSettings(function(zoomSettings) {
+    chrome.tabs.getZoomSettings(streamInfo.tabId, function(zoomSettings) {
       resolve(zoomSettings.defaultZoomFactor);
-    }.bind(this));
-  }.bind(this));
-})();
+    });
+  });
+}
 
 /**
  * A class providing an interface to the browser.
@@ -34,14 +41,13 @@
 
   /**
    * Returns a promise to a BrowserApi.
-   * @param {!Promise.<Object>} streamInfoPromise A promise that will resolve
-   *     the stream object pointing to the data contained in the PDF.
+   * @param {!Object} streamInfo The stream object pointing to the data
+   *     contained in the PDF.
    * @param {boolean} manageZoom Whether to manage zoom.
    */
-  static create(streamInfoPromise, manageZoom) {
-    return Promise.all([streamInfoPromise, defaultZoomPromise]).then(
-        function(results) {
-      return new BrowserApi(results[0], results[1], manageZoom);
+  static create(streamInfo, manageZoom) {
+    return lookupDefaultZoom(streamInfo).then(function(defaultZoom) {
+      return new BrowserApi(streamInfo, defaultZoom, manageZoom);
     });
   }
 
@@ -102,28 +108,28 @@
 
 /**
  * Creates a BrowserApi for an extension running as a mime handler.
- * @return {Promise.<BrowserApi>} A promise to a BrowserApi instance constructed
+ * @return {Promise<BrowserApi>} A promise to a BrowserApi instance constructed
  *     using the mimeHandlerPrivate API.
  */
 function createBrowserApiForMimeHandlerView() {
   return new Promise(function(resolve, reject) {
     chrome.mimeHandlerPrivate.getStreamInfo(resolve);
   }).then(function(streamInfo) {
-    if (streamInfo.embedded || streamInfo.tabId == -1)
-      return BrowserApi.create(streamInfo, false);
-
-    return BrowserApi.create(new Promise(function(resolve, reject) {
+    let manageZoom = !streamInfo.embedded && streamInfo.tabId != -1;
+    return new Promise(function(resolve, reject) {
+      if (!manageZoom) {
+        resolve();
+        return;
+      }
       chrome.tabs.setZoomSettings(
           streamInfo.tabId, {mode: 'manual', scope: 'per-tab'}, resolve);
-      }).then(function() {
-        return streamInfo;
-      }), true);
+    }).then(function() { return BrowserApi.create(streamInfo, manageZoom); });
   });
 }
 
 /**
  * Creates a BrowserApi instance for an extension not running as a mime handler.
- * @return {Promise.<BrowserApi>} A promise to a BrowserApi instance constructed
+ * @return {Promise<BrowserApi>} A promise to a BrowserApi instance constructed
  *     from the URL.
  */
 function createBrowserApiForStandaloneExtension() {
@@ -135,20 +141,21 @@
     embedded: window.parent != window,
     tabId: -1,
   };
-  if (!chrome.tabs)
-    return BrowserApi.create(streamInfo, false);
-
-  return BrowserApi.create(new Promise(function(resolve, reject) {
+  return new Promise(function(resolve, reject) {
+    if (!chrome.tabs) {
+      resolve();
+      return;
+    }
     chrome.tabs.getCurrent(function(tab) {
       streamInfo.tabId = tab.id;
-      resolve(streamInfo);
+      resolve();
     });
-  }), false);
+  }).then(function() { return BrowserApi.create(streamInfo, false); });
 }
 
 /**
  * Returns a promise that will resolve to a BrowserApi instance.
- * @return {Promise.<BrowserApi>} A promise to a BrowserApi instance for the
+ * @return {Promise<BrowserApi>} A promise to a BrowserApi instance for the
  *     current environment.
  */
 function createBrowserApi() {
diff --git a/chrome/browser/resources/print_preview/search/destination_list.css b/chrome/browser/resources/print_preview/search/destination_list.css
index 58c5743..2ebdb9a 100644
--- a/chrome/browser/resources/print_preview/search/destination_list.css
+++ b/chrome/browser/resources/print_preview/search/destination_list.css
@@ -49,5 +49,5 @@
 }
 
 .destination-list .total {
-  color: #999;
+  color: #777;
 }
diff --git a/chrome/browser/resources/print_preview/search/destination_list_item.css b/chrome/browser/resources/print_preview/search/destination_list_item.css
index 3501f28c..c98dc8f 100644
--- a/chrome/browser/resources/print_preview/search/destination_list_item.css
+++ b/chrome/browser/resources/print_preview/search/destination_list_item.css
@@ -82,7 +82,7 @@
 
 .extension-name {
   -webkit-margin-start: 1em;
-  color: #999;
+  color: #777;
   line-height: 24px;
   overflow: hidden;
   text-overflow: ellipsis;
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.cc b/chrome/browser/safe_browsing/client_side_detection_host.cc
index 6291766d..0a063ef 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
 #include "chrome/browser/safe_browsing/database_manager.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/safebrowsing_messages.h"
 #include "content/public/browser/browser_thread.h"
@@ -653,9 +654,13 @@
     callback = base::Bind(&ClientSideDetectionHost::MaybeShowPhishingWarning,
                           weak_factory_.GetWeakPtr());
   }
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
   // Send ping even if the browser feature extraction failed.
   csd_service_->SendClientReportPhishingRequest(
       request.release(),  // The service takes ownership of the request object.
+      profile->GetPrefs()->GetBoolean(
+          prefs::kSafeBrowsingExtendedReportingEnabled),
       callback);
 }
 
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index fc2d6e5..bb744cc 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -113,8 +113,9 @@
   MockClientSideDetectionService() : ClientSideDetectionService(NULL) {}
   virtual ~MockClientSideDetectionService() {}
 
-  MOCK_METHOD2(SendClientReportPhishingRequest,
+  MOCK_METHOD3(SendClientReportPhishingRequest,
                void(ClientPhishingRequest*,
+                    bool,
                     const ClientReportPhishingRequestCallback&));
   MOCK_METHOD2(SendClientReportMalwareRequest,
                void(ClientMalwareRequest*,
@@ -479,8 +480,8 @@
       .WillOnce(InvokeDoneCallback(&verdict));
   EXPECT_CALL(*csd_service_,
               SendClientReportPhishingRequest(
-                  Pointee(PartiallyEqualVerdict(verdict)), _))
-      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
+                  Pointee(PartiallyEqualVerdict(verdict)), _, _))
+      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<2>(&cb)));
   OnPhishingDetectionDone(verdict.SerializeAsString());
   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
   ASSERT_FALSE(cb.is_null());
@@ -511,8 +512,8 @@
       .WillOnce(InvokeDoneCallback(&verdict));
   EXPECT_CALL(*csd_service_,
               SendClientReportPhishingRequest(
-                  Pointee(PartiallyEqualVerdict(verdict)), _))
-      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
+                  Pointee(PartiallyEqualVerdict(verdict)), _, _))
+      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<2>(&cb)));
   OnPhishingDetectionDone(verdict.SerializeAsString());
   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
   ASSERT_FALSE(cb.is_null());
@@ -544,8 +545,8 @@
       .WillOnce(InvokeDoneCallback(&verdict));
   EXPECT_CALL(*csd_service_,
               SendClientReportPhishingRequest(
-                  Pointee(PartiallyEqualVerdict(verdict)), _))
-      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
+                  Pointee(PartiallyEqualVerdict(verdict)), _, _))
+      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<2>(&cb)));
   OnPhishingDetectionDone(verdict.SerializeAsString());
   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
@@ -598,8 +599,8 @@
       .WillOnce(InvokeDoneCallback(&verdict));
   EXPECT_CALL(*csd_service_,
               SendClientReportPhishingRequest(
-                  Pointee(PartiallyEqualVerdict(verdict)), _))
-      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
+                  Pointee(PartiallyEqualVerdict(verdict)), _, _))
+      .WillOnce(DoAll(DeleteArg<0>(), SaveArg<2>(&cb)));
   OnPhishingDetectionDone(verdict.SerializeAsString());
   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
@@ -623,9 +624,9 @@
   verdict.set_client_score(0.8f);
   EXPECT_CALL(*csd_service_,
               SendClientReportPhishingRequest(
-                  Pointee(PartiallyEqualVerdict(verdict)), _))
+                  Pointee(PartiallyEqualVerdict(verdict)), _, _))
       .WillOnce(DoAll(DeleteArg<0>(),
-                      SaveArg<1>(&cb_other),
+                      SaveArg<2>(&cb_other),
                       QuitUIMessageLoop()));
   std::vector<GURL> redirect_chain;
   redirect_chain.push_back(other_phishing_url);
@@ -702,7 +703,7 @@
 
   EXPECT_CALL(*csd_service_,
               SendClientReportPhishingRequest(
-                  Pointee(PartiallyEqualVerdict(verdict)), CallbackIsNull()))
+                  Pointee(PartiallyEqualVerdict(verdict)), _, CallbackIsNull()))
       .WillOnce(DoAll(DeleteArg<0>(), QuitUIMessageLoop()));
   std::vector<GURL> redirect_chain;
   redirect_chain.push_back(url);
@@ -741,7 +742,7 @@
 
   EXPECT_CALL(*csd_service_,
               SendClientReportPhishingRequest(
-                  Pointee(PartiallyEqualVerdict(verdict)), CallbackIsNull()))
+                  Pointee(PartiallyEqualVerdict(verdict)), _, CallbackIsNull()))
       .WillOnce(DoAll(DeleteArg<0>(), QuitUIMessageLoop()));
   std::vector<GURL> redirect_chain;
   redirect_chain.push_back(url);
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.cc b/chrome/browser/safe_browsing/client_side_detection_service.cc
index 3e99968..6f431e5 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service.cc
@@ -7,7 +7,6 @@
 #include <algorithm>
 
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
@@ -16,18 +15,14 @@
 #include "base/prefs/pref_service.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
 #include "base/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
 #include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/safebrowsing_messages.h"
-#include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
@@ -68,14 +63,9 @@
 
 }  // namespace
 
-const size_t ClientSideDetectionService::kMaxModelSizeBytes = 90 * 1024;
-const int ClientSideDetectionService::kMaxReportsPerInterval = 3;
-// TODO(noelutz): once we know this mechanism works as intended we should fetch
-// the model much more frequently.  E.g., every 5 minutes or so.
-const int ClientSideDetectionService::kClientModelFetchIntervalMs = 3600 * 1000;
 const int ClientSideDetectionService::kInitialClientModelFetchDelayMs = 10000;
-
 const int ClientSideDetectionService::kReportsIntervalDays = 1;
+const int ClientSideDetectionService::kMaxReportsPerInterval = 3;
 const int ClientSideDetectionService::kNegativeCacheIntervalDays = 1;
 const int ClientSideDetectionService::kPositiveCacheIntervalMinutes = 30;
 
@@ -83,17 +73,6 @@
     "https://sb-ssl.google.com/safebrowsing/clientreport/phishing";
 const char ClientSideDetectionService::kClientReportMalwareUrl[] =
     "https://sb-ssl.google.com/safebrowsing/clientreport/malware-check";
-const char ClientSideDetectionService::kClientModelUrlPrefix[] =
-    "https://ssl.gstatic.com/safebrowsing/csd/";
-const char ClientSideDetectionService::kClientModelNamePattern[] =
-    "client_model_v5%s_variation_%d.pb";
-
-// Finch names
-const char ClientSideDetectionService::kClientModelFinchExperiment[] =
-    "ClientSideDetectionModel";
-const char ClientSideDetectionService::kClientModelFinchParam[] =
-    "ModelNum";
-
 
 struct ClientSideDetectionService::ClientReportInfo {
   ClientReportPhishingRequestCallback callback;
@@ -115,6 +94,14 @@
     : enabled_(false),
       request_context_getter_(request_context_getter),
       weak_factory_(this) {
+  base::Closure update_renderers =
+      base::Bind(&ClientSideDetectionService::SendModelToRenderers,
+                 base::Unretained(this));
+  model_loader_standard_.reset(
+      new ModelLoader(update_renderers, request_context_getter, false));
+  model_loader_extended_.reset(
+      new ModelLoader(update_renderers, request_context_getter, true));
+
   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
                  content::NotificationService::AllBrowserContextsAndSources());
 }
@@ -143,15 +130,17 @@
     return;
   enabled_ = enabled;
   if (enabled_) {
-    // Refresh the model when the service is enabled.  This can happen when the
-    // preference is toggled, or early during startup if the preference is
-    // already enabled. In a lot of cases the model will be in the cache so it
-    // won't actually be fetched from the network.
-    // We delay the first model fetch to avoid slowing down browser startup.
-    ScheduleFetchModel(kInitialClientModelFetchDelayMs);
+    // Refresh the models when the service is enabled.  This can happen when
+    // either of the preferences are toggled, or early during startup if
+    // safe browsing is already enabled. In a lot of cases the model will be
+    // in the cache so it  won't actually be fetched from the network.
+    // We delay the first model fetches to avoid slowing down browser startup.
+    model_loader_standard_->ScheduleFetch(kInitialClientModelFetchDelayMs);
+    model_loader_extended_->ScheduleFetch(kInitialClientModelFetchDelayMs);
   } else {
-    // Cancel pending requests.
-    model_fetcher_.reset();
+    // Cancel model loads in progress.
+    model_loader_standard_->CancelFetcher();
+    model_loader_extended_->CancelFetcher();
     // Invoke pending callbacks with a false verdict.
     for (std::map<const net::URLFetcher*, ClientReportInfo*>::iterator it =
              client_phishing_reports_.begin();
@@ -179,12 +168,14 @@
 
 void ClientSideDetectionService::SendClientReportPhishingRequest(
     ClientPhishingRequest* verdict,
+    bool is_extended_reporting,
     const ClientReportPhishingRequestCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::Bind(&ClientSideDetectionService::StartClientReportPhishingRequest,
-                 weak_factory_.GetWeakPtr(), verdict, callback));
+                 weak_factory_.GetWeakPtr(), verdict, is_extended_reporting,
+                 callback));
 }
 
 void ClientSideDetectionService::SendClientReportMalwareRequest(
@@ -213,12 +204,8 @@
     const net::URLFetcher* source) {
   std::string data;
   source->GetResponseAsString(&data);
-  if (source == model_fetcher_.get()) {
-    HandleModelResponse(
-        source, source->GetURL(), source->GetStatus(),
-        source->GetResponseCode(), source->GetCookies(), data);
-  } else if (client_phishing_reports_.find(source) !=
-             client_phishing_reports_.end()) {
+
+  if (client_phishing_reports_.find(source) != client_phishing_reports_.end()) {
     HandlePhishingVerdict(
         source, source->GetURL(), source->GetStatus(),
         source->GetResponseCode(), source->GetCookies(), data);
@@ -238,10 +225,6 @@
     const content::NotificationDetails& details) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_CREATED);
-  if (!model_.get()) {
-    // Model might not be ready or maybe there was an error.
-    return;
-  }
   SendModelToProcess(
       content::Source<content::RenderProcessHost>(source).ptr());
 }
@@ -250,12 +233,21 @@
     content::RenderProcessHost* process) {
   // The ClientSideDetectionService is enabled if _any_ active profile has
   // SafeBrowsing turned on.  Here we check the profile for each renderer
-  // process and only send the model to those that have SafeBrowsing enabled.
+  // process and only send the model to those that have SafeBrowsing enabled,
+  // and we select the model based on the extended reporting setting.
   Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
   std::string model;
   if (profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
-    DVLOG(2) << "Sending phishing model to RenderProcessHost @" << process;
-    model = model_str_;
+    if (profile->GetPrefs()->GetBoolean(
+            prefs::kSafeBrowsingExtendedReportingEnabled)) {
+      DVLOG(2) << "Sending phishing model " << model_loader_extended_->name()
+               << " to RenderProcessHost @" << process;
+      model = model_loader_extended_->model_str();
+    } else {
+      DVLOG(2) << "Sending phishing model " << model_loader_standard_->name()
+               << " to RenderProcessHost @" << process;
+      model = model_loader_standard_->model_str();
+    }
   } else {
     DVLOG(2) << "Disabling client-side phishing detection for "
              << "RenderProcessHost @" << process;
@@ -271,79 +263,9 @@
   }
 }
 
-void ClientSideDetectionService::ScheduleFetchModel(int64 delay_ms) {
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kSbDisableAutoUpdate))
-    return;
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, base::Bind(&ClientSideDetectionService::StartFetchModel,
-                            weak_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(delay_ms));
-}
-
-// static
-std::string ClientSideDetectionService::MakeModelName() {
-  std::string num_str = variations::GetVariationParamValue(
-      kClientModelFinchExperiment, kClientModelFinchParam);
-  int model_number = 0;
-  if (!base::StringToInt(num_str, &model_number)) {
-    model_number = 0;  // Default model
-  }
-
-  // TODO(nparker): Factor out the model-fetching so we can have
-  // up to two models: One for extended reporting, and one for not.
-  // Until then, we'll use the non-extended reporting model.
-  return FillInModelName(false /* is_extended_reporting */, model_number);
-}
-
-// static
-std::string ClientSideDetectionService::FillInModelName(
-    bool is_extended_reporting,
-    int model_number) {
-  return base::StringPrintf(kClientModelNamePattern,
-                            is_extended_reporting ? "_ext" : "", model_number);
-}
-
-void ClientSideDetectionService::StartFetchModel() {
-  if (enabled_) {
-    // Start fetching the model either from the cache or possibly from the
-    // network if the model isn't in the cache.
-    fetching_model_name_ = MakeModelName();
-    std::string model_url = kClientModelUrlPrefix + fetching_model_name_;
-    model_fetcher_ = net::URLFetcher::Create(0 /* ID used for testing */,
-                                             GURL(model_url),
-                                             net::URLFetcher::GET, this);
-    model_fetcher_->SetRequestContext(request_context_getter_.get());
-    model_fetcher_->Start();
-  }
-}
-
-void ClientSideDetectionService::EndFetchModel(ClientModelStatus status) {
-  UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.ClientModelStatus",
-                            status,
-                            MODEL_STATUS_MAX);
-  if (status == MODEL_SUCCESS) {
-    SetBadSubnets(*model_, &bad_subnets_);
-    SendModelToRenderers();
-  }
-  int delay_ms = kClientModelFetchIntervalMs;
-  // If the most recently fetched model had a valid max-age and the model was
-  // valid we're scheduling the next model update for after the max-age expired.
-  if (model_max_age_.get() &&
-      (status == MODEL_SUCCESS || status == MODEL_NOT_CHANGED)) {
-    // We're adding 60s of additional delay to make sure we're past
-    // the model's age.
-    *model_max_age_ += base::TimeDelta::FromMinutes(1);
-    delay_ms = model_max_age_->InMilliseconds();
-  }
-  model_max_age_.reset();
-
-  // Schedule the next model reload.
-  ScheduleFetchModel(delay_ms);
-}
-
 void ClientSideDetectionService::StartClientReportPhishingRequest(
     ClientPhishingRequest* verdict,
+    bool is_extended_reporting,
     const ClientReportPhishingRequestCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_ptr<ClientPhishingRequest> request(verdict);
@@ -354,7 +276,17 @@
     return;
   }
 
-  request->set_model_filename(model_name_);
+  // Fill in metadata about which model we used.
+  if (is_extended_reporting) {
+    request->set_model_filename(model_loader_extended_->name());
+    request->mutable_population()->set_user_population(
+        ChromeUserPopulation::EXTENDED_REPORTING);
+  } else {
+    request->set_model_filename(model_loader_standard_->name());
+    request->mutable_population()->set_user_population(
+        ChromeUserPopulation::SAFE_BROWSING);
+  }
+  DVLOG(2) << "Starting report for hit on model " << request->model_filename();
 
   std::string request_data;
   if (!request->SerializeToString(&request_data)) {
@@ -432,48 +364,6 @@
   malware_report_times_.push(base::Time::Now());
 }
 
-void ClientSideDetectionService::HandleModelResponse(
-    const net::URLFetcher* source,
-    const GURL& url,
-    const net::URLRequestStatus& status,
-    int response_code,
-    const net::ResponseCookies& cookies,
-    const std::string& data) {
-  base::TimeDelta max_age;
-  if (status.is_success() && net::HTTP_OK == response_code &&
-      source->GetResponseHeaders() &&
-      source->GetResponseHeaders()->GetMaxAgeValue(&max_age)) {
-    model_max_age_.reset(new base::TimeDelta(max_age));
-  }
-  scoped_ptr<ClientSideModel> model(new ClientSideModel());
-  ClientModelStatus model_status;
-  if (!status.is_success() || net::HTTP_OK != response_code) {
-    model_status = MODEL_FETCH_FAILED;
-  } else if (data.empty()) {
-    model_status = MODEL_EMPTY;
-  } else if (data.size() > kMaxModelSizeBytes) {
-    model_status = MODEL_TOO_LARGE;
-  } else if (!model->ParseFromString(data)) {
-    model_status = MODEL_PARSE_ERROR;
-  } else if (!model->IsInitialized() || !model->has_version()) {
-    model_status = MODEL_MISSING_FIELDS;
-  } else if (!ModelHasValidHashIds(*model)) {
-    model_status = MODEL_BAD_HASH_IDS;
-  } else if (model->version() < 0 ||
-             (model_.get() && model->version() < model_->version())) {
-    model_status = MODEL_INVALID_VERSION_NUMBER;
-  } else if (model_.get() && model->version() == model_->version()) {
-    model_status = MODEL_NOT_CHANGED;
-  } else {
-    // The model is valid => replace the existing model with the new one.
-    model_str_.assign(data);
-    model_.swap(model);
-    model_name_ = fetching_model_name_;
-    fetching_model_name_ = "";
-    model_status = MODEL_SUCCESS;
-  }
-  EndFetchModel(model_status);
-}
 
 void ClientSideDetectionService::HandlePhishingVerdict(
     const net::URLFetcher* source,
@@ -626,53 +516,6 @@
 }
 
 // static
-void ClientSideDetectionService::SetBadSubnets(const ClientSideModel& model,
-                                               BadSubnetMap* bad_subnets) {
-  bad_subnets->clear();
-  for (int i = 0; i < model.bad_subnet_size(); ++i) {
-    int size = model.bad_subnet(i).size();
-    if (size < 0 || size > static_cast<int>(net::kIPv6AddressSize) * 8) {
-      DLOG(ERROR) << "Invalid bad subnet size: " << size;
-      continue;
-    }
-    if (model.bad_subnet(i).prefix().size() != crypto::kSHA256Length) {
-      DLOG(ERROR) << "Invalid bad subnet prefix length: "
-                  << model.bad_subnet(i).prefix().size();
-      continue;
-    }
-    // We precompute the mask for the given subnet size to speed up lookups.
-    // Basically we need to create a 16B long string which has the highest
-    // |size| bits sets to one.
-    std::string mask(net::kIPv6AddressSize, '\x00');
-    mask.replace(0, size / 8, size / 8, '\xFF');
-    if (size % 8) {
-      mask[size / 8] = 0xFF << (8 - (size % 8));
-    }
-    (*bad_subnets)[mask].insert(model.bad_subnet(i).prefix());
-  }
-}
-
-// static
-bool ClientSideDetectionService::ModelHasValidHashIds(
-    const ClientSideModel& model) {
-  const int max_index = model.hashes_size() - 1;
-  for (int i = 0; i < model.rule_size(); ++i) {
-    for (int j = 0; j < model.rule(i).feature_size(); ++j) {
-      if (model.rule(i).feature(j) < 0 ||
-          model.rule(i).feature(j) > max_index) {
-        return false;
-      }
-    }
-  }
-  for (int i = 0; i < model.page_term_size(); ++i) {
-    if (model.page_term(i) < 0 || model.page_term(i) > max_index) {
-      return false;
-    }
-  }
-  return true;
-}
-
-// static
 GURL ClientSideDetectionService::GetClientReportUrl(
     const std::string& report_url) {
   GURL url(report_url);
@@ -682,4 +525,5 @@
 
   return url;
 }
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.h b/chrome/browser/safe_browsing/client_side_detection_service.h
index 5ed0f117..09c36b4 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.h
+++ b/chrome/browser/safe_browsing/client_side_detection_service.h
@@ -28,6 +28,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "chrome/browser/safe_browsing/client_side_model_loader.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
@@ -58,6 +59,8 @@
 class ClientPhishingResponse;
 class ClientSideModel;
 
+// Main service which pushes models to the renderers, responds to classification
+// requests. This owns two ModelLoader objects.
 class ClientSideDetectionService : public net::URLFetcherDelegate,
                                    public content::NotificationObserver {
  public:
@@ -101,13 +104,15 @@
   // The URL scheme of the |url()| in the request should be HTTP.  This method
   // takes ownership of the |verdict| as well as the |callback| and calls the
   // the callback once the result has come back from the server or if an error
-  // occurs during the fetch.  If the service is disabled or an error occurs
+  // occurs during the fetch.  |is_extended_reporting| should be set based on
+  // the active profile setting. If the service is disabled or an error occurs
   // the phishing verdict will always be false.  The callback is always called
   // after SendClientReportPhishingRequest() returns and on the same thread as
   // SendClientReportPhishingRequest() was called.  You may set |callback| to
   // NULL if you don't care about the server verdict.
   virtual void SendClientReportPhishingRequest(
       ClientPhishingRequest* verdict,
+      bool is_extended_reporting,
       const ClientReportPhishingRequestCallback& callback);
 
   // Similar to above one, instead send ClientMalwareRequest
@@ -139,50 +144,20 @@
   // reports in the last kReportsInterval.
   virtual bool OverMalwareReportLimit();
 
+  // Sends a model to each renderer.
+  virtual void SendModelToRenderers();
+
+  base::WeakPtr<ClientSideDetectionService> GetWeakPtr();
+
  protected:
   // Use Create() method to create an instance of this object.
   explicit ClientSideDetectionService(
       net::URLRequestContextGetter* request_context_getter);
 
-  // Enum used to keep stats about why we fail to get the client model.
-  enum ClientModelStatus {
-    MODEL_SUCCESS,
-    MODEL_NOT_CHANGED,
-    MODEL_FETCH_FAILED,
-    MODEL_EMPTY,
-    MODEL_TOO_LARGE,
-    MODEL_PARSE_ERROR,
-    MODEL_MISSING_FIELDS,
-    MODEL_INVALID_VERSION_NUMBER,
-    MODEL_BAD_HASH_IDS,
-    MODEL_STATUS_MAX  // Always add new values before this one.
-  };
-
-  // Starts fetching the model from the network or the cache.  This method
-  // is called periodically to check whether a new client model is available
-  // for download.
-  void StartFetchModel();
-
-  // Schedules the next fetch of the model.
-  virtual void ScheduleFetchModel(int64 delay_ms);  // Virtual for testing.
-
-  // This method is called when we're done fetching the model either because
-  // we hit an error somewhere or because we're actually done fetch and
-  // validating the model.
-  virtual void EndFetchModel(ClientModelStatus status);  // Virtual for testing.
-
  private:
   friend class ClientSideDetectionServiceTest;
-  FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionServiceTest, FetchModelTest);
-  FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionServiceTest, SetBadSubnets);
   FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionServiceTest,
                            SetEnabledAndRefreshState);
-  FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionServiceTest, IsBadIpAddress);
-  FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionServiceTest,
-                           ModelHasValidHashIds);
-  FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionServiceTest, ModelNamesTest);
-  FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionServiceTest,
-                           FetchExperimentalModelTest);
 
   // CacheState holds all information necessary to respond to a caller without
   // actually making a HTTP request.
@@ -194,24 +169,9 @@
   };
   typedef std::map<GURL, linked_ptr<CacheState> > PhishingCache;
 
-  // A tuple of (IP address block, prefix size) representing a private
-  // IP address range.
-  typedef std::pair<net::IPAddressNumber, size_t> AddressRange;
-
-  // Maps a IPv6 subnet mask to a set of hashed IPv6 subnets.  The IPv6
-  // subnets are in network order and hashed with sha256.
-  typedef std::map<std::string /* subnet mask */,
-                   std::set<std::string /* hashed subnet */> > BadSubnetMap;
-
   static const char kClientReportMalwareUrl[];
   static const char kClientReportPhishingUrl[];
-  static const char kClientModelUrlPrefix[];
-  static const char kClientModelNamePattern[];
-  static const char kClientModelFinchExperiment[];
-  static const char kClientModelFinchParam[];
-  static const size_t kMaxModelSizeBytes;
   static const int kMaxReportsPerInterval;
-  static const int kClientModelFetchIntervalMs;
   static const int kInitialClientModelFetchDelayMs;
   static const int kReportsIntervalDays;
   static const int kNegativeCacheIntervalDays;
@@ -221,21 +181,13 @@
   // This method takes ownership of both pointers.
   void StartClientReportPhishingRequest(
       ClientPhishingRequest* verdict,
+      bool is_extended_reporting,
       const ClientReportPhishingRequestCallback& callback);
 
   void StartClientReportMalwareRequest(
       ClientMalwareRequest* verdict,
       const ClientReportMalwareRequestCallback& callback);
 
-  // Called by OnURLFetchComplete to handle the response from fetching the
-  // model.
-  void HandleModelResponse(const net::URLFetcher* source,
-                           const GURL& url,
-                           const net::URLRequestStatus& status,
-                           int response_code,
-                           const net::ResponseCookies& cookies,
-                           const std::string& data);
-
   // Called by OnURLFetchComplete to handle the server response from
   // sending the client-side phishing request.
   void HandlePhishingVerdict(const net::URLFetcher* source,
@@ -270,43 +222,17 @@
   // Send the model to the given renderer.
   void SendModelToProcess(content::RenderProcessHost* process);
 
-  // Same as above but sends the model to all rendereres.
-  void SendModelToRenderers();
-
-  // Reads the bad subnets from the client model and inserts them into
-  // |bad_subnets| for faster lookups.  This method is static to simplify
-  // testing.
-  static void SetBadSubnets(const ClientSideModel& model,
-                            BadSubnetMap* bad_subnets);
-
-
-  // Returns true iff all the hash id's in the client-side model point to
-  // valid hashes in the model.
-  static bool ModelHasValidHashIds(const ClientSideModel& model);
-
   // Returns the URL that will be used for phishing requests.
   static GURL GetClientReportUrl(const std::string& report_url);
 
-  // Construct a model name from parameters.
-  static std::string FillInModelName(bool is_extended_reporting,
-                                     int model_number);
-
-  // Construct a model name from client state.
-  static std::string MakeModelName();
-
   // Whether the service is running or not.  When the service is not running,
   // it won't download the model nor report detected phishing URLs.
   bool enabled_;
 
-  std::string model_str_;
-  scoped_ptr<ClientSideModel> model_;
-  scoped_ptr<base::TimeDelta> model_max_age_;
-  scoped_ptr<net::URLFetcher> model_fetcher_;
-
-  // Name of the model in model_.  This is the last component of the URL path.
-  std::string model_name_;
-  // Name of the model currently being fetched.
-  std::string fetching_model_name_;
+  // We load two models: One for stadard Safe Browsing profiles,
+  // and one for those opted into extended reporting.
+  scoped_ptr<ModelLoader> model_loader_standard_;
+  scoped_ptr<ModelLoader> model_loader_extended_;
 
   // Map of client report phishing request to the corresponding callback that
   // has to be invoked when the request is done.
@@ -339,10 +265,6 @@
   // The context we use to issue network requests.
   scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
 
-  // Map of bad subnets which are copied from the client model and put into
-  // this map to speed up lookups.
-  BadSubnetMap bad_subnets_;
-
   content::NotificationRegistrar registrar_;
 
   // Used to asynchronously call the callbacks for
@@ -351,6 +273,7 @@
 
   DISALLOW_COPY_AND_ASSIGN(ClientSideDetectionService);
 };
+
 }  // namespace safe_browsing
 
 #endif  // CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_H_
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
index 0bd2371..883aecf6 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
@@ -35,50 +35,38 @@
 
 namespace safe_browsing {
 namespace {
+
+class MockModelLoader : public ModelLoader {
+ public:
+  explicit MockModelLoader(const std::string model_name)
+      : ModelLoader(base::Closure(), model_name) {}
+  ~MockModelLoader() override {}
+
+  MOCK_METHOD1(ScheduleFetch, void(int64));
+  MOCK_METHOD0(CancelFetcher, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockModelLoader);
+};
+
 class MockClientSideDetectionService : public ClientSideDetectionService {
  public:
   MockClientSideDetectionService() : ClientSideDetectionService(NULL) {}
-  virtual ~MockClientSideDetectionService() {}
 
-  MOCK_METHOD1(EndFetchModel, void(ClientModelStatus));
-  MOCK_METHOD1(ScheduleFetchModel, void(int64));
-
-  void Schedule(int64) {
-    // Ignore the delay when testing.
-    StartFetchModel();
-  }
-
-  void Disable(int) {
-    // Ignore the status.
-    SetEnabledAndRefreshState(false);
-  }
+  ~MockClientSideDetectionService() override {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService);
 };
 
-ACTION(QuitCurrentMessageLoop) {
-  base::MessageLoop::current()->Quit();
-}
-
 }  // namespace
 
 class ClientSideDetectionServiceTest : public testing::Test {
  protected:
-  ClientSideDetectionServiceTest() {
-    // Needed to set the singlton.
-    field_trials_.reset(new base::FieldTrialList(NULL));
-  }
-
   void SetUp() override {
-    variations::testing::ClearAllVariationIDs();
-    variations::testing::ClearAllVariationParams();
-
     file_thread_.reset(new content::TestBrowserThread(BrowserThread::FILE,
                                                       &msg_loop_));
-
     factory_.reset(new net::FakeURLFetcherFactory(NULL));
-
     browser_thread_.reset(new content::TestBrowserThread(BrowserThread::UI,
                                                          &msg_loop_));
   }
@@ -98,6 +86,7 @@
     request->set_is_phishing(true);  // client thinks the URL is phishing.
     csd_service_->SendClientReportPhishingRequest(
         request,
+        false,
         base::Bind(&ClientSideDetectionServiceTest::SendRequestDone,
                    base::Unretained(this)));
     phishing_url_ = phishing_url;
@@ -117,35 +106,16 @@
     return is_malware_;
   }
 
-  std::string MakeModelUrl() {
-    return ClientSideDetectionService::kClientModelUrlPrefix +
-           ClientSideDetectionService::MakeModelName();
-  }
-
-  // Set up the finch experiment to control the model number
-  // used in the model URL.
-  void SetFinchModelNumber(int model_number) {
-    variations::testing::ClearAllVariationIDs();
-    variations::testing::ClearAllVariationParams();
-
-    const std::string group_name = "ModelFoo";  // Not used in CSD code.
-    base::FieldTrialList::CreateFieldTrial(
-        ClientSideDetectionService::kClientModelFinchExperiment, group_name);
-
-    std::map<std::string, std::string> params;
-    params[ClientSideDetectionService::kClientModelFinchParam] =
-        base::IntToString(model_number);
-
-    variations::AssociateVariationParams(
-        ClientSideDetectionService::kClientModelFinchExperiment, group_name,
-        params);
-  }
-
-  void SetModelFetchResponse(std::string response_data,
-                             net::HttpStatusCode response_code,
-                             net::URLRequestStatus::Status status) {
-    factory_->SetFakeResponse(GURL(MakeModelUrl()), response_data,
-                              response_code, status);
+  void SetModelFetchResponses() {
+    // Set reponses for both models.
+    factory_->SetFakeResponse(GURL(ModelLoader::kClientModelUrlPrefix +
+                                   ModelLoader::FillInModelName(false, 0)),
+                              "bogusmodel", net::HTTP_OK,
+                              net::URLRequestStatus::SUCCESS);
+    factory_->SetFakeResponse(GURL(ModelLoader::kClientModelUrlPrefix +
+                                   ModelLoader::FillInModelName(true, 0)),
+                              "bogusmodel", net::HTTP_OK,
+                              net::URLRequestStatus::SUCCESS);
   }
 
   void SetClientReportPhishingResponse(std::string response_data,
@@ -287,173 +257,9 @@
   bool is_malware_;
 };
 
-TEST_F(ClientSideDetectionServiceTest, FetchModelTest) {
-  // We don't want to use a real service class here because we can't call
-  // the real EndFetchModel.  It would reschedule a reload which might
-  // make the test flaky.
-  MockClientSideDetectionService service;
-  EXPECT_CALL(service, ScheduleFetchModel(_)).Times(1);
-  service.SetEnabledAndRefreshState(true);
-
-  // The model fetch failed.
-  SetModelFetchResponse("blamodel", net::HTTP_INTERNAL_SERVER_ERROR,
-                        net::URLRequestStatus::FAILED);
-  EXPECT_CALL(service, EndFetchModel(
-      ClientSideDetectionService::MODEL_FETCH_FAILED))
-      .WillOnce(QuitCurrentMessageLoop());
-  service.StartFetchModel();
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
-  Mock::VerifyAndClearExpectations(&service);
-
-  // Empty model file.
-  SetModelFetchResponse(std::string(), net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
-  EXPECT_CALL(service, EndFetchModel(ClientSideDetectionService::MODEL_EMPTY))
-      .WillOnce(QuitCurrentMessageLoop());
-  service.StartFetchModel();
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
-  Mock::VerifyAndClearExpectations(&service);
-
-  // Model is too large.
-  SetModelFetchResponse(
-      std::string(ClientSideDetectionService::kMaxModelSizeBytes + 1, 'x'),
-      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
-  EXPECT_CALL(service, EndFetchModel(
-      ClientSideDetectionService::MODEL_TOO_LARGE))
-      .WillOnce(QuitCurrentMessageLoop());
-  service.StartFetchModel();
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
-  Mock::VerifyAndClearExpectations(&service);
-
-  // Unable to parse the model file.
-  SetModelFetchResponse("Invalid model file", net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
-  EXPECT_CALL(service, EndFetchModel(
-      ClientSideDetectionService::MODEL_PARSE_ERROR))
-      .WillOnce(QuitCurrentMessageLoop());
-  service.StartFetchModel();
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
-  Mock::VerifyAndClearExpectations(&service);
-
-  // Model that is missing some required fields (missing the version field).
-  ClientSideModel model;
-  model.set_max_words_per_term(4);
-  SetModelFetchResponse(model.SerializePartialAsString(), net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
-  EXPECT_CALL(service, EndFetchModel(
-      ClientSideDetectionService::MODEL_MISSING_FIELDS))
-      .WillOnce(QuitCurrentMessageLoop());
-  service.StartFetchModel();
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
-  Mock::VerifyAndClearExpectations(&service);
-
-  // Model that points to hashes that don't exist.
-  model.set_version(10);
-  model.add_hashes("bla");
-  model.add_page_term(1);  // Should be 0 instead of 1.
-  SetModelFetchResponse(model.SerializePartialAsString(), net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
-  EXPECT_CALL(service, EndFetchModel(
-      ClientSideDetectionService::MODEL_BAD_HASH_IDS))
-      .WillOnce(QuitCurrentMessageLoop());
-  service.StartFetchModel();
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
-  Mock::VerifyAndClearExpectations(&service);
-  model.set_page_term(0, 0);
-
-  // Model version number is wrong.
-  model.set_version(-1);
-  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
-  EXPECT_CALL(service, EndFetchModel(
-      ClientSideDetectionService::MODEL_INVALID_VERSION_NUMBER))
-      .WillOnce(QuitCurrentMessageLoop());
-  service.StartFetchModel();
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
-  Mock::VerifyAndClearExpectations(&service);
-
-  // Normal model.
-  model.set_version(10);
-  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
-  EXPECT_CALL(service, EndFetchModel(
-      ClientSideDetectionService::MODEL_SUCCESS))
-      .WillOnce(QuitCurrentMessageLoop());
-  service.StartFetchModel();
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
-  Mock::VerifyAndClearExpectations(&service);
-
-  // Model version number is decreasing.  Set the model version number of the
-  // model that is currently loaded in the service object to 11.
-  service.model_.reset(new ClientSideModel(model));
-  service.model_->set_version(11);
-  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
-  EXPECT_CALL(service, EndFetchModel(
-      ClientSideDetectionService::MODEL_INVALID_VERSION_NUMBER))
-      .WillOnce(QuitCurrentMessageLoop());
-  service.StartFetchModel();
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
-  Mock::VerifyAndClearExpectations(&service);
-
-  // Model version hasn't changed since the last reload.
-  service.model_->set_version(10);
-  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
-  EXPECT_CALL(service, EndFetchModel(
-      ClientSideDetectionService::MODEL_NOT_CHANGED))
-      .WillOnce(QuitCurrentMessageLoop());
-  service.StartFetchModel();
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
-  Mock::VerifyAndClearExpectations(&service);
-}
-
-TEST_F(ClientSideDetectionServiceTest, ModelNamesTest) {
-  // Test the name-templating.
-  EXPECT_EQ(ClientSideDetectionService::FillInModelName(true, 3),
-            "client_model_v5_ext_variation_3.pb");
-  EXPECT_EQ(ClientSideDetectionService::FillInModelName(false, 5),
-            "client_model_v5_variation_5.pb");
-
-  // Use Finch.  Should default to 0.
-  EXPECT_EQ(ClientSideDetectionService::MakeModelName(),
-            "client_model_v5_variation_0.pb");
-
-  SetFinchModelNumber(2);
-  EXPECT_EQ(ClientSideDetectionService::MakeModelName(),
-            "client_model_v5_variation_2.pb");
-
-  SetFinchModelNumber(0);
-  EXPECT_EQ(ClientSideDetectionService::MakeModelName(),
-            "client_model_v5_variation_0.pb");
-}
-
-// Like FetchModelTest, but we vary the finch params.
-TEST_F(ClientSideDetectionServiceTest, FetchExperimentalModelTest) {
-  MockClientSideDetectionService service;
-  EXPECT_CALL(service, ScheduleFetchModel(_)).Times(1);
-  service.SetEnabledAndRefreshState(true);
-
-  ClientSideModel model;
-  model.set_max_words_per_term(0);
-  model.set_version(10);
-  model.add_hashes("bla");
-  model.add_page_term(0);
-
-  SetFinchModelNumber(1);
-  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
-  EXPECT_CALL(service, EndFetchModel(
-      ClientSideDetectionService::MODEL_SUCCESS))
-      .WillOnce(QuitCurrentMessageLoop());
-  service.StartFetchModel();
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
-  Mock::VerifyAndClearExpectations(&service);
-}
 
 TEST_F(ClientSideDetectionServiceTest, ServiceObjectDeletedBeforeCallbackDone) {
-  SetModelFetchResponse("bogus model", net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
+  SetModelFetchResponses();
   csd_service_.reset(ClientSideDetectionService::Create(NULL));
   csd_service_->SetEnabledAndRefreshState(true);
   EXPECT_TRUE(csd_service_.get() != NULL);
@@ -466,8 +272,7 @@
 }
 
 TEST_F(ClientSideDetectionServiceTest, SendClientReportPhishingRequest) {
-  SetModelFetchResponse("bogus model", net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
+  SetModelFetchResponses();
   csd_service_.reset(ClientSideDetectionService::Create(NULL));
   csd_service_->SetEnabledAndRefreshState(true);
 
@@ -517,8 +322,7 @@
 }
 
 TEST_F(ClientSideDetectionServiceTest, SendClientReportMalwareRequest) {
-  SetModelFetchResponse("bogus model", net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
+  SetModelFetchResponses();
   csd_service_.reset(ClientSideDetectionService::Create(NULL));
   csd_service_->SetEnabledAndRefreshState(true);
   GURL url("http://a.com/");
@@ -576,8 +380,7 @@
 }
 
 TEST_F(ClientSideDetectionServiceTest, GetNumReportTest) {
-  SetModelFetchResponse("bogus model", net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
+  SetModelFetchResponses();
   csd_service_.reset(ClientSideDetectionService::Create(NULL));
 
   std::queue<base::Time>& report_times = GetPhishingReportTimes();
@@ -592,16 +395,14 @@
 }
 
 TEST_F(ClientSideDetectionServiceTest, CacheTest) {
-  SetModelFetchResponse("bogus model", net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
+  SetModelFetchResponses();
   csd_service_.reset(ClientSideDetectionService::Create(NULL));
 
   TestCache();
 }
 
 TEST_F(ClientSideDetectionServiceTest, IsPrivateIPAddress) {
-  SetModelFetchResponse("bogus model", net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
+  SetModelFetchResponses();
   csd_service_.reset(ClientSideDetectionService::Create(NULL));
 
   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("10.1.2.3"));
@@ -621,163 +422,65 @@
   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("blah"));
 }
 
-TEST_F(ClientSideDetectionServiceTest, SetBadSubnets) {
-  ClientSideModel model;
-  ClientSideDetectionService::BadSubnetMap bad_subnets;
-  ClientSideDetectionService::SetBadSubnets(model, &bad_subnets);
-  EXPECT_EQ(0U, bad_subnets.size());
-
-  // Bad subnets are skipped.
-  ClientSideModel::IPSubnet* subnet = model.add_bad_subnet();
-  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
-  subnet->set_size(130);  // Invalid size.
-
-  subnet = model.add_bad_subnet();
-  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
-  subnet->set_size(-1);  // Invalid size.
-
-  subnet = model.add_bad_subnet();
-  subnet->set_prefix(std::string(16, '.'));  // Invalid len.
-  subnet->set_size(64);
-
-  ClientSideDetectionService::SetBadSubnets(model, &bad_subnets);
-  EXPECT_EQ(0U, bad_subnets.size());
-
-  subnet = model.add_bad_subnet();
-  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
-  subnet->set_size(64);
-
-  subnet = model.add_bad_subnet();
-  subnet->set_prefix(std::string(crypto::kSHA256Length, ','));
-  subnet->set_size(64);
-
-  subnet = model.add_bad_subnet();
-  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
-  subnet->set_size(128);
-
-  subnet = model.add_bad_subnet();
-  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
-  subnet->set_size(100);
-
-  ClientSideDetectionService::SetBadSubnets(model, &bad_subnets);
-  EXPECT_EQ(3U, bad_subnets.size());
-  ClientSideDetectionService::BadSubnetMap::const_iterator it;
-  std::string mask = std::string(8, '\xFF') + std::string(8, '\x00');
-  EXPECT_TRUE(bad_subnets.count(mask));
-  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, '.')));
-  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, ',')));
-
-  mask = std::string(16, '\xFF');
-  EXPECT_TRUE(bad_subnets.count(mask));
-  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, '.')));
-
-  mask = std::string(12, '\xFF') + "\xF0" + std::string(3, '\x00');
-  EXPECT_TRUE(bad_subnets.count(mask));
-  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, '.')));
-}
-
-TEST_F(ClientSideDetectionServiceTest, ModelHasValidHashIds) {
-  ClientSideModel model;
-  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
-  model.add_hashes("bla");
-  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
-  model.add_page_term(0);
-  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
-
-  model.add_page_term(-1);
-  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
-  model.set_page_term(1, 1);
-  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
-  model.set_page_term(1, 0);
-  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
-
-  // Test bad rules.
-  model.add_hashes("blu");
-  ClientSideModel::Rule* rule = model.add_rule();
-  rule->add_feature(0);
-  rule->add_feature(1);
-  rule->set_weight(0.1f);
-  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
-
-  rule = model.add_rule();
-  rule->add_feature(0);
-  rule->add_feature(1);
-  rule->add_feature(-1);
-  rule->set_weight(0.2f);
-  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
-
-  rule->set_feature(2, 2);
-  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
-
-  rule->set_feature(2, 1);
-  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
-}
-
 TEST_F(ClientSideDetectionServiceTest, SetEnabledAndRefreshState) {
   // Check that the model isn't downloaded until the service is enabled.
   csd_service_.reset(ClientSideDetectionService::Create(NULL));
   EXPECT_FALSE(csd_service_->enabled());
-  EXPECT_TRUE(csd_service_->model_fetcher_.get() == NULL);
+  EXPECT_TRUE(csd_service_->model_loader_standard_->fetcher_.get() == NULL);
 
   // Use a MockClientSideDetectionService for the rest of the test, to avoid
   // the scheduling delay.
   MockClientSideDetectionService* service =
       new StrictMock<MockClientSideDetectionService>();
+  // Inject mock loaders.
+  MockModelLoader* loader_1 = new StrictMock<MockModelLoader>("model1");
+  MockModelLoader* loader_2 = new StrictMock<MockModelLoader>("model2");
+  service->model_loader_standard_.reset(loader_1);
+  service->model_loader_extended_.reset(loader_2);
   csd_service_.reset(service);
+
   EXPECT_FALSE(csd_service_->enabled());
-  EXPECT_TRUE(csd_service_->model_fetcher_.get() == NULL);
   // No calls expected yet.
   Mock::VerifyAndClearExpectations(service);
+  Mock::VerifyAndClearExpectations(loader_1);
+  Mock::VerifyAndClearExpectations(loader_2);
 
-  ClientSideModel model;
-  model.set_version(10);
-  model.set_max_words_per_term(4);
-  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
-                        net::URLRequestStatus::SUCCESS);
-  EXPECT_CALL(*service, ScheduleFetchModel(_))
-      .WillOnce(Invoke(service, &MockClientSideDetectionService::Schedule));
-  EXPECT_CALL(*service, EndFetchModel(
-      ClientSideDetectionService::MODEL_SUCCESS))
-      .WillOnce(QuitCurrentMessageLoop());
+  // Check that initial ScheduleFetch() calls are made.
+  EXPECT_CALL(*loader_1,
+              ScheduleFetch(
+                  ClientSideDetectionService::kInitialClientModelFetchDelayMs));
+  EXPECT_CALL(*loader_2,
+              ScheduleFetch(
+                  ClientSideDetectionService::kInitialClientModelFetchDelayMs));
   csd_service_->SetEnabledAndRefreshState(true);
-  EXPECT_TRUE(csd_service_->model_fetcher_.get() != NULL);
-  msg_loop_.Run();  // EndFetchModel will quit the message loop.
+  msg_loop_.RunUntilIdle();
   Mock::VerifyAndClearExpectations(service);
+  Mock::VerifyAndClearExpectations(loader_1);
+  Mock::VerifyAndClearExpectations(loader_2);
 
   // Check that enabling again doesn't request the model.
   csd_service_->SetEnabledAndRefreshState(true);
   // No calls expected.
+  msg_loop_.RunUntilIdle();
   Mock::VerifyAndClearExpectations(service);
+  Mock::VerifyAndClearExpectations(loader_1);
+  Mock::VerifyAndClearExpectations(loader_2);
 
   // Check that disabling the service cancels pending requests.
-  EXPECT_CALL(*service, ScheduleFetchModel(_))
-      .WillOnce(Invoke(service, &MockClientSideDetectionService::Schedule));
+  EXPECT_CALL(*loader_1, CancelFetcher());
+  EXPECT_CALL(*loader_2, CancelFetcher());
   csd_service_->SetEnabledAndRefreshState(false);
-  csd_service_->SetEnabledAndRefreshState(true);
-  Mock::VerifyAndClearExpectations(service);
-  EXPECT_TRUE(csd_service_->model_fetcher_.get() != NULL);
-  csd_service_->SetEnabledAndRefreshState(false);
-  EXPECT_TRUE(csd_service_->model_fetcher_.get() == NULL);
   msg_loop_.RunUntilIdle();
+  Mock::VerifyAndClearExpectations(service);
+  Mock::VerifyAndClearExpectations(loader_1);
+  Mock::VerifyAndClearExpectations(loader_2);
+
+  // Check that disabling again doesn't request the model.
+  csd_service_->SetEnabledAndRefreshState(false);
   // No calls expected.
+  msg_loop_.RunUntilIdle();
   Mock::VerifyAndClearExpectations(service);
-
-  // Requests always return false when the service is disabled.
-  ClientPhishingResponse response;
-  response.set_phishy(true);
-  SetClientReportPhishingResponse(response.SerializeAsString(), net::HTTP_OK,
-                                  net::URLRequestStatus::SUCCESS);
-  EXPECT_FALSE(SendClientReportPhishingRequest(GURL("http://a.com/"), 0.4f));
-
-  // Pending requests also return false if the service is disabled before they
-  // report back.
-  EXPECT_CALL(*service, ScheduleFetchModel(_))
-      .WillOnce(Invoke(service, &MockClientSideDetectionService::Schedule));
-  EXPECT_CALL(*service, EndFetchModel(
-      ClientSideDetectionService::MODEL_NOT_CHANGED))
-      .WillOnce(Invoke(service, &MockClientSideDetectionService::Disable));
-  csd_service_->SetEnabledAndRefreshState(true);
-  EXPECT_FALSE(SendClientReportPhishingRequest(GURL("http://a.com/"), 0.4f));
-  Mock::VerifyAndClearExpectations(service);
+  Mock::VerifyAndClearExpectations(loader_1);
+  Mock::VerifyAndClearExpectations(loader_2);
 }
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/client_side_model_loader.cc b/chrome/browser/safe_browsing/client_side_model_loader.cc
new file mode 100644
index 0000000..c73019e
--- /dev/null
+++ b/chrome/browser/safe_browsing/client_side_model_loader.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/safe_browsing/client_side_model_loader.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/time/time.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/safe_browsing/client_model.pb.h"
+#include "chrome/common/safe_browsing/csd.pb.h"
+#include "chrome/common/safe_browsing/safebrowsing_messages.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/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+// Model Loader strings
+const size_t ModelLoader::kMaxModelSizeBytes = 150 * 1024;
+const int ModelLoader::kClientModelFetchIntervalMs = 3600 * 1000;
+const char ModelLoader::kClientModelUrlPrefix[] =
+    "https://ssl.gstatic.com/safebrowsing/csd/";
+const char ModelLoader::kClientModelNamePattern[] =
+    "client_model_v5%s_variation_%d.pb";
+const char ModelLoader::kClientModelFinchExperiment[] =
+    "ClientSideDetectionModel";
+const char ModelLoader::kClientModelFinchParam[] =
+    "ModelNum";
+
+
+// static
+int ModelLoader::GetModelNumber() {
+  std::string num_str = variations::GetVariationParamValue(
+      kClientModelFinchExperiment, kClientModelFinchParam);
+  int model_number = 0;
+  if (!base::StringToInt(num_str, &model_number)) {
+    model_number = 0;  // Default model
+  }
+  return model_number;
+}
+
+// static
+std::string ModelLoader::FillInModelName(bool is_extended_reporting,
+                                         int model_number) {
+  return base::StringPrintf(kClientModelNamePattern,
+                            is_extended_reporting ? "_ext" : "", model_number);
+}
+
+// static
+bool ModelLoader::ModelHasValidHashIds(const ClientSideModel& model) {
+  const int max_index = model.hashes_size() - 1;
+  for (int i = 0; i < model.rule_size(); ++i) {
+    for (int j = 0; j < model.rule(i).feature_size(); ++j) {
+      if (model.rule(i).feature(j) < 0 ||
+          model.rule(i).feature(j) > max_index) {
+        return false;
+      }
+    }
+  }
+  for (int i = 0; i < model.page_term_size(); ++i) {
+    if (model.page_term(i) < 0 || model.page_term(i) > max_index) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// Model name and URL are a function of is_extended_reporting and Finch.
+ModelLoader::ModelLoader(base::Closure update_renderers_callback,
+                         net::URLRequestContextGetter* request_context_getter,
+                         bool is_extended_reporting)
+    : name_(FillInModelName(is_extended_reporting, GetModelNumber())),
+      url_(kClientModelUrlPrefix + name_),
+      update_renderers_callback_(update_renderers_callback),
+      request_context_getter_(request_context_getter),
+      weak_factory_(this) {
+  DCHECK(url_.is_valid());
+}
+
+// For testing only
+ModelLoader::ModelLoader(base::Closure update_renderers_callback,
+                         const std::string model_name)
+    : name_(model_name),
+      url_(kClientModelUrlPrefix + name_),
+      update_renderers_callback_(update_renderers_callback),
+      request_context_getter_(NULL),
+      weak_factory_(this) {
+  DCHECK(url_.is_valid());
+}
+
+ModelLoader::~ModelLoader() {
+}
+
+void ModelLoader::StartFetch() {
+  // Start fetching the model either from the cache or possibly from the
+  // network if the model isn't in the cache.
+
+  // TODO(nparker): If no profile needs this model, we shouldn't fetch it.
+  // Then only re-fetch when a profile setting changes to need it.
+  // This will save on the order of ~50KB/week/client of bandwidth.
+  fetcher_ = net::URLFetcher::Create(0 /* ID used for testing */, url_,
+                                     net::URLFetcher::GET, this);
+  fetcher_->SetRequestContext(request_context_getter_);
+  fetcher_->Start();
+}
+
+void ModelLoader::OnURLFetchComplete(const net::URLFetcher* source) {
+  DCHECK_EQ(fetcher_, source);
+  DCHECK_EQ(url_, source->GetURL());
+
+  std::string data;
+  source->GetResponseAsString(&data);
+  const bool is_success = source->GetStatus().is_success();
+  const int response_code = source->GetResponseCode();
+
+  // max_age is valid iff !0.
+  base::TimeDelta max_age;
+  if (is_success && net::HTTP_OK == response_code &&
+      source->GetResponseHeaders()) {
+    source->GetResponseHeaders()->GetMaxAgeValue(&max_age);
+  }
+  scoped_ptr<ClientSideModel> model(new ClientSideModel());
+  ClientModelStatus model_status;
+  if (!is_success || net::HTTP_OK != response_code) {
+    model_status = MODEL_FETCH_FAILED;
+  } else if (data.empty()) {
+    model_status = MODEL_EMPTY;
+  } else if (data.size() > kMaxModelSizeBytes) {
+    model_status = MODEL_TOO_LARGE;
+  } else if (!model->ParseFromString(data)) {
+    model_status = MODEL_PARSE_ERROR;
+  } else if (!model->IsInitialized() || !model->has_version()) {
+    model_status = MODEL_MISSING_FIELDS;
+  } else if (!ModelHasValidHashIds(*model)) {
+    model_status = MODEL_BAD_HASH_IDS;
+  } else if (model->version() < 0 ||
+             (model_.get() && model->version() < model_->version())) {
+    model_status = MODEL_INVALID_VERSION_NUMBER;
+  } else if (model_.get() && model->version() == model_->version()) {
+    model_status = MODEL_NOT_CHANGED;
+  } else {
+    // The model is valid => replace the existing model with the new one.
+    model_str_.assign(data);
+    model_.swap(model);
+    model_status = MODEL_SUCCESS;
+  }
+  EndFetch(model_status, max_age);
+}
+
+void ModelLoader::EndFetch(ClientModelStatus status, base::TimeDelta max_age) {
+  // We don't differentiate models in the UMA stats.
+  UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.ClientModelStatus",
+                            status,
+                            MODEL_STATUS_MAX);
+  if (status == MODEL_SUCCESS) {
+    update_renderers_callback_.Run();
+  }
+  int delay_ms = kClientModelFetchIntervalMs;
+  // If the most recently fetched model had a valid max-age and the model was
+  // valid we're scheduling the next model update for after the max-age expired.
+  if (!max_age.is_zero() &&
+      (status == MODEL_SUCCESS || status == MODEL_NOT_CHANGED)) {
+    // We're adding 60s of additional delay to make sure we're past
+    // the model's age.
+    max_age += base::TimeDelta::FromMinutes(1);
+    delay_ms = max_age.InMilliseconds();
+  }
+
+  // Schedule the next model reload.
+  ScheduleFetch(delay_ms);
+}
+
+void ModelLoader::ScheduleFetch(int64 delay_ms) {
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSbDisableAutoUpdate))
+    return;
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&ModelLoader::StartFetch, weak_factory_.GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(delay_ms));
+}
+
+void ModelLoader::CancelFetcher() {
+  // Invalidate any scheduled request.
+  weak_factory_.InvalidateWeakPtrs();
+  // Cancel any request in progress.
+  fetcher_.reset();
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/client_side_model_loader.h b/chrome/browser/safe_browsing/client_side_model_loader.h
new file mode 100644
index 0000000..5a0c1b24
--- /dev/null
+++ b/chrome/browser/safe_browsing/client_side_model_loader.h
@@ -0,0 +1,143 @@
+// 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.
+//
+// Helper class loads models for client-side phishing detection
+// from the the SafeBrowsing backends.
+//
+// This class is not thread-safe and expects all calls to be made on the UI
+// thread.
+
+#ifndef CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_MODEL_LOADER_H_
+#define CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_MODEL_LOADER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/net_util.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "url/gurl.h"
+
+class SafeBrowsingService;
+
+namespace base {
+class TimeDelta;
+}
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+}  // namespace net
+
+namespace safe_browsing {
+class ClientSideModel;
+
+// Class which owns and loads a single client-Side detection model.
+// The ClientSideDetectionService uses this.
+class ModelLoader : public net::URLFetcherDelegate {
+ public:
+  static const size_t kMaxModelSizeBytes;
+  static const int kClientModelFetchIntervalMs;
+  static const char kClientModelFinchExperiment[];
+  static const char kClientModelFinchParam[];
+  static const char kClientModelUrlPrefix[];
+  static const char kClientModelNamePattern[];
+
+  ModelLoader(base::Closure update_renderers,
+              net::URLRequestContextGetter* request_context_getter,
+              bool is_extended_reporting);
+  ~ModelLoader() override;
+
+  // From the net::URLFetcherDelegate interface.
+  void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+  // Schedules the next fetch of the model.
+  virtual void ScheduleFetch(int64 delay_ms);
+
+  // Cancel any pending model fetch.
+  virtual void CancelFetcher();
+
+  const std::string& model_str() const { return model_str_; }
+  const std::string& name() const { return name_; }
+
+ protected:
+  // Enum used to keep stats about why we fail to get the client model.
+  enum ClientModelStatus {
+    MODEL_SUCCESS,
+    MODEL_NOT_CHANGED,
+    MODEL_FETCH_FAILED,
+    MODEL_EMPTY,
+    MODEL_TOO_LARGE,
+    MODEL_PARSE_ERROR,
+    MODEL_MISSING_FIELDS,
+    MODEL_INVALID_VERSION_NUMBER,
+    MODEL_BAD_HASH_IDS,
+    MODEL_STATUS_MAX  // Always add new values before this one.
+  };
+
+  // For testing only.
+  ModelLoader(base::Closure update_renderers, const std::string model_name);
+
+  // This is called periodically to check whether a new client model is
+  // available for download.
+  virtual void StartFetch();
+
+  // This method is called when we're done fetching the model either because
+  // we hit an error somewhere or because we're actually done fetch and
+  // validating the model.  If |max_age| is not 0, it's used to schedule the
+  // next fetch.
+  virtual void EndFetch(ClientModelStatus status, base::TimeDelta max_age);
+
+ private:
+  // Use Finch to pick a model number.
+  static int GetModelNumber();
+
+  // Construct a model name from parameters.
+  static std::string FillInModelName(bool is_extended_reporting,
+                                     int model_number);
+
+  // Returns true iff all the hash id's in the client-side model point to
+  // valid hashes in the model.
+  static bool ModelHasValidHashIds(const ClientSideModel& model);
+
+  // The name of the model is the last component of the URL path.
+  const std::string name_;
+  // Full URL of the model.
+  const GURL url_;
+
+  // If the model isn't yet loaded, model_str_ will be empty.
+  std::string model_str_;
+  scoped_ptr<ClientSideModel> model_;
+  scoped_ptr<net::URLFetcher> fetcher_;
+
+  // Callback to invoke when we've got a new model.  CSD will send it around.
+  base::Closure update_renderers_callback_;
+
+  // Not owned, must outlive this obj or be NULL.
+  net::URLRequestContextGetter* request_context_getter_;
+
+  // Used to protect the delayed callback to StartFetchModel()
+  base::WeakPtrFactory<ModelLoader> weak_factory_;
+
+  friend class ClientSideDetectionServiceTest;
+  friend class ModelLoaderTest;
+  FRIEND_TEST_ALL_PREFIXES(ModelLoaderTest, FetchModelTest);
+  FRIEND_TEST_ALL_PREFIXES(ModelLoaderTest, ModelHasValidHashIds);
+  FRIEND_TEST_ALL_PREFIXES(ModelLoaderTest, ModelNamesTest);
+  FRIEND_TEST_ALL_PREFIXES(ModelLoaderTest, RescheduleFetchTest);
+  FRIEND_TEST_ALL_PREFIXES(ModelLoaderTest, UpdateRenderersTest);
+  FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionServiceTest,
+                           SetEnabledAndRefreshState);
+  DISALLOW_COPY_AND_ASSIGN(ModelLoader);
+};
+
+}  // namespace safe_browsing
+
+#endif  // CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_MODEL_LOADER_H_
diff --git a/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc b/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
new file mode 100644
index 0000000..4679b82
--- /dev/null
+++ b/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
@@ -0,0 +1,342 @@
+// 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 <map>
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/field_trial.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "chrome/browser/safe_browsing/client_side_model_loader.h"
+#include "chrome/common/safe_browsing/client_model.pb.h"
+#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/variations/variations_associated_data.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using ::testing::Invoke;
+using ::testing::Mock;
+using ::testing::StrictMock;
+using ::testing::_;
+
+namespace safe_browsing {
+namespace {
+
+class MockModelLoader : public ModelLoader {
+ public:
+  explicit MockModelLoader(base::Closure update_renderers_callback,
+                           const std::string model_name)
+      : ModelLoader(update_renderers_callback, model_name) {}
+  ~MockModelLoader() override {}
+
+  MOCK_METHOD1(ScheduleFetch, void(int64));
+  MOCK_METHOD2(EndFetch, void(ClientModelStatus, base::TimeDelta));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockModelLoader);
+};
+
+}  // namespace
+
+class ModelLoaderTest : public testing::Test {
+ protected:
+  ModelLoaderTest()
+      : factory_(new net::FakeURLFetcherFactory(NULL)),
+        field_trials_(new base::FieldTrialList(NULL)) {}
+
+  void SetUp() override {
+    variations::testing::ClearAllVariationIDs();
+    variations::testing::ClearAllVariationParams();
+  }
+
+  // Set up the finch experiment to control the model number
+  // used in the model URL. This clears all existing state.
+  void SetFinchModelNumber(int model_number) {
+    // Destroy the existing FieldTrialList before creating a new one to avoid
+    // a DCHECK.
+    field_trials_.reset();
+    field_trials_.reset(new base::FieldTrialList(NULL));
+    variations::testing::ClearAllVariationIDs();
+    variations::testing::ClearAllVariationParams();
+
+    const std::string group_name = "ModelFoo";  // Not used in CSD code.
+    ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
+        ModelLoader::kClientModelFinchExperiment, group_name));
+
+    std::map<std::string, std::string> params;
+    params[ModelLoader::kClientModelFinchParam] =
+        base::IntToString(model_number);
+
+    ASSERT_TRUE(variations::AssociateVariationParams(
+        ModelLoader::kClientModelFinchExperiment, group_name, params));
+  }
+
+  // Set the URL for future SetModelFetchResponse() calls.
+  void SetModelUrl(const ModelLoader& loader) { model_url_ = loader.url_; }
+
+  void SetModelFetchResponse(std::string response_data,
+                             net::HttpStatusCode response_code,
+                             net::URLRequestStatus::Status status) {
+    CHECK(model_url_.is_valid());
+    factory_->SetFakeResponse(model_url_, response_data, response_code, status);
+  }
+
+ private:
+  content::TestBrowserThreadBundle thread_bundle_;
+  scoped_ptr<net::FakeURLFetcherFactory> factory_;
+  scoped_ptr<base::FieldTrialList> field_trials_;
+  GURL model_url_;
+};
+
+ACTION_P(InvokeClosure, closure) {
+  closure.Run();
+}
+
+// Test the reponse to many variations of model responses.
+TEST_F(ModelLoaderTest, FetchModelTest) {
+  StrictMock<MockModelLoader> loader(base::Closure(), "top_model.pb");
+  SetModelUrl(loader);
+
+  // The model fetch failed.
+  {
+    base::RunLoop loop;
+    SetModelFetchResponse("blamodel", net::HTTP_INTERNAL_SERVER_ERROR,
+                          net::URLRequestStatus::FAILED);
+    EXPECT_CALL(loader, EndFetch(ModelLoader::MODEL_FETCH_FAILED, _))
+        .WillOnce(InvokeClosure(loop.QuitClosure()));
+    loader.StartFetch();
+    loop.Run();
+    Mock::VerifyAndClearExpectations(&loader);
+  }
+
+  // Empty model file.
+  {
+    base::RunLoop loop;
+    SetModelFetchResponse(std::string(), net::HTTP_OK,
+                          net::URLRequestStatus::SUCCESS);
+    EXPECT_CALL(loader, EndFetch(ModelLoader::MODEL_EMPTY, _))
+        .WillOnce(InvokeClosure(loop.QuitClosure()));
+    loader.StartFetch();
+    loop.Run();
+    Mock::VerifyAndClearExpectations(&loader);
+  }
+
+  // Model is too large.
+  {
+    base::RunLoop loop;
+    SetModelFetchResponse(std::string(ModelLoader::kMaxModelSizeBytes + 1, 'x'),
+                          net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+    EXPECT_CALL(loader, EndFetch(ModelLoader::MODEL_TOO_LARGE, _))
+        .WillOnce(InvokeClosure(loop.QuitClosure()));
+    loader.StartFetch();
+    loop.Run();
+    Mock::VerifyAndClearExpectations(&loader);
+  }
+
+  // Unable to parse the model file.
+  {
+    base::RunLoop loop;
+    SetModelFetchResponse("Invalid model file", net::HTTP_OK,
+                          net::URLRequestStatus::SUCCESS);
+    EXPECT_CALL(loader, EndFetch(ModelLoader::MODEL_PARSE_ERROR, _))
+        .WillOnce(InvokeClosure(loop.QuitClosure()));
+    loader.StartFetch();
+    loop.Run();
+    Mock::VerifyAndClearExpectations(&loader);
+  }
+
+  // Model that is missing some required fields (missing the version field).
+  ClientSideModel model;
+  model.set_max_words_per_term(4);
+  {
+    base::RunLoop loop;
+    SetModelFetchResponse(model.SerializePartialAsString(), net::HTTP_OK,
+                          net::URLRequestStatus::SUCCESS);
+    EXPECT_CALL(loader, EndFetch(ModelLoader::MODEL_MISSING_FIELDS, _))
+        .WillOnce(InvokeClosure(loop.QuitClosure()));
+    loader.StartFetch();
+    loop.Run();
+    Mock::VerifyAndClearExpectations(&loader);
+  }
+
+  // Model that points to hashes that don't exist.
+  model.set_version(10);
+  model.add_hashes("bla");
+  model.add_page_term(1);  // Should be 0 instead of 1.
+  {
+    base::RunLoop loop;
+    SetModelFetchResponse(model.SerializePartialAsString(), net::HTTP_OK,
+                          net::URLRequestStatus::SUCCESS);
+    EXPECT_CALL(loader, EndFetch(ModelLoader::MODEL_BAD_HASH_IDS, _))
+        .WillOnce(InvokeClosure(loop.QuitClosure()));
+    loader.StartFetch();
+    loop.Run();
+    Mock::VerifyAndClearExpectations(&loader);
+  }
+  model.set_page_term(0, 0);
+
+  // Model version number is wrong.
+  model.set_version(-1);
+  {
+    base::RunLoop loop;
+    SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
+                          net::URLRequestStatus::SUCCESS);
+    EXPECT_CALL(loader, EndFetch(ModelLoader::MODEL_INVALID_VERSION_NUMBER, _))
+        .WillOnce(InvokeClosure(loop.QuitClosure()));
+    loader.StartFetch();
+    loop.Run();
+    Mock::VerifyAndClearExpectations(&loader);
+  }
+
+  // Normal model.
+  model.set_version(10);
+  {
+    base::RunLoop loop;
+    SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
+                          net::URLRequestStatus::SUCCESS);
+    EXPECT_CALL(loader, EndFetch(ModelLoader::MODEL_SUCCESS, _))
+        .WillOnce(InvokeClosure(loop.QuitClosure()));
+    loader.StartFetch();
+    loop.Run();
+    Mock::VerifyAndClearExpectations(&loader);
+  }
+
+  // Model version number is decreasing.  Set the model version number of the
+  // model that is currently loaded in the loader object to 11.
+  loader.model_.reset(new ClientSideModel(model));
+  loader.model_->set_version(11);
+  {
+    base::RunLoop loop;
+    SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
+                          net::URLRequestStatus::SUCCESS);
+    EXPECT_CALL(loader, EndFetch(ModelLoader::MODEL_INVALID_VERSION_NUMBER, _))
+        .WillOnce(InvokeClosure(loop.QuitClosure()));
+    loader.StartFetch();
+    loop.Run();
+    Mock::VerifyAndClearExpectations(&loader);
+  }
+
+  // Model version hasn't changed since the last reload.
+  loader.model_->set_version(10);
+  {
+    base::RunLoop loop;
+    SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
+                          net::URLRequestStatus::SUCCESS);
+    EXPECT_CALL(loader, EndFetch(ModelLoader::MODEL_NOT_CHANGED, _))
+        .WillOnce(InvokeClosure(loop.QuitClosure()));
+    loader.StartFetch();
+    loop.Run();
+    Mock::VerifyAndClearExpectations(&loader);
+  }
+}
+
+// Test that a successful reponse will update the renderers
+TEST_F(ModelLoaderTest, UpdateRenderersTest) {
+  // Use runloop for convenient callback detection.
+  base::RunLoop loop;
+  StrictMock<MockModelLoader> loader(loop.QuitClosure(), "top_model.pb");
+  EXPECT_CALL(loader, ScheduleFetch(_));
+  loader.ModelLoader::EndFetch(ModelLoader::MODEL_SUCCESS, base::TimeDelta());
+  loop.Run();
+  Mock::VerifyAndClearExpectations(&loader);
+}
+
+// Test that a one fetch schedules another fetch.
+TEST_F(ModelLoaderTest, RescheduleFetchTest) {
+  StrictMock<MockModelLoader> loader(base::Closure(), "top_model.pb");
+
+  // Zero max_age.  Uses default.
+  base::TimeDelta max_age;
+  EXPECT_CALL(loader, ScheduleFetch(ModelLoader::kClientModelFetchIntervalMs));
+  loader.ModelLoader::EndFetch(ModelLoader::MODEL_NOT_CHANGED, max_age);
+  Mock::VerifyAndClearExpectations(&loader);
+
+  // Non-zero max_age from header.
+  max_age = base::TimeDelta::FromMinutes(42);
+  EXPECT_CALL(loader, ScheduleFetch((max_age + base::TimeDelta::FromMinutes(1))
+                                        .InMilliseconds()));
+  loader.ModelLoader::EndFetch(ModelLoader::MODEL_NOT_CHANGED, max_age);
+  Mock::VerifyAndClearExpectations(&loader);
+
+  // Non-zero max_age, but failed load should use default interval.
+  max_age = base::TimeDelta::FromMinutes(42);
+  EXPECT_CALL(loader, ScheduleFetch(ModelLoader::kClientModelFetchIntervalMs));
+  loader.ModelLoader::EndFetch(ModelLoader::MODEL_FETCH_FAILED, max_age);
+  Mock::VerifyAndClearExpectations(&loader);
+}
+
+// Test that Finch params control the model names.
+TEST_F(ModelLoaderTest, ModelNamesTest) {
+  // Test the name-templating.
+  EXPECT_EQ(ModelLoader::FillInModelName(true, 3),
+            "client_model_v5_ext_variation_3.pb");
+  EXPECT_EQ(ModelLoader::FillInModelName(false, 5),
+            "client_model_v5_variation_5.pb");
+
+  // No Finch setup. Should default to 0.
+  scoped_ptr<ModelLoader> loader;
+  loader.reset(new ModelLoader(base::Closure(), NULL,
+                               false /* is_extended_reporting */));
+  EXPECT_EQ(loader->name(), "client_model_v5_variation_0.pb");
+  EXPECT_EQ(loader->url_.spec(),
+            "https://ssl.gstatic.com/safebrowsing/csd/"
+            "client_model_v5_variation_0.pb");
+
+  // Model 1, no extended reporting.
+  SetFinchModelNumber(1);
+  loader.reset(new ModelLoader(base::Closure(), NULL, false));
+  EXPECT_EQ(loader->name(), "client_model_v5_variation_1.pb");
+
+  // Model 2, with extended reporting.
+  SetFinchModelNumber(2);
+  loader.reset(new ModelLoader(base::Closure(), NULL, true));
+  EXPECT_EQ(loader->name(), "client_model_v5_ext_variation_2.pb");
+}
+
+TEST_F(ModelLoaderTest, ModelHasValidHashIds) {
+  ClientSideModel model;
+  EXPECT_TRUE(ModelLoader::ModelHasValidHashIds(model));
+  model.add_hashes("bla");
+  EXPECT_TRUE(ModelLoader::ModelHasValidHashIds(model));
+  model.add_page_term(0);
+  EXPECT_TRUE(ModelLoader::ModelHasValidHashIds(model));
+
+  model.add_page_term(-1);
+  EXPECT_FALSE(ModelLoader::ModelHasValidHashIds(model));
+  model.set_page_term(1, 1);
+  EXPECT_FALSE(ModelLoader::ModelHasValidHashIds(model));
+  model.set_page_term(1, 0);
+  EXPECT_TRUE(ModelLoader::ModelHasValidHashIds(model));
+
+  // Test bad rules.
+  model.add_hashes("blu");
+  ClientSideModel::Rule* rule = model.add_rule();
+  rule->add_feature(0);
+  rule->add_feature(1);
+  rule->set_weight(0.1f);
+  EXPECT_TRUE(ModelLoader::ModelHasValidHashIds(model));
+
+  rule = model.add_rule();
+  rule->add_feature(0);
+  rule->add_feature(1);
+  rule->add_feature(-1);
+  rule->set_weight(0.2f);
+  EXPECT_FALSE(ModelLoader::ModelHasValidHashIds(model));
+
+  rule->set_feature(2, 2);
+  EXPECT_FALSE(ModelLoader::ModelHasValidHashIds(model));
+
+  rule->set_feature(2, 1);
+  EXPECT_TRUE(ModelLoader::ModelHasValidHashIds(model));
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index 162e6c2..c78ff00 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -207,7 +207,8 @@
 SafeBrowsingService::SafeBrowsingService()
     : protocol_manager_(NULL),
       ping_manager_(NULL),
-      enabled_(false) {
+      enabled_(false),
+      enabled_by_prefs_(false) {
 }
 
 SafeBrowsingService::~SafeBrowsingService() {
@@ -591,6 +592,11 @@
   registrar->Add(prefs::kSafeBrowsingEnabled,
                  base::Bind(&SafeBrowsingService::RefreshState,
                             base::Unretained(this)));
+  // ClientSideDetectionService will need to be refresh the models
+  // renderers have if extended-reporting changes.
+  registrar->Add(prefs::kSafeBrowsingExtendedReportingEnabled,
+                 base::Bind(&SafeBrowsingService::RefreshState,
+                            base::Unretained(this)));
   prefs_map_[pref_service] = registrar;
   RefreshState();
 }
@@ -605,7 +611,15 @@
   }
 }
 
+scoped_ptr<SafeBrowsingService::StateSubscription>
+SafeBrowsingService::RegisterStateCallback(
+      const base::Callback<void(void)>& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  return state_callback_list_.Add(callback);
+}
+
 void SafeBrowsingService::RefreshState() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Check if any profile requires the service to be active.
   bool enable = false;
   std::map<PrefService*, PrefChangeRegistrar*>::iterator iter;
@@ -616,11 +630,15 @@
     }
   }
 
+  enabled_by_prefs_ = enable;
+
   if (enable)
     Start();
   else
     Stop(false);
 
+  state_callback_list_.Notify();
+
 #if defined(FULL_SAFE_BROWSING)
   if (csd_service_)
     csd_service_->SetEnabledAndRefreshState(enable);
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h
index 0f0fef3..e75670f9 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.h
+++ b/chrome/browser/safe_browsing/safe_browsing_service.h
@@ -12,6 +12,7 @@
 #include <string>
 
 #include "base/callback.h"
+#include "base/callback_list.h"
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
@@ -107,6 +108,12 @@
     return enabled_;
   }
 
+  // Whether the service is enabled by the current set of profiles.
+  bool enabled_by_prefs() const {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    return enabled_by_prefs_;
+  }
+
   safe_browsing::ClientSideDetectionService*
       safe_browsing_detection_service() const {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -152,6 +159,15 @@
   // activity.
   void OnResourceRequest(const net::URLRequest* request);
 
+  // Type for subscriptions to SafeBrowsing service state.
+  typedef base::CallbackList<void(void)>::Subscription StateSubscription;
+
+  // Adds a listener for when SafeBrowsing preferences might have changed.
+  // To get the current state, the callback should call enabled_by_prefs().
+  // Should only be called on the UI thread.
+  scoped_ptr<StateSubscription> RegisterStateCallback(
+      const base::Callback<void(void)>& callback);
+
  protected:
   // Creates the safe browsing service.  Need to initialize before using.
   SafeBrowsingService();
@@ -245,6 +261,10 @@
   // on the IO thread during normal operations.
   bool enabled_;
 
+  // Whether SafeBrowsing is enabled by the current set of profiles.
+  // Accessed on UI thread.
+  bool enabled_by_prefs_;
+
   // Tracks existing PrefServices, and the safe browsing preference on each.
   // This is used to determine if any profile is currently using the safe
   // browsing service, and to start it up or shut it down accordingly.
@@ -254,6 +274,10 @@
   // Used to track creation and destruction of profiles on the UI thread.
   content::NotificationRegistrar prefs_registrar_;
 
+  // Callbacks when SafeBrowsing state might have changed.
+  // Should only be accessed on the UI thread.
+  base::CallbackList<void(void)> state_callback_list_;
+
   // The ClientSideDetectionService is managed by the SafeBrowsingService,
   // since its running state and lifecycle depends on SafeBrowsingService's.
   // Accessed on UI thread.
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index 7a69139..0df6f8c 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -1072,6 +1072,7 @@
   EXPECT_TRUE(pref_service->GetBoolean(prefs::kSafeBrowsingEnabled));
 
   // SBS might still be starting, make sure this doesn't flake.
+  EXPECT_TRUE(sb_service->enabled_by_prefs());
   WaitForIOAndCheckEnabled(sb_service, true);
   EXPECT_TRUE(csd_service->enabled());
 
@@ -1085,11 +1086,13 @@
   PrefService* pref_service2 = profile2->GetPrefs();
   EXPECT_TRUE(pref_service2->GetBoolean(prefs::kSafeBrowsingEnabled));
   // We don't expect the state to have changed, but if it did, wait for it.
+  EXPECT_TRUE(sb_service->enabled_by_prefs());
   WaitForIOAndCheckEnabled(sb_service, true);
   EXPECT_TRUE(csd_service->enabled());
 
   // Change one of the prefs. SBS should keep running.
   pref_service->SetBoolean(prefs::kSafeBrowsingEnabled, false);
+  EXPECT_TRUE(sb_service->enabled_by_prefs());
   WaitForIOAndCheckEnabled(sb_service, true);
   EXPECT_TRUE(csd_service->enabled());
 
@@ -1099,6 +1102,7 @@
 // TODO(mattm): Remove this when crbug.com/461493 is fixed.
 #if defined(OS_CHROMEOS)
   // On Chrome OS we should disable safe browsing for signin profile.
+  EXPECT_TRUE(sb_service->enabled_by_prefs());
   WaitForIOAndCheckEnabled(sb_service, true);
   EXPECT_TRUE(csd_service->enabled());
   chromeos::ProfileHelper::GetSigninProfile()
@@ -1107,17 +1111,20 @@
       ->SetBoolean(prefs::kSafeBrowsingEnabled, false);
   WaitForIOThread();
 #endif
+  EXPECT_FALSE(sb_service->enabled_by_prefs());
   WaitForIOAndCheckEnabled(sb_service, false);
   EXPECT_FALSE(csd_service->enabled());
 
   // Turn it back on. SBS comes back.
   pref_service2->SetBoolean(prefs::kSafeBrowsingEnabled, true);
+  EXPECT_TRUE(sb_service->enabled_by_prefs());
   WaitForIOAndCheckEnabled(sb_service, true);
   EXPECT_TRUE(csd_service->enabled());
 
   // Delete the Profile. SBS stops again.
   pref_service2 = NULL;
   profile2.reset();
+  EXPECT_FALSE(sb_service->enabled_by_prefs());
   WaitForIOAndCheckEnabled(sb_service, false);
   EXPECT_FALSE(csd_service->enabled());
 }
diff --git a/chrome/browser/signin/chrome_signin_client.cc b/chrome/browser/signin/chrome_signin_client.cc
index 087ae68a..dc76c7b 100644
--- a/chrome/browser/signin/chrome_signin_client.cc
+++ b/chrome/browser/signin/chrome_signin_client.cc
@@ -16,13 +16,16 @@
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/signin/local_auth.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_cookie_changed_subscription.h"
 #include "chrome/browser/web_data_service_factory.h"
 #include "chrome/common/chrome_version_info.h"
 #include "components/metrics/metrics_service.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/common/profile_management_switches.h"
 #include "components/signin/core/common/signin_pref_names.h"
 #include "components/signin/core/common/signin_switches.h"
+#include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "url/gurl.h"
@@ -47,7 +50,8 @@
 
 ChromeSigninClient::ChromeSigninClient(
     Profile* profile, SigninErrorController* signin_error_controller)
-    : profile_(profile),
+    : OAuth2TokenService::Consumer("chrome_signin_client"),
+      profile_(profile),
       signin_error_controller_(signin_error_controller) {
   signin_error_controller_->AddObserver(this);
 #if !defined(OS_CHROMEOS)
@@ -92,6 +96,10 @@
 #endif
 }
 
+void ChromeSigninClient::DoFinalInit() {
+  MaybeFetchSigninTokenHandle();
+}
+
 // static
 bool ChromeSigninClient::ProfileAllowsSigninCookies(Profile* profile) {
   CookieSettings* cookie_settings =
@@ -294,6 +302,53 @@
       signin_error_controller_->HasError());
 }
 
+void ChromeSigninClient::OnGetTokenInfoResponse(
+    scoped_ptr<base::DictionaryValue> token_info) {
+  if (!token_info->HasKey("error")) {
+    std::string handle;
+    if (token_info->GetString("token_handle", &handle)) {
+      ProfileInfoCache& info_cache =
+          g_browser_process->profile_manager()->GetProfileInfoCache();
+      size_t index = info_cache.GetIndexOfProfileWithPath(profile_->GetPath());
+      info_cache.SetPasswordChangeDetectionTokenAtIndex(index, handle);
+    } else {
+      NOTREACHED();
+    }
+  }
+  oauth_request_.reset();
+}
+
+void ChromeSigninClient::OnOAuthError() {
+  // Ignore the failure.  It's not essential and we'll try again next time.
+    oauth_request_.reset();
+}
+
+void ChromeSigninClient::OnNetworkError(int response_code) {
+  // Ignore the failure.  It's not essential and we'll try again next time.
+    oauth_request_.reset();
+}
+
+void ChromeSigninClient::OnGetTokenSuccess(
+    const OAuth2TokenService::Request* request,
+    const std::string& access_token,
+    const base::Time& expiration_time) {
+  // Exchange the access token for a handle that can be used for later
+  // verification that the token is still valid (i.e. the password has not
+  // been changed).
+    if (!oauth_client_) {
+        oauth_client_.reset(new gaia::GaiaOAuthClient(
+            profile_->GetRequestContext()));
+    }
+    oauth_client_->GetTokenInfo(access_token, 3 /* retries */, this);
+}
+
+void ChromeSigninClient::OnGetTokenFailure(
+    const OAuth2TokenService::Request* request,
+    const GoogleServiceAuthError& error) {
+  // Ignore the failure.  It's not essential and we'll try again next time.
+  oauth_request_.reset();
+}
+
 #if !defined(OS_CHROMEOS)
 void ChromeSigninClient::OnNetworkChanged(
     net::NetworkChangeNotifier::ConnectionType type) {
@@ -329,3 +384,29 @@
     net::URLRequestContextGetter* getter) {
   return new GaiaAuthFetcher(consumer, source, getter);
 }
+
+void ChromeSigninClient::MaybeFetchSigninTokenHandle() {
+#if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS)
+  // We get a "handle" that can be used to reference the signin token on the
+  // server.  We fetch this if we don't have one so that later we can check
+  // it to know if the signin token to which it is attached has been revoked
+  // and thus distinguish between a password mismatch due to the password
+  // being changed and the user simply mis-typing it.
+  if (profiles::IsLockAvailable(profile_)) {
+    ProfileInfoCache& info_cache =
+        g_browser_process->profile_manager()->GetProfileInfoCache();
+    size_t index = info_cache.GetIndexOfProfileWithPath(profile_->GetPath());
+    std::string token = info_cache.GetPasswordChangeDetectionTokenAtIndex(
+        index);
+    std::string account = profile_->GetProfileUserName();
+    if (token.empty() && !oauth_request_) {
+      // If we don't have a token for detecting a password change, create one.
+      ProfileOAuth2TokenService* token_service =
+          ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
+      OAuth2TokenService::ScopeSet scopes;
+      scopes.insert(GaiaConstants::kGoogleUserInfoEmail);
+      oauth_request_ = token_service->StartRequest(account, scopes, this);
+    }
+  }
+#endif
+}
diff --git a/chrome/browser/signin/chrome_signin_client.h b/chrome/browser/signin/chrome_signin_client.h
index 2d57fb9..702c33c0 100644
--- a/chrome/browser/signin/chrome_signin_client.h
+++ b/chrome/browser/signin/chrome_signin_client.h
@@ -9,6 +9,8 @@
 #include "base/compiler_specific.h"
 #include "components/signin/core/browser/signin_client.h"
 #include "components/signin/core/browser/signin_error_controller.h"
+#include "google_apis/gaia/gaia_oauth_client.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 
 #if !defined(OS_CHROMEOS)
 #include "net/base/network_change_notifier.h"
@@ -22,12 +24,15 @@
 #if !defined(OS_CHROMEOS)
       public net::NetworkChangeNotifier::NetworkChangeObserver,
 #endif
-      public SigninErrorController::Observer {
+      public SigninErrorController::Observer,
+      public gaia::GaiaOAuthClient::Delegate,
+      public OAuth2TokenService::Consumer {
  public:
   explicit ChromeSigninClient(
       Profile* profile, SigninErrorController* signin_error_controller);
   ~ChromeSigninClient() override;
   void Shutdown() override;
+  void DoFinalInit() override;
 
   // Utility methods.
   static bool ProfileAllowsSigninCookies(Profile* profile);
@@ -79,6 +84,19 @@
   // SigninErrorController::Observer implementation.
   void OnErrorChanged() override;
 
+  // gaia::GaiaOAuthClient::Delegate implementation.
+  void OnGetTokenInfoResponse(
+      scoped_ptr<base::DictionaryValue> token_info) override;
+  void OnOAuthError() override;
+  void OnNetworkError(int response_code) override;
+
+  // OAuth2TokenService::Consumer implementation
+  void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
+                         const std::string& access_token,
+                         const base::Time& expiration_time) override;
+  void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+                         const GoogleServiceAuthError& error) override;
+
 #if !defined(OS_CHROMEOS)
   // net::NetworkChangeController::NetworkChangeObserver implementation.
   void OnNetworkChanged(net::NetworkChangeNotifier::ConnectionType type)
@@ -86,6 +104,8 @@
 #endif
 
  private:
+  void MaybeFetchSigninTokenHandle();
+
   Profile* profile_;
 
   SigninErrorController* signin_error_controller_;
@@ -93,6 +113,9 @@
   std::list<base::Closure> delayed_callbacks_;
 #endif
 
+  scoped_ptr<gaia::GaiaOAuthClient> oauth_client_;
+  scoped_ptr<OAuth2TokenService::Request> oauth_request_;
+
   DISALLOW_COPY_AND_ASSIGN(ChromeSigninClient);
 };
 
diff --git a/chrome/browser/spellchecker/spelling_service_client.cc b/chrome/browser/spellchecker/spelling_service_client.cc
index a0c1381..f05cbc18 100644
--- a/chrome/browser/spellchecker/spelling_service_client.cc
+++ b/chrome/browser/spellchecker/spelling_service_client.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/spellchecker/spelling_service_client.h"
 
+#include <algorithm>
+
 #include "base/json/json_reader.h"
 #include "base/json/string_escape.h"
 #include "base/logging.h"
@@ -66,8 +68,16 @@
       &language_code,
       &country_code);
 
+  // Replace typographical apostrophes with typewriter apostrophes, so that
+  // server word breaker behaves correctly.
+  const base::char16 kApostrophe = 0x27;
+  const base::char16 kRightSingleQuotationMark = 0x2019;
+  base::string16 text_copy = text;
+  std::replace(text_copy.begin(), text_copy.end(), kRightSingleQuotationMark,
+               kApostrophe);
+
   // Format the JSON request to be sent to the Spelling service.
-  std::string encoded_text = base::GetQuotedJSONString(text);
+  std::string encoded_text = base::GetQuotedJSONString(text_copy);
 
   static const char kSpellingRequest[] =
       "{"
diff --git a/chrome/browser/spellchecker/spelling_service_client_unittest.cc b/chrome/browser/spellchecker/spelling_service_client_unittest.cc
index e1f805f..cdc16cc7 100644
--- a/chrome/browser/spellchecker/spelling_service_client_unittest.cc
+++ b/chrome/browser/spellchecker/spelling_service_client_unittest.cc
@@ -34,14 +34,14 @@
                          const GURL& url,
                          net::URLFetcherDelegate* d,
                          int version,
-                         const std::string& text,
+                         const std::string& sanitized_text,
                          const std::string& language,
                          int status,
                          const std::string& response)
       : net::TestURLFetcher(0, url, d),
         version_(base::StringPrintf("v%d", version)),
         language_(language.empty() ? std::string("en") : language),
-        text_(text) {
+        sanitized_text_(sanitized_text) {
     set_response_code(status);
     SetResponseString(response);
   }
@@ -64,9 +64,9 @@
     std::string version;
     EXPECT_TRUE(value->GetString("apiVersion", &version));
     EXPECT_EQ(version_, version);
-    std::string text;
-    EXPECT_TRUE(value->GetString("params.text", &text));
-    EXPECT_EQ(text_, text);
+    std::string sanitized_text;
+    EXPECT_TRUE(value->GetString("params.text", &sanitized_text));
+    EXPECT_EQ(sanitized_text_, sanitized_text);
     std::string language;
     EXPECT_TRUE(value->GetString("params.language", &language));
     EXPECT_EQ(language_, language);
@@ -106,7 +106,7 @@
   std::string version_;
   std::string language_;
   std::string country_;
-  std::string text_;
+  std::string sanitized_text_;
 };
 
 // A class derived from the SpellingServiceClient class used by the
@@ -124,10 +124,10 @@
   ~TestingSpellingServiceClient() override {}
 
   void SetHTTPRequest(int type,
-                      const std::string& text,
+                      const std::string& sanitized_text,
                       const std::string& language) {
     request_type_ = type;
-    request_text_ = text;
+    sanitized_request_text_ = sanitized_text;
     request_language_ = language;
   }
 
@@ -151,8 +151,7 @@
                       const base::string16& request_text,
                       const std::vector<SpellCheckResult>& results) {
     EXPECT_EQ(success_, success);
-    base::string16 text(base::UTF8ToUTF16(request_text_));
-    EXPECT_EQ(text, request_text);
+    base::string16 text(base::UTF8ToUTF16(sanitized_request_text_));
     for (std::vector<SpellCheckResult>::const_iterator it = results.begin();
          it != results.end(); ++it) {
       text.replace(it->location, it->length, it->replacement);
@@ -168,15 +167,14 @@
  private:
   scoped_ptr<net::URLFetcher> CreateURLFetcher(const GURL& url) override {
     EXPECT_EQ("https://www.googleapis.com/rpc", url.spec());
-    fetcher_ = new TestSpellingURLFetcher(0, url, this,
-                                          request_type_, request_text_,
-                                          request_language_,
-                                          response_status_, response_data_);
+    fetcher_ = new TestSpellingURLFetcher(
+        0, url, this, request_type_, sanitized_request_text_, request_language_,
+        response_status_, response_data_);
     return scoped_ptr<net::URLFetcher>(fetcher_);
   }
 
   int request_type_;
-  std::string request_text_;
+  std::string sanitized_request_text_;
   std::string request_language_;
   int response_status_;
   std::string response_data_;
@@ -217,7 +215,8 @@
 // misspelled words, |corrected_text| should be equal to |request_text|.)
 TEST_F(SpellingServiceClientTest, RequestTextCheck) {
   static const struct {
-    const char* request_text;
+    const wchar_t* request_text;
+    const char* sanitized_request_text;
     SpellingServiceClient::ServiceType request_type;
     int response_status;
     const char* response_data;
@@ -226,6 +225,7 @@
     const char* language;
   } kTests[] = {
     {
+      L"",
       "",
       SpellingServiceClient::SUGGEST,
       500,
@@ -234,6 +234,7 @@
       "",
       "af",
     }, {
+      L"chromebook",
       "chromebook",
       SpellingServiceClient::SUGGEST,
       200,
@@ -242,6 +243,7 @@
       "chromebook",
       "af",
     }, {
+      L"chrombook",
       "chrombook",
       SpellingServiceClient::SUGGEST,
       200,
@@ -261,6 +263,7 @@
       "chromebook",
       "af",
     }, {
+      L"",
       "",
       SpellingServiceClient::SPELLCHECK,
       500,
@@ -269,6 +272,7 @@
       "",
       "en",
     }, {
+      L"I have been to USA.",
       "I have been to USA.",
       SpellingServiceClient::SPELLCHECK,
       200,
@@ -277,6 +281,7 @@
       "I have been to USA.",
       "en",
     }, {
+      L"I have bean to USA.",
       "I have bean to USA.",
       SpellingServiceClient::SPELLCHECK,
       200,
@@ -295,6 +300,27 @@
       true,
       "I have been to USA.",
       "en",
+    }, {
+      L"I\x2019mattheIn'n'Out.",
+      "I'mattheIn'n'Out.",
+      SpellingServiceClient::SPELLCHECK,
+      200,
+      "{\n"
+      "  \"result\": {\n"
+      "    \"spellingCheckResponse\": {\n"
+      "      \"misspellings\": [{\n"
+      "        \"charStart\": 0,\n"
+      "        \"charLength\": 16,\n"
+      "        \"suggestions\":"
+      " [{ \"suggestion\": \"I'm at the In'N'Out\" }],\n"
+      "        \"canAutoCorrect\": false\n"
+      "      }]\n"
+      "    }\n"
+      "  }\n"
+      "}",
+      true,
+      "I'm at the In'N'Out.",
+      "en",
     },
   };
 
@@ -303,7 +329,8 @@
   pref->SetBoolean(prefs::kSpellCheckUseSpellingService, true);
 
   for (size_t i = 0; i < arraysize(kTests); ++i) {
-    client_.SetHTTPRequest(kTests[i].request_type, kTests[i].request_text,
+    client_.SetHTTPRequest(kTests[i].request_type,
+                           kTests[i].sanitized_request_text,
                            kTests[i].language);
     client_.SetHTTPResponse(kTests[i].response_status, kTests[i].response_data);
     client_.SetExpectedTextCheckResult(kTests[i].success,
@@ -312,7 +339,7 @@
     client_.RequestTextCheck(
         &profile_,
         kTests[i].request_type,
-        base::ASCIIToUTF16(kTests[i].request_text),
+        base::WideToUTF16(kTests[i].request_text),
         base::Bind(&SpellingServiceClientTest::OnTextCheckComplete,
                    base::Unretained(this), 0));
     client_.CallOnURLFetchComplete();
diff --git a/chrome/browser/ssl/connection_security.cc b/chrome/browser/ssl/connection_security.cc
index 5cb039b2..6c1b47f 100644
--- a/chrome/browser/ssl/connection_security.cc
+++ b/chrome/browser/ssl/connection_security.cc
@@ -102,6 +102,11 @@
 
     case content::SECURITY_STYLE_AUTHENTICATED: {
 #if defined(OS_CHROMEOS)
+      // Report if there is a policy cert first, before reporting any other
+      // authenticated-but-with-errors cases. A policy cert is a strong
+      // indicator of a MITM being present (the enterprise), while the
+      // other authenticated-but-with-errors indicate something may
+      // be wrong, or may be wrong in the future, but is unclear now.
       policy::PolicyCertService* service =
           policy::PolicyCertServiceFactory::GetForProfile(
               Profile::FromBrowserContext(web_contents->GetBrowserContext()));
diff --git a/chrome/browser/sync/test/integration/multiple_client_preferences_sync_test.cc b/chrome/browser/sync/test/integration/multiple_client_preferences_sync_test.cc
index 653a991..b4a95c8f 100644
--- a/chrome/browser/sync/test/integration/multiple_client_preferences_sync_test.cc
+++ b/chrome/browser/sync/test/integration/multiple_client_preferences_sync_test.cc
@@ -2,14 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/guid.h"
+#include "base/prefs/pref_service.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "chrome/browser/sync/test/integration/preferences_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/common/pref_names.h"
 
-using preferences_helper::ChangeListPref;
-using preferences_helper::AwaitListPrefMatches;
+using preferences_helper::AwaitStringPrefMatches;
+using preferences_helper::ChangeStringPref;
+using preferences_helper::GetPrefs;
 
 class MultipleClientPreferencesSyncTest : public SyncTest {
  public:
@@ -22,16 +25,15 @@
   DISALLOW_COPY_AND_ASSIGN(MultipleClientPreferencesSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_F(MultipleClientPreferencesSyncTest, Sanity) {
-  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+IN_PROC_BROWSER_TEST_F(MultipleClientPreferencesSyncTest, Sanity_E2ETest) {
   DisableVerifier();
-
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+  ASSERT_TRUE(AwaitStringPrefMatches(prefs::kHomePage));
+  const std::string new_home_page = base::StringPrintf(
+      "https://example.com/%s", base::GenerateGUID().c_str());
+  ChangeStringPref(0, prefs::kHomePage, new_home_page);
+  ASSERT_TRUE(AwaitStringPrefMatches(prefs::kHomePage));
   for (int i = 0; i < num_clients(); ++i) {
-    base::ListValue urls;
-    urls.Append(new base::StringValue(
-        base::StringPrintf("http://www.google.com/%d", i)));
-    ChangeListPref(i, prefs::kURLsToRestoreOnStartup, urls);
+    ASSERT_EQ(new_home_page, GetPrefs(i)->GetString(prefs::kHomePage));
   }
-
-  ASSERT_TRUE(AwaitListPrefMatches(prefs::kURLsToRestoreOnStartup));
 }
diff --git a/chrome/browser/sync/test/integration/two_client_e2e_test.cc b/chrome/browser/sync/test/integration/two_client_e2e_test.cc
index 51455a1..c2ff682 100644
--- a/chrome/browser/sync/test/integration/two_client_e2e_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_e2e_test.cc
@@ -4,15 +4,18 @@
 
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
+#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
+#include "components/bookmarks/browser/bookmark_node.h"
 
-// The E2E tests are designed to run only against real backend servers. They are
-// disabled on regular bots. http://crbug.com/431366
+// The E2E_ONLY tests are designed to run only against real backend
+// servers. They are disabled on regular bots. http://crbug.com/431366
 #define E2E_ONLY(x) DISABLED_##x
 
 using bookmarks_helper::AddURL;
 using bookmarks_helper::AwaitAllModelsMatch;
 using bookmarks_helper::CountAllBookmarks;
+using bookmarks_helper::GetBookmarkBarNode;
 
 class TwoClientE2ETest : public SyncTest {
  public:
@@ -76,3 +79,33 @@
         "Total bookmark count is wrong.";
   }
 }
+
+// Verify that a bookmark added on a client with bookmark syncing disabled gets
+// synced to a second client once bookmark syncing is re-enabled.
+IN_PROC_BROWSER_TEST_F(TwoClientE2ETest, AddBookmarkWhileDisabled) {
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+  ASSERT_TRUE(AwaitAllModelsMatch())
+      << "Initial bookmark models did not match for all profiles";
+  const int initial_count = CountAllBookmarks(0);
+
+  // Verify that we can sync. Add a bookmark on the first client and verify it's
+  // synced to the second client.
+  const std::string url_title = "a happy little url";
+  const GURL url("https://example.com");
+  ASSERT_TRUE(AddURL(0, GetBookmarkBarNode(0), 0, url_title, url) != NULL);
+  ASSERT_TRUE(AwaitAllModelsMatch());
+  ASSERT_EQ(initial_count + 1, CountAllBookmarks(0));
+  ASSERT_EQ(initial_count + 1, CountAllBookmarks(1));
+
+  // Disable bookmark syncing on the first client, add another bookmark,
+  // re-enable bookmark syncing and see that the second bookmark reaches the
+  // second client.
+  ASSERT_TRUE(GetClient(0)->DisableSyncForDatatype(syncer::BOOKMARKS));
+  const std::string url_title_2 = "another happy little url";
+  const GURL url_2("https://example.com/second");
+  ASSERT_TRUE(AddURL(0, GetBookmarkBarNode(0), 0, url_title_2, url_2) != NULL);
+  ASSERT_TRUE(GetClient(0)->EnableSyncForDatatype(syncer::BOOKMARKS));
+  ASSERT_TRUE(AwaitAllModelsMatch());
+  ASSERT_EQ(initial_count + 2, CountAllBookmarks(0));
+  ASSERT_EQ(initial_count + 2, CountAllBookmarks(1));
+}
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate_chromeos.cc b/chrome/browser/ui/ash/chrome_shell_delegate_chromeos.cc
index 7eb8da5b..0ea94183 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate_chromeos.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/speech/tts_controller.h"
 #include "chrome/browser/sync/sync_error_notifier_factory_ash.h"
 #include "chrome/browser/ui/ash/chrome_new_window_delegate_chromeos.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/media_delegate_chromeos.h"
 #include "chrome/browser/ui/ash/session_state_delegate_chromeos.h"
 #include "chrome/browser/ui/ash/system_tray_delegate_chromeos.h"
@@ -262,6 +263,11 @@
         SigninErrorNotifierFactory::GetForProfile(profile);
         SyncErrorNotifierFactory::GetForProfile(profile);
       }
+      // Do not use chrome::NOTIFICATION_PROFILE_ADDED because the
+      // profile is not fully initialized by user_manager.  Use
+      // chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED instead.
+      if (shelf_delegate_)
+        shelf_delegate_->OnUserProfileReadyToSwitch(profile);
       ash::Shell::GetInstance()->OnLoginUserProfilePrepared();
       break;
     }
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index 295ca31..4be5c65 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -68,8 +68,6 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/favicon/content/content_favicon_driver.h"
 #include "content/public/browser/navigation_entry.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
@@ -275,20 +273,13 @@
 // A class to get events from ChromeOS when a user gets changed or added.
 class ChromeLauncherControllerUserSwitchObserverChromeOS
     : public ChromeLauncherControllerUserSwitchObserver,
-      public user_manager::UserManager::UserSessionStateObserver,
-      content::NotificationObserver {
+      public user_manager::UserManager::UserSessionStateObserver {
  public:
   ChromeLauncherControllerUserSwitchObserverChromeOS(
       ChromeLauncherController* controller)
       : controller_(controller) {
     DCHECK(user_manager::UserManager::IsInitialized());
     user_manager::UserManager::Get()->AddSessionStateObserver(this);
-    // A UserAddedToSession notification can be sent before a profile is loaded.
-    // Since our observers require that we have already a profile, we might have
-    // to postpone the notification until the ProfileManager lets us know that
-    // the profile for that newly added user was added to the ProfileManager.
-    registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
-                   content::NotificationService::AllSources());
   }
   ~ChromeLauncherControllerUserSwitchObserverChromeOS() override {
     user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
@@ -297,10 +288,8 @@
   // user_manager::UserManager::UserSessionStateObserver overrides:
   void UserAddedToSession(const user_manager::User* added_user) override;
 
-  // content::NotificationObserver overrides:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
+  // ChromeLauncherControllerUserSwitchObserver:
+  void OnUserProfileReadyToSwitch(Profile* profile) override;
 
  private:
   // Add a user to the session.
@@ -309,10 +298,6 @@
   // The owning ChromeLauncherController.
   ChromeLauncherController* controller_;
 
-  // The notification registrar to track the Profile creations after a user got
-  // added to the session (if required).
-  content::NotificationRegistrar registrar_;
-
   // Users which were just added to the system, but which profiles were not yet
   // (fully) loaded.
   std::set<std::string> added_user_ids_waiting_for_profiles_;
@@ -332,14 +317,10 @@
     AddUser(profile);
 }
 
-void ChromeLauncherControllerUserSwitchObserverChromeOS::Observe(
-    int type,
-    const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-  if (type == chrome::NOTIFICATION_PROFILE_ADDED &&
-      !added_user_ids_waiting_for_profiles_.empty()) {
+void ChromeLauncherControllerUserSwitchObserverChromeOS::
+    OnUserProfileReadyToSwitch(Profile* profile) {
+  if (!added_user_ids_waiting_for_profiles_.empty()) {
     // Check if the profile is from a user which was on the waiting list.
-    Profile* profile = content::Source<Profile>(source).ptr();
     std::string user_id = multi_user_util::GetUserIDFromProfile(profile);
     std::set<std::string>::iterator it = std::find(
         added_user_ids_waiting_for_profiles_.begin(),
@@ -1523,6 +1504,11 @@
              GetShelfAlignmentFromPrefs(other_profile, root_window);
 }
 
+void ChromeLauncherController::OnUserProfileReadyToSwitch(Profile* profile) {
+  if (user_switch_observer_.get())
+    user_switch_observer_->OnUserProfileReadyToSwitch(profile);
+}
+
 void ChromeLauncherController::LauncherItemClosed(ash::ShelfID id) {
   IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
   CHECK(iter != id_to_item_controller_map_.end());
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
index 6c30663..4207b59 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -73,11 +73,14 @@
 
 // A class which needs to be overwritten dependent on the used OS to moitor
 // user switching.
+// TODO(oshima): move this to .cc
 class ChromeLauncherControllerUserSwitchObserver {
  public:
   ChromeLauncherControllerUserSwitchObserver() {}
   virtual ~ChromeLauncherControllerUserSwitchObserver() {}
 
+  virtual void OnUserProfileReadyToSwitch(Profile* profile) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerUserSwitchObserver);
 };
@@ -405,6 +408,9 @@
   bool ShelfBoundsChangesProbablyWithUser(aura::Window* root_window,
                                           const std::string& user_id) const;
 
+  // Called when the user profile is fully loaded and ready to switch to.
+  void OnUserProfileReadyToSwitch(Profile* profile);
+
   // Access to the BrowserStatusMonitor for tests.
   BrowserStatusMonitor* browser_status_monitor_for_test() {
     return browser_status_monitor_.get();
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
index 91f4a40..1a295a4 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
@@ -8,8 +8,9 @@
 
 #include "base/memory/singleton.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/extensions/api/automation_internal/automation_util.h"
+#include "chrome/browser/extensions/api/automation_internal/automation_event_router.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/extensions/chrome_extension_messages.h"
 #include "content/public/browser/ax_event_notification_details.h"
 #include "content/public/browser/browser_context.h"
 #include "ui/aura/window.h"
@@ -19,6 +20,7 @@
 #include "ui/views/widget/widget.h"
 
 using content::BrowserContext;
+using extensions::AutomationEventRouter;
 
 // static
 AutomationManagerAura* AutomationManagerAura::GetInstance() {
@@ -129,18 +131,11 @@
 void AutomationManagerAura::SendEvent(BrowserContext* context,
                                       views::AXAuraObjWrapper* aura_obj,
                                       ui::AXEvent event_type) {
-  ui::AXTreeUpdate update;
-  current_tree_serializer_->SerializeChanges(aura_obj, &update);
-
-  // Route this event to special process/routing ids recognized by the
-  // Automation API as the desktop tree.
-  // TODO(dtseng): Would idealy define these special desktop constants in idl.
-  content::AXEventNotificationDetails detail(
-      update.node_id_to_clear, update.nodes, event_type, aura_obj->GetID(),
-      0, /* process_id */
-      0 /* routing_id */);
-  std::vector<content::AXEventNotificationDetails> details;
-  details.push_back(detail);
-  extensions::automation_util::DispatchAccessibilityEventsToAutomation(
-      details, context, gfx::Vector2d());
+  ExtensionMsg_AccessibilityEventParams params;
+  current_tree_serializer_->SerializeChanges(aura_obj, &params.update);
+  params.tree_id = 0;
+  params.id = aura_obj->GetID();
+  params.event_type = event_type;
+  AutomationEventRouter* router = AutomationEventRouter::GetInstance();
+  router->DispatchAccessibilityEvent(params);
 }
diff --git a/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm b/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm
index 360c81a..0713f42ba9 100644
--- a/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm
+++ b/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm
@@ -35,12 +35,6 @@
   // A temporary window that holds |snapshotLayer_|.
   base::scoped_nsobject<NSWindow> snapshotWindow_;
 
-  // The animation applied to |snapshotLayer_|.
-  base::scoped_nsobject<CAAnimationGroup> snapshotAnimation_;
-
-  // The animation applied to the root layer of |primaryWindow_|.
-  base::scoped_nsobject<CAAnimationGroup> primaryWindowAnimation_;
-
   // The frame of the |primaryWindow_| before the transition began.
   NSRect primaryWindowInitialFrame_;
 
@@ -99,12 +93,6 @@
   return self;
 }
 
-- (void)dealloc {
-  [snapshotAnimation_ setDelegate:nil];
-  [primaryWindowAnimation_ setDelegate:nil];
-  [super dealloc];
-}
-
 - (NSArray*)customWindowsToEnterFullScreen {
   [self takeSnapshot];
   [self makeAndPrepareSnapshotWindow];
@@ -222,7 +210,6 @@
   [group setValue:kSnapshotWindowAnimationID forKey:kAnimationIDKey];
   group.delegate = self;
 
-  snapshotAnimation_.reset([group retain]);
   [snapshotLayer_ addAnimation:group forKey:nil];
 }
 
@@ -273,8 +260,6 @@
   [group setValue:kPrimaryWindowAnimationID forKey:kAnimationIDKey];
   group.delegate = self;
 
-  primaryWindowAnimation_.reset([group retain]);
-
   [root addAnimation:group forKey:kPrimaryWindowAnimationID];
 }
 
diff --git a/chrome/browser/ui/cocoa/profiles/user_manager_mac.mm b/chrome/browser/ui/cocoa/profiles/user_manager_mac.mm
index 857753e..18d717c9 100644
--- a/chrome/browser/ui/cocoa/profiles/user_manager_mac.mm
+++ b/chrome/browser/ui/cocoa/profiles/user_manager_mac.mm
@@ -43,6 +43,7 @@
 // An open User Manager window. There can only be one open at a time. This
 // is reset to NULL when the window is closed.
 UserManagerMac* instance_ = NULL;  // Weak.
+BOOL instance_under_construction_ = NO;
 
 // Custom WebContentsDelegate that allows handling of hotkeys.
 class UserManagerWebContentsDelegate : public content::WebContentsDelegate {
@@ -199,6 +200,14 @@
     return;
   }
 
+  // Under some startup conditions, we can try twice to create the User Manager.
+  // Because creating the System profile is asynchronous, it's possible for
+  // there to then be multiple pending operations and eventually multiple
+  // User Managers.
+  if (instance_under_construction_)
+      return;
+  instance_under_construction_ = YES;
+
   // Create the guest profile, if necessary, and open the User Manager
   // from the guest profile.
   profiles::CreateSystemProfileForUserManager(
@@ -238,6 +247,7 @@
   instance_ = new UserManagerMac(system_profile);
   instance_->set_user_manager_started_showing(start_time);
   [instance_->window_controller() showURL:GURL(url)];
+  instance_under_construction_ = NO;
 }
 
 void UserManagerMac::LogTimeToOpen() {
diff --git a/chrome/browser/ui/extensions/extension_enable_flow.cc b/chrome/browser/ui/extensions/extension_enable_flow.cc
index db518c7a..c1c8e72 100644
--- a/chrome/browser/ui/extensions/extension_enable_flow.cc
+++ b/chrome/browser/ui/extensions/extension_enable_flow.cc
@@ -96,7 +96,7 @@
     return;
   }
 
-  if (profiles::IsProfileLocked(profile_)) {
+  if (profiles::IsProfileLocked(profile_->GetPath())) {
     UserManager::Show(base::FilePath(),
                       profiles::USER_MANAGER_NO_TUTORIAL,
                       profiles::USER_MANAGER_SELECT_PROFILE_APP_LAUNCHER);
diff --git a/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.cc b/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.cc
index e4a24c72..2a3d1ba8 100644
--- a/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.cc
+++ b/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/autocomplete/shortcuts_backend.h"
 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
@@ -13,6 +12,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/infobars/core/infobar.h"
+#include "components/omnibox/shortcuts_backend.h"
 #include "content/public/browser/web_contents.h"
 #include "grit/theme_resources.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ui/omnibox/omnibox_edit_model.cc b/chrome/browser/ui/omnibox/omnibox_edit_model.cc
index 52f2837f..0106ddb 100644
--- a/chrome/browser/ui/omnibox/omnibox_edit_model.cc
+++ b/chrome/browser/ui/omnibox/omnibox_edit_model.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
-#include "chrome/browser/autocomplete/history_url_provider.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/bookmarks/bookmark_stats.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -55,6 +54,7 @@
 #include "components/metrics/proto/omnibox_event.pb.h"
 #include "components/omnibox/autocomplete_match_type.h"
 #include "components/omnibox/autocomplete_provider.h"
+#include "components/omnibox/history_url_provider.h"
 #include "components/omnibox/keyword_provider.h"
 #include "components/omnibox/omnibox_log.h"
 #include "components/omnibox/search_provider.h"
@@ -297,11 +297,6 @@
 }
 
 bool OmniboxEditModel::UpdatePermanentText() {
-  SearchProvider* search_provider =
-      autocomplete_controller()->search_provider();
-  if (search_provider && delegate_->CurrentPageExists())
-    search_provider->set_current_page_url(delegate_->GetURL());
-
   // When there's new permanent text, and the user isn't interacting with the
   // omnibox, we want to revert the edit to show the new text.  We could simply
   // define "interacting" as "the omnibox has focus", but we still allow updates
diff --git a/chrome/browser/ui/omnibox/omnibox_navigation_observer.cc b/chrome/browser/ui/omnibox/omnibox_navigation_observer.cc
index 2b6bea4..94c7db3 100644
--- a/chrome/browser/ui/omnibox/omnibox_navigation_observer.cc
+++ b/chrome/browser/ui/omnibox/omnibox_navigation_observer.cc
@@ -4,11 +4,11 @@
 
 #include "chrome/browser/ui/omnibox/omnibox_navigation_observer.h"
 
-#include "chrome/browser/autocomplete/shortcuts_backend.h"
 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/intranet_redirect_detector.h"
 #include "chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h"
+#include "components/omnibox/shortcuts_backend.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_details.h"
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index ef63966..a6f2e8e 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -11,7 +11,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/autocomplete/history_quick_provider.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/history/history_service_factory.h"
@@ -38,6 +37,7 @@
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/omnibox/autocomplete_input.h"
 #include "components/omnibox/autocomplete_match.h"
+#include "components/omnibox/history_quick_provider.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
 #include "content/public/browser/notification_service.h"
diff --git a/chrome/browser/ui/views/desktop_media_picker_views_unittest.cc b/chrome/browser/ui/views/desktop_media_picker_views_unittest.cc
index 49700c5..d43e36c9 100644
--- a/chrome/browser/ui/views/desktop_media_picker_views_unittest.cc
+++ b/chrome/browser/ui/views/desktop_media_picker_views_unittest.cc
@@ -15,6 +15,7 @@
 #include "ui/events/event_utils.h"
 #include "ui/views/test/scoped_views_test_helper.h"
 #include "ui/views/widget/widget.h"
+#include "ui/views/window/dialog_client_view.h"
 #include "ui/views/window/dialog_delegate.h"
 
 namespace views {
@@ -25,12 +26,6 @@
   ~DesktopMediaPickerViewsTest() override {}
 
   void SetUp() override {
-    Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
-    params.context = test_helper_.GetContext();
-    params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-    parent_widget_.reset(new Widget);
-    parent_widget_->Init(params);
-
     media_list_ = new FakeDesktopMediaList();
     scoped_ptr<FakeDesktopMediaList> media_list(media_list_);
 
@@ -38,7 +33,7 @@
 
     picker_views_.reset(new DesktopMediaPickerViews());
     picker_views_->Show(NULL,
-                        parent_widget_->GetNativeWindow(),
+                        test_helper_.GetContext(),
                         NULL,
                         app_name,
                         app_name,
@@ -47,6 +42,13 @@
                                    base::Unretained(this)));
   }
 
+  void TearDown() override {
+    if (GetPickerDialogView()) {
+      EXPECT_CALL(*this, OnPickerDone(content::DesktopMediaID()));
+      GetPickerDialogView()->GetWidget()->CloseNow();
+    }
+  }
+
   DesktopMediaPickerDialogView* GetPickerDialogView() const {
     return picker_views_->GetDialogViewForTesting();
   }
@@ -57,7 +59,6 @@
   content::TestBrowserThreadBundle thread_bundle_;
   views::ScopedViewsTestHelper test_helper_;
   FakeDesktopMediaList* media_list_;
-  scoped_ptr<Widget> parent_widget_;
   scoped_ptr<DesktopMediaPickerViews> picker_views_;
 };
 
@@ -73,7 +74,6 @@
   EXPECT_CALL(*this,
               OnPickerDone(content::DesktopMediaID(
                   content::DesktopMediaID::TYPE_WINDOW, kFakeId)));
-
   media_list_->AddSource(kFakeId);
 
   EXPECT_FALSE(
@@ -83,7 +83,7 @@
   EXPECT_TRUE(
       GetPickerDialogView()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
 
-  GetPickerDialogView()->Accept();
+  GetPickerDialogView()->GetDialogClientView()->AcceptWindow();
   base::RunLoop().RunUntilIdle();
 }
 
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 fc8cba7..184c6ee 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -207,7 +207,15 @@
 
     // If the popup is currently closed, we need to create it.
     popup_ = (new AutocompletePopupWidget)->AsWeakPtr();
+    // On Windows use TYPE_MENU to ensure that this window uses the software
+    // compositor which avoids the UI thread blocking issue during command
+    // buffer creation. We can revert this change once http://crbug.com/125248
+    // is fixed.
+#if defined(OS_WIN)
+    views::Widget::InitParams params(views::Widget::InitParams::TYPE_MENU);
+#else
     views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
+#endif
     params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
     params.parent = popup_parent->GetNativeView();
     params.bounds = GetPopupBounds();
diff --git a/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.cc b/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.cc
index e1f5c14..4719d97 100644
--- a/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.cc
+++ b/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.h"
 
+#include "base/callback_helpers.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
@@ -22,9 +23,14 @@
     : CertificateSelector(certificates, web_contents),
       extension_name_(extension_name),
       callback_(callback) {
+  DCHECK(!callback_.is_null());
 }
 
 PlatformKeysCertificateSelector::~PlatformKeysCertificateSelector() {
+  // Ensure to call back even if the dialog was closed because of the views
+  // hierarchy being destroyed.
+  if (!callback_.is_null())
+    base::ResetAndReturn(&callback_).Run(nullptr);
 }
 
 void PlatformKeysCertificateSelector::Init() {
@@ -44,15 +50,17 @@
 }
 
 bool PlatformKeysCertificateSelector::Cancel() {
-  callback_.Run(nullptr);
+  DCHECK(!callback_.is_null());
+  base::ResetAndReturn(&callback_).Run(nullptr);
   return true;
 }
 
 bool PlatformKeysCertificateSelector::Accept() {
+  DCHECK(!callback_.is_null());
   scoped_refptr<net::X509Certificate> cert = GetSelectedCert();
   if (!cert)
     return false;
-  callback_.Run(cert);
+  base::ResetAndReturn(&callback_).Run(cert);
   return true;
 }
 
diff --git a/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.h b/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.h
index 74e86bb..c70575a 100644
--- a/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.h
+++ b/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.h
@@ -26,6 +26,7 @@
 // requests access to certificates.
 class PlatformKeysCertificateSelector : public chrome::CertificateSelector {
  public:
+  // |callback| must not be null.
   PlatformKeysCertificateSelector(const net::CertificateList& certificates,
                                   const std::string& extension_name,
                                   const CertificateSelectedCallback& callback,
@@ -40,7 +41,9 @@
 
  private:
   const std::string extension_name_;
-  const CertificateSelectedCallback callback_;
+
+  // Will be reset to null after it was run.
+  CertificateSelectedCallback callback_;
 
   DISALLOW_COPY_AND_ASSIGN(PlatformKeysCertificateSelector);
 };
diff --git a/chrome/browser/ui/views/profiles/user_manager_view.cc b/chrome/browser/ui/views/profiles/user_manager_view.cc
index 47331b9..6435d04 100644
--- a/chrome/browser/ui/views/profiles/user_manager_view.cc
+++ b/chrome/browser/ui/views/profiles/user_manager_view.cc
@@ -46,6 +46,7 @@
 // An open User Manager window. There can only be one open at a time. This
 // is reset to NULL when the window is closed.
 UserManagerView* instance_ = NULL;
+bool instance_under_construction_ = false;
 
 } // namespace
 
@@ -71,6 +72,13 @@
     return;
   }
 
+  // Under some startup conditions, we can try twice to create the User Manager.
+  // Because creating the System profile is asynchronous, it's possible for
+  // there to then be multiple pending operations and eventually multiple
+  // User Managers.
+  if (instance_under_construction_)
+      return;
+
   // Create the system profile, if necessary, and open the user manager
   // from the system profile.
   UserManagerView* user_manager = new UserManagerView();
@@ -80,7 +88,9 @@
       tutorial_mode,
       profile_open_action,
       base::Bind(&UserManagerView::OnSystemProfileCreated,
-                 base::Passed(make_scoped_ptr(user_manager))));
+                 base::Passed(make_scoped_ptr(user_manager)),
+                 base::Owned(new base::AutoReset<bool>(
+                     &instance_under_construction_, true))));
 }
 
 void UserManager::Hide() {
@@ -111,6 +121,7 @@
 // static
 void UserManagerView::OnSystemProfileCreated(
     scoped_ptr<UserManagerView> instance,
+    base::AutoReset<bool>* pending,
     Profile* system_profile,
     const std::string& url) {
   // If we are showing the User Manager after locking a profile, change the
diff --git a/chrome/browser/ui/views/profiles/user_manager_view.h b/chrome/browser/ui/views/profiles/user_manager_view.h
index 7554b472..ae9e7ca0 100644
--- a/chrome/browser/ui/views/profiles/user_manager_view.h
+++ b/chrome/browser/ui/views/profiles/user_manager_view.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_PROFILES_USER_MANAGER_VIEW_H_
 #define CHROME_BROWSER_UI_VIEWS_PROFILES_USER_MANAGER_VIEW_H_
 
+#include "base/auto_reset.h"
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_window.h"
@@ -25,6 +26,7 @@
   // Creates a new UserManagerView instance for the |system_profile| and shows
   // the |url|.
   static void OnSystemProfileCreated(scoped_ptr<UserManagerView> instance,
+                                     base::AutoReset<bool>* pending,
                                      Profile* system_profile,
                                      const std::string& url);
 
diff --git a/chrome/browser/ui/views/sad_tab_view.cc b/chrome/browser/ui/views/sad_tab_view.cc
index fb7dc88..dfdd33dc 100644
--- a/chrome/browser/ui/views/sad_tab_view.cc
+++ b/chrome/browser/ui/views/sad_tab_view.cc
@@ -34,7 +34,7 @@
 #include "ui/views/widget/widget.h"
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/memory/oom_memory_details.h"
+#include "chrome/browser/memory/oom_memory_details.h"
 #endif
 
 using content::OpenURLParams;
@@ -91,8 +91,8 @@
   DCHECK(web_contents);
 
   // These stats should use the same counting approach and bucket size used for
-  // tab discard events in chromeos::OomPriorityManager so they can be
-  // directly compared.
+  // tab discard events in memory::OomPriorityManager so they can be directly
+  // compared.
   // TODO(jamescook): Maybe track time between sad tabs?
   switch (kind_) {
     case chrome::SAD_TAB_KIND_CRASHED: {
@@ -113,9 +113,8 @@
       RecordKillCreated();
       RecordKillCreatedOOM();
       const std::string spec = web_contents->GetURL().GetOrigin().spec();
-      chromeos::OomMemoryDetails::Log(
-          "Tab OOM-Killed Memory details: " + spec + ", ",
-          base::Closure());
+      memory::OomMemoryDetails::Log(
+          "Tab OOM-Killed Memory details: " + spec + ", ", base::Closure());
       break;
     }
 #endif
@@ -297,8 +296,8 @@
 void SadTabView::OnPaint(gfx::Canvas* canvas) {
   if (!painted_) {
     // These stats should use the same counting approach and bucket size used
-    // for tab discard events in chromeos::OomPriorityManager so they
-    // can be directly compared.
+    // for tab discard events in memory::OomPriorityManager so they can be
+    // directly compared.
     switch (kind_) {
       case chrome::SAD_TAB_KIND_CRASHED: {
         static int crashed = 0;
diff --git a/chrome/browser/ui/views/status_bubble_views.cc b/chrome/browser/ui/views/status_bubble_views.cc
index b940994..5257070 100644
--- a/chrome/browser/ui/views/status_bubble_views.cc
+++ b/chrome/browser/ui/views/status_bubble_views.cc
@@ -581,7 +581,15 @@
       view_ = new StatusView(popup_.get(), frame->GetThemeProvider());
     if (!expand_view_.get())
       expand_view_.reset(new StatusViewExpander(this, view_));
+    // On Windows use TYPE_MENU to ensure that this window uses the software
+    // compositor which avoids the UI thread blocking issue during command
+    // buffer creation. We can revert this change once http://crbug.com/125248
+    // is fixed.
+#if defined(OS_WIN)
+    views::Widget::InitParams params(views::Widget::InitParams::TYPE_MENU);
+#else
     views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
+#endif
     params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
     params.accept_events = false;
     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
diff --git a/chrome/browser/ui/website_settings/permission_bubble_manager.cc b/chrome/browser/ui/website_settings/permission_bubble_manager.cc
index 03051d61..a99d762 100644
--- a/chrome/browser/ui/website_settings/permission_bubble_manager.cc
+++ b/chrome/browser/ui/website_settings/permission_bubble_manager.cc
@@ -55,6 +55,16 @@
 
 }  // namespace
 
+// PermissionBubbleManager::Observer -------------------------------------------
+
+PermissionBubbleManager::Observer::~Observer() {
+}
+
+void PermissionBubbleManager::Observer::OnBubbleAdded() {
+}
+
+// PermissionBubbleManager -----------------------------------------------------
+
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PermissionBubbleManager);
 
 // static
@@ -348,6 +358,7 @@
   // case we may do in-line calling of finalization.
   bubble_showing_ = true;
   view_->Show(requests_, accept_states_);
+  NotifyBubbleAdded();
 
   // If in testing mode, automatically respond to the bubble that was shown.
   if (auto_response_for_test_ != NONE)
@@ -420,6 +431,18 @@
   return false;
 }
 
+void PermissionBubbleManager::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void PermissionBubbleManager::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+void PermissionBubbleManager::NotifyBubbleAdded() {
+  FOR_EACH_OBSERVER(Observer, observer_list_, OnBubbleAdded());
+}
+
 void PermissionBubbleManager::DoAutoResponseForTesting() {
   switch (auto_response_for_test_) {
     case ACCEPT_ALL:
diff --git a/chrome/browser/ui/website_settings/permission_bubble_manager.h b/chrome/browser/ui/website_settings/permission_bubble_manager.h
index 72366d3..d95b9b1 100644
--- a/chrome/browser/ui/website_settings/permission_bubble_manager.h
+++ b/chrome/browser/ui/website_settings/permission_bubble_manager.h
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
 #include "chrome/browser/ui/website_settings/permission_bubble_view.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -30,6 +31,12 @@
       public content::WebContentsUserData<PermissionBubbleManager>,
       public PermissionBubbleView::Delegate {
  public:
+  class Observer {
+   public:
+    virtual ~Observer();
+    virtual void OnBubbleAdded();
+  };
+
   enum AutoResponseType {
     NONE,
     ACCEPT_ALL,
@@ -71,6 +78,10 @@
   // comes in with a user gesture.
   void RequireUserGesture(bool required);
 
+  // For observing the status of the permission bubble manager.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
   // Do NOT use this methods in production code. Use this methods in browser
   // tests that need to accept or deny permissions when requested in
   // JavaScript. Your test needs to set this appropriately, and then the bubble
@@ -134,6 +145,8 @@
   bool HasUserGestureRequest(
       const std::vector<PermissionBubbleRequest*>& queue);
 
+  void NotifyBubbleAdded();
+
   void DoAutoResponseForTesting();
 
   // Whether to delay displaying the bubble until a request with a user gesture.
@@ -157,6 +170,7 @@
 
   std::vector<bool> accept_states_;
 
+  base::ObserverList<Observer> observer_list_;
   AutoResponseType auto_response_for_test_;
 
   base::WeakPtrFactory<PermissionBubbleManager> weak_factory_;
diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc
index ec8cd83..9047f0a 100644
--- a/chrome/browser/ui/webui/about_ui.cc
+++ b/chrome/browser/ui/webui/about_ui.cc
@@ -76,7 +76,7 @@
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/chromeos/customization/customization_document.h"
-#include "chrome/browser/chromeos/memory/oom_priority_manager.h"
+#include "chrome/browser/memory/oom_priority_manager.h"
 #endif
 
 using base::Time;
@@ -516,7 +516,7 @@
       "<p>Tabs sorted from most interesting to least interesting. The least "
       "interesting tab may be discarded if we run out of physical memory.</p>");
 
-  chromeos::OomPriorityManager* oom =
+  memory::OomPriorityManager* oom =
       g_browser_process->platform_part()->oom_priority_manager();
   std::vector<base::string16> titles = oom->GetTabTitles();
   if (!titles.empty()) {
diff --git a/chrome/browser/ui/webui/foreign_session_handler.cc b/chrome/browser/ui/webui/foreign_session_handler.cc
index 2f8e112..4dcf49a 100644
--- a/chrome/browser/ui/webui/foreign_session_handler.cc
+++ b/chrome/browser/ui/webui/foreign_session_handler.cc
@@ -12,10 +12,12 @@
 #include "base/bind_helpers.h"
 #include "base/i18n/time_formatting.h"
 #include "base/memory/scoped_vector.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/prefs/pref_service.h"
 #include "base/prefs/scoped_user_pref_update.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
@@ -53,6 +55,7 @@
 }  // namepace
 
 ForeignSessionHandler::ForeignSessionHandler() {
+  load_attempt_time_ = base::TimeTicks::Now();
 }
 
 // static
@@ -225,6 +228,12 @@
 
   base::ListValue session_list;
   if (open_tabs && open_tabs->GetAllForeignSessions(&sessions)) {
+    if (!load_attempt_time_.is_null()) {
+      UMA_HISTOGRAM_TIMES("Sync.SessionsRefreshDelay",
+                          base::TimeTicks::Now() - load_attempt_time_);
+      load_attempt_time_ = base::TimeTicks();
+    }
+
     // Sort sessions from most recent to least recent.
     std::sort(sessions.begin(), sessions.end(), SortSessionsByRecency);
 
diff --git a/chrome/browser/ui/webui/foreign_session_handler.h b/chrome/browser/ui/webui/foreign_session_handler.h
index 81e96ab9..c0d0195 100644
--- a/chrome/browser/ui/webui/foreign_session_handler.h
+++ b/chrome/browser/ui/webui/foreign_session_handler.h
@@ -94,6 +94,10 @@
   // The Registrar used to register ForeignSessionHandler for notifications.
   content::NotificationRegistrar registrar_;
 
+  // The time at which this WebUI was created. Used to calculate how long
+  // the WebUI was present before the sessions data was visible.
+  base::TimeTicks load_attempt_time_;
+
   DISALLOW_COPY_AND_ASSIGN(ForeignSessionHandler);
 };
 
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui.cc b/chrome/browser/ui/webui/media_router/media_router_ui.cc
index 2b2b54a8..5be8017 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.cc
@@ -195,7 +195,8 @@
   } else {
     // TODO(imcheng): Presentation ID should come from the response
     // as the IDs might not be the same.
-    presentation_session_request_->MaybeInvokeSuccessCallback();
+    presentation_session_request_->MaybeInvokeSuccessCallback(
+        route->media_route_id());
   }
 }
 
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 b1f5dc90..53055850 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -83,6 +83,7 @@
     "logRemoveUserWarningShown";
 
 const size_t kAvatarIconSize = 180;
+const int kMaxOAuthRetries = 3;
 
 void HandleAndDoNothing(const base::ListValue* args) {
 }
@@ -437,27 +438,44 @@
   }
 
   authenticating_profile_index_ = profile_index;
-  if (!LocalAuth::ValidateLocalAuthCredentials(profile_index, password)) {
-    // Make a second attempt via an on-line authentication call.  This handles
-    // profiles that are missing sign-in credentials and also cases where the
-    // password has been changed externally.
-    client_login_.reset(new GaiaAuthFetcher(
-        this,
-        GaiaConstants::kChromeSource,
-        web_ui()->GetWebContents()->GetBrowserContext()->GetRequestContext()));
-
-    client_login_->StartClientLogin(
-        base::UTF16ToUTF8(email_address),
-        password,
-        GaiaConstants::kSyncService,
-        std::string(),
-        std::string(),
-        GaiaAuthFetcher::HostedAccountsAllowed);
-    password_attempt_ = password;
+  if (LocalAuth::ValidateLocalAuthCredentials(profile_index, password)) {
+    ReportAuthenticationResult(true, ProfileMetrics::AUTH_LOCAL);
     return;
   }
 
-  ReportAuthenticationResult(true, ProfileMetrics::AUTH_LOCAL);
+  email_address_ = email_address;
+  password_attempt_ = password;
+
+  // This could be a mis-typed password or typing a new password while we
+  // still have a hash of the old one.  The new way of checking a password
+  // change makes use of a token so we do that... if it's available.
+  if (!oauth_client_) {
+    oauth_client_.reset(new gaia::GaiaOAuthClient(
+        web_ui()->GetWebContents()->GetBrowserContext()->GetRequestContext()));
+  }
+  std::string token = info_cache.GetPasswordChangeDetectionTokenAtIndex(
+      profile_index);
+  if (!token.empty()) {
+    oauth_client_->GetTokenHandleInfo(token, kMaxOAuthRetries, this);
+    return;
+  }
+
+  // In order to support the upgrade case where we have a local hash but no
+  // password token, we fall back on (deprecated) ClientLogin.  This will
+  // have to be removed in future versions as the service gets turned down
+  // but by then we'll have seamlessly updated the majority of users.
+  client_login_.reset(new GaiaAuthFetcher(
+      this,
+      GaiaConstants::kChromeSource,
+      web_ui()->GetWebContents()->GetBrowserContext()->GetRequestContext()));
+
+  client_login_->StartClientLogin(
+      base::UTF16ToUTF8(email_address),
+      password,
+      GaiaConstants::kSyncService,
+      std::string(),
+      std::string(),
+      GaiaAuthFetcher::HostedAccountsAllowed);
 }
 
 void UserManagerScreenHandler::HandleRemoveUser(const base::ListValue* args) {
@@ -553,8 +571,42 @@
   HideUserPodCustomIcon(email);
 }
 
+void UserManagerScreenHandler::OnGetTokenInfoResponse(
+    scoped_ptr<base::DictionaryValue> token_info) {
+  // Password is unchanged so user just mistyped it.  Ask again.
+  ReportAuthenticationResult(false, ProfileMetrics::AUTH_FAILED);
+}
+
+void UserManagerScreenHandler::OnOAuthError() {
+  // Password has changed.  Go through online signin flow.
+  // ... if we had it.  Until then, use deprecated ClientLogin to validate
+  // the password.  This will have to be changed soon.  (TODO: bcwhite)
+    oauth_client_.reset();
+    client_login_.reset(new GaiaAuthFetcher(
+      this,
+      GaiaConstants::kChromeSource,
+      web_ui()->GetWebContents()->GetBrowserContext()->GetRequestContext()));
+
+  DCHECK(!email_address_.empty());
+  DCHECK(!password_attempt_.empty());
+  client_login_->StartClientLogin(
+      base::UTF16ToUTF8(email_address_),
+      password_attempt_,
+      GaiaConstants::kSyncService,
+      std::string(),
+      std::string(),
+      GaiaAuthFetcher::HostedAccountsAllowed);
+}
+
+void UserManagerScreenHandler::OnNetworkError(int response_code) {
+  // Inconclusive but can't do real signin without being online anyway.
+    oauth_client_.reset();
+    ReportAuthenticationResult(false, ProfileMetrics::AUTH_FAILED_OFFLINE);
+}
+
 void UserManagerScreenHandler::OnClientLoginSuccess(
     const ClientLoginResult& result) {
+  oauth_client_.reset();
   LocalAuth::SetLocalAuthCredentials(authenticating_profile_index_,
                                      password_attempt_);
   ReportAuthenticationResult(true, ProfileMetrics::AUTH_ONLINE);
@@ -787,6 +839,7 @@
     bool success,
     ProfileMetrics::ProfileAuth auth) {
   ProfileMetrics::LogProfileAuthResult(auth);
+  email_address_.clear();
   password_attempt_.clear();
 
   if (success) {
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.h b/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
index 98bf442..f1f3044 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
@@ -20,6 +20,7 @@
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/web_ui_message_handler.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
+#include "google_apis/gaia/gaia_oauth_client.h"
 
 class GaiaAuthFetcher;
 
@@ -33,6 +34,7 @@
     : public content::WebUIMessageHandler,
       public proximity_auth::ScreenlockBridge::LockHandler,
       public GaiaAuthConsumer,
+      public gaia::GaiaOAuthClient::Delegate,
       public content::NotificationObserver {
  public:
   UserManagerScreenHandler();
@@ -82,6 +84,11 @@
   void HandleHardlockUserPod(const base::ListValue* args);
 
   // Handle GAIA auth results.
+  void OnGetTokenInfoResponse(
+      scoped_ptr<base::DictionaryValue> token_info) override;
+  void OnOAuthError() override;
+  void OnNetworkError(int response_code) override;
+  // ClientLogin is deprecated
   void OnClientLoginSuccess(const ClientLoginResult& result) override;
   void OnClientLoginFailure(const GoogleServiceAuthError& error) override;
 
@@ -107,12 +114,14 @@
   chrome::HostDesktopType desktop_type_;
 
   // Authenticator used when local-auth fails.
+  scoped_ptr<gaia::GaiaOAuthClient> oauth_client_;
   scoped_ptr<GaiaAuthFetcher> client_login_;
 
   // The index of the profile currently being authenticated.
   size_t authenticating_profile_index_;
 
-  // Login password, held during on-line auth for saving later if correct.
+  // Login email and password, held during on-line auth for later use.
+  base::string16 email_address_;
   std::string password_attempt_;
 
   // URL hash, used to key post-profile actions if present.
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index a7edc93..b14d7c0 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -592,6 +592,14 @@
       'browser/memory_details_linux.cc',
       'browser/memory_details_mac.cc',
       'browser/memory_details_win.cc',
+      'browser/memory/low_memory_observer_chromeos.cc',
+      'browser/memory/low_memory_observer_chromeos.h',
+      'browser/memory/oom_memory_details_chromeos.cc',
+      'browser/memory/oom_memory_details.h',
+      'browser/memory/oom_priority_manager_chromeos.cc',
+      'browser/memory/oom_priority_manager.h',
+      'browser/memory/system_memory_stats_recorder_chromeos.cc',
+      'browser/memory/system_memory_stats_recorder.h',
       'browser/mod_pagespeed/mod_pagespeed_metrics.cc',
       'browser/mod_pagespeed/mod_pagespeed_metrics.h',
       'browser/native_window_notification_source.h',
@@ -1277,26 +1285,14 @@
       'browser/autocomplete/chrome_autocomplete_provider_client.h',
       'browser/autocomplete/chrome_autocomplete_scheme_classifier.cc',
       'browser/autocomplete/chrome_autocomplete_scheme_classifier.h',
-      'browser/autocomplete/history_quick_provider.cc',
-      'browser/autocomplete/history_quick_provider.h',
-      'browser/autocomplete/history_url_provider.cc',
-      'browser/autocomplete/history_url_provider.h',
-      'browser/autocomplete/in_memory_url_index.cc',
-      'browser/autocomplete/in_memory_url_index.h',
       'browser/autocomplete/in_memory_url_index_factory.cc',
       'browser/autocomplete/in_memory_url_index_factory.h',
-      'browser/autocomplete/scored_history_match.cc',
-      'browser/autocomplete/scored_history_match.h',
-      'browser/autocomplete/shortcuts_backend.cc',
-      'browser/autocomplete/shortcuts_backend.h',
       'browser/autocomplete/shortcuts_backend_factory.cc',
       'browser/autocomplete/shortcuts_backend_factory.h',
-      'browser/autocomplete/shortcuts_database.cc',
-      'browser/autocomplete/shortcuts_database.h',
+      'browser/autocomplete/shortcuts_extensions_manager.cc',
+      'browser/autocomplete/shortcuts_extensions_manager.h',
       'browser/autocomplete/shortcuts_provider.cc',
       'browser/autocomplete/shortcuts_provider.h',
-      'browser/autocomplete/url_index_private_data.cc',
-      'browser/autocomplete/url_index_private_data.h',
       'browser/autocomplete/zero_suggest_provider.cc',
       'browser/autocomplete/zero_suggest_provider.h',
     ],
@@ -2458,6 +2454,8 @@
       'browser/safe_browsing/client_side_detection_host.h',
       'browser/safe_browsing/client_side_detection_service.cc',
       'browser/safe_browsing/client_side_detection_service.h',
+      'browser/safe_browsing/client_side_model_loader.cc',
+      'browser/safe_browsing/client_side_model_loader.h',
       'browser/safe_browsing/database_manager.h',
       'browser/safe_browsing/download_feedback.cc',
       'browser/safe_browsing/download_feedback.h',
@@ -3085,7 +3083,6 @@
         'common',
         'common_net',
         'encrypted_cert_logger_proto',
-        'in_memory_url_index_cache_proto',
         'offline_pages_proto',
         'probe_message_proto',
         '../components/components.gyp:autofill_core_browser',
@@ -3238,6 +3235,7 @@
             '../content/app/resources/content_resources.gyp:content_resources',
             '../media/media.gyp:media',
             '../media/mojo/interfaces/mojo_bindings.gyp:platform_verification_api',
+            '../mojo/mojo_base.gyp:mojo_application_base',
             '../mojo/mojo_base.gyp:mojo_common_lib',
             '../mojo/mojo_base.gyp:mojo_environment_chromium',
             '../net/net.gyp:net_extras',
@@ -3602,7 +3600,8 @@
           'dependencies': [
             '../components/components.gyp:feedback_component',
             '../device/core/core.gyp:device_core',
-            '../device/devices_app/devices_app.gyp:devices_app_lib',
+            '../device/devices_app/devices_app.gyp:devices_app_public_cpp',
+            '../device/devices_app/devices_app.gyp:devices_app_public_cpp_factory',
             '../device/usb/usb.gyp:device_usb',
             '../net/net.gyp:net_browser_services',
           ]
@@ -3806,19 +3805,6 @@
       ],
     },
     {
-      # Protobuf compiler / generator for the InMemoryURLIndex caching
-      # protocol buffer.
-      # GN version: //chrome/browser/autocomplete:in_memory_url_index_cache_proto
-      'target_name': 'in_memory_url_index_cache_proto',
-      'type': 'static_library',
-      'sources': [ 'browser/autocomplete/in_memory_url_index_cache.proto', ],
-      'variables': {
-        'proto_in_dir': 'browser/autocomplete',
-        'proto_out_dir': 'chrome/browser/autocomplete',
-      },
-      'includes': [ '../build/protoc.gypi', ],
-    },
-    {
       # Protobuf compiler / generator for the fraudulent certificate reporting
       # protocol buffer.
       # GN version: //chrome/browser/ssl:cert_logger_proto
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index 56faea3..45720b6 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -706,14 +706,6 @@
         'browser/chromeos/login/views/user_board_view.h',
         'browser/chromeos/login/wizard_controller.cc',
         'browser/chromeos/login/wizard_controller.h',
-        'browser/chromeos/memory/low_memory_observer.cc',
-        'browser/chromeos/memory/low_memory_observer.h',
-        'browser/chromeos/memory/oom_memory_details.cc',
-        'browser/chromeos/memory/oom_memory_details.h',
-        'browser/chromeos/memory/oom_priority_manager.cc',
-        'browser/chromeos/memory/oom_priority_manager.h',
-        'browser/chromeos/memory/system_memory_stats_recorder.cc',
-        'browser/chromeos/memory/system_memory_stats_recorder.h',
         'browser/chromeos/mobile/mobile_activator.cc',
         'browser/chromeos/mobile/mobile_activator.h',
         'browser/chromeos/mobile_config.cc',
@@ -762,6 +754,8 @@
         'browser/chromeos/ownership/owner_settings_service_chromeos_factory.h',
         'browser/chromeos/platform_keys/key_permissions.cc',
         'browser/chromeos/platform_keys/key_permissions.h',
+        'browser/chromeos/platform_keys/key_permissions_policy_handler.cc',
+        'browser/chromeos/platform_keys/key_permissions_policy_handler.h',
         'browser/chromeos/platform_keys/platform_keys.cc',
         'browser/chromeos/platform_keys/platform_keys.h',
         'browser/chromeos/platform_keys/platform_keys_nss.cc',
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index c473555e..eef79e2 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -138,8 +138,6 @@
       'browser/extensions/api/automation_internal/automation_event_router.h',
       'browser/extensions/api/automation_internal/automation_internal_api.cc',
       'browser/extensions/api/automation_internal/automation_internal_api.h',
-      'browser/extensions/api/automation_internal/automation_util.cc',
-      'browser/extensions/api/automation_internal/automation_util.h',
       'browser/extensions/api/autotest_private/autotest_private_api.cc',
       'browser/extensions/api/autotest_private/autotest_private_api.h',
       'browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index d3543546..6d9abf8 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -340,6 +340,7 @@
       'browser/media_galleries/fileapi/media_file_validator_browsertest.cc',
       'browser/media_galleries/media_galleries_dialog_controller_mock.cc',
       'browser/media_galleries/media_galleries_dialog_controller_mock.h',
+      'browser/memory/oom_priority_manager_browsertest_chromeos.cc',
       'browser/metrics/metrics_memory_details_browsertest.cc',
       'browser/metrics/metrics_service_browsertest.cc',
       'browser/net/cookie_policy_browsertest.cc',
@@ -747,7 +748,6 @@
       'browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.h',
       'browser/chromeos/login/webview_login_browsertest.cc',
       'browser/chromeos/login/wizard_controller_browsertest.cc',
-      'browser/chromeos/memory/oom_priority_manager_browsertest.cc',
       'browser/chromeos/net/network_portal_detector_impl_browsertest.cc',
       'browser/chromeos/ownership/fake_owner_settings_service.cc',
       'browser/chromeos/ownership/fake_owner_settings_service.h',
@@ -860,7 +860,7 @@
       'browser/ui/webui/media_router/media_router_dialog_controller_browsertest.cc',
     ],
     # Javascript sources. These are combined with the .cc files in the GYP build
-    # and are handled by a rule, but in the GN build theyr're in a separate
+    # and are handled by a rule, but in the GN build they're in a separate
     # action so need to be separated out.
     'chrome_browser_tests_webui_js_sources': [
       'browser/devtools/device/webrtc/devtools_bridge_client_browsertest.js',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 2cdfe5acf..98f7e6e 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -128,6 +128,7 @@
       'browser/manifest/manifest_icon_selector_unittest.cc',
       'browser/media/native_desktop_media_list_unittest.cc',
       'browser/media/midi_permission_context_unittest.cc',
+      'browser/memory/oom_priority_manager_chromeos_unittest.cc',
       'browser/metrics/chrome_metrics_service_accessor_unittest.cc',
       'browser/metrics/cloned_install_detector_unittest.cc',
       'browser/metrics/drive_metrics_provider_unittest.cc',
@@ -997,6 +998,7 @@
       'browser/safe_browsing/chunk_range_unittest.cc',
       'browser/safe_browsing/client_side_detection_host_unittest.cc',
       'browser/safe_browsing/client_side_detection_service_unittest.cc',
+      'browser/safe_browsing/client_side_model_loader_unittest.cc',
       'browser/safe_browsing/download_feedback_service_unittest.cc',
       'browser/safe_browsing/download_feedback_unittest.cc',
       'browser/safe_browsing/download_protection_service_unittest.cc',
@@ -1260,7 +1262,6 @@
       'browser/chromeos/login/users/multi_profile_user_controller_unittest.cc',
       'browser/chromeos/login/users/user_manager_unittest.cc',
       'browser/chromeos/login/users/wallpaper/wallpaper_manager_unittest.cc',
-      'browser/chromeos/memory/oom_priority_manager_unittest.cc',
       'browser/chromeos/mobile/mobile_activator_unittest.cc',
       'browser/chromeos/mobile_config_unittest.cc',
       'browser/chromeos/net/cert_verify_proc_chromeos_unittest.cc',
diff --git a/chrome/chrome_watcher/chrome_watcher_main.cc b/chrome/chrome_watcher/chrome_watcher_main.cc
index a7fa166..8a54796 100644
--- a/chrome/chrome_watcher/chrome_watcher_main.cc
+++ b/chrome/chrome_watcher/chrome_watcher_main.cc
@@ -382,10 +382,12 @@
           .value()
           .c_str(),
       &OnCrashReportUpload, nullptr);
+#ifdef KASKO_HANG_REPORTS
   if (launched_kasko &&
       base::StringPiece16(channel_name) == installer::kChromeChannelCanary) {
     on_hung_callback = base::Bind(&DumpHungBrowserProcess, channel_name);
   }
+#endif  // KASKO_HANG_REPORTS
 #endif  // KASKO
 
   // Run a UI message loop on the main thread.
diff --git a/chrome/common/extensions/api/automation.idl b/chrome/common/extensions/api/automation.idl
index a0d8116..b49c3a5e 100644
--- a/chrome/common/extensions/api/automation.idl
+++ b/chrome/common/extensions/api/automation.idl
@@ -114,6 +114,7 @@
     locationBar,
     log,
     main,
+    mark,
     marquee,
     math,
     menuBar,
@@ -317,8 +318,6 @@
     // The role of this node.
     automation.RoleType role;
 
-    // TODO(aboxhall): expose states as mixins instead
-
     // The $(ref:automation.StateType)s describing this node.
     object state;
 
@@ -369,18 +368,101 @@
     // name, via the $(ref:automation.AutomationNode.name) attribute.
     AutomationNode[] labelledBy;
 
-    // The nodes, if any, which are to be considered children of this node but
-    // are not children in the DOM tree.
-    AutomationNode[] owns;
+    // The node referred to by <code>aria-activedescendant</code>, where
+    // applicable
+    AutomationNode activedescendant;
 
-    // TODO(aboxhall): Make this private?
+    //
+    // Link attributes.
+    //
 
-    // A collection of this node's other attributes.
-    object? attributes;
+    // The URL that this link will navigate to.
+    DOMString url;
 
-    // The index of this node in its parent node's list of children. If this is
-    // the root node, this will be undefined.
-    long? indexInParent;
+    //
+    // Document attributes.
+    //
+
+    // The URL of this document.
+    DOMString docUrl;
+
+    // The title of this document.
+    DOMString docTitle;
+
+    // Whether this document has finished loading.
+    boolean docLoaded;
+
+    // The proportion (out of 1.0) that this doc has completed loading.
+    double docLoadingProgress;
+
+    //
+    // Scrollable container attributes.
+    //
+
+    long scrollX;
+    long scrollXMin;
+    long scrollXMax;
+    long scrollY;
+    long scrollYMin;
+    long scrollYMax;
+
+    //
+    // Editable text field attributes.
+    //
+
+    // The character index of the start of the selection within this editable
+    // text element; -1 if no selection.
+    long textSelStart;
+
+    // The character index of the end of the selection within this editable
+    // text element; -1 if no selection.
+    long textSelEnd;
+
+    // The input type, like email or number.
+    DOMString textInputType;
+
+    //
+    // Range attributes.
+    //
+
+    // The current value for this range.
+    double valueForRange;
+
+    // The minimum possible value for this range.
+    double minValueForRange;
+
+    // The maximum possible value for this range.
+    double maxValueForRange;
+
+    //
+    // Table attributes.
+    //
+
+    // The number of rows in this table.
+    long tableRowCount;
+
+    // The number of columns in this table.
+    long tableColumnCount;
+
+    //
+    // Table cell attributes.
+    //
+
+    // The zero-based index of the column that this cell is in.
+    long tableCellColumnIndex;
+
+    // The number of columns that this cell spans (default is 1).
+    long tableCellColumnSpan;
+
+    // The zero-based index of the row that this cell is in.
+    long tableCellRowIndex;
+
+    // The number of rows that this cell spans (default is 1).
+    long tableCellRowSpan;
+
+    //
+    // Walking the tree.
+    //
 
     AutomationNode[] children;
     AutomationNode parent;
@@ -389,6 +471,14 @@
     AutomationNode previousSibling;
     AutomationNode nextSibling;
 
+    // The index of this node in its parent node's list of children. If this is
+    // the root node, this will be undefined.
+    long? indexInParent;
+
+    //
+    // Actions.
+    //
+
     // Does the default action based on this node's role. This is generally
     // the same action that would result from clicking the node such as
     // expanding a treeitem, toggling a checkbox, selecting a radiobutton,
@@ -404,9 +494,6 @@
     // Sets selection within a text field.
     static void setSelection(long startIndex, long endIndex);
 
-    // Shows the context menu resulting from a right click on this node.
-    static void showContextMenu();
-
     // Adds a listener for the given event type and event phase.
     static void addEventListener(
         EventType eventType, AutomationListener listener, boolean capture);
@@ -443,100 +530,6 @@
     static boolean matches(FindParams params);
   };
 
-  dictionary ActiveDescendantMixin {
-    // The node referred to by <code>aria-activedescendant</code>, where
-    // applicable
-    AutomationNode activedescendant;
-  };
-
-  // Attributes which are mixed in to an AutomationNode if it is a link.
-  dictionary LinkMixins {
-    // TODO(aboxhall): Add visited state
-
-    // The URL that this link will navigate to.
-    DOMString url;
-  };
-
-  // Attributes which are mixed in to an AutomationNode if it is a document.
-  dictionary DocumentMixins {
-    // The URL of this document.
-    DOMString docUrl;
-
-    // The title of this document.
-    DOMString docTitle;
-
-    // Whether this document has finished loading.
-    boolean docLoaded;
-
-    // The proportion (out of 1.0) that this doc has completed loading.
-    double docLoadingProgress;
-  };
-
-  // TODO(aboxhall): document ScrollableMixins (e.g. what is scrollXMin? is it
-  // ever not 0?)
-
-  // Attributes which are mixed in to an AutomationNode if it is scrollable.
-  dictionary ScrollableMixins {
-    long scrollX;
-    long scrollXMin;
-    long scrollXMax;
-    long scrollY;
-    long scrollYMin;
-    long scrollYMax;
-  };
-
-  // Attributes which are mixed in to an AutomationNode if it is editable text.
-  dictionary EditableTextMixins {
-    // The character index of the start of the selection within this editable
-    // text element; -1 if no selection.
-    long textSelStart;
-
-    // The character index of the end of the selection within this editable
-    // text element; -1 if no selection.
-    long textSelEnd;
-
-    // The input type, like email or number.
-    DOMString textInputType;
-  };
-
-  // Attributes which are mixed in to an AutomationNode if it is a range.
-  dictionary RangeMixins {
-    // The current value for this range.
-    double valueForRange;
-
-    // The minimum possible value for this range.
-    double minValueForRange;
-
-    // The maximum possible value for this range.
-    double maxValueForRange;
-  };
-
-  // TODO(aboxhall): live region mixins.
-
-  // Attributes which are mixed in to an AutomationNode if it is a table.
-  dictionary TableMixins {
-    // The number of rows in this table.
-    long tableRowCount;
-
-    // The number of columns in this table.
-    long tableColumnCount;
-  };
-
-  // Attributes which are mixed in to an AutomationNode if it is a table cell.
-  dictionary TableCellMixins {
-    // The zero-based index of the column that this cell is in.
-    long tableCellColumnIndex;
-
-    // The number of columns that this cell spans (default is 1).
-    long tableCellColumnSpan;
-
-    // The zero-based index of the row that this cell is in.
-    long tableCellRowIndex;
-
-    // The number of rows that this cell spans (default is 1).
-    long tableCellRowSpan;
-  };
-
   // Called when the <code>AutomationNode</code> for the page is available.
   callback RootCallback = void(AutomationNode rootNode);
 
diff --git a/chrome/common/extensions/api/automation_internal.idl b/chrome/common/extensions/api/automation_internal.idl
index b290c63..6f568cfa 100644
--- a/chrome/common/extensions/api/automation_internal.idl
+++ b/chrome/common/extensions/api/automation_internal.idl
@@ -6,46 +6,10 @@
 // essentially a translation of the internal accessibility tree update system
 // into an extension API.
 namespace automationInternal {
-  dictionary Rect {
-    long left;
-    long top;
-    long width;
-    long height;
-  };
-
-  // A compact representation of the accessibility information for a
-  // single web object, in a form that can be serialized and sent from
-  // one process to another.
-  // See ui/accessibility/ax_node_data.h
-  dictionary AXNodeData {
-    long id;
-    DOMString role;
-    object state;
-    Rect location;
-
-    object? boolAttributes;
-    object? floatAttributes;
-    object? htmlAttributes;
-    object? intAttributes;
-    object? intlistAttributes;
-    object? stringAttributes;
-    long[] childIds;
-  };
-
-  dictionary AXTreeUpdate {
-    // ID of the node, if any, which should be invalidated before the new data
-    // is applied.
-    long nodeIdToClear;
-
-    // A vector of nodes to update according to the rules described in
-    // ui/accessibility/ax_tree_update.h.
-    AXNodeData[] nodes;
-  };
-
   // Data for an accessibility event and/or an atomic change to an accessibility
   // tree. See ui/accessibility/ax_tree_update.h for an extended explanation of
   // the tree update format.
-  dictionary AXEventParams {
+  [nocompile] dictionary AXEventParams {
     // The tree id of the web contents that this update is for.
     long treeID;
 
@@ -54,10 +18,6 @@
 
     // The type of event that this update represents.
     DOMString eventType;
-
-    // Serialized changes to the tree structure and node data that should be
-    // applied before processing the event.
-    AXTreeUpdate update;
   };
 
   // All possible actions that can be performed on automation nodes.
@@ -133,5 +93,7 @@
     static void onAccessibilityEvent(AXEventParams update);
 
     static void onAccessibilityTreeDestroyed(long treeID);
+
+    static void onTreeChange(long treeID, long nodeID, DOMString changeType);
   };
 };
diff --git a/chrome/common/extensions/docs/server2/url_fetcher.py b/chrome/common/extensions/docs/server2/url_fetcher.py
index 9daa5fa..0609d9d 100644
--- a/chrome/common/extensions/docs/server2/url_fetcher.py
+++ b/chrome/common/extensions/docs/server2/url_fetcher.py
@@ -10,7 +10,7 @@
 from urlparse import urlparse, parse_qs
 
 def _MakeHeaders(headers={}):
-  headers['User-Agent'] = 'Chomium docserver %s' % GetAppVersion()
+  headers['User-Agent'] = 'Chromium-Docserver/%s' % GetAppVersion()
   headers['Cache-Control'] = 'max-age=0'
   return headers
 
diff --git a/chrome/common/mac/app_shim_launch.h b/chrome/common/mac/app_shim_launch.h
index cde3909..9ea6502 100644
--- a/chrome/common/mac/app_shim_launch.h
+++ b/chrome/common/mac/app_shim_launch.h
@@ -26,6 +26,8 @@
   APP_SHIM_LAUNCH_PROFILE_NOT_FOUND,
   // The app was not found.
   APP_SHIM_LAUNCH_APP_NOT_FOUND,
+  // The profile was locked.
+  APP_SHIM_LAUNCH_PROFILE_LOCKED,
   // Counter and end marker.
   APP_SHIM_LAUNCH_NUM_RESULTS
 };
diff --git a/chrome/common/media_galleries/OWNERS b/chrome/common/media_galleries/OWNERS
index e250c3d..857971d 100644
--- a/chrome/common/media_galleries/OWNERS
+++ b/chrome/common/media_galleries/OWNERS
@@ -1,4 +1,3 @@
-estade@chromium.org
-orenb@chromium.org
+reillyg@chromium.org
 thestig@chromium.org
 tommycli@chromium.org
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 67b69e7e..7651516 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -807,6 +807,10 @@
 // unconditionally maximized, overriding the heuristic that normally chooses the
 // window size.
 const char kForceMaximizeOnFirstRun[] = "ui.force_maximize_on_first_run";
+
+// A dictionary pref mapping public keys that identify platform keys to its
+// properties like whether it's meant for corporate usage.
+const char kPlatformKeys[] = "platform_keys";
 #endif  // defined(OS_CHROMEOS)
 
 // A boolean pref set to true if a Home button to open the Home pages should be
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index b657090..3451d08e 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -272,6 +272,7 @@
 extern const char kWakeOnWifiSsid[];
 extern const char kCaptivePortalAuthenticationIgnoresProxy[];
 extern const char kForceMaximizeOnFirstRun[];
+extern const char kPlatformKeys[];
 #endif  // defined(OS_CHROMEOS)
 extern const char kShowHomeButton[];
 extern const char kRecentlySelectedEncoding[];
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index 41eff816..416c3c9 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -80,8 +80,9 @@
 IPC_ENUM_TRAITS_MAX_VALUE(OmniboxFocusState, OMNIBOX_FOCUS_STATE_LAST)
 IPC_ENUM_TRAITS_MAX_VALUE(search_provider::OSDDType,
                           search_provider::OSDD_TYPE_LAST)
-IPC_ENUM_TRAITS_MAX_VALUE(search_provider::InstallState,
-                          search_provider::INSTALLED_STATE_LAST)
+IPC_ENUM_TRAITS_MIN_MAX_VALUE(search_provider::InstallState,
+                              search_provider::DENIED,
+                              search_provider::INSTALLED_STATE_LAST)
 IPC_ENUM_TRAITS_MAX_VALUE(ThemeBackgroundImageAlignment,
                           THEME_BKGRND_IMAGE_ALIGN_LAST)
 IPC_ENUM_TRAITS_MAX_VALUE(ThemeBackgroundImageTiling, THEME_BKGRND_IMAGE_LAST)
diff --git a/chrome/renderer/autofill/page_click_tracker_browsertest.cc b/chrome/renderer/autofill/page_click_tracker_browsertest.cc
index d868870..a6a5a2b 100644
--- a/chrome/renderer/autofill/page_click_tracker_browsertest.cc
+++ b/chrome/renderer/autofill/page_click_tracker_browsertest.cc
@@ -114,6 +114,16 @@
   EXPECT_FALSE(test_listener_.form_control_element_clicked_called_);
 }
 
+// Tests that PageClickTracker does not notify when there is right click.
+TEST_F(PageClickTrackerTest, PageClickTrackerInputRightClicked) {
+  EXPECT_NE(text_, text_.document().focusedElement());
+  // Right click the text field once.
+  EXPECT_TRUE(SimulateElementRightClick("text_1"));
+  EXPECT_FALSE(test_listener_.form_control_element_clicked_called_);
+  EXPECT_FALSE(test_listener_.was_focused_);
+  EXPECT_NE(text_, test_listener_.form_control_element_clicked_);
+}
+
 TEST_F(PageClickTrackerTest, PageClickTrackerInputFocusedAndClicked) {
   // Focus the text field without a click.
   ExecuteJavaScript("document.getElementById('text_1').focus();");
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index 540604d..e016871 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -183,6 +183,15 @@
     "  <INPUT type='submit' value='Login'/>"
     "</FORM>";
 
+const char kPasswordChangeFormHTML[] =
+    "<FORM name='ChangeWithUsernameForm' action='http://www.bidule.com'>"
+    "  <INPUT type='text' id='username'/>"
+    "  <INPUT type='password' id='password'/>"
+    "  <INPUT type='password' id='newpassword'/>"
+    "  <INPUT type='password' id='confirmpassword'/>"
+    "  <INPUT type='submit' value='Login'/>"
+    "</FORM>";
+
 // Sets the "readonly" attribute of |element| to the value given by |read_only|.
 void SetElementReadOnly(WebInputElement& element, bool read_only) {
   element.setAttribute(WebString::fromUTF8("readonly"),
@@ -1913,4 +1922,111 @@
   ExpectPasswordGenerationAvailable(password_generation_, true);
 }
 
+// Tests that a password change form is properly filled with the username and
+// password.
+TEST_F(PasswordAutofillAgentTest, FillSuggestionPasswordChangeForms) {
+  LoadHTML(kPasswordChangeFormHTML);
+  UpdateOriginForHTML(kPasswordChangeFormHTML);
+  UpdateUsernameAndPasswordElements();
+  // Simulate the browser sending the login info, but set |wait_for_username|
+  // to prevent the form from being immediately filled.
+  fill_data_.wait_for_username = true;
+  fill_data_.is_possible_change_password_form = true;
+  SimulateOnFillPasswordForm(fill_data_);
+
+  // Neither field should have been autocompleted.
+  CheckTextFieldsDOMState(std::string(), false, std::string(), false);
+
+  EXPECT_TRUE(password_autofill_agent_->FillSuggestion(
+      username_element_, kAliceUsername, kAlicePassword));
+  CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
+}
+
+// Tests that a password change form is properly filled with the password when
+// the user click on the password field.
+TEST_F(PasswordAutofillAgentTest,
+       FillSuggestionPasswordChangeFormsOnlyPassword) {
+  LoadHTML(kPasswordChangeFormHTML);
+  UpdateOriginForHTML(kPasswordChangeFormHTML);
+  UpdateUsernameAndPasswordElements();
+  // Simulate the browser sending the login info, but set |wait_for_username|
+  // to prevent the form from being immediately filled.
+  fill_data_.wait_for_username = true;
+  fill_data_.is_possible_change_password_form = true;
+  SimulateOnFillPasswordForm(fill_data_);
+
+  // Neither field should have been autocompleted.
+  CheckTextFieldsDOMState(std::string(), false, std::string(), false);
+
+  EXPECT_TRUE(password_autofill_agent_->FillSuggestion(
+      password_element_, kAliceUsername, kAlicePassword));
+  CheckTextFieldsDOMState("", false, kAlicePassword, true);
+}
+
+// Tests that one user click on a username field is sufficient to bring up a
+// credential suggestion popup on a change password form.
+TEST_F(PasswordAutofillAgentTest,
+       SuggestionsOnUsernameFieldOfChangePasswordForm) {
+  LoadHTML(kPasswordChangeFormHTML);
+  UpdateOriginForHTML(kPasswordChangeFormHTML);
+  UpdateUsernameAndPasswordElements();
+
+  ClearUsernameAndPasswordFields();
+  fill_data_.wait_for_username = true;
+  fill_data_.is_possible_change_password_form = true;
+  SimulateOnFillPasswordForm(fill_data_);
+  // Simulate a user clicking on the username element. This should produce a
+  // message.
+  render_thread_->sink().ClearMessages();
+  static_cast<PageClickListener*>(autofill_agent_)
+      ->FormControlElementClicked(username_element_, true);
+  CheckSuggestions("", true);
+}
+
+// Tests that one user click on a password field is sufficient to bring up a
+// credential suggestion popup on a change password form.
+TEST_F(PasswordAutofillAgentTest,
+       SuggestionsOnPasswordFieldOfChangePasswordForm) {
+  LoadHTML(kPasswordChangeFormHTML);
+  UpdateOriginForHTML(kPasswordChangeFormHTML);
+  UpdateUsernameAndPasswordElements();
+
+  ClearUsernameAndPasswordFields();
+  fill_data_.wait_for_username = true;
+  fill_data_.is_possible_change_password_form = true;
+  SimulateOnFillPasswordForm(fill_data_);
+  // Simulate a user clicking on the password element. This should produce a
+  // message.
+  render_thread_->sink().ClearMessages();
+  static_cast<PageClickListener*>(autofill_agent_)
+      ->FormControlElementClicked(password_element_, true);
+  CheckSuggestions("", false);
+}
+
+// Tests that there are no autosuggestions from the password manager when the
+// user clicks on the password field of change password form after the user
+// typed in the username field.
+TEST_F(PasswordAutofillAgentTest,
+       NoSuggestionsOnPasswordFieldOfChangePasswordFormAfterUsernameTyping) {
+  LoadHTML(kPasswordChangeFormHTML);
+  UpdateOriginForHTML(kPasswordChangeFormHTML);
+  UpdateUsernameAndPasswordElements();
+
+  ClearUsernameAndPasswordFields();
+  fill_data_.wait_for_username = true;
+  fill_data_.is_possible_change_password_form = true;
+  SimulateOnFillPasswordForm(fill_data_);
+
+  // Clear the text fields to start fresh.
+  SimulateUsernameChange("temp");
+
+  // Simulate a user clicking on the password element. This should produce no
+  // message.
+  render_thread_->sink().ClearMessages();
+  static_cast<PageClickListener*>(autofill_agent_)
+      ->FormControlElementClicked(password_element_, false);
+  EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
+      AutofillHostMsg_ShowPasswordSuggestions::ID));
+}
+
 }  // namespace autofill
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.cc b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
index ea8cb57..132bbd3b 100644
--- a/chrome/renderer/extensions/automation_internal_custom_bindings.cc
+++ b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
@@ -9,7 +9,6 @@
 #include "base/values.h"
 #include "chrome/common/extensions/chrome_extension_messages.h"
 #include "chrome/common/extensions/manifest_handlers/automation.h"
-#include "content/public/child/v8_value_converter.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/render_view.h"
 #include "extensions/common/extension.h"
@@ -39,6 +38,9 @@
 
 namespace extensions {
 
+TreeCache::TreeCache() {}
+TreeCache::~TreeCache() {}
+
 class AutomationMessageFilter : public IPC::MessageFilter {
  public:
   explicit AutomationMessageFilter(AutomationInternalCustomBindings* owner)
@@ -46,6 +48,7 @@
         removed_(false) {
     DCHECK(owner);
     content::RenderThread::Get()->AddFilter(this);
+    task_runner_ = content::RenderThread::Get()->GetTaskRunner();
   }
 
   void Detach() {
@@ -55,10 +58,15 @@
 
   // IPC::MessageFilter
   bool OnMessageReceived(const IPC::Message& message) override {
-    if (owner_)
-      return owner_->OnMessageReceived(message);
-    else
-      return false;
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(
+            &AutomationMessageFilter::OnMessageReceivedOnRenderThread,
+            this, message));
+
+    // Always return false in case there are multiple
+    // AutomationInternalCustomBindings instances attached to the same thread.
+    return false;
   }
 
   void OnFilterRemoved() override {
@@ -66,6 +74,11 @@
   }
 
 private:
+  void OnMessageReceivedOnRenderThread(const IPC::Message& message) {
+    if (owner_)
+      owner_->OnMessageReceived(message);
+  }
+
   ~AutomationMessageFilter() override {
     Remove();
   }
@@ -79,6 +92,7 @@
 
   AutomationInternalCustomBindings* owner_;
   bool removed_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(AutomationMessageFilter);
 };
@@ -88,35 +102,46 @@
   // It's safe to use base::Unretained(this) here because these bindings
   // will only be called on a valid AutomationInternalCustomBindings instance
   // and none of the functions have any side effects.
-  RouteFunction(
-      "IsInteractPermitted",
-      base::Bind(&AutomationInternalCustomBindings::IsInteractPermitted,
-                 base::Unretained(this)));
-  RouteFunction(
-      "GetSchemaAdditions",
-      base::Bind(&AutomationInternalCustomBindings::GetSchemaAdditions,
-                 base::Unretained(this)));
-  RouteFunction(
-      "GetRoutingID",
-      base::Bind(&AutomationInternalCustomBindings::GetRoutingID,
-                 base::Unretained(this)));
+  #define ROUTE_FUNCTION(FN) \
+  RouteFunction(#FN, \
+                base::Bind(&AutomationInternalCustomBindings::FN, \
+                base::Unretained(this)))
 
-  message_filter_ = new AutomationMessageFilter(this);
+  ROUTE_FUNCTION(IsInteractPermitted);
+  ROUTE_FUNCTION(GetSchemaAdditions);
+  ROUTE_FUNCTION(GetRoutingID);
+  ROUTE_FUNCTION(StartCachingAccessibilityTrees);
+  ROUTE_FUNCTION(DestroyAccessibilityTree);
+  ROUTE_FUNCTION(GetRootID);
+  ROUTE_FUNCTION(GetParentID);
+  ROUTE_FUNCTION(GetChildCount);
+  ROUTE_FUNCTION(GetChildIDAtIndex);
+  ROUTE_FUNCTION(GetIndexInParent);
+  ROUTE_FUNCTION(GetState);
+  ROUTE_FUNCTION(GetRole);
+  ROUTE_FUNCTION(GetLocation);
+  ROUTE_FUNCTION(GetStringAttribute);
+  ROUTE_FUNCTION(GetBoolAttribute);
+  ROUTE_FUNCTION(GetIntAttribute);
+  ROUTE_FUNCTION(GetFloatAttribute);
+  ROUTE_FUNCTION(GetIntListAttribute);
+  ROUTE_FUNCTION(GetHtmlAttribute);
+
+  #undef ROUTE_FUNCTION
 }
 
 AutomationInternalCustomBindings::~AutomationInternalCustomBindings() {
-  message_filter_->Detach();
+  if (message_filter_)
+    message_filter_->Detach();
+  STLDeleteContainerPairSecondPointers(tree_id_to_tree_cache_map_.begin(),
+                                       tree_id_to_tree_cache_map_.end());
 }
 
-bool AutomationInternalCustomBindings::OnMessageReceived(
+void AutomationInternalCustomBindings::OnMessageReceived(
     const IPC::Message& message) {
   IPC_BEGIN_MESSAGE_MAP(AutomationInternalCustomBindings, message)
     IPC_MESSAGE_HANDLER(ExtensionMsg_AccessibilityEvent, OnAccessibilityEvent)
   IPC_END_MESSAGE_MAP()
-
-  // Always return false in case there are multiple
-  // AutomationInternalCustomBindings instances attached to the same thread.
-  return false;
 }
 
 void AutomationInternalCustomBindings::IsInteractPermitted(
@@ -135,6 +160,12 @@
   args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), routing_id));
 }
 
+void AutomationInternalCustomBindings::StartCachingAccessibilityTrees(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  if (!message_filter_)
+    message_filter_ = new AutomationMessageFilter(this);
+}
+
 void AutomationInternalCustomBindings::GetSchemaAdditions(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   v8::Local<v8::Object> additions = v8::Object::New(GetIsolate());
@@ -158,9 +189,438 @@
   args.GetReturnValue().Set(additions);
 }
 
+void AutomationInternalCustomBindings::DestroyAccessibilityTree(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  if (args.Length() != 1 || !args[0]->IsNumber()) {
+    ThrowInvalidArgumentsException(args);
+    return;
+  }
+
+  int tree_id = args[0]->Int32Value();
+  auto iter = tree_id_to_tree_cache_map_.find(tree_id);
+  if (iter == tree_id_to_tree_cache_map_.end())
+    return;
+
+  TreeCache* cache = iter->second;
+  tree_id_to_tree_cache_map_.erase(tree_id);
+  axtree_to_tree_cache_map_.erase(&cache->tree);
+  delete cache;
+}
+
+//
+// Access the cached accessibility trees and properties of their nodes.
+//
+
+void AutomationInternalCustomBindings::GetRootID(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  if (args.Length() != 1 || !args[0]->IsNumber()) {
+    ThrowInvalidArgumentsException(args);
+    return;
+  }
+
+  int tree_id = args[0]->Int32Value();
+  const auto iter = tree_id_to_tree_cache_map_.find(tree_id);
+  if (iter == tree_id_to_tree_cache_map_.end())
+    return;
+
+  TreeCache* cache = iter->second;
+  int root_id = cache->tree.root()->id();
+  args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), root_id));
+}
+
+void AutomationInternalCustomBindings::GetParentID(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ui::AXNode* node = nullptr;
+  if (!GetNodeHelper(args, nullptr, &node))
+    return;
+
+  if (!node->parent())
+    return;
+
+  int parent_id = node->parent()->id();
+  args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), parent_id));
+}
+
+void AutomationInternalCustomBindings::GetChildCount(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ui::AXNode* node = nullptr;
+  if (!GetNodeHelper(args, nullptr, &node))
+    return;
+
+  int child_count = node->child_count();
+  args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_count));
+}
+
+void AutomationInternalCustomBindings::GetChildIDAtIndex(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  if (args.Length() < 3 || !args[2]->IsNumber()) {
+    ThrowInvalidArgumentsException(args);
+    return;
+  }
+
+  ui::AXNode* node = nullptr;
+  if (!GetNodeHelper(args, nullptr, &node))
+    return;
+
+  int index = args[2]->Int32Value();
+  if (index < 0 || index >= node->child_count())
+    return;
+
+  int child_id = node->children()[index]->id();
+  args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_id));
+}
+
+void AutomationInternalCustomBindings::GetIndexInParent(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ui::AXNode* node = nullptr;
+  if (!GetNodeHelper(args, nullptr, &node))
+    return;
+
+  int index_in_parent = node->index_in_parent();
+  args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), index_in_parent));
+}
+
+void AutomationInternalCustomBindings::GetState(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ui::AXNode* node = nullptr;
+  if (!GetNodeHelper(args, nullptr, &node))
+    return;
+
+  v8::Local<v8::Object> state(v8::Object::New(GetIsolate()));
+  uint32 state_pos = 0, state_shifter = node->data().state;
+  while (state_shifter) {
+    if (state_shifter & 1) {
+      std::string key = ToString(static_cast<ui::AXState>(state_pos));
+      state->Set(CreateV8String(key),
+                 v8::Boolean::New(GetIsolate(), true));
+    }
+    state_shifter = state_shifter >> 1;
+    state_pos++;
+  }
+
+  args.GetReturnValue().Set(state);
+}
+
+void AutomationInternalCustomBindings::GetRole(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ui::AXNode* node = nullptr;
+  if (!GetNodeHelper(args, nullptr, &node))
+    return;
+
+  std::string role_name = ui::ToString(node->data().role);
+  args.GetReturnValue().Set(
+      v8::String::NewFromUtf8(GetIsolate(), role_name.c_str()));
+}
+
+void AutomationInternalCustomBindings::GetLocation(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  TreeCache* cache;
+  ui::AXNode* node = nullptr;
+  if (!GetNodeHelper(args, &cache, &node))
+    return;
+
+  v8::Local<v8::Object> location_obj(v8::Object::New(GetIsolate()));
+  gfx::Rect location = node->data().location;
+  location.Offset(cache->location_offset);
+  location_obj->Set(CreateV8String("left"),
+                    v8::Integer::New(GetIsolate(), location.x()));
+  location_obj->Set(CreateV8String("top"),
+                    v8::Integer::New(GetIsolate(), location.y()));
+  location_obj->Set(CreateV8String("width"),
+                    v8::Integer::New(GetIsolate(), location.width()));
+  location_obj->Set(CreateV8String("height"),
+                    v8::Integer::New(GetIsolate(), location.height()));
+  args.GetReturnValue().Set(location_obj);
+}
+
+void AutomationInternalCustomBindings::GetStringAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ui::AXNode* node = nullptr;
+  std::string attribute_name;
+  if (!GetAttributeHelper(args, &node, &attribute_name))
+    return;
+
+  ui::AXStringAttribute attribute = ui::ParseAXStringAttribute(attribute_name);
+  std::string attr_value;
+  if (!node->data().GetStringAttribute(attribute, &attr_value))
+    return;
+
+  args.GetReturnValue().Set(
+      v8::String::NewFromUtf8(GetIsolate(), attr_value.c_str()));
+}
+
+void AutomationInternalCustomBindings::GetBoolAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ui::AXNode* node = nullptr;
+  std::string attribute_name;
+  if (!GetAttributeHelper(args, &node, &attribute_name))
+    return;
+
+  ui::AXBoolAttribute attribute = ui::ParseAXBoolAttribute(attribute_name);
+  bool attr_value;
+  if (!node->data().GetBoolAttribute(attribute, &attr_value))
+    return;
+
+  args.GetReturnValue().Set(v8::Boolean::New(GetIsolate(), attr_value));
+}
+
+void AutomationInternalCustomBindings::GetIntAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ui::AXNode* node = nullptr;
+  std::string attribute_name;
+  if (!GetAttributeHelper(args, &node, &attribute_name))
+    return;
+
+  ui::AXIntAttribute attribute = ui::ParseAXIntAttribute(attribute_name);
+  int attr_value;
+  if (!node->data().GetIntAttribute(attribute, &attr_value))
+    return;
+
+  args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), attr_value));
+}
+
+void AutomationInternalCustomBindings::GetFloatAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ui::AXNode* node = nullptr;
+  std::string attribute_name;
+  if (!GetAttributeHelper(args, &node, &attribute_name))
+    return;
+
+  ui::AXFloatAttribute attribute = ui::ParseAXFloatAttribute(attribute_name);
+  float attr_value;
+
+  if (!node->data().GetFloatAttribute(attribute, &attr_value))
+    return;
+
+  args.GetReturnValue().Set(v8::Number::New(GetIsolate(), attr_value));
+}
+
+void AutomationInternalCustomBindings::GetIntListAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ui::AXNode* node = nullptr;
+  std::string attribute_name;
+  if (!GetAttributeHelper(args, &node, &attribute_name))
+    return;
+
+  ui::AXIntListAttribute attribute =
+      ui::ParseAXIntListAttribute(attribute_name);
+  if (!node->data().HasIntListAttribute(attribute))
+    return;
+  const std::vector<int32>& attr_value =
+      node->data().GetIntListAttribute(attribute);
+
+  v8::Local<v8::Array> result(v8::Array::New(GetIsolate(), attr_value.size()));
+  for (size_t i = 0; i < attr_value.size(); ++i)
+    result->Set(static_cast<uint32>(i),
+                v8::Integer::New(GetIsolate(), attr_value[i]));
+  args.GetReturnValue().Set(result);
+}
+
+void AutomationInternalCustomBindings::GetHtmlAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ui::AXNode* node = nullptr;
+  std::string attribute_name;
+  if (!GetAttributeHelper(args, &node, &attribute_name))
+    return;
+
+  std::string attr_value;
+  if (!node->data().GetHtmlAttribute(attribute_name.c_str(), &attr_value))
+    return;
+
+  args.GetReturnValue().Set(
+      v8::String::NewFromUtf8(GetIsolate(), attr_value.c_str()));
+}
+
+//
+// Helper functions.
+//
+
+void AutomationInternalCustomBindings::ThrowInvalidArgumentsException(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  GetIsolate()->ThrowException(
+      v8::String::NewFromUtf8(
+          GetIsolate(),
+          "Invalid arguments to AutomationInternalCustomBindings function",
+          v8::NewStringType::kNormal).ToLocalChecked());
+
+  LOG(FATAL)
+      << "Invalid arguments to AutomationInternalCustomBindings function"
+      << context()->GetStackTraceAsString();
+}
+
+bool AutomationInternalCustomBindings::GetNodeHelper(
+    const v8::FunctionCallbackInfo<v8::Value>& args,
+    TreeCache** out_cache,
+    ui::AXNode** out_node) {
+  if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsNumber()) {
+    ThrowInvalidArgumentsException(args);
+    return false;
+  }
+
+  int tree_id = args[0]->Int32Value();
+  int node_id = args[1]->Int32Value();
+
+  const auto iter = tree_id_to_tree_cache_map_.find(tree_id);
+  if (iter == tree_id_to_tree_cache_map_.end())
+    return false;
+
+  TreeCache* cache = iter->second;
+  ui::AXNode* node = cache->tree.GetFromId(node_id);
+
+  if (out_cache)
+    *out_cache = cache;
+  if (out_node)
+    *out_node = node;
+
+  return node != nullptr;
+}
+
+bool AutomationInternalCustomBindings::GetAttributeHelper(
+    const v8::FunctionCallbackInfo<v8::Value>& args,
+    ui::AXNode** out_node,
+    std::string* out_attribute_name) {
+  if (args.Length() != 3 ||
+      !args[2]->IsString()) {
+    ThrowInvalidArgumentsException(args);
+    return false;
+  }
+
+  TreeCache* cache = nullptr;
+  if (!GetNodeHelper(args, &cache, out_node))
+    return false;
+
+  *out_attribute_name = *v8::String::Utf8Value(args[2]);
+  return true;
+}
+
+v8::Local<v8::Value> AutomationInternalCustomBindings::CreateV8String(
+    const char* str) {
+  return v8::String::NewFromUtf8(
+      GetIsolate(), str, v8::String::kNormalString, strlen(str));
+}
+
+v8::Local<v8::Value> AutomationInternalCustomBindings::CreateV8String(
+    const std::string& str) {
+  return v8::String::NewFromUtf8(
+      GetIsolate(), str.c_str(), v8::String::kNormalString, str.length());
+}
+
+//
+// Handle accessibility events from the browser process.
+//
+
 void AutomationInternalCustomBindings::OnAccessibilityEvent(
     const ExtensionMsg_AccessibilityEventParams& params) {
-  // TODO(dmazzoni): finish implementing this.
+  int tree_id = params.tree_id;
+  TreeCache* cache;
+  auto iter = tree_id_to_tree_cache_map_.find(tree_id);
+  if (iter == tree_id_to_tree_cache_map_.end()) {
+    cache = new TreeCache();
+    cache->tab_id = -1;
+    cache->tree_id = params.tree_id;
+    cache->tree.SetDelegate(this);
+    tree_id_to_tree_cache_map_.insert(std::make_pair(tree_id, cache));
+    axtree_to_tree_cache_map_.insert(std::make_pair(&cache->tree, cache));
+  } else {
+    cache = iter->second;
+  }
+
+  cache->location_offset = params.location_offset;
+  if (!cache->tree.Unserialize(params.update)) {
+    LOG(FATAL) << cache->tree.error();
+    return;
+  }
+
+  v8::HandleScope handle_scope(GetIsolate());
+  v8::Context::Scope context_scope(context()->v8_context());
+  v8::Local<v8::Array> args(v8::Array::New(GetIsolate(), 1U));
+  v8::Local<v8::Object> event_params(v8::Object::New(GetIsolate()));
+  event_params->Set(CreateV8String("treeID"),
+                    v8::Integer::New(GetIsolate(), params.tree_id));
+  event_params->Set(CreateV8String("targetID"),
+                    v8::Integer::New(GetIsolate(), params.id));
+  event_params->Set(CreateV8String("eventType"),
+                    CreateV8String(ToString(params.event_type)));
+  args->Set(0U, event_params);
+  context()->DispatchEvent("automationInternal.onAccessibilityEvent", args);
+}
+
+void AutomationInternalCustomBindings::OnNodeWillBeDeleted(ui::AXTree* tree,
+                                                           ui::AXNode* node) {
+  SendTreeChangeEvent(
+      api::automation::TREE_CHANGE_TYPE_NODEREMOVED,
+      tree, node);
+}
+
+void AutomationInternalCustomBindings::OnSubtreeWillBeDeleted(
+    ui::AXTree* tree,
+    ui::AXNode* node) {
+  // This isn't strictly needed, as OnNodeWillBeDeleted will already be
+  // called. We could send a JS event for this only if it turns out to
+  // be needed for something.
+}
+
+void AutomationInternalCustomBindings::OnNodeCreated(ui::AXTree* tree,
+                                                     ui::AXNode* node) {
+  // Not needed, this is called in the middle of an update so it's not
+  // safe to trigger JS from here. Wait for the notification in
+  // OnAtomicUpdateFinished instead.
+}
+
+void AutomationInternalCustomBindings::OnNodeChanged(ui::AXTree* tree,
+                                                     ui::AXNode* node) {
+  // Not needed, this is called in the middle of an update so it's not
+  // safe to trigger JS from here. Wait for the notification in
+  // OnAtomicUpdateFinished instead.
+}
+
+void AutomationInternalCustomBindings::OnAtomicUpdateFinished(
+    ui::AXTree* tree,
+    bool root_changed,
+    const std::vector<ui::AXTreeDelegate::Change>& changes) {
+  auto iter = axtree_to_tree_cache_map_.find(tree);
+  if (iter == axtree_to_tree_cache_map_.end())
+    return;
+
+  for (auto change : changes) {
+    ui::AXNode* node = change.node;
+    switch (change.type) {
+      case NODE_CREATED:
+        SendTreeChangeEvent(
+            api::automation::TREE_CHANGE_TYPE_NODECREATED,
+            tree, node);
+        break;
+      case SUBTREE_CREATED:
+        SendTreeChangeEvent(
+            api::automation::TREE_CHANGE_TYPE_SUBTREECREATED,
+            tree, node);
+        break;
+      case NODE_CHANGED:
+        SendTreeChangeEvent(
+            api::automation::TREE_CHANGE_TYPE_NODECHANGED,
+            tree, node);
+        break;
+    }
+  }
+}
+
+void AutomationInternalCustomBindings::SendTreeChangeEvent(
+    api::automation::TreeChangeType change_type,
+    ui::AXTree* tree,
+    ui::AXNode* node) {
+  auto iter = axtree_to_tree_cache_map_.find(tree);
+  if (iter == axtree_to_tree_cache_map_.end())
+    return;
+
+  int tree_id = iter->second->tree_id;
+
+  v8::HandleScope handle_scope(GetIsolate());
+  v8::Context::Scope context_scope(context()->v8_context());
+  v8::Local<v8::Array> args(v8::Array::New(GetIsolate(), 3U));
+  args->Set(0U, v8::Integer::New(GetIsolate(), tree_id));
+  args->Set(1U, v8::Integer::New(GetIsolate(), node->id()));
+  args->Set(2U, CreateV8String(ToString(change_type)));
+  context()->DispatchEvent("automationInternal.onTreeChange", args);
 }
 
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.h b/chrome/renderer/extensions/automation_internal_custom_bindings.h
index 1bc8a795..c4f2cd2 100644
--- a/chrome/renderer/extensions/automation_internal_custom_bindings.h
+++ b/chrome/renderer/extensions/automation_internal_custom_bindings.h
@@ -6,6 +6,7 @@
 #define CHROME_RENDERER_EXTENSIONS_AUTOMATION_INTERNAL_CUSTOM_BINDINGS_H_
 
 #include "base/compiler_specific.h"
+#include "chrome/common/extensions/api/automation.h"
 #include "extensions/renderer/object_backed_native_handler.h"
 #include "ipc/ipc_message.h"
 #include "ui/accessibility/ax_tree.h"
@@ -17,15 +18,27 @@
 
 class AutomationMessageFilter;
 
+struct TreeCache {
+  TreeCache();
+  ~TreeCache();
+
+  int tab_id;
+  int tree_id;
+
+  gfx::Vector2d location_offset;
+  ui::AXTree tree;
+};
+
 // The native component of custom bindings for the chrome.automationInternal
 // API.
-class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
+class AutomationInternalCustomBindings : public ObjectBackedNativeHandler,
+                                         public ui::AXTreeDelegate {
  public:
   explicit AutomationInternalCustomBindings(ScriptContext* context);
 
   ~AutomationInternalCustomBindings() override;
 
-  bool OnMessageReceived(const IPC::Message& message);
+  void OnMessageReceived(const IPC::Message& message);
 
  private:
   // Returns whether this extension has the "interact" permission set (either
@@ -39,10 +52,120 @@
   // Get the routing ID for the extension.
   void GetRoutingID(const v8::FunctionCallbackInfo<v8::Value>& args);
 
+  // This is called by automation_internal_custom_bindings.js to indicate
+  // that an API was called that needs access to accessibility trees. This
+  // enables the MessageFilter that allows us to listen to accessibility
+  // events forwarded to this process.
+  void StartCachingAccessibilityTrees(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Called when an accessibility tree is destroyed and needs to be
+  // removed from our cache.
+  // Args: int ax_tree_id
+  void DestroyAccessibilityTree(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  //
+  // Access the cached accessibility trees and properties of their nodes.
+  //
+
+  // Args: int ax_tree_id, Returns: int root_node_id.
+  void GetRootID(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id, Returns: int parent_node_id.
+  void GetParentID(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id, Returns: int child_count.
+  void GetChildCount(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id, Returns: int child_id.
+  void GetChildIDAtIndex(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id, Returns: int index_in_parent.
+  void GetIndexInParent(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id
+  // Returns: JS object with a string key for each state flag that's set.
+  void GetState(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id, Returns: string role_name
+  void GetRole(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id
+  // Returns: JS object with {left, top, width, height}
+  void GetLocation(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id, string attribute_name
+  // Returns: string attribute_value.
+  void GetStringAttribute(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id, string attribute_name
+  // Returns: bool attribute_value.
+  void GetBoolAttribute(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id, string attribute_name
+  // Returns: int attribute_value.
+  void GetIntAttribute(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id, string attribute_name
+  // Returns: float attribute_value.
+  void GetFloatAttribute(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id, string attribute_name
+  // Returns: JS array of int attribute_values.
+  void GetIntListAttribute(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Args: int ax_tree_id, int node_id, string attribute_name
+  // Returns: string attribute_value.
+  void GetHtmlAttribute(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  //
+  // Helper functions.
+  //
+
+  // Throw an exception if we get bad arguments.
+  void ThrowInvalidArgumentsException(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // For any function that takes int ax_tree_id, int node_id as its first
+  // two arguments, returns the tree and node it corresponds to, or returns
+  // false if not found.
+  bool GetNodeHelper(
+      const v8::FunctionCallbackInfo<v8::Value>& args,
+      TreeCache** out_cache,
+      ui::AXNode** out_node);
+
+  // For any function that takes int ax_tree_id, int node_id, string attr
+  // as its first, returns the node it corresponds to and the string as
+  // a UTF8 string.
+  bool GetAttributeHelper(
+      const v8::FunctionCallbackInfo<v8::Value>& args,
+      ui::AXNode** out_node,
+      std::string* out_attribute_name);
+
+  // Create a V8 string from a string.
+  v8::Local<v8::Value> CreateV8String(const char* str);
+  v8::Local<v8::Value> CreateV8String(const std::string& str);
+
   // Handle accessibility events from the browser process.
   void OnAccessibilityEvent(
       const ExtensionMsg_AccessibilityEventParams& params);
 
+  // AXTreeDelegate implementation.
+  void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override;
+  void OnSubtreeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override;
+  void OnNodeCreated(ui::AXTree* tree, ui::AXNode* node) override;
+  void OnNodeChanged(ui::AXTree* tree, ui::AXNode* node) override;
+  void OnAtomicUpdateFinished(ui::AXTree* tree,
+                              bool root_changed,
+                              const std::vector<Change>& changes) override;
+
+  void SendTreeChangeEvent(api::automation::TreeChangeType change_type,
+                           ui::AXTree* tree,
+                           ui::AXNode* node);
+
+  base::hash_map<int, TreeCache*> tree_id_to_tree_cache_map_;
+  base::hash_map<ui::AXTree*, TreeCache*> axtree_to_tree_cache_map_;
   scoped_refptr<AutomationMessageFilter> message_filter_;
 
   DISALLOW_COPY_AND_ASSIGN(AutomationInternalCustomBindings);
diff --git a/chrome/renderer/resources/extensions/OWNERS b/chrome/renderer/resources/extensions/OWNERS
index b4e8133..83c0102 100644
--- a/chrome/renderer/resources/extensions/OWNERS
+++ b/chrome/renderer/resources/extensions/OWNERS
@@ -15,7 +15,8 @@
 per-file searchbox_api.js=dcblack@chromium.org
 per-file searchbox_api.js=jered@chromium.org
 per-file searchbox_api.js=samarth@chromium.org
-per-file media_galleries*.js=orenb@chromium.org
+per-file media_galleries*.js=thestig@chromium.org
+per-file media_galleries*.js=tommycli@chromium.org
 per-file enterprise_platform_keys*=pneubeck@chromium.org
 per-file notifications_*.js=dewittj@chromium.org
 per-file image_util.js=dewittj@chromium.org
diff --git a/chrome/renderer/resources/extensions/automation/automation_node.js b/chrome/renderer/resources/extensions/automation/automation_node.js
index 11ac8d8a3..e5de712 100644
--- a/chrome/renderer/resources/extensions/automation/automation_node.js
+++ b/chrome/renderer/resources/extensions/automation/automation_node.js
@@ -8,6 +8,124 @@
 var IsInteractPermitted =
     requireNative('automationInternal').IsInteractPermitted;
 
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?number} The id of the root node.
+ */
+var GetRootID = requireNative('automationInternal').GetRootID;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?number} The id of the node's parent, or undefined if it's the
+ *    root of its tree or if the tree or node wasn't found.
+ */
+var GetParentID = requireNative('automationInternal').GetParentID;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?number} The number of children of the node, or undefined if
+ *     the tree or node wasn't found.
+ */
+var GetChildCount = requireNative('automationInternal').GetChildCount;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {number} childIndex An index of a child of this node.
+ * @return {?number} The id of the child at the given index, or undefined
+ *     if the tree or node or child at that index wasn't found.
+ */
+var GetChildIDAtIndex = requireNative('automationInternal').GetChildIDAtIndex;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?number} The index of this node in its parent, or undefined if
+ *     the tree or node or node parent wasn't found.
+ */
+var GetIndexInParent = requireNative('automationInternal').GetIndexInParent;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?Object} An object with a string key for every state flag set,
+ *     or undefined if the tree or node or node parent wasn't found.
+ */
+var GetState = requireNative('automationInternal').GetState;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {string} The role of the node, or undefined if the tree or
+ *     node wasn't found.
+ */
+var GetRole = requireNative('automationInternal').GetRole;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?automation.Rect} The location of the node, or undefined if
+ *     the tree or node wasn't found.
+ */
+var GetLocation = requireNative('automationInternal').GetLocation;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of a string attribute.
+ * @return {?string} The value of this attribute, or undefined if the tree,
+ *     node, or attribute wasn't found.
+ */
+var GetStringAttribute = requireNative('automationInternal').GetStringAttribute;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an attribute.
+ * @return {?boolean} The value of this attribute, or undefined if the tree,
+ *     node, or attribute wasn't found.
+ */
+var GetBoolAttribute = requireNative('automationInternal').GetBoolAttribute;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an attribute.
+ * @return {?number} The value of this attribute, or undefined if the tree,
+ *     node, or attribute wasn't found.
+ */
+var GetIntAttribute = requireNative('automationInternal').GetIntAttribute;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an attribute.
+ * @return {?number} The value of this attribute, or undefined if the tree,
+ *     node, or attribute wasn't found.
+ */
+var GetFloatAttribute = requireNative('automationInternal').GetFloatAttribute;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an attribute.
+ * @return {?Array.<number>} The value of this attribute, or undefined
+ *     if the tree, node, or attribute wasn't found.
+ */
+var GetIntListAttribute =
+    requireNative('automationInternal').GetIntListAttribute;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an HTML attribute.
+ * @return {?string} The value of this attribute, or undefined if the tree,
+ *     node, or attribute wasn't found.
+ */
+var GetHtmlAttribute = requireNative('automationInternal').GetHtmlAttribute;
+
 var lastError = require('lastError');
 var logging = requireNative('logging');
 var schema = requireNative('automationInternal').GetSchemaAdditions();
@@ -20,16 +138,12 @@
  */
 function AutomationNodeImpl(root) {
   this.rootImpl = root;
-  this.childIds = [];
   // Public attributes. No actual data gets set on this object.
-  this.attributes = {};
-  // Internal object holding all attributes.
-  this.attributesInternal = {};
   this.listeners = {};
-  this.location = { left: 0, top: 0, width: 0, height: 0 };
 }
 
 AutomationNodeImpl.prototype = {
+  treeID: -1,
   id: -1,
   role: '',
   state: { busy: true },
@@ -40,16 +154,51 @@
   },
 
   get parent() {
-    return this.hostTree || this.rootImpl.get(this.parentID);
+    if (this.hostNode_)
+      return this.hostNode_;
+    var parentID = GetParentID(this.treeID, this.id);
+    return this.rootImpl.get(parentID);
+  },
+
+  get state() {
+    return GetState(this.treeID, this.id);
+  },
+
+  get role() {
+    return GetRole(this.treeID, this.id);
+  },
+
+  get location() {
+    return GetLocation(this.treeID, this.id);
+  },
+
+  get indexInParent() {
+    return GetIndexInParent(this.treeID, this.id);
+  },
+
+  get childTree() {
+    var childTreeID = GetIntAttribute(this.treeID, this.id, 'childTreeId');
+    if (childTreeID)
+      return AutomationRootNodeImpl.get(childTreeID);
   },
 
   get firstChild() {
-    return this.childTree || this.rootImpl.get(this.childIds[0]);
+    if (this.childTree)
+      return this.childTree;
+    if (!GetChildCount(this.treeID, this.id))
+      return undefined;
+    var firstChildID = GetChildIDAtIndex(this.treeID, this.id, 0);
+    return this.rootImpl.get(firstChildID);
   },
 
   get lastChild() {
-    var childIds = this.childIds;
-    return this.childTree || this.rootImpl.get(childIds[childIds.length - 1]);
+    if (this.childTree)
+      return this.childTree;
+    var count = GetChildCount(this.treeID, this.id);
+    if (!count)
+      return undefined;
+    var lastChildID = GetChildIDAtIndex(this.treeID, this.id, count - 1);
+    return this.rootImpl.get(lastChildID);
   },
 
   get children() {
@@ -57,24 +206,28 @@
       return [this.childTree];
 
     var children = [];
-    for (var i = 0, childID; childID = this.childIds[i]; i++) {
-      logging.CHECK(this.rootImpl.get(childID));
-      children.push(this.rootImpl.get(childID));
+    var count = GetChildCount(this.treeID, this.id);
+    for (var i = 0; i < count; ++i) {
+      var childID = GetChildIDAtIndex(this.treeID, this.id, i);
+      var child = this.rootImpl.get(childID);
+      children.push(child);
     }
     return children;
   },
 
   get previousSibling() {
     var parent = this.parent;
-    if (parent && this.indexInParent > 0)
-      return parent.children[this.indexInParent - 1];
+    var indexInParent = GetIndexInParent(this.treeID, this.id);
+    if (parent && indexInParent > 0)
+      return parent.children[indexInParent - 1];
     return undefined;
   },
 
   get nextSibling() {
     var parent = this.parent;
-    if (parent && this.indexInParent < parent.children.length)
-      return parent.children[this.indexInParent + 1];
+    var indexInParent = GetIndexInParent(this.treeID, this.id);
+    if (parent && indexInParent < parent.children.length)
+      return parent.children[indexInParent + 1];
     return undefined;
   },
 
@@ -170,12 +323,20 @@
     var impl = privates(this).impl;
     if (!impl)
       impl = this;
+
+    var parentID = GetParentID(this.treeID, this.id);
+    var count = GetChildCount(this.treeID, this.id);
+    var childIDs = [];
+    for (var i = 0; i < count; ++i) {
+      var childID = GetChildIDAtIndex(this.treeID, this.id, i);
+      childIDs.push(childID);
+    }
+
     return 'node id=' + impl.id +
         ' role=' + this.role +
         ' state=' + $JSON.stringify(this.state) +
-        ' parentID=' + impl.parentID +
-        ' childIds=' + $JSON.stringify(impl.childIds) +
-        ' attributes=' + $JSON.stringify(this.attributes);
+        ' parentID=' + parentID +
+        ' childIds=' + $JSON.stringify(childIDs);
   },
 
   dispatchEventAtCapturing_: function(event, path) {
@@ -219,9 +380,9 @@
       try {
         listeners[i].callback(event);
       } catch (e) {
-        console.error('Error in event handler for ' + event.type +
-                      'during phase ' + eventPhase + ': ' +
-                      e.message + '\nStack trace: ' + e.stack);
+        logging.WARNING('Error in event handler for ' + event.type +
+                        ' during phase ' + eventPhase + ': ' +
+                        e.message + '\nStack trace: ' + e.stack);
       }
     }
   },
@@ -312,17 +473,14 @@
     }
     if ('attributes' in params) {
       for (var attribute in params.attributes) {
-        if (!(attribute in this.attributesInternal))
-          return false;
-
         var attrValue = params.attributes[attribute];
         if (typeof attrValue != 'object') {
-          if (this.attributesInternal[attribute] !== attrValue)
+          if (this[attribute] !== attrValue)
             return false;
         } else if (attrValue instanceof RegExp) {
-          if (typeof this.attributesInternal[attribute] != 'string')
+          if (typeof this[attribute] != 'string')
             return false;
-          if (!attrValue.test(this.attributesInternal[attribute]))
+          if (!attrValue.test(this[attribute]))
             return false;
         } else {
           // TODO(aboxhall): handle intlist case.
@@ -334,171 +492,196 @@
   }
 };
 
-// Maps an attribute to its default value in an invalidated node.
-// These attributes are taken directly from the Automation idl.
-var AutomationAttributeDefaults = {
-  'id': -1,
-  'role': '',
-  'state': {},
-  'location': { left: 0, top: 0, width: 0, height: 0 }
-};
+var stringAttributes = [
+    'accessKey',
+    'action',
+    'ariaInvalidValue',
+    'autoComplete',
+    'containerLiveRelevant',
+    'containerLiveStatus',
+    'description',
+    'display',
+    'docDoctype',
+    'docMimetype',
+    'docTitle',
+    'docUrl',
+    'dropeffect',
+    'help',
+    'htmlTag',
+    'liveRelevant',
+    'liveStatus',
+    'name',
+    'placeholder',
+    'shortcut',
+    'textInputType',
+    'url',
+    'value'];
 
+var boolAttributes = [
+    'ariaReadonly',
+    'buttonMixed',
+    'canSetValue',
+    'canvasHasFallback',
+    'containerLiveAtomic',
+    'containerLiveBusy',
+    'docLoaded',
+    'grabbed',
+    'isAxTreeHost',
+    'liveAtomic',
+    'liveBusy',
+    'updateLocationOnly'];
 
-var AutomationAttributeTypes = [
-  'boolAttributes',
-  'floatAttributes',
-  'htmlAttributes',
-  'intAttributes',
-  'intlistAttributes',
-  'stringAttributes'
-];
+var intAttributes = [
+    'backgroundColor',
+    'color',
+    'colorValue',
+    'hierarchicalLevel',
+    'invalidState',
+    'posInSet',
+    'scrollX',
+    'scrollXMax',
+    'scrollXMin',
+    'scrollY',
+    'scrollYMax',
+    'scrollYMin',
+    'setSize',
+    'sortDirection',
+    'tableCellColumnIndex',
+    'tableCellColumnSpan',
+    'tableCellRowIndex',
+    'tableCellRowSpan',
+    'tableColumnCount',
+    'tableColumnIndex',
+    'tableRowCount',
+    'tableRowIndex',
+    'textDirection',
+    'textSelEnd',
+    'textSelStart',
+    'textStyle'];
 
-/**
- * Maps an attribute name to another attribute who's value is an id or an array
- * of ids referencing an AutomationNode.
- * @param {!Object<string>}
- * @const
- */
-var ATTRIBUTE_NAME_TO_ID_ATTRIBUTE = {
-  'aria-activedescendant': 'activedescendantId',
-  'aria-controls': 'controlsIds',
-  'aria-describedby': 'describedbyIds',
-  'aria-flowto': 'flowtoIds',
-  'aria-labelledby': 'labelledbyIds',
-  'aria-owns': 'ownsIds'
-};
+var nodeRefAttributes = [
+    ['activedescendantId', 'activedescendant'],
+    ['tableColumnHeaderId', 'tableColumnHeader'],
+    ['tableHeaderId', 'tableHeader'],
+    ['tableRowHeaderId', 'tableRowHeader'],
+    ['titleUiElement', 'titleUIElement']];
 
-/**
- * A set of attributes ignored in the automation API.
- * @param {!Object<boolean>}
- * @const
- */
-var ATTRIBUTE_BLACKLIST = {'activedescendantId': true,
-                           'childTreeId': true,
-                           'controlsIds': true,
-                           'describedbyIds': true,
-                           'flowtoIds': true,
-                           'labelledbyIds': true,
-                           'ownsIds': true
-};
+var intListAttributes = [
+    'characterOffsets',
+    'lineBreaks',
+    'wordEnds',
+    'wordStarts'];
 
-function defaultStringAttribute(opt_defaultVal) {
-  return { default: undefined, reflectFrom: 'stringAttributes' };
-}
+var nodeRefListAttributes = [
+    ['cellIds', 'cells'],
+    ['controlsIds', 'controls'],
+    ['describedbyIds', 'describedBy'],
+    ['flowtoIds', 'flowTo'],
+    ['labelledbyIds', 'labelledBy'],
+    ['uniqueCellIds', 'uniqueCells']];
 
-function defaultIntAttribute(opt_defaultVal) {
-  var defaultVal = (opt_defaultVal !== undefined) ? opt_defaultVal : 0;
-  return { default: defaultVal, reflectFrom: 'intAttributes' };
-}
+var floatAttributes = [
+    'docLoadingProgress',
+    'valueForRange',
+    'minValueForRange',
+    'maxValueForRange',
+    'fontSize'];
 
-function defaultFloatAttribute(opt_defaultVal) {
-  var defaultVal = (opt_defaultVal !== undefined) ? opt_defaultVal : 0;
-  return { default: defaultVal, reflectFrom: 'floatAttributes' };
-}
+var htmlAttributes = [
+    ['type', 'inputType']];
 
-function defaultBoolAttribute(opt_defaultVal) {
-  var defaultVal = (opt_defaultVal !== undefined) ? opt_defaultVal : false;
-  return { default: defaultVal, reflectFrom: 'boolAttributes' };
-}
+var publicAttributes = [];
 
-function defaultHtmlAttribute(opt_defaultVal) {
-  var defaultVal = (opt_defaultVal !== undefined) ? opt_defaultVal : '';
-  return { default: defaultVal, reflectFrom: 'htmlAttributes' };
-}
+stringAttributes.forEach(function (attributeName) {
+  publicAttributes.push(attributeName);
+  Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
+    get: function() {
+      return GetStringAttribute(this.treeID, this.id, attributeName);
+    }
+  });
+});
 
-function defaultIntListAttribute(opt_defaultVal) {
-  var defaultVal = (opt_defaultVal !== undefined) ? opt_defaultVal : [];
-  return { default: defaultVal, reflectFrom: 'intlistAttributes' };
-}
+boolAttributes.forEach(function (attributeName) {
+  publicAttributes.push(attributeName);
+  Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
+    get: function() {
+      return GetBoolAttribute(this.treeID, this.id, attributeName);
+    }
+  });
+});
 
-function defaultNodeRefAttribute(idAttribute, opt_defaultVal) {
-  var defaultVal = (opt_defaultVal !== undefined) ? opt_defaultVal : null;
-  return { default: defaultVal,
-           idFrom: 'intAttributes',
-           idAttribute: idAttribute,
-           isRef: true };
-}
+intAttributes.forEach(function (attributeName) {
+  publicAttributes.push(attributeName);
+  Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
+    get: function() {
+      return GetIntAttribute(this.treeID, this.id, attributeName);
+    }
+  });
+});
 
-function defaultNodeRefListAttribute(idAttribute, opt_defaultVal) {
-  var defaultVal = (opt_defaultVal !== undefined) ? opt_defaultVal : [];
-  return { default: [],
-           idFrom: 'intlistAttributes',
-           idAttribute: idAttribute,
-           isRef: true };
-}
+nodeRefAttributes.forEach(function (params) {
+  var srcAttributeName = params[0];
+  var dstAttributeName = params[1];
+  publicAttributes.push(dstAttributeName);
+  Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
+    get: function() {
+      var id = GetIntAttribute(this.treeID, this.id, srcAttributeName);
+      if (id)
+        return this.rootImpl.get(id);
+      else
+        return undefined;
+    }
+  });
+});
 
-// Maps an attribute to its default value in an invalidated node.
-// These attributes are taken directly from the Automation idl.
-var DefaultMixinAttributes = {
-  description: defaultStringAttribute(),
-  help: defaultStringAttribute(),
-  name: defaultStringAttribute(),
-  value: defaultStringAttribute(),
-  htmlTag: defaultStringAttribute(),
-  hierarchicalLevel: defaultIntAttribute(),
-  controls: defaultNodeRefListAttribute('controlsIds'),
-  describedby: defaultNodeRefListAttribute('describedbyIds'),
-  flowto: defaultNodeRefListAttribute('flowtoIds'),
-  labelledby: defaultNodeRefListAttribute('labelledbyIds'),
-  owns: defaultNodeRefListAttribute('ownsIds'),
-  wordStarts: defaultIntListAttribute(),
-  wordEnds: defaultIntListAttribute()
-};
+intListAttributes.forEach(function (attributeName) {
+  publicAttributes.push(attributeName);
+  Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
+    get: function() {
+      return GetIntListAttribute(this.treeID, this.id, attributeName);
+    }
+  });
+});
 
-var ActiveDescendantMixinAttribute = {
-  activedescendant: defaultNodeRefAttribute('activedescendantId')
-};
+nodeRefListAttributes.forEach(function (params) {
+  var srcAttributeName = params[0];
+  var dstAttributeName = params[1];
+  publicAttributes.push(dstAttributeName);
+  Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
+    get: function() {
+      var ids = GetIntListAttribute(this.treeID, this.id, srcAttributeName);
+      if (!ids)
+        return undefined;
+      var result = [];
+      for (var i = 0; i < ids.length; ++i) {
+        var node = this.rootImpl.get(ids[i]);
+        if (node)
+          result.push(node);
+      }
+      return result;
+    }
+  });
+});
 
-var LinkMixinAttributes = {
-  url: defaultStringAttribute()
-};
+floatAttributes.forEach(function (attributeName) {
+  publicAttributes.push(attributeName);
+  Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
+    get: function() {
+      return GetFloatAttribute(this.treeID, this.id, attributeName);
+    }
+  });
+});
 
-var DocumentMixinAttributes = {
-  docUrl: defaultStringAttribute(),
-  docTitle: defaultStringAttribute(),
-  docLoaded: defaultStringAttribute(),
-  docLoadingProgress: defaultFloatAttribute()
-};
-
-var ScrollableMixinAttributes = {
-  scrollX: defaultIntAttribute(),
-  scrollXMin: defaultIntAttribute(),
-  scrollXMax: defaultIntAttribute(),
-  scrollY: defaultIntAttribute(),
-  scrollYMin: defaultIntAttribute(),
-  scrollYMax: defaultIntAttribute()
-};
-
-var EditableTextMixinAttributes = {
-  textSelStart: defaultIntAttribute(-1),
-  textSelEnd: defaultIntAttribute(-1),
-  type: defaultHtmlAttribute()
-};
-
-var RangeMixinAttributes = {
-  valueForRange: defaultFloatAttribute(),
-  minValueForRange: defaultFloatAttribute(),
-  maxValueForRange: defaultFloatAttribute()
-};
-
-var TableMixinAttributes = {
-  tableRowCount: defaultIntAttribute(),
-  tableColumnCount: defaultIntAttribute()
-};
-
-var TableCellMixinAttributes = {
-  tableCellColumnIndex: defaultIntAttribute(),
-  tableCellColumnSpan: defaultIntAttribute(1),
-  tableCellRowIndex: defaultIntAttribute(),
-  tableCellRowSpan: defaultIntAttribute(1)
-};
-
-var LiveRegionMixinAttributes = {
-  containerLiveAtomic: defaultBoolAttribute(),
-  containerLiveBusy: defaultBoolAttribute(),
-  containerLiveRelevant: defaultStringAttribute(),
-  containerLiveStatus: defaultStringAttribute(),
-};
+htmlAttributes.forEach(function (params) {
+  var srcAttributeName = params[0];
+  var dstAttributeName = params[1];
+  publicAttributes.push(dstAttributeName);
+  Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
+    get: function() {
+      return GetHtmlAttribute(this.treeID, this.id, srcAttributeName);
+    }
+  });
+});
 
 /**
  * AutomationRootNode.
@@ -523,107 +706,88 @@
   this.axNodeDataCache_ = {};
 }
 
+AutomationRootNodeImpl.idToAutomationRootNode_ = {};
+
+AutomationRootNodeImpl.get = function(treeID) {
+  var result = AutomationRootNodeImpl.idToAutomationRootNode_[treeID];
+  return result || undefined;
+}
+
+AutomationRootNodeImpl.getOrCreate = function(treeID) {
+  if (AutomationRootNodeImpl.idToAutomationRootNode_[treeID])
+    return AutomationRootNodeImpl.idToAutomationRootNode_[treeID];
+  var result = new AutomationRootNode(treeID);
+  AutomationRootNodeImpl.idToAutomationRootNode_[treeID] = result;
+  return result;
+}
+
+AutomationRootNodeImpl.destroy = function(treeID) {
+  delete AutomationRootNodeImpl.idToAutomationRootNode_[treeID];
+}
+
 AutomationRootNodeImpl.prototype = {
   __proto__: AutomationNodeImpl.prototype,
 
+  /**
+   * @type {boolean}
+   */
   isRootNode: true,
+
+  /**
+   * @type {number}
+   */
   treeID: -1,
 
+  /**
+   * The parent of this node from a different tree.
+   * @type {?AutomationNode}
+   * @private
+   */
+  hostNode_: null,
+
+  /**
+   * A map from id to AutomationNode.
+   * @type {Object.<number, AutomationNode>}
+   * @private
+   */
+  axNodeDataCache_: null,
+
+  get id() {
+    return GetRootID(this.treeID);
+  },
+
   get: function(id) {
     if (id == undefined)
       return undefined;
 
-    return this.axNodeDataCache_[id];
+    if (id == this.id)
+      return this.wrapper;
+
+    var obj = this.axNodeDataCache_[id];
+    if (obj)
+      return obj;
+
+    obj = new AutomationNode(this);
+    privates(obj).impl.treeID = this.treeID;
+    privates(obj).impl.id = id;
+    this.axNodeDataCache_[id] = obj;
+
+    return obj;
   },
 
-  unserialize: function(update) {
-    var updateState = { pendingNodes: {}, newNodes: {} };
-    var oldRootId = this.id;
-
-    if (update.nodeIdToClear < 0) {
-        logging.WARNING('Bad nodeIdToClear: ' + update.nodeIdToClear);
-        lastError.set('automation',
-                      'Bad update received on automation tree',
-                      null,
-                      chrome);
-        return false;
-    } else if (update.nodeIdToClear > 0) {
-      var nodeToClear = this.axNodeDataCache_[update.nodeIdToClear];
-      if (!nodeToClear) {
-        logging.WARNING('Bad nodeIdToClear: ' + update.nodeIdToClear +
-                        ' (not in cache)');
-        lastError.set('automation',
-                      'Bad update received on automation tree',
-                      null,
-                      chrome);
-        return false;
-      }
-      if (nodeToClear === this.wrapper) {
-        this.invalidate_(nodeToClear);
-      } else {
-        var children = nodeToClear.children;
-        for (var i = 0; i < children.length; i++)
-          this.invalidate_(children[i]);
-        var nodeToClearImpl = privates(nodeToClear).impl;
-        nodeToClearImpl.childIds = []
-        updateState.pendingNodes[nodeToClearImpl.id] = nodeToClear;
-      }
-    }
-
-    for (var i = 0; i < update.nodes.length; i++) {
-      if (!this.updateNode_(update.nodes[i], updateState))
-        return false;
-    }
-
-    if (Object.keys(updateState.pendingNodes).length > 0) {
-      logging.WARNING('Nodes left pending by the update: ' +
-          $JSON.stringify(updateState.pendingNodes));
-      lastError.set('automation',
-                    'Bad update received on automation tree',
-                    null,
-                    chrome);
-      return false;
-    }
-
-    // Notify tree change observers of new nodes.
-    // TODO(dmazzoni): Notify tree change observers of changed nodes,
-    // and handle subtreeCreated and nodeCreated properly.
-    var observers = automationUtil.treeChangeObservers;
-    if (observers.length > 0) {
-      for (var nodeId in updateState.newNodes) {
-        var node = updateState.newNodes[nodeId];
-        var treeChange =
-            {target: node, type: schema.TreeChangeType.nodeCreated};
-        for (var i = 0; i < observers.length; i++) {
-          try {
-            observers[i](treeChange);
-          } catch (e) {
-            console.error('Error in tree change observer for ' +
-                treeChange.type + ': ' + e.message +
-                '\nStack trace: ' + e.stack);
-          }
-        }
-      }
-    }
-
-    return true;
+  remove: function(id) {
+    delete this.axNodeDataCache_[id];
   },
 
   destroy: function() {
-    if (this.hostTree)
-      this.hostTree.childTree = undefined;
-    this.hostTree = undefined;
-
     this.dispatchEvent(schema.EventType.destroyed);
-    this.invalidate_(this.wrapper);
+  },
+
+  setHostNode(hostNode) {
+    this.hostNode_ = hostNode;
   },
 
   onAccessibilityEvent: function(eventParams) {
-    if (!this.unserialize(eventParams.update)) {
-      logging.WARNING('unserialization failed');
-      return false;
-    }
-
     var targetNode = this.get(eventParams.targetID);
     if (targetNode) {
       var targetNodeImpl = privates(targetNode).impl;
@@ -651,373 +815,8 @@
     }
     return toStringInternal(this, 0);
   },
-
-  invalidate_: function(node) {
-    if (!node)
-      return;
-
-    // Notify tree change observers of the removed node.
-    var observers = automationUtil.treeChangeObservers;
-    if (observers.length > 0) {
-      var treeChange = {target: node, type: schema.TreeChangeType.nodeRemoved};
-      for (var i = 0; i < observers.length; i++) {
-        try {
-          observers[i](treeChange);
-        } catch (e) {
-          console.error('Error in tree change observer for ' + treeChange.type +
-              ': ' + e.message + '\nStack trace: ' + e.stack);
-        }
-      }
-    }
-
-    var children = node.children;
-
-    for (var i = 0, child; child = children[i]; i++) {
-      // Do not invalidate into subrooted nodes.
-      // TODO(dtseng): Revisit logic once out of proc iframes land.
-      if (child.root != node.root)
-        continue;
-      this.invalidate_(child);
-    }
-
-    // Retrieve the internal AutomationNodeImpl instance for this node.
-    // This object is not accessible outside of bindings code, but we can access
-    // it here.
-    var nodeImpl = privates(node).impl;
-    var id = nodeImpl.id;
-    for (var key in AutomationAttributeDefaults) {
-      nodeImpl[key] = AutomationAttributeDefaults[key];
-    }
-
-    nodeImpl.attributesInternal = {};
-    for (var key in DefaultMixinAttributes) {
-      var mixinAttribute = DefaultMixinAttributes[key];
-      if (!mixinAttribute.isRef)
-        nodeImpl.attributesInternal[key] = mixinAttribute.default;
-    }
-    nodeImpl.childIds = [];
-    nodeImpl.id = id;
-    delete this.axNodeDataCache_[id];
-  },
-
-  deleteOldChildren_: function(node, newChildIds) {
-    // Create a set of child ids in |src| for fast lookup, and return false
-    // if a duplicate is found;
-    var newChildIdSet = {};
-    for (var i = 0; i < newChildIds.length; i++) {
-      var childId = newChildIds[i];
-      if (newChildIdSet[childId]) {
-        logging.WARNING('Node ' + privates(node).impl.id +
-                        ' has duplicate child id ' + childId);
-        lastError.set('automation',
-                      'Bad update received on automation tree',
-                      null,
-                      chrome);
-        return false;
-      }
-      newChildIdSet[newChildIds[i]] = true;
-    }
-
-    // Delete the old children.
-    var nodeImpl = privates(node).impl;
-    var oldChildIds = nodeImpl.childIds;
-    for (var i = 0; i < oldChildIds.length;) {
-      var oldId = oldChildIds[i];
-      if (!newChildIdSet[oldId]) {
-        this.invalidate_(this.axNodeDataCache_[oldId]);
-        oldChildIds.splice(i, 1);
-      } else {
-        i++;
-      }
-    }
-    nodeImpl.childIds = oldChildIds;
-
-    return true;
-  },
-
-  createNewChildren_: function(node, newChildIds, updateState) {
-    logging.CHECK(node);
-    var success = true;
-
-    for (var i = 0; i < newChildIds.length; i++) {
-      var childId = newChildIds[i];
-      var childNode = this.axNodeDataCache_[childId];
-      if (childNode) {
-        if (childNode.parent != node) {
-          var parentId = -1;
-          if (childNode.parent) {
-            var parentImpl = privates(childNode.parent).impl;
-            parentId = parentImpl.id;
-          }
-          // This is a serious error - nodes should never be reparented.
-          // If this case occurs, continue so this node isn't left in an
-          // inconsistent state, but return failure at the end.
-          logging.WARNING('Node ' + childId + ' reparented from ' +
-                          parentId + ' to ' + privates(node).impl.id);
-          lastError.set('automation',
-                        'Bad update received on automation tree',
-                        null,
-                        chrome);
-          success = false;
-          continue;
-        }
-      } else {
-        childNode = new AutomationNode(this);
-        this.axNodeDataCache_[childId] = childNode;
-        privates(childNode).impl.id = childId;
-        updateState.pendingNodes[childId] = childNode;
-        updateState.newNodes[childId] = childNode;
-      }
-      privates(childNode).impl.indexInParent = i;
-      privates(childNode).impl.parentID = privates(node).impl.id;
-    }
-
-    return success;
-  },
-
-  setData_: function(node, nodeData) {
-    var nodeImpl = privates(node).impl;
-
-    // TODO(dtseng): Make into set listing all hosting node roles.
-    if (nodeData.role == schema.RoleType.webView) {
-      if (nodeImpl.childTreeID !== nodeData.intAttributes.childTreeId)
-        nodeImpl.pendingChildFrame = true;
-
-      if (nodeImpl.pendingChildFrame) {
-        nodeImpl.childTreeID = nodeData.intAttributes.childTreeId;
-        automationUtil.storeTreeCallback(nodeImpl.childTreeID, function(root) {
-          nodeImpl.pendingChildFrame = false;
-          nodeImpl.childTree = root;
-          privates(root).impl.hostTree = node;
-          if (root.attributes.docLoadingProgress == 1)
-            privates(root).impl.dispatchEvent(schema.EventType.loadComplete);
-          nodeImpl.dispatchEvent(schema.EventType.childrenChanged);
-        });
-        automationInternal.enableFrame(nodeImpl.childTreeID);
-      }
-    }
-    for (var key in AutomationAttributeDefaults) {
-      if (key in nodeData)
-        nodeImpl[key] = nodeData[key];
-      else
-        nodeImpl[key] = AutomationAttributeDefaults[key];
-    }
-
-    // Set all basic attributes.
-    this.mixinAttributes_(nodeImpl, DefaultMixinAttributes, nodeData);
-
-    // If this is a rootWebArea or webArea, set document attributes.
-    if (nodeData.role == schema.RoleType.rootWebArea ||
-        nodeData.role == schema.RoleType.webArea) {
-      this.mixinAttributes_(nodeImpl, DocumentMixinAttributes, nodeData);
-    }
-
-    // If this is a scrollable area, set scrollable attributes.
-    for (var scrollAttr in ScrollableMixinAttributes) {
-      var spec = ScrollableMixinAttributes[scrollAttr];
-      if (this.findAttribute_(scrollAttr, spec, nodeData) !== undefined) {
-        this.mixinAttributes_(nodeImpl, ScrollableMixinAttributes, nodeData);
-        break;
-      }
-    }
-
-    // If this is inside a live region, set live region mixins.
-    var attr = 'containerLiveStatus';
-    var spec = LiveRegionMixinAttributes[attr];
-    if (this.findAttribute_(attr, spec, nodeData) !== undefined) {
-      this.mixinAttributes_(nodeImpl, LiveRegionMixinAttributes, nodeData);
-    }
-
-    // If this is a link, set link attributes
-    if (nodeData.role == 'link') {
-      this.mixinAttributes_(nodeImpl, LinkMixinAttributes, nodeData);
-    }
-
-    // If this is an editable text area, set editable text attributes.
-    if (nodeData.role == schema.RoleType.textField ||
-        nodeData.role == schema.RoleType.spinButton) {
-      this.mixinAttributes_(nodeImpl, EditableTextMixinAttributes, nodeData);
-    }
-
-    // If this is a range type, set range attributes.
-    if (nodeData.role == schema.RoleType.progressIndicator ||
-        nodeData.role == schema.RoleType.scrollBar ||
-        nodeData.role == schema.RoleType.slider ||
-        nodeData.role == schema.RoleType.spinButton) {
-      this.mixinAttributes_(nodeImpl, RangeMixinAttributes, nodeData);
-    }
-
-    // If this is a table, set table attributes.
-    if (nodeData.role == schema.RoleType.table) {
-      this.mixinAttributes_(nodeImpl, TableMixinAttributes, nodeData);
-    }
-
-    // If this is a table cell, set table cell attributes.
-    if (nodeData.role == schema.RoleType.cell) {
-      this.mixinAttributes_(nodeImpl, TableCellMixinAttributes, nodeData);
-    }
-
-    // If this has an active descendant, expose it.
-    if ('intAttributes' in nodeData &&
-        'activedescendantId' in nodeData.intAttributes) {
-      this.mixinAttributes_(nodeImpl, ActiveDescendantMixinAttribute, nodeData);
-    }
-
-    for (var i = 0; i < AutomationAttributeTypes.length; i++) {
-      var attributeType = AutomationAttributeTypes[i];
-      for (var attributeName in nodeData[attributeType]) {
-        nodeImpl.attributesInternal[attributeName] =
-            nodeData[attributeType][attributeName];
-        if (ATTRIBUTE_BLACKLIST.hasOwnProperty(attributeName) ||
-            nodeImpl.attributes.hasOwnProperty(attributeName)) {
-          continue;
-        } else if (
-          ATTRIBUTE_NAME_TO_ID_ATTRIBUTE.hasOwnProperty(attributeName)) {
-          this.defineReadonlyAttribute_(nodeImpl,
-                                        nodeImpl.attributes,
-                                        attributeName,
-                                        true);
-        } else {
-          this.defineReadonlyAttribute_(nodeImpl,
-                                        nodeImpl.attributes,
-                                        attributeName);
-        }
-      }
-    }
-  },
-
-  mixinAttributes_: function(nodeImpl, attributes, nodeData) {
-    for (var attribute in attributes) {
-      var spec = attributes[attribute];
-      if (spec.isRef)
-        this.mixinRelationshipAttribute_(nodeImpl, attribute, spec, nodeData);
-      else
-        this.mixinAttribute_(nodeImpl, attribute, spec, nodeData);
-    }
-  },
-
-  mixinAttribute_: function(nodeImpl, attribute, spec, nodeData) {
-    var value = this.findAttribute_(attribute, spec, nodeData);
-    if (value === undefined)
-      value = spec.default;
-    nodeImpl.attributesInternal[attribute] = value;
-    this.defineReadonlyAttribute_(nodeImpl, nodeImpl, attribute);
-  },
-
-  mixinRelationshipAttribute_: function(nodeImpl, attribute, spec, nodeData) {
-    var idAttribute = spec.idAttribute;
-    var idValue = spec.default;
-    if (spec.idFrom in nodeData) {
-      idValue = idAttribute in nodeData[spec.idFrom]
-          ? nodeData[spec.idFrom][idAttribute] : idValue;
-    }
-
-    // Ok to define a list attribute with an empty list, but not a
-    // single ref with a null ID.
-    if (idValue === null)
-      return;
-
-    nodeImpl.attributesInternal[idAttribute] = idValue;
-    this.defineReadonlyAttribute_(
-      nodeImpl, nodeImpl, attribute, true, idAttribute);
-  },
-
-  findAttribute_: function(attribute, spec, nodeData) {
-    if (!('reflectFrom' in spec))
-      return;
-    var attributeGroup = spec.reflectFrom;
-    if (!(attributeGroup in nodeData))
-      return;
-
-    return nodeData[attributeGroup][attribute];
-  },
-
-  defineReadonlyAttribute_: function(
-      node, object, attributeName, opt_isIDRef, opt_idAttribute) {
-    if (attributeName in object)
-      return;
-
-    if (opt_isIDRef) {
-      $Object.defineProperty(object, attributeName, {
-        enumerable: true,
-        get: function() {
-          var idAttribute = opt_idAttribute ||
-                            ATTRIBUTE_NAME_TO_ID_ATTRIBUTE[attributeName];
-          var idValue = node.attributesInternal[idAttribute];
-          if (Array.isArray(idValue)) {
-            return idValue.map(function(current) {
-              return node.rootImpl.get(current);
-            }, this);
-          }
-          return node.rootImpl.get(idValue);
-        }.bind(this),
-      });
-    } else {
-      $Object.defineProperty(object, attributeName, {
-        enumerable: true,
-        get: function() {
-          return node.attributesInternal[attributeName];
-        }.bind(this),
-      });
-    }
-
-    if (object instanceof AutomationNodeImpl) {
-      // Also expose attribute publicly on the wrapper.
-      $Object.defineProperty(object.wrapper, attributeName, {
-        enumerable: true,
-        get: function() {
-          return object[attributeName];
-        },
-      });
-
-    }
-  },
-
-  updateNode_: function(nodeData, updateState) {
-    var node = this.axNodeDataCache_[nodeData.id];
-    var didUpdateRoot = false;
-    if (node) {
-      delete updateState.pendingNodes[privates(node).impl.id];
-    } else {
-      if (nodeData.role != schema.RoleType.rootWebArea &&
-          nodeData.role != schema.RoleType.desktop) {
-        logging.WARNING(String(nodeData.id) +
-                     ' is not in the cache and not the new root.');
-        lastError.set('automation',
-                      'Bad update received on automation tree',
-                      null,
-                      chrome);
-        return false;
-      }
-      // |this| is an AutomationRootNodeImpl; retrieve the
-      // AutomationRootNode instance instead.
-      node = this.wrapper;
-      didUpdateRoot = true;
-      updateState.newNodes[this.id] = this.wrapper;
-    }
-    this.setData_(node, nodeData);
-
-    // TODO(aboxhall): send onChanged event?
-    logging.CHECK(node);
-    if (!this.deleteOldChildren_(node, nodeData.childIds)) {
-      if (didUpdateRoot) {
-        this.invalidate_(this.wrapper);
-      }
-      return false;
-    }
-    var nodeImpl = privates(node).impl;
-
-    var success = this.createNewChildren_(node,
-                                          nodeData.childIds,
-                                          updateState);
-    nodeImpl.childIds = nodeData.childIds;
-    this.axNodeDataCache_[nodeImpl.id] = node;
-
-    return success;
-  }
 };
 
-
 var AutomationNode = utils.expose('AutomationNode',
                                   AutomationNodeImpl,
                                   { functions: ['doDefault',
@@ -1032,7 +831,8 @@
                                                 'removeEventListener',
                                                 'domQuerySelector',
                                                 'toString' ],
-                                    readonly: ['parent',
+                                    readonly: publicAttributes.concat(
+                                              ['parent',
                                                'firstChild',
                                                'lastChild',
                                                'children',
@@ -1042,13 +842,24 @@
                                                'role',
                                                'state',
                                                'location',
-                                               'attributes',
                                                'indexInParent',
-                                               'root'] });
+                                               'root']) });
 
 var AutomationRootNode = utils.expose('AutomationRootNode',
                                       AutomationRootNodeImpl,
                                       { superclass: AutomationNode });
 
+AutomationRootNode.get = function(treeID) {
+  return AutomationRootNodeImpl.get(treeID);
+}
+
+AutomationRootNode.getOrCreate = function(treeID) {
+  return AutomationRootNodeImpl.getOrCreate(treeID);
+}
+
+AutomationRootNode.destroy = function(treeID) {
+  AutomationRootNodeImpl.destroy(treeID);
+}
+
 exports.AutomationNode = AutomationNode;
 exports.AutomationRootNode = AutomationRootNode;
diff --git a/chrome/renderer/resources/extensions/automation_custom_bindings.js b/chrome/renderer/resources/extensions/automation_custom_bindings.js
index b2fc5c3b7..c157a5b 100644
--- a/chrome/renderer/resources/extensions/automation_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/automation_custom_bindings.js
@@ -16,6 +16,11 @@
 var nativeAutomationInternal = requireNative('automationInternal');
 var GetRoutingID = nativeAutomationInternal.GetRoutingID;
 var GetSchemaAdditions = nativeAutomationInternal.GetSchemaAdditions;
+var DestroyAccessibilityTree =
+    nativeAutomationInternal.DestroyAccessibilityTree;
+var GetIntAttribute = nativeAutomationInternal.GetIntAttribute;
+var StartCachingAccessibilityTrees =
+    nativeAutomationInternal.StartCachingAccessibilityTrees;
 var schema = GetSchemaAdditions();
 
 /**
@@ -24,7 +29,6 @@
 window.automationUtil = function() {};
 
 // TODO(aboxhall): Look into using WeakMap
-var idToAutomationRootNode = {};
 var idToCallback = {};
 
 var DESKTOP_TREE_ID = 0;
@@ -33,7 +37,7 @@
   if (!callback)
     return;
 
-  var targetTree = idToAutomationRootNode[id];
+  var targetTree = AutomationRootNode.get(id);
   if (!targetTree) {
     // If we haven't cached the tree, hold the callback until the tree is
     // populated by the initial onAccessibilityEvent call.
@@ -58,6 +62,7 @@
   // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj.
   apiFunctions.setHandleRequest('getTree', function getTree(tabID, callback) {
     var routingID = GetRoutingID();
+    StartCachingAccessibilityTrees();
 
     // enableTab() ensures the renderer for the active or specified tab has
     // accessibility enabled, and fetches its ax tree id to use as
@@ -79,8 +84,8 @@
 
   var desktopTree = null;
   apiFunctions.setHandleRequest('getDesktop', function(callback) {
-    desktopTree =
-        idToAutomationRootNode[DESKTOP_TREE_ID];
+    StartCachingAccessibilityTrees();
+    desktopTree = AutomationRootNode.get(DESKTOP_TREE_ID);
     if (!desktopTree) {
       if (DESKTOP_TREE_ID in idToCallback)
         idToCallback[DESKTOP_TREE_ID].push(callback);
@@ -93,8 +98,7 @@
       // scope.
       automationInternal.enableDesktop(routingID, function() {
         if (lastError.hasError(chrome)) {
-          delete idToAutomationRootNode[
-              DESKTOP_TREE_ID];
+          AutomationRootNode.destroy(DESKTOP_TREE_ID);
           callback();
           return;
         }
@@ -125,19 +129,65 @@
 
 });
 
+automationInternal.onTreeChange.addListener(function(treeID,
+                                                     nodeID,
+                                                     changeType) {
+  var tree = AutomationRootNode.get(treeID);
+  if (!tree)
+    return;
+
+  var node = privates(tree).impl.get(nodeID);
+  if (!node)
+    return;
+
+  if (node.role == 'webView') {
+    // A WebView in the desktop tree has a different AX tree as its child.
+    // When we encounter a WebView with a child AX tree id that we don't
+    // currently have cached, explicitly request that AX tree from the
+    // browser process and set up a callback when it loads to attach that
+    // tree as a child of this node and fire appropriate events.
+    var childTreeID = GetIntAttribute(treeID, nodeID, 'childTreeId');
+    if (!AutomationRootNode.get(childTreeID)) {
+      automationUtil.storeTreeCallback(childTreeID, function(root) {
+        privates(root).impl.setHostNode(node);
+
+        if (root.docLoaded)
+          privates(root).impl.dispatchEvent(schema.EventType.loadComplete);
+
+        privates(node).impl.dispatchEvent(schema.EventType.childrenChanged);
+      });
+
+      automationInternal.enableFrame(childTreeID);
+    }
+  }
+
+  var treeChange = {target: node, type: changeType};
+
+  // Make a copy of the observers in case one of these callbacks tries
+  // to change the list of observers.
+  var observers = automationUtil.treeChangeObservers.slice();
+  for (var i = 0; i < observers.length; i++) {
+    try {
+      observers[i](treeChange);
+    } catch (e) {
+      logging.WARNING('Error in tree change observer for ' +
+          treeChange.type + ': ' + e.message +
+          '\nStack trace: ' + e.stack);
+    }
+  }
+
+  if (changeType == schema.TreeChangeType.nodeRemoved) {
+    privates(tree).impl.remove(nodeID);
+  }
+});
+
 // Listen to the automationInternal.onAccessibilityEvent event, which is
 // essentially a proxy for the AccessibilityHostMsg_Events IPC from the
 // renderer.
 automationInternal.onAccessibilityEvent.addListener(function(data) {
   var id = data.treeID;
-  var targetTree = idToAutomationRootNode[id];
-  if (!targetTree) {
-    // If this is the first time we've gotten data for this tree, it will
-    // contain all of the tree's data, so create a new tree which will be
-    // bootstrapped from |data|.
-    targetTree = new AutomationRootNode(id);
-    idToAutomationRootNode[id] = targetTree;
-  }
+  var targetTree = AutomationRootNode.getOrCreate(id);
+
   if (!privates(targetTree).impl.onAccessibilityEvent(data))
     return;
 
@@ -149,7 +199,7 @@
   // attribute or child nodes. If we've got that, wait for the full tree before
   // calling the callback.
   // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553)
-  if (id != DESKTOP_TREE_ID && !targetTree.attributes.url &&
+  if (id != DESKTOP_TREE_ID && !targetTree.url &&
       targetTree.children.length == 0) {
     return;
   }
@@ -158,7 +208,6 @@
   // have been cached in idToCallback, so call and delete it now that we
   // have the complete tree.
   for (var i = 0; i < idToCallback[id].length; i++) {
-    console.log('calling getTree() callback');
     var callback = idToCallback[id][i];
     callback(targetTree);
   }
@@ -166,14 +215,17 @@
 });
 
 automationInternal.onAccessibilityTreeDestroyed.addListener(function(id) {
-  var targetTree = idToAutomationRootNode[id];
+  // Destroy the AutomationRootNode.
+  var targetTree = AutomationRootNode.get(id);
   if (targetTree) {
     privates(targetTree).impl.destroy();
-    delete idToAutomationRootNode[id];
+    AutomationRootNode.destroy(id);
   } else {
     logging.WARNING('no targetTree to destroy');
   }
-  delete idToAutomationRootNode[id];
+
+  // Destroy the native cache of the accessibility tree.
+  DestroyAccessibilityTree(id);
 });
 
 exports.binding = automation.generate();
diff --git a/chrome/renderer/spellchecker/spellcheck.cc b/chrome/renderer/spellchecker/spellcheck.cc
index a43c7277..fecb307 100644
--- a/chrome/renderer/spellchecker/spellcheck.cc
+++ b/chrome/renderer/spellchecker/spellcheck.cc
@@ -97,6 +97,29 @@
   return true;
 }
 
+bool IsApostrophe(base::char16 c) {
+  const base::char16 kApostrophe = 0x27;
+  const base::char16 kRightSingleQuotationMark = 0x2019;
+  return c == kApostrophe || c == kRightSingleQuotationMark;
+}
+
+// Makes sure that the apostrophes in the |spelling_suggestion| are the same
+// type as in the |misspelled_word| and in the same order. Ignore differences in
+// the number of apostrophes.
+void PreserveOriginalApostropheTypes(const base::string16& misspelled_word,
+                                     base::string16* spelling_suggestion) {
+  auto it = spelling_suggestion->begin();
+  for (const base::char16& c : misspelled_word) {
+    if (IsApostrophe(c)) {
+      it = std::find_if(it, spelling_suggestion->end(), IsApostrophe);
+      if (it == spelling_suggestion->end())
+        return;
+
+      *it++ = c;
+    }
+  }
+}
+
 }  // namespace
 
 class SpellCheck::SpellcheckRequest {
@@ -383,33 +406,44 @@
     const base::string16& line_text,
     const std::vector<SpellCheckResult>& spellcheck_results,
     WebVector<WebTextCheckingResult>* textcheck_results) {
-  // Double-check misspelled words with our spellchecker and attach grammar
-  // markers to them if our spellchecker tells they are correct words, i.e. they
-  // are probably contextually-misspelled words.
-  const base::char16* text = line_text.c_str();
-  std::vector<WebTextCheckingResult> list;
-  for (size_t i = 0; i < spellcheck_results.size(); ++i) {
-    SpellCheckResult::Decoration decoration = spellcheck_results[i].decoration;
-    int word_location = spellcheck_results[i].location;
-    int word_length = spellcheck_results[i].length;
-    int misspelling_start = 0;
-    int misspelling_length = 0;
+  std::vector<WebTextCheckingResult> results;
+  for (const SpellCheckResult& spellcheck_result : spellcheck_results) {
+    const base::string16& misspelled_word =
+        line_text.substr(spellcheck_result.location, spellcheck_result.length);
+
+    // Ignore words in custom dictionary.
+    if (custom_dictionary_.SpellCheckWord(misspelled_word, 0,
+                                          misspelled_word.length())) {
+      continue;
+    }
+
+    // Use the same types of appostrophes as in the mispelled word.
+    base::string16 replacement = spellcheck_result.replacement;
+    PreserveOriginalApostropheTypes(misspelled_word, &replacement);
+
+    // Ignore misspellings due the typographical apostrophe.
+    if (misspelled_word == replacement)
+      continue;
+
+    // Double-check misspelled words with out spellchecker and attach grammar
+    // markers to them if our spellchecker tells us they are correct words,
+    // i.e. they are probably contextually-misspelled words.
+    SpellCheckResult::Decoration decoration = spellcheck_result.decoration;
+    int unused_misspelling_start = 0;
+    int unused_misspelling_length = 0;
     if (decoration == SpellCheckResult::SPELLING &&
-        filter == USE_NATIVE_CHECKER) {
-      if (SpellCheckWord(text + word_location, word_length, 0,
-                         &misspelling_start, &misspelling_length, NULL)) {
-        decoration = SpellCheckResult::GRAMMAR;
-      }
+        filter == USE_NATIVE_CHECKER &&
+        SpellCheckWord(misspelled_word.c_str(), misspelled_word.length(), 0,
+                       &unused_misspelling_start, &unused_misspelling_length,
+                       nullptr)) {
+      decoration = SpellCheckResult::GRAMMAR;
     }
-    if (!custom_dictionary_.SpellCheckWord(
-            line_text, word_location, word_length)) {
-      list.push_back(WebTextCheckingResult(
-          static_cast<WebTextDecorationType>(decoration),
-          word_location + line_offset,
-          word_length,
-          spellcheck_results[i].replacement,
-          spellcheck_results[i].hash));
-    }
+
+    results.push_back(WebTextCheckingResult(
+        static_cast<WebTextDecorationType>(decoration),
+        line_offset + spellcheck_result.location, spellcheck_result.length,
+        replacement, spellcheck_result.hash));
   }
-  textcheck_results->assign(list);
+
+  textcheck_results->assign(results);
 }
diff --git a/chrome/renderer/spellchecker/spellcheck_unittest.cc b/chrome/renderer/spellchecker/spellcheck_unittest.cc
index 34886aa2..0e36c24 100644
--- a/chrome/renderer/spellchecker/spellcheck_unittest.cc
+++ b/chrome/renderer/spellchecker/spellcheck_unittest.cc
@@ -17,6 +17,8 @@
 #include "third_party/WebKit/public/web/WebTextCheckingCompletion.h"
 #include "third_party/WebKit/public/web/WebTextCheckingResult.h"
 
+#define TYPOGRAPHICAL_APOSTROPHE L"\x2019"
+
 namespace {
 
 base::FilePath GetHunspellDirectory() {
@@ -80,6 +82,10 @@
         base::ASCIIToUTF16(word), tag);
   }
 
+  bool IsValidContraction(const base::string16& word, int tag) {
+    return spell_check_->spellcheck_.IsValidContraction(word, tag);
+  }
+
 #if !defined(OS_MACOSX)
  protected:
   void TestSpellCheckParagraph(
@@ -201,7 +207,7 @@
     // A valid English contraction
     {L"isn't", true},
     // A valid English contraction with a typographical apostrophe.
-    {L"isn\x2019t", true},
+    {L"isn" TYPOGRAPHICAL_APOSTROPHE L"t", true},
     // A valid English word enclosed with underscores.
     {L"_hello_", true},
 
@@ -1140,7 +1146,7 @@
                                              text,
                                              spellcheck_results,
                                              &textcheck_results);
-    EXPECT_EQ(spellcheck_results.size(), textcheck_results.size());
+    ASSERT_EQ(spellcheck_results.size(), textcheck_results.size());
     EXPECT_EQ(blink::WebTextDecorationTypeSpelling,
               textcheck_results[0].decoration);
     EXPECT_EQ(spellcheck_results[0].location, textcheck_results[0].location);
@@ -1160,12 +1166,78 @@
                                              text,
                                              spellcheck_results,
                                              &textcheck_results);
-    EXPECT_EQ(spellcheck_results.size(), textcheck_results.size());
+    ASSERT_EQ(spellcheck_results.size(), textcheck_results.size());
     EXPECT_EQ(blink::WebTextDecorationTypeGrammar,
               textcheck_results[0].decoration);
     EXPECT_EQ(spellcheck_results[0].location, textcheck_results[0].location);
     EXPECT_EQ(spellcheck_results[0].length, textcheck_results[0].length);
   }
+
+  // Verify that the SpellCheck preserves the original apostrophe type in the
+  // checked text, regardless of the type of apostrophe the browser returns.
+  {
+    base::string16 text = base::WideToUTF16(
+        L"Ik've havn" TYPOGRAPHICAL_APOSTROPHE L"t ni'n"
+        TYPOGRAPHICAL_APOSTROPHE L"out-s I've I" TYPOGRAPHICAL_APOSTROPHE
+        L"ve");
+    std::vector<SpellCheckResult> spellcheck_results;
+
+    // All typewriter apostrophe results.
+    spellcheck_results.push_back(SpellCheckResult(
+        SpellCheckResult::SPELLING, 0, 5, base::UTF8ToUTF16("I've")));
+    spellcheck_results.push_back(SpellCheckResult(
+        SpellCheckResult::SPELLING, 6, 6, base::UTF8ToUTF16("haven't")));
+    spellcheck_results.push_back(SpellCheckResult(
+        SpellCheckResult::SPELLING, 13, 10, base::UTF8ToUTF16("in'n'out's")));
+
+    // Replacements that differ only by apostrophe type should be ignored.
+    spellcheck_results.push_back(SpellCheckResult(
+        SpellCheckResult::SPELLING, 24, 4, base::UTF8ToUTF16("I've")));
+    spellcheck_results.push_back(SpellCheckResult(
+        SpellCheckResult::SPELLING, 29, 4, base::UTF8ToUTF16("I've")));
+
+    // All typographical apostrophe results.
+    spellcheck_results.push_back(SpellCheckResult(
+        SpellCheckResult::SPELLING, 0, 5,
+        base::WideToUTF16(L"I" TYPOGRAPHICAL_APOSTROPHE L"ve")));
+    spellcheck_results.push_back(SpellCheckResult(
+        SpellCheckResult::SPELLING, 6, 6,
+        base::WideToUTF16(L"haven" TYPOGRAPHICAL_APOSTROPHE L"t")));
+    spellcheck_results.push_back(SpellCheckResult(
+        SpellCheckResult::SPELLING, 13, 10, base::WideToUTF16(
+            L"in" TYPOGRAPHICAL_APOSTROPHE L"n" TYPOGRAPHICAL_APOSTROPHE L"out"
+            TYPOGRAPHICAL_APOSTROPHE L"s")));
+
+    // Replacements that differ only by apostrophe type should be ignored.
+    spellcheck_results.push_back(SpellCheckResult(
+        SpellCheckResult::SPELLING, 24, 4,
+        base::WideToUTF16(L"I" TYPOGRAPHICAL_APOSTROPHE L"ve")));
+    spellcheck_results.push_back(SpellCheckResult(
+        SpellCheckResult::SPELLING, 29, 4,
+        base::WideToUTF16(L"I" TYPOGRAPHICAL_APOSTROPHE L"ve")));
+
+    blink::WebVector<blink::WebTextCheckingResult> textcheck_results;
+    spell_check()->CreateTextCheckingResults(SpellCheck::USE_NATIVE_CHECKER, 0,
+                                             text, spellcheck_results,
+                                             &textcheck_results);
+
+    static const wchar_t* kExpectedReplacements[] = {
+        L"I've",
+        L"haven" TYPOGRAPHICAL_APOSTROPHE L"t",
+        L"in'n" TYPOGRAPHICAL_APOSTROPHE L"out's",
+        L"I've",
+        L"haven" TYPOGRAPHICAL_APOSTROPHE L"t",
+        L"in'n" TYPOGRAPHICAL_APOSTROPHE L"out" TYPOGRAPHICAL_APOSTROPHE L"s",
+    };
+
+    ASSERT_EQ(arraysize(kExpectedReplacements), textcheck_results.size());
+    for (size_t i = 0; i < arraysize(kExpectedReplacements); ++i) {
+      EXPECT_EQ(base::WideToUTF16(kExpectedReplacements[i]),
+                textcheck_results[i].replacement)
+          << "i=" << i << "\nactual: \""
+          << base::string16(textcheck_results[i].replacement) << "\"";
+    }
+  }
 }
 
 #endif
@@ -1373,3 +1445,24 @@
       EXPECT_EQ(suggestions[0], base::ASCIIToUTF16(kTestCases[i].suggestion));
   }
 }
+
+// Words with apostrophes should be valid contractions.
+TEST_F(SpellCheckTest, IsValidContraction) {
+  static const char* kLanguages[] = {
+    "en-AU",
+    "en-CA",
+    "en-GB",
+    "en-US",
+  };
+
+  static const wchar_t* kWords[] = {
+    L"in'n'out",
+    L"in" TYPOGRAPHICAL_APOSTROPHE L"n" TYPOGRAPHICAL_APOSTROPHE L"out",
+  };
+
+  for (size_t i = 0; i < arraysize(kLanguages); ++i) {
+    ReinitializeSpellCheck(kLanguages[i]);
+    for (size_t j = 0; j < arraysize(kWords); ++j)
+      EXPECT_TRUE(IsValidContraction(base::WideToUTF16(kWords[j]), 0));
+  }
+}
diff --git a/chrome/renderer/spellchecker/spellcheck_worditerator_unittest.cc b/chrome/renderer/spellchecker/spellcheck_worditerator_unittest.cc
index d45470cd..491a8f2 100644
--- a/chrome/renderer/spellchecker/spellcheck_worditerator_unittest.cc
+++ b/chrome/renderer/spellchecker/spellcheck_worditerator_unittest.cc
@@ -229,6 +229,54 @@
   }
 }
 
+// Vertify SpellcheckWordIterator treats typographical apostrophe as a part of
+// the word.
+TEST(SpellcheckWordIteratorTest, TypographicalApostropheIsPartOfWord) {
+  static const struct {
+    const char* language;
+    const wchar_t* word;
+  } kTestCases[] = {
+    // Typewriter apostrophe:
+    {
+      "en-AU", L"you're"
+    }, {
+      "en-CA", L"you're"
+    }, {
+      "en-GB", L"you're"
+    }, {
+      "en-US", L"you're"
+    },
+    // Typographical apostrophe:
+    {
+      "en-AU", L"you\x2019re"
+    }, {
+      "en-CA", L"you\x2019re"
+    }, {
+      "en-GB", L"you\x2019re"
+    }, {
+      "en-US", L"you\x2019re"
+    },
+  };
+
+  for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+    SpellcheckCharAttribute attributes;
+    attributes.SetDefaultLanguage(kTestCases[i].language);
+
+    base::string16 input_word(base::WideToUTF16(kTestCases[i].word));
+    SpellcheckWordIterator iterator;
+    EXPECT_TRUE(iterator.Initialize(&attributes, true));
+    EXPECT_TRUE(iterator.SetText(input_word.c_str(), input_word.length()));
+
+    base::string16 actual_word;
+    int actual_start, actual_end;
+    EXPECT_TRUE(iterator.GetNextWord(&actual_word, &actual_start, &actual_end));
+    EXPECT_EQ(input_word, actual_word);
+    EXPECT_EQ(0, actual_start);
+    EXPECT_EQ(input_word.length(),
+              static_cast<base::string16::size_type>(actual_end));
+  }
+}
+
 TEST(SpellcheckWordIteratorTest, Initialization) {
   // Test initialization works when a default language is set.
   {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabLoadObserver.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabLoadObserver.java
index 440bfd8..1d28e1f 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabLoadObserver.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/TabLoadObserver.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.test.util.browser;
 
+import android.text.TextUtils;
+
 import org.chromium.base.ThreadUtils;
 import org.chromium.chrome.browser.EmptyTabObserver;
 import org.chromium.chrome.browser.Tab;
@@ -14,15 +16,29 @@
  * Monitors that a Tab starts loading and stops loading a URL.
  */
 public class TabLoadObserver extends EmptyTabObserver implements Criteria {
+    private static final float FLOAT_EPSILON = 0.001f;
+
+    private final Tab mTab;
+    private final String mExpectedTitle;
+    private final Float mExpectedScale;
+
     private boolean mTabLoadStarted;
     private boolean mTabLoadStopped;
 
-    public TabLoadObserver(final Tab tab, final String url) {
-        tab.addObserver(this);
+    public TabLoadObserver(Tab tab, final String url) {
+        this(tab, url, null, null);
+    }
+
+    public TabLoadObserver(Tab tab, final String url, String expectedTitle, Float expectedScale) {
+        mTab = tab;
+        mExpectedTitle = expectedTitle;
+        mExpectedScale = expectedScale;
+
+        mTab.addObserver(this);
         ThreadUtils.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                tab.loadUrl(new LoadUrlParams(url));
+                mTab.loadUrl(new LoadUrlParams(url));
             }
         });
     }
@@ -39,6 +55,19 @@
 
     @Override
     public boolean isSatisfied() {
-        return mTabLoadStarted && mTabLoadStopped;
+        if (!mTabLoadStarted) return false;
+        if (!mTabLoadStopped) return false;
+        if (!mTab.isLoadingAndRenderingDone()) return false;
+
+        if (mExpectedTitle != null && !TextUtils.equals(mExpectedTitle, mTab.getTitle())) {
+            return false;
+        }
+        if (mExpectedScale != null) {
+            if (mTab.getContentViewCore() == null) return false;
+            if (Math.abs(mExpectedScale - mTab.getContentViewCore().getScale()) >= FLOAT_EPSILON) {
+                return false;
+            }
+        }
+        return true;
     }
 }
\ No newline at end of file
diff --git a/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java b/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java
index 1c97d3e..ce55c65 100644
--- a/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java
+++ b/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java
@@ -438,8 +438,7 @@
                             ChromeLauncherActivity.launchDocumentInstance(getActivity(), incognito,
                                     ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND, url,
                                     DocumentMetricIds.STARTED_BY_UNKNOWN,
-                                    PageTransition.AUTO_TOPLEVEL,
-                                    false, null);
+                                    PageTransition.AUTO_TOPLEVEL, null);
                         }
                     });
                 }
diff --git a/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeTabbedActivityLollipopAndAboveTest.java b/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeTabbedActivityLollipopAndAboveTest.java
index 41ef29b..cc47cdaa 100644
--- a/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeTabbedActivityLollipopAndAboveTest.java
+++ b/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeTabbedActivityLollipopAndAboveTest.java
@@ -56,7 +56,7 @@
                     public void run() {
                         ChromeLauncherActivity.launchDocumentInstance(null, false,
                                 ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND, "about:blank",
-                                DocumentMetricIds.STARTED_BY_UNKNOWN, 0, false, null);
+                                DocumentMetricIds.STARTED_BY_UNKNOWN, 0, null);
                     }
                 });
             }
diff --git a/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/MultiActivityTestBase.java b/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/MultiActivityTestBase.java
index f659e6e..c70d1c4 100644
--- a/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/MultiActivityTestBase.java
+++ b/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/MultiActivityTestBase.java
@@ -9,13 +9,17 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
+import android.text.TextUtils;
 
 import junit.framework.Assert;
 
 import org.chromium.base.ApplicationState;
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.UrlUtils;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.omaha.OmahaClient;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
@@ -27,6 +31,51 @@
         ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE
         })
 public abstract class MultiActivityTestBase extends RestrictedInstrumentationTestCase {
+    protected static final String URL_1 = createTestUrl(1);
+    protected static final String URL_2 = createTestUrl(2);
+    protected static final String URL_3 = createTestUrl(3);
+    protected static final String URL_4 = createTestUrl(4);
+
+    /** Defines one gigantic link spanning the whole page that creates a new window with URL_4. */
+    protected static final String HREF_LINK = UrlUtils.encodeHtmlDataUri(
+            "<html>"
+            + "  <head>"
+            + "    <title>href link page</title>"
+            + "    <meta name='viewport'"
+            + "        content='width=device-width initial-scale=0.5, maximum-scale=0.5'>"
+            + "    <style>"
+            + "      body {margin: 0em;} div {width: 100%; height: 100%; background: #011684;}"
+            + "    </style>"
+            + "  </head>"
+            + "  <body>"
+            + "    <a href='" + URL_4 + "' target='_blank'><div></div></a>"
+            + "  </body>"
+            + "</html>");
+
+    /** Clicking the body triggers a window.open() call to open URL_4. */
+    protected static final String SUCCESS_URL = UrlUtils.encodeHtmlDataUri("opened!");
+    protected static final String ONCLICK_LINK = UrlUtils.encodeHtmlDataUri(
+            "<html>"
+            + "  <head>"
+            + "    <title>window.open page</title>"
+            + "    <meta name='viewport'"
+            + "        content='width=device-width initial-scale=0.5, maximum-scale=0.5'>"
+            + "    <style>"
+            + "      body {margin: 0em;} div {width: 100%; height: 100%; background: #011684;}"
+            + "    </style>"
+            + "    <script>"
+            + "      function openNewWindow() {"
+            + "        if (window.open('" + URL_4 + "')) location.href = '" + SUCCESS_URL + "';"
+            + "      }"
+            + "    </script>"
+            + "  </head>"
+            + "  <body id='body'>"
+            + "    <div onclick='openNewWindow()'></div></a>"
+            + "  </body>"
+            + "</html>");
+
+    private static final float FLOAT_EPSILON = 0.001f;
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -116,4 +165,54 @@
         }));
     }
 
+    /**
+     * Approximates when a ChromeActivity is fully ready and loaded, which is hard to gauge
+     * because Android's Activity transition animations are not monitorable.
+     */
+    protected void waitForFullLoad(final ChromeActivity activity, final String expectedTitle)
+            throws Exception {
+        assertWaitForPageScaleFactorMatch(activity, 0.5f);
+        final Tab tab = activity.getActivityTab();
+        assert tab != null;
+
+        assertTrue(CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                if (!tab.isLoadingAndRenderingDone()) return false;
+                if (!TextUtils.equals(expectedTitle, tab.getTitle())) return false;
+                return true;
+            }
+        }));
+    }
+
+    /**
+     * Proper use of this function requires waiting for a page scale factor that isn't 1.0f because
+     * the default seems to be 1.0f.
+     * TODO(dfalcantara): Combine this one and ChromeActivityTestCaseBase's (crbug.com/498973)
+     */
+    private void assertWaitForPageScaleFactorMatch(
+            final ChromeActivity activity, final float expectedScale) throws Exception {
+        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                if (activity.getCurrentContentViewCore() == null) return false;
+
+                return Math.abs(activity.getCurrentContentViewCore().getScale() - expectedScale)
+                        < FLOAT_EPSILON;
+            }
+        }));
+    }
+
+    private static final String createTestUrl(int index) {
+        String[] colors = {"#000000", "#ff0000", "#00ff00", "#0000ff", "#ffff00"};
+        return UrlUtils.encodeHtmlDataUri(
+            "<html>"
+            + "  <head>"
+            + "    <title>Page " + index + "</title>"
+            + "    <meta name='viewport' content='width=device-width "
+            + "        initial-scale=0.5 maximum-scale=0.5'>"
+            + "  </head>"
+            + "  <body style='margin: 0em; background: " + colors[index] + ";'></body>"
+            + "</html>");
+    }
 }
diff --git a/chrome/test/base/history_index_restore_observer.h b/chrome/test/base/history_index_restore_observer.h
index fae0404..d15f066 100644
--- a/chrome/test/base/history_index_restore_observer.h
+++ b/chrome/test/base/history_index_restore_observer.h
@@ -8,7 +8,7 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "base/compiler_specific.h"
-#include "chrome/browser/autocomplete/in_memory_url_index.h"
+#include "components/omnibox/in_memory_url_index.h"
 
 // HistoryIndexRestoreObserver is used when blocking until the InMemoryURLIndex
 // finishes restoring. As soon as the InMemoryURLIndex finishes restoring the
diff --git a/chrome/test/base/testing_browser_process_platform_part.cc b/chrome/test/base/testing_browser_process_platform_part.cc
index ae9eb19d..f208b1dd 100644
--- a/chrome/test/base/testing_browser_process_platform_part.cc
+++ b/chrome/test/base/testing_browser_process_platform_part.cc
@@ -11,8 +11,8 @@
 }
 
 #if defined(OS_CHROMEOS)
-chromeos::OomPriorityManager*
-    TestingBrowserProcessPlatformPart::oom_priority_manager() {
+memory::OomPriorityManager*
+TestingBrowserProcessPlatformPart::oom_priority_manager() {
   return NULL;
 }
 #endif
diff --git a/chrome/test/base/testing_browser_process_platform_part.h b/chrome/test/base/testing_browser_process_platform_part.h
index 8fd9869..98a2258 100644
--- a/chrome/test/base/testing_browser_process_platform_part.h
+++ b/chrome/test/base/testing_browser_process_platform_part.h
@@ -17,7 +17,7 @@
 
 #if defined(OS_CHROMEOS)
   // Overridden from BrowserProcessPlatformPart:
-  chromeos::OomPriorityManager* oom_priority_manager() override;
+  memory::OomPriorityManager* oom_priority_manager() override;
 #endif
 
  private:
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 97ab29f..6984adc 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -15,7 +15,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/thread_task_runner_handle.h"
 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
-#include "chrome/browser/autocomplete/in_memory_url_index.h"
 #include "chrome/browser/autocomplete/in_memory_url_index_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/bookmarks/chrome_bookmark_client.h"
@@ -66,6 +65,7 @@
 #include "components/history/core/browser/history_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/core/refcounted_keyed_service.h"
+#include "components/omnibox/in_memory_url_index.h"
 #include "components/policy/core/common/policy_service.h"
 #include "components/ui/zoom/zoom_event_manager.h"
 #include "components/user_prefs/user_prefs.h"
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index 576bea2..c1ec10e 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -293,14 +293,15 @@
 Status WebViewImpl::DispatchMouseEvents(const std::list<MouseEvent>& events,
                                         const std::string& frame) {
   double page_scale_factor = 1.0;
-  if (browser_info_->build_no >= 2358 &&
+  if (browser_info_->build_no >= 2358 && browser_info_->build_no <= 2430 &&
       (browser_info_->is_android ||
        mobile_emulation_override_manager_->IsEmulatingTouch())) {
     // As of crrev.com/323900, on Android and under mobile emulation,
     // Input.dispatchMouseEvent fails to apply the page scale factor to the
     // mouse event coordinates. This leads to the MouseEvent being triggered on
-    // the wrong location on the page.
-    // TODO(samuong): remove once crbug.com/490157 is fixed on the browser side.
+    // the wrong location on the page. This was fixed on the browser side in
+    // crrev.com/333979.
+    // TODO(samuong): remove once we stop supporting M45.
     scoped_ptr<base::Value> value;
     Status status = EvaluateScript(
         std::string(), "window.screen.width / window.innerWidth;", &value);
@@ -577,8 +578,11 @@
   base::DictionaryValue params;
   params.SetInteger("x", x);
   params.SetInteger("y", y);
-  params.SetInteger("xdistance", xoffset);
-  params.SetInteger("ydistance", yoffset);
+  // Chrome's synthetic scroll gesture is actually a "swipe" gesture, so the
+  // direction of the swipe is opposite to the scroll (i.e. a swipe up scrolls
+  // down, and a swipe left scrolls right).
+  params.SetInteger("xDistance", -xoffset);
+  params.SetInteger("yDistance", -yoffset);
   return client_->SendCommand("Input.synthesizeScrollGesture", params);
 }
 
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 9d9258e..b70409e 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -975,13 +975,14 @@
   def testTouchScrollElement(self):
     self._driver.Load(self.GetHttpUrlForFile(
         '/chromedriver/touch_action_tests.html'))
+    scroll_left = 'return document.body.scrollLeft;'
+    scroll_top = 'return document.body.scrollTop;'
+    self.assertEquals(0, self._driver.ExecuteScript(scroll_left))
+    self.assertEquals(0, self._driver.ExecuteScript(scroll_top))
     events = self._driver.FindElement('id', 'events')
-    self.assertTrue(events.IsDisplayed())
-    xoffset = 0
-    yoffset = self._driver.ExecuteScript('return window.screen.height * 2;')
-    self._driver.TouchScroll(events, xoffset, yoffset)
-    bottom = self._driver.FindElement('id', 'bottom')
-    self.assertTrue(bottom.IsDisplayed())
+    self._driver.TouchScroll(events, 47, 53)
+    self.assertEquals(47, self._driver.ExecuteScript(scroll_left))
+    self.assertEquals(53, self._driver.ExecuteScript(scroll_top))
 
   def testTouchDoubleTapElement(self):
     self._driver.Load(self.GetHttpUrlForFile(
diff --git a/chrome/test/data/chromedriver/touch_action_tests.html b/chrome/test/data/chromedriver/touch_action_tests.html
index 9a1e15d..46c8c8c 100644
--- a/chrome/test/data/chromedriver/touch_action_tests.html
+++ b/chrome/test/data/chromedriver/touch_action_tests.html
@@ -26,6 +26,7 @@
       var padding = document.getElementById('padding');
       padding.style.border = 'solid';
       padding.style.height = window.screen.height * 2 + 'px';
+      padding.style.width = window.screen.width * 2 + 'px';
     </script>
   </body>
 </html>
diff --git a/chrome/test/data/downloads/image-octet-stream.png b/chrome/test/data/downloads/image-octet-stream.png
new file mode 100644
index 0000000..7a3361f
--- /dev/null
+++ b/chrome/test/data/downloads/image-octet-stream.png
Binary files differ
diff --git a/chrome/test/data/downloads/image-octet-stream.png.mock-http-headers b/chrome/test/data/downloads/image-octet-stream.png.mock-http-headers
new file mode 100644
index 0000000..2cbc087
--- /dev/null
+++ b/chrome/test/data/downloads/image-octet-stream.png.mock-http-headers
@@ -0,0 +1,4 @@
+HTTP/1.1 200 OK
+Cache-Control: no-cache
+Content-Type: application/octet-stream
+X-Content-Type-Options: nosniff
diff --git a/chrome/test/data/extensions/api_test/automation/sites/mixins.html b/chrome/test/data/extensions/api_test/automation/sites/attributes.html
similarity index 70%
rename from chrome/test/data/extensions/api_test/automation/sites/mixins.html
rename to chrome/test/data/extensions/api_test/automation/sites/attributes.html
index c27429d..5b8f63f9 100644
--- a/chrome/test/data/extensions/api_test/automation/sites/mixins.html
+++ b/chrome/test/data/extensions/api_test/automation/sites/attributes.html
@@ -5,12 +5,12 @@
 -->
 <html>
 <head>
-<title>Automation Tests - Mixin attributes</title>
+<title>Automation Tests - Attributes</title>
 </head>
 <body>
-  <!-- activedescendant mixin, owns default mixin-->
+  <!-- activedescendant attribute, owns default attribute-->
   <input type="text" aria-activedescendant="opt6" aria-readonly="true"
-         aria-owns="combobox-list" aria-autocomplete="list" role="combobox"
+         aria-autocomplete="list" role="combobox"
          id="combobox-edit">
   <ul aria-expanded="true" role="listbox" id="combobox-list">
     <li role="option" id="opt1">Alabama</li>
@@ -22,14 +22,14 @@
     <li role="option" id="opt7">Colorado</li>
   </ul>
 
-  <!-- link mixins -->
+  <!-- link attributes -->
   <a href="about://blank" id="real-link">Real link</a>
   <div role="link" id="link-role">ARIA link</div>
 
-  <!-- editable text mixins -->
-  <input type="text" value="Text input" id="text-input"></input>
-  <textarea id="textarea">Textarea</textarea>
-  <div tabindex="0" contenteditable role="textbox" id="textbox-role">
+  <!-- editable text attributes -->
+  <input type="text" value="Text input" aria-label="text-input" id="text-input"></input>
+  <textarea aria-label="textarea">Textarea</textarea>
+  <div tabindex="0" contenteditable role="textbox" aria-label="textbox-role">
     Textbox
   </div>
 
@@ -37,21 +37,22 @@
     document.querySelector('#text-input').setSelectionRange(2, 8);
   </script>
 
-  <!-- range mixins -->
-  <input type="range" id="range-input" max="5" value="4"></input>
+  <!-- range attributes -->
+  <input type="range" aria-label="range-input" max="5" value="4"></input>
   <div tabindex="0" role="slider" aria-valuemin="1" aria-valuemax="10"
-       aria-valuenow="7" aria-valuetext="seven stars" id="slider-role"></div>
+       aria-valuenow="7" aria-valuetext="seven stars"
+       aria-label="slider-role"></div>
   <div tabindex="0" role="spinbutton" aria-valuemin="1" aria-valuemax="31"
        aria-valuenow="14" id="spinbutton-role"></div>
   <div tabindex="0" role="progressbar" aria-valuemin="0" aria-valuenow="0.9"
-       aria-valuemax="1.0" id="progressbar-role"></div>
+       aria-valuemax="1.0" aria-label="progressbar-role"></div>
   <div tabindex="0" role="scrollbar" aria-valuemin="0" aria-valuenow="0"
        aria-valuemax="1.0" aria-orientation="vertical" aria-controls="main"
        id="scrollbar-role"></div>
 
-  <div id="main">Content for scrollbar to control</div>
+  <div id="main" aria-label="main">Content for scrollbar to control</div>
 
-  <!-- table and cell mixins -->
+  <!-- table and cell attributes -->
   <table id="table" role="grid">
     <tr role="row">
       <td role="cell">Cell spanning one column</td>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/tabs/mixins.html b/chrome/test/data/extensions/api_test/automation/tests/tabs/attributes.html
similarity index 84%
rename from chrome/test/data/extensions/api_test/automation/tests/tabs/mixins.html
rename to chrome/test/data/extensions/api_test/automation/tests/tabs/attributes.html
index f60d19c..c2eba0b 100644
--- a/chrome/test/data/extensions/api_test/automation/tests/tabs/mixins.html
+++ b/chrome/test/data/extensions/api_test/automation/tests/tabs/attributes.html
@@ -4,4 +4,4 @@
  * LICENSE file.
 -->
 <script src="common.js"></script>
-<script src="mixins.js"></script>
+<script src="attributes.js"></script>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/tabs/attributes.js b/chrome/test/data/extensions/api_test/automation/tests/tabs/attributes.js
new file mode 100644
index 0000000..0e0595e
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/automation/tests/tabs/attributes.js
@@ -0,0 +1,241 @@
+// 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.
+
+var ActiveDescendantAttribute = [ 'activedescendant' ];
+var LinkAttributes = [ 'url' ];
+var DocumentAttributes = [ 'docUrl',
+                           'docTitle',
+                           'docLoaded',
+                           'docLoadingProgress' ];
+var ScrollableAttributes = [ 'scrollX',
+                             'scrollXMin',
+                             'scrollXMax',
+                             'scrollY',
+                             'scrollYMin',
+                             'scrollYMax' ];
+var EditableTextAttributes = [ 'textSelStart',
+                               'textSelEnd' ];
+var RangeAttributes = [ 'valueForRange',
+                        'minValueForRange',
+                        'maxValueForRange' ];
+var TableAttributes = [ 'tableRowCount',
+                        'tableColumnCount' ];
+var TableCellAttributes = [ 'tableCellColumnIndex',
+                            'tableCellColumnSpan',
+                            'tableCellRowIndex',
+                            'tableCellRowSpan' ];
+
+var allTests = [
+  function testDocumentAndScrollAttributes() {
+    for (var i = 0; i < DocumentAttributes.length; i++) {
+      var attribute = DocumentAttributes[i];
+      assertTrue(attribute in rootNode,
+                 'rootNode should have a ' + attribute + ' attribute');
+    }
+    for (var i = 0; i < ScrollableAttributes.length; i++) {
+      var attribute = ScrollableAttributes[i];
+      assertTrue(attribute in rootNode,
+                 'rootNode should have a ' + attribute + ' attribute');
+    }
+
+    assertEq(url, rootNode.docUrl);
+    assertEq('Automation Tests - Attributes', rootNode.docTitle);
+    assertEq(true, rootNode.docLoaded);
+    assertEq(1, rootNode.docLoadingProgress);
+    assertEq(0, rootNode.scrollX);
+    assertEq(0, rootNode.scrollXMin);
+    assertEq(0, rootNode.scrollXMax);
+    assertEq(0, rootNode.scrollY);
+    assertEq(0, rootNode.scrollYMin);
+    assertEq(0, rootNode.scrollYMax);
+    chrome.test.succeed();
+  },
+
+  function testActiveDescendant() {
+    var combobox = rootNode.find({ role: 'comboBox' });
+    assertTrue('activedescendant' in combobox,
+               'combobox should have an activedescendant attribute');
+    var listbox = rootNode.find({ role: 'listBox' });
+    var opt6 = listbox.children[5];
+    assertEq(opt6, combobox.activedescendant);
+    chrome.test.succeed();
+  },
+
+  function testLinkAttributes() {
+    var links = rootNode.findAll({ role: 'link' });
+    assertEq(2, links.length);
+
+    var realLink = links[0];
+    assertEq('Real link', realLink.name);
+    assertTrue('url' in realLink, 'realLink should have a url attribute');
+    assertEq('about://blank', realLink.url);
+
+    var ariaLink = links[1];
+    assertEq('ARIA link', ariaLink.name);
+    assertTrue('url' in ariaLink, 'ariaLink should have an empty url');
+    assertEq(undefined, ariaLink.url);
+    chrome.test.succeed();
+  },
+
+  function testEditableTextAttributes() {
+    var textFields = rootNode.findAll({ role: 'textField' });
+    assertEq(3, textFields.length);
+    var EditableTextAttributes = [ 'textSelStart', 'textSelEnd' ];
+    for (var i = 0; i < textFields.length; i++) {
+      var textField = textFields[i];
+      var description = textField.description;
+      for (var j = 0; j < EditableTextAttributes.length; j++) {
+        var attribute = EditableTextAttributes[j];
+        assertTrue(attribute in textField,
+                   'textField (' + description + ') should have a ' +
+                   attribute + ' attribute');
+      }
+    }
+    var input = textFields[0];
+    assertEq('text-input', input.description);
+    assertEq(2, input.textSelStart);
+    assertEq(8, input.textSelEnd);
+
+    var textArea = textFields[1];
+    assertEq('textarea', textArea.description);
+    for (var i = 0; i < EditableTextAttributes.length; i++) {
+      var attribute = EditableTextAttributes[i];
+      assertTrue(attribute in textArea,
+                 'textArea should have a ' + attribute + ' attribute');
+    }
+    assertEq(0, textArea.textSelStart);
+    assertEq(0, textArea.textSelEnd);
+
+    var ariaTextbox = textFields[2];
+    assertEq('textbox-role', ariaTextbox.description);
+    assertEq(0, ariaTextbox.textSelStart);
+    assertEq(0, ariaTextbox.textSelEnd);
+
+    chrome.test.succeed();
+  },
+
+  function testRangeAttributes() {
+    var sliders = rootNode.findAll({ role: 'slider' });
+    assertEq(2, sliders.length);
+    var spinButtons = rootNode.findAll({ role: 'spinButton' });
+    assertEq(1, spinButtons.length);
+    var progressIndicators = rootNode.findAll({ role: 'progressIndicator' });
+    assertEq(1, progressIndicators.length);
+    assertEq('progressbar-role', progressIndicators[0].description);
+    var scrollBars = rootNode.findAll({ role: 'scrollBar' });
+    assertEq(1, scrollBars.length);
+
+    var ranges = sliders.concat(spinButtons, progressIndicators, scrollBars);
+    assertEq(5, ranges.length);
+
+    for (var i = 0; i < ranges.length; i++) {
+      var range = ranges[i];
+      for (var j = 0; j < RangeAttributes.length; j++) {
+        var attribute = RangeAttributes[j];
+        assertTrue(attribute in range,
+                   range.role + ' (' + range.description + ') should have a '
+                   + attribute + ' attribute');
+      }
+    }
+
+    var inputRange = sliders[0];
+    assertEq('range-input', inputRange.description);
+    assertEq(4, inputRange.valueForRange);
+    assertEq(0, inputRange.minValueForRange);
+    assertEq(5, inputRange.maxValueForRange);
+
+    var ariaSlider = sliders[1];
+    assertEq('slider-role', ariaSlider.description);
+    assertEq(7, ariaSlider.valueForRange);
+    assertEq(1, ariaSlider.minValueForRange);
+    assertEq(10, ariaSlider.maxValueForRange);
+
+    var spinButton = spinButtons[0];
+    assertEq(14, spinButton.valueForRange);
+    assertEq(1, spinButton.minValueForRange);
+    assertEq(31, spinButton.maxValueForRange);
+
+    assertEq('0.9', progressIndicators[0].valueForRange.toPrecision(1));
+    assertEq(0, progressIndicators[0].minValueForRange);
+    assertEq(1, progressIndicators[0].maxValueForRange);
+
+    assertEq(0, scrollBars[0].valueForRange);
+    assertEq(0, scrollBars[0].minValueForRange);
+    assertEq(1, scrollBars[0].maxValueForRange);
+
+    chrome.test.succeed();
+  },
+
+  function testTableAttributes() {
+    var table = rootNode.find({ role: 'table' });;
+    assertEq(3, table.tableRowCount);
+    assertEq(3, table.tableColumnCount);
+
+    var row1 = table.firstChild;
+    var cell1 = row1.firstChild;
+    assertEq(0, cell1.tableCellColumnIndex);
+    assertEq(1, cell1.tableCellColumnSpan);
+    assertEq(0, cell1.tableCellRowIndex);
+    assertEq(1, cell1.tableCellRowSpan);
+
+    var cell2 = cell1.nextSibling;
+    assertEq(1, cell2.tableCellColumnIndex);
+    assertEq(1, cell2.tableCellColumnSpan);
+    assertEq(0, cell2.tableCellRowIndex);
+    assertEq(1, cell2.tableCellRowSpan);
+
+    var cell3 = cell2.nextSibling;
+    assertEq(2, cell3.tableCellColumnIndex);
+    assertEq(1, cell3.tableCellColumnSpan);
+    assertEq(0, cell3.tableCellRowIndex);
+    assertEq(1, cell3.tableCellRowSpan);
+
+    var row2 = row1.nextSibling;
+    var cell4 = row2.firstChild;
+    assertEq(0, cell4.tableCellColumnIndex);
+    assertEq(2, cell4.tableCellColumnSpan);
+    assertEq(1, cell4.tableCellRowIndex);
+    assertEq(1, cell4.tableCellRowSpan);
+
+    var cell5 = cell4.nextSibling;
+    assertEq(2, cell5.tableCellColumnIndex);
+    assertEq(1, cell5.tableCellColumnSpan);
+    assertEq(1, cell5.tableCellRowIndex);
+    assertEq(2, cell5.tableCellRowSpan);
+
+    var row3 = row2.nextSibling;
+    var cell6 = row3.firstChild;
+    assertEq(0, cell6.tableCellColumnIndex);
+    assertEq(1, cell6.tableCellColumnSpan);
+    assertEq(2, cell6.tableCellRowIndex);
+    assertEq(1, cell6.tableCellRowSpan);
+
+    var cell7 = cell6.nextSibling;
+    assertEq(1, cell7.tableCellColumnIndex);
+    assertEq(1, cell7.tableCellColumnSpan);
+    assertEq(2, cell7.tableCellRowIndex);
+    assertEq(1, cell7.tableCellRowSpan);
+
+    chrome.test.succeed();
+  },
+
+  function testNoAttributes() {
+    var div = rootNode.find({ attributes: { description: 'main' } });
+    assertTrue(div !== undefined);
+    var allAttributes = [].concat(ActiveDescendantAttribute,
+                              LinkAttributes,
+                              DocumentAttributes,
+                              ScrollableAttributes,
+                              EditableTextAttributes,
+                              RangeAttributes,
+                              TableAttributes,
+                              TableCellAttributes);
+    for (var attributeAttr in allAttributes) {
+      assertFalse(attributeAttr in div);
+    }
+    chrome.test.succeed();
+  }
+];
+
+setUpAndRunTests(allTests, 'attributes.html');
diff --git a/chrome/test/data/extensions/api_test/automation/tests/tabs/mixins.js b/chrome/test/data/extensions/api_test/automation/tests/tabs/mixins.js
deleted file mode 100644
index b3ae61b..0000000
--- a/chrome/test/data/extensions/api_test/automation/tests/tabs/mixins.js
+++ /dev/null
@@ -1,246 +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.
-
-var ActiveDescendantMixin = [ 'activedescendant' ];
-var LinkMixins = [ 'url' ];
-var DocumentMixins = [ 'docUrl',
-                       'docTitle',
-                       'docLoaded',
-                       'docLoadingProgress' ];
-var ScrollableMixins = [ 'scrollX',
-                         'scrollXMin',
-                         'scrollXMax',
-                         'scrollY',
-                         'scrollYMin',
-                         'scrollYMax' ];
-var EditableTextMixins = [ 'textSelStart',
-                           'textSelEnd' ];
-var RangeMixins = [ 'valueForRange',
-                    'minValueForRange',
-                    'maxValueForRange' ];
-var TableMixins = [ 'tableRowCount',
-                    'tableColumnCount' ];
-var TableCellMixins = [ 'tableCellColumnIndex',
-                        'tableCellColumnSpan',
-                        'tableCellRowIndex',
-                        'tableCellRowSpan' ];
-
-var allTests = [
-  function testDocumentAndScrollMixins() {
-    for (var i = 0; i < DocumentMixins.length; i++) {
-      var mixinAttribute = DocumentMixins[i];
-      assertTrue(mixinAttribute in rootNode,
-                 'rootNode should have a ' + mixinAttribute + ' attribute');
-    }
-    for (var i = 0; i < ScrollableMixins.length; i++) {
-      var mixinAttribute = ScrollableMixins[i];
-      assertTrue(mixinAttribute in rootNode,
-                 'rootNode should have a ' + mixinAttribute + ' attribute');
-    }
-
-    assertEq(url, rootNode.docUrl);
-    assertEq('Automation Tests - Mixin attributes', rootNode.docTitle);
-    assertEq(true, rootNode.docLoaded);
-    assertEq(1, rootNode.docLoadingProgress);
-    assertEq(0, rootNode.scrollX);
-    assertEq(0, rootNode.scrollXMin);
-    assertEq(0, rootNode.scrollXMax);
-    assertEq(0, rootNode.scrollY);
-    assertEq(0, rootNode.scrollYMin);
-    assertEq(0, rootNode.scrollYMax);
-    chrome.test.succeed();
-  },
-
-  function testActiveDescendantAndOwns() {
-    var combobox = rootNode.find({ role: 'comboBox' });
-
-    assertTrue('owns' in combobox, 'combobox should have an owns attribute');
-    assertEq(1, combobox.owns.length);
-    var listbox = rootNode.find({ role: 'listBox' });
-    assertEq(listbox, combobox.owns[0]);
-
-    assertTrue('activedescendant' in combobox,
-               'combobox should have an activedescendant attribute');
-    var opt6 = listbox.children[5];
-    assertEq(opt6, combobox.activedescendant);
-    chrome.test.succeed();
-  },
-
-  function testLinkMixins() {
-    var links = rootNode.findAll({ role: 'link' });
-    assertEq(2, links.length);
-
-    var realLink = links[0];
-    assertEq('Real link', realLink.name);
-    assertTrue('url' in realLink, 'realLink should have a url attribute');
-    assertEq('about://blank', realLink.url);
-
-    var ariaLink = links[1];
-    assertEq('ARIA link', ariaLink.name);
-    assertTrue('url' in ariaLink, 'ariaLink should have a url attribute');
-    assertEq('', ariaLink.url);
-    chrome.test.succeed();
-  },
-
-  function testEditableTextMixins() {
-    var textFields = rootNode.findAll({ role: 'textField' });
-    assertEq(3, textFields.length);
-    var EditableTextMixins = [ 'textSelStart', 'textSelEnd' ];
-    for (var i = 0; i < textFields.length; i++) {
-      var textField = textFields[i];
-      var id = textField.attributes.id;
-      for (var j = 0; j < EditableTextMixins.length; j++) {
-        var mixinAttribute = EditableTextMixins[j];
-        assertTrue(mixinAttribute in textField,
-                   'textField (' + id + ') should have a ' + mixinAttribute +
-                   ' attribute');
-      }
-    }
-    var input = textFields[0];
-    assertEq('text-input', input.attributes.id);
-    assertEq(2, input.textSelStart);
-    assertEq(8, input.textSelEnd);
-
-    var textArea = textFields[1];
-    assertEq('textarea', textArea.attributes.id);
-    for (var i = 0; i < EditableTextMixins.length; i++) {
-      var mixinAttribute = EditableTextMixins[i];
-      assertTrue(mixinAttribute in textArea,
-                 'textArea should have a ' + mixinAttribute + ' attribute');
-    }
-    assertEq(0, textArea.textSelStart);
-    assertEq(0, textArea.textSelEnd);
-
-    var ariaTextbox = textFields[2];
-    assertEq('textbox-role', ariaTextbox.attributes.id);
-    assertEq(0, ariaTextbox.textSelStart);
-    assertEq(0, ariaTextbox.textSelEnd);
-
-    chrome.test.succeed();
-  },
-
-  function testRangeMixins() {
-    var sliders = rootNode.findAll({ role: 'slider' });
-    assertEq(2, sliders.length);
-    var spinButtons = rootNode.findAll({ role: 'spinButton' });
-    assertEq(1, spinButtons.length);
-    var progressIndicators = rootNode.findAll({ role: 'progressIndicator' });
-    assertEq(1, progressIndicators.length);
-    assertEq('progressbar-role', progressIndicators[0].attributes.id);
-    var scrollBars = rootNode.findAll({ role: 'scrollBar' });
-    assertEq(1, scrollBars.length);
-
-    var ranges = sliders.concat(spinButtons, progressIndicators, scrollBars);
-    assertEq(5, ranges.length);
-
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i];
-      for (var j = 0; j < RangeMixins.length; j++) {
-        var mixinAttribute = RangeMixins[j];
-        assertTrue(mixinAttribute in range,
-                   range.role + ' (' + range.attributes.id + ') should have a '
-                   + mixinAttribute + ' attribute');
-      }
-    }
-
-    var inputRange = sliders[0];
-    assertEq('range-input', inputRange.attributes.id);
-    assertEq(4, inputRange.valueForRange);
-    assertEq(0, inputRange.minValueForRange);
-    assertEq(5, inputRange.maxValueForRange);
-
-    var ariaSlider = sliders[1];
-    assertEq('slider-role', ariaSlider.attributes.id);
-    assertEq(7, ariaSlider.valueForRange);
-    assertEq(1, ariaSlider.minValueForRange);
-    assertEq(10, ariaSlider.maxValueForRange);
-
-    var spinButton = spinButtons[0];
-    assertEq(14, spinButton.valueForRange);
-    assertEq(1, spinButton.minValueForRange);
-    assertEq(31, spinButton.maxValueForRange);
-
-    assertEq('0.9', progressIndicators[0].valueForRange.toPrecision(1));
-    assertEq(0, progressIndicators[0].minValueForRange);
-    assertEq(1, progressIndicators[0].maxValueForRange);
-
-    assertEq(0, scrollBars[0].valueForRange);
-    assertEq(0, scrollBars[0].minValueForRange);
-    assertEq(1, scrollBars[0].maxValueForRange);
-
-    chrome.test.succeed();
-  },
-
-  function testTableMixins() {
-    var table = rootNode.find({ role: 'table' });;
-    assertEq(3, table.tableRowCount);
-    assertEq(3, table.tableColumnCount);
-
-    var row1 = table.firstChild;
-    var cell1 = row1.firstChild;
-    assertEq(0, cell1.tableCellColumnIndex);
-    assertEq(1, cell1.tableCellColumnSpan);
-    assertEq(0, cell1.tableCellRowIndex);
-    assertEq(1, cell1.tableCellRowSpan);
-
-    var cell2 = cell1.nextSibling;
-    assertEq(1, cell2.tableCellColumnIndex);
-    assertEq(1, cell2.tableCellColumnSpan);
-    assertEq(0, cell2.tableCellRowIndex);
-    assertEq(1, cell2.tableCellRowSpan);
-
-    var cell3 = cell2.nextSibling;
-    assertEq(2, cell3.tableCellColumnIndex);
-    assertEq(1, cell3.tableCellColumnSpan);
-    assertEq(0, cell3.tableCellRowIndex);
-    assertEq(1, cell3.tableCellRowSpan);
-
-    var row2 = row1.nextSibling;
-    var cell4 = row2.firstChild;
-    assertEq(0, cell4.tableCellColumnIndex);
-    assertEq(2, cell4.tableCellColumnSpan);
-    assertEq(1, cell4.tableCellRowIndex);
-    assertEq(1, cell4.tableCellRowSpan);
-
-    var cell5 = cell4.nextSibling;
-    assertEq(2, cell5.tableCellColumnIndex);
-    assertEq(1, cell5.tableCellColumnSpan);
-    assertEq(1, cell5.tableCellRowIndex);
-    assertEq(2, cell5.tableCellRowSpan);
-
-    var row3 = row2.nextSibling;
-    var cell6 = row3.firstChild;
-    assertEq(0, cell6.tableCellColumnIndex);
-    assertEq(1, cell6.tableCellColumnSpan);
-    assertEq(2, cell6.tableCellRowIndex);
-    assertEq(1, cell6.tableCellRowSpan);
-
-    var cell7 = cell6.nextSibling;
-    assertEq(1, cell7.tableCellColumnIndex);
-    assertEq(1, cell7.tableCellColumnSpan);
-    assertEq(2, cell7.tableCellRowIndex);
-    assertEq(1, cell7.tableCellRowSpan);
-
-    chrome.test.succeed();
-  },
-
-  function testNoMixins() {
-    var div = rootNode.find({ attributes: { id: 'main' } });
-    assertTrue(div !== undefined);
-    var allMixins = [].concat(ActiveDescendantMixin,
-                              LinkMixins,
-                              DocumentMixins,
-                              ScrollableMixins,
-                              EditableTextMixins,
-                              RangeMixins,
-                              TableMixins,
-                              TableCellMixins);
-    for (var mixinAttr in allMixins) {
-      assertFalse(mixinAttr in div);
-    }
-    chrome.test.succeed();
-  }
-];
-
-setUpAndRunTests(allTests, 'mixins.html');
diff --git a/chrome/test/data/extensions/api_test/automation/tests/tabs/sanity_check.js b/chrome/test/data/extensions/api_test/automation/tests/tabs/sanity_check.js
index 64c4e36..c45bd6e 100644
--- a/chrome/test/data/extensions/api_test/automation/tests/tabs/sanity_check.js
+++ b/chrome/test/data/extensions/api_test/automation/tests/tabs/sanity_check.js
@@ -5,48 +5,47 @@
 // Do not test orientation or hover attributes (similar to exclusions on native
 // accessibility), since they can be inconsistent depending on the environment.
 var RemoveUntestedStates = function(state) {
-  delete state[StateType.horizontal];
-  delete state[StateType.hovered];
-  delete state[StateType.vertical];
+  var result = JSON.parse(JSON.stringify(state));
+  delete result[StateType.horizontal];
+  delete result[StateType.hovered];
+  delete result[StateType.vertical];
+  return result;
 };
 
 var allTests = [
   function testSimplePage() {
     var title = rootNode.docTitle;
     assertEq('Automation Tests', title);
-    RemoveUntestedStates(rootNode.state);
+
+    var state = RemoveUntestedStates(rootNode.state);
     assertEq(
-      {enabled: true, focusable: true, readOnly: true},
-      rootNode.state);
+        {enabled: true, focusable: true, readOnly: true},
+        state);
+
     var children = rootNode.children;
     assertEq(RoleType.rootWebArea, rootNode.role);
     assertEq(1, children.length);
     var body = children[0];
     assertEq('body', body.htmlTag);
-
-    RemoveUntestedStates(body.state);
-    assertEq({enabled: true, readOnly: true},
-               body.state);
+    state = RemoveUntestedStates(body.state);
+    assertEq({enabled: true, readOnly: true}, state);
 
     var contentChildren = body.children;
     assertEq(3, contentChildren.length);
     var okButton = contentChildren[0];
     assertEq('Ok', okButton.name);
-    RemoveUntestedStates(okButton.state);
-    assertEq({enabled: true, focusable: true, readOnly: true},
-             okButton.state);
+    state = RemoveUntestedStates(okButton.state);
+    assertEq({enabled: true, focusable: true, readOnly: true}, state);
     var userNameInput = contentChildren[1];
     assertEq('Username',
              userNameInput.description);
-    RemoveUntestedStates(userNameInput.state);
-    assertEq({enabled: true, focusable: true},
-             userNameInput.state);
+    state = RemoveUntestedStates(userNameInput.state);
+    assertEq({enabled: true, focusable: true}, state);
     var cancelButton = contentChildren[2];
     assertEq('Cancel',
              cancelButton.name);
-    RemoveUntestedStates(cancelButton.state);
-    assertEq({enabled: true, focusable: true, readOnly: true},
-             cancelButton.state);
+    state = RemoveUntestedStates(cancelButton.state);
+    assertEq({enabled: true, focusable: true, readOnly: true}, state);
 
     // Traversal.
     assertEq(undefined, rootNode.parent);
diff --git a/chrome/test/data/extensions/api_test/automation/tests/tabs/tree_change.js b/chrome/test/data/extensions/api_test/automation/tests/tabs/tree_change.js
index dcb1861b..19a1416 100644
--- a/chrome/test/data/extensions/api_test/automation/tests/tabs/tree_change.js
+++ b/chrome/test/data/extensions/api_test/automation/tests/tabs/tree_change.js
@@ -5,7 +5,7 @@
 var allTests = [
   function testTreeChangedObserverForCreatingNode() {
     chrome.automation.addTreeChangeObserver(function(change) {
-      if (change.type == "nodeCreated" && change.target.name == "New") {
+      if (change.type == "subtreeCreated" && change.target.name == "New") {
         chrome.test.succeed();
       }
     });
diff --git a/chrome/test/data/extensions/api_test/automation/tests/unit/manifest.json b/chrome/test/data/extensions/api_test/automation/tests/unit/manifest.json
deleted file mode 100644
index 867927b..0000000
--- a/chrome/test/data/extensions/api_test/automation/tests/unit/manifest.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "name": "chrome.automation.unit",
-  "version": "0.1",
-  "manifest_version": 2,
-  "description": "Unittests for chrome.automation.",
-  "permissions": ["tabs", "http://a.com/"],
-  "automation": true
-}
diff --git a/chrome/test/data/extensions/api_test/automation/tests/unit/test.js b/chrome/test/data/extensions/api_test/automation/tests/unit/test.js
deleted file mode 100644
index 95a2cab..0000000
--- a/chrome/test/data/extensions/api_test/automation/tests/unit/test.js
+++ /dev/null
@@ -1,507 +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.
-
-chrome.test.runWithModuleSystem(function(moduleSystem) {
-  window.AutomationRootNode =
-      moduleSystem.require('automationNode').AutomationRootNode;
-  window.privates = moduleSystem.privates;
-  // Unused.
-  window.automationUtil = function() {};
-  window.automationUtil.storeTreeCallback = function() {};
-  window.automationUtil.treeChangeObservers = [];
-});
-
-var assertEq = chrome.test.assertEq;
-var assertFalse = chrome.test.assertFalse;
-var assertTrue = chrome.test.assertTrue;
-var assertIsDef = function(obj) {
-  assertTrue(obj !== null && obj !== undefined);
-}
-var assertIsNotDef = function(obj) {
-  assertTrue(obj === null || obj === undefined);
-}
-var succeed = chrome.test.succeed;
-
-var tests = [
-  function testAutomationRootNode() {
-    var root = new AutomationRootNode();
-    assertTrue(root.isRootNode);
-
-    succeed();
-  },
-
-  function testAriaRelationshipAttributes() {
-    var data = {
-      'eventType': 'loadComplete',
-          'processID': 17, 'routingID': 2, 'targetID': 1,
-      'update': { 'nodeIdToClear': 0, 'nodes': [
-        {
-          'id': 1, 'role': 'rootWebArea',
-          'boolAttributes': {'docLoaded': true },
-          'childIds': [5, 6, 7, 8, 9, 10, 11]
-        },
-        {
-          'id': 11,        'role': 'div',
-          'childIds': [],
-          'htmlAttributes': {'id': 'target' }
-        },
-        {
-          'id': 5, 'role': 'div',
-          'childIds': [],
-          'htmlAttributes': {'aria-activedescendant': 'target',
-                             'id': 'activedescendant'},
-          'intAttributes': {'activedescendantId': 11},
-        },
-        {
-          'id': 6, 'role': 'div',
-          'childIds': [],
-          'htmlAttributes': {'aria-controls': 'target', 'id': 'controlledBy'},
-          'intlistAttributes': {'controlsIds': [11]},
-        },
-        {
-          'id': 7, 'role': 'div',
-          'childIds': [],
-          'htmlAttributes': {'aria-describedby': 'target', 'id': 'describedBy'},
-          'intlistAttributes': {'describedbyIds': [11, 6]},
-        },
-        {
-          'id': 8, 'role': 'div',
-          'childIds': [],
-          'htmlAttributes': {'aria-flowto': 'target', 'id': 'flowTo'},
-          'intlistAttributes': {'flowtoIds': [11]},
-        },
-        {
-          'id': 9, 'role': 'div',
-          'childIds': [],
-          'htmlAttributes': {'aria-labelledby': 'target', 'id': 'labelledBy'},
-          'intlistAttributes': {'labelledbyIds': [11]}
-        },
-        {
-          'id': 10, 'role': 'div',
-          'childIds': [],
-          'htmlAttributes': {'aria-owns': 'target', 'id': 'owns'},
-          'intlistAttributes': {'ownsIds': [11]},
-        }
-      ]}
-    }
-
-    var tree = new AutomationRootNode();
-    try {
-    privates(tree).impl.onAccessibilityEvent(data);
-    } catch (e) {
-      console.log(e.stack);
-    }
-
-    var activedescendant = tree.firstChild;
-    assertIsDef(activedescendant);
-    assertEq('activedescendant', activedescendant.attributes.id);
-    assertEq(
-        'target',
-        activedescendant.attributes['aria-activedescendant'].attributes.id);
-
-    assertFalse(activedescendant.activedescendant == null,
-                'activedescendant should not be null');
-    assertEq(
-        'target',
-        activedescendant.activedescendant.attributes.id);
-    assertIsNotDef(activedescendant.attributes.activedescendantId);
-
-    var controlledBy = activedescendant.nextSibling;
-    assertIsDef(controlledBy);
-    assertEq('controlledBy', controlledBy.attributes.id);
-    assertEq(1, controlledBy.attributes['aria-controls'].length);
-    assertEq('target',
-             controlledBy.attributes['aria-controls'][0].attributes.id);
-    assertEq(1, controlledBy.controls.length);
-    assertEq('target', controlledBy.controls[0].attributes.id);
-    assertIsNotDef(controlledBy.attributes.controlledbyIds);
-
-    var describedBy = controlledBy.nextSibling;
-    assertIsDef(describedBy);
-    assertEq('describedBy', describedBy.attributes.id);
-    assertEq(2, describedBy.attributes['aria-describedby'].length);
-    assertEq('target',
-             describedBy.attributes['aria-describedby'][0].attributes.id);
-    assertEq('controlledBy',
-             describedBy.attributes['aria-describedby'][1].attributes.id);
-    assertEq(2, describedBy.describedby.length);
-    assertEq('target', describedBy.describedby[0].attributes.id);
-    assertEq('controlledBy',
-             describedBy.describedby[1].attributes.id);
-    assertIsNotDef(describedBy.attributes.describedbyIds);
-
-    var flowTo = describedBy.nextSibling;
-    assertIsDef(flowTo);
-    assertEq('flowTo', flowTo.attributes.id);
-    assertEq(1, flowTo.attributes['aria-flowto'].length);
-    assertEq('target',
-             flowTo.attributes['aria-flowto'][0].attributes.id);
-    assertEq(1, flowTo.flowto.length);
-    assertEq('target', flowTo.flowto[0].attributes.id);
-    assertIsNotDef(flowTo.attributes.flowtoIds);
-
-    var labelledBy = flowTo.nextSibling;
-    assertIsDef(labelledBy);
-    assertEq('labelledBy', labelledBy.attributes.id);
-    assertEq(1, labelledBy.attributes['aria-labelledby'].length);
-    assertEq('target',
-             labelledBy.attributes['aria-labelledby'][0].attributes.id);
-    assertEq(1, labelledBy.labelledby.length);
-    assertEq('target',
-             labelledBy.labelledby[0].attributes.id);
-    assertIsNotDef(labelledBy.attributes.labelledbyIds);
-
-    var owns = labelledBy.nextSibling;
-    assertIsDef(owns);
-    assertEq('owns', owns.attributes.id);
-    assertEq(1, owns.attributes['aria-owns'].length);
-    assertEq('target', owns.attributes['aria-owns'][0].attributes.id);
-    assertEq(1, owns.owns.length);
-    assertEq('target', owns.owns[0].attributes.id);
-    assertIsNotDef(owns.attributes.ownsIds);
-
-    succeed();
-  },
-
-  function testCannotSetAttribute() {
-    var update =
-        {
-          'nodeIdToClear': 0, 'nodes': [
-            {
-              'id': 1, 'role': 'rootWebArea',
-              'boolAttributes': {'docLoaded': true},
-              'childIds': [11]
-            },
-            {
-              'id': 11, 'role': 'button',
-              'stringAttributes': {'name': 'foo'},
-              'childIds': []
-            }]
-        }
-
-    var tree = new AutomationRootNode();
-    assertTrue(privates(tree).impl.unserialize(update));
-    var button = tree.firstChild;
-    assertEq('button', button.role);
-    assertEq('foo', button.name);
-    button.name = 'bar';
-    assertEq('foo', button.name);
-
-    succeed();
-  },
-
-  function testBadUpdateInvalidChildIds() {
-    var update =
-        {
-          'nodeIdToClear': 0, 'nodes': [
-            {
-              'id': 1, 'role': 'rootWebArea',
-              'boolAttributes': {'docLoaded': true},
-              'childIds': [5, 6, 7, 8, 9, 10, 11]
-            }]
-        }
-
-    var tree = new AutomationRootNode();
-
-    // This is a bad update because the root references non existent child ids.
-    assertFalse(privates(tree).impl.unserialize(update));
-
-    succeed();
-  },
-
-  function testMultipleUpdateNameChanged() {
-    var update =
-        {
-          'nodeIdToClear': 0, 'nodes': [
-            {
-              'id': 1, 'role': 'rootWebArea',
-              'boolAttributes': {'docLoaded': true},
-              'childIds': [11]
-            },
-            {
-              'id': 11, 'role': 'button',
-              'stringAttributes': {'name': 'foo'},
-              'childIds': []
-            }]
-        }
-
-    // First, setup the initial tree.
-    var tree = new AutomationRootNode();
-    assertTrue(privates(tree).impl.unserialize(update));
-    var button = tree.firstChild;
-    assertEq('button', button.role);
-    assertEq('foo', button.name);
-
-    // Now, apply an update that changes the button's name.
-    // Remove the root since the native serializer stops at the LCA.
-    update.nodes.splice(0, 1);
-    update.nodes[0].stringAttributes.name = 'bar';
-
-    // Make sure the name changes.
-    assertTrue(privates(tree).impl.unserialize(update));
-    assertEq('bar', button.name);
-
-    succeed();
-  },
-
-  function testDocumentAndScrollableMixins() {
-    var update = { 'nodeIdToClear': 0, 'nodes': [
-        {
-          'id': 1, 'role': 'rootWebArea',
-          'boolAttributes': { 'docLoaded': false },
-          'stringAttributes': { 'docUrl': 'chrome://terms',
-                                'docTitle': 'Google Chrome Terms of Service' },
-          'intAttributes': { 'scrollY': 583,
-                             'scrollYMax': 9336 },
-          'floatAttributes': { 'docLoadingProgress': 0.9 },
-          'childIds': [2]
-        },
-        {
-          'id': 2, 'role': 'div',
-          'childIds': [],
-          'htmlAttributes': { 'id': 'child' },
-        },
-    ] };
-
-    var tree = new AutomationRootNode();
-    assertTrue(privates(tree).impl.unserialize(update));
-    assertEq(false, tree.docLoaded);
-    assertEq('chrome://terms', tree.docUrl);
-    assertEq('Google Chrome Terms of Service', tree.docTitle);
-    assertEq('0.9', tree.docLoadingProgress.toPrecision(1));
-    assertEq(583, tree.scrollY);
-    assertEq(9336, tree.scrollYMax);
-    // Default values will be set for mixin attributes even if not in data.
-    assertEq(0, tree.scrollYMin);
-    assertEq(0, tree.scrollX);
-    assertEq(0, tree.scrollXMin);
-    assertEq(0, tree.scrollXMax);
-
-    succeed();
-  },
-
-  function testEditableTextMixins() {
-    var update = { 'nodeIdToClear': 0, 'nodes': [
-        {
-          'id': 1, 'role': 'rootWebArea',
-          'boolAttributes': { 'docLoaded': true },
-          'stringAttributes': { 'docUrl': 'chrome://terms',
-                                'docTitle': 'Google Chrome Terms of Service' },
-          'intAttributes': { 'scrollY': 583,
-                             'scrollYMax': 9336 },
-          'childIds': [2, 3]
-        },
-        {
-          'id': 2, 'role': 'textField',
-          'intAttributes': { 'textSelStart': 10, 'textSelEnd': 20 },
-          'childIds': []
-        },
-        {
-          'id': 3, 'role': 'textField',
-          'childIds': []
-        },
-
-    ] };
-
-    var tree = new AutomationRootNode();
-    assertTrue(privates(tree).impl.unserialize(update));
-    assertEq(true, tree.docLoaded);
-    assertFalse('textSelStart' in tree);
-    assertFalse('textSelEnd' in tree);
-    var textField = tree.firstChild;
-    assertEq(10, textField.textSelStart);
-    assertEq(20, textField.textSelEnd);
-    var textArea = textField.nextSibling;
-    assertEq(-1, textArea.textSelStart);
-    assertEq(-1, textArea.textSelEnd);
-
-    succeed();
-  },
-
-  function testRangeMixins() {
-    var update = { 'nodeIdToClear': 0, 'nodes': [
-        {
-          'id': 1, 'role': 'rootWebArea',
-          'boolAttributes': { 'docLoaded': true },
-          'stringAttributes': { 'docUrl': 'chrome://terms',
-                                'docTitle': 'Google Chrome Terms of Service' },
-          'intAttributes': { 'scrollY': 583,
-                             'scrollYMax': 9336 },
-          'childIds': [2, 3, 4, 5]
-        },
-        {
-          'id': 2, 'role': 'progressIndicator',
-          'floatAttributes': { 'valueForRange': 1.0,
-                               'minValueForRange': 0.0,
-                               'maxValueForRange': 1.0 },
-          'childIds': []
-        },
-        {
-          'id': 3, 'role': 'scrollBar',
-          'floatAttributes': { 'valueForRange': 0.3,
-                               'minValueForRange': 0.0,
-                               'maxValueForRange': 1.0 },
-          'childIds': []
-        },
-        {
-          'id': 4, 'role': 'slider',
-          'floatAttributes': { 'valueForRange': 3.0,
-                               'minValueForRange': 1.0,
-                               'maxValueForRange': 5.0 },
-          'childIds': []
-        },
-        {
-          'id': 5, 'role': 'spinButton',
-          'floatAttributes': { 'valueForRange': 14.0,
-                               'minValueForRange': 1.0,
-                               'maxValueForRange': 31.0 },
-          'childIds': []
-        }
-    ] };
-
-    var tree = new AutomationRootNode();
-    assertTrue(privates(tree).impl.unserialize(update));
-    assertEq(true, tree.docLoaded);
-    assertFalse('valueForRange' in tree);
-    assertFalse('minValueForRange' in tree);
-    assertFalse('maxValueForRange' in tree);
-
-    var progressIndicator = tree.firstChild;
-    assertEq(1.0, progressIndicator.valueForRange);
-    assertEq(0.0, progressIndicator.minValueForRange);
-    assertEq(1.0, progressIndicator.maxValueForRange);
-
-    var scrollBar = progressIndicator.nextSibling;
-    assertEq(0.3, scrollBar.valueForRange);
-    assertEq(0.0, scrollBar.minValueForRange);
-    assertEq(1.0, scrollBar.maxValueForRange);
-
-    var slider = scrollBar.nextSibling;
-    assertEq(3.0, slider.valueForRange);
-    assertEq(1.0, slider.minValueForRange);
-    assertEq(5.0, slider.maxValueForRange);
-
-    var spinButton = slider.nextSibling;
-    assertEq(14.0, spinButton.valueForRange);
-    assertEq(1.0, spinButton.minValueForRange);
-    assertEq(31.0, spinButton.maxValueForRange);
-
-    succeed();
-  },
-
-  function testTableMixins() {
-        var update = { 'nodeIdToClear': 0, 'nodes': [
-        {
-          'id': 1, 'role': 'rootWebArea',
-          'boolAttributes': { 'docLoaded': true },
-          'stringAttributes': { 'docUrl': 'chrome://terms',
-                                'docTitle': 'Google Chrome Terms of Service' },
-          'intAttributes': { 'scrollY': 583,
-                             'scrollYMax': 9336 },
-          'childIds': [2]
-        },
-        {
-          'id': 2, 'role': 'table',
-          'childIds': [3, 6],
-          'intAttributes': { tableRowCount: 2, tableColumnCount: 3 }
-        },
-        {
-          'id': 3, 'role': 'row',
-          'childIds': [4, 5]
-        },
-        {
-          'id': 4, 'role': 'cell',
-          'intAttributes': { 'tableCellColumnIndex': 0,
-                             'tableCellColumnSpan': 2,
-                             'tableCellRowIndex': 0,
-                             'tableCellRowSpan': 1 },
-          'childIds': []
-        },
-        {
-          'id': 5, 'role': 'cell',
-          'intAttributes': { 'tableCellColumnIndex': 2,
-                             'tableCellColumnSpan': 1,
-                             'tableCellRowIndex': 0,
-                             'tableCellRowSpan': 2 },
-          'childIds': []
-        },
-        {
-          'id': 6, 'role': 'row',
-          'childIds': [7, 8]
-        },
-        {
-          'id': 7, 'role': 'cell',
-          'intAttributes': { 'tableCellColumnIndex': 0,
-                             'tableCellColumnSpan': 1,
-                             'tableCellRowIndex': 1,
-                             'tableCellRowSpan': 1 },
-          'childIds': []
-        },
-        {
-          'id': 8, 'role': 'cell',
-          'intAttributes': { 'tableCellColumnIndex': 1,
-                             'tableCellColumnSpan': 1,
-                             'tableCellRowIndex': 1,
-                             'tableCellRowSpan': 1 },
-          'childIds': []
-        }
-    ] };
-
-    var tree = new AutomationRootNode();
-    try {
-    assertTrue(privates(tree).impl.unserialize(update));
-    } catch (e) {
-      console.log(e.stack);
-    }
-    var TableMixinAttributes = {
-      tableRowCount: 0,
-      tableColumnCount: 0
-    };
-    for (var attribute in TableMixinAttributes)
-      assertFalse(attribute in tree);
-
-    var TableCellMixinAttributes = {
-      tableCellColumnIndex: 0,
-      tableCellColumnSpan: 1,
-      tableCellRowIndex: 0,
-      tableCellRowSpan: 1
-    };
-    for (var attribute in TableCellMixinAttributes)
-      assertFalse(attribute in tree);
-
-    var table = tree.firstChild;
-    assertEq(2, table.tableRowCount);
-    assertEq(3, table.tableColumnCount);
-
-    var row1 = table.firstChild;
-    var cell1 = row1.firstChild;
-    assertEq(0, cell1.tableCellColumnIndex);
-    assertEq(2, cell1.tableCellColumnSpan);
-    assertEq(0, cell1.tableCellRowIndex);
-    assertEq(1, cell1.tableCellRowSpan);
-
-    var cell2 = cell1.nextSibling;
-    assertEq(2, cell2.tableCellColumnIndex);
-    assertEq(1, cell2.tableCellColumnSpan);
-    assertEq(0, cell2.tableCellRowIndex);
-    assertEq(2, cell2.tableCellRowSpan);
-
-    var row2 = row1.nextSibling;
-    var cell3 = row2.firstChild;
-    assertEq(0, cell3.tableCellColumnIndex);
-    assertEq(1, cell3.tableCellColumnSpan);
-    assertEq(1, cell3.tableCellRowIndex);
-    assertEq(1, cell3.tableCellRowSpan);
-
-    var cell4 = cell3.nextSibling;
-    assertEq(1, cell4.tableCellColumnIndex);
-    assertEq(1, cell4.tableCellColumnSpan);
-    assertEq(1, cell4.tableCellRowIndex);
-    assertEq(1, cell4.tableCellRowSpan);
-
-    succeed();
-  }
-];
-
-chrome.test.runTests(tests);
diff --git a/chrome/test/data/extensions/api_test/automation/tests/unit/unit.html b/chrome/test/data/extensions/api_test/automation/tests/unit/unit.html
deleted file mode 100644
index 77bad90..0000000
--- a/chrome/test/data/extensions/api_test/automation/tests/unit/unit.html
+++ /dev/null
@@ -1,6 +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.
--->
-<script src="test.js"></script>
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 f8d97c1..6241908 100644
--- a/chrome/test/data/extensions/api_test/platform_keys/basic.js
+++ b/chrome/test/data/extensions/api_test/platform_keys/basic.js
@@ -589,6 +589,43 @@
     ];
     chrome.test.runTests(tests);
   },
+
+  corporateKeyWithoutPermissionTests: function() {
+    var tests = [
+      // Directly trying to sign must fail
+      testSignClient1Fails,
+
+      // Interactively selecting must not show any cert to the user.
+      testInteractiveSelectNoCerts,
+    ];
+    chrome.test.runTests(tests);
+  },
+
+  corporateKeyWithPermissionTests: function() {
+    var tests = [
+      // The extension has non-interactive access to all corporate keys, even
+      // without previous additional consent of the user.
+      testSignSha1Client1,
+
+      // Interactively selecting for client_1 will work as well.
+      testInteractiveSelectClient1,
+    ];
+    chrome.test.runTests(tests);
+  },
+
+  policyDoesGrantAccessToNonCorporateKey: function() {
+    // The permission from policy must not affect usage of non-corproate keys.
+    var tests = [
+      // Attempts to sign must fail.
+      testSignClient1Fails,
+
+      // Interactive selection must not prompt the user and not return any
+      // certificate.
+      testInteractiveSelectNoCerts,
+    ];
+    chrome.test.runTests(tests);
+  },
+
 };
 
 setUp(testSuites[selectedTestSuite]);
diff --git a/chrome/test/data/extensions/api_test/platform_keys_genkey/main.html b/chrome/test/data/extensions/api_test/platform_keys_genkey/main.html
new file mode 100644
index 0000000..87520a9
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/platform_keys_genkey/main.html
@@ -0,0 +1,5 @@
+<!--
+ * 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.
+-->
diff --git a/chrome/test/data/extensions/api_test/platform_keys_genkey/manifest.json b/chrome/test/data/extensions/api_test/platform_keys_genkey/manifest.json
new file mode 100644
index 0000000..57adc800
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/platform_keys_genkey/manifest.json
@@ -0,0 +1,5 @@
+{
+  "name": "Fake key generating extension",
+  "version": "0.1",
+  "manifest_version": 2
+}
diff --git a/chrome/test/data/extensions/extension_throttle/test_request_eventually_throttled.js b/chrome/test/data/extensions/extension_throttle/test_request_eventually_throttled.js
index ac3d158..1e3b4bf 100644
--- a/chrome/test/data/extensions/extension_throttle/test_request_eventually_throttled.js
+++ b/chrome/test/data/extensions/extension_throttle/test_request_eventually_throttled.js
@@ -6,28 +6,21 @@
 // (and a lot of other test code) to it. https://crbug.com/303152
 var url = decodeURIComponent(/url=([^&]*)/.exec(location.search)[1]);
 var filter = {urls: ['http://www.example.com/*'], types: ['xmlhttprequest']};
-var numSuccessfulRequests = 0;
-var numThrottledRequests = 0;
+var numRequests = 0;
 
 chrome.webRequest.onCompleted.addListener(function(details) {
-  numSuccessfulRequests++;
-  chrome.test.assertEq(0, numThrottledRequests);
   chrome.test.assertEq(503, details.statusCode);
+  numRequests++;
   chrome.runtime.sendMessage({type: 'xhr', method: 'GET', url: url});
 }, filter);
 
 chrome.webRequest.onErrorOccurred.addListener(function(details) {
-  numThrottledRequests++;
-  // Should not throttle the first request.
-  chrome.test.assertTrue(numSuccessfulRequests > 1);
+  // Should thottle the third request, which is one request after
+  // an error is seen.
+  chrome.test.assertEq(3, numRequests);
   chrome.test.assertEq('net::ERR_TEMPORARILY_THROTTLED', details.error);
-  // Send 10 requests in a row so that backoff time is large enough to avoid
-  // flakiness on slow bots. See: crbug.com/501362.
-  if (numThrottledRequests < 10) {
-    chrome.runtime.sendMessage({type: 'xhr', method: 'GET', url: url});
-  } else {
-    chrome.test.notifyPass();
-  }
+  chrome.test.notifyPass();
 }, filter);
 
+numRequests++;
 chrome.runtime.sendMessage({type: 'xhr', method: 'GET', url: url});
diff --git a/chrome/test/data/extensions/extension_throttle/test_request_not_throttled.js b/chrome/test/data/extensions/extension_throttle/test_request_not_throttled.js
index 457beb8..6c4ef6f 100644
--- a/chrome/test/data/extensions/extension_throttle/test_request_not_throttled.js
+++ b/chrome/test/data/extensions/extension_throttle/test_request_not_throttled.js
@@ -10,16 +10,19 @@
 
 chrome.webRequest.onCompleted.addListener(function(details) {
   chrome.test.assertEq(503, details.statusCode);
-  if (numRequests == 20) {
+  // If the third request goes through, it means that throttling logic did not
+  // apply.
+  if (numRequests == 3) {
      chrome.test.notifyPass();
   } else {
      numRequests++;
      chrome.runtime.sendMessage({type: 'xhr', method: 'GET', url: url});
-   }
+  }
 }, filter);
 
 chrome.webRequest.onErrorOccurred.addListener(function(details) {
   chrome.test.notifyFail('Unexpected error');
 }, filter);
 
+numRequests++;
 chrome.runtime.sendMessage({type: 'xhr', method: 'GET', url: url});
diff --git a/chrome/test/data/is_search_provider_installed.html b/chrome/test/data/is_search_provider_installed.html
index 23b64f14..f23a14ef 100644
--- a/chrome/test/data/is_search_provider_installed.html
+++ b/chrome/test/data/is_search_provider_installed.html
@@ -39,53 +39,58 @@
   }
 }
 
-try {
-  var differentProtocol =
-      document.location.protocol == "http:" ? "https:" : "http:";
-  var differentPort =
-      (!document.location.port || document.location.port == "80") ? ":81" : ":80";
-
-  var origin = document.location.protocol + "//" + document.location.host + "/";
-  var originWithDifferentProtocol = differentProtocol + "//" +
-      document.location.host + "/";
-  var originWithDifferentPort = document.location.protocol + "//" +
-      document.location.hostname + differentPort + "/";
-
-  // Verify existence of the api.
-  var foundApi = false;
+function main() {
   try {
-    if (window.external.IsSearchProviderInstalled)
-      foundApi = true;
-  } catch (e) {
-  }
+    var differentProtocol =
+        document.location.protocol == "http:" ? "https:" : "http:";
+    var differentPort =
+        (!document.location.port || document.location.port == "80") ? ":81" : ":80";
 
-  if (foundApi)
-    logPassed("IsSearchProvider api exists.");
-  else {
-    logFailed("IsSearchProvider api doesn't exist.");
+    var origin =
+        document.location.protocol + "//" + document.location.host + "/";
+    var originWithDifferentProtocol = differentProtocol + "//" +
+        document.location.host + "/";
+    var originWithDifferentPort = document.location.protocol + "//" +
+        document.location.hostname + differentPort + "/";
+
+    // Verify existence of the api.
+    var foundApi = false;
+    try {
+      if (window.external.IsSearchProviderInstalled)
+        foundApi = true;
+    } catch (e) {}
+
+    if (foundApi) {
+      logPassed("IsSearchProvider api exists.");
+    } else {
+      logFailed("IsSearchProvider api doesn't exist.");
+      writeResult();
+      return;
+    }
+
+    // Verify the search provider state for the current page.
+    var installed = window.external.IsSearchProviderInstalled(origin)
+    var installedMessage = "Search provider ("+ origin +"): " + installed + ".";
+    if (installed == document.location.hash.substring(1)) {
+      logPassed(installedMessage);
+    } else {
+      logFailed(installedMessage +
+          " The expected result is passed as the hash.");
+    }
+
+    // Verify that cases that should result in exceptions.
+    verifyExceptionFor("different host", "http://example.org/");
+    verifyExceptionFor("different protocol", originWithDifferentProtocol);
+    verifyExceptionFor("different port", originWithDifferentPort);
+
     writeResult();
-    return;
+  } catch (e) {
+    logFailed("An exception occurred. Name: " + e.name + " Message: " +
+              e.message);
+    writeResult();
   }
-
-  // Verify the search provider state for the current page.
-  var installed = window.external.IsSearchProviderInstalled(origin)
-  var installedMessage = "Search provider ("+ origin +"): " + installed + ".";
-  if (installed == document.location.hash.substring(1))
-    logPassed(installedMessage);
-  else
-    logFailed(installedMessage + " The expected result is passed as the hash.");
-
-  // Verify that cases that should result in exceptions.
-  verifyExceptionFor("different host", "http://example.org/");
-  verifyExceptionFor("different protocol", originWithDifferentProtocol);
-  verifyExceptionFor("different port", originWithDifferentPort);
-
-  writeResult();
-} catch (e) {
-  logFailed("An exception occurred. Name: " + e.name + " Message: " +
-            e.message);
-  writeResult();
 }
+main();
 </script>
 </body>
 </html>
diff --git a/chrome/test/data/notifications/notification_tester.html b/chrome/test/data/notifications/notification_tester.html
index abc17fd..20de190 100644
--- a/chrome/test/data/notifications/notification_tester.html
+++ b/chrome/test/data/notifications/notification_tester.html
@@ -39,23 +39,32 @@
   g_notifications[id].close();
 }
 
-// Requests permission for this origin to create notifications.
-function requestPermission() {
-  Notification.requestPermission(onPermissionGranted);
-  sendResultToTest(1);
+// Requests permission for this origin to create notifications. Immediately
+// sends the result without waiting for callbacks to complete.
+function requestPermissionAndRespond() {
+  Notification.requestPermission(permissionCallbackNoResponse);
+  sendResultToTest('requested');
 }
 
-// Waits for the permission to create notifications to be granted.
-function waitForPermissionGranted() {
-  if (g_permissionGranted) {
-    sendResultToTest(1);
-  } else {
-    setTimeout(waitForPermissionGranted, 50);
-  }
+// Requests permission for this origin to create notifications. The result will
+// be sent from the callback (permissionCallbackWithResponse);
+function requestPermission() {
+  Notification.requestPermission(permissionCallbackWithResponse);
+}
+
+// Callback for requestPermissionAndRespond. Will send the permission status to
+// the test driver.
+function permissionCallbackWithResponse(permissionStatus) {
+  if (permissionStatus === 'granted')
+    sendResultToTest('request-callback-granted');
+  else if (permissionStatus === 'denied')
+    sendResultToTest('request-callback-denied');
+  else if (permissionStatus === 'default')
+    sendResultToTest('request-callback-default');
 }
 
 // Callback for requesting notification privileges.
-function onPermissionGranted() {
+function permissionCallbackNoResponse() {
   g_permissionGranted = true;
 }
 
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 38d4f2d2..d45e429 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2240,6 +2240,9 @@
     ]
   },
 
+  "KeyPermissions": {
+  },
+
   "----- Chrome OS device policies ---------------------------------------": {},
 
   "DevicePolicyRefreshRate": {
diff --git a/chrome/test/data/webui/cr_elements/cr_checkbox_tests.js b/chrome/test/data/webui/cr_elements/cr_checkbox_tests.js
index c0894a1..b7c20d2 100644
--- a/chrome/test/data/webui/cr_elements/cr_checkbox_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_checkbox_tests.js
@@ -13,10 +13,9 @@
       var checkbox;
 
       // Import cr_checkbox.html before running suite.
-      suiteSetup(function(done) {
-        PolymerTest.importHref(
-            'chrome://resources/cr_elements/v1_0/cr_checkbox/cr_checkbox.html',
-            done);
+      suiteSetup(function() {
+        return PolymerTest.importHtml(
+            'chrome://resources/cr_elements/v1_0/cr_checkbox/cr_checkbox.html');
       });
 
       // Initialize a checked cr-checkbox before each test.
diff --git a/chrome/test/data/webui/polymer_browser_test_base.js b/chrome/test/data/webui/polymer_browser_test_base.js
index 19118096..b768ef7 100644
--- a/chrome/test/data/webui/polymer_browser_test_base.js
+++ b/chrome/test/data/webui/polymer_browser_test_base.js
@@ -47,33 +47,33 @@
     testing.Test.prototype.setUp.call(this);
 
     // Import Polymer and iron-test-helpers before running tests.
-    suiteSetup(function(done) {
-      PolymerTest.importHref(
-          'chrome://resources/polymer/v1_0/polymer/polymer.html', done);
-      PolymerTest.importHref(
-          'chrome://resources/polymer/v1_0/iron-test-helpers/' +
-          'iron-test-helpers.html',
-          done);
+    suiteSetup(function() {
+      return Promise.all([
+        PolymerTest.importHtml(
+            'chrome://resources/polymer/v1_0/polymer/polymer.html'),
+        PolymerTest.importHtml(
+            'chrome://resources/polymer/v1_0/iron-test-helpers/' +
+            'iron-test-helpers.html'),
+      ]);
     });
   },
 };
 
 /**
- * Imports the HTML file, then calls |done| on success or throws an error.
- * @param {string} href The URL to load.
- * @param {function(Error=)} done The done callback.
+ * Imports the HTML file.
+ * @param {string} src The URL to load.
+ * @return {Promise} A promise that is resolved/rejected on success/failure.
  */
-PolymerTest.importHref = function(href, done) {
+PolymerTest.importHtml = function(src) {
   var link = document.createElement('link');
   link.rel = 'import';
-  link.onload = function() {
-    done();
-  };
-  link.onerror = function() {
-    done(new Error('Failed to load ' + href));
-  };
-  link.href = href;
+  var promise = new Promise(function(resolve, reject) {
+    link.onload = resolve;
+    link.onerror = reject;
+  });
+  link.href = src;
   document.head.appendChild(link);
+  return promise;
 };
 
 /**
diff --git a/chrome/test/nacl/nacl_browsertest_util.cc b/chrome/test/nacl/nacl_browsertest_util.cc
index 3ecb690..bd79bdf 100644
--- a/chrome/test/nacl/nacl_browsertest_util.cc
+++ b/chrome/test/nacl/nacl_browsertest_util.cc
@@ -301,7 +301,7 @@
 void NaClBrowserTestTransitionalNonSfi::SetUpCommandLine(
     base::CommandLine* command_line) {
   NaClBrowserTestNonSfiMode::SetUpCommandLine(command_line);
-  command_line->AppendSwitchASCII(switches::kUseNaClHelperNonSfi, "false");
+  command_line->AppendSwitch(switches::kUseNaClHelperNonSfi);
 }
 
 base::FilePath::StringType NaClBrowserTestStatic::Variant() {
diff --git a/chrome/test/nacl/nacl_browsertest_util.h b/chrome/test/nacl/nacl_browsertest_util.h
index cfe4971..b40bf89b 100644
--- a/chrome/test/nacl/nacl_browsertest_util.h
+++ b/chrome/test/nacl/nacl_browsertest_util.h
@@ -137,14 +137,11 @@
   base::FilePath::StringType Variant() override;
 };
 
-// "Transitional" here means that this uses nacl_helper in Non-SFI mode.
-// nacl_helper_nonsfi, which is replacing nacl_helper in Non-SFI mode, is being
-// launched. In the meanwhile, nacl_helper in Non-SFI is still kept just in
-// case. When the launching is successfully done, it will be removed.
-// "Transitional" tests are for ensuring compatibility between those two
-// binaries.
-// TODO(hidehiko): Remove the tests when nacl_helper in Non-SFI mode is
-// removed.
+// "Transitional" here means that this uses nacl_helper_nonsfi which has
+// nacl_helper feature for Non-SFI mode, but statically linked to newlib
+// instead of using host glibc. It is still under development.
+// TODO(hidehiko): Switch NonSfi tests to use nacl_helper_nonsfi, when
+// it is launched officially.
 class NaClBrowserTestPnaclTransitionalNonSfi
     : public NaClBrowserTestPnaclNonSfi {
  public:
@@ -157,8 +154,8 @@
   base::FilePath::StringType Variant() override;
 };
 
-// TODO(hidehiko): Remove this when clean-up to drop Non-SFI support from
-// nacl_helper is done. See NaClBrowserTestPnaclTransitionalNonSfi
+// TODO(hidehiko): Switch NonSfi tests to use nacl_helper_nonsfi, when
+// it is launched officially. See NaClBrowserTestPnaclTransitionalNonSfi
 // for more details.
 class NaClBrowserTestTransitionalNonSfi : public NaClBrowserTestNonSfiMode {
  public:
@@ -202,38 +199,38 @@
 #  define MAYBE_GLIBC(test_name) DISABLED_##test_name
 #endif
 
+// Sanitizers internally use some syscalls which non-SFI NaCl disallows.
+#if defined(OS_LINUX) && !defined(ADDRESS_SANITIZER) && \
+    !defined(THREAD_SANITIZER) && !defined(MEMORY_SANITIZER) && \
+    !defined(LEAK_SANITIZER)
+#  define MAYBE_NONSFI(test_case) test_case
+#else
+#  define MAYBE_NONSFI(test_case) DISABLED_##test_case
+#endif
+
 // Currently, we only support it on x86-32 or ARM architecture.
 // TODO(hidehiko,mazda): Enable this on x86-64, too, when it is supported.
 #if defined(OS_LINUX) && !defined(ADDRESS_SANITIZER) && \
     !defined(THREAD_SANITIZER) && !defined(MEMORY_SANITIZER) && \
     !defined(LEAK_SANITIZER) && \
     (defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARMEL))
-#  define MAYBE_NONSFI(test_case) test_case
-#else
-#  define MAYBE_NONSFI(test_case) DISABLED_##test_case
-#endif
-
-// Sanitizers internally use some syscalls which non-SFI NaCl disallows.
-#if defined(OS_LINUX) && !defined(ADDRESS_SANITIZER) && \
-    !defined(THREAD_SANITIZER) && !defined(MEMORY_SANITIZER) && \
-    !defined(LEAK_SANITIZER)
 #  define MAYBE_TRANSITIONAL_NONSFI(test_case) test_case
 #else
 #  define MAYBE_TRANSITIONAL_NONSFI(test_case) DISABLED_##test_case
 #endif
 
-// Similar to MAYBE_NONSFI, this is available only on x86-32, x86-64 or
-// ARM linux.
-#if defined(OS_LINUX) && \
-    (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL))
+// Currently, translation from pexe to non-sfi nexe is supported only for
+// x86-32 or ARM binary.
+#if defined(OS_LINUX) && (defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARMEL))
 #  define MAYBE_PNACL_NONSFI(test_case) test_case
 #else
 #  define MAYBE_PNACL_NONSFI(test_case) DISABLED_##test_case
 #endif
 
-// Currently, translation from pexe to non-sfi nexe is supported only for
-// x86-32 or ARM binary.
-#if defined(OS_LINUX) && (defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARMEL))
+// Similar to MAYBE_TRANSITIONAL_NONSFI, this is available only on x86-32,
+// x86-64 or ARM linux.
+#if defined(OS_LINUX) && \
+    (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL))
 #  define MAYBE_PNACL_TRANSITIONAL_NONSFI(test_case) test_case
 #else
 #  define MAYBE_PNACL_TRANSITIONAL_NONSFI(test_case) DISABLED_##test_case
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index 9225f0f..76bcc8d2f 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -1400,15 +1400,14 @@
   }
 };
 
-// TODO(hidehiko): Remove this when clean-up to drop Non-SFI support from
-// nacl_helper is done. See NaClBrowserTestPnaclTransitionalNonSfi
+// TODO(hidehiko): Switch for NonSfi tests to use nacl_helper_nonsfi, when
+// it is launched officially. See NaClBrowserTestPnaclTransitionalNonSfi
 // for more details.
 class TransitionalNonSfiPackagedAppTest : public NonSfiPackagedAppTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     NonSfiPackagedAppTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII(switches::kUseNaClHelperNonSfi,
-                                    "false");
+    command_line->AppendSwitch(switches::kUseNaClHelperNonSfi);
   }
 };
 
diff --git a/chrome/test/ppapi/ppapi_test.cc b/chrome/test/ppapi/ppapi_test.cc
index 11e287d..6e9fc03 100644
--- a/chrome/test/ppapi/ppapi_test.cc
+++ b/chrome/test/ppapi/ppapi_test.cc
@@ -433,7 +433,7 @@
     base::CommandLine* command_line) {
   PPAPINaClPNaClNonSfiTest::SetUpCommandLine(command_line);
 #if !defined(DISABLE_NACL)
-  command_line->AppendSwitchASCII(switches::kUseNaClHelperNonSfi, "false");
+  command_line->AppendSwitch(switches::kUseNaClHelperNonSfi);
 #endif
 }
 
@@ -447,7 +447,7 @@
     base::CommandLine* command_line) {
   PPAPIPrivateNaClPNaClNonSfiTest::SetUpCommandLine(command_line);
 #if !defined(DISABLE_NACL)
-  command_line->AppendSwitchASCII(switches::kUseNaClHelperNonSfi, "false");
+  command_line->AppendSwitch(switches::kUseNaClHelperNonSfi);
 #endif
 }
 
diff --git a/chrome/test/ppapi/ppapi_test.h b/chrome/test/ppapi/ppapi_test.h
index 92805f4..0110ca5 100644
--- a/chrome/test/ppapi/ppapi_test.h
+++ b/chrome/test/ppapi/ppapi_test.h
@@ -182,8 +182,8 @@
                          const std::string& test_case) override;
 };
 
-// TODO(hidehiko): Remove this when clean-up to drop Non-SFI support from
-// nacl_helper is done. See NaClBrowserTestPnaclTransitionalNonSfi
+// TODO(hidehiko): Switch NonSfi tests to use nacl_helper_nonsfi, when
+// it is launched officially. See NaClBrowserTestPnaclTransitionalNonSfi
 // for more details.
 class PPAPINaClPNaClTransitionalNonSfiTest : public PPAPINaClPNaClNonSfiTest {
  public:
@@ -195,8 +195,8 @@
   void SetUpCommandLine(base::CommandLine* command_line) override;
 };
 
-// TODO(hidehiko): Remove this when clean-up to drop Non-SFI support from
-// nacl_helper is done. See NaClBrowserTestPnaclTransitionalNonSfi
+// TODO(hidehiko): Switch NonSfi tests to use nacl_helper_nonsfi, when
+// it is launched officially. See NaClBrowserTestPnaclTransitionalNonSfi
 // for more details.
 class PPAPIPrivateNaClPNaClTransitionalNonSfiTest
     : public PPAPIPrivateNaClPNaClNonSfiTest {
diff --git a/chrome/utility/media_galleries/OWNERS b/chrome/utility/media_galleries/OWNERS
index 3ddd92e0..857971d 100644
--- a/chrome/utility/media_galleries/OWNERS
+++ b/chrome/utility/media_galleries/OWNERS
@@ -1,3 +1,3 @@
-orenb@chromium.org
+reillyg@chromium.org
 thestig@chromium.org
 tommycli@chromium.org
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastApplication.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastApplication.java
index 44733b9..6de72889 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastApplication.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastApplication.java
@@ -26,7 +26,6 @@
 
     private static final String[] MANDATORY_PAK_FILES = new String[] {
         "cast_shell.pak",
-        "icudtl.dat",
         "natives_blob.bin",
         "snapshot_blob.bin"
     };
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index de1abe07..f141c2a6 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -9,7 +9,6 @@
 #include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/files/scoped_file.h"
-#include "base/i18n/icu_util.h"
 #include "base/i18n/rtl.h"
 #include "base/path_service.h"
 #include "chromecast/base/cast_paths.h"
@@ -39,7 +38,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/web_preferences.h"
-#include "gin/v8_initializer.h"
 #include "media/audio/audio_manager_factory.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -54,9 +52,7 @@
 namespace shell {
 
 CastContentBrowserClient::CastContentBrowserClient()
-    : v8_natives_fd_(-1),
-      v8_snapshot_fd_(-1),
-      url_request_context_factory_(new URLRequestContextFactory()) {
+    : url_request_context_factory_(new URLRequestContextFactory()) {
 }
 
 CastContentBrowserClient::~CastContentBrowserClient() {
@@ -178,21 +174,6 @@
   return false;
 }
 
-void CastContentBrowserClient::AppendMappedFileCommandLineSwitches(
-    base::CommandLine* command_line) {
-  std::string process_type =
-      command_line->GetSwitchValueNative(switches::kProcessType);
-
-#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
-  if (process_type != switches::kZygoteProcess) {
-    DCHECK(natives_fd_exists());
-    command_line->AppendSwitch(::switches::kV8NativesPassedByFD);
-    if (snapshot_fd_exists())
-      command_line->AppendSwitch(::switches::kV8SnapshotPassedByFD);
-  }
-}
-
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
 void CastContentBrowserClient::AppendExtraCommandLineSwitches(
     base::CommandLine* command_line,
     int child_process_id) {
@@ -355,22 +336,6 @@
     const base::CommandLine& command_line,
     int child_process_id,
     content::FileDescriptorInfo* mappings) {
-#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
-  if (!natives_fd_exists()) {
-    int v8_natives_fd = -1;
-    int v8_snapshot_fd = -1;
-    if (gin::V8Initializer::OpenV8FilesForChildProcesses(&v8_natives_fd,
-                                                         &v8_snapshot_fd)) {
-      v8_natives_fd_.reset(v8_natives_fd);
-      v8_snapshot_fd_.reset(v8_snapshot_fd);
-    }
-  }
-  // V8 can't start up without the source of the natives, but it can
-  // start up (slower) without the snapshot.
-  DCHECK(natives_fd_exists());
-  mappings->Share(kV8NativesDataDescriptor, v8_natives_fd_.get());
-  mappings->Share(kV8SnapshotDataDescriptor, v8_snapshot_fd_.get());
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
 #if defined(OS_ANDROID)
   const int flags_open_read = base::File::FLAG_OPEN | base::File::FLAG_READ;
   base::FilePath pak_file_path;
@@ -395,16 +360,6 @@
                          base::ScopedFD(minidump_file.TakePlatformFile()));
     }
   }
-
-  base::FilePath app_data_path;
-  CHECK(PathService::Get(base::DIR_ANDROID_APP_DATA, &app_data_path));
-  base::FilePath icudata_path =
-      app_data_path.AppendASCII(base::i18n::kIcuDataFileName);
-  base::File icudata_file(icudata_path, flags_open_read);
-  if (!icudata_file.IsValid())
-    NOTREACHED() << "Failed to open ICU file when creating renderer process";
-  mappings->Transfer(kAndroidICUDataDescriptor,
-                     base::ScopedFD(icudata_file.TakePlatformFile()));
 #else
   int crash_signal_fd = GetCrashSignalFD(command_line);
   if (crash_signal_fd >= 0) {
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index 092d0a7..942a4c0 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -78,8 +78,6 @@
   bool IsHandledURL(const GURL& url) override;
   void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                       int child_process_id) override;
-  void AppendMappedFileCommandLineSwitches(
-      base::CommandLine* command_line) override;
   content::AccessTokenStore* CreateAccessTokenStore() override;
   void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
                            content::WebPreferences* prefs) override;
@@ -151,11 +149,6 @@
   std::map<std::string, breakpad::CrashHandlerHostLinux*> crash_handlers_;
 #endif
 
-  base::ScopedFD v8_natives_fd_;
-  base::ScopedFD v8_snapshot_fd_;
-  bool natives_fd_exists() { return v8_natives_fd_ != -1; }
-  bool snapshot_fd_exists() { return v8_snapshot_fd_ != -1; }
-
   scoped_ptr<URLRequestContextFactory> url_request_context_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CastContentBrowserClient);
diff --git a/chromecast/media/cdm/chromecast_init_data.cc b/chromecast/media/cdm/chromecast_init_data.cc
index d9e76a5..7f4d03f 100644
--- a/chromecast/media/cdm/chromecast_init_data.cc
+++ b/chromecast/media/cdm/chromecast_init_data.cc
@@ -5,7 +5,9 @@
 #include "chromecast/media/cdm/chromecast_init_data.h"
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "media/base/bit_reader.h"
+#include "media/cdm/cenc_utils.h"
 
 namespace chromecast {
 namespace media {
@@ -22,33 +24,6 @@
     0x2b, 0xf8, 0x66, 0x80, 0xc6, 0xe5, 0x4e, 0x24,
     0xbe, 0x23, 0x0f, 0x81, 0x5a, 0x60, 0x6e, 0xb2};
 
-// ASCII "uuid" as an 4-byte integer
-const uint32_t kBoxTypeUuid = 0x75756964;
-// ASCII "pssh" as an 4-byte integer
-const uint32_t kBoxTypePssh = 0x70737368;
-
-bool IsChromecastSystemId(::media::BitReader& reader) {
-  for (size_t i = 0; i < arraysize(kChromecastPlayreadyUuid); ++i) {
-    uint8_t one_byte;
-    RCHECK(reader.ReadBits(8, &one_byte));
-    if (one_byte != kChromecastPlayreadyUuid[i])
-      return false;
-  }
-  return true;
-}
-
-bool ParseLegacyUuidBox(::media::BitReader& reader,
-                        ChromecastInitData* chromecast_init_data_out) {
-  // See b/10246367 for format.
-  RCHECK(IsChromecastSystemId(reader));
-  chromecast_init_data_out->type = InitDataMessageType::CUSTOM_DATA;
-  size_t custom_data_size = reader.bits_available() / 8;
-  chromecast_init_data_out->data.resize(custom_data_size);
-  for (size_t i = 0; i < custom_data_size; ++i)
-    RCHECK(reader.ReadBits(8, &chromecast_init_data_out->data[i]));
-  return true;
-}
-
 }  // namespace
 
 ChromecastInitData::ChromecastInitData() {
@@ -66,64 +41,27 @@
   // * |type| (2 bytes, InitDataMessageType)
   // * |data| (all remaining bytes)
   // Data may or may not be present and is specific to the given |type|.
-  ::media::BitReader reader(&init_data[0], init_data.size());
-  while (reader.bits_available() > 64) {
-    uint32_t box_size;
-    uint32_t box_type;
-    reader.ReadBits(4 * 8, &box_size);
-    reader.ReadBits(4 * 8, &box_type);
-    int box_bytes_read = 8;
 
-    // TODO(gunsch): uuid boxes in initData are no longer supported. This should
-    // be removed when apps have updated.
-    if (box_type == kBoxTypeUuid) {
-      ::media::BitReader uuidReader(&init_data[reader.bits_read() / 8],
-                                    box_size - box_bytes_read);
-      if (type == InitDataMessageType::CUSTOM_DATA &&
-          ParseLegacyUuidBox(uuidReader, chromecast_init_data_out)) {
-        return true;
-      }
-
-      // Box size includes the bytes already consumed
-      reader.SkipBits((box_size - box_bytes_read) * 8);
-      continue;
-    }
-
-    if (box_type != kBoxTypePssh) {
-      reader.SkipBits((box_size - box_bytes_read) * 8);
-      continue;
-    }
-
-    // 8-bit version, 24-bit flags
-    RCHECK(reader.SkipBits(4 * 8));
-    box_bytes_read += 4;
-
-    ::media::BitReader systemIdReader(&init_data[reader.bits_read() / 8],
-                                      box_size - box_bytes_read);
-    if (!IsChromecastSystemId(systemIdReader)) {
-      reader.SkipBits((box_size - box_bytes_read) * 8);
-      continue;
-    }
-    RCHECK(reader.SkipBits(16 * 8));
-    box_bytes_read += 16;
-
-    uint32_t data_size;
-    RCHECK(reader.ReadBits(4 * 8, &data_size));
-    box_bytes_read += 4;
-    RCHECK(data_size == box_size - box_bytes_read);
-
-    uint16_t msg_type;
-    RCHECK(reader.ReadBits(2 * 8, &msg_type));
-    box_bytes_read += 2;
-    RCHECK(msg_type < static_cast<uint16_t>(InitDataMessageType::END));
-
-    chromecast_init_data_out->type = static_cast<InitDataMessageType>(msg_type);
-    const uint8_t* data_start = &init_data[0] + reader.bits_read() / 8;
-    chromecast_init_data_out->data.assign(
-        data_start, data_start + box_size - box_bytes_read);
-    return true;
+  std::vector<uint8_t> pssh_data;
+  if (!::media::GetPsshData(
+          init_data, std::vector<uint8_t>(kChromecastPlayreadyUuid,
+                                          kChromecastPlayreadyUuid +
+                                              sizeof(kChromecastPlayreadyUuid)),
+          &pssh_data)) {
+    return false;
   }
-  return false;
+
+  ::media::BitReader reader(vector_as_array(&pssh_data), pssh_data.size());
+
+  uint16_t msg_type;
+  RCHECK(reader.ReadBits(2 * 8, &msg_type));
+  RCHECK(msg_type < static_cast<uint16_t>(InitDataMessageType::END));
+  RCHECK(msg_type == static_cast<uint16_t>(type));
+
+  chromecast_init_data_out->type = static_cast<InitDataMessageType>(msg_type);
+  chromecast_init_data_out->data.assign(
+      pssh_data.begin() + reader.bits_read() / 8, pssh_data.end());
+  return true;
 }
 
 }  // namespace media
diff --git a/chromecast/media/cdm/chromecast_init_data_unittest.cc b/chromecast/media/cdm/chromecast_init_data_unittest.cc
index 8a4ad3254..640b95c 100644
--- a/chromecast/media/cdm/chromecast_init_data_unittest.cc
+++ b/chromecast/media/cdm/chromecast_init_data_unittest.cc
@@ -11,55 +11,6 @@
 namespace chromecast {
 namespace media {
 
-TEST(ChromecastInitDataTest, TestLegacyUuidCustomData) {
-  const uint8_t kUuidBlob[] = {
-      0x00, 0x00, 0x00, 0x28,  // length
-      0x75, 0x75, 0x69, 0x64,  // 'uuid'
-      0x2B, 0xF8, 0x66, 0x80, 0xC6, 0xE5, 0x4E, 0x24, 0xBE,
-      0x23, 0x0F, 0x81, 0x5A, 0x60, 0x6E, 0xB2,  // UUID
-      0x54, 0x65, 0x73, 0x74, 0x20, 0x63, 0x75, 0x73, 0x74,
-      0x6F, 0x6D, 0x20, 0x64, 0x61, 0x74, 0x61  // 'Test custom data'
-  };
-
-  ChromecastInitData init_data;
-  EXPECT_TRUE(FindChromecastInitData(
-      std::vector<uint8_t>(kUuidBlob, kUuidBlob + sizeof(kUuidBlob)),
-      InitDataMessageType::CUSTOM_DATA, &init_data));
-
-  EXPECT_EQ(InitDataMessageType::CUSTOM_DATA, init_data.type);
-  EXPECT_EQ(16u, init_data.data.size());
-  EXPECT_EQ("Test custom data",
-            std::string(init_data.data.begin(), init_data.data.end()));
-}
-
-TEST(ChromecastInitDataTest, TestUuidAfterPssh) {
-  const uint8_t kInitDataBlob[] = {
-      0x00, 0x00, 0x00, 0x1C,  // length
-      0x70, 0x73, 0x73, 0x68,  // 'pssh'
-      0x00, 0x00, 0x00, 0x00,  // version / flags
-      0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xab,
-      0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95,  // UUID
-
-      0x00, 0x00, 0x00, 0x28,  // length
-      0x75, 0x75, 0x69, 0x64,  // 'uuid'
-      0x2B, 0xF8, 0x66, 0x80, 0xC6, 0xE5, 0x4E, 0x24, 0xBE,
-      0x23, 0x0F, 0x81, 0x5A, 0x60, 0x6E, 0xB2,  // UUID
-      0x54, 0x65, 0x73, 0x74, 0x20, 0x63, 0x75, 0x73, 0x74,
-      0x6F, 0x6D, 0x20, 0x64, 0x61, 0x74, 0x61  // 'Test custom data'
-  };
-
-  ChromecastInitData init_data;
-  EXPECT_TRUE(FindChromecastInitData(
-      std::vector<uint8_t>(kInitDataBlob,
-                           kInitDataBlob + sizeof(kInitDataBlob)),
-      InitDataMessageType::CUSTOM_DATA, &init_data));
-
-  EXPECT_EQ(InitDataMessageType::CUSTOM_DATA, init_data.type);
-  EXPECT_EQ(16u, init_data.data.size());
-  EXPECT_EQ("Test custom data",
-            std::string(init_data.data.begin(), init_data.data.end()));
-}
-
 TEST(ChromecastInitDataTest, TestPsshCustomData) {
   const uint8_t kInitDataBlob[] = {
       0x00, 0x00, 0x00, 0x32,  // length
diff --git a/chromecast/public/media/decoder_config.h b/chromecast/public/media/decoder_config.h
index 07cc96f..45fd1d4 100644
--- a/chromecast/public/media/decoder_config.h
+++ b/chromecast/public/media/decoder_config.h
@@ -13,16 +13,12 @@
 namespace chromecast {
 namespace media {
 
-namespace {
-
 // Maximum audio bytes per sample.
 static const int kMaxBytesPerSample = 4;
 
 // Maximum audio sampling rate.
 static const int kMaxSampleRate = 192000;
 
-}  // namespace
-
 enum AudioCodec {
   kAudioCodecUnknown = 0,
   kCodecAAC,
@@ -165,6 +161,10 @@
 inline bool IsValidConfig(const AudioConfig& config) {
   return config.codec >= kAudioCodecMin &&
       config.codec <= kAudioCodecMax &&
+      config.codec != kAudioCodecUnknown &&
+      config.sample_format >= kSampleFormatMin &&
+      config.sample_format <= kSampleFormatMax &&
+      config.sample_format != kUnknownSampleFormat &&
       config.channel_number > 0 &&
       config.bytes_per_channel > 0 &&
       config.bytes_per_channel <= kMaxBytesPerSample &&
@@ -173,7 +173,9 @@
 }
 
 inline bool IsValidConfig(const VideoConfig& config) {
-  return config.codec >= kVideoCodecMin && config.codec <= kVideoCodecMax;
+  return config.codec >= kVideoCodecMin &&
+      config.codec <= kVideoCodecMax &&
+      config.codec != kVideoCodecUnknown;
 }
 
 }  // namespace media
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 93e66fc8..dca9f69 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-7175.0.0
\ No newline at end of file
+7184.0.0
\ No newline at end of file
diff --git a/cloud_print/cloud_print.gyp b/cloud_print/cloud_print.gyp
index e54dc86f..a6774385 100644
--- a/cloud_print/cloud_print.gyp
+++ b/cloud_print/cloud_print.gyp
@@ -10,12 +10,13 @@
       'target_name': 'cloud_print',
       'type': 'none',
       'dependencies': [
-        'service/service.gyp:*',
         'gcp20/prototype/gcp20_device.gyp:*',
+        'service/service.gyp:*',
       ],
       'conditions': [
         ['OS=="win"', {
           'dependencies': [
+            'service/win/service.gyp:*',
             'virtual_driver/win/install/virtual_driver_install.gyp:*',
             'virtual_driver/win/virtual_driver.gyp:*',
           ],
diff --git a/cloud_print/service/service.gyp b/cloud_print/service/service.gyp
index fc680a8..1eb2b23 100644
--- a/cloud_print/service/service.gyp
+++ b/cloud_print/service/service.gyp
@@ -119,92 +119,5 @@
         'win/setup_listener.h',
       ],
     },
-    {
-      'target_name': 'cloud_print_service',
-      'type': 'executable',
-      'sources': [
-        '<(SHARED_INTERMEDIATE_DIR)/cloud_print/cloud_print_service_exe_version.rc',
-        'win/cloud_print_service.cc',
-      ],
-      'includes': [
-        'win/service_resources.gypi'
-      ],
-      'dependencies': [
-        'cloud_print_service_lib',
-      ],
-      'msvs_settings': {
-        'VCLinkerTool': {
-          'SubSystem': '1',         # Set /SUBSYSTEM:CONSOLE
-          'UACExecutionLevel': '2', # /level='requireAdministrator'
-          'AdditionalDependencies': [
-              'secur32.lib',
-          ],
-        },
-      },
-    },
-    {
-      'target_name': 'cloud_print_service_config',
-      'type': 'executable',
-      'sources': [
-        '<(SHARED_INTERMEDIATE_DIR)/cloud_print/cloud_print_service_config_exe_version.rc',
-        'win/cloud_print_service_config.cc',
-      ],
-      'includes': [
-        'win/service_resources.gypi'
-      ],
-      'dependencies': [
-        '<(DEPTH)/cloud_print/common/common.gyp:cloud_print_install_lib',
-        'cloud_print_service_lib',
-      ],
-      'msvs_settings': {
-        'VCManifestTool': {
-          'AdditionalManifestFiles': [
-            'common-controls.manifest',
-          ],
-        },
-        'VCLinkerTool': {
-          'SubSystem': '2',         # Set /SUBSYSTEM:WINDOWS
-          'UACExecutionLevel': '2', # /level='requireAdministrator'
-          'AdditionalDependencies': [
-              'secur32.lib',
-          ],
-        },
-        'conditions': [
-          ['clang==1', {
-            # atlapp.h contains a global "using namespace WTL;".
-            # TODO: Remove once cloud_print_service_config.cc no longer depends
-            # on atlapp.h, http://crbug.com/5027
-            'VCCLCompilerTool': {
-              'AdditionalOptions': ['-Wno-header-hygiene'],
-            },
-          }],
-        ],
-      },
-    },
-    {
-      'target_name': 'cloud_print_service_setup',
-      'type': 'executable',
-      'sources': [
-        '<(SHARED_INTERMEDIATE_DIR)/cloud_print/cloud_print_service_setup_exe_version.rc',
-        'win/installer.cc',
-        'win/installer.h',
-      ],
-      'includes': [
-        'win/service_resources.gypi'
-      ],
-      'dependencies': [
-        '<(DEPTH)/cloud_print/common/common.gyp:cloud_print_install_lib',
-        'cloud_print_service_lib',
-      ],
-      'msvs_settings': {
-        'VCLinkerTool': {
-          'SubSystem': '2',         # Set /SUBSYSTEM:WINDOWS
-          'UACExecutionLevel': '2', # /level='requireAdministrator'
-          'AdditionalDependencies': [
-              'secur32.lib',
-          ],
-        },
-      },
-    },
   ],
 }
diff --git a/cloud_print/service/common-controls.manifest b/cloud_print/service/win/common-controls.manifest
similarity index 100%
rename from cloud_print/service/common-controls.manifest
rename to cloud_print/service/win/common-controls.manifest
diff --git a/cloud_print/service/win/service.gyp b/cloud_print/service/win/service.gyp
new file mode 100644
index 0000000..1221842d
--- /dev/null
+++ b/cloud_print/service/win/service.gyp
@@ -0,0 +1,120 @@
+# 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.
+{
+  'target_defaults': {
+    'variables': {
+      'chromium_code': 1,
+      'enable_wexit_time_destructors': 1,
+    },
+    'include_dirs': [
+      '<(DEPTH)',
+      # To allow including "version.h"
+      '<(SHARED_INTERMEDIATE_DIR)',
+    ],
+    'defines' : [
+      'COMPILE_CONTENT_STATICALLY',
+      'SECURITY_WIN32',
+      'STRICT',
+      '_ATL_APARTMENT_THREADED',
+      '_ATL_CSTRING_EXPLICIT_CONSTRUCTORS',
+      '_ATL_NO_COM_SUPPORT',
+      '_ATL_NO_AUTOMATIC_NAMESPACE',
+      '_ATL_NO_EXCEPTIONS',
+    ],
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    'msvs_disabled_warnings': [ 4267, ],
+  },
+  'targets': [
+    {
+      'target_name': 'cloud_print_service',
+      'type': 'executable',
+      'sources': [
+        '<(SHARED_INTERMEDIATE_DIR)/cloud_print/cloud_print_service_exe_version.rc',
+        'cloud_print_service.cc',
+      ],
+      'includes': [
+        'service_resources.gypi'
+      ],
+      'dependencies': [
+        '<(DEPTH)/cloud_print/cloud_print_resources.gyp:cloud_print_version_resources',
+        '<(DEPTH)/cloud_print/service/service.gyp:cloud_print_service_lib',
+      ],
+      'msvs_settings': {
+        'VCLinkerTool': {
+          'SubSystem': '1',         # Set /SUBSYSTEM:CONSOLE
+          'UACExecutionLevel': '2', # /level='requireAdministrator'
+          'AdditionalDependencies': [
+              'secur32.lib',
+          ],
+        },
+      },
+    },
+    {
+      'target_name': 'cloud_print_service_config',
+      'type': 'executable',
+      'sources': [
+        '<(SHARED_INTERMEDIATE_DIR)/cloud_print/cloud_print_service_config_exe_version.rc',
+        'cloud_print_service_config.cc',
+      ],
+      'includes': [
+        'service_resources.gypi'
+      ],
+      'dependencies': [
+        '<(DEPTH)/cloud_print/cloud_print_resources.gyp:cloud_print_version_resources',
+        '<(DEPTH)/cloud_print/common/common.gyp:cloud_print_install_lib',
+        '<(DEPTH)/cloud_print/service/service.gyp:cloud_print_service_lib',
+      ],
+      'msvs_settings': {
+        'VCManifestTool': {
+          'AdditionalManifestFiles': [
+            'common-controls.manifest',
+          ],
+        },
+        'VCLinkerTool': {
+          'SubSystem': '2',         # Set /SUBSYSTEM:WINDOWS
+          'UACExecutionLevel': '2', # /level='requireAdministrator'
+          'AdditionalDependencies': [
+              'secur32.lib',
+          ],
+        },
+        'conditions': [
+          ['clang==1', {
+            # atlapp.h contains a global "using namespace WTL;".
+            # TODO: Remove once cloud_print_service_config.cc no longer depends
+            # on atlapp.h, http://crbug.com/5027
+            'VCCLCompilerTool': {
+              'AdditionalOptions': ['-Wno-header-hygiene'],
+            },
+          }],
+        ],
+      },
+    },
+    {
+      'target_name': 'cloud_print_service_setup',
+      'type': 'executable',
+      'sources': [
+        '<(SHARED_INTERMEDIATE_DIR)/cloud_print/cloud_print_service_setup_exe_version.rc',
+        'installer.cc',
+        'installer.h',
+      ],
+      'includes': [
+        'service_resources.gypi'
+      ],
+      'dependencies': [
+        '<(DEPTH)/cloud_print/cloud_print_resources.gyp:cloud_print_version_resources',
+        '<(DEPTH)/cloud_print/common/common.gyp:cloud_print_install_lib',
+        '<(DEPTH)/cloud_print/service/service.gyp:cloud_print_service_lib',
+      ],
+      'msvs_settings': {
+        'VCLinkerTool': {
+          'SubSystem': '2',         # Set /SUBSYSTEM:WINDOWS
+          'UACExecutionLevel': '2', # /level='requireAdministrator'
+          'AdditionalDependencies': [
+              'secur32.lib',
+          ],
+        },
+      },
+    },
+  ],
+}
diff --git a/components/autofill/content/common/autofill_messages.h b/components/autofill/content/common/autofill_messages.h
index 2c816370a1..90565e9 100644
--- a/components/autofill/content/common/autofill_messages.h
+++ b/components/autofill/content/common/autofill_messages.h
@@ -96,6 +96,7 @@
   IPC_STRUCT_TRAITS_MEMBER(additional_logins)
   IPC_STRUCT_TRAITS_MEMBER(other_possible_usernames)
   IPC_STRUCT_TRAITS_MEMBER(wait_for_username)
+  IPC_STRUCT_TRAITS_MEMBER(is_possible_change_password_form)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(autofill::PasswordAndRealm)
diff --git a/components/autofill/content/renderer/page_click_tracker.cc b/components/autofill/content/renderer/page_click_tracker.cc
index 8f1b0c2..193dd7a 100644
--- a/components/autofill/content/renderer/page_click_tracker.cc
+++ b/components/autofill/content/renderer/page_click_tracker.cc
@@ -74,28 +74,9 @@
 PageClickTracker::~PageClickTracker() {
 }
 
-void PageClickTracker::DidHandleMouseEvent(const WebMouseEvent& event) {
-  if (event.type != WebInputEvent::MouseDown ||
-      event.button != WebMouseEvent::ButtonLeft) {
-    return;
-  }
-
-  WebNode clicked_node = render_frame()->GetRenderView()->GetWebView()
-      ->hitTestResultAt(WebPoint(event.x, event.y)).node();
-  focused_node_was_last_clicked_ = !clicked_node.isNull() &&
-                                   clicked_node.focused();
-}
-
-void PageClickTracker::DidHandleGestureEvent(const WebGestureEvent& event) {
-  if (event.type != WebGestureEvent::GestureTap)
-    return;
-
-  WebNode tapped_node = render_frame()->GetRenderView()->GetWebView()
-      ->hitTestResultForTap(
-          WebPoint(event.x, event.y),
-          WebSize(event.data.tap.width, event.data.tap.height)).node();
-  focused_node_was_last_clicked_ = !tapped_node.isNull() &&
-                                   tapped_node.focused();
+void PageClickTracker::OnMouseDown(const WebNode& mouse_down_node) {
+  focused_node_was_last_clicked_ = !mouse_down_node.isNull() &&
+                                   mouse_down_node.focused();
 }
 
 void PageClickTracker::FocusedNodeChanged(const WebNode& node) {
@@ -149,13 +130,8 @@
   // No-op. Don't delete |this|.
 }
 
-void PageClickTracker::Legacy::DidHandleMouseEvent(const WebMouseEvent& event) {
-  tracker_->DidHandleMouseEvent(event);
-}
-
-void PageClickTracker::Legacy::DidHandleGestureEvent(
-    const WebGestureEvent& event) {
-  tracker_->DidHandleGestureEvent(event);
+void PageClickTracker::Legacy::OnMouseDown(const WebNode& mouse_down_node) {
+  tracker_->OnMouseDown(mouse_down_node);
 }
 
 void PageClickTracker::Legacy::FocusChangeComplete() {
diff --git a/components/autofill/content/renderer/page_click_tracker.h b/components/autofill/content/renderer/page_click_tracker.h
index 0c6585af..eed10f9a8 100644
--- a/components/autofill/content/renderer/page_click_tracker.h
+++ b/components/autofill/content/renderer/page_click_tracker.h
@@ -42,8 +42,7 @@
 
     // RenderViewObserver implementation.
     void OnDestruct() override;
-    void DidHandleMouseEvent(const blink::WebMouseEvent& event) override;
-    void DidHandleGestureEvent(const blink::WebGestureEvent& event) override;
+    void OnMouseDown(const blink::WebNode& mouse_down_node) override;
     void FocusChangeComplete() override;
 
    private:
@@ -56,8 +55,7 @@
 
   // RenderViewObserver methods forwarded from Legacy. Should be
   // merged into RenderFrameObserver.
-  void DidHandleMouseEvent(const blink::WebMouseEvent& event);
-  void DidHandleGestureEvent(const blink::WebGestureEvent& event);
+  void OnMouseDown(const blink::WebNode& mouse_down_node);
   void FocusChangeComplete();
   void DoFocusChangeComplete();
 
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index b305540..f0dcefd8 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -722,6 +722,12 @@
   if (element.isTextField())
     nonscript_modified_values_[element] = element.value();
 
+  LoginToPasswordInfoMap::iterator password_info_iter =
+      login_to_password_info_.find(element);
+  if (password_info_iter != login_to_password_info_.end()) {
+    password_info_iter->second.username_was_edited = true;
+  }
+
   DCHECK_EQ(element.document().frame(), render_frame()->GetWebFrame());
 
   if (element.isPasswordField()) {
@@ -859,7 +865,9 @@
   // is no username or the corresponding username element is not editable since
   // it is only in that case that the username element does not have a
   // suggestions popup.
-  if (element.isPasswordField() && username_is_available)
+  if (element.isPasswordField() && username_is_available &&
+      (!password_info->fill_data.is_possible_change_password_form ||
+       password_info->username_was_edited))
     return true;
 
   UMA_HISTOGRAM_BOOLEAN(
@@ -1295,7 +1303,9 @@
 // PasswordAutofillAgent, private:
 
 PasswordAutofillAgent::PasswordInfo::PasswordInfo()
-    : backspace_pressed_last(false), password_was_edited_last(false) {
+    : backspace_pressed_last(false),
+      password_was_edited_last(false),
+      username_was_edited(false) {
 }
 
 bool PasswordAutofillAgent::ShowSuggestionPopup(
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index 08e1cff..9f070f35 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -113,6 +113,8 @@
     // The user manually edited the password more recently than the username was
     // changed.
     bool password_was_edited_last;
+    // The user edited the username field after page loading.
+    bool username_was_edited;
     PasswordInfo();
   };
   typedef std::map<blink::WebInputElement, PasswordInfo> LoginToPasswordInfoMap;
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.cc b/components/autofill/content/renderer/password_form_conversion_utils.cc
index aebdbf59..a44a4f7 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -143,13 +143,17 @@
       *current_password = passwords[0];
       break;
     case 2:
-      if (passwords[0].value() == passwords[1].value()) {
-        // Two identical passwords: assume we are seeing a new password with a
-        // confirmation. This can be either a sign-up form or a password change
-        // form that does not ask for the old password.
+      if (!passwords[0].value().isEmpty() &&
+          passwords[0].value() == passwords[1].value()) {
+        // Two identical non-empty passwords: assume we are seeing a new
+        // password with a confirmation. This can be either a sign-up form or a
+        // password change form that does not ask for the old password.
         *new_password = passwords[0];
       } else {
         // Assume first is old password, second is new (no choice but to guess).
+        // This case also includes empty passwords in order to allow filling of
+        // password change forms (that also could autofill for sign up form, but
+        // we can't do anything with this using only client side information).
         *current_password = passwords[0];
         *new_password = passwords[1];
       }
diff --git a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
index d733c53..d6b95e9 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
@@ -292,13 +292,13 @@
     const char* expected_new_password_element;
     const char* expected_new_password_value;
   } cases[] = {
-      // Twp non-empty fields with the same value should be treated as a new
+      // Two non-empty fields with the same value should be treated as a new
       // password field plus a confirmation field for the new password.
       {{"alpha", "alpha"}, "", "", "password1", "alpha"},
       // The same goes if the fields are yet empty: we speculate that we will
       // identify them as new password fields once they are filled out, and we
       // want to keep our abstract interpretation of the form less flaky.
-      {{"", ""}, "", "", "password1", ""},
+      {{"", ""}, "password1", "", "password2", ""},
       // Two different values should be treated as a password change form, one
       // that also asks for the current password, but only once for the new.
       {{"alpha", ""}, "password1", "alpha", "password2", ""},
diff --git a/components/autofill/core/common/password_form.cc b/components/autofill/core/common/password_form.cc
index e5edaca9..af4529ad 100644
--- a/components/autofill/core/common/password_form.cc
+++ b/components/autofill/core/common/password_form.cc
@@ -90,6 +90,11 @@
   return !original_signon_realm.empty();
 }
 
+bool PasswordForm::IsPossibleChangePasswordForm() const {
+  return !new_password_element.empty() &&
+         layout != PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP;
+}
+
 bool PasswordForm::operator==(const PasswordForm& form) const {
   return scheme == form.scheme &&
       signon_realm == form.signon_realm &&
diff --git a/components/autofill/core/common/password_form.h b/components/autofill/core/common/password_form.h
index 9663326..11c1e2f 100644
--- a/components/autofill/core/common/password_form.h
+++ b/components/autofill/core/common/password_form.h
@@ -271,6 +271,10 @@
   // Returns true if this match was found using public suffix matching.
   bool IsPublicSuffixMatch() const;
 
+  // Return true if we consider this form to be a change password form.
+  // We use only client heuristics, so it could include signup forms.
+  bool IsPossibleChangePasswordForm() const;
+
   // Equality operators for testing.
   bool operator==(const PasswordForm& form) const;
   bool operator!=(const PasswordForm& form) const;
diff --git a/components/autofill/core/common/password_form_fill_data.cc b/components/autofill/core/common/password_form_fill_data.cc
index 714e8f57b..9bf0d02 100644
--- a/components/autofill/core/common/password_form_fill_data.cc
+++ b/components/autofill/core/common/password_form_fill_data.cc
@@ -23,7 +23,9 @@
 }
 
 PasswordFormFillData::PasswordFormFillData()
-    : user_submitted(false), wait_for_username(false) {
+    : user_submitted(false),
+      wait_for_username(false),
+      is_possible_change_password_form(false) {
 }
 
 PasswordFormFillData::~PasswordFormFillData() {
@@ -55,6 +57,8 @@
   result->username_field = username_field;
   result->password_field = password_field;
   result->wait_for_username = wait_for_username_before_autofill;
+  result->is_possible_change_password_form =
+      form_on_page.IsPossibleChangePasswordForm();
 
   result->preferred_realm = preferred_match->original_signon_realm;
 
diff --git a/components/autofill/core/common/password_form_fill_data.h b/components/autofill/core/common/password_form_fill_data.h
index 0eea16fb..12ce6b4c 100644
--- a/components/autofill/core/common/password_form_fill_data.h
+++ b/components/autofill/core/common/password_form_fill_data.h
@@ -76,6 +76,9 @@
   // and our saved representation don't match up.
   bool wait_for_username;
 
+  // True if this form is a change password form.
+  bool is_possible_change_password_form;
+
   PasswordFormFillData();
   ~PasswordFormFillData();
 };
diff --git a/components/chrome_apps/webstore_widget/app/main.css b/components/chrome_apps/webstore_widget/app/main.css
index 770e3d0..4b0ac29a 100644
--- a/components/chrome_apps/webstore_widget/app/main.css
+++ b/components/chrome_apps/webstore_widget/app/main.css
@@ -6,44 +6,6 @@
   margin: 0;
 }
 
-button {
-  background-color: #fafafa;
-  background-image: none;
-  border: 5px solid transparent;
-  border-image: -webkit-image-set(
-    url(chrome://resources/images/apps/button.png) 1x,
-    url(chrome://resources/images/2x/apps/button.png)
-        2x) 5 / 5px / 2px repeat;
-  box-sizing: content-box;
-  color: #222;
-  cursor: default;
-  line-height: 21px;
-  margin: 0;
-  min-height: 21px;
-  min-width: 55px;
-  padding: 0 10px;
-}
-
-button:hover  {
-  border-image: -webkit-image-set(
-    url(chrome://resources/images/apps/button_hover.png) 1x,
-    url(chrome://resources/images/2x/apps/button_hover.png)
-        2x) 5 fill / 5px / 2px repeat;
-  color: #222;
-}
-
-button:active {
-  border-image: -webkit-image-set(
-    url(chrome://resources/images/apps/button_pressed.png) 1x,
-    url(chrome://resources/images/2x/apps/button_pressed.png)
-        2x) 5 fill / 5px / 2px repeat;
-  color: #333;
-}
-
-button:focus {
-  outline-color: rgb(77, 144, 254);
-}
-
 .title-area {
   -webkit-app-region: drag;
   background-color: white;
diff --git a/components/crash/app/breakpad_win.cc b/components/crash/app/breakpad_win.cc
index 423bb4f..bfce051a 100644
--- a/components/crash/app/breakpad_win.cc
+++ b/components/crash/app/breakpad_win.cc
@@ -368,6 +368,20 @@
   return WrapMessageBoxWithSEH(message.c_str(), title.c_str(), flags, exit_now);
 }
 
+extern "C" void __declspec(dllexport) TerminateProcessWithoutDump() {
+  // Patched stub exists based on conditions (See InitCrashReporter).
+  // As a side note this function also gets called from
+  // WindowProcExceptionFilter.
+  if (g_real_terminate_process_stub == NULL) {
+    ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
+  } else {
+    NtTerminateProcessPtr real_terminate_proc =
+        reinterpret_cast<NtTerminateProcessPtr>(
+            static_cast<char*>(g_real_terminate_process_stub));
+    real_terminate_proc(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
+  }
+}
+
 // Crashes the process after generating a dump for the provided exception. Note
 // that the crash reporter should be initialized before calling this function
 // for it to do anything.
@@ -378,17 +392,7 @@
     EXCEPTION_POINTERS* info) {
   if (g_breakpad) {
     g_breakpad->WriteMinidumpForException(info);
-    // Patched stub exists based on conditions (See InitCrashReporter).
-    // As a side note this function also gets called from
-    // WindowProcExceptionFilter.
-    if (g_real_terminate_process_stub == NULL) {
-      ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
-    } else {
-      NtTerminateProcessPtr real_terminate_proc =
-          reinterpret_cast<NtTerminateProcessPtr>(
-              static_cast<char*>(g_real_terminate_process_stub));
-      real_terminate_proc(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
-    }
+    TerminateProcessWithoutDump();
   }
   return EXCEPTION_CONTINUE_SEARCH;
 }
diff --git a/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc b/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc
index 1b00b19..c01ebffe 100644
--- a/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc
+++ b/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc
@@ -29,8 +29,7 @@
     content::ResourceType resource_type,
     const DataReductionProxyIOData* io_data) {
   if (io_data && io_data->IsEnabled() &&
-      data_reduction_proxy::DataReductionProxyParams::
-          WarnIfNoDataReductionProxy()) {
+      data_reduction_proxy::params::WarnIfNoDataReductionProxy()) {
     DCHECK(io_data->debug_ui_service());
     DCHECK(request);
     return scoped_ptr<DataReductionProxyDebugResourceThrottle>(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
index 6ef6ac6c..b9f71da 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
@@ -140,8 +140,7 @@
   DataReductionProxyBypassStats::DetectAndRecordMissingViaHeaderResponseCode(
       !data_reduction_proxy_type_info.is_fallback, response_headers);
 
-  if (DataReductionProxyParams::
-          IsIncludedInRelaxMissingViaHeaderOtherBypassFieldTrial() &&
+  if (params::IsIncludedInRelaxMissingViaHeaderOtherBypassFieldTrial() &&
       HasDataReductionProxyViaHeader(response_headers, NULL)) {
     DCHECK(config_->IsDataReductionProxy(request->proxy_server(), NULL));
     via_header_producing_proxies_.insert(request->proxy_server());
@@ -153,10 +152,8 @@
       response_headers, data_reduction_proxy_info);
 
   if (bypass_type == BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER) {
-    if (DataReductionProxyParams::
-            IsIncludedInRemoveMissingViaHeaderOtherBypassFieldTrial() ||
-        (DataReductionProxyParams::
-             IsIncludedInRelaxMissingViaHeaderOtherBypassFieldTrial() &&
+    if (params::IsIncludedInRemoveMissingViaHeaderOtherBypassFieldTrial() ||
+        (params::IsIncludedInRelaxMissingViaHeaderOtherBypassFieldTrial() &&
          via_header_producing_proxies_.find(request->proxy_server()) !=
              via_header_producing_proxies_.end())) {
       // Ignore MISSING_VIA_HEADER_OTHER proxy bypass events if the client is
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 2adc560..73f6521c 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
@@ -187,8 +187,7 @@
     scoped_ptr<DataReductionProxyConfigValues> config_values,
     DataReductionProxyConfigurator* configurator,
     DataReductionProxyEventCreator* event_creator)
-    : secure_proxy_allowed_(
-          DataReductionProxyParams::ShouldUseSecureProxyByDefault()),
+    : secure_proxy_allowed_(params::ShouldUseSecureProxyByDefault()),
       disabled_on_vpn_(false),
       unreachable_(false),
       enabled_by_user_(false),
@@ -206,7 +205,7 @@
       lofi_status_(LOFI_STATUS_TEMPORARILY_OFF) {
   DCHECK(configurator);
   DCHECK(event_creator);
-  if (DataReductionProxyParams::IsLoFiDisabledViaFlags())
+  if (params::IsLoFiDisabledViaFlags())
     SetLoFiModeOff();
   // Constructed on the UI thread, but should be checked on the IO thread.
   thread_checker_.DetachFromThread();
@@ -420,14 +419,14 @@
 
 bool DataReductionProxyConfig::IsIncludedInLoFiEnabledFieldTrial() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return FieldTrialList::FindFullName(
-             DataReductionProxyParams::GetLoFiFieldTrialName()) == kEnabled;
+  return FieldTrialList::FindFullName(params::GetLoFiFieldTrialName()) ==
+         kEnabled;
 }
 
 bool DataReductionProxyConfig::IsIncludedInLoFiControlFieldTrial() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return FieldTrialList::FindFullName(
-             DataReductionProxyParams::GetLoFiFieldTrialName()) == kControl;
+  return FieldTrialList::FindFullName(params::GetLoFiFieldTrialName()) ==
+         kControl;
 }
 
 LoFiStatus DataReductionProxyConfig::GetLoFiStatus() const {
@@ -469,7 +468,7 @@
 
   uint64_t auto_lofi_minimum_rtt_msec;
   std::string variation_value = variations::GetVariationParamValue(
-      DataReductionProxyParams::GetLoFiFieldTrialName(), "rtt_msec");
+      params::GetLoFiFieldTrialName(), "rtt_msec");
   if (!variation_value.empty() &&
       base::StringToUint64(variation_value, &auto_lofi_minimum_rtt_msec)) {
     auto_lofi_minimum_rtt_ =
@@ -479,7 +478,7 @@
 
   int32_t auto_lofi_maximum_kbps;
   variation_value = variations::GetVariationParamValue(
-      DataReductionProxyParams::GetLoFiFieldTrialName(), "kbps");
+      params::GetLoFiFieldTrialName(), "kbps");
   if (!variation_value.empty() &&
       base::StringToInt(variation_value, &auto_lofi_maximum_kbps)) {
     auto_lofi_maximum_kbps_ = auto_lofi_maximum_kbps;
@@ -488,8 +487,7 @@
 
   uint32_t auto_lofi_hysteresis_period_seconds;
   variation_value = variations::GetVariationParamValue(
-      DataReductionProxyParams::GetLoFiFieldTrialName(),
-      "hysteresis_period_seconds");
+      params::GetLoFiFieldTrialName(), "hysteresis_period_seconds");
   if (!variation_value.empty() &&
       base::StringToUint(variation_value,
                          &auto_lofi_hysteresis_period_seconds)) {
@@ -694,8 +692,7 @@
       return;
     }
 
-    bool should_use_secure_proxy =
-        DataReductionProxyParams::ShouldUseSecureProxyByDefault();
+    bool should_use_secure_proxy = params::ShouldUseSecureProxyByDefault();
     if (!should_use_secure_proxy && secure_proxy_allowed_) {
       secure_proxy_allowed_ = false;
       RecordSecureProxyCheckFetchResult(PROXY_DISABLED_BEFORE_CHECK);
@@ -797,12 +794,12 @@
     }
   }
 
-  if (DataReductionProxyParams::IsLoFiAlwaysOnViaFlags()) {
+  if (params::IsLoFiAlwaysOnViaFlags()) {
     lofi_status_ = LOFI_STATUS_ACTIVE_FROM_FLAGS;
     return;
   }
 
-  if (DataReductionProxyParams::IsLoFiCellularOnlyViaFlags()) {
+  if (params::IsLoFiCellularOnlyViaFlags()) {
     if (net::NetworkChangeNotifier::IsConnectionCellular(
             net::NetworkChangeNotifier::GetConnectionType())) {
       lofi_status_ = LOFI_STATUS_ACTIVE_FROM_FLAGS;
@@ -846,7 +843,7 @@
 }
 
 bool DataReductionProxyConfig::MaybeDisableIfVPN() {
-  if (DataReductionProxyParams::IsIncludedInUseDataSaverOnVPNFieldTrial()) {
+  if (params::IsIncludedInUseDataSaverOnVPNFieldTrial()) {
     return false;
   }
   net::NetworkInterfaceList network_interfaces;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
index 84dd1e95..790fd5d 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
@@ -133,8 +133,7 @@
       net_log_(net_log),
       config_storer_(config_storer),
       backoff_entry_(&backoff_policy),
-      config_service_url_(
-          AddApiKeyToUrl(DataReductionProxyParams::GetConfigServiceURL())),
+      config_service_url_(AddApiKeyToUrl(params::GetConfigServiceURL())),
       use_local_config_(!config_service_url_.is_valid()),
       remote_config_applied_(false),
       url_request_context_getter_(nullptr),
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
index e481a93f..a73a2c5 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
@@ -462,8 +462,7 @@
       base::FieldTrialList::CreateFieldTrial(kConfigServiceFieldTrial,
                                              test.trial_group_value);
     }
-    EXPECT_EQ(test.expected, DataReductionProxyParams::GetConfigServiceURL())
-        << test.test_case;
+    EXPECT_EQ(test.expected, params::GetConfigServiceURL()) << test.test_case;
   }
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
index 8927db6..d9f84f48 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
@@ -1260,12 +1260,11 @@
   variation_params["spurious_field"] = "480";
 
   ASSERT_TRUE(variations::AssociateVariationParams(
-      DataReductionProxyParams::GetLoFiFieldTrialName(), "Enabled",
-      variation_params));
+      params::GetLoFiFieldTrialName(), "Enabled", variation_params));
 
   base::FieldTrialList field_trial_list(nullptr);
-  base::FieldTrialList::CreateFieldTrial(
-      DataReductionProxyParams::GetLoFiFieldTrialName(), "Enabled");
+  base::FieldTrialList::CreateFieldTrial(params::GetLoFiFieldTrialName(),
+                                         "Enabled");
 
   config.PopulateAutoLoFiParams();
 
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 f9b68cc..c99c28c 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
@@ -113,7 +113,7 @@
   event_creator_.reset(new DataReductionProxyEventCreator(this));
   configurator_.reset(
       new DataReductionProxyConfigurator(net_log, event_creator_.get()));
-  bool use_config_client = DataReductionProxyParams::IsConfigClientEnabled();
+  bool use_config_client = params::IsConfigClientEnabled();
   DataReductionProxyMutableConfigValues* raw_mutable_config = nullptr;
   if (use_config_client) {
     scoped_ptr<DataReductionProxyMutableConfigValues> mutable_config =
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
index 81532b8..c9022f2 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
@@ -302,7 +302,7 @@
   }
 
   if ((load_flags & net::LOAD_BYPASS_DATA_REDUCTION_PROXY) &&
-      DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial()) {
+      params::IsIncludedInCriticalPathBypassFieldTrial()) {
     if (!result->is_empty() && !result->is_direct() &&
         config->IsDataReductionProxy(result->proxy_server().host_port_pair(),
                                      NULL)) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
index c6a60a0..0e915391 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
@@ -599,8 +599,7 @@
   base::FieldTrialList field_trial_list(new base::MockEntropyProvider());
   base::FieldTrialList::CreateFieldTrial("DataCompressionProxyCriticalBypass",
                                          "Enabled");
-  EXPECT_TRUE(
-      DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial());
+  EXPECT_TRUE(params::IsIncludedInCriticalPathBypassFieldTrial());
 
   load_flags = net::LOAD_NORMAL;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
index 5778286..205352d9 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
@@ -145,19 +145,17 @@
   DCHECK(CalledOnValidThread());
   if (prefs_) {
     int lo_fi_user_requests_for_images_per_session =
-        DataReductionProxyParams::GetFieldTrialParameterAsInteger(
-            DataReductionProxyParams::GetLoFiFieldTrialName(),
-            "load_images_requests_per_session", 3, 0);
+        params::GetFieldTrialParameterAsInteger(
+            params::GetLoFiFieldTrialName(), "load_images_requests_per_session",
+            3, 0);
 
-    int lo_fi_implicit_opt_out_epoch =
-        DataReductionProxyParams::GetFieldTrialParameterAsInteger(
-            DataReductionProxyParams::GetLoFiFieldTrialName(),
-            "implicit_opt_out_epoch", 0, 0);
+    int lo_fi_implicit_opt_out_epoch = params::GetFieldTrialParameterAsInteger(
+        params::GetLoFiFieldTrialName(), "implicit_opt_out_epoch", 0, 0);
 
     int lo_fi_consecutive_session_disables =
-        DataReductionProxyParams::GetFieldTrialParameterAsInteger(
-            DataReductionProxyParams::GetLoFiFieldTrialName(),
-            "consecutive_session_disables", 3, 0);
+        params::GetFieldTrialParameterAsInteger(params::GetLoFiFieldTrialName(),
+                                                "consecutive_session_disables",
+                                                3, 0);
 
     if (prefs_->GetInteger(prefs::kLoFiImplicitOptOutEpoch) <
         lo_fi_implicit_opt_out_epoch) {
@@ -168,7 +166,7 @@
                          lo_fi_implicit_opt_out_epoch);
       settings_->RecordLoFiImplicitOptOutAction(
           LO_FI_OPT_OUT_ACTION_NEXT_EPOCH);
-    } else if (!DataReductionProxyParams::IsLoFiAlwaysOnViaFlags() &&
+    } else if (!params::IsLoFiAlwaysOnViaFlags() &&
                (prefs_->GetInteger(prefs::kLoFiConsecutiveSessionDisables) >=
                 lo_fi_consecutive_session_disables)) {
       // If Lo-Fi isn't always on and and the number of
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
index ca34807..8dce4be 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
@@ -45,13 +45,11 @@
       prefs_(NULL),
       config_(nullptr) {
   lo_fi_user_requests_for_images_per_session_ =
-      DataReductionProxyParams::GetFieldTrialParameterAsInteger(
-          DataReductionProxyParams::GetLoFiFieldTrialName(),
-          "load_images_requests_per_session", 3, 0);
-  lo_fi_consecutive_session_disables_ =
-      DataReductionProxyParams::GetFieldTrialParameterAsInteger(
-          DataReductionProxyParams::GetLoFiFieldTrialName(),
-          "consecutive_session_disables", 3, 0);
+      params::GetFieldTrialParameterAsInteger(
+          params::GetLoFiFieldTrialName(), "load_images_requests_per_session",
+          3, 0);
+  lo_fi_consecutive_session_disables_ = params::GetFieldTrialParameterAsInteger(
+      params::GetLoFiFieldTrialName(), "consecutive_session_disables", 3, 0);
 }
 
 DataReductionProxySettings::~DataReductionProxySettings() {
@@ -119,7 +117,7 @@
 
 bool DataReductionProxySettings::IsDataReductionProxyEnabled() const {
   return spdy_proxy_auth_enabled_.GetValue() ||
-         DataReductionProxyParams::ShouldForceEnableDataReductionProxy();
+         params::ShouldForceEnableDataReductionProxy();
 }
 
 bool DataReductionProxySettings::CanUseDataReductionProxy(
@@ -204,7 +202,7 @@
 }
 
 void DataReductionProxySettings::IncrementLoFiUserRequestsForImages() {
-  if (!prefs_ || DataReductionProxyParams::IsLoFiAlwaysOnViaFlags())
+  if (!prefs_ || params::IsLoFiAlwaysOnViaFlags())
     return;
   prefs_->SetInteger(prefs::kLoFiLoadImagesPerSession,
                      prefs_->GetInteger(prefs::kLoFiLoadImagesPerSession) + 1);
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 fa7fe34..569b067 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
@@ -545,13 +545,11 @@
 
     base::FieldTrialList field_trial_list(new base::MockEntropyProvider());
     if (enable_quic) {
-      base::FieldTrialList::CreateFieldTrial(
-          DataReductionProxyParams::GetQuicFieldTrialName(),
-          "Enabled");
+      base::FieldTrialList::CreateFieldTrial(params::GetQuicFieldTrialName(),
+                                             "Enabled");
     } else {
-      base::FieldTrialList::CreateFieldTrial(
-          DataReductionProxyParams::GetQuicFieldTrialName(),
-          "Disabled");
+      base::FieldTrialList::CreateFieldTrial(params::GetQuicFieldTrialName(),
+                                             "Disabled");
     }
     test_context_->config()->EnableQuic(enable_quic);
 
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 0a1bb9c..89cfb04 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
@@ -58,9 +58,9 @@
 }  // namespace
 
 namespace data_reduction_proxy {
+namespace params {
 
-// static
-bool DataReductionProxyParams::IsIncludedInAlternativeFieldTrial() {
+bool IsIncludedInAlternativeFieldTrial() {
   const std::string group_name = base::FieldTrialList::FindFullName(
       "DataCompressionProxyAlternativeConfiguration");
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -70,52 +70,41 @@
   return group_name == kEnabled;
 }
 
-// static
-bool DataReductionProxyParams::IsIncludedInPromoFieldTrial() {
+bool IsIncludedInPromoFieldTrial() {
   return FieldTrialList::FindFullName(
       "DataCompressionProxyPromoVisibility") == kEnabled;
 }
 
-// static
-bool DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial() {
+bool IsIncludedInCriticalPathBypassFieldTrial() {
   return FieldTrialList::FindFullName(
           "DataCompressionProxyCriticalBypass") == kEnabled;
 }
 
-// static
-bool DataReductionProxyParams::IsIncludedInHoldbackFieldTrial() {
+bool IsIncludedInHoldbackFieldTrial() {
   return FieldTrialList::FindFullName(
       "DataCompressionProxyHoldback") == kEnabled;
 }
 
-// static
-bool DataReductionProxyParams::
-    IsIncludedInRemoveMissingViaHeaderOtherBypassFieldTrial() {
+bool IsIncludedInRemoveMissingViaHeaderOtherBypassFieldTrial() {
   return FieldTrialList::FindFullName(
       "DataReductionProxyRemoveMissingViaHeaderOtherBypass") == kEnabled;
 }
 
-// static
-bool DataReductionProxyParams::
-    IsIncludedInRelaxMissingViaHeaderOtherBypassFieldTrial() {
+bool IsIncludedInRelaxMissingViaHeaderOtherBypassFieldTrial() {
   return FieldTrialList::FindFullName(
       "DataReductionProxyRemoveMissingViaHeaderOtherBypass") == "Relaxed";
 }
 
-// static
-bool DataReductionProxyParams::IsIncludedInAndroidOnePromoFieldTrial(
-    const char* build_fingerprint) {
+bool IsIncludedInAndroidOnePromoFieldTrial(const char* build_fingerprint) {
   base::StringPiece fingerprint(build_fingerprint);
   return (fingerprint.find(kAndroidOneIdentifier) != std::string::npos);
 }
 
-// static
-std::string DataReductionProxyParams::GetLoFiFieldTrialName() {
+std::string GetLoFiFieldTrialName() {
   return kLoFiFieldTrial;
 }
 
-// static
-bool DataReductionProxyParams::IsLoFiAlwaysOnViaFlags() {
+bool IsLoFiAlwaysOnViaFlags() {
   const std::string& lo_fi_value =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           data_reduction_proxy::switches::kDataReductionProxyLoFi);
@@ -123,8 +112,7 @@
          data_reduction_proxy::switches::kDataReductionProxyLoFiValueAlwaysOn;
 }
 
-// static
-bool DataReductionProxyParams::IsLoFiCellularOnlyViaFlags() {
+bool IsLoFiCellularOnlyViaFlags() {
   const std::string& lo_fi_value =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           data_reduction_proxy::switches::kDataReductionProxyLoFi);
@@ -132,8 +120,7 @@
                             kDataReductionProxyLoFiValueCellularOnly;
 }
 
-// static
-bool DataReductionProxyParams::IsLoFiDisabledViaFlags() {
+bool IsLoFiDisabledViaFlags() {
   const std::string& lo_fi_value =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           data_reduction_proxy::switches::kDataReductionProxyLoFi);
@@ -141,8 +128,7 @@
          data_reduction_proxy::switches::kDataReductionProxyLoFiValueDisabled;
 }
 
-//static
-bool DataReductionProxyParams::WarnIfNoDataReductionProxy() {
+bool WarnIfNoDataReductionProxy() {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           data_reduction_proxy::switches::
           kEnableDataReductionProxyBypassWarning)) {
@@ -151,29 +137,20 @@
   return false;
 }
 
-// static
-bool DataReductionProxyParams::CanProxyURLScheme(const GURL& url) {
-  return url.SchemeIs(url::kHttpScheme);
-}
-
-// static
-bool DataReductionProxyParams::IsIncludedInQuicFieldTrial() {
+bool IsIncludedInQuicFieldTrial() {
   return FieldTrialList::FindFullName(kQuicFieldTrial) == kEnabled;
 }
 
-// static
-std::string DataReductionProxyParams::GetQuicFieldTrialName() {
+std::string GetQuicFieldTrialName() {
   return kQuicFieldTrial;
 }
 
-// static
-bool DataReductionProxyParams::IsIncludedInUseDataSaverOnVPNFieldTrial() {
+bool IsIncludedInUseDataSaverOnVPNFieldTrial() {
   return FieldTrialList::FindFullName("DataReductionProxyUseDataSaverOnVPN") ==
          kEnabled;
 }
 
-// static
-bool DataReductionProxyParams::IsConfigClientEnabled() {
+bool IsConfigClientEnabled() {
   std::string group_value =
       base::FieldTrialList::FindFullName(kConfigServiceFieldTrial);
   base::StringPiece group = group_value;
@@ -183,8 +160,7 @@
          group.starts_with(kEnabled);
 }
 
-// static
-GURL DataReductionProxyParams::GetConfigServiceURL() {
+GURL GetConfigServiceURL() {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   std::string url;
   if (command_line->HasSwitch(switches::kDataReductionProxyConfigURL)) {
@@ -209,14 +185,12 @@
   return GURL(kClientConfigURL);
 }
 
-// static
-bool DataReductionProxyParams::ShouldForceEnableDataReductionProxy() {
+bool ShouldForceEnableDataReductionProxy() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       data_reduction_proxy::switches::kEnableDataReductionProxy);
 }
 
-// static
-bool DataReductionProxyParams::ShouldUseSecureProxyByDefault() {
+bool ShouldUseSecureProxyByDefault() {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           data_reduction_proxy::switches::
               kDataReductionProxyStartSecureDisabled))
@@ -229,12 +203,10 @@
   return true;
 }
 
-// static
-int DataReductionProxyParams::GetFieldTrialParameterAsInteger(
-    const std::string& group,
-    const std::string& param_name,
-    int default_value,
-    int min_value) {
+int GetFieldTrialParameterAsInteger(const std::string& group,
+                                    const std::string& param_name,
+                                    int default_value,
+                                    int min_value) {
   DCHECK(default_value >= min_value);
   std::string param_value =
       variations::GetVariationParamValue(group, param_name);
@@ -247,9 +219,11 @@
   return value;
 }
 
+}  // namespace params
+
 void DataReductionProxyParams::EnableQuic(bool enable) {
   quic_enabled_ = enable;
-  DCHECK(!quic_enabled_ || IsIncludedInQuicFieldTrial());
+  DCHECK(!quic_enabled_ || params::IsIncludedInQuicFieldTrial());
   if (override_quic_origin_.empty() && quic_enabled_) {
     origin_ = net::ProxyServer::FromURI(kDefaultQuicOrigin,
                                         net::ProxyServer::SCHEME_HTTP);
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 b770094558..3ab7ce588f021 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
@@ -23,6 +23,102 @@
 
 namespace data_reduction_proxy {
 
+// The data_reduction_proxy::params namespace is a collection of methods to
+// determine the operating parameters of the Data Reduction Proxy as specified
+// by field trials and command line switches.
+namespace params {
+
+// Returns true if this client is part of field trial to use an alternative
+// configuration for the data reduction proxy.
+bool IsIncludedInAlternativeFieldTrial();
+
+// Returns true if this client is part of the field trial that should display
+// a promotion for the data reduction proxy.
+bool IsIncludedInPromoFieldTrial();
+
+// Returns true if this client is part of a field trial that bypasses the
+// proxy if the request resource type is on the critical path (e.g. HTML).
+bool IsIncludedInCriticalPathBypassFieldTrial();
+
+// Returns true if this client is part of a field trial that runs a holdback
+// experiment. A holdback experiment is one in which a fraction of browser
+// instances will not be configured to use the data reduction proxy even if
+// users have enabled it to be used. The UI will not indicate that a holdback
+// is in effect.
+bool IsIncludedInHoldbackFieldTrial();
+
+// Returns true if this client is part of a field trial that removes the
+// |MISSING_VIA_HEADER_OTHER| proxy bypass case. This experiment changes proxy
+// bypass logic to not trigger a proxy bypass when a response with a non-4xx
+// response code is expected to have a data reduction proxy via header, but
+// the data reduction proxy via header is missing.
+bool IsIncludedInRemoveMissingViaHeaderOtherBypassFieldTrial();
+
+// Returns true if this client is part of a field trial that relaxes the
+// |MISSING_VIA_HEADER_OTHER| proxy bypass case. In this experiment, if a
+// response with a data reduction proxy via header has been received through
+// the proxy since the last network change, then don't bypass on missing via
+// headers in responses with non-4xx response codes.
+bool IsIncludedInRelaxMissingViaHeaderOtherBypassFieldTrial();
+
+// Returns true if this client is part of the field trial that should display
+// a promotion for the data reduction proxy on Android One devices.
+bool IsIncludedInAndroidOnePromoFieldTrial(const char* build_fingerprint);
+
+// Returns true if this client has the command line switch to enable Lo-Fi
+// mode always on.
+bool IsLoFiAlwaysOnViaFlags();
+
+// Returns true if this client has the command line switch to enable Lo-Fi
+// mode only on cellular connections.
+bool IsLoFiCellularOnlyViaFlags();
+
+// Returns true if this client has the command line switch to disable Lo-Fi
+// mode.
+bool IsLoFiDisabledViaFlags();
+
+// Returns true if this client has the command line switch to show
+// interstitials for data reduction proxy bypasses.
+bool WarnIfNoDataReductionProxy();
+
+// Returns true if this client is part of a field trial that sets the origin
+// proxy server as quic://proxy.googlezip.net.
+bool IsIncludedInQuicFieldTrial();
+
+// Returns the name of the Lo-Fi field trial.
+std::string GetLoFiFieldTrialName();
+
+std::string GetQuicFieldTrialName();
+
+// Returns true if this client is part of a field trial that allows Data Saver
+// to be used on VPN.
+bool IsIncludedInUseDataSaverOnVPNFieldTrial();
+
+// Returns true if the Data Reduction Proxy config client should be used.
+bool IsConfigClientEnabled();
+
+// If the Data Reduction Proxy config client is being used, the URL for the
+// Data Reduction Proxy config service.
+GURL GetConfigServiceURL();
+
+// Returns true if the Data Reduction Proxy is forced to be enabled from the
+// command line.
+bool ShouldForceEnableDataReductionProxy();
+
+// Returns true if the secure Data Reduction Proxy should be used until the
+// secure proxy check fails.
+bool ShouldUseSecureProxyByDefault();
+
+// Retrieves the int stored in |param_name| from the field trial group
+// |group|. If the value is not present, cannot be parsed, or is less than
+// |min_value|, returns |default_value|.
+int GetFieldTrialParameterAsInteger(const std::string& group,
+                                    const std::string& param_name,
+                                    int default_value,
+                                    int min_value);
+
+}  // namespace params
+
 class ClientConfig;
 
 // Contains information about a given proxy server. |proxies_for_http| and
@@ -59,100 +155,6 @@
   static const unsigned int kPromoAllowed = (1 << 4);
   static const unsigned int kHoldback = (1 << 5);
 
-  // Returns true if this client is part of field trial to use an alternative
-  // configuration for the data reduction proxy.
-  static bool IsIncludedInAlternativeFieldTrial();
-
-  // Returns true if this client is part of the field trial that should display
-  // a promotion for the data reduction proxy.
-  static bool IsIncludedInPromoFieldTrial();
-
-  // Returns true if this client is part of a field trial that bypasses the
-  // proxy if the request resource type is on the critical path (e.g. HTML).
-  static bool IsIncludedInCriticalPathBypassFieldTrial();
-
-  // Returns true if this client is part of a field trial that runs a holdback
-  // experiment. A holdback experiment is one in which a fraction of browser
-  // instances will not be configured to use the data reduction proxy even if
-  // users have enabled it to be used. The UI will not indicate that a holdback
-  // is in effect.
-  static bool IsIncludedInHoldbackFieldTrial();
-
-  // Returns true if this client is part of a field trial that removes the
-  // |MISSING_VIA_HEADER_OTHER| proxy bypass case. This experiment changes proxy
-  // bypass logic to not trigger a proxy bypass when a response with a non-4xx
-  // response code is expected to have a data reduction proxy via header, but
-  // the data reduction proxy via header is missing.
-  static bool IsIncludedInRemoveMissingViaHeaderOtherBypassFieldTrial();
-
-  // Returns true if this client is part of a field trial that relaxes the
-  // |MISSING_VIA_HEADER_OTHER| proxy bypass case. In this experiment, if a
-  // response with a data reduction proxy via header has been received through
-  // the proxy since the last network change, then don't bypass on missing via
-  // headers in responses with non-4xx response codes.
-  static bool IsIncludedInRelaxMissingViaHeaderOtherBypassFieldTrial();
-
-  // Returns true if this client is part of the field trial that should display
-  // a promotion for the data reduction proxy on Android One devices.
-  static bool IsIncludedInAndroidOnePromoFieldTrial(
-      const char* build_fingerprint);
-
-  // Returns true if this client has the command line switch to enable Lo-Fi
-  // mode always on.
-  static bool IsLoFiAlwaysOnViaFlags();
-
-  // Returns true if this client has the command line switch to enable Lo-Fi
-  // mode only on cellular connections.
-  static bool IsLoFiCellularOnlyViaFlags();
-
-  // Returns true if this client has the command line switch to disable Lo-Fi
-  // mode.
-  static bool IsLoFiDisabledViaFlags();
-
-  // Returns true if this client has the command line switch to show
-  // interstitials for data reduction proxy bypasses.
-  static bool WarnIfNoDataReductionProxy();
-
-  // Returns true if the Data Reduction Proxy supports the scheme of the
-  // provided |url|.
-  static bool CanProxyURLScheme(const GURL& url);
-
-  // Returns true if this client is part of a field trial that sets the origin
-  // proxy server as quic://proxy.googlezip.net.
-  static bool IsIncludedInQuicFieldTrial();
-
-  // Returns the name of the Lo-Fi field trial.
-  static std::string GetLoFiFieldTrialName();
-
-  static std::string GetQuicFieldTrialName();
-
-  // Returns true if this client is part of a field trial that allows Data Saver
-  // to be used on VPN.
-  static bool IsIncludedInUseDataSaverOnVPNFieldTrial();
-
-  // Returns true if the Data Reduction Proxy config client should be used.
-  static bool IsConfigClientEnabled();
-
-  // If the Data Reduction Proxy config client is being used, the URL for the
-  // Data Reduction Proxy config service.
-  static GURL GetConfigServiceURL();
-
-  // Returns true if the Data Reduction Proxy is forced to be enabled from the
-  // command line.
-  static bool ShouldForceEnableDataReductionProxy();
-
-  // Returns true if the secure Data Reduction Proxy should be used until the
-  // secure proxy check fails.
-  static bool ShouldUseSecureProxyByDefault();
-
-  // Retrieves the int stored in |param_name| from the field trial group
-  // |group|. If the value is not present, cannot be parsed, or is less than
-  // |min_value|, returns |default_value|.
-  static int GetFieldTrialParameterAsInteger(const std::string& group,
-                                             const std::string& param_name,
-                                             int default_value,
-                                             int min_value);
-
   // 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
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 e7e2743..e3715258 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
@@ -575,9 +575,9 @@
 }
 
 TEST_F(DataReductionProxyParamsTest, AndroidOnePromoFieldTrial) {
-  EXPECT_TRUE(DataReductionProxyParams::IsIncludedInAndroidOnePromoFieldTrial(
+  EXPECT_TRUE(params::IsIncludedInAndroidOnePromoFieldTrial(
       "google/sprout/sprout:4.4.4/KPW53/1379542:user/release-keys"));
-  EXPECT_FALSE(DataReductionProxyParams::IsIncludedInAndroidOnePromoFieldTrial(
+  EXPECT_FALSE(params::IsIncludedInAndroidOnePromoFieldTrial(
       "google/hammerhead/hammerhead:5.0/LRX210/1570415:user/release-keys"));
 }
 
@@ -623,8 +623,7 @@
       base::FieldTrialList::CreateFieldTrial(kConfigServiceFieldTrial,
                                              test.trial_group_value);
     }
-    EXPECT_EQ(test.expected, DataReductionProxyParams::IsConfigClientEnabled())
-        << test.test_case;
+    EXPECT_EQ(test.expected, params::IsConfigClientEnabled()) << test.test_case;
   }
 }
 
@@ -673,7 +672,7 @@
     }
 
     EXPECT_EQ(test_case.expected_use_by_default,
-              DataReductionProxyParams::ShouldUseSecureProxyByDefault())
+              params::ShouldUseSecureProxyByDefault())
         << test_index;
     test_index++;
   }
diff --git a/components/gcm_driver/gcm_client_impl.cc b/components/gcm_driver/gcm_client_impl.cc
index 8ffc695..cf745a8 100644
--- a/components/gcm_driver/gcm_client_impl.cc
+++ b/components/gcm_driver/gcm_client_impl.cc
@@ -75,6 +75,7 @@
 
 const char kGCMScope[] = "GCM";
 const int kMaxRegistrationRetries = 5;
+const int kMaxUnregistrationRetries = 5;
 const char kMessageTypeDataMessage[] = "gcm";
 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
 const char kMessageTypeKey[] = "message_type";
@@ -1050,6 +1051,7 @@
       base::Bind(&GCMClientImpl::OnUnregisterCompleted,
                   weak_ptr_factory_.GetWeakPtr(),
                   registration_info),
+      kMaxUnregistrationRetries,
       url_request_context_getter_,
       &recorder_);
   pending_unregistration_requests_[registration_info] = unregistration_request;
diff --git a/components/gcm_driver/gcm_client_impl_unittest.cc b/components/gcm_driver/gcm_client_impl_unittest.cc
index 673f45d..ee5b17b 100644
--- a/components/gcm_driver/gcm_client_impl_unittest.cc
+++ b/components/gcm_driver/gcm_client_impl_unittest.cc
@@ -470,6 +470,8 @@
   fetcher->set_response_code(net::HTTP_OK);
   fetcher->SetResponseString(response_string);
   fetcher->delegate()->OnURLFetchComplete(fetcher);
+  // Give a chance for GCMStoreImpl::Backend to finish persisting data.
+  PumpLoopUntilIdle();
 }
 
 void GCMClientImplTest::CompleteRegistration(
@@ -481,6 +483,8 @@
   fetcher->set_response_code(net::HTTP_OK);
   fetcher->SetResponseString(response);
   fetcher->delegate()->OnURLFetchComplete(fetcher);
+  // Give a chance for GCMStoreImpl::Backend to finish persisting data.
+  PumpLoopUntilIdle();
 }
 
 void GCMClientImplTest::CompleteUnregistration(
@@ -492,6 +496,8 @@
   fetcher->set_response_code(net::HTTP_OK);
   fetcher->SetResponseString(response);
   fetcher->delegate()->OnURLFetchComplete(fetcher);
+  // Give a chance for GCMStoreImpl::Backend to finish persisting data.
+  PumpLoopUntilIdle();
 }
 
 void GCMClientImplTest::VerifyPendingRequestFetcherDeleted() {
@@ -749,6 +755,7 @@
   Register(kAppId, senders);
 
   gcm_client()->Stop();
+  PumpLoopUntilIdle();
   VerifyPendingRequestFetcherDeleted();
 }
 
@@ -1373,6 +1380,8 @@
   fetcher->set_response_code(net::HTTP_OK);
   fetcher->SetResponseString(response);
   fetcher->delegate()->OnURLFetchComplete(fetcher);
+  // Give a chance for GCMStoreImpl::Backend to finish persisting data.
+  PumpLoopUntilIdle();
 }
 
 bool GCMClientInstanceIDTest::ExistsToken(const std::string& app_id,
diff --git a/components/gcm_driver/gcm_stats_recorder_impl.cc b/components/gcm_driver/gcm_stats_recorder_impl.cc
index 792202b..010aa8e5 100644
--- a/components/gcm_driver/gcm_stats_recorder_impl.cc
+++ b/components/gcm_driver/gcm_stats_recorder_impl.cc
@@ -339,14 +339,17 @@
 
 void GCMStatsRecorderImpl::RecordUnregistrationRetryDelayed(
     const std::string& app_id,
-    int64 delay_msec) {
+    int64 delay_msec,
+    int retries_left) {
   if (!is_recording_)
     return;
-  RecordRegistration(app_id,
-                     std::string(),
-                     "Unregistration retry delayed",
-                     base::StringPrintf("Delayed for %" PRId64 " msec",
-                                        delay_msec));
+  RecordRegistration(
+      app_id,
+      std::string(),
+      "Unregistration retry delayed",
+      base::StringPrintf("Delayed for %" PRId64 " msec, retries left: %d",
+                         delay_msec,
+                         retries_left));
 }
 
 void GCMStatsRecorderImpl::RecordReceiving(
diff --git a/components/gcm_driver/gcm_stats_recorder_impl.h b/components/gcm_driver/gcm_stats_recorder_impl.h
index a8d3e3b..d09ddf04 100644
--- a/components/gcm_driver/gcm_stats_recorder_impl.h
+++ b/components/gcm_driver/gcm_stats_recorder_impl.h
@@ -68,7 +68,8 @@
       const std::string& app_id,
       UnregistrationRequest::Status status) override;
   void RecordUnregistrationRetryDelayed(const std::string& app_id,
-                                        int64 delay_msec) override;
+                                        int64 delay_msec,
+                                        int retries_left) override;
   void RecordDataMessageReceived(const std::string& app_id,
                                  const std::string& from,
                                  int message_byte_size,
diff --git a/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc b/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc
index e45ab59..871378d 100644
--- a/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc
+++ b/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc
@@ -77,7 +77,7 @@
 static const char kUnregistrationRetryDelayedEvent[] =
     "Unregistration retry delayed";
 static const char kUnregistrationRetryDelayedDetails[] =
-    "Delayed for 15000 msec";
+    "Delayed for 15000 msec, retries left: 3";
 
 static const char kDataReceivedEvent[] = "Data msg received";
 static const char kDataReceivedDetails[] = "";
@@ -383,7 +383,7 @@
   recorder_.RecordRegistrationRetryRequested(kAppId, sender_ids_, kRetries);
   recorder_.RecordUnregistrationSent(kAppId);
   recorder_.RecordUnregistrationResponse(kAppId, kUnregistrationStatus);
-  recorder_.RecordUnregistrationRetryDelayed(kAppId, kDelay);
+  recorder_.RecordUnregistrationRetryDelayed(kAppId, kDelay, kRetries);
   VerifyAllActivityQueueEmpty("no unregistration");
 
   recorder_.RecordDataMessageReceived(kAppId, kFrom, kByteSize, true,
@@ -478,7 +478,7 @@
   VerifyRecordedRegistrationCount(5);
   VerifyUnregistrationResponse("5th call");
 
-  recorder_.RecordUnregistrationRetryDelayed(kAppId, kDelay);
+  recorder_.RecordUnregistrationRetryDelayed(kAppId, kDelay, kRetries);
   VerifyRecordedRegistrationCount(6);
   VerifyUnregistrationRetryDelayed("6th call");
 }
diff --git a/components/html_viewer/BUILD.gn b/components/html_viewer/BUILD.gn
index 1ff16e64..0fce7c51 100644
--- a/components/html_viewer/BUILD.gn
+++ b/components/html_viewer/BUILD.gn
@@ -143,6 +143,7 @@
     "//ui/events/gestures/blink",
     "//ui/gfx",
     "//ui/gfx/geometry",
+    "//ui/mojo/init",
     "//ui/native_theme",
 
     # TODO(sky): we shouldn't be using ui_test_pak.
@@ -170,9 +171,6 @@
 mojo_native_application("html_viewer") {
   sources = [
     "html_viewer.cc",
-    "ui_setup.h",
-    "ui_setup_android.cc",
-    "ui_setup_android.h",
   ]
   deps = [
     ":html_viewer_resources_grit",
diff --git a/components/html_viewer/DEPS b/components/html_viewer/DEPS
index 0a5b15a..6a70ad00 100644
--- a/components/html_viewer/DEPS
+++ b/components/html_viewer/DEPS
@@ -36,6 +36,7 @@
   "+ui/gfx",
   "+ui/mojo/events",
   "+ui/mojo/geometry",
+  "+ui/mojo/init",
   "+ui/native_theme",
   "+v8/include",
 ]
diff --git a/components/html_viewer/setup.cc b/components/html_viewer/setup.cc
index 928f617..6a8562b 100644
--- a/components/html_viewer/setup.cc
+++ b/components/html_viewer/setup.cc
@@ -19,14 +19,9 @@
 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_paths.h"
+#include "ui/mojo/init/ui_init.h"
 #include "v8/include/v8.h"
 
-#if defined(OS_ANDROID)
-#include "components/html_viewer/ui_setup_android.h"
-#else
-#include "components/html_viewer/ui_setup.h"
-#endif
-
 namespace html_viewer {
 
 namespace {
@@ -102,7 +97,8 @@
     return;
   }
 
-  ui_setup_.reset(new UISetup(screen_size_in_pixels, device_pixel_ratio));
+  ui_init_.reset(
+      new ui::mojo::UIInit(screen_size_in_pixels, device_pixel_ratio));
   base::DiscardableMemoryAllocator::SetInstance(&discardable_memory_allocator_);
 
   renderer_scheduler_ = scheduler::RendererScheduler::Create();
diff --git a/components/html_viewer/setup.h b/components/html_viewer/setup.h
index 941f3182..83154b53 100644
--- a/components/html_viewer/setup.h
+++ b/components/html_viewer/setup.h
@@ -19,6 +19,12 @@
 class Shell;
 }
 
+namespace ui {
+namespace mojo {
+class UIInit;
+}
+}
+
 namespace scheduler {
 class RendererScheduler;
 }
@@ -26,7 +32,6 @@
 namespace html_viewer {
 
 class BlinkPlatformImpl;
-class UISetup;
 class MediaFactory;
 
 // Setup encapsulates the necessary state needed by HTMLViewer. Some objects
@@ -87,7 +92,7 @@
 
   gfx::Size screen_size_in_pixels_;
 
-  scoped_ptr<UISetup> ui_setup_;
+  scoped_ptr<ui::mojo::UIInit> ui_init_;
 
   // Skia requires that we have one of these. Unlike the one used in chrome,
   // this doesn't use purgable shared memory. Instead, it tries to free the
diff --git a/components/html_viewer/ui_setup.h b/components/html_viewer/ui_setup.h
deleted file mode 100644
index 7ab0915..0000000
--- a/components/html_viewer/ui_setup.h
+++ /dev/null
@@ -1,28 +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 COMPONENTS_HTML_VIEWER_UI_SETUP_H_
-#define COMPONENTS_HTML_VIEWER_UI_SETUP_H_
-
-#include "base/basictypes.h"
-
-namespace gfx {
-class Size;
-}
-
-namespace html_viewer {
-
-// UISetup is intended for platform specific UI setup.
-class UISetup {
- public:
-  UISetup(const gfx::Size& screen_size_in_pixels, float device_pixel_ratio) {}
-  ~UISetup() {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(UISetup);
-};
-
-}  // namespace html_viewer
-
-#endif  // COMPONENTS_HTML_VIEWER_UI_SETUP_H_
diff --git a/components/html_viewer/ui_setup_android.cc b/components/html_viewer/ui_setup_android.cc
deleted file mode 100644
index 5fbbc72..0000000
--- a/components/html_viewer/ui_setup_android.cc
+++ /dev/null
@@ -1,115 +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 "components/html_viewer/ui_setup_android.h"
-
-#include "base/memory/singleton.h"
-#include "ui/events/gesture_detection/gesture_configuration.h"
-#include "ui/gfx/geometry/size_conversions.h"
-#include "ui/gfx/screen.h"
-
-namespace html_viewer {
-namespace {
-
-class ScreenMandoline : public gfx::Screen {
- public:
-  ScreenMandoline(const gfx::Size& screen_size_in_pixels,
-                  float device_pixel_ratio)
-      : screen_size_in_pixels_(screen_size_in_pixels),
-        device_pixel_ratio_(device_pixel_ratio) {}
-
-  gfx::Point GetCursorScreenPoint() override { return gfx::Point(); }
-
-  gfx::NativeWindow GetWindowUnderCursor() override {
-    NOTIMPLEMENTED();
-    return NULL;
-  }
-
-  gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override {
-    NOTIMPLEMENTED();
-    return NULL;
-  }
-
-  gfx::Display GetPrimaryDisplay() const override {
-    const gfx::Rect bounds_in_pixels(gfx::Point(), screen_size_in_pixels_);
-    const gfx::Rect bounds_in_dip(gfx::ToCeiledSize(
-        gfx::ScaleSize(bounds_in_pixels.size(), 1.0f / device_pixel_ratio_)));
-    gfx::Display display(0, bounds_in_dip);
-    display.set_device_scale_factor(device_pixel_ratio_);
-    return display;
-  }
-
-  gfx::Display GetDisplayNearestWindow(gfx::NativeView view) const override {
-    return GetPrimaryDisplay();
-  }
-
-  gfx::Display GetDisplayNearestPoint(const gfx::Point& point) const override {
-    return GetPrimaryDisplay();
-  }
-
-  int GetNumDisplays() const override { return 1; }
-
-  std::vector<gfx::Display> GetAllDisplays() const override {
-    return std::vector<gfx::Display>(1, GetPrimaryDisplay());
-  }
-
-  gfx::Display GetDisplayMatching(const gfx::Rect& match_rect) const override {
-    return GetPrimaryDisplay();
-  }
-
-  void AddObserver(gfx::DisplayObserver* observer) override {
-    // no display change on Android.
-  }
-
-  void RemoveObserver(gfx::DisplayObserver* observer) override {
-    // no display change on Android.
-  }
-
- private:
-  const gfx::Size screen_size_in_pixels_;
-  const float device_pixel_ratio_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScreenMandoline);
-};
-
-}  // namespace
-
-// TODO(sky): this needs to come from system.
-class GestureConfigurationMandoline : public ui::GestureConfiguration {
- public:
-  GestureConfigurationMandoline() : GestureConfiguration() {
-    set_double_tap_enabled(false);
-    set_double_tap_timeout_in_ms(semi_long_press_time_in_ms());
-    set_gesture_begin_end_types_enabled(true);
-    set_min_gesture_bounds_length(default_radius());
-    set_min_pinch_update_span_delta(0);
-    set_min_scaling_touch_major(default_radius() * 2);
-    set_velocity_tracker_strategy(
-        ui::VelocityTracker::Strategy::LSQ2_RESTRICTED);
-    set_span_slop(max_touch_move_in_pixels_for_click() * 2);
-    set_swipe_enabled(true);
-    set_two_finger_tap_enabled(true);
-  }
-
-  ~GestureConfigurationMandoline() override {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(GestureConfigurationMandoline);
-};
-
-UISetup::UISetup(const gfx::Size& screen_size_in_pixels,
-                 float device_pixel_ratio)
-    : screen_(new ScreenMandoline(screen_size_in_pixels, device_pixel_ratio)),
-      gesture_configuration_(new GestureConfigurationMandoline) {
-  gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE,
-                                 screen_.get());
-  ui::GestureConfiguration::SetInstance(gesture_configuration_.get());
-}
-
-UISetup::~UISetup() {
-  gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, nullptr);
-  ui::GestureConfiguration::SetInstance(nullptr);
-}
-
-}  // namespace html_viewer
diff --git a/components/html_viewer/ui_setup_android.h b/components/html_viewer/ui_setup_android.h
deleted file mode 100644
index d9638e1..0000000
--- a/components/html_viewer/ui_setup_android.h
+++ /dev/null
@@ -1,38 +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 COMPONENTS_HTML_VIEWER_UI_SETUP_ANDROID_H_
-#define COMPONENTS_HTML_VIEWER_UI_SETUP_ANDROID_H_
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-
-namespace gfx {
-class Screen;
-class Size;
-}
-
-namespace ui {
-class GestureConfiguration;
-}
-
-namespace html_viewer {
-
-class GestureConfigurationMandoline;
-
-class UISetup {
- public:
-  UISetup(const gfx::Size& screen_size_in_pixels, float device_pixel_ratio);
-  ~UISetup();
-
- private:
-  scoped_ptr<gfx::Screen> screen_;
-  scoped_ptr<GestureConfigurationMandoline> gesture_configuration_;
-
-  DISALLOW_COPY_AND_ASSIGN(UISetup);
-};
-
-}  // namespace html_viewer
-
-#endif  // COMPONENTS_HTML_VIEWER_UI_SETUP_ANDROID_H_
diff --git a/components/keyed_service/ios/browser_state_keyed_service_factory.cc b/components/keyed_service/ios/browser_state_keyed_service_factory.cc
index 7c75278..4588feba 100644
--- a/components/keyed_service/ios/browser_state_keyed_service_factory.cc
+++ b/components/keyed_service/ios/browser_state_keyed_service_factory.cc
@@ -77,10 +77,7 @@
 scoped_ptr<KeyedService>
 BrowserStateKeyedServiceFactory::BuildServiceInstanceFor(
     base::SupportsUserData* context) const {
-  // TODO(isherman): The wrapped BuildServiceInstanceFor() should return a
-  // scoped_ptr as well.
-  return make_scoped_ptr(
-      BuildServiceInstanceFor(static_cast<web::BrowserState*>(context)));
+  return BuildServiceInstanceFor(static_cast<web::BrowserState*>(context));
 }
 
 bool BrowserStateKeyedServiceFactory::IsOffTheRecord(
diff --git a/components/keyed_service/ios/browser_state_keyed_service_factory.h b/components/keyed_service/ios/browser_state_keyed_service_factory.h
index 69f28fd..10628758 100644
--- a/components/keyed_service/ios/browser_state_keyed_service_factory.h
+++ b/components/keyed_service/ios/browser_state_keyed_service_factory.h
@@ -90,7 +90,7 @@
 
   // All subclasses of BrowserStateKeyedServiceFactory must return a
   // KeyedService instead of just a BrowserStateKeyedBase.
-  virtual KeyedService* BuildServiceInstanceFor(
+  virtual scoped_ptr<KeyedService> BuildServiceInstanceFor(
       web::BrowserState* context) const = 0;
 
   // A helper object actually listens for notifications about BrowserState
diff --git a/components/nacl/browser/nacl_process_host.cc b/components/nacl/browser/nacl_process_host.cc
index dda1bd3..cc292bf 100644
--- a/components/nacl/browser/nacl_process_host.cc
+++ b/components/nacl/browser/nacl_process_host.cc
@@ -464,7 +464,8 @@
 #if defined(OS_LINUX)
     nonsfi_mode_forced_by_command_line =
         cmd->HasSwitch(switches::kEnableNaClNonSfiMode);
-#if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL)
+#if defined(OS_CHROMEOS) && \
+    (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL))
     nonsfi_mode_allowed = NaClBrowser::GetDelegate()->IsNonSfiModeAllowed(
         nacl_host_message_filter->profile_directory(), manifest_url_);
 #endif
diff --git a/components/nacl/common/nacl_paths.cc b/components/nacl/common/nacl_paths.cc
index f709380..442d1493 100644
--- a/components/nacl/common/nacl_paths.cc
+++ b/components/nacl/common/nacl_paths.cc
@@ -40,13 +40,12 @@
     case FILE_NACL_HELPER:
       return GetNaClHelperPath(kInternalNaClHelperFileName, result);
     case FILE_NACL_HELPER_NONSFI:
-      if (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-              switches::kUseNaClHelperNonSfi) == "false") {
-        // nacl_helper_nonsfi is disabled by the flag. So, use nacl_helper
-        // in Non-SFI mode instead. This is old behavior just in case.
-        // TODO(hidehiko): Remove this code path, when the old feature is
-        // cleaned after nacl_helper_nonsfi is officially launched and
-        // its stability is confirmed.
+      if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+              switches::kUseNaClHelperNonSfi)) {
+        // Currently nacl_helper_nonsfi is disabled, so use nacl_helper
+        // in Non-SFI mode instead.
+        // TODO(hidehiko): Remove this code path after nacl_helper_nonsfi
+        // is supported.
         return GetNaClHelperPath(kInternalNaClHelperFileName, result);
       }
       return GetNaClHelperPath(kInternalNaClHelperNonSfiFileName, result);
diff --git a/components/nacl/loader/nacl_helper_linux.cc b/components/nacl/loader/nacl_helper_linux.cc
index 5b92b5c1..12339512 100644
--- a/components/nacl/loader/nacl_helper_linux.cc
+++ b/components/nacl/loader/nacl_helper_linux.cc
@@ -41,7 +41,9 @@
 #include "crypto/nss_util.h"
 #include "ipc/ipc_descriptors.h"
 #include "ipc/ipc_switches.h"
+#include "sandbox/linux/services/credentials.h"
 #include "sandbox/linux/services/libc_urandom_override.h"
+#include "sandbox/linux/services/namespace_sandbox.h"
 
 #if defined(OS_NACL_NONSFI)
 #include "native_client/src/public/nonsfi/irt_exception_handling.h"
@@ -190,12 +192,28 @@
   }
 
   VLOG(1) << "nacl_helper: forking";
-  pid_t child_pid = fork();
+  pid_t child_pid;
+  if (sandbox::NamespaceSandbox::InNewUserNamespace()) {
+    child_pid = sandbox::NamespaceSandbox::ForkInNewPidNamespace(
+        /*drop_capabilities_in_child=*/true);
+  } else {
+    child_pid = sandbox::Credentials::ForkAndDropCapabilitiesInChild();
+  }
+
   if (child_pid < 0) {
     PLOG(ERROR) << "*** fork() failed.";
   }
 
   if (child_pid == 0) {
+    // Install termiantion signal handlers for nonsfi NaCl. The SFI NaCl runtime
+    // will install signal handlers for SIGINT, SIGTERM, etc. so we do not need
+    // to install termination signal handlers ourselves (in fact, it will crash
+    // if signal handlers for these are present).
+    if (uses_nonsfi_mode && getpid() == 1) {
+      // Note that nonsfi NaCl may override some of these signal handlers, which
+      // is fine.
+      sandbox::NamespaceSandbox::InstallDefaultTerminationSignalHandlers();
+    }
     ChildNaClLoaderInit(child_fds.Pass(),
                         system_info,
                         uses_nonsfi_mode,
diff --git a/components/nacl/loader/nonsfi/irt_exception_handling.cc b/components/nacl/loader/nonsfi/irt_exception_handling.cc
index 327f6a4..ebbeaa0 100644
--- a/components/nacl/loader/nonsfi/irt_exception_handling.cc
+++ b/components/nacl/loader/nonsfi/irt_exception_handling.cc
@@ -49,7 +49,7 @@
                                        not useful for NonSFI NaCl. */);
     signal_handler_function_pointer(&exception_frame.context);
   }
-  _exit(-1);
+  _exit(-sig);
 }
 
 int IrtExceptionHandler(NaClExceptionHandler handler,
diff --git a/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.cc b/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.cc
index 869658f..4ff1d4c0 100644
--- a/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.cc
+++ b/components/nacl/loader/sandbox_linux/nacl_sandbox_linux.cc
@@ -139,10 +139,14 @@
     layer_one_enabled_ = true;
   } else if (sandbox::NamespaceSandbox::InNewUserNamespace()) {
     CHECK(sandbox::Credentials::MoveToNewUserNS());
-    // This relies on SealLayerOneSandbox() to be called later since this
-    // class is keeping a file descriptor to /proc/.
     CHECK(sandbox::Credentials::DropFileSystemAccess(proc_fd_.get()));
-    CHECK(sandbox::Credentials::DropAllCapabilities(proc_fd_.get()));
+
+    // We do not drop CAP_SYS_ADMIN because we need it to place each child
+    // process in its own PID namespace later on.
+    std::vector<sandbox::Credentials::Capability> caps;
+    caps.push_back(sandbox::Credentials::Capability::SYS_ADMIN);
+    CHECK(sandbox::Credentials::SetCapabilities(proc_fd_.get(), caps));
+
     CHECK(IsSandboxed());
     layer_one_enabled_ = true;
   }
diff --git a/components/omnibox.gypi b/components/omnibox.gypi
index 2e3ebe5..b35c90b6 100644
--- a/components/omnibox.gypi
+++ b/components/omnibox.gypi
@@ -12,6 +12,8 @@
         '../base/base.gyp:base',
         '../base/base.gyp:base_i18n',
         '../net/net.gyp:net',
+        '../sql/sql.gyp:sql',
+        '../third_party/protobuf/protobuf.gyp:protobuf_lite',
         '../ui/base/ui_base.gyp:ui_base',
         '../url/url.gyp:url_lib',
         'bookmarks_browser',
@@ -19,6 +21,8 @@
         'components_resources.gyp:components_resources',
         'components_strings.gyp:components_strings',
         'history_core_browser',
+        'keyed_service_core',
+        'omnibox_in_memory_url_index_cache_proto',
         'query_parser',
         'search',
         'search_engines',
@@ -54,6 +58,12 @@
         'omnibox/bookmark_provider.h',
         'omnibox/history_provider.cc',
         'omnibox/history_provider.h',
+        'omnibox/history_quick_provider.cc',
+        'omnibox/history_quick_provider.h',
+        'omnibox/history_url_provider.cc',
+        'omnibox/history_url_provider.h',
+        'omnibox/in_memory_url_index.cc',
+        'omnibox/in_memory_url_index.h',
         'omnibox/in_memory_url_index_types.cc',
         'omnibox/in_memory_url_index_types.h',
         'omnibox/keyword_extensions_delegate.cc',
@@ -66,17 +76,38 @@
         'omnibox/omnibox_log.h',
         'omnibox/omnibox_switches.cc',
         'omnibox/omnibox_switches.h',
+        'omnibox/scored_history_match.cc',
+        'omnibox/scored_history_match.h',
         'omnibox/search_provider.cc',
         'omnibox/search_provider.h',
         'omnibox/search_suggestion_parser.cc',
         'omnibox/search_suggestion_parser.h',
+        'omnibox/shortcuts_backend.cc',
+        'omnibox/shortcuts_backend.h',
+        'omnibox/shortcuts_database.cc',
+        'omnibox/shortcuts_database.h',
         'omnibox/suggestion_answer.cc',
         'omnibox/suggestion_answer.h',
+        'omnibox/url_index_private_data.cc',
+        'omnibox/url_index_private_data.h',
         'omnibox/url_prefix.cc',
         'omnibox/url_prefix.h',
       ],
     },
     {
+      # Protobuf compiler / generator for the InMemoryURLIndex caching
+      # protocol buffer.
+      # GN version: //components/omnibox:in_memory_url_index_cache_proto
+      'target_name': 'omnibox_in_memory_url_index_cache_proto',
+      'type': 'static_library',
+      'sources': [ 'omnibox/in_memory_url_index_cache.proto', ],
+      'variables': {
+        'proto_in_dir': 'omnibox',
+        'proto_out_dir': 'components/omnibox',
+      },
+      'includes': [ '../build/protoc.gypi', ],
+    },
+    {
       # GN version: //components/omnibox:test_support
       'target_name': 'omnibox_test_support',
       'type': 'static_library',
diff --git a/components/omnibox/BUILD.gn b/components/omnibox/BUILD.gn
index a7f9b196..1fa2a2d48 100644
--- a/components/omnibox/BUILD.gn
+++ b/components/omnibox/BUILD.gn
@@ -2,6 +2,8 @@
 # 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")
+
 static_library("omnibox") {
   sources = [
     "answers_cache.cc",
@@ -23,6 +25,12 @@
     "bookmark_provider.h",
     "history_provider.cc",
     "history_provider.h",
+    "history_quick_provider.cc",
+    "history_quick_provider.h",
+    "history_url_provider.cc",
+    "history_url_provider.h",
+    "in_memory_url_index.cc",
+    "in_memory_url_index.h",
     "in_memory_url_index_types.cc",
     "in_memory_url_index_types.h",
     "keyword_extensions_delegate.cc",
@@ -35,12 +43,20 @@
     "omnibox_log.h",
     "omnibox_switches.cc",
     "omnibox_switches.h",
+    "scored_history_match.cc",
+    "scored_history_match.h",
     "search_provider.cc",
     "search_provider.h",
     "search_suggestion_parser.cc",
     "search_suggestion_parser.h",
+    "shortcuts_backend.cc",
+    "shortcuts_backend.h",
+    "shortcuts_database.cc",
+    "shortcuts_database.h",
     "suggestion_answer.cc",
     "suggestion_answer.h",
+    "url_index_private_data.cc",
+    "url_index_private_data.h",
     "url_prefix.cc",
     "url_prefix.h",
   ]
@@ -49,10 +65,12 @@
     "//components/metrics/proto",
   ]
   deps = [
+    ":in_memory_url_index_cache_proto",
     "//base",
     "//base:i18n",
     "//components/bookmarks/browser",
     "//components/history/core/browser",
+    "//components/keyed_service/core",
     "//components/query_parser",
     "//components/resources",
     "//components/search",
@@ -62,11 +80,19 @@
     "//components/variations",
     "//components/variations/net",
     "//net",
+    "//sql",
+    "//third_party/protobuf:protobuf_lite",
     "//ui/base",
     "//url",
   ]
 }
 
+proto_library("in_memory_url_index_cache_proto") {
+  sources = [
+    "in_memory_url_index_cache.proto",
+  ]
+}
+
 static_library("test_support") {
   sources = [
     "test_scheme_classifier.cc",
diff --git a/components/omnibox/DEPS b/components/omnibox/DEPS
index ddeca30..27ba584 100644
--- a/components/omnibox/DEPS
+++ b/components/omnibox/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+components/bookmarks/browser",
   "+components/history/core/browser",
+  "+components/keyed_service/core",
   "+components/metrics/proto",
   "+components/query_parser",
   "+components/search",
@@ -10,6 +11,8 @@
   "+components/variations",
   "+grit",
   "+net",
+  "+sql",
+  "+third_party/protobuf/src/google",
   "+ui/base",
   "+url",
 ]
diff --git a/components/omnibox/autocomplete_provider_client.h b/components/omnibox/autocomplete_provider_client.h
index fa93cea..484612d1 100644
--- a/components/omnibox/autocomplete_provider_client.h
+++ b/components/omnibox/autocomplete_provider_client.h
@@ -9,6 +9,7 @@
 #include "components/history/core/browser/keyword_id.h"
 #include "components/metrics/proto/omnibox_event.pb.h"
 
+class AutocompleteController;
 struct AutocompleteMatch;
 class AutocompleteSchemeClassifier;
 class GURL;
@@ -70,6 +71,16 @@
       const base::string16& term) = 0;
 
   virtual void PrefetchImage(const GURL& url) = 0;
+
+  // Called by |controller| when its results have changed and all providers are
+  // done processing the autocomplete request. At the //chrome level, this
+  // callback results in firing the
+  // NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY notification.
+  // TODO(blundell): Remove the //chrome-level notification entirely in favor of
+  // having AutocompleteController expose a CallbackList that //chrome-level
+  // listeners add themselves to, and then kill this method.
+  virtual void OnAutocompleteControllerResultReady(
+      AutocompleteController* controller) {}
 };
 
 #endif  // COMPONENTS_OMNIBOX_AUTOCOMPLETE_PROVIDER_CLIENT_H_
diff --git a/chrome/browser/autocomplete/history_quick_provider.cc b/components/omnibox/history_quick_provider.cc
similarity index 98%
rename from chrome/browser/autocomplete/history_quick_provider.cc
rename to components/omnibox/history_quick_provider.cc
index ab52fdc..40037b1 100644
--- a/chrome/browser/autocomplete/history_quick_provider.cc
+++ b/components/omnibox/history_quick_provider.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/autocomplete/history_quick_provider.h"
+#include "components/omnibox/history_quick_provider.h"
 
 #include <vector>
 
@@ -15,8 +15,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/autocomplete/history_url_provider.h"
-#include "chrome/browser/autocomplete/in_memory_url_index.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/history/core/browser/history_database.h"
 #include "components/history/core/browser/history_service.h"
@@ -24,6 +22,8 @@
 #include "components/omnibox/autocomplete_match_type.h"
 #include "components/omnibox/autocomplete_provider_client.h"
 #include "components/omnibox/autocomplete_result.h"
+#include "components/omnibox/history_url_provider.h"
+#include "components/omnibox/in_memory_url_index.h"
 #include "components/omnibox/in_memory_url_index_types.h"
 #include "components/omnibox/omnibox_field_trial.h"
 #include "components/search_engines/template_url.h"
diff --git a/chrome/browser/autocomplete/history_quick_provider.h b/components/omnibox/history_quick_provider.h
similarity index 89%
rename from chrome/browser/autocomplete/history_quick_provider.h
rename to components/omnibox/history_quick_provider.h
index 0192efe..a1552d07 100644
--- a/chrome/browser/autocomplete/history_quick_provider.h
+++ b/components/omnibox/history_quick_provider.h
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_AUTOCOMPLETE_HISTORY_QUICK_PROVIDER_H_
-#define CHROME_BROWSER_AUTOCOMPLETE_HISTORY_QUICK_PROVIDER_H_
+#ifndef COMPONENTS_OMNIBOX_HISTORY_QUICK_PROVIDER_H_
+#define COMPONENTS_OMNIBOX_HISTORY_QUICK_PROVIDER_H_
 
 #include <string>
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
-#include "chrome/browser/autocomplete/in_memory_url_index.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/omnibox/autocomplete_input.h"
 #include "components/omnibox/autocomplete_match.h"
 #include "components/omnibox/history_provider.h"
+#include "components/omnibox/in_memory_url_index.h"
 
 struct ScoredHistoryMatch;
 
@@ -62,4 +62,4 @@
   DISALLOW_COPY_AND_ASSIGN(HistoryQuickProvider);
 };
 
-#endif  // CHROME_BROWSER_AUTOCOMPLETE_HISTORY_QUICK_PROVIDER_H_
+#endif  // COMPONENTS_OMNIBOX_HISTORY_QUICK_PROVIDER_H_
diff --git a/chrome/browser/autocomplete/history_url_provider.cc b/components/omnibox/history_url_provider.cc
similarity index 99%
rename from chrome/browser/autocomplete/history_url_provider.cc
rename to components/omnibox/history_url_provider.cc
index bf1f1bc..ec3aa31e8 100644
--- a/chrome/browser/autocomplete/history_url_provider.cc
+++ b/components/omnibox/history_url_provider.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/autocomplete/history_url_provider.h"
+#include "components/omnibox/history_url_provider.h"
 
 #include <algorithm>
 
@@ -17,7 +17,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
-#include "chrome/browser/autocomplete/scored_history_match.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/history/core/browser/history_backend.h"
 #include "components/history/core/browser/history_database.h"
@@ -29,6 +28,7 @@
 #include "components/omnibox/autocomplete_result.h"
 #include "components/omnibox/in_memory_url_index_types.h"
 #include "components/omnibox/omnibox_field_trial.h"
+#include "components/omnibox/scored_history_match.h"
 #include "components/omnibox/url_prefix.h"
 #include "components/search_engines/search_terms_data.h"
 #include "components/search_engines/template_url_service.h"
diff --git a/chrome/browser/autocomplete/history_url_provider.h b/components/omnibox/history_url_provider.h
similarity index 98%
rename from chrome/browser/autocomplete/history_url_provider.h
rename to components/omnibox/history_url_provider.h
index 04c6e8ab..6589cfc 100644
--- a/chrome/browser/autocomplete/history_url_provider.h
+++ b/components/omnibox/history_url_provider.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_AUTOCOMPLETE_HISTORY_URL_PROVIDER_H_
-#define CHROME_BROWSER_AUTOCOMPLETE_HISTORY_URL_PROVIDER_H_
+#ifndef COMPONENTS_OMNIBOX_HISTORY_URL_PROVIDER_H_
+#define COMPONENTS_OMNIBOX_HISTORY_URL_PROVIDER_H_
 
 #include <string>
 #include <vector>
@@ -339,4 +339,4 @@
   DISALLOW_COPY_AND_ASSIGN(HistoryURLProvider);
 };
 
-#endif  // CHROME_BROWSER_AUTOCOMPLETE_HISTORY_URL_PROVIDER_H_
+#endif  // COMPONENTS_OMNIBOX_HISTORY_URL_PROVIDER_H_
diff --git a/chrome/browser/autocomplete/in_memory_url_index.cc b/components/omnibox/in_memory_url_index.cc
similarity index 98%
rename from chrome/browser/autocomplete/in_memory_url_index.cc
rename to components/omnibox/in_memory_url_index.cc
index bd0a33c1..cdcb7a32 100644
--- a/chrome/browser/autocomplete/in_memory_url_index.cc
+++ b/components/omnibox/in_memory_url_index.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/autocomplete/in_memory_url_index.h"
+#include "components/omnibox/in_memory_url_index.h"
 
 #include "base/files/file_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
-#include "chrome/browser/autocomplete/url_index_private_data.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/url_database.h"
+#include "components/omnibox/url_index_private_data.h"
 
 using in_memory_url_index::InMemoryURLIndexCacheItem;
 
diff --git a/chrome/browser/autocomplete/in_memory_url_index.h b/components/omnibox/in_memory_url_index.h
similarity index 97%
rename from chrome/browser/autocomplete/in_memory_url_index.h
rename to components/omnibox/in_memory_url_index.h
index 32399ea3..58bf210 100644
--- a/chrome/browser/autocomplete/in_memory_url_index.h
+++ b/components/omnibox/in_memory_url_index.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_AUTOCOMPLETE_IN_MEMORY_URL_INDEX_H_
-#define CHROME_BROWSER_AUTOCOMPLETE_IN_MEMORY_URL_INDEX_H_
+#ifndef COMPONENTS_OMNIBOX_IN_MEMORY_URL_INDEX_H_
+#define COMPONENTS_OMNIBOX_IN_MEMORY_URL_INDEX_H_
 
 #include <functional>
 #include <map>
@@ -20,11 +20,11 @@
 #include "base/task/cancelable_task_tracker.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_checker.h"
-#include "chrome/browser/autocomplete/scored_history_match.h"
 #include "components/history/core/browser/history_db_task.h"
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/omnibox/scored_history_match.h"
 
 class HistoryQuickProviderTest;
 
@@ -322,4 +322,4 @@
   DISALLOW_COPY_AND_ASSIGN(InMemoryURLIndex);
 };
 
-#endif  // CHROME_BROWSER_AUTOCOMPLETE_IN_MEMORY_URL_INDEX_H_
+#endif  // COMPONENTS_OMNIBOX_IN_MEMORY_URL_INDEX_H_
diff --git a/chrome/browser/autocomplete/in_memory_url_index_cache.proto b/components/omnibox/in_memory_url_index_cache.proto
similarity index 100%
rename from chrome/browser/autocomplete/in_memory_url_index_cache.proto
rename to components/omnibox/in_memory_url_index_cache.proto
diff --git a/chrome/browser/autocomplete/scored_history_match.cc b/components/omnibox/scored_history_match.cc
similarity index 99%
rename from chrome/browser/autocomplete/scored_history_match.cc
rename to components/omnibox/scored_history_match.cc
index d90f930..2914f7d 100644
--- a/chrome/browser/autocomplete/scored_history_match.cc
+++ b/components/omnibox/scored_history_match.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/autocomplete/scored_history_match.h"
+#include "components/omnibox/scored_history_match.h"
 
 #include <math.h>
 
@@ -16,8 +16,8 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_offset_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/autocomplete/history_url_provider.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/omnibox/history_url_provider.h"
 #include "components/omnibox/omnibox_field_trial.h"
 #include "components/omnibox/url_prefix.h"
 
diff --git a/chrome/browser/autocomplete/scored_history_match.h b/components/omnibox/scored_history_match.h
similarity index 97%
rename from chrome/browser/autocomplete/scored_history_match.h
rename to components/omnibox/scored_history_match.h
index d9b5ae45..055e635 100644
--- a/chrome/browser/autocomplete/scored_history_match.h
+++ b/components/omnibox/scored_history_match.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_AUTOCOMPLETE_SCORED_HISTORY_MATCH_H_
-#define CHROME_BROWSER_AUTOCOMPLETE_SCORED_HISTORY_MATCH_H_
+#ifndef COMPONENTS_OMNIBOX_SCORED_HISTORY_MATCH_H_
+#define COMPONENTS_OMNIBOX_SCORED_HISTORY_MATCH_H_
 
 #include <string>
 #include <vector>
@@ -193,4 +193,4 @@
 };
 typedef std::vector<ScoredHistoryMatch> ScoredHistoryMatches;
 
-#endif  // CHROME_BROWSER_AUTOCOMPLETE_SCORED_HISTORY_MATCH_H_
+#endif  // COMPONENTS_OMNIBOX_SCORED_HISTORY_MATCH_H_
diff --git a/components/omnibox/search_provider.cc b/components/omnibox/search_provider.cc
index 77e73d0..4b518d3c 100644
--- a/components/omnibox/search_provider.cc
+++ b/components/omnibox/search_provider.cc
@@ -847,11 +847,11 @@
     return NULL;
   // Send the current page URL if user setting and URL requirements are met and
   // the user is in the field trial.
-  if (CanSendURL(current_page_url_, suggest_url, template_url,
+  if (CanSendURL(input.current_url(), suggest_url, template_url,
                  input.current_page_classification(),
                  template_url_service_->search_terms_data(), client_) &&
       OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial()) {
-    search_term_args.current_page_url = current_page_url_.spec();
+    search_term_args.current_page_url = input.current_url().spec();
     // Create the suggest URL again with the current page URL.
     suggest_url = GURL(template_url->suggestions_url_ref().ReplaceSearchTerms(
         search_term_args,
diff --git a/components/omnibox/search_provider.h b/components/omnibox/search_provider.h
index ab68727..1c633685 100644
--- a/components/omnibox/search_provider.h
+++ b/components/omnibox/search_provider.h
@@ -69,11 +69,6 @@
   // AutocompleteProvider:
   void ResetSession() override;
 
-  // This URL may be sent with suggest requests; see comments on CanSendURL().
-  void set_current_page_url(const GURL& current_page_url) {
-    current_page_url_ = current_page_url;
-  }
-
  protected:
   ~SearchProvider() override;
 
@@ -404,8 +399,6 @@
   // The top navigation suggestion, left blank/invalid if none.
   GURL top_navigation_suggestion_;
 
-  GURL current_page_url_;
-
   // Session token management.
   std::string current_token_;
   base::TimeTicks token_expiration_time_;
diff --git a/chrome/browser/autocomplete/shortcuts_backend.cc b/components/omnibox/shortcuts_backend.cc
similarity index 70%
rename from chrome/browser/autocomplete/shortcuts_backend.cc
rename to components/omnibox/shortcuts_backend.cc
index 5fe69dc7d7..945639b 100644
--- a/chrome/browser/autocomplete/shortcuts_backend.cc
+++ b/components/omnibox/shortcuts_backend.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/autocomplete/shortcuts_backend.h"
+#include "components/omnibox/shortcuts_backend.h"
 
 #include <map>
 #include <string>
@@ -13,13 +13,7 @@
 #include "base/guid.h"
 #include "base/i18n/case_conversion.h"
 #include "base/strings/string_util.h"
-#include "chrome/browser/autocomplete/shortcuts_database.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/history/history_service_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search_engines/template_url_service_factory.h"
-#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
-#include "chrome/common/chrome_constants.h"
+#include "base/thread_task_runner_handle.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/omnibox/autocomplete_input.h"
 #include "components/omnibox/autocomplete_match.h"
@@ -27,16 +21,7 @@
 #include "components/omnibox/autocomplete_result.h"
 #include "components/omnibox/base_search_provider.h"
 #include "components/omnibox/omnibox_log.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_source.h"
-
-#if defined(ENABLE_EXTENSIONS)
-#include "extensions/browser/notification_types.h"
-#include "extensions/common/extension.h"
-#endif
-
-using content::BrowserThread;
+#include "components/omnibox/shortcuts_database.h"
 
 namespace {
 
@@ -76,28 +61,24 @@
 
 // ShortcutsBackend -----------------------------------------------------------
 
-ShortcutsBackend::ShortcutsBackend(Profile* profile, bool suppress_db)
-    : profile_(profile),
+ShortcutsBackend::ShortcutsBackend(
+    TemplateURLService* template_url_service,
+    scoped_ptr<SearchTermsData> search_terms_data,
+    history::HistoryService* history_service,
+    scoped_refptr<base::SequencedTaskRunner> db_runner,
+    base::FilePath database_path,
+    bool suppress_db)
+    : template_url_service_(template_url_service),
+      search_terms_data_(search_terms_data.Pass()),
       current_state_(NOT_INITIALIZED),
       history_service_observer_(this),
+      main_runner_(base::ThreadTaskRunnerHandle::Get()),
+      db_runner_(db_runner),
       no_db_access_(suppress_db) {
-  if (!suppress_db) {
-    db_ = new ShortcutsDatabase(
-        profile->GetPath().Append(chrome::kShortcutsDatabaseName));
-  }
-  // |profile| can be NULL in tests.
-  if (profile) {
-#if defined(ENABLE_EXTENSIONS)
-    notification_registrar_.Add(
-        this,
-        extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
-        content::Source<Profile>(profile));
-#endif
-    history::HistoryService* hs = HistoryServiceFactory::GetForProfile(
-        profile, ServiceAccessType::EXPLICIT_ACCESS);
-    if (hs)
-      history_service_observer_.Add(hs);
-  }
+  if (!suppress_db)
+    db_ = new ShortcutsDatabase(database_path);
+  if (history_service)
+    history_service_observer_.Add(history_service);
 }
 
 bool ShortcutsBackend::Init() {
@@ -110,14 +91,19 @@
   }
 
   current_state_ = INITIALIZING;
-  return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
-      base::Bind(&ShortcutsBackend::InitInternal, this));
+  return db_runner_->PostTask(
+      FROM_HERE, base::Bind(&ShortcutsBackend::InitInternal, this));
 }
 
 bool ShortcutsBackend::DeleteShortcutsWithURL(const GURL& shortcut_url) {
   return initialized() && DeleteShortcutsWithURL(shortcut_url, true);
 }
 
+bool ShortcutsBackend::DeleteShortcutsBeginningWithURL(
+    const GURL& shortcut_url) {
+  return initialized() && DeleteShortcutsWithURL(shortcut_url, false);
+}
+
 void ShortcutsBackend::AddObserver(ShortcutsBackendObserver* obs) {
   observer_list_.AddObserver(obs);
 }
@@ -137,13 +123,16 @@
        ++it) {
     if (match.destination_url == it->second.match_core.destination_url) {
       UpdateShortcut(ShortcutsDatabase::Shortcut(
-          it->second.id, text, MatchToMatchCore(match, profile_), now,
-          it->second.number_of_hits + 1));
+          it->second.id, text, MatchToMatchCore(match, template_url_service_,
+                                                search_terms_data_.get()),
+          now, it->second.number_of_hits + 1));
       return;
     }
   }
   AddShortcut(ShortcutsDatabase::Shortcut(
-      base::GenerateGUID(), text, MatchToMatchCore(match, profile_), now, 1));
+      base::GenerateGUID(), text,
+      MatchToMatchCore(match, template_url_service_, search_terms_data_.get()),
+      now, 1));
 }
 
 ShortcutsBackend::~ShortcutsBackend() {
@@ -151,7 +140,7 @@
     auto* db = db_.get();
     db->AddRef();
     db_ = nullptr;
-    if (!BrowserThread::ReleaseSoon(BrowserThread::DB, FROM_HERE, db))
+    if (!db_runner_->ReleaseSoon(FROM_HERE, db))
       db->Release();
   }
 }
@@ -159,18 +148,17 @@
 // static
 ShortcutsDatabase::Shortcut::MatchCore ShortcutsBackend::MatchToMatchCore(
     const AutocompleteMatch& match,
-    Profile* profile) {
+    TemplateURLService* template_url_service,
+    SearchTermsData* search_terms_data) {
   const AutocompleteMatch::Type match_type = GetTypeForShortcut(match.type);
-  TemplateURLService* service =
-      TemplateURLServiceFactory::GetForProfile(profile);
   const AutocompleteMatch& normalized_match =
-      AutocompleteMatch::IsSpecializedSearchType(match.type) ?
-          BaseSearchProvider::CreateSearchSuggestion(
-              match.search_terms_args->search_terms, match_type,
-              (match.transition == ui::PAGE_TRANSITION_KEYWORD),
-              match.GetTemplateURL(service, false),
-              UIThreadSearchTermsData(profile)) :
-          match;
+      AutocompleteMatch::IsSpecializedSearchType(match.type)
+          ? BaseSearchProvider::CreateSearchSuggestion(
+                match.search_terms_args->search_terms, match_type,
+                (match.transition == ui::PAGE_TRANSITION_KEYWORD),
+                match.GetTemplateURL(template_url_service, false),
+                *search_terms_data)
+          : match;
   return ShortcutsDatabase::Shortcut::MatchCore(
       normalized_match.fill_into_edit, normalized_match.destination_url,
       normalized_match.contents,
@@ -181,28 +169,9 @@
 }
 
 void ShortcutsBackend::ShutdownOnUIThread() {
-  DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::UI) ||
-         BrowserThread::CurrentlyOn(BrowserThread::UI));
-  notification_registrar_.RemoveAll();
   history_service_observer_.RemoveAll();
 }
 
-void ShortcutsBackend::Observe(int type,
-                               const content::NotificationSource& source,
-                               const content::NotificationDetails& details) {
-#if defined(ENABLE_EXTENSIONS)
-  DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, type);
-  if (!initialized())
-    return;
-
-  // When an extension is unloaded, we want to remove any Shortcuts associated
-  // with it.
-  DeleteShortcutsWithURL(content::Details<extensions::UnloadedExtensionInfo>(
-                             details)->extension->url(),
-                         false);
-#endif
-}
-
 void ShortcutsBackend::OnURLsDeleted(history::HistoryService* history_service,
                                      bool all_history,
                                      bool expired,
@@ -242,8 +211,8 @@
     (*temp_guid_map_)[it->first] = temp_shortcuts_map_->insert(
         std::make_pair(base::i18n::ToLower(it->second.text), it->second));
   }
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-      base::Bind(&ShortcutsBackend::InitCompleted, this));
+  main_runner_->PostTask(FROM_HERE,
+                         base::Bind(&ShortcutsBackend::InitCompleted, this));
 }
 
 void ShortcutsBackend::InitCompleted() {
@@ -266,8 +235,8 @@
   FOR_EACH_OBSERVER(ShortcutsBackendObserver, observer_list_,
                     OnShortcutsChanged());
   return no_db_access_ ||
-         BrowserThread::PostTask(
-             BrowserThread::DB, FROM_HERE,
+         db_runner_->PostTask(
+             FROM_HERE,
              base::Bind(base::IgnoreResult(&ShortcutsDatabase::AddShortcut),
                         db_.get(), shortcut));
 }
@@ -284,8 +253,8 @@
   FOR_EACH_OBSERVER(ShortcutsBackendObserver, observer_list_,
                     OnShortcutsChanged());
   return no_db_access_ ||
-         BrowserThread::PostTask(
-             BrowserThread::DB, FROM_HERE,
+         db_runner_->PostTask(
+             FROM_HERE,
              base::Bind(base::IgnoreResult(&ShortcutsDatabase::UpdateShortcut),
                         db_.get(), shortcut));
 }
@@ -304,8 +273,8 @@
   FOR_EACH_OBSERVER(ShortcutsBackendObserver, observer_list_,
                     OnShortcutsChanged());
   return no_db_access_ ||
-         BrowserThread::PostTask(
-             BrowserThread::DB, FROM_HERE,
+         db_runner_->PostTask(
+             FROM_HERE,
              base::Bind(
                  base::IgnoreResult(&ShortcutsDatabase::DeleteShortcutsWithIDs),
                  db_.get(), shortcut_ids));
@@ -330,8 +299,8 @@
   FOR_EACH_OBSERVER(ShortcutsBackendObserver, observer_list_,
                     OnShortcutsChanged());
   return no_db_access_ ||
-         BrowserThread::PostTask(
-             BrowserThread::DB, FROM_HERE,
+         db_runner_->PostTask(
+             FROM_HERE,
              base::Bind(
                  base::IgnoreResult(&ShortcutsDatabase::DeleteShortcutsWithURL),
                  db_.get(), url_spec));
@@ -345,9 +314,8 @@
   FOR_EACH_OBSERVER(ShortcutsBackendObserver, observer_list_,
                     OnShortcutsChanged());
   return no_db_access_ ||
-         BrowserThread::PostTask(
-             BrowserThread::DB, FROM_HERE,
-             base::Bind(
-                 base::IgnoreResult(&ShortcutsDatabase::DeleteAllShortcuts),
-                 db_.get()));
+         db_runner_->PostTask(
+             FROM_HERE, base::Bind(base::IgnoreResult(
+                                       &ShortcutsDatabase::DeleteAllShortcuts),
+                                   db_.get()));
 }
diff --git a/chrome/browser/autocomplete/shortcuts_backend.h b/components/omnibox/shortcuts_backend.h
similarity index 81%
rename from chrome/browser/autocomplete/shortcuts_backend.h
rename to components/omnibox/shortcuts_backend.h
index 84290347..5b06f59 100644
--- a/chrome/browser/autocomplete/shortcuts_backend.h
+++ b/components/omnibox/shortcuts_backend.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_AUTOCOMPLETE_SHORTCUTS_BACKEND_H_
-#define CHROME_BROWSER_AUTOCOMPLETE_SHORTCUTS_BACKEND_H_
+#ifndef COMPONENTS_OMNIBOX_SHORTCUTS_BACKEND_H_
+#define COMPONENTS_OMNIBOX_SHORTCUTS_BACKEND_H_
 
 #include <map>
 #include <string>
@@ -15,36 +15,40 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
 #include "base/scoped_observer.h"
+#include "base/sequenced_task_runner.h"
 #include "base/strings/string16.h"
 #include "base/synchronization/lock.h"
 #include "base/time/time.h"
-#include "chrome/browser/autocomplete/shortcuts_database.h"
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/keyed_service/core/refcounted_keyed_service.h"
 #include "components/omnibox/autocomplete_match.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
+#include "components/omnibox/shortcuts_database.h"
+#include "components/search_engines/search_terms_data.h"
 #include "url/gurl.h"
 
-class Profile;
+class TemplateURLService;
 
 namespace history {
+class HistoryService;
 class ShortcutsDatabase;
 };  // namespace history
 
 // This class manages the shortcut provider backend - access to database on the
 // db thread, etc.
 class ShortcutsBackend : public RefcountedKeyedService,
-                         public content::NotificationObserver,
                          public history::HistoryServiceObserver {
  public:
   typedef std::multimap<base::string16, const ShortcutsDatabase::Shortcut>
       ShortcutMap;
 
-  // |profile| is necessary for profile notifications only and can be NULL in
-  // unit-tests. For unit testing, set |suppress_db| to true to prevent creation
+  // For unit testing, set |suppress_db| to true to prevent creation
   // of the database, in which case all operations are performed in memory only.
-  ShortcutsBackend(Profile* profile, bool suppress_db);
+  ShortcutsBackend(TemplateURLService* template_url_service,
+                   scoped_ptr<SearchTermsData> search_terms_data,
+                   history::HistoryService* history_service,
+                   scoped_refptr<base::SequencedTaskRunner> db_runner,
+                   base::FilePath database_path,
+                   bool suppress_db);
 
   // The interface is guaranteed to be called on the thread AddObserver()
   // was called.
@@ -71,6 +75,9 @@
   // Deletes the Shortcuts with the url.
   bool DeleteShortcutsWithURL(const GURL& shortcut_url);
 
+  // Deletes the Shortcuts that begin with the url.
+  bool DeleteShortcutsBeginningWithURL(const GURL& shortcut_url);
+
   void AddObserver(ShortcutsBackendObserver* obs);
   void RemoveObserver(ShortcutsBackendObserver* obs);
 
@@ -98,16 +105,12 @@
 
   static ShortcutsDatabase::Shortcut::MatchCore MatchToMatchCore(
       const AutocompleteMatch& match,
-      Profile* profile);
+      TemplateURLService* template_url_service,
+      SearchTermsData* search_terms_data);
 
   // RefcountedKeyedService:
   void ShutdownOnUIThread() override;
 
-  // content::NotificationObserver:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
   // history::HistoryServiceObserver:
   void OnURLsDeleted(history::HistoryService* history_service,
                      bool all_history,
@@ -139,7 +142,9 @@
   // Deletes all of the shortcuts.
   bool DeleteAllShortcuts();
 
-  Profile* profile_;
+  TemplateURLService* template_url_service_;
+  scoped_ptr<SearchTermsData> search_terms_data_;
+
   CurrentState current_state_;
   base::ObserverList<ShortcutsBackendObserver> observer_list_;
   scoped_refptr<ShortcutsDatabase> db_;
@@ -154,14 +159,16 @@
   // This is a helper map for quick access to a shortcut by guid.
   GuidMap guid_map_;
 
-  content::NotificationRegistrar notification_registrar_;
   ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
       history_service_observer_;
 
+  scoped_refptr<base::SequencedTaskRunner> main_runner_;
+  scoped_refptr<base::SequencedTaskRunner> db_runner_;
+
   // For some unit-test only.
   bool no_db_access_;
 
   DISALLOW_COPY_AND_ASSIGN(ShortcutsBackend);
 };
 
-#endif  // CHROME_BROWSER_AUTOCOMPLETE_SHORTCUTS_BACKEND_H_
+#endif  // COMPONENTS_OMNIBOX_SHORTCUTS_BACKEND_H_
diff --git a/chrome/browser/autocomplete/shortcuts_database.cc b/components/omnibox/shortcuts_database.cc
similarity index 98%
rename from chrome/browser/autocomplete/shortcuts_database.cc
rename to components/omnibox/shortcuts_database.cc
index 94bbe891..29fb26d4 100644
--- a/chrome/browser/autocomplete/shortcuts_database.cc
+++ b/components/omnibox/shortcuts_database.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/autocomplete/shortcuts_database.h"
+#include "components/omnibox/shortcuts_database.h"
 
 #include <string>
 
diff --git a/chrome/browser/autocomplete/shortcuts_database.h b/components/omnibox/shortcuts_database.h
similarity index 95%
rename from chrome/browser/autocomplete/shortcuts_database.h
rename to components/omnibox/shortcuts_database.h
index 3e3ace0..c942dde 100644
--- a/chrome/browser/autocomplete/shortcuts_database.h
+++ b/components/omnibox/shortcuts_database.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_AUTOCOMPLETE_SHORTCUTS_DATABASE_H_
-#define CHROME_BROWSER_AUTOCOMPLETE_SHORTCUTS_DATABASE_H_
+#ifndef COMPONENTS_OMNIBOX_SHORTCUTS_DATABASE_H_
+#define COMPONENTS_OMNIBOX_SHORTCUTS_DATABASE_H_
 
 #include <map>
 #include <string>
@@ -130,4 +130,4 @@
   DISALLOW_COPY_AND_ASSIGN(ShortcutsDatabase);
 };
 
-#endif  // CHROME_BROWSER_AUTOCOMPLETE_SHORTCUTS_DATABASE_H_
+#endif  // COMPONENTS_OMNIBOX_SHORTCUTS_DATABASE_H_
diff --git a/chrome/browser/autocomplete/url_index_private_data.cc b/components/omnibox/url_index_private_data.cc
similarity index 99%
rename from chrome/browser/autocomplete/url_index_private_data.cc
rename to components/omnibox/url_index_private_data.cc
index ed493249..32e611f 100644
--- a/chrome/browser/autocomplete/url_index_private_data.cc
+++ b/components/omnibox/url_index_private_data.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/autocomplete/url_index_private_data.h"
+#include "components/omnibox/url_index_private_data.h"
 
 #include <functional>
 #include <iterator>
@@ -19,12 +19,12 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
-#include "chrome/browser/autocomplete/in_memory_url_index.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/history/core/browser/history_database.h"
 #include "components/history/core/browser/history_db_task.h"
 #include "components/history/core/browser/history_service.h"
+#include "components/omnibox/in_memory_url_index.h"
 #include "net/base/net_util.h"
 
 #if defined(USE_SYSTEM_PROTOBUF)
diff --git a/chrome/browser/autocomplete/url_index_private_data.h b/components/omnibox/url_index_private_data.h
similarity index 97%
rename from chrome/browser/autocomplete/url_index_private_data.h
rename to components/omnibox/url_index_private_data.h
index c6f47613..b745bf2 100644
--- a/chrome/browser/autocomplete/url_index_private_data.h
+++ b/components/omnibox/url_index_private_data.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_AUTOCOMPLETE_URL_INDEX_PRIVATE_DATA_H_
-#define CHROME_BROWSER_AUTOCOMPLETE_URL_INDEX_PRIVATE_DATA_H_
+#ifndef COMPONENTS_OMNIBOX_URL_INDEX_PRIVATE_DATA_H_
+#define COMPONENTS_OMNIBOX_URL_INDEX_PRIVATE_DATA_H_
 
 #include <set>
 #include <string>
@@ -11,10 +11,10 @@
 #include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted.h"
-#include "chrome/browser/autocomplete/in_memory_url_index_cache.pb.h"
-#include "chrome/browser/autocomplete/scored_history_match.h"
 #include "components/history/core/browser/history_service.h"
+#include "components/omnibox/in_memory_url_index_cache.pb.h"
 #include "components/omnibox/in_memory_url_index_types.h"
+#include "components/omnibox/scored_history_match.h"
 
 class HistoryQuickProviderTest;
 
@@ -407,4 +407,4 @@
   size_t post_scoring_item_count_;  // After performing final filter/scoring.
 };
 
-#endif  // CHROME_BROWSER_AUTOCOMPLETE_URL_INDEX_PRIVATE_DATA_H_
+#endif  // COMPONENTS_OMNIBOX_URL_INDEX_PRIVATE_DATA_H_
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index e786566..d4609ce 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -199,7 +199,7 @@
 }  // namespace
 
 LoginDatabase::LoginDatabase(const base::FilePath& db_path)
-    : db_path_(db_path) {
+    : db_path_(db_path), clear_password_values_(false) {
 }
 
 LoginDatabase::~LoginDatabase() {
@@ -534,8 +534,9 @@
   if (!DoesMatchConstraints(form))
     return list;
   std::string encrypted_password;
-  if (EncryptedString(form.password_value, &encrypted_password) !=
-      ENCRYPTION_RESULT_SUCCESS)
+  if (EncryptedString(
+          clear_password_values_ ? base::string16() : form.password_value,
+          &encrypted_password) != ENCRYPTION_RESULT_SUCCESS)
     return list;
 
   // You *must* change LoginTableColumns if this query changes.
@@ -578,8 +579,9 @@
 
 PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) {
   std::string encrypted_password;
-  if (EncryptedString(form.password_value, &encrypted_password) !=
-      ENCRYPTION_RESULT_SUCCESS)
+  if (EncryptedString(
+          clear_password_values_ ? base::string16() : form.password_value,
+          &encrypted_password) != ENCRYPTION_RESULT_SUCCESS)
     return PasswordStoreChangeList();
 
   // Replacement is necessary to deal with updating imported credentials. See
diff --git a/components/password_manager/core/browser/login_database.h b/components/password_manager/core/browser/login_database.h
index ca3db84..96257e7 100644
--- a/components/password_manager/core/browser/login_database.h
+++ b/components/password_manager/core/browser/login_database.h
@@ -111,6 +111,8 @@
 
   StatisticsTable& stats_table() { return stats_table_; }
 
+  void set_clear_password_values(bool val) { clear_password_values_ = val; }
+
  private:
   // Result values for encryption/decryption actions.
   enum EncryptionResult {
@@ -170,6 +172,13 @@
   sql::MetaTable meta_table_;
   StatisticsTable stats_table_;
 
+  // If set to 'true', then the password values are cleared before encrypting
+  // and storing in the database. At the same time AddLogin/UpdateLogin return
+  // PasswordStoreChangeList containing the real password.
+  // This is a temporary measure for migration the Keychain on Mac.
+  // crbug.com/466638
+  bool clear_password_values_;
+
   DISALLOW_COPY_AND_ASSIGN(LoginDatabase);
 };
 
diff --git a/components/password_manager/core/browser/login_database_mac.cc b/components/password_manager/core/browser/login_database_mac.cc
index ad6eaa5..ffaa1c34 100644
--- a/components/password_manager/core/browser/login_database_mac.cc
+++ b/components/password_manager/core/browser/login_database_mac.cc
@@ -4,9 +4,7 @@
 
 #include "components/password_manager/core/browser/login_database.h"
 
-// On the Mac, the LoginDatabase nulls out passwords, so that we can use the
-// rest of the database as a suplemental storage system to complement Keychain,
-// providing storage of fields Keychain doesn't allow.
+#include "components/os_crypt/os_crypt.h"
 
 namespace password_manager {
 
@@ -14,16 +12,18 @@
 LoginDatabase::EncryptionResult LoginDatabase::EncryptedString(
     const base::string16& plain_text,
     std::string* cipher_text) {
-  *cipher_text = std::string();
-  return ENCRYPTION_RESULT_SUCCESS;
+  return OSCrypt::EncryptString16(plain_text, cipher_text)
+             ? ENCRYPTION_RESULT_SUCCESS
+             : ENCRYPTION_RESULT_SERVICE_FAILURE;
 }
 
 // static
 LoginDatabase::EncryptionResult LoginDatabase::DecryptedString(
     const std::string& cipher_text,
     base::string16* plain_text) {
-  *plain_text = base::string16();
-  return ENCRYPTION_RESULT_SUCCESS;
+  return OSCrypt::DecryptString16(cipher_text, plain_text)
+             ? ENCRYPTION_RESULT_SUCCESS
+             : ENCRYPTION_RESULT_SERVICE_FAILURE;
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index fd11d08..9e323ce8 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -23,6 +23,10 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if defined(OS_MACOSX)
+#include "components/os_crypt/os_crypt.h"
+#endif
+
 using autofill::PasswordForm;
 using base::ASCIIToUTF16;
 using ::testing::Eq;
@@ -39,15 +43,6 @@
       1, PasswordStoreChange(PasswordStoreChange::UPDATE, form));
 }
 
-void FormsAreEqual(const PasswordForm& expected, const PasswordForm& actual) {
-  PasswordForm expected_copy(expected);
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-  // On the Mac we should never be storing passwords in the database.
-  expected_copy.password_value = ASCIIToUTF16("");
-#endif
-  EXPECT_EQ(expected_copy, actual);
-}
-
 void GenerateExamplePasswordForm(PasswordForm* form) {
   form->origin = GURL("http://accounts.google.com/LoginAuth");
   form->action = GURL("http://accounts.google.com/Login");
@@ -80,6 +75,9 @@
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     file_ = temp_dir_.path().AppendASCII("TestMetadataStoreMacDatabase");
+#if defined(OS_MACOSX)
+    OSCrypt::UseMockKeychain(true);
+#endif  // defined(OS_MACOSX)
 
     db_.reset(new LoginDatabase(file_));
     ASSERT_TRUE(db_->Init());
@@ -183,13 +181,13 @@
   EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
   EXPECT_TRUE(db().GetAutofillableLogins(&result));
   ASSERT_EQ(1U, result.size());
-  FormsAreEqual(form, *result[0]);
+  EXPECT_EQ(form, *result[0]);
   result.clear();
 
   // Match against an exact copy.
   EXPECT_TRUE(db().GetLogins(form, &result));
   ASSERT_EQ(1U, result.size());
-  FormsAreEqual(form, *result[0]);
+  EXPECT_EQ(form, *result[0]);
   result.clear();
 
   // The example site changes...
@@ -267,12 +265,7 @@
   EXPECT_TRUE(db().GetAutofillableLogins(&result));
   EXPECT_EQ(1U, result.size());
   // Password element was updated.
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-  // On the Mac we should never be storing passwords in the database.
-  EXPECT_EQ(base::string16(), result[0]->password_value);
-#else
   EXPECT_EQ(form6.password_value, result[0]->password_value);
-#endif
   // Preferred login.
   EXPECT_TRUE(form6.preferred);
   result.clear();
@@ -752,13 +745,13 @@
   // GetLogins should give the blacklisted result.
   EXPECT_TRUE(db().GetLogins(form, &result));
   ASSERT_EQ(1U, result.size());
-  FormsAreEqual(form, *result[0]);
+  EXPECT_EQ(form, *result[0]);
   result.clear();
 
   // So should GetAllBlacklistedLogins.
   EXPECT_TRUE(db().GetBlacklistLogins(&result));
   ASSERT_EQ(1U, result.size());
-  FormsAreEqual(form, *result[0]);
+  EXPECT_EQ(form, *result[0]);
   result.clear();
 }
 
@@ -815,13 +808,7 @@
   EXPECT_EQ(incomplete_form.origin, result[0]->origin);
   EXPECT_EQ(incomplete_form.signon_realm, result[0]->signon_realm);
   EXPECT_EQ(incomplete_form.username_value, result[0]->username_value);
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-  // On Mac, passwords are not stored in login database, instead they're in
-  // the keychain.
-  EXPECT_TRUE(result[0]->password_value.empty());
-#else
   EXPECT_EQ(incomplete_form.password_value, result[0]->password_value);
-#endif  // OS_MACOSX && !OS_IOS
   EXPECT_TRUE(result[0]->preferred);
   EXPECT_FALSE(result[0]->ssl_valid);
 
@@ -851,9 +838,6 @@
 
   // This time we should have all the info available.
   PasswordForm expected_form(completed_form);
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-  expected_form.password_value.clear();
-#endif  // OS_MACOSX && !OS_IOS
   EXPECT_EQ(expected_form, *result[0]);
   result.clear();
 }
@@ -903,12 +887,6 @@
   EXPECT_TRUE(db().GetAutofillableLogins(&result));
   ASSERT_EQ(2U, result.size());
 
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-  // On Mac, passwords are not stored in login database, instead they're in
-  // the keychain.
-  complete_form.password_value.clear();
-  incomplete_form.password_value.clear();
-#endif  // OS_MACOSX && !OS_IOS
   if (result[0]->username_element.empty())
     std::swap(result[0], result[1]);
   EXPECT_EQ(complete_form, *result[0]);
@@ -987,11 +965,6 @@
   ScopedVector<autofill::PasswordForm> result;
   EXPECT_TRUE(db().GetLogins(form, &result));
   ASSERT_EQ(1U, result.size());
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-  // On Mac, passwords are not stored in login database, instead they're in
-  // the keychain.
-  form.password_value.clear();
-#endif  // OS_MACOSX && !OS_IOS
   EXPECT_EQ(form, *result[0]);
 }
 
@@ -1121,6 +1094,39 @@
       1);
 }
 
+TEST_F(LoginDatabaseTest, ClearPasswordValues) {
+  db().set_clear_password_values(true);
+
+  // Add a PasswordForm, the password should be cleared.
+  base::HistogramTester histogram_tester;
+  PasswordForm form;
+  form.origin = GURL("http://accounts.google.com/LoginAuth");
+  form.signon_realm = "http://accounts.google.com/";
+  form.username_value = ASCIIToUTF16("my_username");
+  form.password_value = ASCIIToUTF16("12345");
+  EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
+
+  ScopedVector<autofill::PasswordForm> result;
+  EXPECT_TRUE(db().GetLogins(form, &result));
+  ASSERT_EQ(1U, result.size());
+  PasswordForm expected_form = form;
+  expected_form.password_value.clear();
+  EXPECT_EQ(expected_form, *result[0]);
+
+  // Update the password, it should stay empty.
+  form.password_value = ASCIIToUTF16("password");
+  EXPECT_EQ(UpdateChangeForForm(form), db().UpdateLogin(form));
+  EXPECT_TRUE(db().GetLogins(form, &result));
+  ASSERT_EQ(1U, result.size());
+  EXPECT_EQ(expected_form, *result[0]);
+
+  // Encrypting/decrypting shouldn't happen. Thus there should be no keychain
+  // access on Mac.
+  scoped_ptr<base::HistogramSamples> samples =
+      histogram_tester.GetHistogramSamplesSinceCreation("OSX.Keychain.Access");
+  EXPECT_TRUE(!samples || samples->TotalCount() == 0);
+}
+
 #if defined(OS_POSIX)
 // Only the current user has permission to read the database.
 //
@@ -1144,6 +1150,9 @@
                                   .AppendASCII("data")
                                   .AppendASCII("password_manager");
     database_path_ = temp_dir_.path().AppendASCII("test.db");
+#if defined(OS_MACOSX)
+    OSCrypt::UseMockKeychain(true);
+#endif  // defined(OS_MACOSX)
   }
 
   // Creates the databse from |sql_file|.
@@ -1235,7 +1244,7 @@
     ScopedVector<autofill::PasswordForm> result;
     EXPECT_TRUE(db.GetLogins(form, &result));
     ASSERT_EQ(1U, result.size());
-    FormsAreEqual(form, *result[0]);
+    EXPECT_EQ(form, *result[0]);
     EXPECT_TRUE(db.RemoveLogin(form));
   }
   // New date, in microseconds since platform independent epoch.
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index b83e1c0..238825f 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -470,32 +470,21 @@
   if (best_matches_.empty())
     return;
 
-  // Do not autofill on sign-up or change password forms (until we have some
-  // working change password functionality). Combined login-sign-up forms are OK
-  // to autofill in the login part.
-  if (observed_form_.layout != PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP &&
-      !observed_form_.new_password_element.empty()) {
-    if (client_->IsLoggingActive()) {
-      BrowserSavePasswordProgressLogger logger(client_);
-      logger.LogMessage(Logger::STRING_PROCESS_FRAME_METHOD);
-      logger.LogMessage(Logger::STRING_FORM_NOT_AUTOFILLED);
-    }
-    return;
-  }
-
   // Proceed to autofill.
   // Note that we provide the choices but don't actually prefill a value if:
   // (1) we are in Incognito mode, (2) the ACTION paths don't match,
-  // or (3) if it matched using public suffix domain matching.
-  // However, 2 and 3 should not apply to Android-based credentials found via
-  // affiliation-based matching (we want to autofill them).
+  // (3) if it matched using public suffix domain matching, or
+  // (4) the form is change password form.
+  // However, 2 and 3 should not apply to Android-based credentials found
+  // via affiliation-based matching (we want to autofill them).
   // TODO(engedy): Clean this up. See: https://crbug.com/476519.
   bool wait_for_username =
       client_->IsOffTheRecord() ||
       (!IsValidAndroidFacetURI(preferred_match_->original_signon_realm) &&
        (observed_form_.action.GetWithEmptyPath() !=
             preferred_match_->action.GetWithEmptyPath() ||
-        preferred_match_->IsPublicSuffixMatch()));
+        preferred_match_->IsPublicSuffixMatch() ||
+        observed_form_.IsPossibleChangePasswordForm()));
   if (wait_for_username)
     manager_action_ = kManagerActionNone;
   else
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index 1e1c71f..236e882 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -1522,4 +1522,26 @@
   EXPECT_EQ(kNewValue, PasswordFormManager::PasswordToSave(form));
 }
 
+TEST_F(PasswordFormManagerTest, TestSuggestingPasswordChangeForms) {
+  // Suggesting password on the password change form on the previously visited
+  // site. Credentials are found in the password store, and are not blacklisted.
+  TestPasswordManager password_manager(client());
+  PasswordForm observed_change_password_form = *observed_form();
+  observed_change_password_form.new_password_element =
+      base::ASCIIToUTF16("new_pwd");
+  PasswordFormManager manager_creds(&password_manager, client(),
+                                    client()->driver(),
+                                    observed_change_password_form, false);
+  manager_creds.SimulateFetchMatchingLoginsFromPasswordStore();
+  ScopedVector<PasswordForm> simulated_results;
+  simulated_results.push_back(CreateSavedMatch(false));
+  manager_creds.OnGetPasswordStoreResults(simulated_results.Pass());
+  Mock::VerifyAndClearExpectations(client()->mock_driver());
+
+  // Check that Autofill message was sent.
+  EXPECT_EQ(1u, password_manager.GetLatestBestMatches().size());
+  // Check that we suggest, not autofill.
+  EXPECT_TRUE(password_manager.GetLatestWaitForUsername());
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 2df5a91b..1837fcc 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -15,6 +15,7 @@
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/common/form_data_predictions.h"
 #include "components/autofill/core/common/password_form_field_prediction_map.h"
+#include "components/password_manager/core/browser/affiliation_utils.h"
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
 #include "components/password_manager/core/browser/password_autofill_manager.h"
 #include "components/password_manager/core/browser/password_form_manager.h"
@@ -110,6 +111,19 @@
   return true;
 }
 
+bool ContainsAndroidCredentials(
+    const autofill::PasswordFormFillData& fill_data) {
+  for (const auto& login : fill_data.additional_logins) {
+    if (FacetURI::FromPotentiallyInvalidSpec(
+            login.second.realm).IsValidAndroidFacetURI()) {
+      return true;
+    }
+  }
+
+  return FacetURI::FromPotentiallyInvalidSpec(
+             fill_data.preferred_realm).IsValidAndroidFacetURI();
+}
+
 }  // namespace
 
 // static
@@ -671,6 +685,9 @@
                                &fill_data);
       if (logger)
         logger->LogBoolean(Logger::STRING_WAIT_FOR_USERNAME, wait_for_username);
+      UMA_HISTOGRAM_BOOLEAN(
+          "PasswordManager.FillSuggestionsIncludeAndroidAppCredentials",
+          ContainsAndroidCredentials(fill_data));
       driver->FillPasswordForm(fill_data);
       break;
     }
diff --git a/components/policy/BUILD.gn b/components/policy/BUILD.gn
index 6965da8..59d77a58 100644
--- a/components/policy/BUILD.gn
+++ b/components/policy/BUILD.gn
@@ -284,7 +284,7 @@
       consolidate_target_name = "${target_name}__consolidate_resources"
       copy(consolidate_target_name) {
         sources = [
-          app_restrictions_path
+          app_restrictions_path,
         ]
         outputs = [
           "$generated_resources_dir/xml-v21/app_restrictions.xml",
@@ -300,7 +300,8 @@
       # use. The public rules (in rules.gni) should be updated to support what
       # we are doing here.
       build_config_path = "$target_gen_dir/$target_name.build_config"
-      write_build_config("${target_name}__build_config") {
+      build_config_target_name = "${target_name}__build_config"
+      write_build_config(build_config_target_name) {
         build_config = build_config_path
         resources_zip = "$root_build_dir/$resources_zip"
         type = "android_resources"
@@ -312,9 +313,8 @@
         outputs = [
           "$root_build_dir/$resources_zip",
         ]
-        generated_resources =
-            get_target_outputs(":$consolidate_target_name") +
-            policy_templates_android_outputs
+        generated_resources = get_target_outputs(":$consolidate_target_name") +
+                              policy_templates_android_outputs
         inputs = generated_resources
         rebased_inputs = rebase_path(generated_resources, root_build_dir)
         rebased_resources_dir =
@@ -331,6 +331,7 @@
       }
 
       deps = [
+        ":$build_config_target_name",
         ":$zip_target_name",
       ]
     }
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index fef1df2..196aaf3 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -123,7 +123,7 @@
 #   persistent IDs for all fields (but not for groups!) are needed. These are
 #   specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs,
 #   because doing so would break the deployed wire format!
-#   For your editing convenience: highest ID currently used: 301
+#   For your editing convenience: highest ID currently used: 302
 #
 # Placeholders:
 #   The following placeholder strings are automatically substituted:
@@ -7357,7 +7357,46 @@
       'desc': '''If this policy is set to true or not set usage of QUIC protocol in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> is allowed.
       If this policy is set to false usage of QUIC protocol is disallowed.''',
     },
+    {
+      'name': 'KeyPermissions',
+      'type': 'dict',
+      'schema': {
+        'type': 'object',
+        'additionalProperties': {
+          'type': 'object',
+          'properties': {
+            'allowCorporateKeyUsage': {
+              'description': '''If set to true, this extension can use all keys that are designated for corporate usage to sign arbitrary data. If set to false, it cannot access any such keys and the user cannot grant such permission either.''',
+              'type': 'boolean',
+            },
+          },
+        },
+      },
+      'supported_on': ['chrome_os:45-'],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'example_value': {
+        'extension1': {
+          'allowCorporateKeyUsage': 'true'
+        },
+        'extension2': {
+          'allowCorporateKeyUsage': 'false'
+        }
+      },
+      'id': 302,
+      'caption': 'Key Permissions',
+      'desc': '''Grants access to corporate keys to extensions.
 
+      Keys are designated for corporate usage if they're generated using the chrome.platformKeys API on a managed account. Keys imported or generated in another way are not designated for corporate usage.
+
+      Access to keys designated for corporate usage is solely controlled by this policy. The user can neither grant nor withdraw access to corporate keys to or from extensions.
+
+      By default an extension cannot use a key designated for corporate usage, which is equivalent to setting allowCorporateKeyUsage to false for that extension.
+
+      Only if allowCorporateKeyUsage is set to true for an extension, it can use any platform key marked for corporate usage to sign arbitrary data. This permission should only be granted if the extension is trusted to secure access to the key against attackers.''',
+    },
   ],
   'messages': {
     # Messages that are not associated to any policies.
diff --git a/components/rappor/rappor_metric_unittest.cc b/components/rappor/rappor_metric_unittest.cc
index b8e28dd..7b0fc27 100644
--- a/components/rappor/rappor_metric_unittest.cc
+++ b/components/rappor/rappor_metric_unittest.cc
@@ -20,7 +20,7 @@
     PROBABILITY_50 /* Fake one probability */,
     PROBABILITY_75 /* One coin probability */,
     PROBABILITY_50 /* Zero coin probability */,
-    FINE_LEVEL /* Reporting level (not used) */};
+    UMA_RAPPOR_GROUP /* Recording group (not used) */};
 
 const RapporParameters kTestStatsRapporParameters = {
     1 /* Num cohorts */,
@@ -30,7 +30,7 @@
     PROBABILITY_50 /* Fake one probability */,
     PROBABILITY_75 /* One coin probability */,
     PROBABILITY_50 /* Zero coin probability */,
-    FINE_LEVEL /* Reporting level (not used) */};
+    UMA_RAPPOR_GROUP /* Recording group (not used) */};
 
 // Check for basic syntax and use.
 TEST(RapporMetricTest, BasicMetric) {
diff --git a/components/rappor/rappor_parameters.cc b/components/rappor/rappor_parameters.cc
index ef2eeb86..14e455b 100644
--- a/components/rappor/rappor_parameters.cc
+++ b/components/rappor/rappor_parameters.cc
@@ -18,7 +18,7 @@
       fake_one_prob,
       one_coin_prob,
       zero_coin_prob,
-      recording_level);
+      recording_group);
 }
 
 const int RapporParameters::kMaxCohorts = 512;
diff --git a/components/rappor/rappor_parameters.h b/components/rappor/rappor_parameters.h
index 814ed2f..807b246c 100644
--- a/components/rappor/rappor_parameters.h
+++ b/components/rappor/rappor_parameters.h
@@ -16,15 +16,13 @@
 };
 
 
-// A metric is reported when it's reporting level is >= the reporting level
+// A metric is reported when its reporting group is in the set of groups
 // passed in to RapporService::Start()
-enum RecordingLevel {
-  // No metrics are reported at this level.
-  RECORDING_DISABLED = 0,
-  // Metrics suitable for broader populations.
-  COARSE_LEVEL,
-  // Metrics suitable for UMA opt-in users.
-  FINE_LEVEL,
+enum RecordingGroup {
+  // Metrics for UMA users.
+  UMA_RAPPOR_GROUP = 1 << 0,
+  // Metrics related to SafeBrowsing, for SafeBrowsing users.
+  SAFEBROWSING_RAPPOR_GROUP = 1 << 1,
 };
 
 // An object describing a rappor metric and the parameters used to generate it.
@@ -59,7 +57,7 @@
   Probability zero_coin_prob;
 
   // The reporting level this metric is reported at.
-  RecordingLevel recording_level;
+  RecordingGroup recording_group;
 };
 
 }  // namespace rappor
diff --git a/components/rappor/rappor_service.cc b/components/rappor/rappor_service.cc
index b6f88359..ff1323e7 100644
--- a/components/rappor/rappor_service.cc
+++ b/components/rappor/rappor_service.cc
@@ -56,8 +56,8 @@
      rappor::PROBABILITY_50 /* Fake one probability */,
      rappor::PROBABILITY_75 /* One coin probability */,
      rappor::PROBABILITY_25 /* Zero coin probability */,
-     FINE_LEVEL /* Recording level */},
-    // COARSE_RAPPOR_TYPE
+     UMA_RAPPOR_GROUP /* Recording group */},
+    // SAFEBROWSING_RAPPOR_TYPE
     {128 /* Num cohorts */,
      1 /* Bloom filter size bytes */,
      2 /* Bloom filter hash count */,
@@ -65,7 +65,7 @@
      rappor::PROBABILITY_50 /* Fake one probability */,
      rappor::PROBABILITY_75 /* One coin probability */,
      rappor::PROBABILITY_25 /* Zero coin probability */,
-     COARSE_LEVEL /* Recording level */},
+     SAFEBROWSING_RAPPOR_GROUP /* Recording group */},
     // ETLD_PLUS_ONE_RAPPOR_TYPE
     {128 /* Num cohorts */,
      16 /* Bloom filter size bytes */,
@@ -74,7 +74,7 @@
      rappor::PROBABILITY_50 /* Fake one probability */,
      rappor::PROBABILITY_75 /* One coin probability */,
      rappor::PROBABILITY_25 /* Zero coin probability */,
-     FINE_LEVEL /* Recording level */},
+     UMA_RAPPOR_GROUP /* Recording group */},
 };
 
 }  // namespace
@@ -88,7 +88,7 @@
       daily_event_(pref_service,
                    prefs::kRapporLastDailySample,
                    kRapporDailyEventHistogram),
-      recording_level_(RECORDING_DISABLED) {
+      recording_groups_(0) {
 }
 
 RapporService::~RapporService() {
@@ -101,6 +101,7 @@
 }
 
 void RapporService::Initialize(net::URLRequestContextGetter* request_context) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!IsInitialized());
   const GURL server_url = GetServerUrl();
   if (!server_url.is_valid()) {
@@ -116,26 +117,27 @@
                      internal::LoadSecret(pref_service_));
 }
 
-void RapporService::Update(RecordingLevel recording_level, bool may_upload) {
+void RapporService::Update(int recording_groups, bool may_upload) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(IsInitialized());
-  if (recording_level_ != recording_level) {
-    if (recording_level == RECORDING_DISABLED) {
-      DVLOG(1) << "Rappor service stopped due to RECORDING_DISABLED.";
-      recording_level_ = RECORDING_DISABLED;
+  if (recording_groups_ != recording_groups) {
+    if (recording_groups == 0) {
+      DVLOG(1) << "Rappor service stopped because all groups were disabled.";
+      recording_groups_ = 0;
       CancelNextLogRotation();
-    } else if (recording_level_ == RECORDING_DISABLED) {
-      DVLOG(1) << "RapporService started at recording level: "
-               << recording_level;
-      recording_level_ = recording_level;
+    } else if (recording_groups_ == 0) {
+      DVLOG(1) << "RapporService started for groups: "
+               << recording_groups;
+      recording_groups_ = recording_groups;
       ScheduleNextLogRotation(
           base::TimeDelta::FromSeconds(kInitialLogIntervalSeconds));
     } else {
-      DVLOG(1) << "RapporService recording_level changed:" << recording_level;
-      recording_level_ = recording_level;
+      DVLOG(1) << "RapporService recording_groups changed:" << recording_groups;
+      recording_groups_ = recording_groups;
     }
   }
 
-  DVLOG(1) << "RapporService recording_level=" << recording_level_
+  DVLOG(1) << "RapporService recording_groups=" << recording_groups_
            << " may_upload=" << may_upload;
   if (may_upload) {
     uploader_->Start();
@@ -153,6 +155,7 @@
     scoped_ptr<LogUploaderInterface> uploader,
     int32_t cohort,
     const std::string& secret) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!IsInitialized());
   DCHECK(secret_.empty());
   uploader_.swap(uploader);
@@ -160,11 +163,8 @@
   secret_ = secret;
 }
 
-void RapporService::SetRecordingLevel(RecordingLevel recording_level) {
-  recording_level_ = recording_level;
-}
-
 void RapporService::CancelNextLogRotation() {
+  DCHECK(thread_checker_.CalledOnValidThread());
   STLDeleteValues(&metrics_map_);
   log_rotation_timer_.Stop();
 }
@@ -177,6 +177,7 @@
 }
 
 void RapporService::OnLogInterval() {
+  DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(uploader_);
   DVLOG(2) << "RapporService::OnLogInterval";
   daily_event_.CheckInterval();
@@ -193,6 +194,7 @@
 }
 
 bool RapporService::ExportMetrics(RapporReports* reports) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK_GE(cohort_, 0);
   reports->set_cohort(cohort_);
 
@@ -216,9 +218,25 @@
   return cohort_ >= 0;
 }
 
+bool RapporService::RecordingAllowed(const RapporParameters& parameters) {
+  // Skip recording in incognito mode.
+  if (is_incognito_callback_.Run()) {
+    DVLOG(2) << "Metric not logged due to incognito mode.";
+    return false;
+  }
+  // Skip this metric if its recording_group is not enabled.
+  if (!(recording_groups_ & parameters.recording_group)) {
+    DVLOG(2) << "Metric not logged due to recording_group "
+             << recording_groups_ << " < " << parameters.recording_group;
+    return false;
+  }
+  return true;
+}
+
 void RapporService::RecordSample(const std::string& metric_name,
                                  RapporType type,
                                  const std::string& sample) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   // Ignore the sample if the service hasn't started yet.
   if (!IsInitialized())
     return;
@@ -233,24 +251,17 @@
 void RapporService::RecordSampleInternal(const std::string& metric_name,
                                          const RapporParameters& parameters,
                                          const std::string& sample) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(IsInitialized());
-  if (is_incognito_callback_.Run()) {
-    DVLOG(2) << "Metric not logged due to incognito mode.";
+  if (!RecordingAllowed(parameters))
     return;
-  }
-  // Skip this metric if its reporting level is less than the enabled
-  // reporting level.
-  if (recording_level_ < parameters.recording_level) {
-    DVLOG(2) << "Metric not logged due to recording_level "
-             << recording_level_ << " < " << parameters.recording_level;
-    return;
-  }
   RapporMetric* metric = LookUpMetric(metric_name, parameters);
   metric->AddSample(sample);
 }
 
 RapporMetric* RapporService::LookUpMetric(const std::string& metric_name,
                                           const RapporParameters& parameters) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(IsInitialized());
   std::map<std::string, RapporMetric*>::const_iterator it =
       metrics_map_.find(metric_name);
@@ -266,6 +277,7 @@
 }
 
 scoped_ptr<Sample> RapporService::CreateSample(RapporType type) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(IsInitialized());
   return scoped_ptr<Sample>(
       new Sample(cohort_, kRapporParametersForType[type]));
@@ -273,18 +285,9 @@
 
 void RapporService::RecordSampleObj(const std::string& metric_name,
                                     scoped_ptr<Sample> sample) {
-  if (is_incognito_callback_.Run()) {
-    DVLOG(2) << "Metric not logged due to incognito mode.";
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!RecordingAllowed(sample->parameters()))
     return;
-  }
-  // Skip this metric if its reporting level is less than the enabled
-  // reporting level.
-  if (recording_level_ < sample->parameters().recording_level) {
-    DVLOG(2) << "Metric not logged due to recording_level "
-             << recording_level_ << " < "
-             << sample->parameters().recording_level;
-    return;
-  }
   sampler_.AddSample(metric_name, sample.Pass());
 }
 
diff --git a/components/rappor/rappor_service.h b/components/rappor/rappor_service.h
index 2a3daa8..286d7ce 100644
--- a/components/rappor/rappor_service.h
+++ b/components/rappor/rappor_service.h
@@ -12,6 +12,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
 #include "base/timer/timer.h"
 #include "components/metrics/daily_event.h"
 #include "components/rappor/rappor_parameters.h"
@@ -36,10 +37,11 @@
   // Generic metrics from UMA opt-in users.
   UMA_RAPPOR_TYPE = 0,
   // Generic metrics for SafeBrowsing users.
-  COARSE_RAPPOR_TYPE,
+  SAFEBROWSING_RAPPOR_TYPE,
   // Deprecated: Use UMA_RAPPOR_TYPE for new metrics
   ETLD_PLUS_ONE_RAPPOR_TYPE,
   NUM_RAPPOR_TYPES,
+  COARSE_RAPPOR_TYPE = SAFEBROWSING_RAPPOR_TYPE,
 };
 
 // This class provides an interface for recording samples for rappor metrics,
@@ -63,10 +65,12 @@
 
   // Updates the settings for metric recording and uploading.
   // The RapporService must be initialized before this method is called.
-  // If |recording_level| > REPORTING_DISABLED, periodic reports will be
+  // |recording_groups| should be set of flags, e.g.
+  //    UMA_RECORDING_GROUP | SAFEBROWSING_RECORDING_GROUP
+  // If it contains any enabled groups, periodic reports will be
   // generated and queued for upload.
   // If |may_upload| is true, reports will be uploaded from the queue.
-  void Update(RecordingLevel recording_level, bool may_upload);
+  void Update(int recording_groups, bool may_upload);
 
   // Constructs a Sample object for the caller to record fields in.
   scoped_ptr<Sample> CreateSample(RapporType);
@@ -104,9 +108,6 @@
                           int32_t cohort,
                           const std::string& secret);
 
-  // Sets the recording level.
-  void SetRecordingLevel(RecordingLevel parameters);
-
   // Cancels the next call to OnLogInterval.
   virtual void CancelNextLogRotation();
 
@@ -133,6 +134,11 @@
   // reports and pass it to the uploader.
   void OnLogInterval();
 
+  // Check if recording of the metric is allowed, given it's parameters.
+  // This will check that we are not in incognito mode, and that the
+  // appropriate recording group is enabled.
+  bool RecordingAllowed(const RapporParameters& parameters);
+
   // Finds a metric in the metrics_map_, creating it if it doesn't already
   // exist.
   RapporMetric* LookUpMetric(const std::string& metric_name,
@@ -159,8 +165,9 @@
   // A private LogUploader instance for sending reports to the server.
   scoped_ptr<LogUploaderInterface> uploader_;
 
-  // What reporting level of metrics are being reported.
-  RecordingLevel recording_level_;
+  // The set of recording groups that metrics are being recorded, e.g.
+  //     UMA_RECORDING_GROUP | SAFEBROWSING_RECORDING_GROUP
+  int recording_groups_;
 
   // We keep all registered metrics in a map, from name to metric.
   // The map owns the metrics it contains.
@@ -168,6 +175,8 @@
 
   internal::Sampler sampler_;
 
+  base::ThreadChecker thread_checker_;
+
   DISALLOW_COPY_AND_ASSIGN(RapporService);
 };
 
diff --git a/components/rappor/rappor_service_unittest.cc b/components/rappor/rappor_service_unittest.cc
index de612cc..3b38c1a 100644
--- a/components/rappor/rappor_service_unittest.cc
+++ b/components/rappor/rappor_service_unittest.cc
@@ -18,19 +18,24 @@
 namespace rappor {
 
 TEST(RapporServiceTest, Update) {
+  // Test rappor service initially has uploading and reporting enabled.
   TestRapporService rappor_service;
   EXPECT_LT(base::TimeDelta(), rappor_service.next_rotation());
   EXPECT_TRUE(rappor_service.test_uploader()->is_running());
 
-  rappor_service.Update(RECORDING_DISABLED, false);
+  // Disabling both should stop both uploads and reports.
+  rappor_service.Update(0, false);
   EXPECT_EQ(base::TimeDelta(), rappor_service.next_rotation());
   EXPECT_FALSE(rappor_service.test_uploader()->is_running());
 
-  rappor_service.Update(FINE_LEVEL, false);
+  // Some recording, but no reporting.
+  rappor_service.Update(UMA_RAPPOR_GROUP, false);
+  // Reports generation should still be scheduled.
   EXPECT_LT(base::TimeDelta(), rappor_service.next_rotation());
   EXPECT_FALSE(rappor_service.test_uploader()->is_running());
 
-  rappor_service.Update(COARSE_LEVEL, true);
+  // Some recording and reporting enabled.
+  rappor_service.Update(SAFEBROWSING_RAPPOR_GROUP, true);
   EXPECT_LT(base::TimeDelta(), rappor_service.next_rotation());
   EXPECT_TRUE(rappor_service.test_uploader()->is_running());
 }
@@ -53,13 +58,26 @@
   EXPECT_EQ(16u, report.bits().size());
 }
 
-// Check that the reporting level is respected.
-TEST(RapporServiceTest, RecordingLevel) {
+// Check that the reporting groups are respected.
+TEST(RapporServiceTest, UmaRecordingGroup) {
   TestRapporService rappor_service;
-  rappor_service.Update(COARSE_LEVEL, false);
+  rappor_service.Update(SAFEBROWSING_RAPPOR_GROUP, false);
 
-  // ETLD_PLUS_ONE_RAPPOR_TYPE is a FINE_LEVEL metric
-  rappor_service.RecordSample("FineMetric", ETLD_PLUS_ONE_RAPPOR_TYPE, "foo");
+  // Wrong recording group.
+  rappor_service.RecordSample("UmaMetric", UMA_RAPPOR_TYPE, "foo");
+
+  RapporReports reports;
+  rappor_service.GetReports(&reports);
+  EXPECT_EQ(0, reports.report_size());
+}
+
+// Check that the reporting groups are respected.
+TEST(RapporServiceTest, SafeBrowsingRecordingGroup) {
+  TestRapporService rappor_service;
+  rappor_service.Update(UMA_RAPPOR_GROUP, false);
+
+  // Wrong recording group.
+  rappor_service.RecordSample("SbMetric", SAFEBROWSING_RAPPOR_TYPE, "foo");
 
   RapporReports reports;
   rappor_service.GetReports(&reports);
@@ -89,7 +107,7 @@
   TestRapporService rappor_service;
   rappor_service.set_is_incognito(true);
 
-  rappor_service.RecordSample("MyMetric", COARSE_RAPPOR_TYPE, "foo");
+  rappor_service.RecordSample("MyMetric", SAFEBROWSING_RAPPOR_TYPE, "foo");
 
   RapporReports reports;
   rappor_service.GetReports(&reports);
@@ -99,7 +117,8 @@
 // Check that Sample objects record correctly.
 TEST(RapporServiceTest, RecordSample) {
   TestRapporService rappor_service;
-  scoped_ptr<Sample> sample = rappor_service.CreateSample(COARSE_RAPPOR_TYPE);
+  scoped_ptr<Sample> sample =
+      rappor_service.CreateSample(SAFEBROWSING_RAPPOR_TYPE);
   sample->SetStringField("Url", "example.com");
   sample->SetFlagsField("Flags1", 0xbcd, 12);
   rappor_service.RecordSampleObj("ObjMetric", sample.Pass());
diff --git a/components/rappor/sampler_unittest.cc b/components/rappor/sampler_unittest.cc
index 2790c6f..122abc26 100644
--- a/components/rappor/sampler_unittest.cc
+++ b/components/rappor/sampler_unittest.cc
@@ -18,7 +18,7 @@
     PROBABILITY_50 /* Fake one probability */,
     PROBABILITY_75 /* One coin probability */,
     PROBABILITY_50 /* Zero coin probability */,
-    FINE_LEVEL /* Reporting level (not used) */};
+    UMA_RAPPOR_GROUP /* Recording group (not used) */};
 
 class TestSamplerFactory {
  public:
diff --git a/components/rappor/test_rappor_service.cc b/components/rappor/test_rappor_service.cc
index aa54a94..8d347c58 100644
--- a/components/rappor/test_rappor_service.cc
+++ b/components/rappor/test_rappor_service.cc
@@ -29,7 +29,7 @@
   InitializeInternal(make_scoped_ptr(test_uploader_),
                      0,
                      HmacByteVectorGenerator::GenerateEntropyInput());
-  Update(FINE_LEVEL, true);
+  Update(UMA_RAPPOR_GROUP | SAFEBROWSING_RAPPOR_GROUP, true);
 }
 
 TestRapporService::~TestRapporService() {}
diff --git a/components/scheduler/child/idle_helper_unittest.cc b/components/scheduler/child/idle_helper_unittest.cc
index c687986..20ae1b5c 100644
--- a/components/scheduler/child/idle_helper_unittest.cc
+++ b/components/scheduler/child/idle_helper_unittest.cc
@@ -180,6 +180,7 @@
             new SchedulerHelper(nestable_task_runner_,
                                 "test.idle",
                                 TRACE_DISABLED_BY_DEFAULT("test.idle"),
+                                TRACE_DISABLED_BY_DEFAULT("test.idle.debug"),
                                 SchedulerHelper::TASK_QUEUE_COUNT + 1)),
         idle_helper_(new IdleHelperForTest(
             scheduler_helper_.get(),
diff --git a/components/scheduler/child/scheduler_helper.cc b/components/scheduler/child/scheduler_helper.cc
index 650ea85a..0e13c76 100644
--- a/components/scheduler/child/scheduler_helper.cc
+++ b/components/scheduler/child/scheduler_helper.cc
@@ -15,13 +15,15 @@
     scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner,
     const char* tracing_category,
     const char* disabled_by_default_tracing_category,
+    const char* disabled_by_default_verbose_tracing_category,
     size_t total_task_queue_count)
     : task_queue_selector_(new PrioritizingTaskQueueSelector()),
       task_queue_manager_(
           new TaskQueueManager(total_task_queue_count,
                                main_task_runner,
                                task_queue_selector_.get(),
-                               disabled_by_default_tracing_category)),
+                               disabled_by_default_tracing_category,
+                               disabled_by_default_verbose_tracing_category)),
       quiescence_monitored_task_queue_mask_(
           ((1ull << total_task_queue_count) - 1ull) &
           ~(1ull << QueueId::CONTROL_TASK_QUEUE) &
diff --git a/components/scheduler/child/scheduler_helper.h b/components/scheduler/child/scheduler_helper.h
index e781803..3c439c7 100644
--- a/components/scheduler/child/scheduler_helper.h
+++ b/components/scheduler/child/scheduler_helper.h
@@ -27,6 +27,7 @@
       scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner,
       const char* tracing_category,
       const char* disabled_by_default_tracing_category,
+      const char* disabled_by_default_verbose_tracing_category,
       size_t total_task_queue_count);
   ~SchedulerHelper();
 
diff --git a/components/scheduler/child/scheduler_helper_unittest.cc b/components/scheduler/child/scheduler_helper_unittest.cc
index 0f71675..85c811a 100644
--- a/components/scheduler/child/scheduler_helper_unittest.cc
+++ b/components/scheduler/child/scheduler_helper_unittest.cc
@@ -52,6 +52,7 @@
             new SchedulerHelper(nestable_task_runner_,
                                 "test.scheduler",
                                 TRACE_DISABLED_BY_DEFAULT("test.scheduler"),
+                                TRACE_DISABLED_BY_DEFAULT("test.scheduler.dbg"),
                                 SchedulerHelper::TASK_QUEUE_COUNT)),
         default_task_runner_(scheduler_helper_->DefaultTaskRunner()) {
     clock_->Advance(base::TimeDelta::FromMicroseconds(5000));
diff --git a/components/scheduler/child/task_queue_manager.cc b/components/scheduler/child/task_queue_manager.cc
index 569c4fa..71b2d86 100644
--- a/components/scheduler/child/task_queue_manager.cc
+++ b/components/scheduler/child/task_queue_manager.cc
@@ -47,7 +47,8 @@
 class TaskQueue : public base::SingleThreadTaskRunner {
  public:
   TaskQueue(TaskQueueManager* task_queue_manager,
-            const char* disabled_by_default_tracing_category);
+            const char* disabled_by_default_tracing_category,
+            const char* disabled_by_default_verbose_tracing_category);
 
   // base::SingleThreadTaskRunner implementation.
   bool RunsTasksOnCurrentThread() const override;
@@ -144,6 +145,7 @@
   TaskQueueManager::PumpPolicy pump_policy_;
   const char* name_;
   const char* disabled_by_default_tracing_category_;
+  const char* disabled_by_default_verbose_tracing_category_;
   base::DelayedTaskQueue delayed_task_queue_;
   std::set<base::TimeTicks> in_flight_kick_delayed_tasks_;
 
@@ -155,13 +157,16 @@
 };
 
 TaskQueue::TaskQueue(TaskQueueManager* task_queue_manager,
-                     const char* disabled_by_default_tracing_category)
+                     const char* disabled_by_default_tracing_category,
+                     const char* disabled_by_default_verbose_tracing_category)
     : thread_id_(base::PlatformThread::CurrentId()),
       task_queue_manager_(task_queue_manager),
       pump_policy_(TaskQueueManager::PumpPolicy::AUTO),
       name_(nullptr),
       disabled_by_default_tracing_category_(
           disabled_by_default_tracing_category),
+      disabled_by_default_verbose_tracing_category_(
+          disabled_by_default_verbose_tracing_category),
       wakeup_policy_(TaskQueueManager::WakeupPolicy::CAN_WAKE_OTHER_QUEUES) {
 }
 
@@ -409,15 +414,23 @@
                    TaskQueueManager::PumpPolicyToString(pump_policy_));
   state->SetString("wakeup_policy",
                    TaskQueueManager::WakeupPolicyToString(wakeup_policy_));
-  state->BeginArray("incoming_queue");
-  QueueAsValueInto(incoming_queue_, state);
-  state->EndArray();
-  state->BeginArray("work_queue");
-  QueueAsValueInto(work_queue_, state);
-  state->EndArray();
-  state->BeginArray("delayed_task_queue");
-  QueueAsValueInto(delayed_task_queue_, state);
-  state->EndArray();
+  bool verbose_tracing_enabled = false;
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED(
+      disabled_by_default_verbose_tracing_category_, &verbose_tracing_enabled);
+  state->SetInteger("incoming_queue_size", incoming_queue_.size());
+  state->SetInteger("work_queue_size", work_queue_.size());
+  state->SetInteger("delayed_task_queue_size", delayed_task_queue_.size());
+  if (verbose_tracing_enabled) {
+    state->BeginArray("incoming_queue");
+    QueueAsValueInto(incoming_queue_, state);
+    state->EndArray();
+    state->BeginArray("work_queue");
+    QueueAsValueInto(work_queue_, state);
+    state->EndArray();
+    state->BeginArray("delayed_task_queue");
+    QueueAsValueInto(delayed_task_queue_, state);
+    state->EndArray();
+  }
   state->EndDictionary();
 }
 
@@ -461,7 +474,8 @@
     size_t task_queue_count,
     scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner,
     TaskQueueSelector* selector,
-    const char* disabled_by_default_tracing_category)
+    const char* disabled_by_default_tracing_category,
+    const char* disabled_by_default_verbose_tracing_category)
     : main_task_runner_(main_task_runner),
       selector_(selector),
       task_was_run_bitmap_(0),
@@ -480,7 +494,9 @@
 
   for (size_t i = 0; i < task_queue_count; i++) {
     scoped_refptr<internal::TaskQueue> queue(make_scoped_refptr(
-        new internal::TaskQueue(this, disabled_by_default_tracing_category)));
+        new internal::TaskQueue(this,
+                                disabled_by_default_tracing_category,
+                                disabled_by_default_verbose_tracing_category)));
     queues_.push_back(queue);
   }
 
diff --git a/components/scheduler/child/task_queue_manager.h b/components/scheduler/child/task_queue_manager.h
index c0766c21..b61e7a8 100644
--- a/components/scheduler/child/task_queue_manager.h
+++ b/components/scheduler/child/task_queue_manager.h
@@ -102,7 +102,8 @@
       size_t task_queue_count,
       scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner,
       TaskQueueSelector* selector,
-      const char* disabled_by_default_tracing_category);
+      const char* disabled_by_default_tracing_category,
+      const char* disabled_by_default_verbose_tracing_category);
   ~TaskQueueManager() override;
 
   // Returns the task runner which targets the queue selected by |queue_index|.
diff --git a/components/scheduler/child/task_queue_manager_perftest.cc b/components/scheduler/child/task_queue_manager_perftest.cc
index 80a9c901..7855557 100644
--- a/components/scheduler/child/task_queue_manager_perftest.cc
+++ b/components/scheduler/child/task_queue_manager_perftest.cc
@@ -69,7 +69,7 @@
     selector_ = make_scoped_ptr(new SelectorForTest);
     manager_ = make_scoped_ptr(new TaskQueueManager(
         num_queues, SchedulerMessageLoopDelegate::Create(message_loop_.get()),
-        selector_.get(), "fake.category"));
+        selector_.get(), "fake.category", "fake.category.debug"));
   }
 
   void TestDelayedTask() {
diff --git a/components/scheduler/child/task_queue_manager_unittest.cc b/components/scheduler/child/task_queue_manager_unittest.cc
index 07f7e308..d5af6e42 100644
--- a/components/scheduler/child/task_queue_manager_unittest.cc
+++ b/components/scheduler/child/task_queue_manager_unittest.cc
@@ -125,7 +125,7 @@
     selector_ = make_scoped_ptr(createSelectorForTest(type));
     manager_ = make_scoped_ptr(new TaskQueueManager(
         num_queues, NestableTaskRunnerForTest::Create(test_task_runner_.get()),
-        selector_.get(), "test.scheduler"));
+        selector_.get(), "test.scheduler", "test.scheduler.debug"));
     manager_->SetTimeSourceForTesting(
         make_scoped_ptr(new TestTimeSource(now_src_.get())));
 
@@ -137,7 +137,7 @@
     selector_ = make_scoped_ptr(createSelectorForTest(type));
     manager_ = make_scoped_ptr(new TaskQueueManager(
         num_queues, SchedulerMessageLoopDelegate::Create(message_loop_.get()),
-        selector_.get(), "test.scheduler"));
+        selector_.get(), "test.scheduler", "test.scheduler.debug"));
     EXPECT_EQ(num_queues, selector_->work_queues().size());
   }
 
diff --git a/components/scheduler/child/worker_scheduler_impl.cc b/components/scheduler/child/worker_scheduler_impl.cc
index 4e1d47e6..b5b9c83 100644
--- a/components/scheduler/child/worker_scheduler_impl.cc
+++ b/components/scheduler/child/worker_scheduler_impl.cc
@@ -16,6 +16,7 @@
     : helper_(main_task_runner,
               "worker.scheduler",
               TRACE_DISABLED_BY_DEFAULT("worker.scheduler"),
+              TRACE_DISABLED_BY_DEFAULT("worker.scheduler.debug"),
               TASK_QUEUE_COUNT),
       idle_helper_(&helper_,
                    this,
diff --git a/components/scheduler/renderer/renderer_scheduler.cc b/components/scheduler/renderer/renderer_scheduler.cc
index 95669507e..914b802 100644
--- a/components/scheduler/renderer/renderer_scheduler.cc
+++ b/components/scheduler/renderer/renderer_scheduler.cc
@@ -23,9 +23,14 @@
 
 // static
 scoped_ptr<RendererScheduler> RendererScheduler::Create() {
-  // Ensure worker.scheduler appears as an option in about://tracing
+  // Ensure worker.scheduler, worker.scheduler.debug and
+  // renderer.scheduler.debug appear as an option in about://tracing
   base::trace_event::TraceLog::GetCategoryGroupEnabled(
       TRACE_DISABLED_BY_DEFAULT("worker.scheduler"));
+  base::trace_event::TraceLog::GetCategoryGroupEnabled(
+      TRACE_DISABLED_BY_DEFAULT("worker.scheduler.debug"));
+  base::trace_event::TraceLog::GetCategoryGroupEnabled(
+      TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug"));
 
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kDisableBlinkScheduler)) {
diff --git a/components/scheduler/renderer/renderer_scheduler_impl.cc b/components/scheduler/renderer/renderer_scheduler_impl.cc
index a96051c..906d1a8 100644
--- a/components/scheduler/renderer/renderer_scheduler_impl.cc
+++ b/components/scheduler/renderer/renderer_scheduler_impl.cc
@@ -19,6 +19,7 @@
     : helper_(main_task_runner,
               "renderer.scheduler",
               TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+              TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug"),
               TASK_QUEUE_COUNT),
       idle_helper_(&helper_,
                    this,
diff --git a/components/scheduler/renderer/renderer_scheduler_impl.h b/components/scheduler/renderer/renderer_scheduler_impl.h
index f90ac45..e00d6623 100644
--- a/components/scheduler/renderer/renderer_scheduler_impl.h
+++ b/components/scheduler/renderer/renderer_scheduler_impl.h
@@ -139,8 +139,9 @@
 
   // For the purposes of deciding whether or not it's safe to turn timers on
   // only in idle periods, we regard the system as being as being "idle period"
-  // starved if there hasn't been an idle period in the last 100 ms.
-  static const int kIdlePeriodStarvationThresholdMillis = 100;
+  // starved if there hasn't been an idle period in the last 10 seconds. This
+  // was chosen to be long enough to cover most anticipated user gestures.
+  static const int kIdlePeriodStarvationThresholdMillis = 10000;
 
   // Schedules an immediate PolicyUpdate, if there isn't one already pending and
   // sets |policy_may_need_update_|. Note |any_thread_lock_| must be
diff --git a/components/search_engines/template_url_fetcher_unittest.cc b/components/search_engines/template_url_fetcher_unittest.cc
index 56b6a52..d69f57d 100644
--- a/components/search_engines/template_url_fetcher_unittest.cc
+++ b/components/search_engines/template_url_fetcher_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/path_service.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/search_engines/template_url_service_test_util.h"
-#include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_fetcher.h"
@@ -90,15 +89,24 @@
   DISALLOW_COPY_AND_ASSIGN(TemplateURLFetcherTest);
 };
 
+bool GetTestDataDir(base::FilePath* dir) {
+  if (!PathService::Get(base::DIR_SOURCE_ROOT, dir))
+    return false;
+  *dir = dir->AppendASCII("components")
+             .AppendASCII("test")
+             .AppendASCII("data")
+             .AppendASCII("search_engines");
+  return true;
+}
+
 TemplateURLFetcherTest::TemplateURLFetcherTest()
     : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
       callbacks_destroyed_(0),
       add_provider_called_(0),
       waiting_for_download_(false) {
-  base::FilePath src_dir;
-  CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &src_dir));
-  test_server_.ServeFilesFromDirectory(
-      src_dir.AppendASCII("chrome/test/data"));
+  base::FilePath test_data_dir;
+  CHECK(GetTestDataDir(&test_data_dir));
+  test_server_.ServeFilesFromDirectory(test_data_dir);
 }
 
 void TemplateURLFetcherTest::DestroyedCallback() {
@@ -119,10 +127,9 @@
     const std::string& osdd_file_name,
     TemplateURLFetcher::ProviderType provider_type,
     bool check_that_file_exists) {
-
   if (check_that_file_exists) {
     base::FilePath osdd_full_path;
-    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &osdd_full_path));
+    ASSERT_TRUE(GetTestDataDir(&osdd_full_path));
     osdd_full_path = osdd_full_path.AppendASCII(osdd_file_name);
     ASSERT_TRUE(base::PathExists(osdd_full_path));
     ASSERT_FALSE(base::DirectoryExists(osdd_full_path));
diff --git a/components/signin/core/browser/account_tracker_service.cc b/components/signin/core/browser/account_tracker_service.cc
index ee5a8e7..741857c 100644
--- a/components/signin/core/browser/account_tracker_service.cc
+++ b/components/signin/core/browser/account_tracker_service.cc
@@ -414,7 +414,12 @@
 
 void AccountTrackerService::OnRefreshTokenAvailable(
     const std::string& account_id) {
-  RefreshAccountInfo(account_id, false);
+    // The SigninClient needs a "final init" in order to perform some actions
+    // (such as fetching the signin token "handle" in order to look for password
+    // changes) once everything is initialized and the refresh token is present.
+    signin_client_->DoFinalInit();
+
+    RefreshAccountInfo(account_id, false);
 }
 
 void AccountTrackerService::OnRefreshTokenRevoked(
diff --git a/components/signin/core/browser/signin_client.h b/components/signin/core/browser/signin_client.h
index 934443a..13bdf84 100644
--- a/components/signin/core/browser/signin_client.h
+++ b/components/signin/core/browser/signin_client.h
@@ -47,6 +47,11 @@
 
   ~SigninClient() override {}
 
+  // Call when done local initialization and SigninClient can initiate any work
+  // it has to do that may require other components (like ProfileManager) to be
+  // available.
+  virtual void DoFinalInit() = 0;
+
   // Gets the preferences associated with the client.
   virtual PrefService* GetPrefs() = 0;
 
diff --git a/components/signin/core/browser/test_signin_client.cc b/components/signin/core/browser/test_signin_client.cc
index 0c3768d..45c0fde 100644
--- a/components/signin/core/browser/test_signin_client.cc
+++ b/components/signin/core/browser/test_signin_client.cc
@@ -29,6 +29,8 @@
 
 TestSigninClient::~TestSigninClient() {}
 
+void TestSigninClient::DoFinalInit() {}
+
 PrefService* TestSigninClient::GetPrefs() {
   return pref_service_;
 }
diff --git a/components/signin/core/browser/test_signin_client.h b/components/signin/core/browser/test_signin_client.h
index 80ce089..8c35970 100644
--- a/components/signin/core/browser/test_signin_client.h
+++ b/components/signin/core/browser/test_signin_client.h
@@ -30,6 +30,8 @@
 
   // SigninClient implementation that is specialized for unit tests.
 
+  void DoFinalInit() override;
+
   // Returns NULL.
   // NOTE: This should be changed to return a properly-initalized PrefService
   // once there is a unit test that requires it.
diff --git a/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsApplication.java b/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsApplication.java
index ffcea44..d1b128e 100644
--- a/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsApplication.java
+++ b/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsApplication.java
@@ -15,8 +15,8 @@
  */
 public class ComponentsBrowserTestsApplication extends BaseChromiumApplication {
     private static final String[] MANDATORY_PAK_FILES =
-            new String[] {"components_tests_resources.pak", "content_shell.pak", "icudtl.dat",
-                    "natives_blob.bin", "snapshot_blob.bin"};
+            new String[] {"components_tests_resources.pak", "content_shell.pak", "natives_blob.bin",
+                    "snapshot_blob.bin"};
     static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "components_shell";
 
     @Override
diff --git a/components/test/data/search_engines/simple_open_search.xml b/components/test/data/search_engines/simple_open_search.xml
new file mode 100644
index 0000000..4d40610b
--- /dev/null
+++ b/components/test/data/search_engines/simple_open_search.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+<ShortName>Simple Search</ShortName>
+<Description>Not a creative description.</Description>
+<Url type="text/html" template="http://example.com/{searchTerms}/other_stuff"/>
+</OpenSearchDescription>
diff --git a/components/test_runner/web_ax_object_proxy.cc b/components/test_runner/web_ax_object_proxy.cc
index 125507b..4ed30f2 100644
--- a/components/test_runner/web_ax_object_proxy.cc
+++ b/components/test_runner/web_ax_object_proxy.cc
@@ -132,6 +132,8 @@
       return result.append("Log");
     case blink::WebAXRoleMain:
       return result.append("Main");
+    case blink::WebAXRoleMark:
+      return result.append("Mark");
     case blink::WebAXRoleMarquee:
       return result.append("Marquee");
     case blink::WebAXRoleMath:
diff --git a/components/translate/core/browser/translate_language_list.cc b/components/translate/core/browser/translate_language_list.cc
index c3a16f159..0b03a64 100644
--- a/components/translate/core/browser/translate_language_list.cc
+++ b/components/translate/core/browser/translate_language_list.cc
@@ -257,16 +257,16 @@
 void TranslateLanguageList::SetSupportedLanguages(
     const std::string& language_list) {
   // The format is:
-  // sl({
+  // /* API response */ sl({
   //   "sl": {"XX": "LanguageName", ...},
   //   "tl": {"XX": "LanguageName", ...},
   //   "al": {"XX": 1, ...}
   // })
   // Where "sl(" is set in kLanguageListCallbackName, "tl" is
   // kTargetLanguagesKey and "al" kAlphaLanguagesKey.
-  if (!base::StartsWithASCII(language_list,
-                             TranslateLanguageList::kLanguageListCallbackName,
-                             false) ||
+  size_t start =
+      language_list.find(TranslateLanguageList::kLanguageListCallbackName);
+  if (start == std::string::npos ||
       !base::EndsWith(language_list, ")", false)) {
     // We don't have a NOTREACHED here since this can happen in ui_tests, even
     // though the the BrowserMain function won't call us with parameters.ui_task
@@ -276,8 +276,8 @@
   static const size_t kLanguageListCallbackNameLength =
       strlen(TranslateLanguageList::kLanguageListCallbackName);
   std::string languages_json = language_list.substr(
-      kLanguageListCallbackNameLength,
-      language_list.size() - kLanguageListCallbackNameLength - 1);
+      start + kLanguageListCallbackNameLength,
+      language_list.size() - start - kLanguageListCallbackNameLength - 1);
 
   scoped_ptr<base::Value> json_value(base::JSONReader::DeprecatedRead(
       languages_json, base::JSON_ALLOW_TRAILING_COMMAS));
diff --git a/components/view_manager/connection_manager.cc b/components/view_manager/connection_manager.cc
index 39b6051a..4964428 100644
--- a/components/view_manager/connection_manager.cc
+++ b/components/view_manager/connection_manager.cc
@@ -206,6 +206,11 @@
   OnConnectionMessagedClient(client_connection->service()->id());
 }
 
+void ConnectionManager::OnAccelerator(ServerView* root, mojo::EventPtr event) {
+  // TODO(fsamuel): Support multiple roots.
+  view_manager_root_client_->OnAccelerator(event.Pass());
+}
+
 ViewManagerServiceImpl* ConnectionManager::GetConnection(
     ConnectionSpecificId connection_id) {
   ConnectionMap::iterator i = connection_map_.find(connection_id);
@@ -236,6 +241,13 @@
   return root_->Contains(view) && view != root_.get();
 }
 
+void ConnectionManager::SchedulePaint(const ServerView* view,
+                                      const gfx::Rect& bounds) {
+  // TODO(fsamuel): Direct this paint to the appropriate DisplayManager once we
+  // support multiple roots.
+  display_manager_->SchedulePaint(view, gfx::Rect(view->bounds().size()));
+}
+
 void ConnectionManager::OnConnectionMessagedClient(ConnectionSpecificId id) {
   if (current_change_)
     current_change_->MarkConnectionAsMessaged(id);
@@ -246,6 +258,14 @@
   return current_change_ && current_change_->DidMessageConnection(id);
 }
 
+mojo::ViewportMetricsPtr ConnectionManager::GetViewportMetricsForView(
+    const ServerView* view) {
+   // TODO(fsamuel): Once we support multiple roots, grab the the DisplayManager
+   // associated with the root view of this |view|, and grab that display's
+   // metrics.
+  return display_manager_->GetViewportMetrics().Clone();
+}
+
 const ViewManagerServiceImpl* ConnectionManager::GetConnectionWithRoot(
     const ViewId& id) const {
   for (auto& pair : connection_map_) {
@@ -288,7 +308,7 @@
 
 bool ConnectionManager::CloneAndAnimate(const ViewId& view_id) {
   ServerView* view = GetView(view_id);
-  if (!view || !view->IsDrawn() || view == root_.get())
+  if (!view || !view->IsDrawn() || (view->GetRoot() == view))
     return false;
   if (!animation_timer_.IsRunning()) {
     animation_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(100),
@@ -301,10 +321,6 @@
   return true;
 }
 
-void ConnectionManager::ProcessEvent(mojo::EventPtr event) {
-  event_dispatcher_.OnEvent(event.Pass());
-}
-
 void ConnectionManager::DispatchInputEventToView(const ServerView* view,
                                                  mojo::EventPtr event) {
   // It's possible for events to flow through here from the platform_window
@@ -327,7 +343,7 @@
 }
 
 void ConnectionManager::OnEvent(mojo::EventPtr event) {
-  event_dispatcher_.OnEvent(event.Pass());
+  event_dispatcher_.OnEvent(root_.get(), event.Pass());
 }
 
 void ConnectionManager::OnDisplayClosed() {
@@ -461,7 +477,7 @@
 
 void ConnectionManager::OnScheduleViewPaint(const ServerView* view) {
   if (!in_destructor_)
-    display_manager_->SchedulePaint(view, gfx::Rect(view->bounds().size()));
+    SchedulePaint(view, gfx::Rect(view->bounds().size()));
 }
 
 const ServerView* ConnectionManager::GetRootView(const ServerView* view) const {
@@ -494,14 +510,10 @@
   ProcessViewHierarchyChanged(view, new_parent, old_parent);
 
   // TODO(beng): optimize.
-  if (old_parent) {
-    display_manager_->SchedulePaint(old_parent,
-                                    gfx::Rect(old_parent->bounds().size()));
-  }
-  if (new_parent) {
-    display_manager_->SchedulePaint(new_parent,
-                                    gfx::Rect(new_parent->bounds().size()));
-  }
+  if (old_parent)
+    SchedulePaint(old_parent, gfx::Rect(old_parent->bounds().size()));
+  if (new_parent)
+    SchedulePaint(new_parent, gfx::Rect(new_parent->bounds().size()));
 }
 
 void ConnectionManager::OnViewBoundsChanged(ServerView* view,
@@ -515,15 +527,15 @@
     return;
 
   // TODO(sky): optimize this.
-  display_manager_->SchedulePaint(view->parent(), old_bounds);
-  display_manager_->SchedulePaint(view->parent(), new_bounds);
+  SchedulePaint(view->parent(), old_bounds);
+  SchedulePaint(view->parent(), new_bounds);
 }
 
 void ConnectionManager::OnViewReordered(ServerView* view,
                                         ServerView* relative,
                                         mojo::OrderDirection direction) {
   if (!in_destructor_)
-    display_manager_->SchedulePaint(view, gfx::Rect(view->bounds().size()));
+    SchedulePaint(view, gfx::Rect(view->bounds().size()));
 }
 
 void ConnectionManager::OnWillChangeViewVisibility(ServerView* view) {
@@ -534,7 +546,7 @@
   // hiding) or the view is transitioning to drawn.
   if (view->parent() && (view->IsDrawn() ||
       (!view->visible() && view->parent()->IsDrawn()))) {
-    display_manager_->SchedulePaint(view->parent(), view->bounds());
+    SchedulePaint(view->parent(), view->bounds());
   }
 
   for (auto& pair : connection_map_) {
diff --git a/components/view_manager/connection_manager.h b/components/view_manager/connection_manager.h
index 9b21437..3a78857 100644
--- a/components/view_manager/connection_manager.h
+++ b/components/view_manager/connection_manager.h
@@ -97,6 +97,10 @@
                    const ViewId& view_id,
                    mojo::ViewManagerClientPtr client);
 
+  // Invoked when an accelerator has been triggered on a view tree with the
+  // provided |root|.
+  void OnAccelerator(ServerView* root, mojo::EventPtr event);
+
   // Returns the connection by id.
   ViewManagerServiceImpl* GetConnection(
       mojo::ConnectionSpecificId connection_id);
@@ -113,6 +117,9 @@
 
   DisplayManager* display_manager() { return display_manager_.get(); }
 
+  // Schedules a paint for the specified region in the coordinates of |view|.
+  void SchedulePaint(const ServerView* view, const gfx::Rect& bounds);
+
   bool IsProcessingChange() const { return current_change_ != NULL; }
 
   bool is_processing_delete_view() const {
@@ -126,6 +133,9 @@
   // Returns true if OnConnectionMessagedClient() was invoked for id.
   bool DidConnectionMessageClient(mojo::ConnectionSpecificId id) const;
 
+  // Returns the metrics of the viewport where the provided |view| is displayed.
+  mojo::ViewportMetricsPtr GetViewportMetricsForView(const ServerView* view);
+
   // Returns the ViewManagerServiceImpl that has |id| as a root.
   ViewManagerServiceImpl* GetConnectionWithRoot(const ViewId& id) {
     return const_cast<ViewManagerServiceImpl*>(
@@ -151,9 +161,6 @@
   // ViewManagerRoot implementation helper; see mojom for details.
   bool CloneAndAnimate(const ViewId& view_id);
 
-  // Processes an event, potentially changing focus.
-  void ProcessEvent(mojo::EventPtr event);
-
   // Dispatches |event| directly to the appropriate connection for |view|.
   void DispatchInputEventToView(const ServerView* view, mojo::EventPtr event);
 
diff --git a/components/view_manager/display_manager.cc b/components/view_manager/display_manager.cc
index c292450..67caecc3 100644
--- a/components/view_manager/display_manager.cc
+++ b/components/view_manager/display_manager.cc
@@ -66,7 +66,7 @@
   auto sqs = mojo::CreateDefaultSQS(view->bounds().size());
   sqs->blend_mode = mojo::SK_XFERMODE_kSrcOver_Mode;
   sqs->opacity = combined_opacity;
-  sqs->content_to_target_transform = mojo::Transform::From(node_transform);
+  sqs->quad_to_target_transform = mojo::Transform::From(node_transform);
 
   pass->quads.push_back(surface_quad.Pass());
   pass->shared_quad_states.push_back(sqs.Pass());
diff --git a/components/view_manager/event_dispatcher.cc b/components/view_manager/event_dispatcher.cc
index c3b1604..3a3ec4d 100644
--- a/components/view_manager/event_dispatcher.cc
+++ b/components/view_manager/event_dispatcher.cc
@@ -30,19 +30,19 @@
   accelerators_.erase(Accelerator(keyboard_code, flags));
 }
 
-void EventDispatcher::OnEvent(mojo::EventPtr event) {
+void EventDispatcher::OnEvent(ServerView* root, mojo::EventPtr event) {
   if (event->pointer_data) {
     const gfx::Point root_point(static_cast<int>(event->pointer_data->x),
                                 static_cast<int>(event->pointer_data->y));
     ServerView* target = connection_manager_->GetFocusedView();
-    if (event->action == mojo::EVENT_TYPE_POINTER_DOWN || !target) {
-      target =
-          FindDeepestVisibleView(connection_manager_->GetRoot(), root_point);
+    if (event->action == mojo::EVENT_TYPE_POINTER_DOWN || !target ||
+        !root->Contains(target)) {
+      target = FindDeepestVisibleView(root, root_point);
       CHECK(target);
       connection_manager_->SetFocusedView(target);
     }
     const gfx::PointF local_point(ConvertPointFBetweenViews(
-        connection_manager_->GetRoot(), target,
+        root, target,
         gfx::PointF(event->pointer_data->x, event->pointer_data->y)));
     event->pointer_data->x = local_point.x();
     event->pointer_data->y = local_point.y();
@@ -50,8 +50,7 @@
   } else if (event->action == mojo::EVENT_TYPE_KEY_PRESSED &&
              accelerators_.count(Accelerator(event->key_data->windows_key_code,
                                              event->flags))) {
-    connection_manager_->view_manager_root_client()->OnAccelerator(
-        event.Pass());
+    connection_manager_->OnAccelerator(root, event.Pass());
   } else {
     ServerView* focused_view = connection_manager_->GetFocusedView();
     if (focused_view)
diff --git a/components/view_manager/event_dispatcher.h b/components/view_manager/event_dispatcher.h
index a2d4514..75d1689 100644
--- a/components/view_manager/event_dispatcher.h
+++ b/components/view_manager/event_dispatcher.h
@@ -15,6 +15,7 @@
 namespace view_manager {
 
 class ConnectionManager;
+class ServerView;
 
 // Handles dispatching events to the right location as well as updating focus.
 class EventDispatcher {
@@ -26,7 +27,7 @@
   void RemoveAccelerator(mojo::KeyboardCode keyboard_code,
                          mojo::EventFlags flags);
 
-  void OnEvent(mojo::EventPtr event);
+  void OnEvent(ServerView* root, mojo::EventPtr event);
 
  private:
   struct Accelerator {
diff --git a/components/view_manager/native_viewport/platform_viewport_common.cc b/components/view_manager/native_viewport/platform_viewport_common.cc
index bf20626d..7656c42 100644
--- a/components/view_manager/native_viewport/platform_viewport_common.cc
+++ b/components/view_manager/native_viewport/platform_viewport_common.cc
@@ -56,8 +56,6 @@
     CHECK(!platform_window_);
 
     metrics_ = mojo::ViewportMetrics::New();
-    // TODO(sky): make density real.
-    metrics_->device_pixel_ratio = 1.f;
     metrics_->size_in_pixels = mojo::Size::From(bounds.size());
 
 #if defined(OS_WIN)
@@ -86,9 +84,8 @@
 
   // ui::PlatformWindowDelegate:
   void OnBoundsChanged(const gfx::Rect& new_bounds) override {
-    // TODO(fsamuel): Use the real device_scale_factor.
     delegate_->OnMetricsChanged(new_bounds.size(),
-                                1.f /* device_scale_factor */);
+                                metrics_->device_pixel_ratio);
   }
 
   void OnDamageRect(const gfx::Rect& damaged_region) override {}
@@ -163,7 +160,9 @@
 
   void OnLostCapture() override {}
 
-  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override {
+  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget,
+                                    float device_pixel_ratio) override {
+    metrics_->device_pixel_ratio = device_pixel_ratio;
     delegate_->OnAcceleratedWidgetAvailable(widget,
                                             metrics_->device_pixel_ratio);
   }
diff --git a/components/view_manager/public/cpp/lib/view_manager_init.cc b/components/view_manager/public/cpp/lib/view_manager_init.cc
index 2b5ca5cc..17cf779e 100644
--- a/components/view_manager/public/cpp/lib/view_manager_init.cc
+++ b/components/view_manager/public/cpp/lib/view_manager_init.cc
@@ -30,15 +30,17 @@
 ViewManagerInit::ViewManagerInit(ApplicationImpl* app,
                                  ViewManagerDelegate* delegate,
                                  ViewManagerRootClient* root_client)
-    : app_(app), delegate_(delegate), client_factory_(new ClientFactory(this)) {
+    : app_(app),
+      connection_(nullptr),
+      delegate_(delegate),
+      client_factory_(new ClientFactory(this)) {
   mojo::URLRequestPtr request(mojo::URLRequest::New());
   request->url = mojo::String::From("mojo:view_manager");
-  ApplicationConnection* connection =
-      app_->ConnectToApplication(request.Pass());
-  connection->AddService(client_factory_.get());
-  connection->ConnectToService(&service_);
+  connection_ = app_->ConnectToApplication(request.Pass());
+  connection_->AddService(client_factory_.get());
+  connection_->ConnectToService(&service_);
   service_.set_error_handler(this);
-  connection->ConnectToService(&view_manager_root_);
+  connection_->ConnectToService(&view_manager_root_);
   if (root_client) {
     root_client_binding_.reset(new Binding<ViewManagerRootClient>(root_client));
     ViewManagerRootClientPtr root_client_ptr;
diff --git a/components/view_manager/public/cpp/view_manager_init.h b/components/view_manager/public/cpp/view_manager_init.h
index 316597c1..5bff817 100644
--- a/components/view_manager/public/cpp/view_manager_init.h
+++ b/components/view_manager/public/cpp/view_manager_init.h
@@ -14,6 +14,7 @@
 
 namespace mojo {
 
+class ApplicationConnection;
 class ApplicationImpl;
 class ViewManagerDelegate;
 
@@ -32,6 +33,9 @@
   // supplied to the constructor.
   ViewManagerRoot* view_manager_root() { return view_manager_root_.get(); }
 
+  // Returns the application connection established with the view manager.
+  ApplicationConnection* connection() { return connection_; }
+
  private:
   class ClientFactory;
 
@@ -41,6 +45,7 @@
   void OnConnectionError() override;
 
   ApplicationImpl* app_;
+  ApplicationConnection* connection_;
   ViewManagerDelegate* delegate_;
   scoped_ptr<ClientFactory> client_factory_;
   ViewManagerServicePtr service_;
diff --git a/components/view_manager/public/interfaces/quads.mojom b/components/view_manager/public/interfaces/quads.mojom
index b3af3d59..ae482fd 100644
--- a/components/view_manager/public/interfaces/quads.mojom
+++ b/components/view_manager/public/interfaces/quads.mojom
@@ -183,14 +183,15 @@
 };
 
 struct SharedQuadState {
-  // Transforms from quad's original content space to its target content space.
-  Transform content_to_target_transform;
+  // Transforms quad rects into the target content space.
+  Transform quad_to_target_transform;
 
-  // This size lives in the content space for the quad's originating layer.
-  Size content_bounds;
+  // The size of the quads' originating layer in the space of the quad rects.
+  Size quad_layer_bounds;
 
-  // This rect lives in the content space for the quad's originating layer.
-  Rect visible_content_rect;
+  // The size of the visible area in the quads' originating layer, in the space
+  // of the quad rects.
+  Rect visible_quad_layer_rect;
 
   // This rect lives in the target content space.
   Rect clip_rect;
diff --git a/components/view_manager/view_manager_service_impl.cc b/components/view_manager/view_manager_service_impl.cc
index d7d9729..43f4c62 100644
--- a/components/view_manager/view_manager_service_impl.cc
+++ b/components/view_manager/view_manager_service_impl.cc
@@ -466,7 +466,7 @@
   view_data->visible = view->visible();
   view_data->drawn = view->IsDrawn();
   view_data->viewport_metrics =
-      connection_manager_->display_manager()->GetViewportMetrics().Clone();
+      connection_manager_->GetViewportMetricsForView(view);
   return view_data.Pass();
 }
 
diff --git a/components/view_manager/view_manager_service_unittest.cc b/components/view_manager/view_manager_service_unittest.cc
index 2c9e6a62..9ff8283 100644
--- a/components/view_manager/view_manager_service_unittest.cc
+++ b/components/view_manager/view_manager_service_unittest.cc
@@ -520,7 +520,7 @@
   connection1_client->tracker()->changes()->clear();
   wm_client()->tracker()->changes()->clear();
 
-  connection_manager()->ProcessEvent(CreatePointerDownEvent(21, 22));
+  connection_manager()->OnEvent(CreatePointerDownEvent(21, 22));
   // Focus should go to child1. This results in notifying both the window
   // manager and client connection being notified.
   EXPECT_EQ(v1, connection_manager()->GetFocusedView());
@@ -532,14 +532,14 @@
       "Focused id=2,1",
       ChangesToDescription1(*connection1_client->tracker()->changes())[0]);
 
-  connection_manager()->ProcessEvent(CreatePointerUpEvent(21, 22));
+  connection_manager()->OnEvent(CreatePointerUpEvent(21, 22));
   wm_client()->tracker()->changes()->clear();
   connection1_client->tracker()->changes()->clear();
 
   // Press outside of the embedded view. Focus should go to the root. Notice
   // the client1 doesn't see who has focus as the focused view (root) isn't
   // visible to it.
-  connection_manager()->ProcessEvent(CreatePointerDownEvent(61, 22));
+  connection_manager()->OnEvent(CreatePointerDownEvent(61, 22));
   EXPECT_EQ(connection_manager()->GetRoot(),
             connection_manager()->GetFocusedView());
   ASSERT_GE(wm_client()->tracker()->changes()->size(), 1u);
@@ -550,13 +550,13 @@
       "Focused id=null",
       ChangesToDescription1(*connection1_client->tracker()->changes())[0]);
 
-  connection_manager()->ProcessEvent(CreatePointerUpEvent(21, 22));
+  connection_manager()->OnEvent(CreatePointerUpEvent(21, 22));
   wm_client()->tracker()->changes()->clear();
   connection1_client->tracker()->changes()->clear();
 
   // Press in the same location. Should not get a focus change event (only input
   // event).
-  connection_manager()->ProcessEvent(CreatePointerDownEvent(61, 22));
+  connection_manager()->OnEvent(CreatePointerDownEvent(61, 22));
   EXPECT_EQ(connection_manager()->GetRoot(),
             connection_manager()->GetFocusedView());
   ASSERT_EQ(wm_client()->tracker()->changes()->size(), 1u);
diff --git a/content/app/android/child_process_service.cc b/content/app/android/child_process_service.cc
index 2575d3c..c6301ae 100644
--- a/content/app/android/child_process_service.cc
+++ b/content/app/android/child_process_service.cc
@@ -154,7 +154,7 @@
                                   jint fd,
                                   jlong offset,
                                   jlong size) {
-  base::MemoryMappedFile::Region region(offset, size);
+  base::MemoryMappedFile::Region region = {offset, size};
   base::GlobalDescriptors::GetInstance()->Set(id, fd, region);
 }
 
diff --git a/content/app/strings/content_strings.grd b/content/app/strings/content_strings.grd
index e853a97..64fbc02 100644
--- a/content/app/strings/content_strings.grd
+++ b/content/app/strings/content_strings.grd
@@ -335,6 +335,9 @@
         <ph name="WEEK">$1<ex>Week 38, 2014</ex></ph>, starting on <ph name="WEEK_START_DATE">$2<ex>September 15, 2014</ex></ph>
       </message>
 
+      <message name="IDS_AX_ROLE_ADDRESS" desc="accessibility role description for an address (as in a person's contact information)">
+        address
+      </message>
       <message name="IDS_AX_ROLE_ARTICLE" desc="accessibility role description for article">
         article
       </message>
@@ -353,26 +356,11 @@
       <message name="IDS_AX_ROLE_DESCRIPTION_TERM" desc="accessibility role description for description term(as in a description list)">
         term
       </message>
-      <message name="IDS_AX_ROLE_WEB_AREA" desc="accessibility role description for web area">
-        HTML content
-      </message>
-      <message name="IDS_AX_ROLE_LINK" desc="accessibility role description for link">
-        link
-      </message>
-      <message name="IDS_AX_ROLE_LIST_MARKER" desc="accessibility role description for list marker">
-        list marker
-      </message>
-      <message name="IDS_AX_ROLE_IMAGE_MAP" desc="accessibility role description for image map">
-        image map
-      </message>
-      <message name="IDS_AX_ROLE_HEADING" desc="accessibility role description for headings">
-        heading
-      </message>
       <message name="IDS_AX_ROLE_FIGURE" desc="accessibility role description for figure">
         figure
       </message>
-      <message name="IDS_AX_ROLE_REGION" desc="accessibility role description for region">
-        region
+      <message name="IDS_AX_ROLE_FORM" desc="accessibility role description for form">
+        form
       </message>
       <if expr="is_macosx">
         <message name="IDS_AX_ROLE_FOOTER" desc="accessibility role description for footers">
@@ -385,30 +373,45 @@
           toggle button
         </message>
       </if>
-      <message name="IDS_AX_ROLE_ADDRESS" desc="accessibility role description for an address (as in a person's contact information)">
-        address
+      <message name="IDS_AX_ROLE_HEADING" desc="accessibility role description for headings">
+        heading
+      </message>
+      <message name="IDS_AX_ROLE_IMAGE_MAP" desc="accessibility role description for image map">
+        image map
+      </message>
+      <message name="IDS_AX_ROLE_LINK" desc="accessibility role description for link">
+        link
+      </message>
+      <message name="IDS_AX_ROLE_LIST_MARKER" desc="accessibility role description for list marker">
+        list marker
       </message>
       <message name="IDS_AX_ROLE_MAIN_CONTENT" desc="accessibility role description for main content of the document.">
         main
       </message>
-      <message name="IDS_AX_ROLE_NAVIGATIONAL_LINK" desc="accessibility role description for group of navigational links.">
-        navigation
-      </message>
-      <message name="IDS_AX_ROLE_FORM" desc="accessibility role description for form">
-        form
+      <message name="IDS_AX_ROLE_MARK" desc="accessibility role description for highlighted content.">
+        highlighted content
       </message>
       <message name="IDS_AX_ROLE_MATH" desc="accessibility role description for math">
         math
       </message>
-      <message name="IDS_AX_ROLE_STATUS" desc="accessibility role description for status">
-        status
+      <message name="IDS_AX_ROLE_NAVIGATIONAL_LINK" desc="accessibility role description for group of navigational links.">
+        navigation
+      </message>
+      <message name="IDS_AX_ROLE_REGION" desc="accessibility role description for region">
+        region
       </message>
       <message name="IDS_AX_ROLE_SEARCH_BOX" desc="accessibility role description for search text field">
         search text field
       </message>
+      <message name="IDS_AX_ROLE_STATUS" desc="accessibility role description for status">
+        status
+      </message>
       <message name="IDS_AX_ROLE_SWITCH" desc="accessibility role description for switch">
         switch
       </message>
+      <message name="IDS_AX_ROLE_WEB_AREA" desc="accessibility role description for web area">
+        HTML content
+      </message>
 
       <message name="IDS_AX_BUTTON_ACTION_VERB" desc="Verb stating the action that will occur when a button is pressed, as used by accessibility.">
         press
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 3cdfc88..66ed8d8 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -114,6 +114,7 @@
       "//content/common:mojo_bindings",
       "//content/public/common:mojo_bindings",
       "//device/bluetooth",
+      "//gin",
       "//mojo/application/public/interfaces",
       "//storage/browser",
       "//storage/common",
@@ -479,4 +480,23 @@
   if (enable_media_mojo_renderer) {
     deps += [ "//media/mojo/services:renderer_service" ]
   }
+
+  if (enable_webvr) {
+    sources += [
+      "vr/vr_device.cc",
+      "vr/vr_device.h",
+      "vr/vr_device_manager.cc",
+      "vr/vr_device_manager.h",
+      "vr/vr_device_provider.h",
+    ]
+  }
+
+  if (enable_webvr && is_android) {
+    sources += [
+      "vr/android/cardboard/cardboard_vr_device.cc",
+      "vr/android/cardboard/cardboard_vr_device.h",
+      "vr/android/cardboard/cardboard_vr_device_provider.cc",
+      "vr/android/cardboard/cardboard_vr_device_provider.h",
+    ]
+  }
 }
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 2ed7e5c..46b4e981 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -9,6 +9,7 @@
   "+content/public/browser",
   "+device/battery",  # For battery status service.
   "+device/vibration",  # For Vibration API
+  "+gin/v8_initializer.h",
   "+media/audio",  # For audio input for speech input feature.
   "+media/base",  # For Android JNI registration.
   "+media/filters",  # For reporting GPU decoding UMA.
@@ -63,6 +64,7 @@
   "+third_party/WebKit/public/platform/modules/indexeddb/WebIDBTypes.h",
   "+third_party/WebKit/public/platform/modules/notifications/WebNotificationPermission.h",
   "+third_party/WebKit/public/platform/modules/push_messaging/WebPushPermissionStatus.h",
+  "+third_party/WebKit/public/platform/modules/vr/WebVR.h",
   "+third_party/WebKit/public/web/mac/WebScrollbarTheme.h",
   "+third_party/WebKit/public/web/WebAXEnums.h",
   "+third_party/WebKit/public/web/WebCompositionUnderline.h",
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index 2b5903bd..b2fef33 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -971,6 +971,9 @@
   case ui::AX_ROLE_MAIN:
     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
         IDS_AX_ROLE_MAIN_CONTENT));
+  case ui::AX_ROLE_MARK:
+    return base::SysUTF16ToNSString(content_client->GetLocalizedString(
+        IDS_AX_ROLE_MARK));
   case ui::AX_ROLE_MATH:
     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
         IDS_AX_ROLE_MATH));
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index 79ac47d..79f2b1cc 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -284,6 +284,12 @@
         base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj(),
         node->IsLink());
   }
+  base::string16 element_id;
+  if (node->GetHtmlAttribute("id", &element_id)) {
+    Java_BrowserAccessibilityManager_setAccessibilityNodeInfoViewIdResourceName(
+        env, obj, info,
+        base::android::ConvertUTF16ToJavaString(env, element_id).obj());
+  }
 
   gfx::Rect absolute_rect = node->GetLocalBoundsRect();
   gfx::Rect parent_relative_rect = absolute_rect;
diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc
index 3c79052a..285f99d 100644
--- a/content/browser/accessibility/browser_accessibility_win.cc
+++ b/content/browser/accessibility/browser_accessibility_win.cc
@@ -4120,6 +4120,10 @@
       ia_role = ROLE_SYSTEM_GROUPING;
       ia2_role = IA2_ROLE_PARAGRAPH;
       break;
+    case ui::AX_ROLE_MARK:
+      ia_role = ROLE_SYSTEM_TEXT;
+      ia2_role = IA2_ROLE_TEXT_FRAME;
+      break;
     case ui::AX_ROLE_MARQUEE:
       ia_role = ROLE_SYSTEM_ANIMATION;
       break;
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 288e6e46..bae4365e 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -965,7 +965,7 @@
   RunHtmlTest(FILE_PATH_LITERAL("main.html"));
 }
 
-IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, DISABLED_AccessibilityMark) {
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityMark) {
   RunHtmlTest(FILE_PATH_LITERAL("mark.html"));
 }
 
diff --git a/content/browser/android/browser_jni_registrar.cc b/content/browser/android/browser_jni_registrar.cc
index 808e3f3e..43adba8 100644
--- a/content/browser/android/browser_jni_registrar.cc
+++ b/content/browser/android/browser_jni_registrar.cc
@@ -40,6 +40,7 @@
 #include "content/browser/screen_orientation/screen_orientation_delegate_android.h"
 #include "content/browser/speech/speech_recognizer_impl_android.h"
 #include "content/browser/time_zone_monitor_android.h"
+#include "content/browser/vr/android/cardboard/cardboard_vr_device.h"
 #include "content/browser/web_contents/web_contents_android.h"
 #include "mojo/android/system/core_impl.h"
 
@@ -52,6 +53,10 @@
     {"BrowserAccessibilityManager",
      content::RegisterBrowserAccessibilityManager},
     {"BrowserStartupController", content::RegisterBrowserStartupController},
+#if defined(ENABLE_WEBVR)
+    {"CardboardVRDevice",
+     content::CardboardVRDevice::RegisterCardboardVRDevice},
+#endif
     {"ChildProcessLauncher", content::RegisterChildProcessLauncher},
     {"ContentReadbackHandler",
      content::ContentReadbackHandler::RegisterContentReadbackHandler},
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc
index a47ded2..7632641 100644
--- a/content/browser/android/content_view_core_impl.cc
+++ b/content/browser/android/content_view_core_impl.cc
@@ -1216,6 +1216,15 @@
   return rwhva->GetNativeImeAdapter();
 }
 
+void ContentViewCoreImpl::ForceUpdateImeAdapter(long native_ime_adapter) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+  if (obj.is_null())
+    return;
+  Java_ContentViewCore_forceUpdateImeAdapter(env, obj.obj(),
+                                             native_ime_adapter);
+}
+
 void ContentViewCoreImpl::UpdateImeAdapter(long native_ime_adapter,
                                            int text_input_type,
                                            int text_input_flags,
diff --git a/content/browser/android/content_view_core_impl.h b/content/browser/android/content_view_core_impl.h
index a4e1bddc..f50ffde 100644
--- a/content/browser/android/content_view_core_impl.h
+++ b/content/browser/android/content_view_core_impl.h
@@ -225,6 +225,7 @@
                        const gfx::Vector2dF& content_offset,
                        bool is_mobile_optimized_hint);
 
+  void ForceUpdateImeAdapter(long native_ime_adapter);
   void UpdateImeAdapter(long native_ime_adapter,
                         int text_input_type,
                         int text_input_flags,
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index 10a6313..30d33d4a 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -112,6 +112,7 @@
   SWDH_UPDATE_NO_HOST = 86,
   SWDH_UPDATE_BAD_REGISTRATION_ID = 87,
   SWDH_UPDATE_CANNOT = 88,
+  SWDH_UNREGISTER_BAD_REGISTRATION_ID = 89,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 9f189de..67f5a8a 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -841,6 +841,11 @@
             "Thread", "BrowserThread::IO");
         thread_to_start = &io_thread_;
         options = io_message_loop_options;
+#if defined(OS_ANDROID)
+        // Up the priority of the |io_thread_| as some of its IPCs relate to
+        // display tasks.
+        options.priority = base::ThreadPriority::DISPLAY;
+#endif
         break;
       case BrowserThread::UI:
       case BrowserThread::ID_COUNT:
@@ -1124,9 +1129,7 @@
   // GpuDataManager for in-process initialized in PreCreateThreads.
   bool initialize_gpu_data_manager = !UsingInProcessGpu();
 #if defined(OS_ANDROID)
-  // Up the priority of anything that touches with display tasks
-  // (this thread is UI thread, and io_thread_ is for IPCs).
-  io_thread_->SetPriority(base::ThreadPriority::DISPLAY);
+  // Up the priority of the UI thread.
   base::PlatformThread::SetThreadPriority(base::PlatformThread::CurrentHandle(),
                                           base::ThreadPriority::DISPLAY);
 
diff --git a/content/browser/cache_storage/cache_storage_cache.cc b/content/browser/cache_storage/cache_storage_cache.cc
index ae64bad..31a5129 100644
--- a/content/browser/cache_storage/cache_storage_cache.cc
+++ b/content/browser/cache_storage/cache_storage_cache.cc
@@ -32,8 +32,24 @@
 
 namespace {
 
-typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
-    EntryBoolCallback;
+// This class ensures that the cache and the entry have a lifetime as long as
+// the blob that is created to contain them.
+class CacheStorageCacheDataHandle
+    : public storage::BlobDataBuilder::DataHandle {
+ public:
+  CacheStorageCacheDataHandle(const scoped_refptr<CacheStorageCache>& cache,
+                              disk_cache::ScopedEntryPtr entry)
+      : cache_(cache), entry_(entry.Pass()) {}
+
+ private:
+  ~CacheStorageCacheDataHandle() override {}
+
+  scoped_refptr<CacheStorageCache> cache_;
+  disk_cache::ScopedEntryPtr entry_;
+
+  DISALLOW_COPY_AND_ASSIGN(CacheStorageCacheDataHandle);
+};
+
 typedef base::Callback<void(scoped_ptr<CacheMetadata>)> MetadataCallback;
 
 enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY };
@@ -42,9 +58,6 @@
 // per-origin.
 const int kMaxCacheBytes = 512 * 1024 * 1024;
 
-// Buffer size for cache and blob reading/writing.
-const int kBufferSize = 1024 * 512;
-
 void NotReachedCompletionCallback(int rv) {
   NOTREACHED();
 }
@@ -198,39 +211,6 @@
   DISALLOW_COPY_AND_ASSIGN(KeysContext);
 };
 
-struct CacheStorageCache::MatchContext {
-  MatchContext(scoped_ptr<ServiceWorkerFetchRequest> request,
-               const CacheStorageCache::ResponseCallback& callback,
-               base::WeakPtr<storage::BlobStorageContext> blob_storage_context)
-      : request(request.Pass()),
-        original_callback(callback),
-        blob_storage_context(blob_storage_context),
-        entry(nullptr),
-        total_bytes_read(0) {}
-
-  ~MatchContext() {
-    if (entry)
-      entry->Close();
-  }
-
-  // Input
-  scoped_ptr<ServiceWorkerFetchRequest> request;
-  CacheStorageCache::ResponseCallback original_callback;
-  base::WeakPtr<storage::BlobStorageContext> blob_storage_context;
-  disk_cache::Entry* entry;
-
-  // Output
-  scoped_ptr<ServiceWorkerResponse> response;
-  scoped_ptr<storage::BlobDataBuilder> blob_data;
-
-  // For reading the cache entry data into a blob.
-  scoped_refptr<net::IOBufferWithSize> response_body_buffer;
-  size_t total_bytes_read;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MatchContext);
-};
-
 // The state needed to pass between CacheStorageCache::Put callbacks.
 struct CacheStorageCache::PutContext {
   PutContext(
@@ -247,12 +227,7 @@
         blob_data_handle(blob_data_handle.Pass()),
         callback(callback),
         request_context_getter(request_context_getter),
-        quota_manager_proxy(quota_manager_proxy),
-        cache_entry(NULL) {}
-  ~PutContext() {
-    if (cache_entry)
-      cache_entry->Close();
-  }
+        quota_manager_proxy(quota_manager_proxy) {}
 
   // Input parameters to the Put function.
   GURL origin;
@@ -262,10 +237,7 @@
   CacheStorageCache::ErrorCallback callback;
   scoped_refptr<net::URLRequestContextGetter> request_context_getter;
   scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy;
-
-  // This isn't a scoped_ptr because the disk_cache needs an Entry** as input to
-  // CreateEntry.
-  disk_cache::Entry* cache_entry;
+  disk_cache::ScopedEntryPtr cache_entry;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(PutContext);
@@ -478,15 +450,14 @@
     return;
   }
 
-  scoped_ptr<MatchContext> match_context(
-      new MatchContext(request.Pass(), callback, blob_storage_context_));
+  scoped_ptr<disk_cache::Entry*> scoped_entry_ptr(new disk_cache::Entry*());
+  disk_cache::Entry** entry_ptr = scoped_entry_ptr.get();
+  ServiceWorkerFetchRequest* request_ptr = request.get();
 
-  disk_cache::Entry** entry_ptr = &match_context->entry;
-  ServiceWorkerFetchRequest* request_ptr = match_context->request.get();
-
-  net::CompletionCallback open_entry_callback = base::Bind(
-      &CacheStorageCache::MatchDidOpenEntry, weak_ptr_factory_.GetWeakPtr(),
-      base::Passed(match_context.Pass()));
+  net::CompletionCallback open_entry_callback =
+      base::Bind(&CacheStorageCache::MatchDidOpenEntry,
+                 weak_ptr_factory_.GetWeakPtr(), base::Passed(request.Pass()),
+                 callback, base::Passed(scoped_entry_ptr.Pass()));
 
   int rv = backend_->OpenEntry(request_ptr->url.spec(), entry_ptr,
                                open_entry_callback);
@@ -495,44 +466,43 @@
 }
 
 void CacheStorageCache::MatchDidOpenEntry(
-    scoped_ptr<MatchContext> match_context,
+    scoped_ptr<ServiceWorkerFetchRequest> request,
+    const ResponseCallback& callback,
+    scoped_ptr<disk_cache::Entry*> entry_ptr,
     int rv) {
   if (rv != net::OK) {
-    match_context->original_callback.Run(CACHE_STORAGE_ERROR_NOT_FOUND,
-                                         scoped_ptr<ServiceWorkerResponse>(),
-                                         scoped_ptr<storage::BlobDataHandle>());
+    callback.Run(CACHE_STORAGE_ERROR_NOT_FOUND,
+                 scoped_ptr<ServiceWorkerResponse>(),
+                 scoped_ptr<storage::BlobDataHandle>());
     return;
   }
-
-  // Copy the entry pointer before passing it in base::Bind.
-  disk_cache::Entry* tmp_entry_ptr = match_context->entry;
-  DCHECK(tmp_entry_ptr);
+  disk_cache::ScopedEntryPtr entry(*entry_ptr);
 
   MetadataCallback headers_callback = base::Bind(
       &CacheStorageCache::MatchDidReadMetadata, weak_ptr_factory_.GetWeakPtr(),
-      base::Passed(match_context.Pass()));
+      base::Passed(request.Pass()), callback, base::Passed(entry.Pass()));
 
-  ReadMetadata(tmp_entry_ptr, headers_callback);
+  ReadMetadata(*entry_ptr, headers_callback);
 }
 
 void CacheStorageCache::MatchDidReadMetadata(
-    scoped_ptr<MatchContext> match_context,
+    scoped_ptr<ServiceWorkerFetchRequest> request,
+    const ResponseCallback& callback,
+    disk_cache::ScopedEntryPtr entry,
     scoped_ptr<CacheMetadata> metadata) {
   if (!metadata) {
-    match_context->original_callback.Run(CACHE_STORAGE_ERROR_STORAGE,
-                                         scoped_ptr<ServiceWorkerResponse>(),
-                                         scoped_ptr<storage::BlobDataHandle>());
+    callback.Run(CACHE_STORAGE_ERROR_STORAGE,
+                 scoped_ptr<ServiceWorkerResponse>(),
+                 scoped_ptr<storage::BlobDataHandle>());
     return;
   }
 
-  match_context->response.reset(new ServiceWorkerResponse(
-      match_context->request->url, metadata->response().status_code(),
+  scoped_ptr<ServiceWorkerResponse> response(new ServiceWorkerResponse(
+      request->url, metadata->response().status_code(),
       metadata->response().status_text(),
       ProtoResponseTypeToWebResponseType(metadata->response().response_type()),
       ServiceWorkerHeaderMap(), "", 0, GURL()));
 
-  ServiceWorkerResponse* response = match_context->response.get();
-
   if (metadata->response().has_url())
     response->url = GURL(metadata->response().url());
 
@@ -551,107 +521,39 @@
     cached_request_headers[header.name()] = header.value();
   }
 
-  if (!VaryMatches(match_context->request->headers, cached_request_headers,
+  if (!VaryMatches(request->headers, cached_request_headers,
                    response->headers)) {
-    match_context->original_callback.Run(CACHE_STORAGE_ERROR_NOT_FOUND,
-                                         scoped_ptr<ServiceWorkerResponse>(),
-                                         scoped_ptr<storage::BlobDataHandle>());
+    callback.Run(CACHE_STORAGE_ERROR_NOT_FOUND,
+                 scoped_ptr<ServiceWorkerResponse>(),
+                 scoped_ptr<storage::BlobDataHandle>());
     return;
   }
 
-  if (match_context->entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) {
-    match_context->original_callback.Run(CACHE_STORAGE_OK,
-                                         match_context->response.Pass(),
-                                         scoped_ptr<storage::BlobDataHandle>());
+  if (entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) {
+    callback.Run(CACHE_STORAGE_OK, response.Pass(),
+                 scoped_ptr<storage::BlobDataHandle>());
     return;
   }
 
-  // Stream the response body into a blob.
-  if (!match_context->blob_storage_context) {
-    match_context->original_callback.Run(CACHE_STORAGE_ERROR_STORAGE,
-                                         scoped_ptr<ServiceWorkerResponse>(),
-                                         scoped_ptr<storage::BlobDataHandle>());
+  if (!blob_storage_context_) {
+    callback.Run(CACHE_STORAGE_ERROR_STORAGE,
+                 scoped_ptr<ServiceWorkerResponse>(),
+                 scoped_ptr<storage::BlobDataHandle>());
     return;
   }
 
+  // Create a blob with the response body data.
+  response->blob_size = entry->GetDataSize(INDEX_RESPONSE_BODY);
   response->blob_uuid = base::GenerateGUID();
+  storage::BlobDataBuilder blob_data(response->blob_uuid);
 
-  match_context->blob_data.reset(
-      new storage::BlobDataBuilder(response->blob_uuid));
-  match_context->response_body_buffer = new net::IOBufferWithSize(kBufferSize);
-
-  disk_cache::Entry* tmp_entry_ptr = match_context->entry;
-  net::IOBufferWithSize* response_body_buffer =
-      match_context->response_body_buffer.get();
-
-  net::CompletionCallback read_callback = base::Bind(
-      &CacheStorageCache::MatchDidReadResponseBodyData,
-      weak_ptr_factory_.GetWeakPtr(), base::Passed(match_context.Pass()));
-
-  int read_rv =
-      tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY, 0, response_body_buffer,
-                              response_body_buffer->size(), read_callback);
-
-  if (read_rv != net::ERR_IO_PENDING)
-    read_callback.Run(read_rv);
-}
-
-void CacheStorageCache::MatchDidReadResponseBodyData(
-    scoped_ptr<MatchContext> match_context,
-    int rv) {
-  if (rv < 0) {
-    match_context->original_callback.Run(CACHE_STORAGE_ERROR_STORAGE,
-                                         scoped_ptr<ServiceWorkerResponse>(),
-                                         scoped_ptr<storage::BlobDataHandle>());
-    return;
-  }
-
-  if (rv == 0) {
-    match_context->response->blob_uuid = match_context->blob_data->uuid();
-    match_context->response->blob_size = match_context->total_bytes_read;
-    MatchDoneWithBody(match_context.Pass());
-    return;
-  }
-
-  // TODO(jkarlin): This copying of the the entire cache response into memory is
-  // awful. Create a new interface around SimpleCache that provides access the
-  // data directly from the file. See bug http://crbug.com/403493.
-  match_context->blob_data->AppendData(
-      match_context->response_body_buffer->data(), rv);
-  match_context->total_bytes_read += rv;
-  int total_bytes_read = match_context->total_bytes_read;
-
-  // Grab some pointers before passing match_context in bind.
-  net::IOBufferWithSize* buffer = match_context->response_body_buffer.get();
-  disk_cache::Entry* tmp_entry_ptr = match_context->entry;
-
-  net::CompletionCallback read_callback = base::Bind(
-      &CacheStorageCache::MatchDidReadResponseBodyData,
-      weak_ptr_factory_.GetWeakPtr(), base::Passed(match_context.Pass()));
-
-  int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY, total_bytes_read,
-                                        buffer, buffer->size(), read_callback);
-
-  if (read_rv != net::ERR_IO_PENDING)
-    read_callback.Run(read_rv);
-}
-
-void CacheStorageCache::MatchDoneWithBody(
-    scoped_ptr<MatchContext> match_context) {
-  if (!match_context->blob_storage_context) {
-    match_context->original_callback.Run(CACHE_STORAGE_ERROR_STORAGE,
-                                         scoped_ptr<ServiceWorkerResponse>(),
-                                         scoped_ptr<storage::BlobDataHandle>());
-    return;
-  }
-
+  disk_cache::Entry* temp_entry = entry.get();
+  blob_data.AppendDiskCacheEntry(
+      new CacheStorageCacheDataHandle(this, entry.Pass()), temp_entry,
+      INDEX_RESPONSE_BODY);
   scoped_ptr<storage::BlobDataHandle> blob_data_handle(
-      match_context->blob_storage_context->AddFinishedBlob(
-          match_context->blob_data.get()));
-
-  match_context->original_callback.Run(CACHE_STORAGE_OK,
-                                       match_context->response.Pass(),
-                                       blob_data_handle.Pass());
+      blob_storage_context_->AddFinishedBlob(&blob_data));
+  callback.Run(CACHE_STORAGE_OK, response.Pass(), blob_data_handle.Pass());
 }
 
 void CacheStorageCache::Put(const CacheStorageBatchOperation& operation,
@@ -722,13 +624,14 @@
     return;
   }
 
-  disk_cache::Entry** entry_ptr = &put_context->cache_entry;
+  scoped_ptr<disk_cache::Entry*> scoped_entry_ptr(new disk_cache::Entry*());
+  disk_cache::Entry** entry_ptr = scoped_entry_ptr.get();
   ServiceWorkerFetchRequest* request_ptr = put_context->request.get();
   disk_cache::Backend* backend_ptr = backend_.get();
 
   net::CompletionCallback create_entry_callback = base::Bind(
       &CacheStorageCache::PutDidCreateEntry, weak_ptr_factory_.GetWeakPtr(),
-      base::Passed(put_context.Pass()));
+      base::Passed(scoped_entry_ptr.Pass()), base::Passed(put_context.Pass()));
 
   int create_rv = backend_ptr->CreateEntry(request_ptr->url.spec(), entry_ptr,
                                            create_entry_callback);
@@ -737,14 +640,15 @@
     create_entry_callback.Run(create_rv);
 }
 
-void CacheStorageCache::PutDidCreateEntry(scoped_ptr<PutContext> put_context,
-                                          int rv) {
+void CacheStorageCache::PutDidCreateEntry(
+    scoped_ptr<disk_cache::Entry*> entry_ptr,
+    scoped_ptr<PutContext> put_context,
+    int rv) {
   if (rv != net::OK) {
     put_context->callback.Run(CACHE_STORAGE_ERROR_EXISTS);
     return;
   }
-
-  DCHECK(put_context->cache_entry);
+  put_context->cache_entry.reset(*entry_ptr);
 
   CacheMetadata metadata;
   CacheRequest* request_metadata = metadata.mutable_request();
@@ -785,15 +689,15 @@
       new net::StringIOBuffer(serialized.Pass()));
 
   // Get a temporary copy of the entry pointer before passing it in base::Bind.
-  disk_cache::Entry* tmp_entry_ptr = put_context->cache_entry;
+  disk_cache::Entry* temp_entry_ptr = put_context->cache_entry.get();
 
   net::CompletionCallback write_headers_callback = base::Bind(
       &CacheStorageCache::PutDidWriteHeaders, weak_ptr_factory_.GetWeakPtr(),
       base::Passed(put_context.Pass()), buffer->size());
 
-  rv = tmp_entry_ptr->WriteData(INDEX_HEADERS, 0 /* offset */, buffer.get(),
-                                buffer->size(), write_headers_callback,
-                                true /* truncate */);
+  rv = temp_entry_ptr->WriteData(INDEX_HEADERS, 0 /* offset */, buffer.get(),
+                                 buffer->size(), write_headers_callback,
+                                 true /* truncate */);
 
   if (rv != net::ERR_IO_PENDING)
     write_headers_callback.Run(rv);
@@ -825,11 +729,13 @@
 
   DCHECK(put_context->blob_data_handle);
 
-  disk_cache::ScopedEntryPtr entry(put_context->cache_entry);
+  disk_cache::ScopedEntryPtr entry(put_context->cache_entry.Pass());
   put_context->cache_entry = NULL;
-  scoped_ptr<CacheStorageBlobToDiskCache> reader(
-      new CacheStorageBlobToDiskCache());
-  CacheStorageBlobToDiskCache* reader_ptr = reader.get();
+
+  CacheStorageBlobToDiskCache* blob_to_cache =
+      new CacheStorageBlobToDiskCache();
+  BlobToDiskCacheIDMap::KeyType blob_to_cache_key =
+      active_blob_to_disk_cache_writers_.Add(blob_to_cache);
 
   // Grab some pointers before passing put_context in Bind.
   scoped_refptr<net::URLRequestContextGetter> request_context_getter =
@@ -837,22 +743,23 @@
   scoped_ptr<storage::BlobDataHandle> blob_data_handle =
       put_context->blob_data_handle.Pass();
 
-  reader_ptr->StreamBlobToCache(
+  blob_to_cache->StreamBlobToCache(
       entry.Pass(), INDEX_RESPONSE_BODY, request_context_getter,
       blob_data_handle.Pass(),
       base::Bind(&CacheStorageCache::PutDidWriteBlobToCache,
                  weak_ptr_factory_.GetWeakPtr(),
-                 base::Passed(put_context.Pass()),
-                 base::Passed(reader.Pass())));
+                 base::Passed(put_context.Pass()), blob_to_cache_key));
 }
 
 void CacheStorageCache::PutDidWriteBlobToCache(
     scoped_ptr<PutContext> put_context,
-    scoped_ptr<CacheStorageBlobToDiskCache> blob_reader,
+    BlobToDiskCacheIDMap::KeyType blob_to_cache_key,
     disk_cache::ScopedEntryPtr entry,
     bool success) {
   DCHECK(entry);
-  put_context->cache_entry = entry.release();
+  put_context->cache_entry = entry.Pass();
+
+  active_blob_to_disk_cache_writers_.Remove(blob_to_cache_key);
 
   if (!success) {
     put_context->cache_entry->Doom();
diff --git a/content/browser/cache_storage/cache_storage_cache.h b/content/browser/cache_storage/cache_storage_cache.h
index 3d1552a..74d5fb2 100644
--- a/content/browser/cache_storage/cache_storage_cache.h
+++ b/content/browser/cache_storage/cache_storage_cache.h
@@ -9,6 +9,7 @@
 
 #include "base/callback.h"
 #include "base/files/file_path.h"
+#include "base/id_map.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "content/common/cache_storage/cache_storage_types.h"
@@ -104,7 +105,6 @@
   friend class TestCacheStorageCache;
 
   struct KeysContext;
-  struct MatchContext;
   struct PutContext;
 
   // The backend progresses from uninitialized, to open, to closed, and cannot
@@ -117,6 +117,8 @@
 
   using Entries = std::vector<disk_cache::Entry*>;
   using ScopedBackendPtr = scoped_ptr<disk_cache::Backend>;
+  using BlobToDiskCacheIDMap =
+      IDMap<CacheStorageBlobToDiskCache, IDMapOwnPointer>;
 
   CacheStorageCache(
       const GURL& origin,
@@ -131,12 +133,14 @@
   // Match callbacks
   void MatchImpl(scoped_ptr<ServiceWorkerFetchRequest> request,
                  const ResponseCallback& callback);
-  void MatchDidOpenEntry(scoped_ptr<MatchContext> match_context, int rv);
-  void MatchDidReadMetadata(scoped_ptr<MatchContext> match_context,
+  void MatchDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
+                         const ResponseCallback& callback,
+                         scoped_ptr<disk_cache::Entry*> entry_ptr,
+                         int rv);
+  void MatchDidReadMetadata(scoped_ptr<ServiceWorkerFetchRequest> request,
+                            const ResponseCallback& callback,
+                            disk_cache::ScopedEntryPtr entry,
                             scoped_ptr<CacheMetadata> headers);
-  void MatchDidReadResponseBodyData(scoped_ptr<MatchContext> match_context,
-                                    int rv);
-  void MatchDoneWithBody(scoped_ptr<MatchContext> match_context);
 
   // Puts the request and response object in the cache. The response body (if
   // present) is stored in the cache, but not the request body. Returns OK on
@@ -146,15 +150,16 @@
   void PutImpl(scoped_ptr<PutContext> put_context);
   void PutDidDelete(scoped_ptr<PutContext> put_context,
                     CacheStorageError delete_error);
-  void PutDidCreateEntry(scoped_ptr<PutContext> put_context, int rv);
+  void PutDidCreateEntry(scoped_ptr<disk_cache::Entry*> entry_ptr,
+                         scoped_ptr<PutContext> put_context,
+                         int rv);
   void PutDidWriteHeaders(scoped_ptr<PutContext> put_context,
                           int expected_bytes,
                           int rv);
-  void PutDidWriteBlobToCache(
-      scoped_ptr<PutContext> put_context,
-      scoped_ptr<CacheStorageBlobToDiskCache> blob_reader,
-      disk_cache::ScopedEntryPtr entry,
-      bool success);
+  void PutDidWriteBlobToCache(scoped_ptr<PutContext> put_context,
+                              BlobToDiskCacheIDMap::KeyType blob_to_cache_key,
+                              disk_cache::ScopedEntryPtr entry,
+                              bool success);
 
   // Returns ERROR_NOT_FOUND if not found. Otherwise deletes and returns OK.
   void Delete(const CacheStorageBatchOperation& operation,
@@ -214,6 +219,9 @@
   scoped_ptr<CacheStorageScheduler> scheduler_;
   bool initializing_;
 
+  // Owns the elements of the list
+  BlobToDiskCacheIDMap active_blob_to_disk_cache_writers_;
+
   // Whether or not to store data in disk or memory.
   bool memory_only_;
 
diff --git a/content/browser/cache_storage/cache_storage_cache_unittest.cc b/content/browser/cache_storage/cache_storage_cache_unittest.cc
index 6a9ce3d..bd74385 100644
--- a/content/browser/cache_storage/cache_storage_cache_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_cache_unittest.cc
@@ -19,6 +19,7 @@
 #include "content/public/common/referrer.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "net/base/test_completion_callback.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_job_factory_impl.h"
@@ -375,10 +376,33 @@
   }
 
   void CopyBody(storage::BlobDataHandle* blob_handle, std::string* output) {
+    *output = std::string();
     scoped_ptr<storage::BlobDataSnapshot> data = blob_handle->CreateSnapshot();
     const auto& items = data->items();
     for (const auto& item : items) {
-      output->append(item->bytes(), item->length());
+      switch (item->type()) {
+        case storage::DataElement::TYPE_BYTES: {
+          output->append(item->bytes(), item->length());
+          break;
+        }
+        case storage::DataElement::TYPE_DISK_CACHE_ENTRY: {
+          disk_cache::Entry* entry = item->disk_cache_entry();
+          int32 body_size = entry->GetDataSize(item->disk_cache_stream_index());
+
+          scoped_refptr<net::IOBuffer> io_buffer = new net::IOBuffer(body_size);
+          net::TestCompletionCallback callback;
+          int rv =
+              entry->ReadData(item->disk_cache_stream_index(), 0,
+                              io_buffer.get(), body_size, callback.callback());
+          if (rv == net::ERR_IO_PENDING)
+            rv = callback.WaitForResult();
+          EXPECT_EQ(body_size, rv);
+          if (rv > 0)
+            output->append(io_buffer->data(), rv);
+          break;
+        }
+        default: { ADD_FAILURE() << "invalid response blob type"; } break;
+      }
     }
   }
 
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index 48c5e94..4f8e1909 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
+#include "base/i18n/icu_util.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/metrics/histogram.h"
@@ -43,6 +44,7 @@
 #if defined(OS_POSIX)
 #include "base/posix/global_descriptors.h"
 #include "content/browser/file_descriptor_info_impl.h"
+#include "gin/v8_initializer.h"
 #endif
 
 namespace content {
@@ -141,15 +143,45 @@
 #endif
 #endif
 
-#if defined(OS_ANDROID)
-  // Android WebView runs in single process, ensure that we never get here
-  // when running in single process mode.
-  CHECK(!cmd_line->HasSwitch(switches::kSingleProcess));
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
   std::map<int, base::MemoryMappedFile::Region> regions;
   GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess(
       *cmd_line, child_process_id, files_to_register.get());
+#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
+  base::PlatformFile natives_pf =
+      gin::V8Initializer::GetOpenNativesFileForChildProcesses(
+          &regions[kV8NativesDataDescriptor]);
+  DCHECK_GE(natives_pf, 0);
+  files_to_register->Share(kV8NativesDataDescriptor, natives_pf);
 
-  GetContentClient()->browser()->AppendMappedFileCommandLineSwitches(cmd_line);
+  base::MemoryMappedFile::Region snapshot_region;
+  base::PlatformFile snapshot_pf =
+      gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(
+          &snapshot_region);
+  // Failure to load the V8 snapshot is not necessarily an error. V8 can start
+  // up (slower) without the snapshot.
+  if (snapshot_pf != -1) {
+    files_to_register->Share(kV8SnapshotDataDescriptor, snapshot_pf);
+    regions.insert(std::make_pair(kV8SnapshotDataDescriptor, snapshot_region));
+  }
+
+  if (process_type != switches::kZygoteProcess) {
+    cmd_line->AppendSwitch(::switches::kV8NativesPassedByFD);
+    if (snapshot_pf != -1) {
+      cmd_line->AppendSwitch(::switches::kV8SnapshotPassedByFD);
+    }
+  }
+#endif  // defined(V8_USE_EXTERNAL_STARTUP_DATA)
+#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)
+
+#if defined(OS_ANDROID)
+  files_to_register->Share(
+      kAndroidICUDataDescriptor,
+      base::i18n::GetIcuDataFileHandle(&regions[kAndroidICUDataDescriptor]));
+
+  // Android WebView runs in single process, ensure that we never get here
+  // when running in single process mode.
+  CHECK(!cmd_line->HasSwitch(switches::kSingleProcess));
 
   StartChildProcess(
       cmd_line->argv(), child_process_id, files_to_register.Pass(), regions,
@@ -161,11 +193,6 @@
   // child termination.
 
 #if !defined(OS_MACOSX)
-  GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess(
-      *cmd_line, child_process_id, files_to_register.get());
-
-  GetContentClient()->browser()->AppendMappedFileCommandLineSwitches(cmd_line);
-
   if (use_zygote) {
     base::ProcessHandle handle = ZygoteHostImpl::GetInstance()->ForkRequest(
         cmd_line->argv(), files_to_register.Pass(), process_type);
diff --git a/content/browser/compositor/software_output_device_ozone_unittest.cc b/content/browser/compositor/software_output_device_ozone_unittest.cc
index 99c55bd..19d5c56 100644
--- a/content/browser/compositor/software_output_device_ozone_unittest.cc
+++ b/content/browser/compositor/software_output_device_ozone_unittest.cc
@@ -38,7 +38,8 @@
   void OnClosed() override {}
   void OnWindowStateChanged(ui::PlatformWindowState new_state) override {}
   void OnLostCapture() override {}
-  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override {
+  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget,
+                                    float device_pixel_ratio) override {
     widget_ = widget;
   }
   void OnActivationChanged(bool active) override {}
diff --git a/content/browser/devtools/protocol/service_worker_handler.cc b/content/browser/devtools/protocol/service_worker_handler.cc
index 98173e6..0d856419 100644
--- a/content/browser/devtools/protocol/service_worker_handler.cc
+++ b/content/browser/devtools/protocol/service_worker_handler.cc
@@ -439,12 +439,6 @@
   return Response::OK();
 }
 
-// TODO(horo): I will remove it in crrev.com/1143363009.
-Response ServiceWorkerHandler::GetTargetInfo(DevToolsCommandId command_id,
-                                             const std::string& target_id) {
-  return Response::InternalError("Not implemented yet");
-}
-
 Response ServiceWorkerHandler::GetTargetInfo(
     const std::string& target_id,
     scoped_refptr<TargetInfo>* target_info) {
diff --git a/content/browser/devtools/protocol/service_worker_handler.h b/content/browser/devtools/protocol/service_worker_handler.h
index b4906fe3..81cd71f8 100644
--- a/content/browser/devtools/protocol/service_worker_handler.h
+++ b/content/browser/devtools/protocol/service_worker_handler.h
@@ -59,9 +59,6 @@
   Response DeliverPushMessage(const std::string& origin,
                               const std::string& registration_id,
                               const std::string& data);
-  // TODO(horo): I will remove it in crrev.com/1143363009.
-  Response GetTargetInfo(DevToolsCommandId command_id,
-                         const std::string& target_id);
   Response GetTargetInfo(const std::string& target_id,
                          scoped_refptr<TargetInfo>* target_info);
   Response ActivateTarget(const std::string& target_id);
diff --git a/content/browser/fileapi/blob_storage_context_unittest.cc b/content/browser/fileapi/blob_storage_context_unittest.cc
index 29270b7..5ae280e 100644
--- a/content/browser/fileapi/blob_storage_context_unittest.cc
+++ b/content/browser/fileapi/blob_storage_context_unittest.cc
@@ -14,6 +14,9 @@
 #include "base/run_loop.h"
 #include "base/time/time.h"
 #include "content/browser/fileapi/blob_storage_host.h"
+#include "net/base/io_buffer.h"
+#include "net/base/test_completion_callback.h"
+#include "net/disk_cache/disk_cache.h"
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_data_item.h"
@@ -30,6 +33,45 @@
 namespace content {
 namespace {
 
+const int kTestDiskCacheStreamIndex = 0;
+
+// Our disk cache tests don't need a real data handle since the tests themselves
+// scope the disk cache and entries.
+class EmptyDataHandle : public storage::BlobDataBuilder::DataHandle {
+ private:
+  ~EmptyDataHandle() override {}
+};
+
+scoped_ptr<disk_cache::Backend> CreateInMemoryDiskCache() {
+  scoped_ptr<disk_cache::Backend> cache;
+  net::TestCompletionCallback callback;
+  int rv = disk_cache::CreateCacheBackend(net::MEMORY_CACHE,
+                                          net::CACHE_BACKEND_DEFAULT,
+                                          base::FilePath(), 0,
+                                          false, nullptr, nullptr, &cache,
+                                          callback.callback());
+  EXPECT_EQ(net::OK, callback.GetResult(rv));
+
+  return cache.Pass();
+}
+
+disk_cache::ScopedEntryPtr CreateDiskCacheEntry(disk_cache::Backend* cache,
+                                                const char* key,
+                                                const std::string& data) {
+  disk_cache::Entry* temp_entry = nullptr;
+  net::TestCompletionCallback callback;
+  int rv = cache->CreateEntry(key, &temp_entry, callback.callback());
+  if (callback.GetResult(rv) != net::OK)
+    return nullptr;
+  disk_cache::ScopedEntryPtr entry(temp_entry);
+
+  scoped_refptr<net::StringIOBuffer> iobuffer = new net::StringIOBuffer(data);
+  rv = entry->WriteData(kTestDiskCacheStreamIndex, 0, iobuffer.get(),
+                        iobuffer->size(), callback.callback(), false);
+  EXPECT_EQ(static_cast<int>(data.size()), callback.GetResult(rv));
+  return entry.Pass();
+}
+
 void SetupBasicBlob(BlobStorageHost* host, const std::string& id) {
   EXPECT_TRUE(host->StartBuildingBlob(id));
   DataElement item;
@@ -262,9 +304,48 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST(BlobStorageContextTest, BuildDiskCacheBlob) {
+  base::MessageLoop fake_io_message_loop;
+  scoped_refptr<BlobDataBuilder::DataHandle>
+      data_handle = new EmptyDataHandle();
+
+  {
+    scoped_ptr<BlobStorageContext> context(new BlobStorageContext);
+    BlobStorageHost host(context.get());
+
+    scoped_ptr<disk_cache::Backend> cache = CreateInMemoryDiskCache();
+    ASSERT_TRUE(cache);
+
+    const std::string kTestBlobData = "Test Blob Data";
+    disk_cache::ScopedEntryPtr entry =
+        CreateDiskCacheEntry(cache.get(), "test entry", kTestBlobData);
+
+    const std::string kId1Prime("id1.prime");
+    BlobDataBuilder canonicalized_blob_data(kId1Prime);
+    canonicalized_blob_data.AppendData(kTestBlobData.c_str());
+
+    const std::string kId1("id1");
+    BlobDataBuilder builder(kId1);
+
+    builder.AppendDiskCacheEntry(
+        data_handle, entry.get(), kTestDiskCacheStreamIndex);
+
+    scoped_ptr<BlobDataHandle> blob_data_handle =
+        context->AddFinishedBlob(&builder);
+    scoped_ptr<BlobDataSnapshot> data = blob_data_handle->CreateSnapshot();
+    EXPECT_EQ(*data, builder);
+    EXPECT_FALSE(data_handle->HasOneRef())
+        << "Data handle was destructed while context and builder still exist.";
+  }
+  EXPECT_TRUE(data_handle->HasOneRef())
+      << "Data handle was not destructed along with blob storage context.";
+  base::RunLoop().RunUntilIdle();
+}
+
 TEST(BlobStorageContextTest, CompoundBlobs) {
   const std::string kId1("id1");
   const std::string kId2("id2");
+  const std::string kId3("id3");
   const std::string kId2Prime("id2.prime");
 
   base::MessageLoop fake_io_message_loop;
@@ -286,6 +367,15 @@
   blob_data2.AppendFile(base::FilePath(FILE_PATH_LITERAL("File2.txt")), 0, 20,
                         time2);
 
+  BlobDataBuilder blob_data3(kId3);
+  blob_data3.AppendData("Data4");
+  scoped_ptr<disk_cache::Backend> cache = CreateInMemoryDiskCache();
+  ASSERT_TRUE(cache);
+  disk_cache::ScopedEntryPtr disk_cache_entry =
+      CreateDiskCacheEntry(cache.get(), "another key", "Data5");
+  blob_data3.AppendDiskCacheEntry(new EmptyDataHandle(), disk_cache_entry.get(),
+                                  kTestDiskCacheStreamIndex);
+
   BlobDataBuilder canonicalized_blob_data2(kId2Prime);
   canonicalized_blob_data2.AppendData("Data3");
   canonicalized_blob_data2.AppendData("a2___", 2);
@@ -312,6 +402,12 @@
   ASSERT_TRUE(data);
   EXPECT_EQ(*data, canonicalized_blob_data2);
 
+  // Test a blob referring to only data and a disk cache entry.
+  blob_data_handle = context.AddFinishedBlob(&blob_data3);
+  data = blob_data_handle->CreateSnapshot();
+  ASSERT_TRUE(blob_data_handle);
+  EXPECT_EQ(*data, blob_data3);
+
   blob_data_handle.reset();
   base::RunLoop().RunUntilIdle();
 }
diff --git a/content/browser/fileapi/blob_url_request_job_unittest.cc b/content/browser/fileapi/blob_url_request_job_unittest.cc
index e2fa5fe..17526b8 100644
--- a/content/browser/fileapi/blob_url_request_job_unittest.cc
+++ b/content/browser/fileapi/blob_url_request_job_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/numerics/safe_conversions.h"
@@ -16,7 +17,10 @@
 #include "content/browser/fileapi/mock_url_request_delegate.h"
 #include "content/public/test/async_file_test_helper.h"
 #include "content/public/test/test_file_system_context.h"
+#include "net/base/net_errors.h"
 #include "net/base/request_priority.h"
+#include "net/base/test_completion_callback.h"
+#include "net/disk_cache/disk_cache.h"
 #include "net/http/http_byte_range.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
@@ -48,6 +52,8 @@
 const char kTestFileData2[] = "This is sample file.";
 const char kTestFileSystemFileData1[] = "abcdefghijklmnop";
 const char kTestFileSystemFileData2[] = "File system file test data.";
+const char kTestDiskCacheKey[] = "pipe";
+const char kTestDiskCacheData[] = "Ceci n'est pas un pamplemousse.";
 const char kTestContentType[] = "foo/bar";
 const char kTestContentDisposition[] = "attachment; filename=foo.txt";
 
@@ -55,6 +61,45 @@
 const storage::FileSystemType kFileSystemType =
     storage::kFileSystemTypeTemporary;
 
+const int kTestDiskCacheStreamIndex = 0;
+
+// Our disk cache tests don't need a real data handle since the tests themselves
+// scope the disk cache and entries.
+class EmptyDataHandle : public storage::BlobDataBuilder::DataHandle {
+ private:
+  ~EmptyDataHandle() override {}
+};
+
+scoped_ptr<disk_cache::Backend> CreateInMemoryDiskCache() {
+  scoped_ptr<disk_cache::Backend> cache;
+  net::TestCompletionCallback callback;
+  int rv = disk_cache::CreateCacheBackend(net::MEMORY_CACHE,
+                                          net::CACHE_BACKEND_DEFAULT,
+                                          base::FilePath(), 0,
+                                          false, nullptr, nullptr, &cache,
+                                          callback.callback());
+  EXPECT_EQ(net::OK, callback.GetResult(rv));
+
+  return cache.Pass();
+}
+
+disk_cache::ScopedEntryPtr CreateDiskCacheEntry(disk_cache::Backend* cache,
+                                                const char* key,
+                                                const std::string& data) {
+  disk_cache::Entry* temp_entry = nullptr;
+  net::TestCompletionCallback callback;
+  int rv = cache->CreateEntry(key, &temp_entry, callback.callback());
+  if (callback.GetResult(rv) != net::OK)
+    return nullptr;
+  disk_cache::ScopedEntryPtr entry(temp_entry);
+
+  scoped_refptr<net::StringIOBuffer> iobuffer = new net::StringIOBuffer(data);
+  rv = entry->WriteData(kTestDiskCacheStreamIndex, 0, iobuffer.get(),
+                        iobuffer->size(), callback.callback(), false);
+  EXPECT_EQ(static_cast<int>(data.size()), callback.GetResult(rv));
+  return entry.Pass();
+}
+
 }  // namespace
 
 class BlobURLRequestJobTest : public testing::Test {
@@ -101,6 +146,10 @@
     base::GetFileInfo(temp_file2_, &file_info2);
     temp_file_modification_time2_ = file_info2.last_modified;
 
+    disk_cache_backend_ = CreateInMemoryDiskCache();
+    disk_cache_entry_ = CreateDiskCacheEntry(
+        disk_cache_backend_.get(), kTestDiskCacheKey, kTestDiskCacheData);
+
     url_request_job_factory_.SetProtocolHandler("blob",
                                                 new MockProtocolHandler(this));
     url_request_context_.set_job_factory(&url_request_job_factory_);
@@ -208,18 +257,28 @@
 
   void BuildComplicatedData(std::string* expected_result) {
     blob_data_->AppendData(kTestData1 + 1, 2);
+    *expected_result = std::string(kTestData1 + 1, 2);
+
     blob_data_->AppendFile(temp_file1_, 2, 3, temp_file_modification_time1_);
+    *expected_result += std::string(kTestFileData1 + 2, 3);
+
+    blob_data_->AppendDiskCacheEntry(new EmptyDataHandle(),
+                                     disk_cache_entry_.get(),
+                                     kTestDiskCacheStreamIndex);
+    *expected_result += std::string(kTestDiskCacheData);
+
     blob_data_->AppendFileSystemFile(temp_file_system_file1_, 3, 4,
                                      temp_file_system_file_modification_time1_);
+    *expected_result += std::string(kTestFileSystemFileData1 + 3, 4);
+
     blob_data_->AppendData(kTestData2 + 4, 5);
+    *expected_result += std::string(kTestData2 + 4, 5);
+
     blob_data_->AppendFile(temp_file2_, 5, 6, temp_file_modification_time2_);
+    *expected_result += std::string(kTestFileData2 + 5, 6);
+
     blob_data_->AppendFileSystemFile(temp_file_system_file2_, 6, 7,
                                      temp_file_system_file_modification_time2_);
-    *expected_result = std::string(kTestData1 + 1, 2);
-    *expected_result += std::string(kTestFileData1 + 2, 3);
-    *expected_result += std::string(kTestFileSystemFileData1 + 3, 4);
-    *expected_result += std::string(kTestData2 + 4, 5);
-    *expected_result += std::string(kTestFileData2 + 5, 6);
     *expected_result += std::string(kTestFileSystemFileData2 + 6, 7);
   }
 
@@ -256,6 +315,9 @@
   base::Time temp_file_system_file_modification_time1_;
   base::Time temp_file_system_file_modification_time2_;
 
+  scoped_ptr<disk_cache::Backend> disk_cache_backend_;
+  disk_cache::ScopedEntryPtr disk_cache_entry_;
+
   base::MessageLoopForIO message_loop_;
   scoped_refptr<storage::FileSystemContext> file_system_context_;
 
@@ -291,7 +353,7 @@
     large_data.append(1, static_cast<char>(i % 256));
   ASSERT_EQ(static_cast<int>(large_data.size()),
             base::WriteFile(large_temp_file, large_data.data(),
-                                 large_data.size()));
+                            large_data.size()));
   blob_data_->AppendFile(large_temp_file, 0, kuint64max, base::Time());
   TestSuccessNonrangeRequest(large_data, large_data.size());
 }
@@ -371,7 +433,15 @@
   TestSuccessNonrangeRequest(result, 4);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetComplicatedDataAndFileRequest) {
+TEST_F(BlobURLRequestJobTest, TestGetSimpleDiskCacheRequest) {
+  blob_data_->AppendDiskCacheEntry(new EmptyDataHandle(),
+                                   disk_cache_entry_.get(),
+                                   kTestDiskCacheStreamIndex);
+  TestSuccessNonrangeRequest(kTestDiskCacheData,
+                             arraysize(kTestDiskCacheData) - 1);
+}
+
+TEST_F(BlobURLRequestJobTest, TestGetComplicatedDataFileAndDiskCacheRequest) {
   SetUpFileSystem();
   std::string result;
   BuildComplicatedData(&result);
diff --git a/content/browser/frame_host/frame_navigation_entry.cc b/content/browser/frame_host/frame_navigation_entry.cc
index b2def39..1220a12 100644
--- a/content/browser/frame_host/frame_navigation_entry.cc
+++ b/content/browser/frame_host/frame_navigation_entry.cc
@@ -7,14 +7,20 @@
 namespace content {
 
 FrameNavigationEntry::FrameNavigationEntry(int64 frame_tree_node_id)
-    : frame_tree_node_id_(frame_tree_node_id) {
+    : frame_tree_node_id_(frame_tree_node_id),
+      item_sequence_number_(-1),
+      document_sequence_number_(-1) {
 }
 
 FrameNavigationEntry::FrameNavigationEntry(int64 frame_tree_node_id,
+                                           int64 item_sequence_number,
+                                           int64 document_sequence_number,
                                            SiteInstanceImpl* site_instance,
                                            const GURL& url,
                                            const Referrer& referrer)
     : frame_tree_node_id_(frame_tree_node_id),
+      item_sequence_number_(item_sequence_number),
+      document_sequence_number_(document_sequence_number),
       site_instance_(site_instance),
       url_(url),
       referrer_(referrer) {
@@ -25,14 +31,19 @@
 
 FrameNavigationEntry* FrameNavigationEntry::Clone() const {
   FrameNavigationEntry* copy = new FrameNavigationEntry(frame_tree_node_id_);
-  copy->UpdateEntry(site_instance_.get(), url_, referrer_, page_state_);
+  copy->UpdateEntry(item_sequence_number_, document_sequence_number_,
+                    site_instance_.get(), url_, referrer_, page_state_);
   return copy;
 }
 
-void FrameNavigationEntry::UpdateEntry(SiteInstanceImpl* site_instance,
+void FrameNavigationEntry::UpdateEntry(int64 item_sequence_number,
+                                       int64 document_sequence_number,
+                                       SiteInstanceImpl* site_instance,
                                        const GURL& url,
                                        const Referrer& referrer,
                                        const PageState& page_state) {
+  item_sequence_number_ = item_sequence_number;
+  document_sequence_number_ = document_sequence_number;
   site_instance_ = site_instance;
   url_ = url;
   referrer_ = referrer;
diff --git a/content/browser/frame_host/frame_navigation_entry.h b/content/browser/frame_host/frame_navigation_entry.h
index 6fbd7541..7bc591c 100644
--- a/content/browser/frame_host/frame_navigation_entry.h
+++ b/content/browser/frame_host/frame_navigation_entry.h
@@ -27,10 +27,11 @@
  public:
   // TODO(creis): We should not use FTN IDs here, since they will change if you
   // leave a page and come back later.  We should evaluate whether Blink's
-  // frame sequence numbers or unique names would work instead, similar to
-  // HistoryNode.
+  // unique names would work instead, similar to HistoryNode.
   explicit FrameNavigationEntry(int64 frame_tree_node_id);
   FrameNavigationEntry(int64 frame_tree_node_id,
+                       int64 item_sequence_number,
+                       int64 document_sequence_number,
                        SiteInstanceImpl* site_instance,
                        const GURL& url,
                        const Referrer& referrer);
@@ -40,7 +41,9 @@
   FrameNavigationEntry* Clone() const;
 
   // Updates all the members of this entry.
-  void UpdateEntry(SiteInstanceImpl* site_instance,
+  void UpdateEntry(int64 item_sequence_number,
+                   int64 document_sequence_number,
+                   SiteInstanceImpl* site_instance,
                    const GURL& url,
                    const Referrer& referrer,
                    const PageState& page_state);
@@ -51,6 +54,20 @@
   // TODO(creis): Replace with frame sequence number or unique name.
   int64 frame_tree_node_id() const { return frame_tree_node_id_; }
 
+  // Keeps track of where this entry belongs in the frame's session history.
+  // The item sequence number identifies each stop in the back/forward history
+  // and is globally unique.  The document sequence number increments for each
+  // new document and is also globally unique.  In-page navigations get a new
+  // item sequence number but the same document sequence number.
+  void set_item_sequence_number(int64 item_sequence_number) {
+    item_sequence_number_ = item_sequence_number;
+  }
+  int64 item_sequence_number() const { return item_sequence_number_; }
+  void set_document_sequence_number(int64 document_sequence_number) {
+    document_sequence_number_ = document_sequence_number;
+  }
+  int64 document_sequence_number() const { return document_sequence_number_; }
+
   // The SiteInstance responsible for rendering this frame.  All frames sharing
   // a SiteInstance must live in the same process.  This is a refcounted pointer
   // that keeps the SiteInstance (not necessarily the process) alive as long as
@@ -84,6 +101,8 @@
 
   // See the accessors above for descriptions.
   int64 frame_tree_node_id_;
+  int64 item_sequence_number_;
+  int64 document_sequence_number_;
   scoped_refptr<SiteInstanceImpl> site_instance_;
   GURL url_;
   Referrer referrer_;
diff --git a/content/browser/frame_host/frame_tree.cc b/content/browser/frame_host/frame_tree.cc
index 814980e..bfbda1d 100644
--- a/content/browser/frame_host/frame_tree.cc
+++ b/content/browser/frame_host/frame_tree.cc
@@ -224,9 +224,8 @@
   // Create the swapped out RVH for the new SiteInstance. This will create
   // a top-level swapped out RFH as well, which will then be wrapped by a
   // RenderFrameProxyHost.
-  if (!source->IsMainFrame()) {
-    RenderViewHostImpl* render_view_host =
-        source->frame_tree()->GetRenderViewHost(site_instance);
+  if (!source || !source->IsMainFrame()) {
+    RenderViewHostImpl* render_view_host = GetRenderViewHost(site_instance);
     if (!render_view_host) {
       if (base::CommandLine::ForCurrentProcess()->HasSwitch(
             switches::kSitePerProcess)) {
@@ -237,8 +236,8 @@
             CREATE_RF_SWAPPED_OUT | CREATE_RF_HIDDEN, nullptr);
       }
     } else {
-      root()->render_manager()->EnsureRenderViewInitialized(
-          source, render_view_host, site_instance);
+      root()->render_manager()->EnsureRenderViewInitialized(render_view_host,
+                                                            site_instance);
     }
   }
 
diff --git a/content/browser/frame_host/frame_tree.h b/content/browser/frame_host/frame_tree.h
index ea29984a..8473f15 100644
--- a/content/browser/frame_host/frame_tree.h
+++ b/content/browser/frame_host/frame_tree.h
@@ -87,11 +87,12 @@
 
   // This method walks the entire frame tree and creates a RenderFrameProxyHost
   // for the given |site_instance| in each node except the |source| one --
-  // the source will have a RenderFrameHost. It assumes that no frame tree
-  // nodes already have RenderFrameProxyHost for the given |site_instance|.
-  void CreateProxiesForSiteInstance(
-      FrameTreeNode* source,
-      SiteInstance* site_instance);
+  // the source will have a RenderFrameHost.  |source| may be null if there is
+  // no node navigating in this frame tree (such as when this is called
+  // for an opener's frame tree), in which case no nodes are skipped for
+  // RenderFrameProxyHost creation.
+  void CreateProxiesForSiteInstance(FrameTreeNode* source,
+                                    SiteInstance* site_instance);
 
   // Convenience accessor for the main frame's RenderFrameHostImpl.
   RenderFrameHostImpl* GetMainFrame() const;
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 35813678..182e54a 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -387,7 +387,7 @@
 bool NavigationControllerImpl::HasCommittedRealLoad(
     FrameTreeNode* frame_tree_node) const {
   NavigationEntryImpl* last_committed = GetLastCommittedEntry();
-  return last_committed && last_committed->HasFrameEntry(frame_tree_node);
+  return last_committed && last_committed->GetFrameEntry(frame_tree_node);
 }
 
 void NavigationControllerImpl::LoadEntry(NavigationEntryImpl* entry) {
@@ -967,44 +967,9 @@
       rfh->GetSiteInstance(),
       params.page_id);
   if (existing_entry_index == -1) {
-    // The page was not found. It could have been pruned because of the limit on
-    // back/forward entries (not likely since we'll usually tell it to navigate
-    // to such entries). It could also mean that the renderer is smoking crack.
-    NOTREACHED();
-
-    // Because the unknown entry has committed, we risk showing the wrong URL in
-    // release builds. Instead, we'll kill the renderer process to be safe.
-    LOG(ERROR) << "terminating renderer for bad navigation: " << params.url;
-    RecordAction(base::UserMetricsAction("BadMessageTerminate_NC"));
-
-    // Temporary code so we can get more information.  Format:
-    //  http://url/foo.html#page1#max3#frame1#ids:2_Nx,1_1x,3_2
-    std::string temp = params.url.spec();
-    temp.append("#page");
-    temp.append(base::IntToString(params.page_id));
-    temp.append("#max");
-    temp.append(base::IntToString(delegate_->GetMaxPageID()));
-    temp.append("#frame");
-    temp.append(base::IntToString(rfh->GetRoutingID()));
-    temp.append("#ids");
-    for (int i = 0; i < static_cast<int>(entries_.size()); ++i) {
-      // Append entry metadata (e.g., 3_7x):
-      //  3: page_id
-      //  7: SiteInstance ID, or N for null
-      //  x: appended if not from the current SiteInstance
-      temp.append(base::IntToString(entries_[i]->GetPageID()));
-      temp.append("_");
-      if (entries_[i]->site_instance())
-        temp.append(base::IntToString(entries_[i]->site_instance()->GetId()));
-      else
-        temp.append("N");
-      if (entries_[i]->site_instance() != rfh->GetSiteInstance())
-        temp.append("x");
-      temp.append(",");
-    }
-    GURL url(temp);
-    rfh->render_view_host()->Send(new ViewMsg_TempCrashWithData(url));
-    return NAVIGATION_TYPE_NAV_IGNORE;
+    // The renderer has committed a navigation to an entry that no longer
+    // exists. Because the renderer is showing that page, resurrect that entry.
+    return NAVIGATION_TYPE_NEW_PAGE;
   }
   NavigationEntryImpl* existing_entry = entries_[existing_entry_index].get();
 
@@ -1150,12 +1115,9 @@
   // Now we know that the notification is for an existing page. Find that entry.
   int existing_entry_index = GetEntryIndexWithUniqueID(params.nav_entry_id);
   if (existing_entry_index == -1) {
-    // The page was not found. It could have been pruned because of the limit on
-    // back/forward entries (not likely since we'll usually tell it to navigate
-    // to such entries). It could also mean that the renderer is smoking crack.
-    // TODO(avi): Crash the renderer like we do in the old ClassifyNavigation?
-    NOTREACHED() << "Could not find nav entry with id " << params.nav_entry_id;
-    return NAVIGATION_TYPE_NAV_IGNORE;
+    // The renderer has committed a navigation to an entry that no longer
+    // exists. Because the renderer is showing that page, resurrect that entry.
+    return NAVIGATION_TYPE_NEW_PAGE;
   }
 
   // Any top-level navigations with the same base (minus the reference fragment)
@@ -1179,8 +1141,11 @@
   bool update_virtual_url;
   // Only make a copy of the pending entry if it is appropriate for the new page
   // that was just loaded.  We verify this at a coarse grain by checking that
-  // the SiteInstance hasn't been assigned to something else.
-  if (pending_entry_ &&
+  // the SiteInstance hasn't been assigned to something else, and by making sure
+  // that the pending entry was intended as a new entry (rather than being a
+  // history navigation that was interrupted by an unrelated, renderer-initiated
+  // navigation).
+  if (pending_entry_ && pending_entry_index_ == -1 &&
       (!pending_entry_->site_instance() ||
        pending_entry_->site_instance() == rfh->GetSiteInstance())) {
     new_entry = pending_entry_->Clone();
@@ -1223,6 +1188,12 @@
   new_entry->SetOriginalRequestURL(params.original_request_url);
   new_entry->SetIsOverridingUserAgent(params.is_overriding_user_agent);
 
+  // Update the FrameNavigationEntry for new main frame commits.
+  FrameNavigationEntry* frame_entry =
+      new_entry->GetFrameEntry(rfh->frame_tree_node());
+  frame_entry->set_item_sequence_number(params.item_sequence_number);
+  frame_entry->set_document_sequence_number(params.document_sequence_number);
+
   // history.pushState() is classified as a navigation to a new page, but
   // sets was_within_same_page to true. In this case, we already have the
   // title and favicon available, so set them immediately.
@@ -1399,8 +1370,9 @@
           switches::kSitePerProcess)) {
     // Make sure new_entry takes ownership of frame_entry in a scoped_refptr.
     FrameNavigationEntry* frame_entry = new FrameNavigationEntry(
-        rfh->frame_tree_node()->frame_tree_node_id(), rfh->GetSiteInstance(),
-        params.url, params.referrer);
+        rfh->frame_tree_node()->frame_tree_node_id(),
+        params.item_sequence_number, params.document_sequence_number,
+        rfh->GetSiteInstance(), params.url, params.referrer);
     new_entry = GetLastCommittedEntry()->CloneAndReplace(rfh->frame_tree_node(),
                                                          frame_entry);
     CHECK(frame_entry->HasOneRef());
@@ -1455,9 +1427,10 @@
     // 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.
     NavigationEntryImpl* last_committed = GetLastCommittedEntry();
-    last_committed->AddOrUpdateFrameEntry(rfh->frame_tree_node(),
-                                          rfh->GetSiteInstance(), params.url,
-                                          params.referrer, params.page_state);
+    last_committed->AddOrUpdateFrameEntry(
+        rfh->frame_tree_node(), params.item_sequence_number,
+        params.document_sequence_number, rfh->GetSiteInstance(), params.url,
+        params.referrer, params.page_state);
 
     // Cross-process subframe navigations may leave a pending entry around.
     // Clear it if it's actually for the subframe.
@@ -1784,13 +1757,13 @@
                                                     bool replace) {
   DCHECK(entry->GetTransitionType() != ui::PAGE_TRANSITION_AUTO_SUBFRAME);
 
-  // Copy the pending entry's unique ID to the committed entry.
-  // I don't know if pending_entry_index_ can be other than -1 here.
-  const NavigationEntryImpl* const pending_entry =
-      (pending_entry_index_ == -1) ?
-          pending_entry_ : entries_[pending_entry_index_].get();
-  if (pending_entry)
-    entry->set_unique_id(pending_entry->GetUniqueID());
+  // If the pending_entry_index_ is -1, the navigation was to a new page, and we
+  // need to keep continuity with the pending entry, so copy the pending entry's
+  // unique ID to the committed entry. If the pending_entry_index_ isn't -1,
+  // then the renderer navigated on its own, independent of the pending entry,
+  // so don't copy anything.
+  if (pending_entry_ && pending_entry_index_ == -1)
+    entry->set_unique_id(pending_entry_->GetUniqueID());
 
   DiscardNonCommittedEntriesInternal();
 
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index ae5d04d..96fc602 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -105,11 +105,10 @@
             controller.GetEntryAtIndex(1)->GetUniqueID());
 }
 
-// The renderer uses the position in the history list as a clue to whether a
-// navigation is stale. In the case where the entry limit is reached and the
-// history list is pruned, make sure that there is no mismatch that would cause
-// it to start incorrectly rejecting navigations as stale. See
-// http://crbug.com/89798.
+// This test used to make sure that a scheme used to prevent spoofs didn't ever
+// interfere with navigations. We switched to a different scheme, so now this is
+// just a test to make sure we can still navigate once we prune the history
+// list.
 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
                        DontIgnoreBackAfterNavEntryLimit) {
   NavigationController& controller =
@@ -1548,6 +1547,84 @@
   }
 }
 
+// Verifies that item sequence numbers and document sequence numbers update
+// properly for main frames and subframes.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
+                       FrameNavigationEntry_SequenceNumbers) {
+  const NavigationControllerImpl& controller =
+      static_cast<const NavigationControllerImpl&>(
+          shell()->web_contents()->GetController());
+
+  // 1. Navigate the main frame.
+  GURL url(embedded_test_server()->GetURL(
+      "/navigation_controller/page_with_links.html"));
+  NavigateToURL(shell(), url);
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+
+  FrameNavigationEntry* frame_entry =
+      controller.GetLastCommittedEntry()->GetFrameEntry(root);
+  int64 isn_1 = frame_entry->item_sequence_number();
+  int64 dsn_1 = frame_entry->document_sequence_number();
+  EXPECT_NE(-1, isn_1);
+  EXPECT_NE(-1, dsn_1);
+
+  // 2. Do an in-page fragment navigation.
+  std::string script = "document.getElementById('fraglink').click()";
+  EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+
+  frame_entry = controller.GetLastCommittedEntry()->GetFrameEntry(root);
+  int64 isn_2 = frame_entry->item_sequence_number();
+  int64 dsn_2 = frame_entry->document_sequence_number();
+  EXPECT_NE(-1, isn_2);
+  EXPECT_NE(isn_1, isn_2);
+  EXPECT_EQ(dsn_1, dsn_2);
+
+  // Also test subframe sequence numbers, but only in --site-per-proces mode.
+  // (We do not create subframe FrameNavigationEntries in default mode yet.)
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSitePerProcess))
+    return;
+
+  // 3. Add a subframe, which does an AUTO_SUBFRAME navigation.
+  {
+    LoadCommittedCapturer capturer(shell()->web_contents());
+    std::string script = "var iframe = document.createElement('iframe');"
+                         "iframe.src = '" + 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());
+  }
+
+  // The root FrameNavigationEntry hasn't changed.
+  EXPECT_EQ(frame_entry,
+            controller.GetLastCommittedEntry()->GetFrameEntry(root));
+
+  // We should have a unique ISN and DSN for the subframe entry.
+  FrameTreeNode* subframe = root->child_at(0);
+  FrameNavigationEntry* subframe_entry =
+      controller.GetLastCommittedEntry()->GetFrameEntry(subframe);
+  int64 isn_3 = subframe_entry->item_sequence_number();
+  int64 dsn_3 = subframe_entry->document_sequence_number();
+  EXPECT_NE(-1, isn_2);
+  EXPECT_NE(isn_2, isn_3);
+  EXPECT_NE(dsn_2, dsn_3);
+
+  // 4. Do an in-page fragment navigation in the subframe.
+  EXPECT_TRUE(content::ExecuteScript(subframe->current_frame_host(), script));
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+
+  subframe_entry = controller.GetLastCommittedEntry()->GetFrameEntry(subframe);
+  int64 isn_4 = subframe_entry->item_sequence_number();
+  int64 dsn_4 = subframe_entry->document_sequence_number();
+  EXPECT_NE(-1, isn_4);
+  EXPECT_NE(isn_3, isn_4);
+  EXPECT_EQ(dsn_3, dsn_4);
+}
+
 namespace {
 
 class HttpThrottle : public ResourceThrottle {
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 1d855cf7..139ff960 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -5008,4 +5008,66 @@
   }
 }
 
+// Tests that if a stale navigation comes back from the renderer, it is properly
+// resurrected.
+TEST_F(NavigationControllerTest, StaleNavigationsResurrected) {
+  NavigationControllerImpl& controller = controller_impl();
+  TestNotificationTracker notifications;
+  RegisterForAllNavNotifications(&notifications, &controller);
+
+  // Start on page A.
+  const GURL url_a("http://foo.com/a");
+  main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url_a);
+  EXPECT_EQ(1U, navigation_entry_committed_counter_);
+  navigation_entry_committed_counter_ = 0;
+  EXPECT_EQ(1, controller.GetEntryCount());
+  EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+
+  // Go to page B.
+  const GURL url_b("http://foo.com/b");
+  main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_b);
+  EXPECT_EQ(1U, navigation_entry_committed_counter_);
+  navigation_entry_committed_counter_ = 0;
+  EXPECT_EQ(2, controller.GetEntryCount());
+  EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+  int b_entry_id = controller.GetLastCommittedEntry()->GetUniqueID();
+  int b_page_id = controller.GetLastCommittedEntry()->GetPageID();
+
+  // Back to page A.
+  controller.GoBack();
+  contents()->CommitPendingNavigation();
+  EXPECT_EQ(1U, navigation_entry_committed_counter_);
+  navigation_entry_committed_counter_ = 0;
+  EXPECT_EQ(2, controller.GetEntryCount());
+  EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+
+  // Start going forward to page B.
+  controller.GoForward();
+
+  // But the renderer unilaterally navigates to page C, pruning B.
+  const GURL url_c("http://foo.com/c");
+  main_test_rfh()->NavigateAndCommitRendererInitiated(2, true, url_c);
+  EXPECT_EQ(1U, navigation_entry_committed_counter_);
+  navigation_entry_committed_counter_ = 0;
+  EXPECT_EQ(2, controller.GetEntryCount());
+  EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+  int c_entry_id = controller.GetLastCommittedEntry()->GetUniqueID();
+  EXPECT_NE(c_entry_id, b_entry_id);
+
+  // And then the navigation to B gets committed.
+  main_test_rfh()->SendNavigate(b_page_id, b_entry_id, false, url_b);
+  EXPECT_EQ(1U, navigation_entry_committed_counter_);
+  navigation_entry_committed_counter_ = 0;
+
+  // Even though we were doing a history navigation, because the entry was
+  // pruned it will end up as a *new* entry at the end of the entry list. This
+  // means that occasionally a navigation conflict will end up with one entry
+  // bubbling to the end of the entry list, but that's the least-bad option.
+  EXPECT_EQ(3, controller.GetEntryCount());
+  EXPECT_EQ(2, controller.GetCurrentEntryIndex());
+  EXPECT_EQ(url_a, controller.GetEntryAtIndex(0)->GetURL());
+  EXPECT_EQ(url_c, controller.GetEntryAtIndex(1)->GetURL());
+  EXPECT_EQ(url_b, controller.GetEntryAtIndex(2)->GetURL());
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index d170f4d..dd9a97d 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -94,8 +94,8 @@
                                          const base::string16& title,
                                          ui::PageTransition transition_type,
                                          bool is_renderer_initiated)
-    : frame_tree_(
-          new TreeNode(new FrameNavigationEntry(-1, instance, url, referrer))),
+    : frame_tree_(new TreeNode(
+          new FrameNavigationEntry(-1, -1, -1, instance, url, referrer))),
       unique_id_(GetUniqueIDInConstructor()),
       bindings_(kInvalidBindings),
       page_type_(PAGE_TYPE_NORMAL),
@@ -504,6 +504,8 @@
 }
 
 void NavigationEntryImpl::AddOrUpdateFrameEntry(FrameTreeNode* frame_tree_node,
+                                                int64 item_sequence_number,
+                                                int64 document_sequence_number,
                                                 SiteInstanceImpl* site_instance,
                                                 const GURL& url,
                                                 const Referrer& referrer,
@@ -528,7 +530,9 @@
   for (TreeNode* child : parent_node->children) {
     if (child->frame_entry->frame_tree_node_id() == frame_tree_node_id) {
       // Update the existing FrameNavigationEntry (e.g., for replaceState).
-      child->frame_entry->UpdateEntry(site_instance, url, referrer, page_state);
+      child->frame_entry->UpdateEntry(item_sequence_number,
+                                      document_sequence_number, site_instance,
+                                      url, referrer, page_state);
       return;
     }
   }
@@ -539,14 +543,17 @@
   if (url == GURL(url::kAboutBlankURL))
     return;
   FrameNavigationEntry* frame_entry = new FrameNavigationEntry(
-      frame_tree_node_id, site_instance, url, referrer);
+      frame_tree_node_id, item_sequence_number, document_sequence_number,
+      site_instance, url, referrer);
   frame_entry->set_page_state(page_state);
   parent_node->children.push_back(
       new NavigationEntryImpl::TreeNode(frame_entry));
 }
 
-bool NavigationEntryImpl::HasFrameEntry(FrameTreeNode* frame_tree_node) const {
-  return FindFrameEntry(frame_tree_node) != nullptr;
+FrameNavigationEntry* NavigationEntryImpl::GetFrameEntry(
+    FrameTreeNode* frame_tree_node) const {
+  NavigationEntryImpl::TreeNode* tree_node = FindFrameEntry(frame_tree_node);
+  return tree_node ? tree_node->frame_entry.get() : nullptr;
 }
 
 void NavigationEntryImpl::SetScreenshotPNGData(
diff --git a/content/browser/frame_host/navigation_entry_impl.h b/content/browser/frame_host/navigation_entry_impl.h
index a5ce6fc..63fa5e0 100644
--- a/content/browser/frame_host/navigation_entry_impl.h
+++ b/content/browser/frame_host/navigation_entry_impl.h
@@ -172,14 +172,16 @@
   // Does nothing if there is no entry already and |url| is about:blank, since
   // that does not count as a real commit.
   void AddOrUpdateFrameEntry(FrameTreeNode* frame_tree_node,
+                             int64 item_sequence_number,
+                             int64 document_sequence_number,
                              SiteInstanceImpl* site_instance,
                              const GURL& url,
                              const Referrer& referrer,
                              const PageState& page_state);
 
-  // Returns whether this entry has a FrameNavigationEntry for the given
-  // |frame_tree_node|.
-  bool HasFrameEntry(FrameTreeNode* frame_tree_node) const;
+  // Returns the FrameNavigationEntry corresponding to |frame_tree_node|, if
+  // there is one in this NavigationEntry.
+  FrameNavigationEntry* GetFrameEntry(FrameTreeNode* frame_tree_node) const;
 
   void set_unique_id(int unique_id) {
     unique_id_ = unique_id;
diff --git a/content/browser/frame_host/render_frame_host_delegate.cc b/content/browser/frame_host/render_frame_host_delegate.cc
index 45b52b6..1c8ecd93 100644
--- a/content/browser/frame_host/render_frame_host_delegate.cc
+++ b/content/browser/frame_host/render_frame_host_delegate.cc
@@ -72,11 +72,6 @@
   return false;
 }
 
-int RenderFrameHostDelegate::EnsureOpenerRenderViewsExist(
-    RenderFrameHost* source_rfh) {
-  return MSG_ROUTING_NONE;
-}
-
 #if defined(OS_WIN)
 gfx::NativeViewAccessible
     RenderFrameHostDelegate::GetParentNativeViewAccessible() {
diff --git a/content/browser/frame_host/render_frame_host_delegate.h b/content/browser/frame_host/render_frame_host_delegate.h
index 09bb846..7de47f3c 100644
--- a/content/browser/frame_host/render_frame_host_delegate.h
+++ b/content/browser/frame_host/render_frame_host_delegate.h
@@ -153,18 +153,19 @@
       RenderFrameHost* target_rfh,
       SiteInstance* source_site_instance) const;
 
-  // Ensure that |source_rfh| has swapped-out RenderViews and proxies for
-  // itself and for each frame on its opener chain in the current frame's
-  // SiteInstance. Returns the routing ID of the swapped-out RenderView
-  // corresponding to |source_rfh|.
+  // Ensure that |source_rfh| has swapped-out RenderViews and
+  // RenderFrameProxies for itself and for all frames on its opener chain in
+  // the current frame's SiteInstance. Returns the routing ID of the
+  // swapped-out RenderView corresponding to |source_rfh|.
   //
-  // TODO(alexmos): This method will be removed once opener tracking and
-  // CreateOpenerRenderViews moves out of WebContents and into lower layers, as
-  // part of https://crbug.com/225940.  Currently, this method temporarily
-  // supports cross-process postMessage in non-site-per-process mode, where we
-  // need to create any missing proxies for the message's source frame and its
-  // opener chain on demand.
-  virtual int EnsureOpenerRenderViewsExist(RenderFrameHost* source_rfh);
+  // TODO(alexmos): This method currently supports cross-process postMessage,
+  // where we may need to create any missing proxies for the message's source
+  // frame and its opener chain. It currently exists in WebContents due to a
+  // special case for <webview> guests, but this logic should eventually be
+  // moved down into RenderFrameProxyHost::RouteMessageEvent when <webview>
+  // refactoring for --site-per-process mode is further along.  See
+  // https://crbug.com/330264.
+  virtual void EnsureOpenerProxiesExist(RenderFrameHost* source_rfh) {}
 
 #if defined(OS_WIN)
   // Returns the frame's parent's NativeViewAccessible.
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 74ac164..11ae65a 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -81,6 +81,10 @@
 #include "media/mojo/services/mojo_renderer_service.h"
 #endif
 
+#if defined(ENABLE_WEBVR)
+#include "content/browser/vr/vr_device_manager.h"
+#endif
+
 using base::TimeDelta;
 
 namespace content {
@@ -1579,6 +1583,16 @@
   GetServiceRegistry()->AddService<mojo::Shell>(base::Bind(
       &FrameMojoShell::BindRequest, base::Unretained(frame_mojo_shell_.get())));
 
+#if defined(ENABLE_WEBVR)
+  const base::CommandLine& browser_command_line =
+      *base::CommandLine::ForCurrentProcess();
+
+  if (browser_command_line.HasSwitch(switches::kEnableWebVR)) {
+    GetServiceRegistry()->AddService<VRService>(
+        base::Bind(&VRDeviceManager::BindRequest));
+  }
+#endif
+
   GetContentClient()->browser()->OverrideRenderFrameMojoServices(
       GetServiceRegistry(), this);
 }
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index d53f09b..6489f69 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -221,8 +221,8 @@
     dest_render_frame_host->SetUpMojoIfNeeded();
 
     // Recreate the opener chain.
-    int opener_route_id = delegate_->CreateOpenerRenderViewsForRenderManager(
-        dest_render_frame_host->GetSiteInstance());
+    int opener_route_id =
+        CreateOpenerProxiesIfNeeded(dest_render_frame_host->GetSiteInstance());
     if (!InitRenderView(dest_render_frame_host->render_view_host(),
                         opener_route_id,
                         MSG_ROUTING_NONE,
@@ -809,8 +809,8 @@
   // created or has crashed), initialize it.
   if (!navigation_rfh->IsRenderFrameLive()) {
     // Recreate the opener chain.
-    int opener_route_id = delegate_->CreateOpenerRenderViewsForRenderManager(
-        navigation_rfh->GetSiteInstance());
+    int opener_route_id =
+        CreateOpenerProxiesIfNeeded(navigation_rfh->GetSiteInstance());
     if (!InitRenderView(navigation_rfh->render_view_host(), opener_route_id,
                         MSG_ROUTING_NONE, frame_tree_node_->IsMainFrame())) {
       return nullptr;
@@ -1347,7 +1347,7 @@
   if (!new_instance->GetProcess()->Init())
     return;
 
-  int opener_route_id = CreateOpenerRenderViewsIfNeeded(
+  int opener_route_id = CreateProxiesForNewRenderFrameHost(
       old_instance, new_instance, &create_render_frame_flags);
 
   // Create a non-swapped-out RFH with the given opener.
@@ -1356,16 +1356,14 @@
                         create_render_frame_flags, nullptr);
 }
 
-int RenderFrameHostManager::CreateOpenerRenderViewsIfNeeded(
+int RenderFrameHostManager::CreateProxiesForNewRenderFrameHost(
     SiteInstance* old_instance,
     SiteInstance* new_instance,
     int* create_render_frame_flags) {
   int opener_route_id = MSG_ROUTING_NONE;
   // Only create opener proxies if they are in the same BrowsingInstance.
-  if (new_instance->IsRelatedSiteInstance(old_instance)) {
-    opener_route_id =
-        delegate_->CreateOpenerRenderViewsForRenderManager(new_instance);
-  }
+  if (new_instance->IsRelatedSiteInstance(old_instance))
+    opener_route_id = CreateOpenerProxiesIfNeeded(new_instance);
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kSitePerProcess)) {
     // Ensure that the frame tree has RenderFrameProxyHosts for the new
@@ -1441,9 +1439,8 @@
     return false;
 
   int create_render_frame_flags = 0;
-  int opener_route_id =
-      CreateOpenerRenderViewsIfNeeded(old_instance, new_instance,
-                                      &create_render_frame_flags);
+  int opener_route_id = CreateProxiesForNewRenderFrameHost(
+      old_instance, new_instance, &create_render_frame_flags);
 
   if (frame_tree_node_->IsMainFrame())
     create_render_frame_flags |= CREATE_RF_FOR_MAIN_FRAME_NAVIGATION;
@@ -1631,7 +1628,6 @@
 }
 
 void RenderFrameHostManager::EnsureRenderViewInitialized(
-    FrameTreeNode* source,
     RenderViewHostImpl* render_view_host,
     SiteInstance* instance) {
   DCHECK(frame_tree_node_->IsMainFrame());
@@ -1640,11 +1636,10 @@
     return;
 
   // Recreate the opener chain.
-  int opener_route_id =
-      delegate_->CreateOpenerRenderViewsForRenderManager(instance);
+  int opener_route_id = CreateOpenerProxiesIfNeeded(instance);
   RenderFrameProxyHost* proxy = GetRenderFrameProxyHost(instance);
   InitRenderView(render_view_host, opener_route_id, proxy->GetRoutingID(),
-                 source->IsMainFrame());
+                 false);
   proxy->set_render_frame_proxy_created(true);
 }
 
@@ -2190,4 +2185,59 @@
   }
 }
 
+int RenderFrameHostManager::CreateOpenerProxiesIfNeeded(
+    SiteInstance* instance) {
+  FrameTreeNode* opener = frame_tree_node_->opener();
+  if (!opener)
+    return MSG_ROUTING_NONE;
+
+  // Create proxies for the opener chain.
+  return opener->render_manager()->CreateOpenerProxies(instance);
+}
+
+int RenderFrameHostManager::CreateOpenerProxies(SiteInstance* instance) {
+  int opener_route_id = MSG_ROUTING_NONE;
+
+  // If this tab has an opener, recursively create proxies for the nodes on its
+  // frame tree.
+  // TODO(alexmos): Once we allow frame openers to be updated (which can happen
+  // via window.open(url, "target_frame")), this will need to be resilient to
+  // cycles. It will also need to handle tabs that have multiple openers (e.g.,
+  // main frame and subframe could have different openers, each of which must
+  // be traversed).
+  FrameTreeNode* opener = frame_tree_node_->opener();
+  if (opener)
+    opener_route_id = opener->render_manager()->CreateOpenerProxies(instance);
+
+  // If any of the RenderViews (current, pending, or swapped out) for this
+  // FrameTree has the same SiteInstance, use it.  If such a RenderView exists,
+  // that implies that proxies for all nodes in the tree should also exist
+  // (when in site-per-process mode), so it is safe to exit early.
+  FrameTree* frame_tree = frame_tree_node_->frame_tree();
+  RenderViewHostImpl* rvh = frame_tree->GetRenderViewHost(instance);
+  if (rvh)
+    return rvh->GetRoutingID();
+
+  int render_view_routing_id = MSG_ROUTING_NONE;
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSitePerProcess)) {
+    // Ensure that all the nodes in the opener's frame tree have
+    // RenderFrameProxyHosts for the new SiteInstance.
+    frame_tree->CreateProxiesForSiteInstance(nullptr, instance);
+    rvh = frame_tree->GetRenderViewHost(instance);
+    render_view_routing_id = rvh->GetRoutingID();
+  } else {
+    // Create a swapped out RenderView in the given SiteInstance if none exists,
+    // setting its opener to the given route_id.  Since the opener can point to
+    // a subframe, do this on the root frame of the opener's frame tree.
+    // Return the new view's route_id.
+    frame_tree->root()->render_manager()->
+        CreateRenderFrame(instance, nullptr, opener_route_id,
+                          CREATE_RF_FOR_MAIN_FRAME_NAVIGATION |
+                          CREATE_RF_SWAPPED_OUT | CREATE_RF_HIDDEN,
+                          &render_view_routing_id);
+  }
+  return render_view_routing_id;
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_manager.h b/content/browser/frame_host/render_frame_host_manager.h
index 86cc07e..fd65394 100644
--- a/content/browser/frame_host/render_frame_host_manager.h
+++ b/content/browser/frame_host/render_frame_host_manager.h
@@ -147,13 +147,6 @@
     virtual NavigationControllerImpl&
         GetControllerForRenderManager() = 0;
 
-    // Create swapped out RenderViews in the given SiteInstance for each tab in
-    // the opener chain of this tab, if any.  This allows the current tab to
-    // make cross-process script calls to its opener(s).  Returns the route ID
-    // of the immediate opener, if one exists (otherwise MSG_ROUTING_NONE).
-    virtual int CreateOpenerRenderViewsForRenderManager(
-        SiteInstance* instance) = 0;
-
     // Creates a WebUI object for the given URL if one applies. Ownership of the
     // returned pointer will be passed to the caller. If no WebUI applies,
     // returns NULL.
@@ -419,10 +412,18 @@
   // origin.
   void OnDidUpdateOrigin(const url::Origin& origin);
 
-  void EnsureRenderViewInitialized(FrameTreeNode* source,
-                                   RenderViewHostImpl* render_view_host,
+  void EnsureRenderViewInitialized(RenderViewHostImpl* render_view_host,
                                    SiteInstance* instance);
 
+  // Recursively creates swapped out RenderViews and RenderFrameProxies for
+  // this frame's FrameTree and for its opener chain in the given SiteInstance.
+  // This allows other tabs to send cross-process JavaScript calls to their
+  // opener(s) and to any other frames in the opener's FrameTree (e.g.,
+  // supporting calls like window.opener.opener.frames[x][y]). Returns the
+  // route ID of this frame's RenderView for |instance|.
+  // TODO(alexmos): Switch this to return RenderFrame routing IDs.
+  int CreateOpenerProxies(SiteInstance* instance);
+
  private:
   friend class FrameTreeVisualizer;
   friend class NavigatorTestWithBrowserSideNavigation;
@@ -549,14 +550,25 @@
                                     SiteInstance* new_instance,
                                     bool is_main_frame);
 
-  // Ensure that we have created RFHs for the new RFH's opener chain if
-  // we are staying in the same BrowsingInstance. This allows the new RFH
-  // to send cross-process script calls to its opener(s). Returns the opener
-  // route ID to be used for the new RenderView to be created.
-  // |create_render_frame_flags| allows the method to set additional flags.
-  int CreateOpenerRenderViewsIfNeeded(SiteInstance* old_instance,
-                                      SiteInstance* new_instance,
-                                      int* create_render_frame_flags);
+  // Ensure that we have created all needed proxies for a new RFH with
+  // SiteInstance |new_instance|: (1) create swapped-out RVHs and proxies for
+  // the new RFH's opener chain if we are staying in the same BrowsingInstance;
+  // (2) Create proxies for the new RFH's SiteInstance in its own frame tree;
+  // (3) set any additional flags for the new RenderFrame with
+  // |create_render_frame_flags|.
+  // Returns the opener's RVH route ID to be used for the new RenderFrame.
+  // TODO(alexmos): switch this to return opener's RFH routing ID instead.
+  int CreateProxiesForNewRenderFrameHost(SiteInstance* old_instance,
+                                         SiteInstance* new_instance,
+                                         int* create_render_frame_flags);
+
+  // Create swapped out RenderViews and RenderFrameProxies in the given
+  // SiteInstance for all frames on the opener chain of this frame.  Same as
+  // CreateOpenerProxies, but starts from this frame's opener, returning
+  // MSG_ROUTING_NONE if it doesn't exist, and calling CreateOpenerProxies if
+  // it does.
+  // TODO(alexmos): Switch this to return RenderFrame routing IDs.
+  int CreateOpenerProxiesIfNeeded(SiteInstance* instance);
 
   // Creates a RenderFrameHost and corresponding RenderViewHost if necessary.
   scoped_ptr<RenderFrameHostImpl> CreateRenderFrameHost(SiteInstance* instance,
diff --git a/content/browser/frame_host/render_frame_proxy_host.cc b/content/browser/frame_host/render_frame_proxy_host.cc
index 0a27c9e..f4b265e 100644
--- a/content/browser/frame_host/render_frame_proxy_host.cc
+++ b/content/browser/frame_host/render_frame_proxy_host.cc
@@ -230,32 +230,23 @@
     if (!source_rfh) {
       new_params.source_routing_id = MSG_ROUTING_NONE;
     } else {
-      // Ensure that we have a swapped-out RVH and proxy for the source frame.
-      // If it doesn't exist, create it on demand and also create its opener
-      // chain, since those will also be accessible to the target page.
-      //
-      // TODO(alexmos): This currently only works for top-level frames and
-      // won't create the right proxy if the message source is a subframe on a
-      // cross-process tab.  This will be cleaned up as part of moving opener
-      // tracking to FrameTreeNode (https://crbug.com/225940). For now, if the
-      // message is sent from a subframe on a cross-process tab, set the source
-      // routing ID to the main frame of the source tab, which matches legacy
-      // postMessage behavior prior to --site-per-process.
-      int source_view_routing_id =
-          target_rfh->delegate()->EnsureOpenerRenderViewsExist(source_rfh);
+      // Ensure that we have a swapped-out RVH and proxy for the source frame
+      // in the target SiteInstance. If it doesn't exist, create it on demand
+      // and also create its opener chain, since that will also be accessible
+      // to the target page.
+      target_rfh->delegate()->EnsureOpenerProxiesExist(source_rfh);
 
+      // If the message source is a cross-process subframe, its proxy will only
+      // be created in --site-per-process mode.  If the proxy wasn't created,
+      // set the source routing ID to MSG_ROUTING_NONE (see
+      // https://crbug.com/485520 for discussion on why this is ok).
       RenderFrameProxyHost* source_proxy_in_target_site_instance =
           source_rfh->frame_tree_node()
               ->render_manager()
-              ->GetRenderFrameProxyHost(target_rfh->GetSiteInstance());
+              ->GetRenderFrameProxyHost(target_site_instance);
       if (source_proxy_in_target_site_instance) {
         new_params.source_routing_id =
             source_proxy_in_target_site_instance->GetRoutingID();
-      } else if (source_view_routing_id != MSG_ROUTING_NONE) {
-        RenderViewHostImpl* source_rvh = RenderViewHostImpl::FromID(
-            target_rfh->GetProcess()->GetID(), source_view_routing_id);
-        CHECK(source_rvh);
-        new_params.source_routing_id = source_rvh->main_frame_routing_id();
       } else {
         new_params.source_routing_id = MSG_ROUTING_NONE;
       }
diff --git a/content/browser/indexed_db/leveldb/leveldb_database.cc b/content/browser/indexed_db/leveldb/leveldb_database.cc
index 459a16e..2c18f1f 100644
--- a/content/browser/indexed_db/leveldb/leveldb_database.cc
+++ b/content/browser/indexed_db/leveldb/leveldb_database.cc
@@ -86,11 +86,20 @@
 
 LevelDBDatabase::~LevelDBDatabase() {
   // db_'s destructor uses comparator_adapter_; order of deletion is important.
-  db_.reset();
+  CloseDatabase();
   comparator_adapter_.reset();
   env_.reset();
 }
 
+void LevelDBDatabase::CloseDatabase() {
+  if (db_) {
+    base::TimeTicks begin_time = base::TimeTicks::Now();
+    db_.reset();
+    UMA_HISTOGRAM_MEDIUM_TIMES("WebCore.IndexedDB.LevelDB.CloseTime",
+                               base::TimeTicks::Now() - begin_time);
+  }
+}
+
 static leveldb::Status OpenDB(
     leveldb::Comparator* comparator,
     leveldb::Env* env,
diff --git a/content/browser/indexed_db/leveldb/leveldb_database.h b/content/browser/indexed_db/leveldb/leveldb_database.h
index ddc6aa5c..60a38ec 100644
--- a/content/browser/indexed_db/leveldb/leveldb_database.h
+++ b/content/browser/indexed_db/leveldb/leveldb_database.h
@@ -101,6 +101,8 @@
  private:
   friend class LevelDBSnapshot;
 
+  void CloseDatabase();
+
   scoped_ptr<leveldb::Env> env_;
   scoped_ptr<leveldb::Comparator> comparator_adapter_;
   scoped_ptr<leveldb::DB> db_;
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index a76e81e..397d132 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -820,10 +820,10 @@
 
 void ResourceDispatcherHostImpl::DidReceiveResponse(ResourceLoader* loader) {
   ResourceRequestInfoImpl* info = loader->GetRequestInfo();
-
-  if (loader->request()->was_fetched_via_proxy() &&
-      loader->request()->was_fetched_via_spdy() &&
-      loader->request()->url().SchemeIs(url::kHttpScheme)) {
+  net::URLRequest* request = loader->request();
+  if (request->was_fetched_via_proxy() &&
+      request->was_fetched_via_spdy() &&
+      request->url().SchemeIs(url::kHttpScheme)) {
     scheduler_->OnReceivedSpdyProxiedHttpResponse(
         info->GetChildID(), info->GetRouteID());
   }
@@ -834,8 +834,7 @@
 
   // Notify the observers on the UI thread.
   scoped_ptr<ResourceRequestDetails> detail(new ResourceRequestDetails(
-      loader->request(),
-      GetCertID(loader->request(), info->GetChildID())));
+      request, GetCertID(request, info->GetChildID())));
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
       base::Bind(
diff --git a/content/browser/loader/upload_data_stream_builder.cc b/content/browser/loader/upload_data_stream_builder.cc
index c497c16..cab3a10 100644
--- a/content/browser/loader/upload_data_stream_builder.cc
+++ b/content/browser/loader/upload_data_stream_builder.cc
@@ -4,16 +4,25 @@
 
 #include "content/browser/loader/upload_data_stream_builder.h"
 
+#include <utility>
+#include <vector>
+
 #include "base/logging.h"
+#include "base/memory/ref_counted.h"
 #include "content/browser/fileapi/upload_file_system_file_element_reader.h"
 #include "content/common/resource_request_body.h"
 #include "net/base/elements_upload_data_stream.h"
 #include "net/base/upload_bytes_element_reader.h"
+#include "net/base/upload_disk_cache_entry_element_reader.h"
 #include "net/base/upload_file_element_reader.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_data_snapshot.h"
 #include "storage/browser/blob/blob_storage_context.h"
 
+namespace disk_cache {
+class Entry;
+}
+
 namespace content {
 namespace {
 
@@ -60,11 +69,37 @@
   DISALLOW_COPY_AND_ASSIGN(FileElementReader);
 };
 
+// This owns the provided ResourceRequestBody. This is necessary to ensure the
+// BlobData and open disk cache entries survive until upload completion.
+class DiskCacheElementReader : public net::UploadDiskCacheEntryElementReader {
+ public:
+  DiskCacheElementReader(ResourceRequestBody* resource_request_body,
+                         disk_cache::Entry* disk_cache_entry,
+                         int disk_cache_stream_index,
+                         const ResourceRequestBody::Element& element)
+      : net::UploadDiskCacheEntryElementReader(disk_cache_entry,
+                                               disk_cache_stream_index,
+                                               element.offset(),
+                                               element.length()),
+        resource_request_body_(resource_request_body) {
+    DCHECK_EQ(ResourceRequestBody::Element::TYPE_DISK_CACHE_ENTRY,
+              element.type());
+  }
+
+  ~DiskCacheElementReader() override {}
+
+ private:
+  scoped_refptr<ResourceRequestBody> resource_request_body_;
+
+  DISALLOW_COPY_AND_ASSIGN(DiskCacheElementReader);
+};
+
 void ResolveBlobReference(
     ResourceRequestBody* body,
     storage::BlobStorageContext* blob_context,
     const ResourceRequestBody::Element& element,
-    std::vector<const ResourceRequestBody::Element*>* resolved_elements) {
+    std::vector<std::pair<const ResourceRequestBody::Element*,
+                          const storage::BlobDataItem*>>* resolved_elements) {
   DCHECK(blob_context);
   scoped_ptr<storage::BlobDataHandle> handle =
       blob_context->GetBlobDataFromUUID(element.blob_uuid());
@@ -84,7 +119,8 @@
   // Append the elements in the referenced blob data.
   for (const auto& item : snapshot->items()) {
     DCHECK_NE(storage::DataElement::TYPE_BLOB, item->type());
-    resolved_elements->push_back(item->data_element_ptr());
+    resolved_elements->push_back(
+        std::make_pair(item->data_element_ptr(), item.get()));
   }
   const void* key = snapshot.get();
   body->SetUserData(key, snapshot.release());
@@ -98,18 +134,24 @@
     storage::FileSystemContext* file_system_context,
     base::TaskRunner* file_task_runner) {
   // Resolve all blob elements.
-  std::vector<const ResourceRequestBody::Element*> resolved_elements;
+  std::vector<std::pair<const ResourceRequestBody::Element*,
+                        const storage::BlobDataItem*>> resolved_elements;
   for (size_t i = 0; i < body->elements()->size(); ++i) {
     const ResourceRequestBody::Element& element = (*body->elements())[i];
-    if (element.type() == ResourceRequestBody::Element::TYPE_BLOB)
+    if (element.type() == ResourceRequestBody::Element::TYPE_BLOB) {
       ResolveBlobReference(body, blob_context, element, &resolved_elements);
-    else
-      resolved_elements.push_back(&element);
+    } else if (element.type() !=
+               ResourceRequestBody::Element::TYPE_DISK_CACHE_ENTRY) {
+      resolved_elements.push_back(std::make_pair(&element, nullptr));
+    } else {
+      NOTREACHED();
+    }
   }
 
   ScopedVector<net::UploadElementReader> element_readers;
-  for (size_t i = 0; i < resolved_elements.size(); ++i) {
-    const ResourceRequestBody::Element& element = *resolved_elements[i];
+  for (const auto& element_and_blob_item_pair : resolved_elements) {
+    const ResourceRequestBody::Element& element =
+        *element_and_blob_item_pair.first;
     switch (element.type()) {
       case ResourceRequestBody::Element::TYPE_BYTES:
         element_readers.push_back(new BytesElementReader(body, element));
@@ -135,6 +177,17 @@
         // TODO(dmurph): Create blob reader and store the snapshot in there.
         NOTREACHED();
         break;
+      case ResourceRequestBody::Element::TYPE_DISK_CACHE_ENTRY: {
+        // TODO(gavinp): If Build() is called with a DataElement of
+        // TYPE_DISK_CACHE_ENTRY then this code won't work because we won't call
+        // ResolveBlobReference() and so we won't find |item|. Is this OK?
+        const storage::BlobDataItem* item = element_and_blob_item_pair.second;
+        element_readers.push_back(
+            new DiskCacheElementReader(body, item->disk_cache_entry(),
+                                       item->disk_cache_stream_index(),
+                                       element));
+        break;
+      }
       case ResourceRequestBody::Element::TYPE_UNKNOWN:
         NOTREACHED();
         break;
diff --git a/content/browser/loader/upload_data_stream_builder_unittest.cc b/content/browser/loader/upload_data_stream_builder_unittest.cc
index 174a89b..b3edd5d 100644
--- a/content/browser/loader/upload_data_stream_builder_unittest.cc
+++ b/content/browser/loader/upload_data_stream_builder_unittest.cc
@@ -18,7 +18,9 @@
 #include "net/base/test_completion_callback.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/base/upload_data_stream.h"
+#include "net/base/upload_disk_cache_entry_element_reader.h"
 #include "net/base/upload_file_element_reader.h"
+#include "net/disk_cache/disk_cache.h"
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_storage_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -31,6 +33,45 @@
 namespace content {
 namespace {
 
+const int kTestDiskCacheStreamIndex = 0;
+
+// Our disk cache tests don't need a real data handle since the tests themselves
+// scope the disk cache and entries.
+class EmptyDataHandle : public storage::BlobDataBuilder::DataHandle {
+ private:
+  ~EmptyDataHandle() override {}
+};
+
+scoped_ptr<disk_cache::Backend> CreateInMemoryDiskCache() {
+  scoped_ptr<disk_cache::Backend> cache;
+  net::TestCompletionCallback callback;
+  int rv = disk_cache::CreateCacheBackend(net::MEMORY_CACHE,
+                                          net::CACHE_BACKEND_DEFAULT,
+                                          base::FilePath(), 0,
+                                          false, nullptr, nullptr, &cache,
+                                          callback.callback());
+  EXPECT_EQ(net::OK, callback.GetResult(rv));
+
+  return cache.Pass();
+}
+
+disk_cache::ScopedEntryPtr CreateDiskCacheEntry(disk_cache::Backend* cache,
+                                                const char* key,
+                                                const std::string& data) {
+  disk_cache::Entry* temp_entry = nullptr;
+  net::TestCompletionCallback callback;
+  int rv = cache->CreateEntry(key, &temp_entry, callback.callback());
+  if (callback.GetResult(rv) != net::OK)
+    return nullptr;
+  disk_cache::ScopedEntryPtr entry(temp_entry);
+
+  scoped_refptr<net::StringIOBuffer> iobuffer = new net::StringIOBuffer(data);
+  rv = entry->WriteData(kTestDiskCacheStreamIndex, 0, iobuffer.get(),
+                        iobuffer->size(), callback.callback(), false);
+  EXPECT_EQ(static_cast<int>(data.size()), callback.GetResult(rv));
+  return entry.Pass();
+}
+
 bool AreElementsEqual(const net::UploadElementReader& reader,
                       const ResourceRequestBody::Element& element) {
   switch(element.type()) {
@@ -52,6 +93,19 @@
           element.expected_modification_time();
       break;
     }
+    case ResourceRequestBody::Element::TYPE_DISK_CACHE_ENTRY: {
+      // TODO(gavinp): Should we be comparing a higher level structure
+      // such as the BlobDataItem so that we can do stronger equality
+      // comparisons?
+      const net::UploadDiskCacheEntryElementReader* disk_cache_entry_reader =
+          reader.AsDiskCacheEntryReaderForTests();
+      return disk_cache_entry_reader &&
+          disk_cache_entry_reader->range_offset_for_tests() ==
+              static_cast<int>(element.offset()) &&
+          disk_cache_entry_reader->range_length_for_tests() ==
+              static_cast<int>(element.length());
+      break;
+    }
     default:
       NOTREACHED();
   }
@@ -124,11 +178,27 @@
     scoped_ptr<BlobDataHandle> handle2 =
         blob_storage_context.AddFinishedBlob(blob_data_builder.get());
 
+    const std::string blob_id2("id-2");
+    const std::string kDiskCacheData = "DiskCacheData";
+    scoped_ptr<disk_cache::Backend> disk_cache_backend =
+        CreateInMemoryDiskCache();
+    ASSERT_TRUE(disk_cache_backend);
+    disk_cache::ScopedEntryPtr disk_cache_entry =
+        CreateDiskCacheEntry(disk_cache_backend.get(), "a key", kDiskCacheData);
+    ASSERT_TRUE(disk_cache_entry);
+    blob_data_builder.reset(new BlobDataBuilder(blob_id2));
+    blob_data_builder->AppendDiskCacheEntry(
+        new EmptyDataHandle(), disk_cache_entry.get(),
+        kTestDiskCacheStreamIndex);
+    scoped_ptr<BlobDataHandle> handle3 =
+        blob_storage_context.AddFinishedBlob(blob_data_builder.get());
+
     // Setup upload data elements for comparison.
-    ResourceRequestBody::Element blob_element1, blob_element2;
+    ResourceRequestBody::Element blob_element1, blob_element2, blob_element3;
     blob_element1.SetToBytes(kBlobData.c_str(), kBlobData.size());
     blob_element2.SetToFilePathRange(
         base::FilePath(FILE_PATH_LITERAL("BlobFile.txt")), 0, 20, time1);
+    blob_element3.SetToDiskCacheEntryRange(0, kDiskCacheData.size());
 
     ResourceRequestBody::Element upload_element1, upload_element2;
     upload_element1.SetToBytes("Hello", 5);
@@ -181,6 +251,18 @@
     EXPECT_TRUE(AreElementsEqual(
         *(*upload->GetElementReaders())[1], blob_element2));
 
+    // Test having one blob reference which refers to a disk cache entry.
+    request_body = new ResourceRequestBody();
+    request_body->AppendBlob(blob_id2);
+
+    upload = UploadDataStreamBuilder::Build(
+        request_body.get(), &blob_storage_context, nullptr,
+        base::ThreadTaskRunnerHandle::Get().get());
+    ASSERT_TRUE(upload->GetElementReaders());
+    ASSERT_EQ(1U, upload->GetElementReaders()->size());
+    EXPECT_TRUE(AreElementsEqual(
+        *(*upload->GetElementReaders())[0], blob_element3));
+
     // Test having one blob reference at the beginning.
     request_body = new ResourceRequestBody();
     request_body->AppendBlob(blob_id1);
diff --git a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
index f64685c..ac97158 100644
--- a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
@@ -415,13 +415,12 @@
     int id() const override { return id_; }
     size_t size() const override { return buffer_handle_->size(); }
     void* data() override { return buffer_handle_->data(); }
-    gfx::GpuMemoryBufferType GetType() override {
-      return gfx::SHARED_MEMORY_BUFFER;
-    }
     ClientBuffer AsClientBuffer() override { return nullptr; }
-    base::PlatformFile AsPlatformFile() override {
-      return base::PlatformFile();
+#if defined(OS_POSIX)
+    base::FileDescriptor AsPlatformFile() override {
+      return base::FileDescriptor();
     }
+#endif
 
    private:
     ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); }
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 41685db1..c3509ae 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -500,6 +500,10 @@
     CreateLayerTreeHost();
     ui_resource_provider_.SetLayerTreeHost(host_.get());
   }
+  // TODO(jdduke): Consider using View.onWindowVisibilityChange() to drive
+  // WindowAndroid visibility updates. CompositorImpl::SetVisible changes can
+  // occur while the visibile surface is temporarily changing, e.g., when
+  // transitioning to fullscreen video. See crbug.com/471262.
   root_window_->OnVisibilityChanged(visible);
 }
 
diff --git a/content/browser/renderer_host/input/touch_action_browsertest.cc b/content/browser/renderer_host/input/touch_action_browsertest.cc
index 38718b3f..232d0df 100644
--- a/content/browser/renderer_host/input/touch_action_browsertest.cc
+++ b/content/browser/renderer_host/input/touch_action_browsertest.cc
@@ -89,11 +89,19 @@
     NavigateToURL(shell(), data_url);
 
     RenderWidgetHostImpl* host = GetWidgetHost();
+    scoped_refptr<FrameWatcher> frame_watcher(new FrameWatcher());
+    host->GetProcess()->AddFilter(frame_watcher.get());
     host->GetView()->SetSize(gfx::Size(400, 400));
 
     base::string16 ready_title(base::ASCIIToUTF16("ready"));
     TitleWatcher watcher(shell()->web_contents(), ready_title);
     ignore_result(watcher.WaitAndGetTitle());
+
+    // We need to wait until at least one frame has been composited
+    // otherwise the injection of the synthetic gestures may get
+    // dropped because of MainThread/Impl thread sync of touch event
+    // regions.
+    frame_watcher->WaitFrames(1);
   }
 
   // ContentBrowserTest:
@@ -115,7 +123,7 @@
   }
 
   int GetScrollTop() {
-    return ExecuteScriptAndExtractInt("document.documentElement.scrollTop");
+    return ExecuteScriptAndExtractInt("document.scrollingElement.scrollTop");
   }
 
   // Generate touch events for a synthetic scroll from |point| for |distance|.
@@ -161,24 +169,25 @@
   DISALLOW_COPY_AND_ASSIGN(TouchActionBrowserTest);
 };
 
-// TouchActionBrowserTest.DefaultAuto fails under ThreadSanitizer v2, see
-// http://crbug.com/348539 and is flaky on XP, see
-// http://crbug.com/354763
-//
 // Mac doesn't yet have a gesture recognizer, so can't support turning touch
 // events into scroll gestures.
 // Will be fixed with http://crbug.com/337142
 //
 // Verify the test infrastructure works - we can touch-scroll the page and get a
 // touchcancel as expected.
-IN_PROC_BROWSER_TEST_F(TouchActionBrowserTest, DISABLED_DefaultAuto) {
+#if defined(OS_MACOSX)
+#define MAYBE_DefaultAuto DISABLED_DefaultAuto
+#else
+#define MAYBE_DefaultAuto DefaultAuto
+#endif
+IN_PROC_BROWSER_TEST_F(TouchActionBrowserTest, MAYBE_DefaultAuto) {
   LoadURL();
 
   bool scrolled = DoTouchScroll(gfx::Point(50, 50), gfx::Vector2d(0, 45));
   EXPECT_TRUE(scrolled);
 
   EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchstart"));
-  EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchmove"));
+  EXPECT_GE(ExecuteScriptAndExtractInt("eventCounts.touchmove"), 1);
   EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchend"));
   EXPECT_EQ(0, ExecuteScriptAndExtractInt("eventCounts.touchcancel"));
 }
@@ -186,15 +195,19 @@
 // Verify that touching a touch-action: none region disables scrolling and
 // enables all touch events to be sent.
 // Disabled on MacOS because it doesn't support touch input.
-// It's just flaky everywhere.
-IN_PROC_BROWSER_TEST_F(TouchActionBrowserTest, DISABLED_TouchActionNone) {
+#if defined(OS_MACOSX)
+#define MAYBE_TouchActionNone DISABLED_TouchActionNone
+#else
+#define MAYBE_TouchActionNone TouchActionNone
+#endif
+IN_PROC_BROWSER_TEST_F(TouchActionBrowserTest, MAYBE_TouchActionNone) {
   LoadURL();
 
   bool scrolled = DoTouchScroll(gfx::Point(50, 150), gfx::Vector2d(0, 45));
   EXPECT_FALSE(scrolled);
 
   EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchstart"));
-  EXPECT_GT(ExecuteScriptAndExtractInt("eventCounts.touchmove"), 1);
+  EXPECT_GE(ExecuteScriptAndExtractInt("eventCounts.touchmove"), 1);
   EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchend"));
   EXPECT_EQ(0, ExecuteScriptAndExtractInt("eventCounts.touchcancel"));
 }
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool.cc b/content/browser/renderer_host/media/video_capture_buffer_pool.cc
index eeeafc6..70195ba6 100644
--- a/content/browser/renderer_host/media/video_capture_buffer_pool.cc
+++ b/content/browser/renderer_host/media/video_capture_buffer_pool.cc
@@ -67,17 +67,12 @@
 
   size_t size() const override { return size_; }
   void* data() override { return data_; }
-  gfx::GpuMemoryBufferType GetType() override {
-    return gfx::SHARED_MEMORY_BUFFER;
-  }
   ClientBuffer AsClientBuffer() override { return nullptr; }
-  base::PlatformFile AsPlatformFile() override {
 #if defined(OS_POSIX)
-    return handle_.fd;
-#elif defined(OS_WIN)
+  base::FileDescriptor AsPlatformFile() override {
     return handle_;
-#endif
   }
+#endif
 
  private:
   void* const data_;
@@ -104,18 +99,12 @@
 
   size_t size() const override { return size_; }
   void* data() override { return data_[0]; }
-  gfx::GpuMemoryBufferType GetType() override {
-    return gmb_->GetHandle().type;
-  }
   ClientBuffer AsClientBuffer() override { return gmb_->AsClientBuffer(); }
-  base::PlatformFile AsPlatformFile() override {
-    DCHECK_EQ(gmb_->GetHandle().type, gfx::SHARED_MEMORY_BUFFER);
 #if defined(OS_POSIX)
-    return gmb_->GetHandle().handle.fd;
-#elif defined(OS_WIN)
+  base::FileDescriptor AsPlatformFile() override {
     return gmb_->GetHandle().handle;
-#endif
   }
+#endif
 
  private:
   gfx::GpuMemoryBuffer* const gmb_;
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool.h b/content/browser/renderer_host/media/video_capture_buffer_pool.h
index 0214ddd..ddeb9b7 100644
--- a/content/browser/renderer_host/media/video_capture_buffer_pool.h
+++ b/content/browser/renderer_host/media/video_capture_buffer_pool.h
@@ -52,9 +52,10 @@
     virtual ~BufferHandle() {}
     virtual size_t size() const = 0;
     virtual void* data() = 0;
-    virtual gfx::GpuMemoryBufferType GetType() = 0;
     virtual ClientBuffer AsClientBuffer() = 0;
-    virtual base::PlatformFile AsPlatformFile() = 0;
+#if defined(OS_POSIX)
+    virtual base::FileDescriptor AsPlatformFile() = 0;
+#endif
   };
 
   explicit VideoCaptureBufferPool(int count);
diff --git a/content/browser/renderer_host/media/video_capture_device_client.cc b/content/browser/renderer_host/media/video_capture_device_client.cc
index 33030ab6..d1eacd4 100644
--- a/content/browser/renderer_host/media/video_capture_device_client.cc
+++ b/content/browser/renderer_host/media/video_capture_device_client.cc
@@ -118,15 +118,14 @@
   int id() const override { return id_; }
   size_t size() const override { return buffer_handle_->size(); }
   void* data() override { return buffer_handle_->data(); }
-  gfx::GpuMemoryBufferType GetType() override {
-    return buffer_handle_->GetType();
-  }
   ClientBuffer AsClientBuffer() override {
     return buffer_handle_->AsClientBuffer();
   }
-  base::PlatformFile AsPlatformFile() override {
+#if defined(OS_POSIX)
+  base::FileDescriptor AsPlatformFile() override {
     return buffer_handle_->AsPlatformFile();
   }
+#endif
 
  private:
   ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); }
@@ -579,10 +578,16 @@
   video_frame->metadata()->SetDouble(VideoFrameMetadata::FRAME_RATE,
                                      frame_format.frame_rate);
 #if defined(OS_LINUX)
-  if (buffer->GetType() == gfx::OZONE_NATIVE_BUFFER) {
-    video_frame->DuplicateFileDescriptors(
-        std::vector<int>(buffer->AsPlatformFile()));
-  }
+// TODO(mcasas): After http://crev.com/1179323002, use |frame_format| to query
+// the storage type of the buffer and use the appropriate |video_frame| method.
+#if defined(USE_OZONE)
+  DCHECK_EQ(media::VideoFrame::NumPlanes(video_frame->format()), 1u);
+  video_frame->DuplicateFileDescriptors(
+      std::vector<int>(1, buffer->AsPlatformFile().fd));
+#else
+   video_frame->AddSharedMemoryHandle(buffer->AsPlatformFile());
+#endif
+
 #endif
   //TODO(mcasas): use AddSharedMemoryHandle() for gfx::SHARED_MEMORY_BUFFER.
 
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc
index c581a7a..32d545cb 100644
--- a/content/browser/renderer_host/render_message_filter.cc
+++ b/content/browser/renderer_host/render_message_filter.cc
@@ -332,7 +332,8 @@
       BrowserGpuMemoryBufferManager::current();
   if (gpu_memory_buffer_manager)
     gpu_memory_buffer_manager->ProcessRemoved(PeerHandle(), render_process_id_);
-  HostDiscardableSharedMemoryManager::current()->ProcessRemoved(PeerHandle());
+  HostDiscardableSharedMemoryManager::current()->ProcessRemoved(
+      render_process_id_);
 }
 
 void RenderMessageFilter::OnChannelClosing() {
@@ -933,8 +934,8 @@
     IPC::Message* reply_msg) {
   base::SharedMemoryHandle handle;
   HostDiscardableSharedMemoryManager::current()
-      ->AllocateLockedDiscardableSharedMemoryForChild(PeerHandle(), size, id,
-                                                      &handle);
+      ->AllocateLockedDiscardableSharedMemoryForChild(
+          PeerHandle(), render_process_id_, size, id, &handle);
   ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory::
       WriteReplyParams(reply_msg, handle);
   Send(reply_msg);
@@ -954,7 +955,7 @@
 void RenderMessageFilter::DeletedDiscardableSharedMemoryOnFileThread(
     DiscardableSharedMemoryId id) {
   HostDiscardableSharedMemoryManager::current()
-      ->ChildDeletedDiscardableSharedMemory(id, PeerHandle());
+      ->ChildDeletedDiscardableSharedMemory(id, render_process_id_);
 }
 
 void RenderMessageFilter::OnDeletedDiscardableSharedMemory(
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index aa2cee4..baddb20a 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -156,7 +156,6 @@
 #include "ipc/ipc_logging.h"
 #include "ipc/ipc_switches.h"
 #include "ipc/mojo/ipc_channel_mojo.h"
-#include "ipc/mojo/ipc_channel_mojo_host.h"
 #include "media/base/media_switches.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "ppapi/shared_impl/ppapi_switches.h"
@@ -714,14 +713,11 @@
             ->task_runner();
   if (ShouldUseMojoChannel()) {
     VLOG(1) << "Mojo Channel is enabled on host";
-    if (!channel_mojo_host_) {
-      channel_mojo_host_.reset(new IPC::ChannelMojoHost(mojo_task_runner));
-    }
 
     return IPC::ChannelProxy::Create(
         IPC::ChannelMojo::CreateServerFactory(
-            channel_mojo_host_->channel_delegate(), mojo_task_runner,
-            channel_id, content::ChildProcessHost::GetAttachmentBroker()),
+            mojo_task_runner, channel_id,
+            content::ChildProcessHost::GetAttachmentBroker()),
         this, runner.get());
   }
 
@@ -1306,6 +1302,7 @@
     switches::kEnableWebBluetooth,
     switches::kEnableWebGLDraftExtensions,
     switches::kEnableWebGLImageChromium,
+    switches::kEnableWebVR,
     switches::kExplicitlyAllowedPorts,
     switches::kForceDeviceScaleFactor,
     switches::kForceDisplayList2dCanvas,
@@ -2267,7 +2264,8 @@
   // absence of field trials to get coverage on the perf waterfall.
   base::FieldTrial* trial =
       base::FieldTrialList::Find("BackgroundRendererProcesses");
-  if (!trial || !base::StartsWithASCII(trial->group_name(), "Disallow", true)) {
+  if (!trial || !base::StartsWith(trial->group_name(), "Disallow",
+                                  base::CompareCase::SENSITIVE)) {
     child_process_launcher_->SetProcessBackgrounded(backgrounded);
   }
 #else
@@ -2338,8 +2336,6 @@
   tracked_objects::ScopedTracker tracking_profile5(
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
           "465841 RenderProcessHostImpl::OnProcessLaunched::MojoClientLaunch"));
-  if (channel_mojo_host_)
-    channel_mojo_host_->OnClientLaunched(GetHandle());
 
   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/465841
   // is fixed.
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 44831f7a..8937fd1 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -282,10 +282,6 @@
   // browser_process.h)
   scoped_ptr<IPC::ChannelProxy> channel_;
 
-  // A host object ChannelMojo needs. The lifetime is bound to
-  // the RenderProcessHostImpl, not the channel.
-  scoped_ptr<IPC::ChannelMojoHost> channel_mojo_host_;
-
   // True if fast shutdown has been performed on this RPH.
   bool fast_shutdown_started_;
 
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 abdb9eb..f1b29bf9 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -514,6 +514,12 @@
   host_->SetInputMethodActive(true);
   if (overscroll_controller_)
     overscroll_controller_->Enable();
+  if (content_view_core_) {
+    WebContentsImpl* web_contents_impl =
+        static_cast<WebContentsImpl*>(content_view_core_->GetWebContents());
+    if (web_contents_impl->ShowingInterstitialPage())
+      content_view_core_->ForceUpdateImeAdapter(GetNativeImeAdapter());
+  }
 }
 
 bool RenderWidgetHostViewAndroid::HasFocus() const {
@@ -1848,9 +1854,14 @@
   if (visible) {
     ShowInternal();
   } else {
-    bool hide_frontbuffer = true;
-    bool stop_observing_root_window = false;
-    HideInternal(hide_frontbuffer, stop_observing_root_window);
+    // Only hide the active frontbuffer if the Activity has been paused and
+    // we've already hidden the host. Otherwise the root window visibility is
+    // likely to be temporarily changing, e.g., with fullscreen video.
+    if (host_ && host_->is_hidden()) {
+      bool hide_frontbuffer = true;
+      bool stop_observing_root_window = false;
+      HideInternal(hide_frontbuffer, stop_observing_root_window);
+    }
   }
 }
 
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.cc b/content/browser/service_worker/service_worker_dispatcher_host.cc
index ff0c065..fc10274 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host.cc
@@ -428,33 +428,25 @@
     int thread_id,
     int request_id,
     int provider_id,
-    const GURL& pattern) {
+    int64 registration_id) {
   TRACE_EVENT0("ServiceWorker",
                "ServiceWorkerDispatcherHost::OnUnregisterServiceWorker");
   if (!GetContext()) {
     Send(new ServiceWorkerMsg_ServiceWorkerUnregistrationError(
-        thread_id,
-        request_id,
-        blink::WebServiceWorkerError::ErrorTypeAbort,
+        thread_id, request_id, blink::WebServiceWorkerError::ErrorTypeAbort,
         base::ASCIIToUTF16(kShutdownErrorMessage)));
     return;
   }
-  if (!pattern.is_valid()) {
-    bad_message::ReceivedBadMessage(this, bad_message::SWDH_UNREGISTER_BAD_URL);
-    return;
-  }
 
-  ServiceWorkerProviderHost* provider_host = GetContext()->GetProviderHost(
-      render_process_id_, provider_id);
+  ServiceWorkerProviderHost* provider_host =
+      GetContext()->GetProviderHost(render_process_id_, provider_id);
   if (!provider_host) {
     bad_message::ReceivedBadMessage(this, bad_message::SWDH_UNREGISTER_NO_HOST);
     return;
   }
   if (!provider_host->IsContextAlive()) {
     Send(new ServiceWorkerMsg_ServiceWorkerUnregistrationError(
-        thread_id,
-        request_id,
-        blink::WebServiceWorkerError::ErrorTypeAbort,
+        thread_id, request_id, blink::WebServiceWorkerError::ErrorTypeAbort,
         base::ASCIIToUTF16(kShutdownErrorMessage)));
     return;
   }
@@ -462,40 +454,43 @@
   // TODO(ksakamoto): This check can be removed once crbug.com/439697 is fixed.
   if (provider_host->document_url().is_empty()) {
     Send(new ServiceWorkerMsg_ServiceWorkerUnregistrationError(
-        thread_id,
-        request_id,
-        WebServiceWorkerError::ErrorTypeSecurity,
+        thread_id, request_id, WebServiceWorkerError::ErrorTypeSecurity,
         base::ASCIIToUTF16(kNoDocumentURLErrorMessage)));
     return;
   }
 
-  if (!CanUnregisterServiceWorker(provider_host->document_url(), pattern)) {
+  ServiceWorkerRegistration* registration =
+      GetContext()->GetLiveRegistration(registration_id);
+  if (!registration) {
+    // |registration| must be alive because a renderer retains a registration
+    // reference at this point.
+    bad_message::ReceivedBadMessage(
+        this, bad_message::SWDH_UNREGISTER_BAD_REGISTRATION_ID);
+    return;
+  }
+
+  if (!CanUnregisterServiceWorker(provider_host->document_url(),
+                                  registration->pattern())) {
     bad_message::ReceivedBadMessage(this, bad_message::SWDH_UNREGISTER_CANNOT);
     return;
   }
 
   if (!GetContentClient()->browser()->AllowServiceWorker(
-          pattern, provider_host->topmost_frame_url(), resource_context_,
-          render_process_id_, provider_host->frame_id())) {
+          registration->pattern(), provider_host->topmost_frame_url(),
+          resource_context_, render_process_id_, provider_host->frame_id())) {
     Send(new ServiceWorkerMsg_ServiceWorkerUnregistrationError(
-        thread_id,
-        request_id,
-        WebServiceWorkerError::ErrorTypeUnknown,
+        thread_id, request_id, WebServiceWorkerError::ErrorTypeUnknown,
         base::ASCIIToUTF16(kUserDeniedPermissionMessage)));
     return;
   }
 
   TRACE_EVENT_ASYNC_BEGIN1(
-      "ServiceWorker",
-      "ServiceWorkerDispatcherHost::UnregisterServiceWorker",
-      request_id,
-      "Pattern", pattern.spec());
+      "ServiceWorker", "ServiceWorkerDispatcherHost::UnregisterServiceWorker",
+      request_id, "Pattern", registration->pattern().spec());
   GetContext()->UnregisterServiceWorker(
-      pattern,
-      base::Bind(&ServiceWorkerDispatcherHost::UnregistrationComplete,
-                 this,
-                 thread_id,
-                 request_id));
+      registration->pattern(),
+      base::Bind(&ServiceWorkerDispatcherHost::UnregistrationComplete, this,
+                 thread_id, request_id));
 }
 
 void ServiceWorkerDispatcherHost::OnGetRegistration(
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.h b/content/browser/service_worker/service_worker_dispatcher_host.h
index fdfb46b..fbbcf9c 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.h
+++ b/content/browser/service_worker/service_worker_dispatcher_host.h
@@ -92,7 +92,7 @@
   void OnUnregisterServiceWorker(int thread_id,
                                  int request_id,
                                  int provider_id,
-                                 const GURL& pattern);
+                                 int64 registration_id);
   void OnGetRegistration(int thread_id,
                          int request_id,
                          int provider_id,
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 e2f2f7f6..462d093 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
@@ -99,15 +99,17 @@
     dispatcher_host_->ipc_sink()->ClearMessages();
   }
 
-  void SendUnregister(int64 provider_id, GURL pattern) {
+  void SendUnregister(int64 provider_id, int64 registration_id) {
     dispatcher_host_->OnMessageReceived(
-        ServiceWorkerHostMsg_UnregisterServiceWorker(
-            -1, -1, provider_id, pattern));
+        ServiceWorkerHostMsg_UnregisterServiceWorker(-1, -1, provider_id,
+                                                     registration_id));
     base::RunLoop().RunUntilIdle();
   }
 
-  void Unregister(int64 provider_id, GURL pattern, uint32 expected_message) {
-    SendUnregister(provider_id, pattern);
+  void Unregister(int64 provider_id,
+                  int64 registration_id,
+                  uint32 expected_message) {
+    SendUnregister(provider_id, registration_id);
     EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
         expected_message));
     dispatcher_host_->ipc_sink()->ClearMessages();
@@ -184,15 +186,21 @@
            GURL("https://www.example.com/"),
            GURL("https://www.example.com/bar"),
            ServiceWorkerMsg_ServiceWorkerRegistrationError::ID);
-  Unregister(kProviderId,
-             GURL("https://www.example.com/"),
-             ServiceWorkerMsg_ServiceWorkerUnregistrationError::ID);
   GetRegistration(kProviderId,
                   GURL("https://www.example.com/"),
                   ServiceWorkerMsg_ServiceWorkerGetRegistrationError::ID);
   GetRegistrations(kProviderId,
                    ServiceWorkerMsg_ServiceWorkerGetRegistrationsError::ID);
 
+  // Add a registration into a live registration map so that Unregister() can
+  // find it.
+  const int64 kRegistrationId = 999;  // Dummy value
+  scoped_refptr<ServiceWorkerRegistration> registration(
+      new ServiceWorkerRegistration(GURL("https://www.example.com/"),
+                                    kRegistrationId, context()->AsWeakPtr()));
+  Unregister(kProviderId, kRegistrationId,
+             ServiceWorkerMsg_ServiceWorkerUnregistrationError::ID);
+
   SetBrowserClientForTesting(old_browser_client);
 }
 
@@ -350,64 +358,6 @@
   EXPECT_EQ(3, dispatcher_host_->bad_messages_received_count_);
 }
 
-TEST_F(ServiceWorkerDispatcherHostTest, Unregister_HTTPS) {
-  const int64 kProviderId = 99;  // Dummy value
-  scoped_ptr<ServiceWorkerProviderHost> host(
-      CreateServiceWorkerProviderHost(kProviderId));
-  host->SetDocumentUrl(GURL("https://www.example.com/foo"));
-  context()->AddProviderHost(host.Pass());
-
-  Unregister(kProviderId,
-             GURL("https://www.example.com/"),
-             ServiceWorkerMsg_ServiceWorkerUnregistered::ID);
-}
-
-TEST_F(ServiceWorkerDispatcherHostTest,
-       Unregister_NotSecureTransportLocalhost) {
-  const int64 kProviderId = 99;  // Dummy value
-  scoped_ptr<ServiceWorkerProviderHost> host(
-      CreateServiceWorkerProviderHost(kProviderId));
-  host->SetDocumentUrl(GURL("http://localhost/foo"));
-  context()->AddProviderHost(host.Pass());
-
-  Unregister(kProviderId,
-             GURL("http://localhost/"),
-             ServiceWorkerMsg_ServiceWorkerUnregistered::ID);
-}
-
-TEST_F(ServiceWorkerDispatcherHostTest, Unregister_CrossOriginShouldFail) {
-  const int64 kProviderId = 99;  // Dummy value
-  scoped_ptr<ServiceWorkerProviderHost> host(
-      CreateServiceWorkerProviderHost(kProviderId));
-  host->SetDocumentUrl(GURL("https://www.example.com/foo"));
-  context()->AddProviderHost(host.Pass());
-
-  SendUnregister(kProviderId, GURL("https://foo.example.com/"));
-  EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
-}
-
-TEST_F(ServiceWorkerDispatcherHostTest, Unregister_InvalidScopeShouldFail) {
-  const int64 kProviderId = 99;  // Dummy value
-  scoped_ptr<ServiceWorkerProviderHost> host(
-      CreateServiceWorkerProviderHost(kProviderId));
-  host->SetDocumentUrl(GURL("https://www.example.com/foo"));
-  context()->AddProviderHost(host.Pass());
-
-  SendUnregister(kProviderId, GURL(""));
-  EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
-}
-
-TEST_F(ServiceWorkerDispatcherHostTest, Unregister_NonSecureOriginShouldFail) {
-  const int64 kProviderId = 99;  // Dummy value
-  scoped_ptr<ServiceWorkerProviderHost> host(
-      CreateServiceWorkerProviderHost(kProviderId));
-  host->SetDocumentUrl(GURL("http://www.example.com/foo"));
-  context()->AddProviderHost(host.Pass());
-
-  SendUnregister(kProviderId, GURL("http://www.example.com/"));
-  EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
-}
-
 TEST_F(ServiceWorkerDispatcherHostTest, EarlyContextDeletion) {
   helper_->ShutdownContext();
 
diff --git a/content/browser/service_worker/service_worker_info.h b/content/browser/service_worker/service_worker_info.h
index 094cb18..f177e754 100644
--- a/content/browser/service_worker/service_worker_info.h
+++ b/content/browser/service_worker/service_worker_info.h
@@ -16,7 +16,7 @@
 
 struct CONTENT_EXPORT ServiceWorkerVersionInfo {
  public:
-  struct ClientInfo {
+  struct CONTENT_EXPORT ClientInfo {
    public:
     ClientInfo();
     ClientInfo(int process_id, int route_id, ServiceWorkerProviderType type);
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc
index 077ae33..8831a92 100644
--- a/content/browser/service_worker/service_worker_storage.cc
+++ b/content/browser/service_worker/service_worker_storage.cc
@@ -367,13 +367,10 @@
     const GURL& scope) {
   if (state_ != INITIALIZED || !context_)
     return NULL;
-  for (RegistrationRefsById::const_iterator it =
-           uninstalling_registrations_.begin();
-       it != uninstalling_registrations_.end();
-       ++it) {
-    if (it->second->pattern() == scope) {
-      DCHECK(it->second->is_uninstalling());
-      return it->second.get();
+  for (const auto& registration : uninstalling_registrations_) {
+    if (registration.second->pattern() == scope) {
+      DCHECK(registration.second->is_uninstalling());
+      return registration.second.get();
     }
   }
   return NULL;
@@ -874,8 +871,8 @@
     version->script_cache_map()->GetResources(&resources);
 
     std::set<int64> ids;
-    for (size_t i = 0; i < resources.size(); ++i)
-      ids.insert(resources[i].resource_id);
+    for (const auto& resource : resources)
+      ids.insert(resource.resource_id);
 
     database_task_manager_->GetTaskRunner()->PostTask(
         FROM_HERE,
@@ -1008,10 +1005,8 @@
     ScheduleDeleteAndStartOver();
   }
 
-  for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
-       it != pending_tasks_.end(); ++it) {
-    RunSoon(FROM_HERE, *it);
-  }
+  for (const auto& task : pending_tasks_)
+    RunSoon(FROM_HERE, task);
   pending_tasks_.clear();
 }
 
@@ -1145,13 +1140,11 @@
   }
 
   // Add unstored registrations that are being installed.
-  for (RegistrationRefsById::const_iterator it =
-           installing_registrations_.begin();
-       it != installing_registrations_.end(); ++it) {
+  for (const auto& registration : installing_registrations_) {
     if ((!origin_filter.is_valid() ||
-         it->second->pattern().GetOrigin() == origin_filter) &&
-        registration_ids.insert(it->first).second) {
-      registrations.push_back((it->second).get());
+         registration.second->pattern().GetOrigin() == origin_filter) &&
+        registration_ids.insert(registration.first).second) {
+      registrations.push_back(registration.second);
     }
   }
 
@@ -1216,13 +1209,11 @@
   }
 
   // Add unstored registrations that are being installed.
-  for (RegistrationRefsById::const_iterator it =
-           installing_registrations_.begin();
-       it != installing_registrations_.end(); ++it) {
+  for (const auto& registration : installing_registrations_) {
     if ((!origin_filter.is_valid() ||
-         it->second->pattern().GetOrigin() == origin_filter) &&
-        pushed_registrations.insert(it->first).second) {
-      infos.push_back(it->second->GetInfo());
+         registration.second->pattern().GetOrigin() == origin_filter) &&
+        pushed_registrations.insert(registration.first).second) {
+      infos.push_back(registration.second->GetInfo());
     }
   }
 
@@ -1389,24 +1380,18 @@
 
   // TODO(nhiroki): This searches over installing registrations linearly and it
   // couldn't be scalable. Maybe the regs should be partitioned by origin.
-  for (RegistrationRefsById::const_iterator it =
-           installing_registrations_.begin();
-       it != installing_registrations_.end(); ++it) {
-    if (matcher.MatchLongest(it->second->pattern()))
-      match = it->second.get();
-  }
+  for (const auto& registration : installing_registrations_)
+    if (matcher.MatchLongest(registration.second->pattern()))
+      match = registration.second.get();
   return match;
 }
 
 ServiceWorkerRegistration*
 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
     const GURL& scope) {
-  for (RegistrationRefsById::const_iterator it =
-           installing_registrations_.begin();
-       it != installing_registrations_.end(); ++it) {
-    if (it->second->pattern() == scope)
-      return it->second.get();
-  }
+  for (const auto& registration : installing_registrations_)
+    if (registration.second->pattern() == scope)
+      return registration.second.get();
   return NULL;
 }
 
@@ -1533,16 +1518,16 @@
 void ServiceWorkerStorage::StartPurgingResources(
     const std::vector<int64>& ids) {
   DCHECK(has_checked_for_stale_resources_);
-  for (size_t i = 0; i < ids.size(); ++i)
-    purgeable_resource_ids_.push_back(ids[i]);
+  for (const auto& id : ids)
+    purgeable_resource_ids_.push_back(id);
   ContinuePurgingResources();
 }
 
 void ServiceWorkerStorage::StartPurgingResources(
     const ResourceList& resources) {
   DCHECK(has_checked_for_stale_resources_);
-  for (size_t i = 0; i < resources.size(); ++i)
-    purgeable_resource_ids_.push_back(resources[i].resource_id);
+  for (const auto& resource : resources)
+    purgeable_resource_ids_.push_back(resource.resource_id);
   ContinuePurgingResources();
 }
 
@@ -1772,9 +1757,9 @@
     const GURL& document_url,
     const FindInDBCallback& callback) {
   GURL origin = document_url.GetOrigin();
-  RegistrationList registrations;
-  ServiceWorkerDatabase::Status status =
-      database->GetRegistrationsForOrigin(origin, &registrations, nullptr);
+  RegistrationList registration_data_list;
+  ServiceWorkerDatabase::Status status = database->GetRegistrationsForOrigin(
+      origin, &registration_data_list, nullptr);
   if (status != ServiceWorkerDatabase::STATUS_OK) {
     original_task_runner->PostTask(
         FROM_HERE,
@@ -1792,11 +1777,9 @@
   // Find one with a pattern match.
   LongestScopeMatcher matcher(document_url);
   int64 match = kInvalidServiceWorkerRegistrationId;
-  for (size_t i = 0; i < registrations.size(); ++i) {
-    if (matcher.MatchLongest(registrations[i].scope))
-      match = registrations[i].registration_id;
-  }
-
+  for (const auto& registration_data : registration_data_list)
+    if (matcher.MatchLongest(registration_data.scope))
+      match = registration_data.registration_id;
   if (match != kInvalidServiceWorkerRegistrationId)
     status = database->ReadRegistration(match, origin, &data, &resources);
 
@@ -1811,9 +1794,9 @@
     const GURL& scope,
     const FindInDBCallback& callback) {
   GURL origin = scope.GetOrigin();
-  RegistrationList registrations;
-  ServiceWorkerDatabase::Status status =
-      database->GetRegistrationsForOrigin(origin, &registrations, nullptr);
+  RegistrationList registration_data_list;
+  ServiceWorkerDatabase::Status status = database->GetRegistrationsForOrigin(
+      origin, &registration_data_list, nullptr);
   if (status != ServiceWorkerDatabase::STATUS_OK) {
     original_task_runner->PostTask(
         FROM_HERE,
@@ -1828,12 +1811,11 @@
   ServiceWorkerDatabase::RegistrationData data;
   ResourceList resources;
   status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
-  for (RegistrationList::const_iterator it = registrations.begin();
-       it != registrations.end(); ++it) {
-    if (scope != it->scope)
+  for (const auto& registration_data : registration_data_list) {
+    if (scope != registration_data.scope)
       continue;
-    status = database->ReadRegistration(it->registration_id, origin,
-                                        &data, &resources);
+    status = database->ReadRegistration(registration_data.registration_id,
+                                        origin, &data, &resources);
     break;  // We're done looping.
   }
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 9e266bf..d6f6ab1 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -63,6 +63,18 @@
   }
 }
 
+// Helper function to extract and return "window.receivedMessages" from the
+// |sender_ftn| frame.  This variable is used in post_message.html to count the
+// number of messages received via postMessage by the current window.
+int GetReceivedMessages(FrameTreeNode* ftn) {
+  int received_messages = 0;
+  EXPECT_TRUE(ExecuteScriptAndExtractInt(
+      ftn->current_frame_host(),
+      "window.domAutomationController.send(window.receivedMessages);",
+      &received_messages));
+  return received_messages;
+}
+
 class RedirectNotificationObserver : public NotificationObserver {
  public:
   // Register to listen for notifications of the given type from either a
@@ -2329,7 +2341,6 @@
                             ->GetFrameTree()
                             ->root();
 
-  TestNavigationObserver observer(shell()->web_contents());
   ASSERT_EQ(2U, root->child_count());
 
   // Verify the frames start at correct URLs.  First frame should be
@@ -2357,20 +2368,104 @@
   EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
 
   // Verify the total number of received messages for each subframe.  First
-  // frame should have one message (reply from second frame), and second frame
+  // frame should have one message (reply from second frame).  Second frame
   // should have two messages (message from first frame and reply from parent).
-  int subframe1_received_messages = 0;
-  int subframe2_received_messages = 0;
-  EXPECT_TRUE(ExecuteScriptAndExtractInt(
-      root->child_at(0)->current_frame_host(),
-      "window.domAutomationController.send(window.receivedMessages);",
-      &subframe1_received_messages));
-  EXPECT_EQ(1, subframe1_received_messages);
-  EXPECT_TRUE(ExecuteScriptAndExtractInt(
-      root->child_at(1)->current_frame_host(),
-      "window.domAutomationController.send(window.receivedMessages);",
-      &subframe2_received_messages));
-  EXPECT_EQ(2, subframe2_received_messages);
+  // Parent should have one message (from second frame).
+  EXPECT_EQ(1, GetReceivedMessages(root->child_at(0)));
+  EXPECT_EQ(2, GetReceivedMessages(root->child_at(1)));
+  EXPECT_EQ(1, GetReceivedMessages(root));
+}
+
+// Check that postMessage can be sent from a subframe on a cross-process opener
+// tab, and that its event.source points to a valid proxy.
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
+                       PostMessageWithSubframeOnOpenerChain) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_post_message_frames.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // It is safe to obtain the root frame tree node here, as it doesn't change.
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+
+  ASSERT_EQ(2U, root->child_count());
+
+  // Verify the initial state of the world.  First frame should be same-site;
+  // second frame should be cross-site.
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   |--Site A ------- proxies for B\n"
+      "   +--Site B ------- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://foo.com/",
+      DepictFrameTree(root));
+
+  // Open a popup from the first subframe (so that popup's window.opener points
+  // to the subframe) and navigate it to bar.com.
+  ShellAddedObserver new_shell_observer;
+  EXPECT_TRUE(ExecuteScript(root->child_at(0)->current_frame_host(),
+                            "openPopup('about:blank');"));
+  Shell* popup = new_shell_observer.GetShell();
+  GURL popup_url(
+      embedded_test_server()->GetURL("bar.com", "/post_message.html"));
+  EXPECT_TRUE(NavigateToURL(popup, popup_url));
+
+  // From the popup, open another popup for baz.com.  This will be used to
+  // check that the whole opener chain is processed when creating proxies and
+  // not just an immediate opener.
+  ShellAddedObserver new_shell_observer2;
+  EXPECT_TRUE(
+      ExecuteScript(popup->web_contents(), "openPopup('about:blank');"));
+  Shell* popup2 = new_shell_observer2.GetShell();
+  GURL popup2_url(
+      embedded_test_server()->GetURL("baz.com", "/post_message.html"));
+  EXPECT_TRUE(NavigateToURL(popup2, popup2_url));
+
+  // Ensure that we've created proxies for SiteInstances of both popups (C, D)
+  // in the main window's frame tree.
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C D\n"
+      "   |--Site A ------- proxies for B C D\n"
+      "   +--Site B ------- proxies for A C D\n"
+      "Where A = http://a.com/\n"
+      "      B = http://foo.com/\n"
+      "      C = http://bar.com/\n"
+      "      D = http://baz.com/",
+      DepictFrameTree(root));
+
+  // Check the first popup's frame tree as well.  Note that it doesn't have a
+  // proxy for foo.com, since foo.com can't reach the popup.  It does have a
+  // proxy for its opener a.com (which can reach it via the window.open
+  // reference) and second popup (which can reach it via window.opener).
+  FrameTreeNode* popup_root =
+      static_cast<WebContentsImpl*>(popup->web_contents())
+          ->GetFrameTree()
+          ->root();
+  EXPECT_EQ(
+      " Site C ------------ proxies for A D\n"
+      "Where A = http://a.com/\n"
+      "      C = http://bar.com/\n"
+      "      D = http://baz.com/",
+      DepictFrameTree(popup_root));
+
+  // Send a message from first subframe on main page to the first popup and
+  // wait for a reply back. The reply verifies that the proxy for the opener
+  // tab's subframe is targeted properly.
+  PostMessageAndWaitForReply(root->child_at(0), "postToPopup('subframe-msg')",
+                             "\"done-subframe1\"");
+
+  // TODO(alexmos): Once we propagate subframe opener information to new
+  // RenderViews, try sending a postMessage from the popup to window.opener and
+  // ensure that it reaches subframe1.
+
+  // Verify the total number of received messages for each subframe.  First
+  // subframe and popup should have one message each, and other frames
+  // shouldn't have received any messages.
+  EXPECT_EQ(0, GetReceivedMessages(root));
+  EXPECT_EQ(1, GetReceivedMessages(root->child_at(0)));
+  EXPECT_EQ(0, GetReceivedMessages(root->child_at(1)));
+  EXPECT_EQ(1, GetReceivedMessages(popup_root));
 }
 
 // Check that parent.frames[num] references correct sibling frames when the
@@ -2449,26 +2544,9 @@
                              "\"done-1-2-name\"");
 
   // Verify the total number of received messages for each subframe.
-  int child0_received_messages = 0;
-  EXPECT_TRUE(ExecuteScriptAndExtractInt(
-      child0->current_frame_host(),
-      "window.domAutomationController.send(window.receivedMessages);",
-      &child0_received_messages));
-  EXPECT_EQ(1, child0_received_messages);
-
-  int child1_received_messages = 0;
-  EXPECT_TRUE(ExecuteScriptAndExtractInt(
-      child1->current_frame_host(),
-      "window.domAutomationController.send(window.receivedMessages);",
-      &child1_received_messages));
-  EXPECT_EQ(2, child1_received_messages);
-
-  int child2_received_messages = 0;
-  EXPECT_TRUE(ExecuteScriptAndExtractInt(
-      child2->current_frame_host(),
-      "window.domAutomationController.send(window.receivedMessages);",
-      &child2_received_messages));
-  EXPECT_EQ(1, child2_received_messages);
+  EXPECT_EQ(1, GetReceivedMessages(child0));
+  EXPECT_EQ(2, GetReceivedMessages(child1));
+  EXPECT_EQ(1, GetReceivedMessages(child2));
 }
 
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, RFPHDestruction) {
diff --git a/content/browser/vr/OWNERS b/content/browser/vr/OWNERS
new file mode 100644
index 0000000..1c724a6
--- /dev/null
+++ b/content/browser/vr/OWNERS
@@ -0,0 +1,2 @@
+bajones@chromium.org
+kbr@chromium.org
diff --git a/content/browser/vr/android/cardboard/cardboard_vr_device.cc b/content/browser/vr/android/cardboard/cardboard_vr_device.cc
new file mode 100644
index 0000000..11f8505ff
--- /dev/null
+++ b/content/browser/vr/android/cardboard/cardboard_vr_device.cc
@@ -0,0 +1,188 @@
+// 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/vr/android/cardboard/cardboard_vr_device.h"
+
+#include <math.h>
+#include <algorithm>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "jni/CardboardVRDevice_jni.h"
+
+using base::android::AttachCurrentThread;
+
+namespace content {
+
+namespace {
+
+// Source:
+// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
+VRVector4Ptr MatrixToOrientationQuat(const float m[16]) {
+  VRVector4Ptr out = VRVector4::New();
+  float trace = m[0] + m[5] + m[10];
+  float root;
+  if (trace > 0.0f) {
+    root = sqrtf(1.0f + trace) * 2.0f;
+    out->x = (m[9] - m[6]) / root;
+    out->y = (m[2] - m[8]) / root;
+    out->z = (m[4] - m[1]) / root;
+    out->w = 0.25f * root;
+  } else if ((m[0] > m[5]) && (m[0] > m[10])) {
+    root = sqrtf(1.0f + m[0] - m[5] - m[10]) * 2.0f;
+    out->x = 0.25f * root;
+    out->y = (m[1] + m[4]) / root;
+    out->z = (m[2] + m[8]) / root;
+    out->w = (m[9] - m[6]) / root;
+  } else if (m[5] > m[10]) {
+    root = sqrtf(1.0f + m[5] - m[0] - m[10]) * 2.0f;
+    out->x = (m[1] + m[4]) / root;
+    out->y = 0.25f * root;
+    out->z = (m[6] + m[9]) / root;
+    out->w = (m[2] - m[8]) / root;
+  } else {
+    root = sqrtf(1.0f + m[10] - m[0] - m[5]) * 2.0f;
+    out->x = (m[2] + m[8]) / root;
+    out->y = (m[6] + m[9]) / root;
+    out->z = 0.25f * root;
+    out->w = (m[4] - m[1]) / root;
+  }
+
+  return out.Pass();
+}
+
+}  // namespace
+
+bool CardboardVRDevice::RegisterCardboardVRDevice(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+CardboardVRDevice::CardboardVRDevice(VRDeviceProvider* provider)
+    : VRDevice(provider), frame_index_(0) {
+  j_cardboard_device_.Reset(Java_CardboardVRDevice_create(
+      AttachCurrentThread(), base::android::GetApplicationContext()));
+}
+
+CardboardVRDevice::~CardboardVRDevice() {
+  Java_CardboardVRDevice_stopTracking(AttachCurrentThread(),
+                                      j_cardboard_device_.obj());
+}
+
+VRDeviceInfoPtr CardboardVRDevice::GetVRDevice() {
+  VRDeviceInfoPtr device = VRDeviceInfo::New();
+
+  JNIEnv* env = AttachCurrentThread();
+
+  ScopedJavaLocalRef<jstring> j_device_name =
+      Java_CardboardVRDevice_getDeviceName(env, j_cardboard_device_.obj());
+  device->deviceName =
+      base::android::ConvertJavaStringToUTF8(env, j_device_name.obj());
+
+  ScopedJavaLocalRef<jfloatArray> j_fov(env, env->NewFloatArray(4));
+  Java_CardboardVRDevice_getFieldOfView(env, j_cardboard_device_.obj(),
+                                        j_fov.obj());
+
+  std::vector<float> fov;
+  base::android::JavaFloatArrayToFloatVector(env, j_fov.obj(), &fov);
+
+  device->hmdInfo = VRHMDInfo::New();
+  VRHMDInfoPtr& hmdInfo = device->hmdInfo;
+
+  hmdInfo->leftEye = VREyeParameters::New();
+  hmdInfo->rightEye = VREyeParameters::New();
+  VREyeParametersPtr& leftEye = hmdInfo->leftEye;
+  VREyeParametersPtr& rightEye = hmdInfo->rightEye;
+
+  leftEye->recommendedFieldOfView = VRFieldOfView::New();
+  leftEye->recommendedFieldOfView->upDegrees = fov[0];
+  leftEye->recommendedFieldOfView->downDegrees = fov[1];
+  leftEye->recommendedFieldOfView->leftDegrees = fov[2];
+  leftEye->recommendedFieldOfView->rightDegrees = fov[3];
+
+  // Cardboard devices always assume a mirrored FOV, so this is just the left
+  // eye FOV with the left and right degrees swapped.
+  rightEye->recommendedFieldOfView = VRFieldOfView::New();
+  rightEye->recommendedFieldOfView->upDegrees = fov[0];
+  rightEye->recommendedFieldOfView->downDegrees = fov[1];
+  rightEye->recommendedFieldOfView->leftDegrees = fov[3];
+  rightEye->recommendedFieldOfView->rightDegrees = fov[2];
+
+  // Cardboard does not support configurable FOV.
+  leftEye->maximumFieldOfView = leftEye->recommendedFieldOfView.Clone();
+  rightEye->maximumFieldOfView = rightEye->recommendedFieldOfView.Clone();
+  leftEye->minimumFieldOfView = leftEye->recommendedFieldOfView.Clone();
+  rightEye->minimumFieldOfView = rightEye->recommendedFieldOfView.Clone();
+
+  float ipd = Java_CardboardVRDevice_getIpd(env, j_cardboard_device_.obj());
+
+  leftEye->eyeTranslation = VRVector3::New();
+  leftEye->eyeTranslation->x = ipd * -0.5f;
+  leftEye->eyeTranslation->y = 0.0f;
+  leftEye->eyeTranslation->z = 0.0f;
+
+  rightEye->eyeTranslation = VRVector3::New();
+  rightEye->eyeTranslation->x = ipd * 0.5f;
+  rightEye->eyeTranslation->y = 0.0f;
+  rightEye->eyeTranslation->z = 0.0f;
+
+  ScopedJavaLocalRef<jintArray> j_screen_size(env, env->NewIntArray(2));
+  Java_CardboardVRDevice_getScreenSize(env, j_cardboard_device_.obj(),
+                                       j_screen_size.obj());
+
+  std::vector<int> screen_size;
+  base::android::JavaIntArrayToIntVector(env, j_screen_size.obj(),
+                                         &screen_size);
+
+  leftEye->renderRect = VRRect::New();
+  leftEye->renderRect->x = 0;
+  leftEye->renderRect->y = 0;
+  leftEye->renderRect->width = screen_size[0] / 2.0;
+  leftEye->renderRect->height = screen_size[1];
+
+  rightEye->renderRect = VRRect::New();
+  rightEye->renderRect->x = screen_size[0] / 2.0;
+  rightEye->renderRect->y = 0;
+  rightEye->renderRect->width = screen_size[0] / 2.0;
+  rightEye->renderRect->height = screen_size[1];
+
+  return device.Pass();
+}
+
+VRSensorStatePtr CardboardVRDevice::GetSensorState() {
+  VRSensorStatePtr state = VRSensorState::New();
+
+  state->timestamp = base::Time::Now().ToJsTime();
+  state->frameIndex = frame_index_;
+
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jfloatArray> j_head_matrix(env, env->NewFloatArray(16));
+  Java_CardboardVRDevice_getSensorState(env, j_cardboard_device_.obj(),
+                                        j_head_matrix.obj());
+
+  std::vector<float> head_matrix;
+  base::android::JavaFloatArrayToFloatVector(env, j_head_matrix.obj(),
+                                             &head_matrix);
+
+  state->orientation = MatrixToOrientationQuat(&head_matrix[0]);
+
+  state->position = VRVector3::New();
+  state->position->x = -head_matrix[12];
+  state->position->y = head_matrix[13];
+  state->position->z = head_matrix[14];
+
+  frame_index_++;
+
+  return state.Pass();
+}
+
+void CardboardVRDevice::ResetSensor() {
+  Java_CardboardVRDevice_resetSensor(AttachCurrentThread(),
+                                     j_cardboard_device_.obj());
+}
+
+}  // namespace content
diff --git a/content/browser/vr/android/cardboard/cardboard_vr_device.h b/content/browser/vr/android/cardboard/cardboard_vr_device.h
new file mode 100644
index 0000000..5d7baa97
--- /dev/null
+++ b/content/browser/vr/android/cardboard/cardboard_vr_device.h
@@ -0,0 +1,37 @@
+// 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_VR_CARDBOARD_VR_DEVICE_H
+#define CONTENT_BROWSER_VR_CARDBOARD_VR_DEVICE_H
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/basictypes.h"
+#include "content/browser/vr/vr_device.h"
+
+namespace content {
+
+class CardboardVRDevice : public VRDevice {
+ public:
+  static bool RegisterCardboardVRDevice(JNIEnv* env);
+
+  explicit CardboardVRDevice(VRDeviceProvider* provider);
+  ~CardboardVRDevice() override;
+
+  VRDeviceInfoPtr GetVRDevice() override;
+  VRSensorStatePtr GetSensorState() override;
+  void ResetSensor() override;
+
+ private:
+  base::android::ScopedJavaGlobalRef<jobject> j_cardboard_device_;
+
+  unsigned int frame_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(CardboardVRDevice);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_VR_CARDBOARD_VR_DEVICE_H
diff --git a/content/browser/vr/android/cardboard/cardboard_vr_device_provider.cc b/content/browser/vr/android/cardboard/cardboard_vr_device_provider.cc
new file mode 100644
index 0000000..eeb5baf
--- /dev/null
+++ b/content/browser/vr/android/cardboard/cardboard_vr_device_provider.cc
@@ -0,0 +1,29 @@
+// 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/vr/android/cardboard/cardboard_vr_device_provider.h"
+
+#include "content/browser/vr/android/cardboard/cardboard_vr_device.h"
+
+namespace content {
+
+CardboardVRDeviceProvider::CardboardVRDeviceProvider() : VRDeviceProvider() {
+}
+
+CardboardVRDeviceProvider::~CardboardVRDeviceProvider() {
+}
+
+void CardboardVRDeviceProvider::GetDevices(std::vector<VRDevice*>& devices) {
+  if (!cardboard_device_) {
+    cardboard_device_.reset(new CardboardVRDevice(this));
+  }
+
+  devices.push_back(cardboard_device_.get());
+}
+
+void CardboardVRDeviceProvider::Initialize() {
+  // No initialization needed for Cardboard devices.
+}
+
+}  // namespace content
diff --git a/content/browser/vr/android/cardboard/cardboard_vr_device_provider.h b/content/browser/vr/android/cardboard/cardboard_vr_device_provider.h
new file mode 100644
index 0000000..18efa5c
--- /dev/null
+++ b/content/browser/vr/android/cardboard/cardboard_vr_device_provider.h
@@ -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.
+
+#ifndef CONTENT_BROWSER_VR_CARDBOARD_VR_DEVICE_PROVIDER_H
+#define CONTENT_BROWSER_VR_CARDBOARD_VR_DEVICE_PROVIDER_H
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/vr/vr_device.h"
+#include "content/browser/vr/vr_device_provider.h"
+
+namespace content {
+
+class CardboardVRDeviceProvider : public VRDeviceProvider {
+ public:
+  CardboardVRDeviceProvider();
+  ~CardboardVRDeviceProvider() override;
+
+  void GetDevices(std::vector<VRDevice*>& devices) override;
+  void Initialize() override;
+
+ private:
+  scoped_ptr<VRDevice> cardboard_device_;
+
+  DISALLOW_COPY_AND_ASSIGN(CardboardVRDeviceProvider);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_VR_CARDBOARD_VR_DEVICE_PROVIDER_H
diff --git a/content/browser/vr/test/fake_vr_device.cc b/content/browser/vr/test/fake_vr_device.cc
new file mode 100644
index 0000000..7ee5e06
--- /dev/null
+++ b/content/browser/vr/test/fake_vr_device.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 "content/browser/vr/test/fake_vr_device.h"
+
+namespace content {
+
+FakeVRDevice::FakeVRDevice(VRDeviceProvider* provider) : VRDevice(provider) {
+  device_ = VRDeviceInfo::New();
+  state_ = VRSensorState::New();
+}
+
+FakeVRDevice::~FakeVRDevice() {
+}
+
+void FakeVRDevice::SetVRDevice(const VRDeviceInfoPtr& device) {
+  device_ = device.Clone();
+}
+
+void FakeVRDevice::SetSensorState(const VRSensorStatePtr& state) {
+  state_ = state.Clone();
+}
+
+VRDeviceInfoPtr FakeVRDevice::GetVRDevice() {
+  return device_.Clone();
+}
+
+VRSensorStatePtr FakeVRDevice::GetSensorState() {
+  return state_.Clone();
+}
+
+}  // namespace content
\ No newline at end of file
diff --git a/content/browser/vr/test/fake_vr_device.h b/content/browser/vr/test/fake_vr_device.h
new file mode 100644
index 0000000..2cb7384
--- /dev/null
+++ b/content/browser/vr/test/fake_vr_device.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 CONTENT_BROWSER_VR_TEST_FAKE_VR_DEVICE_H_
+#define CONTENT_BROWSER_VR_TEST_FAKE_VR_DEVICE_H_
+
+#include "content/browser/vr/vr_device.h"
+#include "content/browser/vr/vr_device_provider.h"
+
+namespace content {
+
+class FakeVRDevice : public VRDevice {
+ public:
+  explicit FakeVRDevice(VRDeviceProvider* provider);
+  ~FakeVRDevice() override;
+
+  void SetVRDevice(const VRDeviceInfoPtr& device);
+  void SetSensorState(const VRSensorStatePtr& state);
+
+  VRDeviceInfoPtr GetVRDevice() override;
+  VRSensorStatePtr GetSensorState() override;
+  void ResetSensor() override{};
+
+ private:
+  VRDeviceInfoPtr device_;
+  VRSensorStatePtr state_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeVRDevice);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_VR_TEST_FAKE_VR_DEVICE_H_
diff --git a/content/browser/vr/test/fake_vr_device_provider.cc b/content/browser/vr/test/fake_vr_device_provider.cc
new file mode 100644
index 0000000..d69802b
--- /dev/null
+++ b/content/browser/vr/test/fake_vr_device_provider.cc
@@ -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.
+
+#include "content/browser/vr/test/fake_vr_device_provider.h"
+
+namespace content {
+
+FakeVRDeviceProvider::FakeVRDeviceProvider() : VRDeviceProvider() {
+  initialized_ = false;
+}
+
+FakeVRDeviceProvider::~FakeVRDeviceProvider() {
+}
+
+void FakeVRDeviceProvider::AddDevice(VRDevice* device) {
+  devices_.push_back(device);
+}
+
+void FakeVRDeviceProvider::RemoveDevice(VRDevice* device) {
+  std::vector<VRDevice*>::iterator iter = devices_.begin();
+  while (iter != devices_.end()) {
+    if (device == *iter) {
+      iter = devices_.erase(iter);
+    } else {
+      ++iter;
+    }
+  }
+}
+
+void FakeVRDeviceProvider::GetDevices(std::vector<VRDevice*>& devices) {
+  std::vector<VRDevice*>::iterator iter;
+
+  for (auto device : devices_) {
+    devices.push_back(device);
+  }
+}
+
+void FakeVRDeviceProvider::Initialize() {
+  initialized_ = true;
+}
+
+}  // namespace content
\ No newline at end of file
diff --git a/content/browser/vr/test/fake_vr_device_provider.h b/content/browser/vr/test/fake_vr_device_provider.h
new file mode 100644
index 0000000..e5877a3
--- /dev/null
+++ b/content/browser/vr/test/fake_vr_device_provider.h
@@ -0,0 +1,35 @@
+// 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_VR_TEST_FAKE_VR_DEVICE_PROVIDER_H_
+#define CONTENT_BROWSER_VR_TEST_FAKE_VR_DEVICE_PROVIDER_H_
+
+#include <vector>
+#include "content/browser/vr/vr_device.h"
+#include "content/browser/vr/vr_device_provider.h"
+
+namespace content {
+
+class FakeVRDeviceProvider : public VRDeviceProvider {
+ public:
+  FakeVRDeviceProvider();
+  ~FakeVRDeviceProvider() override;
+
+  // Adds devices to the provider with the given device, which will be
+  // returned when GetDevices is queried.
+  void AddDevice(VRDevice* device);
+  void RemoveDevice(VRDevice* device);
+  bool IsInitialized() { return initialized_; }
+
+  void GetDevices(std::vector<VRDevice*>& devices) override;
+  void Initialize() override;
+
+ private:
+  std::vector<VRDevice*> devices_;
+  bool initialized_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_VR_TEST_FAKE_VR_DEVICE_PROVIDER_H_
diff --git a/content/browser/vr/vr_device.cc b/content/browser/vr/vr_device.cc
new file mode 100644
index 0000000..ab7a7bc
--- /dev/null
+++ b/content/browser/vr/vr_device.cc
@@ -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.
+
+#include "content/browser/vr/vr_device.h"
+#include "content/browser/vr/vr_device_provider.h"
+
+namespace content {
+
+unsigned int VRDevice::next_id_ = 1;
+
+VRDevice::VRDevice(VRDeviceProvider* provider)
+    : provider_(provider), id_(next_id_) {
+  // Prevent wraparound. Devices with this ID will be treated as invalid.
+  if (next_id_ != VR_DEVICE_LAST_ID)
+    next_id_++;
+}
+
+VRDevice::~VRDevice() {
+}
+
+}  // namespace content
diff --git a/content/browser/vr/vr_device.h b/content/browser/vr/vr_device.h
new file mode 100644
index 0000000..e3026d73
--- /dev/null
+++ b/content/browser/vr/vr_device.h
@@ -0,0 +1,48 @@
+// 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_VR_VR_DEVICE_H
+#define CONTENT_BROWSER_VR_VR_DEVICE_H
+
+#include "base/macros.h"
+#include "content/common/vr_service.mojom.h"
+
+namespace blink {
+struct WebHMDSensorState;
+}
+
+namespace ui {
+class BaseWindow;
+}
+
+namespace content {
+
+class VRDeviceProvider;
+
+const unsigned int VR_DEVICE_LAST_ID = 0xFFFFFFFF;
+
+class VRDevice {
+ public:
+  explicit VRDevice(VRDeviceProvider* provider);
+  virtual ~VRDevice();
+
+  VRDeviceProvider* provider() const { return provider_; }
+  unsigned int id() const { return id_; }
+
+  virtual VRDeviceInfoPtr GetVRDevice() = 0;
+  virtual VRSensorStatePtr GetSensorState() = 0;
+  virtual void ResetSensor() = 0;
+
+ private:
+  VRDeviceProvider* provider_;
+  unsigned int id_;
+
+  static unsigned int next_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(VRDevice);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_VR_VR_DEVICE_H
diff --git a/content/browser/vr/vr_device_manager.cc b/content/browser/vr/vr_device_manager.cc
new file mode 100644
index 0000000..1451056
--- /dev/null
+++ b/content/browser/vr/vr_device_manager.cc
@@ -0,0 +1,148 @@
+// 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/vr/vr_device_manager.h"
+
+#include "base/memory/singleton.h"
+#include "third_party/WebKit/public/platform/modules/vr/WebVR.h"
+
+#if defined(OS_ANDROID)
+#include "content/browser/vr/android/cardboard/cardboard_vr_device_provider.h"
+#endif
+
+namespace content {
+
+namespace {
+VRDeviceManager* g_vr_device_manager = nullptr;
+}
+
+VRDeviceManager::VRDeviceManager()
+    : vr_initialized_(false), keep_alive_(false) {
+  bindings_.set_error_handler(this);
+
+#if defined(OS_ANDROID)
+  scoped_ptr<VRDeviceProvider> cardboard_provider(
+      new CardboardVRDeviceProvider());
+  RegisterProvider(cardboard_provider.Pass());
+#endif
+}
+
+VRDeviceManager::VRDeviceManager(scoped_ptr<VRDeviceProvider> provider)
+    : vr_initialized_(false), keep_alive_(true) {
+  thread_checker_.DetachFromThread();
+  RegisterProvider(provider.Pass());
+  SetInstance(this);
+}
+
+VRDeviceManager::~VRDeviceManager() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  g_vr_device_manager = nullptr;
+}
+
+void VRDeviceManager::BindRequest(mojo::InterfaceRequest<VRService> request) {
+  VRDeviceManager* device_manager = GetInstance();
+  device_manager->bindings_.AddBinding(device_manager, request.Pass());
+}
+
+void VRDeviceManager::OnConnectionError() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (bindings_.empty() && !keep_alive_) {
+    // Delete the device manager when it has no active connections.
+    delete g_vr_device_manager;
+  }
+}
+
+VRDeviceManager* VRDeviceManager::GetInstance() {
+  if (!g_vr_device_manager)
+    g_vr_device_manager = new VRDeviceManager();
+  return g_vr_device_manager;
+}
+
+void VRDeviceManager::SetInstance(VRDeviceManager* instance) {
+  // Unit tests can create multiple instances but only one should exist at any
+  // given time so g_vr_device_manager should only go from nullptr to
+  // non-nullptr and vica versa.
+  CHECK_NE(!!instance, !!g_vr_device_manager);
+  g_vr_device_manager = instance;
+}
+
+bool VRDeviceManager::HasInstance() {
+  // For testing. Checks to see if a VRDeviceManager instance is active.
+  return !!g_vr_device_manager;
+}
+
+mojo::Array<VRDeviceInfoPtr> VRDeviceManager::GetVRDevices() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  InitializeProviders();
+
+  std::vector<VRDevice*> devices;
+  for (const auto& provider : providers_)
+    provider->GetDevices(devices);
+
+  mojo::Array<VRDeviceInfoPtr> out_devices(0);
+  for (const auto& device : devices) {
+    if (device->id() == VR_DEVICE_LAST_ID)
+      continue;
+
+    if (devices_.find(device->id()) == devices_.end())
+      devices_[device->id()] = device;
+
+    VRDeviceInfoPtr vr_device_info = device->GetVRDevice();
+    if (vr_device_info.is_null())
+      continue;
+
+    vr_device_info->index = device->id();
+    out_devices.push_back(vr_device_info.Pass());
+  }
+
+  return out_devices;
+}
+
+VRDevice* VRDeviceManager::GetDevice(unsigned int index) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  DeviceMap::iterator iter = devices_.find(index);
+  if (iter == devices_.end()) {
+    return nullptr;
+  }
+  return iter->second;
+}
+
+void VRDeviceManager::InitializeProviders() {
+  if (vr_initialized_) {
+    return;
+  }
+
+  for (const auto& provider : providers_)
+    provider->Initialize();
+
+  vr_initialized_ = true;
+}
+
+void VRDeviceManager::RegisterProvider(scoped_ptr<VRDeviceProvider> provider) {
+  providers_.push_back(make_linked_ptr(provider.release()));
+}
+
+void VRDeviceManager::GetDevices(const GetDevicesCallback& callback) {
+  callback.Run(GetVRDevices());
+}
+
+void VRDeviceManager::GetSensorState(uint32_t index,
+                                     const GetSensorStateCallback& callback) {
+  VRDevice* device = GetDevice(index);
+  if (device) {
+    callback.Run(device->GetSensorState());
+  } else {
+    callback.Run(nullptr);
+  }
+}
+
+void VRDeviceManager::ResetSensor(uint32_t index) {
+  VRDevice* device = GetDevice(index);
+  if (device)
+    device->ResetSensor();
+}
+
+}  // namespace content
diff --git a/content/browser/vr/vr_device_manager.h b/content/browser/vr/vr_device_manager.h
new file mode 100644
index 0000000..6c8dc96
--- /dev/null
+++ b/content/browser/vr/vr_device_manager.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 CONTENT_BROWSER_VR_VR_DEVICE_MANAGER_H
+#define CONTENT_BROWSER_VR_VR_DEVICE_MANAGER_H
+
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "content/browser/vr/vr_device.h"
+#include "content/browser/vr/vr_device_provider.h"
+#include "content/common/content_export.h"
+#include "content/common/vr_service.mojom.h"
+#include "mojo/common/weak_binding_set.h"
+
+namespace content {
+
+class VRDeviceManager : public VRService, public mojo::ErrorHandler {
+ public:
+  ~VRDeviceManager() override;
+
+  static void BindRequest(mojo::InterfaceRequest<VRService> request);
+
+  // Returns the VRDeviceManager singleton.
+  static VRDeviceManager* GetInstance();
+
+  mojo::Array<VRDeviceInfoPtr> GetVRDevices();
+  VRDevice* GetDevice(unsigned int index);
+
+ private:
+  friend class VRDeviceManagerTest;
+
+  VRDeviceManager();
+  // Constructor for testing.
+  explicit VRDeviceManager(scoped_ptr<VRDeviceProvider> provider);
+
+  static void SetInstance(VRDeviceManager* service);
+  static bool HasInstance();
+
+  void InitializeProviders();
+  void RegisterProvider(scoped_ptr<VRDeviceProvider> provider);
+
+  // VRService implementation
+  void GetDevices(const GetDevicesCallback& callback) override;
+  void GetSensorState(uint32_t index,
+                      const GetSensorStateCallback& callback) override;
+  void ResetSensor(uint32_t index) override;
+
+  // mojo::ErrorHandler implementation
+  void OnConnectionError() override;
+
+  using ProviderList = std::vector<linked_ptr<VRDeviceProvider>>;
+  ProviderList providers_;
+
+  // Devices are owned by their providers.
+  using DeviceMap = std::map<unsigned int, VRDevice*>;
+  DeviceMap devices_;
+
+  bool vr_initialized_;
+
+  mojo::WeakBindingSet<VRService> bindings_;
+
+  // For testing. If true will not delete self when consumer count reaches 0.
+  bool keep_alive_;
+
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(VRDeviceManager);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_VR_VR_DEVICE_MANAGER_H
diff --git a/content/browser/vr/vr_device_manager_unittest.cc b/content/browser/vr/vr_device_manager_unittest.cc
new file mode 100644
index 0000000..873a296
--- /dev/null
+++ b/content/browser/vr/vr_device_manager_unittest.cc
@@ -0,0 +1,96 @@
+// 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/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/vr/test/fake_vr_device.h"
+#include "content/browser/vr/test/fake_vr_device_provider.h"
+#include "content/browser/vr/vr_device_manager.h"
+#include "content/browser/vr/vr_device_provider.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class VRDeviceManagerTest : public testing::Test {
+ protected:
+  VRDeviceManagerTest();
+  ~VRDeviceManagerTest() override;
+
+  void SetUp() override;
+
+  bool HasServiceInstance() { return VRDeviceManager::HasInstance(); }
+
+ protected:
+  FakeVRDeviceProvider* provider_;
+  scoped_ptr<VRDeviceManager> device_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(VRDeviceManagerTest);
+};
+
+VRDeviceManagerTest::VRDeviceManagerTest() {
+}
+
+VRDeviceManagerTest::~VRDeviceManagerTest() {
+}
+
+void VRDeviceManagerTest::SetUp() {
+  scoped_ptr<FakeVRDeviceProvider> provider(new FakeVRDeviceProvider());
+  provider_ = provider.get();
+  device_manager_.reset(new VRDeviceManager(provider.Pass()));
+}
+
+TEST_F(VRDeviceManagerTest, InitializationTest) {
+  EXPECT_FALSE(provider_->IsInitialized());
+
+  // Calling GetDevices should initialize the service if it hasn't been
+  // initialized yet or the providesr have been released.
+  // The VRService should initialize each of it's providers upon it's own
+  // initialization.
+  mojo::Array<VRDeviceInfoPtr> webvr_devices;
+  webvr_devices = device_manager_->GetVRDevices();
+  EXPECT_TRUE(provider_->IsInitialized());
+}
+
+TEST_F(VRDeviceManagerTest, GetDevicesBasicTest) {
+  mojo::Array<VRDeviceInfoPtr> webvr_devices;
+  webvr_devices = device_manager_->GetVRDevices();
+  // Calling GetVRDevices should initialize the providers.
+  EXPECT_TRUE(provider_->IsInitialized());
+  // Should successfully return zero devices when none are available.
+  EXPECT_EQ(0u, webvr_devices.size());
+
+  // GetDeviceByIndex should return nullptr if an invalid index in queried.
+  VRDevice* queried_device = device_manager_->GetDevice(1);
+  EXPECT_EQ(nullptr, queried_device);
+
+  scoped_ptr<FakeVRDevice> device1(new FakeVRDevice(provider_));
+  provider_->AddDevice(device1.get());
+  webvr_devices = device_manager_->GetVRDevices();
+  // Should have successfully returned one device.
+  EXPECT_EQ(1u, webvr_devices.size());
+  // The WebVRDevice index should match the device id.
+  EXPECT_EQ(webvr_devices[0]->index, device1->id());
+
+  scoped_ptr<FakeVRDevice> device2(new FakeVRDevice(provider_));
+  provider_->AddDevice(device2.get());
+  webvr_devices = device_manager_->GetVRDevices();
+  // Should have successfully returned two devices.
+  EXPECT_EQ(2u, webvr_devices.size());
+  // NOTE: Returned WebVRDevices are not required to be in any particular order.
+
+  // Querying the WebVRDevice index should return the correct device.
+  queried_device = device_manager_->GetDevice(device1->id());
+  EXPECT_EQ(device1.get(), queried_device);
+  queried_device = device_manager_->GetDevice(device2->id());
+  EXPECT_EQ(device2.get(), queried_device);
+
+  provider_->RemoveDevice(device1.get());
+  webvr_devices = device_manager_->GetVRDevices();
+  // Should have successfully returned one device.
+  EXPECT_EQ(1u, webvr_devices.size());
+  // The WebVRDevice index should match the only remaining device id.
+  EXPECT_EQ(webvr_devices[0]->index, device2->id());
+}
+
+}  // namespace content
diff --git a/content/browser/vr/vr_device_provider.h b/content/browser/vr/vr_device_provider.h
new file mode 100644
index 0000000..7a0207c
--- /dev/null
+++ b/content/browser/vr/vr_device_provider.h
@@ -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.
+
+#ifndef CONTENT_BROWSER_VR_VR_DEVICE_PROVIDER_H
+#define CONTENT_BROWSER_VR_VR_DEVICE_PROVIDER_H
+
+#include <vector>
+
+namespace content {
+
+class VRDevice;
+
+class VRDeviceProvider {
+ public:
+  VRDeviceProvider() {}
+  virtual ~VRDeviceProvider() {}
+
+  virtual void GetDevices(std::vector<VRDevice*>& devices) = 0;
+
+  // If the VR API requires initialization that should happen here.
+  virtual void Initialize() = 0;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_VR_VR_DEVICE_PROVIDER_H
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 1e31837..bb74c49 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3935,8 +3935,7 @@
   return GetBrowserPluginGuest() || GetBrowserPluginEmbedder();
 }
 
-int WebContentsImpl::EnsureOpenerRenderViewsExist(
-    RenderFrameHost* source_rfh) {
+void WebContentsImpl::EnsureOpenerProxiesExist(RenderFrameHost* source_rfh) {
   WebContentsImpl* source_web_contents = static_cast<WebContentsImpl*>(
       WebContents::FromRenderFrameHost(source_rfh));
 
@@ -3945,14 +3944,14 @@
       // We create a swapped out RenderView for the embedder in the guest's
       // render process but we intentionally do not expose the embedder's
       // opener chain to it.
-      return
-          source_web_contents->CreateSwappedOutRenderView(GetSiteInstance());
+      source_web_contents->CreateSwappedOutRenderView(GetSiteInstance());
     } else {
-      return source_web_contents->CreateOpenerRenderViews(GetSiteInstance());
+      RenderFrameHostImpl* source_rfhi =
+          static_cast<RenderFrameHostImpl*>(source_rfh);
+      source_rfhi->frame_tree_node()->render_manager()->CreateOpenerProxies(
+          GetSiteInstance());
     }
   }
-
-  return MSG_ROUTING_NONE;
 }
 
 bool WebContentsImpl::AddMessageToConsole(int32 level,
@@ -4133,58 +4132,6 @@
   NotifyViewSwapped(old_host, new_host);
 }
 
-int WebContentsImpl::CreateOpenerRenderViewsForRenderManager(
-    SiteInstance* instance) {
-  WebContentsImpl* opener = GetOpener();
-  if (!opener)
-    return MSG_ROUTING_NONE;
-
-  // Recursively create RenderViews for anything else in the opener chain.
-  return opener->CreateOpenerRenderViews(instance);
-}
-
-int WebContentsImpl::CreateOpenerRenderViews(SiteInstance* instance) {
-  int opener_route_id = MSG_ROUTING_NONE;
-
-  // If this tab has an opener, ensure it has a RenderView in the given
-  // SiteInstance as well.
-  WebContentsImpl* opener = GetOpener();
-  if (opener)
-    opener_route_id = opener->CreateOpenerRenderViews(instance);
-
-  // If any of the renderers (current, pending, or swapped out) for this
-  // WebContents has the same SiteInstance, use it.
-  if (GetRenderManager()->current_host()->GetSiteInstance() == instance)
-    return GetRenderManager()->current_host()->GetRoutingID();
-
-  if (GetRenderManager()->pending_render_view_host() &&
-      GetRenderManager()->pending_render_view_host()->GetSiteInstance() ==
-          instance)
-    return GetRenderManager()->pending_render_view_host()->GetRoutingID();
-
-  RenderViewHostImpl* rvh = GetRenderManager()->GetSwappedOutRenderViewHost(
-      instance);
-  if (rvh)
-    return rvh->GetRoutingID();
-
-  // Create a swapped out RenderView in the given SiteInstance if none exists,
-  // setting its opener to the given route_id.  Return the new view's route_id.
-  int render_view_routing_id = MSG_ROUTING_NONE;
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kSitePerProcess)) {
-    GetRenderManager()->CreateRenderFrameProxy(instance);
-    render_view_routing_id =
-        frame_tree_.GetRenderViewHost(instance)->GetRoutingID();
-  } else {
-    GetRenderManager()->CreateRenderFrame(instance, nullptr, opener_route_id,
-                                          CREATE_RF_FOR_MAIN_FRAME_NAVIGATION |
-                                            CREATE_RF_SWAPPED_OUT |
-                                            CREATE_RF_HIDDEN,
-                                          &render_view_routing_id);
-  }
-  return render_view_routing_id;
-}
-
 NavigationControllerImpl& WebContentsImpl::GetControllerForRenderManager() {
   return GetController();
 }
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 005b591..75f3e63e 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -417,7 +417,7 @@
   bool ShouldRouteMessageEvent(
       RenderFrameHost* target_rfh,
       SiteInstance* source_site_instance) const override;
-  int EnsureOpenerRenderViewsExist(RenderFrameHost* source_rfh) override;
+  void EnsureOpenerProxiesExist(RenderFrameHost* source_rfh) override;
 #if defined(OS_WIN)
   gfx::NativeViewAccessible GetParentNativeViewAccessible() override;
 #endif
@@ -587,7 +587,6 @@
   void NotifyMainFrameSwappedFromRenderManager(
       RenderViewHost* old_host,
       RenderViewHost* new_host) override;
-  int CreateOpenerRenderViewsForRenderManager(SiteInstance* instance) override;
   NavigationControllerImpl& GetControllerForRenderManager() override;
   scoped_ptr<WebUIImpl> CreateWebUIForRenderManager(const GURL& url) override;
   NavigationEntry* GetLastCommittedNavigationEntryForRenderManager() override;
@@ -883,12 +882,6 @@
   bool UpdateTitleForEntry(NavigationEntryImpl* entry,
                            const base::string16& title);
 
-  // Recursively creates swapped out RenderViews for this tab's opener chain
-  // (including this tab) in the given SiteInstance, allowing other tabs to send
-  // cross-process JavaScript calls to their opener(s).  Returns the route ID of
-  // this tab's RenderView for |instance|.
-  int CreateOpenerRenderViews(SiteInstance* instance);
-
   // Helper for CreateNewWidget/CreateNewFullscreenWidget.
   void CreateNewWidget(int render_process_id,
                        int route_id,
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 309a91c3..7bd11f6 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -909,10 +909,10 @@
   TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
 
   // While it is still pending, simulate opening a new tab with the first tab
-  // as its opener.  This will call WebContentsImpl::CreateOpenerRenderViews
-  // on the opener to ensure that an RVH exists.
-  int opener_routing_id =
-      contents()->CreateOpenerRenderViews(pending_rfh->GetSiteInstance());
+  // as its opener.  This will call CreateOpenerProxies on the opener to ensure
+  // that an RVH exists.
+  int opener_routing_id = contents()->GetRenderManager()->CreateOpenerProxies(
+      pending_rfh->GetSiteInstance());
 
   // We should find the pending RVH and not create a new one.
   EXPECT_EQ(pending_rfh->GetRenderViewHost()->GetRoutingID(),
diff --git a/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
index e9ba331..e8e16780 100644
--- a/content/browser/web_contents/web_contents_view_aura_browsertest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
@@ -259,6 +259,9 @@
 
     screenshot_manager_ = new ScreenshotTracker(controller);
     controller->SetScreenshotManager(screenshot_manager_);
+
+    frame_watcher_ = new FrameWatcher();
+    GetRenderWidgetHost()->GetProcess()->AddFilter(frame_watcher_.get());
   }
 
   void SetUpCommandLine(base::CommandLine* cmd) override {
@@ -401,11 +404,9 @@
   }
 
   void WaitAFrame() {
-    uint32 frame = GetRenderWidgetHostView()->RendererFrameNumber();
     while (!GetRenderWidgetHost()->ScheduleComposite())
       GiveItSomeTime();
-    while (GetRenderWidgetHostView()->RendererFrameNumber() == frame)
-      GiveItSomeTime();
+    frame_watcher_->WaitFrames(1);
   }
 
  protected:
@@ -422,6 +423,7 @@
  private:
   ScreenshotTracker* screenshot_manager_;
   scoped_refptr<InputEventMessageFilterWaitsForAcks> filter_;
+  scoped_refptr<FrameWatcher> frame_watcher_;
 
   DISALLOW_COPY_AND_ASSIGN(WebContentsViewAuraTest);
 };
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 78939bb0..ec1e63c4 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -28,6 +28,7 @@
 #include "base/thread_task_runner_handle.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
+#include "base/trace_event/memory_allocator_dump_guid.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "blink/public/resources/grit/blink_image_resources.h"
 #include "blink/public/resources/grit/blink_resources.h"
@@ -734,6 +735,12 @@
   return new WebProcessMemoryDumpImpl();
 }
 
+blink::Platform::WebMemoryAllocatorDumpGuid
+BlinkPlatformImpl::createWebMemoryAllocatorDumpGuid(
+    const blink::WebString& guidStr) {
+  return base::trace_event::MemoryAllocatorDumpGuid(guidStr.utf8()).ToUint64();
+}
+
 namespace {
 
 WebData loadAudioSpatializationResource(const char* name) {
diff --git a/content/child/blink_platform_impl.h b/content/child/blink_platform_impl.h
index 178bfb6..c96951e 100644
--- a/content/child/blink_platform_impl.h
+++ b/content/child/blink_platform_impl.h
@@ -137,6 +137,8 @@
   virtual void registerMemoryDumpProvider(blink::WebMemoryDumpProvider* wmdp);
   virtual void unregisterMemoryDumpProvider(blink::WebMemoryDumpProvider* wmdp);
   virtual blink::WebProcessMemoryDump* createProcessMemoryDump();
+  virtual blink::Platform::WebMemoryAllocatorDumpGuid
+  createWebMemoryAllocatorDumpGuid(const blink::WebString& guidStr);
 
   virtual blink::WebData loadResource(const char* name);
   virtual blink::WebString queryLocalizedString(
diff --git a/content/child/child_process.cc b/content/child/child_process.cc
index e4da856..01675b3 100644
--- a/content/child/child_process.cc
+++ b/content/child/child_process.cc
@@ -44,12 +44,11 @@
   base::StatisticsRecorder::Initialize();
 
   // We can't recover from failing to start the IO thread.
-  CHECK(io_thread_.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
-
+  base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
 #if defined(OS_ANDROID)
-  io_thread_.SetPriority(base::ThreadPriority::DISPLAY);
+  thread_options.priority = base::ThreadPriority::DISPLAY;
 #endif
+  CHECK(io_thread_.StartWithOptions(thread_options));
 }
 
 ChildProcess::~ChildProcess() {
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index 5192c3a..04e020c 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -312,10 +312,9 @@
     VLOG(1) << "Mojo is enabled on child";
     scoped_refptr<base::SequencedTaskRunner> io_task_runner = GetIOTaskRunner();
     DCHECK(io_task_runner);
-    channel_->Init(
-        IPC::ChannelMojo::CreateClientFactory(
-            nullptr, io_task_runner, channel_name_, attachment_broker_.get()),
-        create_pipe_now);
+    channel_->Init(IPC::ChannelMojo::CreateClientFactory(
+                       io_task_runner, channel_name_, attachment_broker_.get()),
+                   create_pipe_now);
     return;
   }
 
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 5ce09de..8309415 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -203,6 +203,11 @@
   if (command_line.HasSwitch(switches::kEnableUnsafeES3APIs))
     WebRuntimeFeatures::enableUnsafeES3APIs(true);
 
+  if (command_line.HasSwitch(switches::kEnableWebVR)) {
+    WebRuntimeFeatures::enableWebVR(true);
+    WebRuntimeFeatures::enableFeatureFromString("GeometryInterfaces", true);
+  }
+
   // Enable explicitly enabled features, and then disable explicitly disabled
   // ones.
   if (command_line.HasSwitch(switches::kEnableBlinkFeatures)) {
diff --git a/content/child/service_worker/service_worker_dispatcher.cc b/content/child/service_worker/service_worker_dispatcher.cc
index 2c921a2..f280581 100644
--- a/content/child/service_worker/service_worker_dispatcher.cc
+++ b/content/child/service_worker/service_worker_dispatcher.cc
@@ -138,29 +138,15 @@
 
 void ServiceWorkerDispatcher::UnregisterServiceWorker(
     int provider_id,
-    const GURL& pattern,
+    int64 registration_id,
     WebServiceWorkerUnregistrationCallbacks* callbacks) {
   DCHECK(callbacks);
-
-  if (pattern.possibly_invalid_spec().size() > GetMaxURLChars()) {
-    scoped_ptr<WebServiceWorkerUnregistrationCallbacks>
-        owned_callbacks(callbacks);
-    std::string error_message(kServiceWorkerUnregisterErrorPrefix);
-    error_message += "The provided scope is too long.";
-    scoped_ptr<WebServiceWorkerError> error(
-        new WebServiceWorkerError(WebServiceWorkerError::ErrorTypeSecurity,
-                                  blink::WebString::fromUTF8(error_message)));
-    callbacks->onError(error.release());
-    return;
-  }
-
   int request_id = pending_unregistration_callbacks_.Add(callbacks);
   TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
                            "ServiceWorkerDispatcher::UnregisterServiceWorker",
-                           request_id,
-                           "Scope", pattern.spec());
+                           request_id, "Registration ID", registration_id);
   thread_safe_sender_->Send(new ServiceWorkerHostMsg_UnregisterServiceWorker(
-      CurrentWorkerId(), request_id, provider_id, pattern));
+      CurrentWorkerId(), request_id, provider_id, registration_id));
 }
 
 void ServiceWorkerDispatcher::GetRegistration(
diff --git a/content/child/service_worker/service_worker_dispatcher.h b/content/child/service_worker/service_worker_dispatcher.h
index 4f33a60..e67aea53 100644
--- a/content/child/service_worker/service_worker_dispatcher.h
+++ b/content/child/service_worker/service_worker_dispatcher.h
@@ -46,8 +46,7 @@
  public:
   typedef blink::WebServiceWorkerProvider::WebServiceWorkerRegistrationCallbacks
       WebServiceWorkerRegistrationCallbacks;
-  typedef
-      blink::WebServiceWorkerProvider::WebServiceWorkerUnregistrationCallbacks
+  typedef blink::WebCallbacks<bool, blink::WebServiceWorkerError>
       WebServiceWorkerUnregistrationCallbacks;
   typedef
       blink::WebServiceWorkerProvider::WebServiceWorkerGetRegistrationCallbacks
@@ -76,7 +75,7 @@
   // Corresponds to ServiceWorkerRegistration.unregister().
   void UnregisterServiceWorker(
       int provider_id,
-      const GURL& pattern,
+      int64 registration_id,
       WebServiceWorkerUnregistrationCallbacks* callbacks);
   // Corresponds to navigator.serviceWorker.getRegistration().
   void GetRegistration(
diff --git a/content/child/service_worker/web_service_worker_provider_impl.cc b/content/child/service_worker/web_service_worker_provider_impl.cc
index 0485d68d..70285faa 100644
--- a/content/child/service_worker/web_service_worker_provider_impl.cc
+++ b/content/child/service_worker/web_service_worker_provider_impl.cc
@@ -57,13 +57,6 @@
       context_->provider_id(), pattern, script_url, callbacks);
 }
 
-void WebServiceWorkerProviderImpl::unregisterServiceWorker(
-    const WebURL& pattern,
-    WebServiceWorkerUnregistrationCallbacks* callbacks) {
-  GetDispatcher()->UnregisterServiceWorker(
-      context_->provider_id(), pattern, callbacks);
-}
-
 void WebServiceWorkerProviderImpl::getRegistration(
     const blink::WebURL& document_url,
     WebServiceWorkerRegistrationCallbacks* callbacks) {
diff --git a/content/child/service_worker/web_service_worker_provider_impl.h b/content/child/service_worker/web_service_worker_provider_impl.h
index 57722cd..3d607bf 100644
--- a/content/child/service_worker/web_service_worker_provider_impl.h
+++ b/content/child/service_worker/web_service_worker_provider_impl.h
@@ -34,11 +34,6 @@
   virtual void registerServiceWorker(const blink::WebURL& pattern,
                                      const blink::WebURL& script_url,
                                      WebServiceWorkerRegistrationCallbacks*);
-
-  virtual void unregisterServiceWorker(
-      const blink::WebURL& pattern,
-      WebServiceWorkerUnregistrationCallbacks*);
-
   virtual void getRegistration(const blink::WebURL& document_url,
                                WebServiceWorkerGetRegistrationCallbacks*);
   virtual void getRegistrations(WebServiceWorkerGetRegistrationsCallbacks*);
diff --git a/content/child/service_worker/web_service_worker_registration_impl.cc b/content/child/service_worker/web_service_worker_registration_impl.cc
index 40364c1ea6..99addd0 100644
--- a/content/child/service_worker/web_service_worker_registration_impl.cc
+++ b/content/child/service_worker/web_service_worker_registration_impl.cc
@@ -124,6 +124,18 @@
                                   registration_id());
 }
 
+void WebServiceWorkerRegistrationImpl::unregister(
+    blink::WebServiceWorkerProvider* provider,
+    WebServiceWorkerUnregistrationCallbacks* callbacks) {
+  WebServiceWorkerProviderImpl* provider_impl =
+      static_cast<WebServiceWorkerProviderImpl*>(provider);
+  ServiceWorkerDispatcher* dispatcher =
+      ServiceWorkerDispatcher::GetThreadSpecificInstance();
+  DCHECK(dispatcher);
+  dispatcher->UnregisterServiceWorker(provider_impl->provider_id(),
+                                      registration_id(), callbacks);
+}
+
 int64 WebServiceWorkerRegistrationImpl::registration_id() const {
   return handle_ref_->registration_id();
 }
diff --git a/content/child/service_worker/web_service_worker_registration_impl.h b/content/child/service_worker/web_service_worker_registration_impl.h
index 9bf5dc3..67ecc1a 100644
--- a/content/child/service_worker/web_service_worker_registration_impl.h
+++ b/content/child/service_worker/web_service_worker_registration_impl.h
@@ -40,6 +40,8 @@
   virtual blink::WebServiceWorkerRegistrationProxy* proxy();
   virtual blink::WebURL scope() const;
   virtual void update(blink::WebServiceWorkerProvider* provider);
+  virtual void unregister(blink::WebServiceWorkerProvider* provider,
+                          WebServiceWorkerUnregistrationCallbacks* callbacks);
 
   int64 registration_id() const;
 
diff --git a/content/child/shared_memory_data_consumer_handle.cc b/content/child/shared_memory_data_consumer_handle.cc
index 24db0590..71804dd 100644
--- a/content/child/shared_memory_data_consumer_handle.cc
+++ b/content/child/shared_memory_data_consumer_handle.cc
@@ -365,4 +365,8 @@
   }
 }
 
+const char* SharedMemoryDataConsumerHandle::debugName() const {
+  return "SharedMemoryDataConsumerHandle";
+}
+
 }  // namespace content
diff --git a/content/child/shared_memory_data_consumer_handle.h b/content/child/shared_memory_data_consumer_handle.h
index 188d028..4987e3b 100644
--- a/content/child/shared_memory_data_consumer_handle.h
+++ b/content/child/shared_memory_data_consumer_handle.h
@@ -69,6 +69,7 @@
 
  private:
   virtual ReaderImpl* obtainReaderInternal(Client* client);
+  const char* debugName() const override;
   void LockImplicitly();
   void UnlockImplicitly();
 
diff --git a/content/child/web_data_consumer_handle_impl.cc b/content/child/web_data_consumer_handle_impl.cc
index 5f1e95b..fff3110 100644
--- a/content/child/web_data_consumer_handle_impl.cc
+++ b/content/child/web_data_consumer_handle_impl.cc
@@ -134,4 +134,8 @@
   return new ReaderImpl(context_, client);
 }
 
+const char* WebDataConsumerHandleImpl::debugName() const {
+  return "WebDataConsumerHandleImpl";
+}
+
 }  // namespace content
diff --git a/content/child/web_data_consumer_handle_impl.h b/content/child/web_data_consumer_handle_impl.h
index 167fe40..78f20f6a 100644
--- a/content/child/web_data_consumer_handle_impl.h
+++ b/content/child/web_data_consumer_handle_impl.h
@@ -45,6 +45,7 @@
 
  private:
   virtual ReaderImpl* obtainReaderInternal(Client* client);
+  const char* debugName() const override;
 
   scoped_refptr<Context> context_;
 };
diff --git a/content/child/web_memory_allocator_dump_impl.cc b/content/child/web_memory_allocator_dump_impl.cc
index 81e66ea..a3fadeb 100644
--- a/content/child/web_memory_allocator_dump_impl.cc
+++ b/content/child/web_memory_allocator_dump_impl.cc
@@ -10,7 +10,8 @@
 
 WebMemoryAllocatorDumpImpl::WebMemoryAllocatorDumpImpl(
     base::trace_event::MemoryAllocatorDump* memory_allocator_dump)
-    : memory_allocator_dump_(memory_allocator_dump) {
+    : memory_allocator_dump_(memory_allocator_dump),
+      guid_(memory_allocator_dump->guid().ToUint64()) {
 }
 
 WebMemoryAllocatorDumpImpl::~WebMemoryAllocatorDumpImpl() {
@@ -33,4 +34,8 @@
                                            const blink::WebString& value) {
   memory_allocator_dump_->AddString(name, units, value.utf8());
 }
+
+blink::WebMemoryAllocatorDumpGuid WebMemoryAllocatorDumpImpl::guid() const {
+  return guid_;
+}
 }  // namespace content
diff --git a/content/child/web_memory_allocator_dump_impl.h b/content/child/web_memory_allocator_dump_impl.h
index 23b7155..d5d97d39 100644
--- a/content/child/web_memory_allocator_dump_impl.h
+++ b/content/child/web_memory_allocator_dump_impl.h
@@ -36,8 +36,11 @@
                          const char* units,
                          const blink::WebString& value);
 
+  virtual blink::WebMemoryAllocatorDumpGuid guid() const;
+
  private:
   base::trace_event::MemoryAllocatorDump* memory_allocator_dump_;  // Not owned.
+  blink::WebMemoryAllocatorDumpGuid guid_;
 
   DISALLOW_COPY_AND_ASSIGN(WebMemoryAllocatorDumpImpl);
 };
diff --git a/content/child/web_process_memory_dump_impl.cc b/content/child/web_process_memory_dump_impl.cc
index 3098d13..688b6c1 100644
--- a/content/child/web_process_memory_dump_impl.cc
+++ b/content/child/web_process_memory_dump_impl.cc
@@ -29,6 +29,25 @@
   // Get a MemoryAllocatorDump from the base/ object.
   base::trace_event::MemoryAllocatorDump* memory_allocator_dump =
       process_memory_dump_->CreateAllocatorDump(absolute_name.utf8());
+
+  return createWebMemoryAllocatorDump(memory_allocator_dump);
+}
+
+blink::WebMemoryAllocatorDump*
+WebProcessMemoryDumpImpl::createMemoryAllocatorDump(
+    const blink::WebString& absolute_name,
+    blink::WebMemoryAllocatorDumpGuid guid) {
+  // Get a MemoryAllocatorDump from the base/ object with given guid.
+  base::trace_event::MemoryAllocatorDump* memory_allocator_dump =
+      process_memory_dump_->CreateAllocatorDump(
+          absolute_name.utf8(),
+          base::trace_event::MemoryAllocatorDumpGuid(guid));
+  return createWebMemoryAllocatorDump(memory_allocator_dump);
+}
+
+blink::WebMemoryAllocatorDump*
+WebProcessMemoryDumpImpl::createWebMemoryAllocatorDump(
+    base::trace_event::MemoryAllocatorDump* memory_allocator_dump) {
   if (!memory_allocator_dump)
     return nullptr;
 
@@ -40,7 +59,6 @@
   // |web_memory_allocator_dumpd_impl|.
   memory_allocator_dumps_.set(memory_allocator_dump,
                               make_scoped_ptr(web_memory_allocator_dump_impl));
-
   return web_memory_allocator_dump_impl;
 }
 
@@ -97,4 +115,21 @@
   DCHECK(other_impl->memory_allocator_dumps_.empty());
 }
 
+void WebProcessMemoryDumpImpl::AddOwnershipEdge(
+    blink::WebMemoryAllocatorDumpGuid source,
+    blink::WebMemoryAllocatorDumpGuid target,
+    int importance) {
+  process_memory_dump_->AddOwnershipEdge(
+      base::trace_event::MemoryAllocatorDumpGuid(source),
+      base::trace_event::MemoryAllocatorDumpGuid(target), importance);
+}
+
+void WebProcessMemoryDumpImpl::AddOwnershipEdge(
+    blink::WebMemoryAllocatorDumpGuid source,
+    blink::WebMemoryAllocatorDumpGuid target) {
+  process_memory_dump_->AddOwnershipEdge(
+      base::trace_event::MemoryAllocatorDumpGuid(source),
+      base::trace_event::MemoryAllocatorDumpGuid(target));
+}
+
 }  // namespace content
diff --git a/content/child/web_process_memory_dump_impl.h b/content/child/web_process_memory_dump_impl.h
index 8882728..221db61 100644
--- a/content/child/web_process_memory_dump_impl.h
+++ b/content/child/web_process_memory_dump_impl.h
@@ -41,10 +41,18 @@
   // blink::WebProcessMemoryDump implementation.
   virtual blink::WebMemoryAllocatorDump* createMemoryAllocatorDump(
       const blink::WebString& absolute_name);
+  virtual blink::WebMemoryAllocatorDump* createMemoryAllocatorDump(
+      const blink::WebString& absolute_name,
+      blink::WebMemoryAllocatorDumpGuid guid);
   virtual blink::WebMemoryAllocatorDump* getMemoryAllocatorDump(
       const blink::WebString& absolute_name) const;
   virtual void clear();
   virtual void takeAllDumpsFrom(blink::WebProcessMemoryDump* other);
+  virtual void AddOwnershipEdge(blink::WebMemoryAllocatorDumpGuid source,
+                                blink::WebMemoryAllocatorDumpGuid target,
+                                int importance);
+  virtual void AddOwnershipEdge(blink::WebMemoryAllocatorDumpGuid source,
+                                blink::WebMemoryAllocatorDumpGuid target);
 
   const base::trace_event::ProcessMemoryDump* process_memory_dump() const {
     return process_memory_dump_;
@@ -53,6 +61,9 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(WebProcessMemoryDumpImplTest, IntegrationTest);
 
+  blink::WebMemoryAllocatorDump* createWebMemoryAllocatorDump(
+      base::trace_event::MemoryAllocatorDump* memory_allocator_dump);
+
   // Only for the case of ProcessMemoryDump being owned (i.e. the default ctor).
   scoped_ptr<base::trace_event::ProcessMemoryDump> owned_process_memory_dump_;
 
diff --git a/content/child/web_process_memory_dump_impl_unittest.cc b/content/child/web_process_memory_dump_impl_unittest.cc
index 40d6aed..1f6c79d7 100644
--- a/content/child/web_process_memory_dump_impl_unittest.cc
+++ b/content/child/web_process_memory_dump_impl_unittest.cc
@@ -102,6 +102,22 @@
   traced_value = new base::trace_event::TracedValue();
   wpmd1->process_memory_dump()->AsValueInto(traced_value.get());
 
+  // Check if a WebMemoryAllocatorDump created with guid, has correct guid.
+  blink::WebMemoryAllocatorDumpGuid guid =
+      base::trace_event::MemoryAllocatorDumpGuid("id_1").ToUint64();
+  auto wmad3 = wpmd1->createMemoryAllocatorDump("1/3", guid);
+  ASSERT_EQ(wmad3->guid(), guid);
+  ASSERT_EQ(wmad3, wpmd1->getMemoryAllocatorDump("1/3"));
+
+  // Check that AddOwnershipEdge is propagated correctly.
+  auto wmad4 = wpmd1->createMemoryAllocatorDump("1/4");
+  wpmd1->AddOwnershipEdge(wmad4->guid(), guid);
+  auto allocator_dumps_edges =
+      wpmd1->process_memory_dump()->allocator_dumps_edges();
+  ASSERT_EQ(1u, allocator_dumps_edges.size());
+  ASSERT_EQ(wmad4->guid(), allocator_dumps_edges[0].source.ToUint64());
+  ASSERT_EQ(guid, allocator_dumps_edges[0].target.ToUint64());
+
   wpmd1.reset();
 }
 
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 09f7a4a..9f9c1e91 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -382,6 +382,10 @@
     if (current_cpu != "arm") {
       sources += [
                    "gpu/media/va_surface.h",
+                   "gpu/media/vaapi_jpeg_decode_accelerator.cc",
+                   "gpu/media/vaapi_jpeg_decode_accelerator.h",
+                   "gpu/media/vaapi_jpeg_decoder.cc",
+                   "gpu/media/vaapi_jpeg_decoder.h",
                    "gpu/media/vaapi_picture.cc",
                    "gpu/media/vaapi_picture.h",
                    "gpu/media/vaapi_video_decode_accelerator.cc",
@@ -488,6 +492,7 @@
     "presentation/presentation_service.mojom",
     "process_control.mojom",
     "render_frame_setup.mojom",
+    "vr_service.mojom",
   ]
 
   import_dirs = [ "//mojo/services" ]
diff --git a/content/common/cc_messages.h b/content/common/cc_messages.h
index 79a3169..938dcc4 100644
--- a/content/common/cc_messages.h
+++ b/content/common/cc_messages.h
@@ -264,9 +264,9 @@
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(cc::SharedQuadState)
-  IPC_STRUCT_TRAITS_MEMBER(content_to_target_transform)
-  IPC_STRUCT_TRAITS_MEMBER(content_bounds)
-  IPC_STRUCT_TRAITS_MEMBER(visible_content_rect)
+IPC_STRUCT_TRAITS_MEMBER(quad_to_target_transform)
+IPC_STRUCT_TRAITS_MEMBER(quad_layer_bounds)
+IPC_STRUCT_TRAITS_MEMBER(visible_quad_layer_rect)
   IPC_STRUCT_TRAITS_MEMBER(clip_rect)
   IPC_STRUCT_TRAITS_MEMBER(is_clipped)
   IPC_STRUCT_TRAITS_MEMBER(opacity)
diff --git a/content/common/cc_messages_unittest.cc b/content/common/cc_messages_unittest.cc
index 88daaa7..3737e585 100644
--- a/content/common/cc_messages_unittest.cc
+++ b/content/common/cc_messages_unittest.cc
@@ -57,9 +57,9 @@
   }
 
   void Compare(const SharedQuadState* a, const SharedQuadState* b) {
-    EXPECT_EQ(a->content_to_target_transform, b->content_to_target_transform);
-    EXPECT_EQ(a->content_bounds, b->content_bounds);
-    EXPECT_EQ(a->visible_content_rect, b->visible_content_rect);
+    EXPECT_EQ(a->quad_to_target_transform, b->quad_to_target_transform);
+    EXPECT_EQ(a->quad_layer_bounds, b->quad_layer_bounds);
+    EXPECT_EQ(a->visible_quad_layer_rect, b->visible_quad_layer_rect);
     EXPECT_EQ(a->clip_rect, b->clip_rect);
     EXPECT_EQ(a->is_clipped, b->is_clipped);
     EXPECT_EQ(a->opacity, b->opacity);
@@ -627,12 +627,12 @@
   ASSERT_EQ(2u, pass_out->shared_quad_state_list.size());
   ASSERT_EQ(2u, pass_out->quad_list.size());
 
-  EXPECT_EQ(
-      gfx::Size(1, 1).ToString(),
-      pass_out->shared_quad_state_list.ElementAt(0)->content_bounds.ToString());
-  EXPECT_EQ(
-      gfx::Size(4, 4).ToString(),
-      pass_out->shared_quad_state_list.ElementAt(1)->content_bounds.ToString());
+  EXPECT_EQ(gfx::Size(1, 1).ToString(),
+            pass_out->shared_quad_state_list.ElementAt(0)
+                ->quad_layer_bounds.ToString());
+  EXPECT_EQ(gfx::Size(4, 4).ToString(),
+            pass_out->shared_quad_state_list.ElementAt(1)
+                ->quad_layer_bounds.ToString());
 }
 
 TEST_F(CCMessagesTest, Resources) {
diff --git a/content/common/font_config_ipc_linux.cc b/content/common/font_config_ipc_linux.cc
index 7d2ac24..7c0066d 100644
--- a/content/common/font_config_ipc_linux.cc
+++ b/content/common/font_config_ipc_linux.cc
@@ -58,7 +58,7 @@
     font_config->RemoveMappedFontFile(this);
   }
 
-  static void ReleaseProc(const void* ptr, size_t length, void* context) {
+  static void ReleaseProc(const void* ptr, void* context) {
     base::ThreadRestrictions::ScopedAllowIO allow_munmap;
     static_cast<MappedFontFile*>(context)->Release();
   }
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 1bf18df..1db78bf 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -129,6 +129,8 @@
 IPC_STRUCT_TRAITS_BEGIN(content::FrameNavigateParams)
   IPC_STRUCT_TRAITS_MEMBER(page_id)
   IPC_STRUCT_TRAITS_MEMBER(nav_entry_id)
+  IPC_STRUCT_TRAITS_MEMBER(item_sequence_number)
+  IPC_STRUCT_TRAITS_MEMBER(document_sequence_number)
   IPC_STRUCT_TRAITS_MEMBER(url)
   IPC_STRUCT_TRAITS_MEMBER(base_url)
   IPC_STRUCT_TRAITS_MEMBER(referrer)
diff --git a/content/common/gpu/DEPS b/content/common/gpu/DEPS
index f58a808..2c01d5f 100644
--- a/content/common/gpu/DEPS
+++ b/content/common/gpu/DEPS
@@ -2,6 +2,7 @@
   "+gpu/command_buffer",
   "+libEGL",
   "+libGLESv2",
+  "+media/video/jpeg_decode_accelerator.h",
   "+media/video/video_decode_accelerator.h",
   "+media/video/video_encode_accelerator.h",
   "+skia",
diff --git a/content/common/gpu/gpu_channel.cc b/content/common/gpu/gpu_channel.cc
index 398eab76..bebde52a 100644
--- a/content/common/gpu/gpu_channel.cc
+++ b/content/common/gpu/gpu_channel.cc
@@ -23,6 +23,7 @@
 #include "content/common/gpu/gpu_channel_manager.h"
 #include "content/common/gpu/gpu_memory_buffer_factory.h"
 #include "content/common/gpu/gpu_messages.h"
+#include "content/common/gpu/media/gpu_jpeg_decode_accelerator.h"
 #include "content/public/common/content_switches.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/value_state.h"
@@ -667,6 +668,8 @@
                         OnCreateOffscreenCommandBuffer)
     IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer,
                         OnDestroyCommandBuffer)
+    IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuMsg_CreateJpegDecoder,
+                                    OnCreateJpegDecoder)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   DCHECK(handled) << msg.type();
@@ -788,6 +791,13 @@
   }
 }
 
+void GpuChannel::OnCreateJpegDecoder(int32 route_id, IPC::Message* reply_msg) {
+  if (!jpeg_decoder_) {
+    jpeg_decoder_.reset(new GpuJpegDecodeAccelerator(this, io_task_runner_));
+  }
+  jpeg_decoder_->AddClient(route_id, reply_msg);
+}
+
 void GpuChannel::MessageProcessed() {
   messages_processed_++;
   if (preempting_flag_.get()) {
diff --git a/content/common/gpu/gpu_channel.h b/content/common/gpu/gpu_channel.h
index a213fe46..a5fb1e17 100644
--- a/content/common/gpu/gpu_channel.h
+++ b/content/common/gpu/gpu_channel.h
@@ -49,6 +49,7 @@
 namespace content {
 class GpuChannelManager;
 class GpuChannelMessageFilter;
+class GpuJpegDecodeAccelerator;
 class GpuWatchdog;
 
 // Encapsulates an IPC channel between the GPU process and one renderer
@@ -186,6 +187,7 @@
       int32 route_id,
       bool* succeeded);
   void OnDestroyCommandBuffer(int32 route_id);
+  void OnCreateJpegDecoder(int32 route_id, IPC::Message* reply_msg);
 
   // Decrement the count of unhandled IPC messages and defer preemption.
   void MessageProcessed();
@@ -231,6 +233,8 @@
   typedef IDMap<GpuCommandBufferStub, IDMapOwnPointer> StubMap;
   StubMap stubs_;
 
+  scoped_ptr<GpuJpegDecodeAccelerator> jpeg_decoder_;
+
   bool log_messages_;  // True if we should log sent and received messages.
   gpu::gles2::DisallowedFeatures disallowed_features_;
   GpuWatchdog* watchdog_;
diff --git a/content/common/gpu/gpu_messages.h b/content/common/gpu/gpu_messages.h
index 7e507e4..7ac3db3 100644
--- a/content/common/gpu/gpu_messages.h
+++ b/content/common/gpu/gpu_messages.h
@@ -27,6 +27,7 @@
 #include "ipc/ipc_channel_handle.h"
 #include "ipc/ipc_message_macros.h"
 #include "media/base/video_frame.h"
+#include "media/video/jpeg_decode_accelerator.h"
 #include "media/video/video_decode_accelerator.h"
 #include "media/video/video_encode_accelerator.h"
 #include "ui/events/latency_info.h"
@@ -59,6 +60,8 @@
 IPC_ENUM_TRAITS_MAX_VALUE(gpu::error::Error, gpu::error::kErrorLast)
 IPC_ENUM_TRAITS_MAX_VALUE(gpu::error::ContextLostReason,
                           gpu::error::kContextLostReasonLast)
+IPC_ENUM_TRAITS_MAX_VALUE(media::JpegDecodeAccelerator::Error,
+                          media::JpegDecodeAccelerator::LARGEST_ERROR_ENUM)
 IPC_ENUM_TRAITS_MAX_VALUE(media::VideoEncodeAccelerator::Error,
                           media::VideoEncodeAccelerator::kErrorMax)
 IPC_ENUM_TRAITS_MAX_VALUE(media::VideoFrame::Format,
@@ -111,6 +114,15 @@
 IPC_STRUCT_END()
 #endif
 
+IPC_STRUCT_BEGIN(AcceleratedJpegDecoderMsg_Decode_Params)
+  IPC_STRUCT_MEMBER(int32, input_buffer_id)
+  IPC_STRUCT_MEMBER(gfx::Size, coded_size)
+  IPC_STRUCT_MEMBER(base::SharedMemoryHandle, input_buffer_handle)
+  IPC_STRUCT_MEMBER(uint32, input_buffer_size)
+  IPC_STRUCT_MEMBER(base::SharedMemoryHandle, output_video_frame_handle)
+  IPC_STRUCT_MEMBER(uint32, output_buffer_size)
+IPC_STRUCT_END()
+
 IPC_STRUCT_BEGIN(GPUCommandBufferConsoleMessage)
   IPC_STRUCT_MEMBER(int32, id)
   IPC_STRUCT_MEMBER(std::string, message)
@@ -275,6 +287,13 @@
                      int32, /* client_id */
                      int32 /* sync_point */)
 
+// Create and initialize a hardware jpeg decoder using the specified route_id.
+// Created decoders should be freed with AcceleratedJpegDecoderMsg_Destroy when
+// no longer needed.
+IPC_SYNC_MESSAGE_CONTROL1_1(GpuMsg_CreateJpegDecoder,
+                            int32 /* route_id */,
+                            bool /* succeeded */)
+
 // Tells the GPU process to create a context for collecting graphics card
 // information.
 IPC_MESSAGE_CONTROL0(GpuMsg_CollectGraphicsInfo)
@@ -758,3 +777,27 @@
 
 // Send destroy request to the encoder.
 IPC_MESSAGE_ROUTED0(AcceleratedVideoEncoderMsg_Destroy)
+
+//------------------------------------------------------------------------------
+// Accelerated JPEG Decoder Messages
+// These messages are sent from the Browser process to GPU process.
+
+// Decode one JPEG image from shared memory |input_buffer_handle| with size
+// |input_buffer_size|. The input buffer is associated with |input_buffer_id|
+// and the size of JPEG image is |coded_size|. Decoded I420 frame data will
+// be put onto shared memory associated with |output_video_frame_handle|
+// with size limit |output_buffer_size|.
+IPC_MESSAGE_ROUTED1(AcceleratedJpegDecoderMsg_Decode,
+                    AcceleratedJpegDecoderMsg_Decode_Params)
+
+// Send destroy request to the decoder.
+IPC_MESSAGE_ROUTED0(AcceleratedJpegDecoderMsg_Destroy)
+
+//------------------------------------------------------------------------------
+// Accelerated JPEG Decoder Host Messages
+// These messages are sent from the GPU process to Browser process.
+//
+// Report decode status.
+IPC_MESSAGE_ROUTED2(AcceleratedJpegDecoderHostMsg_DecodeAck,
+                    int32, /* bitstream_buffer_id */
+                    media::JpegDecodeAccelerator::Error /* error */)
diff --git a/content/common/gpu/media/gpu_jpeg_decode_accelerator.cc b/content/common/gpu/media/gpu_jpeg_decode_accelerator.cc
new file mode 100644
index 0000000..7674ead
--- /dev/null
+++ b/content/common/gpu/media/gpu_jpeg_decode_accelerator.cc
@@ -0,0 +1,384 @@
+// 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/common/gpu/media/gpu_jpeg_decode_accelerator.h"
+
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "base/memory/shared_memory.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/trace_event/trace_event.h"
+#include "content/common/gpu/gpu_channel.h"
+#include "content/common/gpu/gpu_messages.h"
+#include "ipc/ipc_message_macros.h"
+#include "ipc/message_filter.h"
+#include "media/filters/jpeg_parser.h"
+#include "ui/gfx/geometry/size.h"
+
+#if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
+#include "content/common/gpu/media/vaapi_jpeg_decode_accelerator.h"
+#endif
+
+namespace {
+
+void DecodeFinished(scoped_ptr<base::SharedMemory> shm) {
+  // Do nothing. Because VideoFrame is backed by |shm|, the purpose of this
+  // function is to just keep reference of |shm| to make sure it lives util
+  // decode finishes.
+}
+
+bool VerifyDecodeParams(const AcceleratedJpegDecoderMsg_Decode_Params& params) {
+  if (params.input_buffer_id < 0) {
+    LOG(ERROR) << "BitstreamBuffer id " << params.input_buffer_id
+               << " out of range";
+    return false;
+  }
+
+  const int kJpegMaxDimension = UINT16_MAX;
+  if (params.coded_size.IsEmpty() ||
+      params.coded_size.width() > kJpegMaxDimension ||
+      params.coded_size.height() > kJpegMaxDimension) {
+    LOG(ERROR) << "invalid coded_size " << params.coded_size.ToString();
+    return false;
+  }
+
+  if (!base::SharedMemory::IsHandleValid(params.input_buffer_handle)) {
+    LOG(ERROR) << "invalid input_buffer_handle";
+    return false;
+  }
+
+  if (!base::SharedMemory::IsHandleValid(params.output_video_frame_handle)) {
+    LOG(ERROR) << "invalid output_video_frame_handle";
+    return false;
+  }
+
+  if (params.output_buffer_size <
+      media::VideoFrame::AllocationSize(media::VideoFrame::I420,
+                                        params.coded_size)) {
+    LOG(ERROR) << "output_buffer_size is too small: "
+               << params.output_buffer_size;
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace content {
+
+class GpuJpegDecodeAccelerator::Client
+    : public media::JpegDecodeAccelerator::Client,
+      public base::NonThreadSafe {
+ public:
+  Client(content::GpuJpegDecodeAccelerator* owner, int32 route_id)
+      : owner_(owner->AsWeakPtr()), route_id_(route_id) {}
+
+  ~Client() override { DCHECK(CalledOnValidThread()); }
+
+  // media::JpegDecodeAccelerator::Client implementation.
+  void VideoFrameReady(int32_t bitstream_buffer_id) override {
+    DCHECK(CalledOnValidThread());
+    if (owner_)
+      owner_->NotifyDecodeStatus(route_id_, bitstream_buffer_id,
+                                 media::JpegDecodeAccelerator::NO_ERRORS);
+  }
+
+  void NotifyError(int32_t bitstream_buffer_id,
+                   media::JpegDecodeAccelerator::Error error) override {
+    DCHECK(CalledOnValidThread());
+    if (owner_)
+      owner_->NotifyDecodeStatus(route_id_, bitstream_buffer_id, error);
+  }
+
+  void Decode(const media::BitstreamBuffer& bitstream_buffer,
+              const scoped_refptr<media::VideoFrame>& video_frame) {
+    DCHECK(CalledOnValidThread());
+    DCHECK(accelerator_);
+    accelerator_->Decode(bitstream_buffer, video_frame);
+  }
+
+  void set_accelerator(scoped_ptr<media::JpegDecodeAccelerator> accelerator) {
+    DCHECK(CalledOnValidThread());
+    accelerator_ = accelerator.Pass();
+  }
+
+ private:
+  base::WeakPtr<content::GpuJpegDecodeAccelerator> owner_;
+  int32 route_id_;
+  scoped_ptr<media::JpegDecodeAccelerator> accelerator_;
+};
+
+// Create, destroy, and RemoveClient run on child thread. All other methods run
+// on IO thread.
+class GpuJpegDecodeAccelerator::MessageFilter : public IPC::MessageFilter {
+ public:
+  explicit MessageFilter(GpuJpegDecodeAccelerator* owner)
+      : owner_(owner->AsWeakPtr()),
+        child_task_runner_(owner_->child_task_runner_),
+        io_task_runner_(owner_->io_task_runner_) {}
+
+  void OnChannelError() override { sender_ = nullptr; }
+
+  void OnChannelClosing() override { sender_ = nullptr; }
+
+  void OnFilterAdded(IPC::Sender* sender) override { sender_ = sender; }
+
+  bool OnMessageReceived(const IPC::Message& msg) override {
+    const int32 route_id = msg.routing_id();
+    if (client_map_.find(route_id) == client_map_.end())
+      return false;
+
+    bool handled = true;
+    IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MessageFilter, msg, &route_id)
+      IPC_MESSAGE_HANDLER(AcceleratedJpegDecoderMsg_Decode, OnDecodeOnIOThread)
+      IPC_MESSAGE_HANDLER(AcceleratedJpegDecoderMsg_Destroy,
+                          OnDestroyOnIOThread)
+      IPC_MESSAGE_UNHANDLED(handled = false)
+    IPC_END_MESSAGE_MAP()
+    return handled;
+  }
+
+  bool SendOnIOThread(IPC::Message* message) {
+    DCHECK(!message->is_sync());
+    if (!sender_) {
+      delete message;
+      return false;
+    }
+    return sender_->Send(message);
+  }
+
+  void AddClientOnIOThread(int32 route_id,
+                           Client* client,
+                           IPC::Message* reply_msg) {
+    DCHECK(io_task_runner_->BelongsToCurrentThread());
+    DCHECK(client_map_.count(route_id) == 0);
+
+    client_map_[route_id] = client;
+    GpuMsg_CreateJpegDecoder::WriteReplyParams(reply_msg, true);
+    SendOnIOThread(reply_msg);
+  }
+
+  void OnDestroyOnIOThread(const int32* route_id) {
+    DCHECK(io_task_runner_->BelongsToCurrentThread());
+    const auto& it = client_map_.find(*route_id);
+    DCHECK(it != client_map_.end());
+    Client* client = it->second;
+    DCHECK(client);
+    client_map_.erase(it);
+
+    child_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&MessageFilter::DestroyClient, this, client));
+  }
+
+  void DestroyClient(Client* client) {
+    DCHECK(child_task_runner_->BelongsToCurrentThread());
+    delete client;
+    if (owner_)
+      owner_->ClientRemoved();
+  }
+
+  void NotifyDecodeStatusOnIOThread(int32 route_id,
+                                    int32_t buffer_id,
+                                    media::JpegDecodeAccelerator::Error error) {
+    DCHECK(io_task_runner_->BelongsToCurrentThread());
+    SendOnIOThread(new AcceleratedJpegDecoderHostMsg_DecodeAck(
+        route_id, buffer_id, error));
+  }
+
+  void OnDecodeOnIOThread(
+      const int32* route_id,
+      const AcceleratedJpegDecoderMsg_Decode_Params& params) {
+    DCHECK(io_task_runner_->BelongsToCurrentThread());
+    DCHECK(route_id);
+    TRACE_EVENT0("jpeg", "GpuJpegDecodeAccelerator::MessageFilter::OnDecode");
+
+    if (!VerifyDecodeParams(params)) {
+      NotifyDecodeStatusOnIOThread(
+          *route_id, params.input_buffer_id,
+          media::JpegDecodeAccelerator::INVALID_ARGUMENT);
+      if (base::SharedMemory::IsHandleValid(params.input_buffer_handle))
+        base::SharedMemory::CloseHandle(params.input_buffer_handle);
+      if (base::SharedMemory::IsHandleValid(params.output_video_frame_handle))
+        base::SharedMemory::CloseHandle(params.output_video_frame_handle);
+      return;
+    }
+
+    // For handles in |params|, from now on, |params.output_video_frame_handle|
+    // is taken cared by scoper. |params.input_buffer_handle| need to be closed
+    // manually for early exits.
+    scoped_ptr<base::SharedMemory> output_shm(
+        new base::SharedMemory(params.output_video_frame_handle, false));
+    if (!output_shm->Map(params.output_buffer_size)) {
+      LOG(ERROR) << "Could not map output shared memory for input buffer id "
+                 << params.input_buffer_id;
+      NotifyDecodeStatusOnIOThread(
+          *route_id, params.input_buffer_id,
+          media::JpegDecodeAccelerator::PLATFORM_FAILURE);
+      base::SharedMemory::CloseHandle(params.input_buffer_handle);
+      return;
+    }
+
+    media::BitstreamBuffer input_buffer(params.input_buffer_id,
+                                        params.input_buffer_handle,
+                                        params.input_buffer_size);
+
+    uint8_t* shm_memory = static_cast<uint8_t*>(output_shm->memory());
+    scoped_refptr<media::VideoFrame> frame =
+        media::VideoFrame::WrapExternalSharedMemory(
+            media::VideoFrame::I420,           // format
+            params.coded_size,                 // coded_size
+            gfx::Rect(params.coded_size),      // visible_rect
+            params.coded_size,                 // natural_size
+            shm_memory,                        // data
+            params.output_buffer_size,         // data_size
+            params.output_video_frame_handle,  // handle
+            0,                                 // data_offset
+            base::TimeDelta());                // timestamp
+    frame->AddDestructionObserver(
+        base::Bind(DecodeFinished, base::Passed(&output_shm)));
+
+    if (!frame.get()) {
+      LOG(ERROR) << "Could not create VideoFrame for input buffer id "
+                 << params.input_buffer_id;
+      NotifyDecodeStatusOnIOThread(
+          *route_id, params.input_buffer_id,
+          media::JpegDecodeAccelerator::PLATFORM_FAILURE);
+      base::SharedMemory::CloseHandle(params.input_buffer_handle);
+      return;
+    }
+
+    DCHECK_GT(client_map_.count(*route_id), 0u);
+    Client* client = client_map_[*route_id];
+    client->Decode(input_buffer, frame);
+  }
+
+ protected:
+  ~MessageFilter() override {
+    if (client_map_.empty())
+      return;
+
+    if (child_task_runner_->BelongsToCurrentThread()) {
+      STLDeleteValues(&client_map_);
+    } else {
+      // Make sure |Client| are deleted on child thread.
+      scoped_ptr<ClientMap> client_map(new ClientMap);
+      client_map->swap(client_map_);
+
+      child_task_runner_->PostTask(
+          FROM_HERE,
+          base::Bind(&DeleteClientMapOnChildThread, base::Passed(&client_map)));
+    }
+  }
+
+ private:
+  using ClientMap = base::hash_map<int32, Client*>;
+
+  // Must be static because this method runs after destructor.
+  static void DeleteClientMapOnChildThread(scoped_ptr<ClientMap> client_map) {
+    STLDeleteValues(client_map.get());
+  }
+
+  base::WeakPtr<GpuJpegDecodeAccelerator> owner_;
+
+  // GPU child task runner.
+  scoped_refptr<base::SingleThreadTaskRunner> child_task_runner_;
+
+  // GPU IO task runner.
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+  // The sender to which this filter was added.
+  IPC::Sender* sender_;
+
+  // A map from route id to JpegDecodeAccelerator.
+  // Unless in destructor (maybe on child thread), |client_map_| should
+  // only be accessed on IO thread.
+  ClientMap client_map_;
+};
+
+GpuJpegDecodeAccelerator::GpuJpegDecodeAccelerator(
+    GpuChannel* channel,
+    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
+    : channel_(channel),
+      child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      io_task_runner_(io_task_runner),
+      client_number_(0) {
+}
+
+GpuJpegDecodeAccelerator::~GpuJpegDecodeAccelerator() {
+  DCHECK(CalledOnValidThread());
+  if (filter_) {
+    channel_->RemoveFilter(filter_.get());
+  }
+}
+
+void GpuJpegDecodeAccelerator::AddClient(int32 route_id,
+                                         IPC::Message* reply_msg) {
+  DCHECK(CalledOnValidThread());
+  scoped_ptr<media::JpegDecodeAccelerator> accelerator;
+
+// When adding more platforms, GpuJpegDecoder::Supported need
+// update as well.
+#if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
+  accelerator.reset(new VaapiJpegDecodeAccelerator(io_task_runner_));
+#else
+  DVLOG(1) << "HW JPEG decode acceleration not available.";
+#endif
+
+  scoped_ptr<Client> client(new Client(this, route_id));
+  if (!accelerator.get() || !accelerator->Initialize(client.get())) {
+    DLOG(ERROR) << "JPEG accelerator Initialize failed";
+    GpuMsg_CreateJpegDecoder::WriteReplyParams(reply_msg, false);
+    Send(reply_msg);
+    return;
+  }
+  client->set_accelerator(accelerator.Pass());
+
+  if (!filter_) {
+    DCHECK_EQ(client_number_, 0);
+    filter_ = new MessageFilter(this);
+    // This should be before AddClientOnIOThread.
+    channel_->AddFilter(filter_.get());
+  }
+  client_number_++;
+
+  // In this PostTask, |client| may leak if |io_task_runner_| is destroyed
+  // before |client| reached AddClientOnIOThread. However we cannot use scoper
+  // to protect it because |client| can only be deleted on child thread. The IO
+  // thread is destroyed at termination, at which point it's ok to leak since
+  // we're going to tear down the process anyway. So we just crossed fingers
+  // here instead of making the code unnecessary complicated.
+  io_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&MessageFilter::AddClientOnIOThread, filter_,
+                            route_id, client.release(), reply_msg));
+}
+
+void GpuJpegDecodeAccelerator::NotifyDecodeStatus(
+    int32 route_id,
+    int32_t buffer_id,
+    media::JpegDecodeAccelerator::Error error) {
+  DCHECK(CalledOnValidThread());
+  Send(new AcceleratedJpegDecoderHostMsg_DecodeAck(route_id, buffer_id, error));
+}
+
+void GpuJpegDecodeAccelerator::ClientRemoved() {
+  DCHECK(CalledOnValidThread());
+  DCHECK_GT(client_number_, 0);
+  client_number_--;
+  if (client_number_ == 0) {
+    channel_->RemoveFilter(filter_.get());
+    filter_ = nullptr;
+  }
+}
+
+bool GpuJpegDecodeAccelerator::Send(IPC::Message* message) {
+  DCHECK(CalledOnValidThread());
+  return channel_->Send(message);
+}
+
+}  // namespace content
diff --git a/content/common/gpu/media/gpu_jpeg_decode_accelerator.h b/content/common/gpu/media/gpu_jpeg_decode_accelerator.h
new file mode 100644
index 0000000..732e5c1
--- /dev/null
+++ b/content/common/gpu/media/gpu_jpeg_decode_accelerator.h
@@ -0,0 +1,71 @@
+// 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_COMMON_GPU_MEDIA_GPU_JPEG_DECODE_ACCELERATOR_H_
+#define CONTENT_COMMON_GPU_MEDIA_GPU_JPEG_DECODE_ACCELERATOR_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/non_thread_safe.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_sender.h"
+#include "media/video/jpeg_decode_accelerator.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace content {
+class GpuChannel;
+
+class GpuJpegDecodeAccelerator
+    : public IPC::Sender,
+      public base::NonThreadSafe,
+      public base::SupportsWeakPtr<GpuJpegDecodeAccelerator> {
+ public:
+  // |channel| must outlive this object.
+  GpuJpegDecodeAccelerator(
+      GpuChannel* channel,
+      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
+  ~GpuJpegDecodeAccelerator() override;
+
+  void AddClient(int32 route_id, IPC::Message* reply_msg);
+
+  void NotifyDecodeStatus(int32 route_id,
+                          int32_t bitstream_buffer_id,
+                          media::JpegDecodeAccelerator::Error error);
+
+  // Function to delegate sending to actual sender.
+  bool Send(IPC::Message* message) override;
+
+ private:
+  class Client;
+  class MessageFilter;
+
+  void ClientRemoved();
+
+  // The lifetime of objects of this class is managed by a GpuChannel. The
+  // GpuChannels destroy all the GpuJpegDecodeAccelerator that they own when
+  // they are destroyed. So a raw pointer is safe.
+  GpuChannel* channel_;
+
+  // The message filter to run JpegDecodeAccelerator::Decode on IO thread.
+  scoped_refptr<MessageFilter> filter_;
+
+  // GPU child task runner.
+  scoped_refptr<base::SingleThreadTaskRunner> child_task_runner_;
+
+  // GPU IO task runner.
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+  // Number of clients added to |filter_|.
+  int client_number_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(GpuJpegDecodeAccelerator);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_COMMON_GPU_MEDIA_GPU_JPEG_DECODE_ACCELERATOR_H_
diff --git a/content/common/gpu/media/rendering_helper.cc b/content/common/gpu/media/rendering_helper.cc
index 06252a5..b67106e5 100644
--- a/content/common/gpu/media/rendering_helper.cc
+++ b/content/common/gpu/media/rendering_helper.cc
@@ -124,9 +124,10 @@
 
   void OnLostCapture() override {};
 
-  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override {
+  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget,
+                                    float device_pixel_ratio) override {
     accelerated_widget_ = widget;
-  };
+  }
 
   void OnActivationChanged(bool active) override {};
 
diff --git a/content/common/gpu/media/vaapi_jpeg_decode_accelerator.cc b/content/common/gpu/media/vaapi_jpeg_decode_accelerator.cc
new file mode 100644
index 0000000..6d51d27
--- /dev/null
+++ b/content/common/gpu/media/vaapi_jpeg_decode_accelerator.cc
@@ -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.
+
+#include "content/common/gpu/media/vaapi_jpeg_decode_accelerator.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "content/common/gpu/gpu_channel.h"
+#include "content/common/gpu/media/vaapi_picture.h"
+#include "media/base/video_frame.h"
+#include "media/filters/jpeg_parser.h"
+#include "third_party/libyuv/include/libyuv.h"
+
+namespace content {
+
+namespace {
+// UMA errors that the VaapiJpegDecodeAccelerator class reports.
+enum VAJDADecoderFailure {
+  VAAPI_ERROR = 0,
+  VAJDA_DECODER_FAILURES_MAX,
+};
+
+static void ReportToUMA(VAJDADecoderFailure failure) {
+  UMA_HISTOGRAM_ENUMERATION("Media.VAJDA.DecoderFailure", failure,
+                            VAJDA_DECODER_FAILURES_MAX);
+}
+}  // namespace
+
+VaapiJpegDecodeAccelerator::DecodeRequest::DecodeRequest(
+    const media::BitstreamBuffer& bitstream_buffer,
+    scoped_ptr<base::SharedMemory> shm,
+    const scoped_refptr<media::VideoFrame>& video_frame)
+    : bitstream_buffer(bitstream_buffer),
+      shm(shm.Pass()),
+      video_frame(video_frame) {
+}
+
+VaapiJpegDecodeAccelerator::DecodeRequest::~DecodeRequest() {
+}
+
+void VaapiJpegDecodeAccelerator::NotifyError(int32_t bitstream_buffer_id,
+                                             Error error) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DLOG(ERROR) << "Notifying of error " << error;
+  DCHECK(client_);
+  client_->NotifyError(bitstream_buffer_id, error);
+}
+
+void VaapiJpegDecodeAccelerator::NotifyErrorFromDecoderThread(
+    int32_t bitstream_buffer_id,
+    Error error) {
+  DCHECK(decoder_task_runner_->BelongsToCurrentThread());
+  task_runner_->PostTask(FROM_HERE,
+                         base::Bind(&VaapiJpegDecodeAccelerator::NotifyError,
+                                    weak_this_, bitstream_buffer_id, error));
+}
+
+void VaapiJpegDecodeAccelerator::VideoFrameReady(int32_t bitstream_buffer_id) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  client_->VideoFrameReady(bitstream_buffer_id);
+}
+
+VaapiJpegDecodeAccelerator::VaapiJpegDecodeAccelerator(
+    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
+    : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      io_task_runner_(io_task_runner),
+      decoder_thread_("VaapiJpegDecoderThread"),
+      va_surface_id_(VA_INVALID_SURFACE),
+      weak_this_factory_(this) {
+  weak_this_ = weak_this_factory_.GetWeakPtr();
+}
+
+VaapiJpegDecodeAccelerator::~VaapiJpegDecodeAccelerator() {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DVLOG(1) << "Destroying VaapiJpegDecodeAccelerator";
+
+  weak_this_factory_.InvalidateWeakPtrs();
+  decoder_thread_.Stop();
+}
+
+bool VaapiJpegDecodeAccelerator::Initialize(Client* client) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  client_ = client;
+
+  vaapi_wrapper_ =
+      VaapiWrapper::Create(VaapiWrapper::kDecode, VAProfileJPEGBaseline,
+                           base::Bind(&ReportToUMA, VAAPI_ERROR));
+
+  if (!vaapi_wrapper_.get()) {
+    DLOG(ERROR) << "Failed initializing VAAPI";
+    return false;
+  }
+
+  if (!decoder_thread_.Start()) {
+    DLOG(ERROR) << "Failed to start decoding thread.";
+    return false;
+  }
+  decoder_task_runner_ = decoder_thread_.task_runner();
+
+  return true;
+}
+
+bool VaapiJpegDecodeAccelerator::OutputPicture(
+    VASurfaceID va_surface_id,
+    int32_t input_buffer_id,
+    const scoped_refptr<media::VideoFrame>& video_frame) {
+  DCHECK(decoder_task_runner_->BelongsToCurrentThread());
+
+  TRACE_EVENT1("jpeg", "VaapiJpegDecodeAccelerator::OutputPicture",
+               "input_buffer_id", input_buffer_id);
+
+  DVLOG(3) << "Outputting VASurface " << va_surface_id
+           << " into video_frame associated with input buffer id "
+           << input_buffer_id;
+
+  VAImage image;
+  VAImageFormat format;
+  const uint32_t kI420Fourcc = VA_FOURCC('I', '4', '2', '0');
+  memset(&image, 0, sizeof(image));
+  memset(&format, 0, sizeof(format));
+  format.fourcc = kI420Fourcc;
+  format.byte_order = VA_LSB_FIRST;
+  format.bits_per_pixel = 12;  // 12 for I420
+
+  uint8_t* mem = nullptr;
+  gfx::Size coded_size = video_frame->coded_size();
+  if (!vaapi_wrapper_->GetVaImage(va_surface_id, &format, coded_size, &image,
+                                  reinterpret_cast<void**>(&mem))) {
+    DLOG(ERROR) << "Cannot get VAImage";
+    return false;
+  }
+
+  // Copy image content from VAImage to VideoFrame.
+  // The component order of VAImage I420 are Y, U, and V.
+  DCHECK_EQ(image.num_planes, 3u);
+  DCHECK_GE(image.width, coded_size.width());
+  DCHECK_GE(image.height, coded_size.height());
+  const uint8_t* src_y = mem + image.offsets[0];
+  const uint8_t* src_u = mem + image.offsets[1];
+  const uint8_t* src_v = mem + image.offsets[2];
+  size_t src_y_stride = image.pitches[0];
+  size_t src_u_stride = image.pitches[1];
+  size_t src_v_stride = image.pitches[2];
+  uint8_t* dst_y = video_frame->data(media::VideoFrame::kYPlane);
+  uint8_t* dst_u = video_frame->data(media::VideoFrame::kUPlane);
+  uint8_t* dst_v = video_frame->data(media::VideoFrame::kVPlane);
+  size_t dst_y_stride = video_frame->stride(media::VideoFrame::kYPlane);
+  size_t dst_u_stride = video_frame->stride(media::VideoFrame::kUPlane);
+  size_t dst_v_stride = video_frame->stride(media::VideoFrame::kVPlane);
+
+  if (libyuv::I420Copy(src_y, src_y_stride,  // Y
+                       src_u, src_u_stride,  // U
+                       src_v, src_v_stride,  // V
+                       dst_y, dst_y_stride,  // Y
+                       dst_u, dst_u_stride,  // U
+                       dst_v, dst_v_stride,  // V
+                       coded_size.width(), coded_size.height())) {
+    DLOG(ERROR) << "I420Copy failed";
+    return false;
+  }
+
+  vaapi_wrapper_->ReturnVaImage(&image);
+
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(&VaapiJpegDecodeAccelerator::VideoFrameReady,
+                            weak_this_, input_buffer_id));
+
+  return true;
+}
+
+void VaapiJpegDecodeAccelerator::DecodeTask(
+    const scoped_ptr<DecodeRequest>& request) {
+  DVLOG(3) << __func__;
+  DCHECK(decoder_task_runner_->BelongsToCurrentThread());
+  TRACE_EVENT0("jpeg", "DecodeTask");
+
+  media::JpegParseResult parse_result;
+  if (!media::ParseJpegPicture(
+          reinterpret_cast<const uint8_t*>(request->shm->memory()),
+          request->bitstream_buffer.size(), &parse_result)) {
+    DLOG(ERROR) << "ParseJpegPicture failed";
+    NotifyErrorFromDecoderThread(
+        request->bitstream_buffer.id(),
+        media::JpegDecodeAccelerator::PARSE_JPEG_FAILED);
+    return;
+  }
+
+  // Reuse VASurface if size doesn't change.
+  gfx::Size new_coded_size(parse_result.frame_header.coded_width,
+                           parse_result.frame_header.coded_height);
+  if (new_coded_size != coded_size_ || va_surface_id_ == VA_INVALID_SURFACE) {
+    vaapi_wrapper_->DestroySurfaces();
+    va_surface_id_ = VA_INVALID_SURFACE;
+
+    std::vector<VASurfaceID> va_surfaces;
+    if (!vaapi_wrapper_->CreateSurfaces(new_coded_size, 1, &va_surfaces)) {
+      LOG(ERROR) << "Create VA surface failed";
+      NotifyErrorFromDecoderThread(
+          request->bitstream_buffer.id(),
+          media::JpegDecodeAccelerator::PLATFORM_FAILURE);
+      return;
+    }
+    va_surface_id_ = va_surfaces[0];
+    coded_size_ = new_coded_size;
+  }
+
+  if (!VaapiJpegDecoder::Decode(vaapi_wrapper_.get(), parse_result,
+                                va_surface_id_)) {
+    LOG(ERROR) << "Decode JPEG failed";
+    NotifyErrorFromDecoderThread(
+        request->bitstream_buffer.id(),
+        media::JpegDecodeAccelerator::PLATFORM_FAILURE);
+    return;
+  }
+
+  if (!OutputPicture(va_surface_id_, request->bitstream_buffer.id(),
+                     request->video_frame)) {
+    LOG(ERROR) << "Output picture failed";
+    NotifyErrorFromDecoderThread(
+        request->bitstream_buffer.id(),
+        media::JpegDecodeAccelerator::PLATFORM_FAILURE);
+    return;
+  }
+}
+
+void VaapiJpegDecodeAccelerator::Decode(
+    const media::BitstreamBuffer& bitstream_buffer,
+    const scoped_refptr<media::VideoFrame>& video_frame) {
+  DVLOG(3) << __func__;
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  TRACE_EVENT1("jpeg", "Decode", "input_id", bitstream_buffer.id());
+
+  DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id()
+           << " size: " << bitstream_buffer.size();
+  scoped_ptr<base::SharedMemory> shm(
+      new base::SharedMemory(bitstream_buffer.handle(), true));
+
+  if (!shm->Map(bitstream_buffer.size())) {
+    LOG(ERROR) << "Failed to map input buffer";
+    NotifyErrorFromDecoderThread(bitstream_buffer.id(), UNREADABLE_INPUT);
+    return;
+  }
+
+  scoped_ptr<DecodeRequest> request(
+      new DecodeRequest(bitstream_buffer, shm.Pass(), video_frame));
+
+  decoder_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&VaapiJpegDecodeAccelerator::DecodeTask,
+                            base::Unretained(this), base::Passed(&request)));
+}
+
+}  // namespace content
diff --git a/content/common/gpu/media/vaapi_jpeg_decode_accelerator.h b/content/common/gpu/media/vaapi_jpeg_decode_accelerator.h
new file mode 100644
index 0000000..c944db6
--- /dev/null
+++ b/content/common/gpu/media/vaapi_jpeg_decode_accelerator.h
@@ -0,0 +1,113 @@
+// 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_COMMON_GPU_MEDIA_VAAPI_JPEG_DECODE_ACCELERATOR_H_
+#define CONTENT_COMMON_GPU_MEDIA_VAAPI_JPEG_DECODE_ACCELERATOR_H_
+
+#include "base/memory/linked_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/threading/thread.h"
+#include "content/common/content_export.h"
+#include "content/common/gpu/media/vaapi_jpeg_decoder.h"
+#include "content/common/gpu/media/vaapi_wrapper.h"
+#include "media/base/bitstream_buffer.h"
+#include "media/video/jpeg_decode_accelerator.h"
+
+namespace content {
+
+// Class to provide JPEG decode acceleration for Intel systems with hardware
+// support for it, and on which libva is available.
+// Decoding tasks are performed in a separate decoding thread.
+//
+// Threading/life-cycle: this object is created & destroyed on the GPU
+// ChildThread.  A few methods on it are called on the decoder thread which is
+// stopped during |this->Destroy()|, so any tasks posted to the decoder thread
+// can assume |*this| is still alive.  See |weak_this_| below for more details.
+class CONTENT_EXPORT VaapiJpegDecodeAccelerator
+    : public media::JpegDecodeAccelerator {
+ public:
+  VaapiJpegDecodeAccelerator(
+      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
+  ~VaapiJpegDecodeAccelerator() override;
+
+  // media::JpegDecodeAccelerator implementation.
+  bool Initialize(media::JpegDecodeAccelerator::Client* client) override;
+  void Decode(const media::BitstreamBuffer& bitstream_buffer,
+              const scoped_refptr<media::VideoFrame>& video_frame) override;
+
+ private:
+  // An input buffer and the corresponding output video frame awaiting
+  // consumption, provided by the client.
+  struct DecodeRequest {
+    DecodeRequest(const media::BitstreamBuffer& bitstream_buffer,
+                  scoped_ptr<base::SharedMemory> shm,
+                  const scoped_refptr<media::VideoFrame>& video_frame);
+    ~DecodeRequest();
+
+    media::BitstreamBuffer bitstream_buffer;
+    scoped_ptr<base::SharedMemory> shm;
+    scoped_refptr<media::VideoFrame> video_frame;
+  };
+
+  // Notifies the client that an error has occurred and decoding cannot
+  // continue.
+  void NotifyError(int32_t bitstream_buffer_id, Error error);
+  void NotifyErrorFromDecoderThread(int32_t bitstream_buffer_id, Error error);
+  void VideoFrameReady(int32_t bitstream_buffer_id);
+
+  // Processes one decode |request|.
+  void DecodeTask(const scoped_ptr<DecodeRequest>& request);
+
+  // Puts contents of |va_surface| into given |video_frame|, releases the
+  // surface and passes the |input_buffer_id| of the resulting picture to
+  // client for output.
+  bool OutputPicture(VASurfaceID va_surface_id,
+                     int32_t input_buffer_id,
+                     const scoped_refptr<media::VideoFrame>& video_frame);
+
+  // ChildThread's task runner.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // GPU IO task runner.
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+  // The client of this class.
+  Client* client_;
+
+  // WeakPtr<> pointing to |this| for use in posting tasks from the decoder
+  // thread back to the ChildThread.  Because the decoder thread is a member of
+  // this class, any task running on the decoder thread is guaranteed that this
+  // object is still alive.  As a result, tasks posted from ChildThread to
+  // decoder thread should use base::Unretained(this), and tasks posted from the
+  // decoder thread to the ChildThread should use |weak_this_|.
+  base::WeakPtr<VaapiJpegDecodeAccelerator> weak_this_;
+
+  scoped_ptr<VaapiWrapper> vaapi_wrapper_;
+
+  // Comes after vaapi_wrapper_ to ensure its destructor is executed before
+  // |vaapi_wrapper_| is destroyed.
+  scoped_ptr<VaapiJpegDecoder> decoder_;
+  base::Thread decoder_thread_;
+  // Use this to post tasks to |decoder_thread_| instead of
+  // |decoder_thread_.task_runner()| because the latter will be NULL once
+  // |decoder_thread_.Stop()| returns.
+  scoped_refptr<base::SingleThreadTaskRunner> decoder_task_runner_;
+
+  // The current VA surface for decoding.
+  VASurfaceID va_surface_id_;
+  // The coded size associated with |va_surface_id_|.
+  gfx::Size coded_size_;
+
+  // The WeakPtrFactory for |weak_this_|.
+  base::WeakPtrFactory<VaapiJpegDecodeAccelerator> weak_this_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(VaapiJpegDecodeAccelerator);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_COMMON_GPU_MEDIA_VAAPI_JPEG_DECODE_ACCELERATOR_H_
diff --git a/content/common/host_discardable_shared_memory_manager.cc b/content/common/host_discardable_shared_memory_manager.cc
index a209cc0..7085ed0c 100644
--- a/content/common/host_discardable_shared_memory_manager.cc
+++ b/content/common/host_discardable_shared_memory_manager.cc
@@ -17,6 +17,7 @@
 #include "base/sys_info.h"
 #include "base/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
+#include "content/public/common/child_process_host.h"
 
 namespace content {
 namespace {
@@ -126,8 +127,9 @@
   // Note: Use DiscardableSharedMemoryHeap for in-process allocation
   // of discardable memory if the cost of each allocation is too high.
   base::SharedMemoryHandle handle;
-  AllocateLockedDiscardableSharedMemory(current_process_handle, size, new_id,
-                                        &handle);
+  AllocateLockedDiscardableSharedMemory(current_process_handle,
+                                        ChildProcessHost::kInvalidUniqueID,
+                                        size, new_id, &handle);
   CHECK(base::SharedMemory::IsHandleValid(handle));
   scoped_ptr<base::DiscardableSharedMemory> memory(
       new base::DiscardableSharedMemory(handle));
@@ -138,30 +140,30 @@
       memory.Pass(),
       base::Bind(
           &HostDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory,
-          base::Unretained(this), new_id, current_process_handle)));
+          base::Unretained(this), new_id, ChildProcessHost::kInvalidUniqueID)));
 }
 
 void HostDiscardableSharedMemoryManager::
     AllocateLockedDiscardableSharedMemoryForChild(
         base::ProcessHandle process_handle,
+        int child_process_id,
         size_t size,
         DiscardableSharedMemoryId id,
         base::SharedMemoryHandle* shared_memory_handle) {
-  AllocateLockedDiscardableSharedMemory(process_handle, size, id,
-                                        shared_memory_handle);
+  AllocateLockedDiscardableSharedMemory(process_handle, child_process_id, size,
+                                        id, shared_memory_handle);
 }
 
 void HostDiscardableSharedMemoryManager::ChildDeletedDiscardableSharedMemory(
     DiscardableSharedMemoryId id,
-    base::ProcessHandle process_handle) {
-  DeletedDiscardableSharedMemory(id, process_handle);
+    int child_process_id) {
+  DeletedDiscardableSharedMemory(id, child_process_id);
 }
 
-void HostDiscardableSharedMemoryManager::ProcessRemoved(
-    base::ProcessHandle process_handle) {
+void HostDiscardableSharedMemoryManager::ProcessRemoved(int child_process_id) {
   base::AutoLock lock(lock_);
 
-  ProcessMap::iterator process_it = processes_.find(process_handle);
+  ProcessMap::iterator process_it = processes_.find(child_process_id);
   if (process_it == processes_.end())
     return;
 
@@ -198,13 +200,14 @@
 
 void HostDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
     base::ProcessHandle process_handle,
+    int client_process_id,
     size_t size,
     DiscardableSharedMemoryId id,
     base::SharedMemoryHandle* shared_memory_handle) {
   base::AutoLock lock(lock_);
 
   // Make sure |id| is not already in use.
-  MemorySegmentMap& process_segments = processes_[process_handle];
+  MemorySegmentMap& process_segments = processes_[client_process_id];
   if (process_segments.find(id) != process_segments.end()) {
     LOG(ERROR) << "Invalid discardable shared memory ID";
     *shared_memory_handle = base::SharedMemory::NULLHandle();
@@ -264,10 +267,10 @@
 
 void HostDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory(
     DiscardableSharedMemoryId id,
-    base::ProcessHandle process_handle) {
+    int client_process_id) {
   base::AutoLock lock(lock_);
 
-  MemorySegmentMap& process_segments = processes_[process_handle];
+  MemorySegmentMap& process_segments = processes_[client_process_id];
 
   MemorySegmentMap::iterator segment_it = process_segments.find(id);
   if (segment_it == process_segments.end()) {
diff --git a/content/common/host_discardable_shared_memory_manager.h b/content/common/host_discardable_shared_memory_manager.h
index f68ed93..e197cf90 100644
--- a/content/common/host_discardable_shared_memory_manager.h
+++ b/content/common/host_discardable_shared_memory_manager.h
@@ -41,19 +41,20 @@
   // A valid shared memory handle is returned on success.
   void AllocateLockedDiscardableSharedMemoryForChild(
       base::ProcessHandle process_handle,
+      int child_process_id,
       size_t size,
       DiscardableSharedMemoryId id,
       base::SharedMemoryHandle* shared_memory_handle);
 
   // Call this to notify the manager that child process associated with
-  // |process_handle| has deleted discardable memory segment with |id|.
+  // |child_process_id| has deleted discardable memory segment with |id|.
   void ChildDeletedDiscardableSharedMemory(DiscardableSharedMemoryId id,
-                                           base::ProcessHandle process_handle);
+                                           int child_process_id);
 
   // Call this to notify the manager that child process associated with
-  // |process_handle| has been removed. The manager will use this to release
+  // |child_process_id| has been removed. The manager will use this to release
   // memory segments allocated for child process to the OS.
-  void ProcessRemoved(base::ProcessHandle process_handle);
+  void ProcessRemoved(int child_process_id);
 
   // The maximum number of bytes of memory that may be allocated. This will
   // cause memory usage to be reduced if currently above |limit|.
@@ -90,11 +91,12 @@
 
   void AllocateLockedDiscardableSharedMemory(
       base::ProcessHandle process_handle,
+      int client_process_id,
       size_t size,
       DiscardableSharedMemoryId id,
       base::SharedMemoryHandle* shared_memory_handle);
   void DeletedDiscardableSharedMemory(DiscardableSharedMemoryId id,
-                                      base::ProcessHandle process_handle);
+                                      int client_process_id);
   void OnMemoryPressure(
       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
   void ReduceMemoryUsageUntilWithinMemoryLimit();
@@ -109,7 +111,7 @@
   base::Lock lock_;
   typedef base::hash_map<DiscardableSharedMemoryId,
                          scoped_refptr<MemorySegment>> MemorySegmentMap;
-  typedef base::hash_map<base::ProcessHandle, MemorySegmentMap> ProcessMap;
+  typedef base::hash_map<int, MemorySegmentMap> ProcessMap;
   ProcessMap processes_;
   // Note: The elements in |segments_| are arranged in such a way that they form
   // a heap. The LRU memory segment always first.
diff --git a/content/common/host_discardable_shared_memory_manager_unittest.cc b/content/common/host_discardable_shared_memory_manager_unittest.cc
index 4855a2a6..40ce711 100644
--- a/content/common/host_discardable_shared_memory_manager_unittest.cc
+++ b/content/common/host_discardable_shared_memory_manager_unittest.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "content/common/host_discardable_shared_memory_manager.h"
+
+#include "content/public/common/child_process_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
@@ -67,7 +69,8 @@
 
   base::SharedMemoryHandle shared_handle;
   manager_->AllocateLockedDiscardableSharedMemoryForChild(
-      base::GetCurrentProcessHandle(), kDataSize, 0, &shared_handle);
+      base::GetCurrentProcessHandle(), ChildProcessHost::kInvalidUniqueID,
+      kDataSize, 0, &shared_handle);
   ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle));
 
   TestDiscardableSharedMemory memory(shared_handle);
@@ -88,7 +91,8 @@
 
   base::SharedMemoryHandle shared_handle1;
   manager_->AllocateLockedDiscardableSharedMemoryForChild(
-      base::GetCurrentProcessHandle(), kDataSize, 1, &shared_handle1);
+      base::GetCurrentProcessHandle(), ChildProcessHost::kInvalidUniqueID,
+      kDataSize, 1, &shared_handle1);
   ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle1));
 
   TestDiscardableSharedMemory memory1(shared_handle1);
@@ -97,7 +101,8 @@
 
   base::SharedMemoryHandle shared_handle2;
   manager_->AllocateLockedDiscardableSharedMemoryForChild(
-      base::GetCurrentProcessHandle(), kDataSize, 2, &shared_handle2);
+      base::GetCurrentProcessHandle(), ChildProcessHost::kInvalidUniqueID,
+      kDataSize, 2, &shared_handle2);
   ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle2));
 
   TestDiscardableSharedMemory memory2(shared_handle2);
@@ -152,7 +157,8 @@
 
   base::SharedMemoryHandle shared_handle;
   manager_->AllocateLockedDiscardableSharedMemoryForChild(
-      base::GetCurrentProcessHandle(), kDataSize, 0, &shared_handle);
+      base::GetCurrentProcessHandle(), ChildProcessHost::kInvalidUniqueID,
+      kDataSize, 0, &shared_handle);
   ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle));
 
   TestDiscardableSharedMemory memory(shared_handle);
diff --git a/content/common/resource_messages.cc b/content/common/resource_messages.cc
index 2f86ae2e..9cadbff 100644
--- a/content/common/resource_messages.cc
+++ b/content/common/resource_messages.cc
@@ -56,13 +56,20 @@
       WriteParam(m, p.expected_modification_time());
       break;
     }
-    default: {
-      DCHECK(p.type() == storage::DataElement::TYPE_BLOB);
+    case storage::DataElement::TYPE_BLOB: {
       WriteParam(m, p.blob_uuid());
       WriteParam(m, p.offset());
       WriteParam(m, p.length());
       break;
     }
+    case storage::DataElement::TYPE_DISK_CACHE_ENTRY: {
+      NOTREACHED() << "Can't be sent by IPC.";
+      break;
+    }
+    case storage::DataElement::TYPE_UNKNOWN: {
+      NOTREACHED();
+      break;
+    }
   }
 }
 
@@ -113,8 +120,7 @@
                                  expected_modification_time);
       break;
     }
-    default: {
-      DCHECK(type == storage::DataElement::TYPE_BLOB);
+    case storage::DataElement::TYPE_BLOB: {
       std::string blob_uuid;
       uint64 offset, length;
       if (!ReadParam(m, iter, &blob_uuid))
@@ -126,6 +132,14 @@
       r->SetToBlobRange(blob_uuid, offset, length);
       break;
     }
+    case storage::DataElement::TYPE_DISK_CACHE_ENTRY: {
+      NOTREACHED() << "Can't be sent by IPC.";
+      break;
+    }
+    case storage::DataElement::TYPE_UNKNOWN: {
+      NOTREACHED();
+      break;
+    }
   }
   return true;
 }
diff --git a/content/common/service_worker/service_worker_messages.h b/content/common/service_worker/service_worker_messages.h
index 91864fa3..a8ff04f 100644
--- a/content/common/service_worker/service_worker_messages.h
+++ b/content/common/service_worker/service_worker_messages.h
@@ -127,7 +127,7 @@
                      int /* thread_id */,
                      int /* request_id */,
                      int /* provider_id */,
-                     GURL /* scope (url pattern) */)
+                     int64 /* registration_id */)
 
 IPC_MESSAGE_CONTROL4(ServiceWorkerHostMsg_GetRegistration,
                      int /* thread_id */,
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index c7f5b3e1..75942fdb 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -890,10 +890,6 @@
                     int /* job_id */,
                     IPC::PlatformFileForTransit /* file handle */)
 
-// Temporary message to diagnose an unexpected condition in WebContentsImpl.
-IPC_MESSAGE_CONTROL1(ViewMsg_TempCrashWithData,
-                     GURL /* data */)
-
 // An acknowledge to ViewHostMsg_MultipleTargetsTouched to notify the renderer
 // process to release the magnified image.
 IPC_MESSAGE_ROUTED1(ViewMsg_ReleaseDisambiguationPopupBitmap,
diff --git a/content/common/vr_service.mojom b/content/common/vr_service.mojom
new file mode 100644
index 0000000..2d81ec7
--- /dev/null
+++ b/content/common/vr_service.mojom
@@ -0,0 +1,73 @@
+// 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.
+
+module content;
+
+struct VRVector3 {
+  float x;
+  float y;
+  float z;
+};
+
+struct VRVector4 {
+  float x;
+  float y;
+  float z;
+  float w;
+};
+
+struct VRRect {
+  int32 x;
+  int32 y;
+  int32 width;
+  int32 height;
+};
+
+// A field of view, given by 4 degrees describing the view from a center point.
+struct VRFieldOfView {
+  float upDegrees;
+  float downDegrees;
+  float leftDegrees;
+  float rightDegrees;
+};
+
+// A sensor's position, orientation, velocity, and acceleration state at the
+// given timestamp.
+struct VRSensorState {
+  double timestamp;
+  uint32 frameIndex;
+  VRVector4? orientation;
+  VRVector3? position;
+  VRVector3? angularVelocity;
+  VRVector3? linearVelocity;
+  VRVector3? angularAcceleration;
+  VRVector3? linearAcceleration;
+};
+
+// Information about the optical properties for an eye in an HMD.
+struct VREyeParameters {
+  VRFieldOfView minimumFieldOfView;
+  VRFieldOfView maximumFieldOfView;
+  VRFieldOfView recommendedFieldOfView;
+  VRVector3 eyeTranslation;
+  VRRect renderRect;
+};
+
+// Information pertaining to Head Mounted Displays.
+struct VRHMDInfo {
+  VREyeParameters leftEye;
+  VREyeParameters rightEye;
+};
+
+struct VRDeviceInfo {
+   uint32 index;
+   string deviceName;
+   VRHMDInfo? hmdInfo;
+};
+
+interface VRService {
+  GetDevices() => (array<VRDeviceInfo> devices);
+  GetSensorState(uint32 index) => (VRSensorState state);
+  ResetSensor(uint32 index);
+};
diff --git a/content/content.gyp b/content/content.gyp
index 13a8385..beffec2 100644
--- a/content/content.gyp
+++ b/content/content.gyp
@@ -589,6 +589,16 @@
             'motionevent_jni_headers'
           ],
           'includes': [ 'content_jni.gypi' ],
+          'conditions': [
+            ['enable_webvr==1', {
+              'sources': [
+                'public/android/java/src/org/chromium/content/browser/input/CardboardVRDevice.java',
+              ],
+              'dependencies': [
+                '../third_party/cardboard-java/cardboard.gyp:cardboard_jar',
+              ],
+            }],
+          ],
         },
         {
           'target_name': 'content_icudata',
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index b40617326..2709f0bf 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -1785,6 +1785,7 @@
         '../components/mime_util/mime_util.gyp:mime_util',
         '../components/scheduler/scheduler.gyp:scheduler_common',
         '../device/bluetooth/bluetooth.gyp:device_bluetooth',
+        '../gin/gin.gyp:gin',
         '../net/net.gyp:http_server',
         '../storage/storage_browser.gyp:storage',
         '../storage/storage_common.gyp:storage_common',
@@ -2091,5 +2092,22 @@
         '../third_party/boringssl/boringssl.gyp:boringssl',
       ],
     }],
+    ['enable_webvr==1', {
+      'sources': [
+        'browser/vr/vr_device.cc',
+        'browser/vr/vr_device.h',
+        'browser/vr/vr_device_manager.cc',
+        'browser/vr/vr_device_manager.h',
+        'browser/vr/vr_device_provider.h',
+      ]
+    }],
+    ['enable_webvr==1 and OS=="android"', {
+      'sources': [
+        'browser/vr/android/cardboard/cardboard_vr_device.cc',
+        'browser/vr/android/cardboard/cardboard_vr_device.h',
+        'browser/vr/android/cardboard/cardboard_vr_device_provider.cc',
+        'browser/vr/android/cardboard/cardboard_vr_device_provider.h',
+      ],
+    }],
   ],
 }
diff --git a/content/content_common.gypi b/content/content_common.gypi
index 77255c2..d507356 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -340,6 +340,8 @@
       'common/gpu/image_transport_surface_win.cc',
       'common/gpu/media/fake_video_decode_accelerator.cc',
       'common/gpu/media/fake_video_decode_accelerator.h',
+      'common/gpu/media/gpu_jpeg_decode_accelerator.cc',
+      'common/gpu/media/gpu_jpeg_decode_accelerator.h',
       'common/gpu/media/gpu_video_accelerator_util.cc',
       'common/gpu/media/gpu_video_accelerator_util.h',
       'common/gpu/media/gpu_video_decode_accelerator.cc',
@@ -890,6 +892,8 @@
       ],
       'sources': [
         'common/gpu/media/va_surface.h',
+        'common/gpu/media/vaapi_jpeg_decode_accelerator.cc',
+        'common/gpu/media/vaapi_jpeg_decode_accelerator.h',
         'common/gpu/media/vaapi_jpeg_decoder.cc',
         'common/gpu/media/vaapi_jpeg_decoder.h',
         'common/gpu/media/vaapi_picture.cc',
diff --git a/content/content_common_mojo_bindings.gyp b/content/content_common_mojo_bindings.gyp
index 4f01043..528fa67 100644
--- a/content/content_common_mojo_bindings.gyp
+++ b/content/content_common_mojo_bindings.gyp
@@ -18,6 +18,7 @@
           'common/presentation/presentation_service.mojom',
           'common/process_control.mojom',
           'common/render_frame_setup.mojom',
+          'common/vr_service.mojom',
 
           # NOTE: Sources duplicated in
           # //content/public/common/BUILD.gn:mojo_bindings.
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index aaf0c40..1089688 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -837,6 +837,14 @@
     ['use_seccomp_bpf==1', {
       'defines': ['USE_SECCOMP_BPF'],
     }],
+    ['enable_webvr==1', {
+      'sources': [
+        'renderer/vr/vr_dispatcher.cc',
+        'renderer/vr/vr_dispatcher.h',
+        'renderer/vr/vr_type_converters.cc',
+        'renderer/vr/vr_type_converters.h',
+      ]
+    }],
   ],
   'target_conditions': [
     ['OS=="android"', {
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index 71e493b..c6a59fc 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -160,22 +160,9 @@
   bool dead_on_arrival = false;
 
 #if defined(OS_WIN)
-  base::MessageLoop::Type loop_type = base::MessageLoop::TYPE_IO;
   // Use a UI message loop because ANGLE and the desktop GL platform can
   // create child windows to render to.
-  // TODO(ananta) : Recent changes to the UI message pump class on Windows
-  // will cause delays in tasks getting processed in the GPU process which
-  // will show up on the bots in webgl conformance tests, perf tests etc.
-  // It will be great if we can work around the issues with desktop GL and
-  // avoid creating a child window in the GPU process which requires a UI
-  // message pump.
-  if ((command_line.HasSwitch(switches::kUseGL) &&
-       command_line.GetSwitchValueASCII(switches::kUseGL) == "desktop") ||
-      (command_line.HasSwitch(switches::kUseANGLE) &&
-       command_line.GetSwitchValueASCII(switches::kUseANGLE) == "gl")) {
-    loop_type = base::MessageLoop::TYPE_UI;
-  }
-  base::MessageLoop main_message_loop(loop_type);
+  base::MessageLoop main_message_loop(base::MessageLoop::TYPE_UI);
 #elif defined(OS_LINUX) && defined(USE_X11)
   // We need a UI loop so that we can grab the Expose events. See GLSurfaceGLX
   // and https://crbug.com/326995.
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 988a87a..2f0ba19 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -57,6 +57,10 @@
   ]
 
   DEPRECATED_java_in_dir = "java/src"
+
+  if (enable_webvr) {
+    deps += [ "//third_party/cardboard-java:cardboard-java" ]
+  }
 }
 
 java_strings_grd("content_strings_grd") {
@@ -151,6 +155,13 @@
 generate_jni("content_jni_headers") {
   sources = rebase_path(content_jni_gypi_values.sources, "", "../..")
   jni_package = "content"
+
+  if (enable_webvr) {
+    sources += [ "//content/public/android/java/src/org/chromium/content/browser/input/CardboardVRDevice.java" ]
+    deps = [
+      "//third_party/cardboard-java:cardboard-java",
+    ]
+  }
 }
 
 group("jni") {
@@ -174,6 +185,7 @@
     "//mojo/android:system_java",
     "//net/android:net_java",
     "//net/android:net_java_test_support",
+    "//third_party/android_tools:android_support_v13_java",
     "//third_party/mojo/src/mojo/public/interfaces/bindings/tests:test_interfaces_java",
     "//third_party/mojo/src/mojo/public/java:bindings",
     "//third_party/mojo/src/mojo/public/java:system",
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index 0ac0bf1e..2c6d1bb8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -2378,6 +2378,11 @@
         }
     }
 
+    @CalledByNative
+    private void forceUpdateImeAdapter(long nativeImeAdapterAndroid) {
+        mImeAdapter.attach(nativeImeAdapterAndroid);
+    }
+
     @SuppressWarnings("unused")
     @CalledByNative
     private void setTitle(String title) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
index 40015bf..e7f3d3c 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
@@ -761,6 +761,12 @@
     }
 
     @CalledByNative
+    private void setAccessibilityNodeInfoViewIdResourceName(
+            AccessibilityNodeInfo node, String viewIdResourceName) {
+        node.setViewIdResourceName(viewIdResourceName);
+    }
+
+    @CalledByNative
     private void setAccessibilityNodeInfoContentDescription(
             AccessibilityNodeInfo node, String contentDescription, boolean annotateAsLink) {
         if (annotateAsLink) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
index db32292..8214f53 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
@@ -430,7 +430,8 @@
     @Override
     public boolean sendKeyEvent(KeyEvent event) {
         if (DEBUG) {
-            Log.w(TAG, "sendKeyEvent [" + event.getAction() + "] [" + event.getKeyCode() + "]");
+            Log.w(TAG, "sendKeyEvent [" + event.getAction() + "] [" + event.getKeyCode() + "] ["
+                            + event.getUnicodeChar() + "]");
         }
 
         int action = event.getAction();
@@ -438,7 +439,8 @@
         int unicodeChar = event.getUnicodeChar();
 
         // If this isn't a KeyDown event, no need to update composition state; just pass the key
-        // event through and return.
+        // event through and return. But note that some keys, such as enter, may actually be
+        // handled on ACTION_UP in Blink.
         if (action != KeyEvent.ACTION_DOWN) {
             mImeAdapter.translateAndSendNativeEvents(event);
             return true;
@@ -455,11 +457,7 @@
         } else if (keycode == KeyEvent.KEYCODE_ENTER) {
             // Finish text composition when pressing enter, as that may submit a form field.
             // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed.
-            beginBatchEdit();
             finishComposingText();
-            mImeAdapter.translateAndSendNativeEvents(event);
-            endBatchEdit();
-            return true;
         } else if ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) != 0) {
             // Store a pending accent character and make it the current composition.
             int pendingAccent = unicodeChar & KeyCharacterMap.COMBINING_ACCENT_MASK;
@@ -468,40 +466,42 @@
             setComposingText(builder.toString(), 1);
             mPendingAccent = pendingAccent;
             return true;
-        }
-
-        if (unicodeChar != 0) {
-            if (mPendingAccent != 0) {
-                int combined = KeyEvent.getDeadChar(mPendingAccent, unicodeChar);
-                if (combined != 0) {
-                    StringBuilder builder = new StringBuilder();
-                    builder.appendCodePoint(combined);
-                    commitText(builder.toString(), 1);
-                    return true;
-                }
-                // Noncombinable character; commit the accent character and fall through to sending
-                // the key event for the character afterwards.
-                finishComposingText();
+        } else if (mPendingAccent != 0 && unicodeChar != 0) {
+            int combined = KeyEvent.getDeadChar(mPendingAccent, unicodeChar);
+            if (combined != 0) {
+                StringBuilder builder = new StringBuilder();
+                builder.appendCodePoint(combined);
+                commitText(builder.toString(), 1);
+                return true;
             }
-
-            // Update the mEditable state to reflect what Blink will do in response to the KeyDown
-            // for a unicode-mapped key event.
-            int selectionStart = Selection.getSelectionStart(mEditable);
-            int selectionEnd = Selection.getSelectionEnd(mEditable);
-            if (selectionStart > selectionEnd) {
-                int temp = selectionStart;
-                selectionStart = selectionEnd;
-                selectionEnd = temp;
-            }
-            mEditable.replace(selectionStart, selectionEnd,
-                    Character.toString((char) unicodeChar));
+            // Noncombinable character; commit the accent character and fall through to sending
+            // the key event for the character afterwards.
+            finishComposingText();
         }
-
+        replaceSelectionWithUnicodeChar(unicodeChar);
         mImeAdapter.translateAndSendNativeEvents(event);
         return true;
     }
 
     /**
+     * Update the mEditable state to reflect what Blink will do in response to the KeyDown
+     * for a unicode-mapped key event.
+     * @param unicodeChar The Unicode character to update selection with.
+     */
+    private void replaceSelectionWithUnicodeChar(int unicodeChar) {
+        if (unicodeChar == 0) return;
+        int selectionStart = Selection.getSelectionStart(mEditable);
+        int selectionEnd = Selection.getSelectionEnd(mEditable);
+        if (selectionStart > selectionEnd) {
+            int temp = selectionStart;
+            selectionStart = selectionEnd;
+            selectionEnd = temp;
+        }
+        mEditable.replace(selectionStart, selectionEnd, Character.toString((char) unicodeChar));
+        updateSelectionIfRequired();
+    }
+
+    /**
      * @see BaseInputConnection#finishComposingText()
      */
     @Override
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/CardboardVRDevice.java b/content/public/android/java/src/org/chromium/content/browser/input/CardboardVRDevice.java
new file mode 100644
index 0000000..280e3bfe
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/input/CardboardVRDevice.java
@@ -0,0 +1,88 @@
+// 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.content.Context;
+
+import com.google.vrtoolkit.cardboard.CardboardDeviceParams;
+import com.google.vrtoolkit.cardboard.FieldOfView;
+import com.google.vrtoolkit.cardboard.HeadMountedDisplay;
+import com.google.vrtoolkit.cardboard.HeadMountedDisplayManager;
+import com.google.vrtoolkit.cardboard.ScreenParams;
+import com.google.vrtoolkit.cardboard.sensors.HeadTracker;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+/**
+ * This is the implementation of the C++ counterpart CardboardVRDevice.
+ */
+@JNINamespace("content")
+class CardboardVRDevice {
+    private static final String TAG = "CardboardVRDevice";
+    private final HeadMountedDisplayManager mHMDManager;
+    private final HeadTracker mHeadTracker;
+
+    @CalledByNative
+    private static CardboardVRDevice create(Context context) {
+        return new CardboardVRDevice(context);
+    }
+
+    private CardboardVRDevice(Context context) {
+        mHMDManager = new HeadMountedDisplayManager(context);
+
+        mHeadTracker = HeadTracker.createFromContext(context);
+        mHeadTracker.setNeckModelEnabled(true);
+        mHeadTracker.startTracking();
+    }
+
+    @CalledByNative
+    private void getFieldOfView(float[] outFov) {
+        HeadMountedDisplay hmd = mHMDManager.getHeadMountedDisplay();
+        CardboardDeviceParams deviceParams = hmd.getCardboardDeviceParams();
+        FieldOfView fov = deviceParams.getLeftEyeMaxFov();
+        outFov[0] = fov.getTop();
+        outFov[1] = fov.getBottom();
+        outFov[2] = fov.getLeft();
+        outFov[3] = fov.getRight();
+    }
+
+    @CalledByNative
+    private float getIpd() {
+        HeadMountedDisplay hmd = mHMDManager.getHeadMountedDisplay();
+        CardboardDeviceParams deviceParams = hmd.getCardboardDeviceParams();
+        return deviceParams.getInterLensDistance();
+    }
+
+    @CalledByNative
+    private String getDeviceName() {
+        HeadMountedDisplay hmd = mHMDManager.getHeadMountedDisplay();
+        CardboardDeviceParams deviceParams = hmd.getCardboardDeviceParams();
+        return deviceParams.getVendor() + " " + deviceParams.getModel();
+    }
+
+    @CalledByNative
+    private void getScreenSize(int[] outSize) {
+        HeadMountedDisplay hmd = mHMDManager.getHeadMountedDisplay();
+        ScreenParams screenParams = hmd.getScreenParams();
+        outSize[0] = screenParams.getWidth();
+        outSize[1] = screenParams.getHeight();
+    }
+
+    @CalledByNative
+    private void getSensorState(float[] outMatrix) {
+        mHeadTracker.getLastHeadView(outMatrix, 0);
+    }
+
+    @CalledByNative
+    private void stopTracking() {
+        mHeadTracker.stopTracking();
+    }
+
+    @CalledByNative
+    private void resetSensor() {
+        mHeadTracker.resetTracker();
+    }
+}
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
index 5050aee..0e89f24 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
@@ -32,6 +32,7 @@
 import org.chromium.content_shell_apk.ContentShellTestBase;
 
 import java.util.ArrayList;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Integration tests for text input using cases based on fixed regressions.
@@ -327,21 +328,8 @@
     public void testFinishComposingText() throws Throwable {
         DOMUtils.focusNode(mWebContents, "input_radio");
         assertWaitForKeyboardStatus(false);
-        DOMUtils.focusNode(mWebContents, "textarea");
-        assertWaitForKeyboardStatus(true);
-        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                try {
-                    return "textarea".equals(DOMUtils.getFocusedNode(mWebContents));
-                } catch (Exception e) {
-                    return false;
-                }
-            }
-        }));
 
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+        focusElement("textarea");
 
         commitText(mConnection, "hllo", 1);
         waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 1, "hllo", 4, 4, -1, -1);
@@ -416,16 +404,12 @@
     */
     @DisabledTest
     public void testKeyCodesWhileComposingText() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "textarea");
-        assertWaitForKeyboardStatus(true);
+        focusElement("textarea");
 
         // The calls below are a reflection of what the stock Google Keyboard (Android 4.4) sends
         // when the noted key is touched on screen.  Exercise care when altering to make sure
         // that the test reflects reality.  If this test breaks, it's possible that code has
         // changed and different calls need to be made instead.
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
-
         // H
         expectUpdateStateCall(mConnection);
         setComposingText(mConnection, "h", 1);
@@ -490,16 +474,12 @@
     */
     @DisabledTest
     public void testKeyCodesWhileSwipingText() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "textarea");
-        assertWaitForKeyboardStatus(true);
+        focusElement("textarea");
 
         // The calls below are a reflection of what the stock Google Keyboard (Android 4.4) sends
         // when the word is swiped on the soft keyboard.  Exercise care when altering to make sure
         // that the test reflects reality.  If this test breaks, it's possible that code has
         // changed and different calls need to be made instead.
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
-
         // "three"
         expectUpdateStateCall(mConnection);
         setComposingText(mConnection, "three", 1);
@@ -536,15 +516,8 @@
         waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 1, smiley, 2, 2, -1, -1);
 
         // DEL, sent via dispatchKeyEvent like it is in Android WebView or a physical keyboard.
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mContentViewCore.dispatchKeyEvent(
-                        new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
-                mContentViewCore.dispatchKeyEvent(
-                        new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
-            }
-        });
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
 
         waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 2, "", 0, 0, -1, -1);
 
@@ -558,11 +531,7 @@
     @SmallTest
     @Feature({"TextInput", "Main"})
     public void testBackspaceKeycode() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "textarea");
-        assertWaitForKeyboardStatus(true);
-
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+        focusElement("textarea");
 
         // H
         expectUpdateStateCall(mConnection);
@@ -581,15 +550,8 @@
         assertEquals("ho", mConnection.getTextBeforeCursor(9, 0));
 
         // DEL, sent via dispatchKeyEvent like it is in Android WebView or a physical keyboard.
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mContentViewCore.dispatchKeyEvent(
-                        new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
-                mContentViewCore.dispatchKeyEvent(
-                        new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
-            }
-        });
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
 
         // DEL
         expectUpdateStateCall(mConnection);
@@ -601,11 +563,7 @@
     @SmallTest
     @Feature({"TextInput", "Main"})
     public void testRepeatBackspaceKeycode() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "textarea");
-        assertWaitForKeyboardStatus(true);
-
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+        focusElement("textarea");
 
         // H
         expectUpdateStateCall(mConnection);
@@ -625,17 +583,9 @@
 
         // Multiple keydowns should each delete one character (this is for physical keyboard
         // key-repeat).
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mContentViewCore.dispatchKeyEvent(
-                        new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
-                mContentViewCore.dispatchKeyEvent(
-                        new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
-                mContentViewCore.dispatchKeyEvent(
-                        new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
-            }
-        });
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
 
         // DEL
         expectUpdateStateCall(mConnection);
@@ -644,19 +594,14 @@
         assertEquals("", mConnection.getTextBeforeCursor(9, 0));
     }
 
-
     @SmallTest
     @Feature({"TextInput", "Main"})
     public void testKeyCodesWhileTypingText() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "textarea");
-        assertWaitForKeyboardStatus(true);
+        focusElement("textarea");
 
         // The calls below are a reflection of what the Hacker's Keyboard sends when the noted
         // key is touched on screen.  Exercise care when altering to make sure that the test
         // reflects reality.
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
-
         // H
         expectUpdateStateCall(mConnection);
         commitText(mConnection, "h", 1);
@@ -729,92 +674,104 @@
 
     @SmallTest
     @Feature({"TextInput", "Main"})
-    public void testAccentKeyCodesFromPhysicalKeyboard() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "textarea");
-        assertWaitForKeyboardStatus(true);
+    public void testPhysicalKeyboard() throws Throwable {
+        focusElement("textarea");
+        // Type 'a' using a physical keyboard.
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_A));
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 1, "a", 1, 1, -1, -1);
 
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+        // Type 'enter' key.
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER));
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 2, "a\n\n", 2, 2, -1, -1);
+
+        // Type 'b'.
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_B));
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 3, "a\nb", 3, 3, -1, -1);
+    }
+
+    @SmallTest
+    @Feature({"TextInput", "Main"})
+    public void testPhysicalKeyboard_AccentKeyCodes() throws Throwable {
+        focusElement("textarea");
 
         // h
-        dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_H));
-        dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_H));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_H));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_H));
         assertEquals("h", mConnection.getTextBeforeCursor(9, 0));
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 1, "h", 1, 1, -1, -1);
 
         // ALT-i  (circumflex accent key on virtual keyboard)
-        dispatchKeyEvent(
-                mConnection, new KeyEvent(
-                        0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
+        dispatchKeyEvent(new KeyEvent(
+                0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
         assertUpdateStateCall(mConnection, 1000);
         assertEquals("hˆ", mConnection.getTextBeforeCursor(9, 0));
-        dispatchKeyEvent(
-                mConnection, new KeyEvent(
-                        0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
+        dispatchKeyEvent(new KeyEvent(
+                0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
         assertEquals("hˆ", mConnection.getTextBeforeCursor(9, 0));
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 2, "hˆ", 2, 2, 1, 2);
 
         // o
-        dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_O));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_O));
         assertUpdateStateCall(mConnection, 1000);
         assertEquals("hô", mConnection.getTextBeforeCursor(9, 0));
-        dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_O));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_O));
         assertEquals("hô", mConnection.getTextBeforeCursor(9, 0));
-        assertEquals(-1, mConnection.getComposingSpanEnd(mConnection.getEditable()));
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 3, "hô", 2, 2, -1, -1);
 
         // ALT-i
-        dispatchKeyEvent(
-                mConnection, new KeyEvent(
-                        0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
+        dispatchKeyEvent(new KeyEvent(
+                0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
         assertUpdateStateCall(mConnection, 1000);
-        dispatchKeyEvent(
-                mConnection, new KeyEvent(
-                        0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
+        dispatchKeyEvent(new KeyEvent(
+                0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
         assertEquals("hôˆ", mConnection.getTextBeforeCursor(9, 0));
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 4, "hôˆ", 3, 3, 2, 3);
 
         // ALT-i again should have no effect
-        dispatchKeyEvent(
-                mConnection, new KeyEvent(
-                        0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
+        dispatchKeyEvent(new KeyEvent(
+                0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
         assertUpdateStateCall(mConnection, 1000);
-        dispatchKeyEvent(
-                mConnection, new KeyEvent(
-                        0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
+        dispatchKeyEvent(new KeyEvent(
+                0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
         assertEquals("hôˆ", mConnection.getTextBeforeCursor(9, 0));
 
         // b (cannot be accented, should just appear after)
-        dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B));
         assertUpdateStateCall(mConnection, 1000);
         assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0));
-        dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_B));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_B));
         assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0));
-        assertEquals(-1, mConnection.getComposingSpanEnd(mConnection.getEditable()));
+        // A transitional state due to finishComposingText.
+        waitAndVerifyStates(mConnection.mImeUpdateQueue, 5, "hôˆ", 3, 3, -1, -1);
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 6, "hôˆb", 4, 4, -1, -1);
 
         // ALT-i
-        dispatchKeyEvent(
-                mConnection, new KeyEvent(
-                        0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
+        dispatchKeyEvent(new KeyEvent(
+                0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
         assertUpdateStateCall(mConnection, 1000);
-        dispatchKeyEvent(
-                mConnection, new KeyEvent(
-                        0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
+        dispatchKeyEvent(new KeyEvent(
+                0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
         assertEquals("hôˆbˆ", mConnection.getTextBeforeCursor(9, 0));
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 7, "hôˆbˆ", 5, 5, 4, 5);
 
         // Backspace
-        dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
         assertUpdateStateCall(mConnection, 1000);
         assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0));
-        dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
         assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0));
-        assertEquals(-1, mConnection.getComposingSpanEnd(mConnection.getEditable()));
+        // A transitional state due to finishComposingText in deleteSurroundingTextImpl.
+        waitAndVerifyStates(mConnection.mImeUpdateQueue, 8, "hôˆbˆ", 5, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 9, "hôˆb", 4, 4, -1, -1);
     }
 
     @SmallTest
     @Feature({"TextInput", "Main"})
     public void testSetComposingRegionOutOfBounds() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "textarea");
-        assertWaitForKeyboardStatus(true);
-
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+        focusElement("textarea");
         setComposingText(mConnection, "hello", 1);
 
         setComposingRegion(mConnection, 0, 0);
@@ -824,71 +781,60 @@
 
     @SmallTest
     @Feature({"TextInput", "Main"})
-    public void testEnterKeyEventWhileComposingText() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "input_radio");
-        assertWaitForKeyboardStatus(false);
-        DOMUtils.focusNode(mWebContents, "textarea");
-        assertWaitForKeyboardStatus(true);
-        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                try {
-                    return "textarea".equals(DOMUtils.getFocusedNode(mWebContents));
-                } catch (Exception e) {
-                    return false;
-                }
-            }
-        }));
+    public void testEnterKey_AfterCommitText() throws Throwable {
+        focusElement("textarea");
 
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+        commitText(mConnection, "hello", 1);
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 1, "hello", 5, 5, -1, -1);
+
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER));
+        // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed.
+        // The second new line is not a user visible/editable one, it is a side-effect of Blink
+        // using <br> internally. This only happens when \n is at the end.
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 2, "hello\n\n", 6, 6, -1, -1);
+
+        commitText(mConnection, "world", 1);
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 3, "hello\nworld", 11, 11, -1, -1);
+    }
+
+    @SmallTest
+    @Feature({"TextInput", "Main"})
+    public void testEnterKey_WhileComposingText() throws Throwable {
+        focusElement("textarea");
 
         setComposingText(mConnection, "hello", 1);
         waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 1, "hello", 5, 5, 0, 5);
 
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mConnection.sendKeyEvent(
-                        new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
-                mConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER));
-            }
-        });
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER));
 
         // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed.
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 2, "hello", 5, 5, -1, -1);
+        // A transitional state due to finishComposingText.
+        waitAndVerifyStates(mConnection.mImeUpdateQueue, 2, "hello", 5, 5, -1, -1);
         // The second new line is not a user visible/editable one, it is a side-effect of Blink
-        // using <br> internally.
-        // TODO(changwan): [ 6, 6 ] is different from what we report to
-        // InputMethodManager#updateSelection().
-        // Uncomment this once it's fixed.
-        waitAndVerifyStates(mConnection.mImeUpdateQueue, 3, "hello\n\n", 6, 6, -1, -1);
+        // using <br> internally. This only happens when \n is at the end.
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 3, "hello\n\n", 6, 6, -1, -1);
+
+        commitText(mConnection, "world", 1);
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 4, "hello\nworld", 11, 11, -1, -1);
     }
 
     @SmallTest
     @Feature({"TextInput", "Main"})
     public void testDpadKeyCodesWhileSwipingText() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "textarea");
-        assertWaitForKeyboardStatus(true);
-
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+        focusElement("textarea");
 
         // DPAD_CENTER should cause keyboard to appear
         expectUpdateStateCall(mConnection);
-        KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);
-        mContentViewCore.dispatchKeyEvent(event);
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER));
         assertUpdateStateCall(mConnection, 1000);
     }
 
     @SmallTest
     @Feature({"TextInput", "Main"})
     public void testTransitionsWhileComposingText() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "textarea");  // Default with autocomplete="on"
-        assertWaitForKeyboardStatus(true);
-
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+        focusElement("textarea"); // Default with autocomplete="on"
 
         // H
         // Since autocomplete="on" by default, COMPOSITION_KEY_CODE is emitted as key code
@@ -908,11 +854,7 @@
     @SmallTest
     @Feature({"TextInput", "Main"})
     public void testTransitionsWhileEmittingKeyCode() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "textarea2");  // autocomplete="off"
-        assertWaitForKeyboardStatus(true);
-
-        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+        focusElement("textarea2"); // Default with autocomplete="off"
 
         // H
         // Although autocomplete="off", we still emit COMPOSITION_KEY_CODE since synthesized
@@ -959,15 +901,7 @@
         assertWaitForSelectActionBarStatus(true);
         assertTrue(mContentViewCore.hasSelection());
 
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                final KeyEvent downKeyEvent = new KeyEvent(
-                        KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
-                mImeAdapter.dispatchKeyEvent(downKeyEvent);
-            }
-        });
-
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN));
         assertWaitForSelectActionBarStatus(true);
         assertTrue(mContentViewCore.hasSelection());
     }
@@ -1004,8 +938,6 @@
         }));
     }
 
-    // TODO(changwan): replace calls to this by waitAndVerifyStatesAndCalls once
-    // we figure out why updateSelection is called with incorrect values.
     private void waitAndVerifyStates(final ArrayList<TestImeState> states, final int index,
             String text, final int selectionStart, final int selectionEnd,
             final int compositionStart, final int compositionEnd) throws InterruptedException {
@@ -1183,7 +1115,7 @@
         });
     }
 
-    private void dispatchKeyEvent(final AdapterInputConnection connection, final KeyEvent event) {
+    private void dispatchKeyEvent(final KeyEvent event) {
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
@@ -1192,6 +1124,24 @@
         });
     }
 
+    private void focusElement(final String id) throws InterruptedException, TimeoutException {
+        DOMUtils.focusNode(mWebContents, id);
+        assertWaitForKeyboardStatus(true);
+        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                try {
+                    return id.equals(DOMUtils.getFocusedNode(mWebContents));
+                } catch (Exception e) {
+                    return false;
+                }
+            }
+        }));
+        // When we focus another element, the connection may be recreated.
+        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
+        waitAndVerifyStatesAndCalls(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+    }
+
     private static class TestAdapterInputConnectionFactory extends
             ImeAdapter.AdapterInputConnectionFactory {
         @Override
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 0a97ef2..acd568b 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -274,12 +274,6 @@
   virtual void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                               int child_process_id) {}
 
-  // This is called on the process launching thread, when files that should be
-  // passed to the client have been opened.  It allows command line switches to
-  // be added to tell the client where to find its files.
-  virtual void AppendMappedFileCommandLineSwitches(
-      base::CommandLine* command_line) {}
-
   // Returns the locale used by the application.
   // This is called on the UI and IO threads.
   virtual std::string GetApplicationLocale();
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 1f7e4b0..5c8efcde 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -477,6 +477,9 @@
 // Enables WebGL rendering into a scanout buffer for overlay support.
 const char kEnableWebGLImageChromium[] = "enable-webgl-image-chromium";
 
+// Enables interaction with virtual reality devices.
+const char kEnableWebVR[] = "enable-webvr";
+
 // Enable rasterizer that writes directly to GPU memory associated with tiles.
 const char kEnableZeroCopy[]                = "enable-zero-copy";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index aead5ac..4b45d2e 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -142,6 +142,7 @@
 CONTENT_EXPORT extern const char kEnableVtune[];
 CONTENT_EXPORT extern const char kEnableWebGLDraftExtensions[];
 CONTENT_EXPORT extern const char kEnableWebGLImageChromium[];
+CONTENT_EXPORT extern const char kEnableWebVR[];
 CONTENT_EXPORT extern const char kEnableZeroCopy[];
 CONTENT_EXPORT extern const char kExplicitlyAllowedPorts[];
 CONTENT_EXPORT extern const char kExtraPluginDir[];
diff --git a/content/public/common/frame_navigate_params.cc b/content/public/common/frame_navigate_params.cc
index 1aef9e5..bcc90b5e 100644
--- a/content/public/common/frame_navigate_params.cc
+++ b/content/public/common/frame_navigate_params.cc
@@ -9,6 +9,8 @@
 FrameNavigateParams::FrameNavigateParams()
     : page_id(0),
       nav_entry_id(0),
+      item_sequence_number(-1),
+      document_sequence_number(-1),
       transition(ui::PAGE_TRANSITION_LINK),
       should_update_history(false) {
 }
diff --git a/content/public/common/frame_navigate_params.h b/content/public/common/frame_navigate_params.h
index 884a1d1..e3b93342 100644
--- a/content/public/common/frame_navigate_params.h
+++ b/content/public/common/frame_navigate_params.h
@@ -33,6 +33,17 @@
   // means. If the navigation was renderer-initiated, this value is 0.
   int nav_entry_id;
 
+  // The item sequence number identifies each stop in the session history.  It
+  // is unique within the renderer process and makes a best effort to be unique
+  // across browser sessions (using a renderer process timestamp).
+  int64 item_sequence_number;
+
+  // The document sequence number is used to identify cross-document navigations
+  // in session history.  It increments for each new document and is unique in
+  // the same way as |item_sequence_number|.  In-page navigations get a new item
+  // sequence number but the same document sequence number.
+  int64 document_sequence_number;
+
   // URL of the page being loaded.
   GURL url;
 
diff --git a/content/public/renderer/media_stream_audio_renderer.h b/content/public/renderer/media_stream_audio_renderer.h
index 21479b9..4b4fe37 100644
--- a/content/public/renderer/media_stream_audio_renderer.h
+++ b/content/public/renderer/media_stream_audio_renderer.h
@@ -5,8 +5,13 @@
 #ifndef CONTENT_PUBLIC_RENDERER_MEDIA_STREAM_AUDIO_RENDERER_H_
 #define CONTENT_PUBLIC_RENDERER_MEDIA_STREAM_AUDIO_RENDERER_H_
 
+#include <string>
+
+#include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
+#include "media/base/audio_renderer_sink.h"
+#include "url/gurl.h"
 
 namespace content {
 
@@ -29,6 +34,22 @@
   // Sets the output volume.
   virtual void SetVolume(float volume) = 0;
 
+  // Attempts to switch the audio output device.
+  // Once the attempt is finished, |callback| is invoked with the
+  // result of the operation passed as a parameter. The result is a value from
+  // the media::SwitchOutputDeviceResult enum.
+  // There is no guarantee about the thread where |callback| will
+  // be invoked, so users are advised to use media::BindToCurrentLoop() to
+  // ensure that |callback| runs on the correct thread.
+  // Note also that copy constructors and destructors for arguments bound to
+  // |callback| may run on arbitrary threads as |callback| is moved across
+  // threads. It is advisable to bind arguments such that they are released by
+  // |callback| when it runs in order to avoid surprises.
+  virtual void SwitchOutputDevice(
+      const std::string& device_id,
+      const GURL& security_origin,
+      const media::SwitchOutputDeviceCB& callback) = 0;
+
   // Time stamp that reflects the current render time. Should not be updated
   // when paused.
   virtual base::TimeDelta GetCurrentRenderTime() const = 0;
diff --git a/content/public/renderer/render_view_observer.h b/content/public/renderer/render_view_observer.h
index 6345aea5..12095c4 100644
--- a/content/public/renderer/render_view_observer.h
+++ b/content/public/renderer/render_view_observer.h
@@ -80,9 +80,10 @@
   virtual void DidUpdateLayout() {}
 
   // These match the RenderView methods.
-  virtual void DidHandleMouseEvent(const blink::WebMouseEvent& event) {}
   virtual void DidHandleGestureEvent(const blink::WebGestureEvent& event) {}
 
+  virtual void OnMouseDown(const blink::WebNode& mouse_down_node) {}
+
   // These match incoming IPCs.
   virtual void Navigate(const GURL& url) {}
   virtual void ClosePage() {}
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 821d975..05d09db 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -4,6 +4,7 @@
 
 #include "content/public/test/browser_test_utils.h"
 
+#include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/json/json_reader.h"
@@ -19,6 +20,7 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/web_contents/web_contents_view.h"
 #include "content/common/input/synthetic_web_input_event_builders.h"
+#include "content/common/view_messages.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/dom_operation_notification_details.h"
 #include "content/public/browser/histogram_fetcher.h"
@@ -959,4 +961,34 @@
   return false;
 }
 
+FrameWatcher::FrameWatcher()
+    : BrowserMessageFilter(ViewMsgStart), frames_to_wait_(0) {
+}
+
+FrameWatcher::~FrameWatcher() {
+}
+
+void FrameWatcher::ReceivedFrameSwap() {
+  --frames_to_wait_;
+  if (frames_to_wait_ == 0)
+    quit_.Run();
+}
+
+bool FrameWatcher::OnMessageReceived(const IPC::Message& message) {
+  if (message.type() == ViewHostMsg_SwapCompositorFrame::ID) {
+    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+                            base::Bind(&FrameWatcher::ReceivedFrameSwap, this));
+  }
+  return false;
+}
+
+void FrameWatcher::WaitFrames(int frames_to_wait) {
+  if (frames_to_wait <= 0)
+    return;
+  base::RunLoop run_loop;
+  base::AutoReset<base::Closure> reset_quit(&quit_, run_loop.QuitClosure());
+  base::AutoReset<int> reset_frames_to_wait(&frames_to_wait_, frames_to_wait);
+  run_loop.Run();
+}
+
 }  // namespace content
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 7faff192..51cabcf 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -15,6 +15,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/process/process.h"
 #include "base/strings/string16.h"
+#include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/render_process_host_observer.h"
@@ -400,6 +401,30 @@
   DISALLOW_COPY_AND_ASSIGN(WebContentsAddedObserver);
 };
 
+// Watches compositor frame changes, blocking until a frame has been
+// composited. This class is intended to be run on the main thread; to
+// synchronize the main thread against the impl thread.
+class FrameWatcher : public BrowserMessageFilter {
+ public:
+  FrameWatcher();
+
+  // Wait for |frames_to_wait| swap mesages from the compositor.
+  void WaitFrames(int frames_to_wait);
+
+ private:
+  ~FrameWatcher() override;
+
+  // Overridden BrowserMessageFilter methods.
+  bool OnMessageReceived(const IPC::Message& message) override;
+
+  void ReceivedFrameSwap();
+
+  int frames_to_wait_;
+  base::Closure quit_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameWatcher);
+};
+
 }  // namespace content
 
 #endif  // CONTENT_PUBLIC_TEST_BROWSER_TEST_UTILS_H_
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 2626240..c56cf3e6 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -386,6 +386,30 @@
       InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(), false));
 }
 
+
+bool RenderViewTest::SimulateElementRightClick(const std::string& element_id) {
+  gfx::Rect bounds = GetElementBounds(element_id);
+  if (bounds.IsEmpty())
+    return false;
+  SimulatePointRightClick(bounds.CenterPoint());
+  return true;
+}
+
+void RenderViewTest::SimulatePointRightClick(const gfx::Point& point) {
+  WebMouseEvent mouse_event;
+  mouse_event.type = WebInputEvent::MouseDown;
+  mouse_event.button = WebMouseEvent::ButtonRight;
+  mouse_event.x = point.x();
+  mouse_event.y = point.y();
+  mouse_event.clickCount = 1;
+  RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_);
+  impl->OnMessageReceived(
+      InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(), false));
+  mouse_event.type = WebInputEvent::MouseUp;
+  impl->OnMessageReceived(
+      InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(), false));
+}
+
 void RenderViewTest::SimulateRectTap(const gfx::Rect& rect) {
   WebGestureEvent gesture_event;
   gesture_event.x = rect.CenterPoint().x();
diff --git a/content/public/test/render_view_test.h b/content/public/test/render_view_test.h
index de9397b..36ca0be 100644
--- a/content/public/test/render_view_test.h
+++ b/content/public/test/render_view_test.h
@@ -115,6 +115,14 @@
   // Sends a left mouse click at the |point|.
   void SimulatePointClick(const gfx::Point& point);
 
+  // Sends a right mouse click in the middle of the element with id
+  // |element_id|. Returns true if the event was sent, false otherwise
+  // (typically because the element was not found).
+  bool SimulateElementRightClick(const std::string& element_id);
+
+  // Sends a right mouse click at the |point|.
+  void SimulatePointRightClick(const gfx::Point& point);
+
   // Sends a tap at the |rect|.
   void SimulateRectTap(const gfx::Rect& rect);
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 9287ccb..9c078e4 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -199,4 +199,13 @@
     ]
     deps += [ "//media/mojo/services:proxy" ]
   }
+
+  if (enable_webvr) {
+    sources += [
+      "vr/vr_dispatcher.cc",
+      "vr/vr_dispatcher.h",
+      "vr/vr_type_converters.cc",
+      "vr/vr_type_converters.h",
+    ]
+  }
 }
diff --git a/content/renderer/accessibility/blink_ax_enum_conversion.cc b/content/renderer/accessibility/blink_ax_enum_conversion.cc
index 376426be1..a48994a 100644
--- a/content/renderer/accessibility/blink_ax_enum_conversion.cc
+++ b/content/renderer/accessibility/blink_ax_enum_conversion.cc
@@ -205,6 +205,8 @@
       return ui::AX_ROLE_MAIN;
     case blink::WebAXRoleMarquee:
       return ui::AX_ROLE_MARQUEE;
+    case blink::WebAXRoleMark:
+      return ui::AX_ROLE_MARK;
     case blink::WebAXRoleMath:
       return ui::AX_ROLE_MATH;
     case blink::WebAXRoleMenu:
diff --git a/content/renderer/cache_storage/cache_storage_dispatcher.cc b/content/renderer/cache_storage/cache_storage_dispatcher.cc
index 7eafde77..a80381c2 100644
--- a/content/renderer/cache_storage/cache_storage_dispatcher.cc
+++ b/content/renderer/cache_storage/cache_storage_dispatcher.cc
@@ -126,15 +126,9 @@
 void ClearCallbacksMapWithErrors(T* callbacks_map) {
   typename T::iterator iter(callbacks_map);
   while (!iter.IsAtEnd()) {
-#ifdef CRBUG_494884
     iter.GetCurrentValue()->onError(
         new blink::WebServiceWorkerCacheError(
             blink::WebServiceWorkerCacheErrorNotFound));
-#else
-    blink::WebServiceWorkerCacheError reason =
-        blink::WebServiceWorkerCacheErrorNotFound;
-    iter.GetCurrentValue()->onError(&reason);
-#endif
     callbacks_map->Remove(iter.GetCurrentKey());
     iter.Advance();
   }
@@ -361,11 +355,7 @@
   DCHECK_EQ(thread_id, CurrentWorkerId());
   WebServiceWorkerCacheStorage::CacheStorageCallbacks* callbacks =
       has_callbacks_.Lookup(request_id);
-#ifdef CRBUG_494884
   callbacks->onError(new blink::WebServiceWorkerCacheError(reason));
-#else
-  callbacks->onError(&reason);
-#endif
   has_callbacks_.Remove(request_id);
   has_times_.erase(request_id);
 }
@@ -377,11 +367,7 @@
   DCHECK_EQ(thread_id, CurrentWorkerId());
   WebServiceWorkerCacheStorage::CacheStorageWithCacheCallbacks* callbacks =
       open_callbacks_.Lookup(request_id);
-#ifdef CRBUG_494884
   callbacks->onError(new blink::WebServiceWorkerCacheError(reason));
-#else
-  callbacks->onError(&reason);
-#endif
   open_callbacks_.Remove(request_id);
   open_times_.erase(request_id);
 }
@@ -393,11 +379,7 @@
   DCHECK_EQ(thread_id, CurrentWorkerId());
   WebServiceWorkerCacheStorage::CacheStorageCallbacks* callbacks =
       delete_callbacks_.Lookup(request_id);
-#ifdef CRBUG_494884
   callbacks->onError(new blink::WebServiceWorkerCacheError(reason));
-#else
-  callbacks->onError(&reason);
-#endif
   delete_callbacks_.Remove(request_id);
   delete_times_.erase(request_id);
 }
@@ -409,11 +391,7 @@
   DCHECK_EQ(thread_id, CurrentWorkerId());
   WebServiceWorkerCacheStorage::CacheStorageKeysCallbacks* callbacks =
       keys_callbacks_.Lookup(request_id);
-#ifdef CRBUG_494884
   callbacks->onError(new blink::WebServiceWorkerCacheError(reason));
-#else
-  callbacks->onError(&reason);
-#endif
   keys_callbacks_.Remove(request_id);
   keys_times_.erase(request_id);
 }
@@ -425,11 +403,7 @@
   DCHECK_EQ(thread_id, CurrentWorkerId());
   WebServiceWorkerCacheStorage::CacheStorageMatchCallbacks* callbacks =
       match_callbacks_.Lookup(request_id);
-#ifdef CRBUG_494884
   callbacks->onError(new blink::WebServiceWorkerCacheError(reason));
-#else
-  callbacks->onError(&reason);
-#endif
   match_callbacks_.Remove(request_id);
   match_times_.erase(request_id);
 }
@@ -506,11 +480,7 @@
   DCHECK_EQ(thread_id, CurrentWorkerId());
   blink::WebServiceWorkerCache::CacheMatchCallbacks* callbacks =
       cache_match_callbacks_.Lookup(request_id);
-#ifdef CRBUG_494884
   callbacks->onError(new blink::WebServiceWorkerCacheError(reason));
-#else
-  callbacks->onError(&reason);
-#endif
   cache_match_callbacks_.Remove(request_id);
   cache_match_times_.erase(request_id);
 }
@@ -522,11 +492,7 @@
   DCHECK_EQ(thread_id, CurrentWorkerId());
   blink::WebServiceWorkerCache::CacheWithResponsesCallbacks* callbacks =
       cache_match_all_callbacks_.Lookup(request_id);
-#ifdef CRBUG_494884
   callbacks->onError(new blink::WebServiceWorkerCacheError(reason));
-#else
-  callbacks->onError(&reason);
-#endif
   cache_match_all_callbacks_.Remove(request_id);
   cache_match_all_times_.erase(request_id);
 }
@@ -538,11 +504,7 @@
   DCHECK_EQ(thread_id, CurrentWorkerId());
   blink::WebServiceWorkerCache::CacheWithRequestsCallbacks* callbacks =
       cache_keys_callbacks_.Lookup(request_id);
-#ifdef CRBUG_494884
   callbacks->onError(new blink::WebServiceWorkerCacheError(reason));
-#else
-  callbacks->onError(&reason);
-#endif
   cache_keys_callbacks_.Remove(request_id);
   cache_keys_times_.erase(request_id);
 }
@@ -554,11 +516,7 @@
   DCHECK_EQ(thread_id, CurrentWorkerId());
   blink::WebServiceWorkerCache::CacheBatchCallbacks* callbacks =
       cache_batch_callbacks_.Lookup(request_id);
-#ifdef CRBUG_494884
   callbacks->onError(new blink::WebServiceWorkerCacheError(reason));
-#else
-  callbacks->onError(&reason);
-#endif
   cache_batch_callbacks_.Remove(request_id);
   cache_batch_times_.erase(request_id);
 }
diff --git a/content/renderer/media/webmediaplayer_ms.cc b/content/renderer/media/webmediaplayer_ms.cc
index 07d630591..033f25c9 100644
--- a/content/renderer/media/webmediaplayer_ms.cc
+++ b/content/renderer/media/webmediaplayer_ms.cc
@@ -257,11 +257,20 @@
 }
 
 void WebMediaPlayerMS::setSinkId(const blink::WebString& device_id,
-                                 media::WebSetSinkIdCB* raw_web_callbacks) {
+                                 media::WebSetSinkIdCB* web_callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  scoped_ptr<media::WebSetSinkIdCB> web_callbacks(raw_web_callbacks);
-  web_callbacks->onError(new blink::WebSetSinkIdError(
-      blink::WebSetSinkIdError::ErrorTypeNotSupported, "Not Supported"));
+  std::string device_id_str(device_id.utf8());
+  GURL security_origin(frame_->securityOrigin().toString().utf8());
+  DVLOG(1) << __FUNCTION__
+           << "(" << device_id_str << ", " << security_origin << ")";
+  media::SwitchOutputDeviceCB callback =
+      media::ConvertToSwitchOutputDeviceCB(web_callback);
+  if (audio_renderer_.get()) {
+    audio_renderer_->SwitchOutputDevice(device_id_str, security_origin,
+                                        callback);
+  } else {
+    callback.Run(media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_SUPPORTED);
+  }
 }
 
 void WebMediaPlayerMS::setPreload(WebMediaPlayer::Preload preload) {
diff --git a/content/renderer/media/webmediaplayer_ms.h b/content/renderer/media/webmediaplayer_ms.h
index 607ba13c..3254f83 100644
--- a/content/renderer/media/webmediaplayer_ms.h
+++ b/content/renderer/media/webmediaplayer_ms.h
@@ -77,7 +77,7 @@
   virtual void setRate(double rate);
   virtual void setVolume(double volume);
   virtual void setSinkId(const blink::WebString& device_id,
-                         media::WebSetSinkIdCB* raw_web_callbacks);
+                         media::WebSetSinkIdCB* web_callback);
   virtual void setPreload(blink::WebMediaPlayer::Preload preload);
   virtual blink::WebTimeRanges buffered() const;
   virtual blink::WebTimeRanges seekable() const;
diff --git a/content/renderer/media/webrtc_audio_renderer.cc b/content/renderer/media/webrtc_audio_renderer.cc
index dcc153c..23090d1 100644
--- a/content/renderer/media/webrtc_audio_renderer.cc
+++ b/content/renderer/media/webrtc_audio_renderer.cc
@@ -106,6 +106,16 @@
     on_play_state_changed_.Run(media_stream_, &playing_state_);
   }
 
+  void SwitchOutputDevice(
+      const std::string& device_id,
+      const GURL& security_origin,
+      const media::SwitchOutputDeviceCB& callback) override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    DVLOG(1) << __FUNCTION__
+             << "(" << device_id << ", " << security_origin << ")";
+    delegate_->SwitchOutputDevice(device_id, security_origin, callback);
+  }
+
   base::TimeDelta GetCurrentRenderTime() const override {
     DCHECK(thread_checker_.CalledOnValidThread());
     return delegate_->GetCurrentRenderTime();
@@ -406,6 +416,17 @@
   OnPlayStateChanged(media_stream_, &playing_state_);
 }
 
+void WebRtcAudioRenderer::SwitchOutputDevice(
+    const std::string& device_id,
+    const GURL& security_origin,
+    const media::SwitchOutputDeviceCB& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(sink_);
+  DVLOG(1) << __FUNCTION__
+           << "(" << device_id << ", " << security_origin << ")";
+  sink_->SwitchOutputDevice(device_id, security_origin, callback);
+}
+
 base::TimeDelta WebRtcAudioRenderer::GetCurrentRenderTime() const {
   DCHECK(thread_checker_.CalledOnValidThread());
   base::AutoLock auto_lock(lock_);
diff --git a/content/renderer/media/webrtc_audio_renderer.h b/content/renderer/media/webrtc_audio_renderer.h
index 86a00a2f..1092d300 100644
--- a/content/renderer/media/webrtc_audio_renderer.h
+++ b/content/renderer/media/webrtc_audio_renderer.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_RENDERER_MEDIA_WEBRTC_AUDIO_RENDERER_H_
 #define CONTENT_RENDERER_MEDIA_WEBRTC_AUDIO_RENDERER_H_
 
+#include <string>
+
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/non_thread_safe.h"
@@ -114,6 +116,9 @@
   void Pause() override;
   void Stop() override;
   void SetVolume(float volume) override;
+  void SwitchOutputDevice(const std::string& device_id,
+                          const GURL& security_origin,
+                          const media::SwitchOutputDeviceCB& callback) override;
   base::TimeDelta GetCurrentRenderTime() const override;
   bool IsLocalRenderer() const override;
 
diff --git a/content/renderer/media/webrtc_local_audio_renderer.cc b/content/renderer/media/webrtc_local_audio_renderer.cc
index b400b454..22382d1 100644
--- a/content/renderer/media/webrtc_local_audio_renderer.cc
+++ b/content/renderer/media/webrtc_local_audio_renderer.cc
@@ -207,6 +207,20 @@
     sink_->SetVolume(volume);
 }
 
+void WebRtcLocalAudioRenderer::SwitchOutputDevice(
+    const std::string& device_id,
+    const GURL& security_origin,
+    const media::SwitchOutputDeviceCB& callback) {
+  DVLOG(1) << __FUNCTION__
+           << "(" << device_id << ", " << security_origin << ")";
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  if (sink_.get())
+    sink_->SwitchOutputDevice(device_id, security_origin, callback);
+  else
+    callback.Run(media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_SUPPORTED);
+}
+
 base::TimeDelta WebRtcLocalAudioRenderer::GetCurrentRenderTime() const {
   DCHECK(task_runner_->BelongsToCurrentThread());
   base::AutoLock auto_lock(thread_lock_);
diff --git a/content/renderer/media/webrtc_local_audio_renderer.h b/content/renderer/media/webrtc_local_audio_renderer.h
index 3ee17f0..784077e 100644
--- a/content/renderer/media/webrtc_local_audio_renderer.h
+++ b/content/renderer/media/webrtc_local_audio_renderer.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_RENDERER_MEDIA_WEBRTC_LOCAL_AUDIO_RENDERER_H_
 #define CONTENT_RENDERER_MEDIA_WEBRTC_LOCAL_AUDIO_RENDERER_H_
 
+#include <string>
 #include <vector>
 
 #include "base/callback.h"
@@ -62,6 +63,9 @@
   void Play() override;
   void Pause() override;
   void SetVolume(float volume) override;
+  void SwitchOutputDevice(const std::string& device_id,
+                          const GURL& security_origin,
+                          const media::SwitchOutputDeviceCB& callback) override;
   base::TimeDelta GetCurrentRenderTime() const override;
   bool IsLocalRenderer() const override;
 
diff --git a/content/renderer/pepper/pepper_webplugin_impl.cc b/content/renderer/pepper/pepper_webplugin_impl.cc
index 078f41f..f39c9ca 100644
--- a/content/renderer/pepper/pepper_webplugin_impl.cc
+++ b/content/renderer/pepper/pepper_webplugin_impl.cc
@@ -270,8 +270,7 @@
   return instance_->PrintBegin(print_params);
 }
 
-void PepperWebPluginImpl::printPage(int page_number, blink::WebCanvas* canvas,
-                                    bool unused) {
+void PepperWebPluginImpl::printPage(int page_number, blink::WebCanvas* canvas) {
   instance_->PrintPage(page_number, canvas);
 }
 
diff --git a/content/renderer/pepper/pepper_webplugin_impl.h b/content/renderer/pepper/pepper_webplugin_impl.h
index 3d821c68..5467d74d 100644
--- a/content/renderer/pepper/pepper_webplugin_impl.h
+++ b/content/renderer/pepper/pepper_webplugin_impl.h
@@ -83,8 +83,7 @@
   virtual bool isPrintScalingDisabled() override;
 
   virtual int printBegin(const blink::WebPrintParams& print_params) override;
-  void printPage(int page_number, blink::WebCanvas* canvas,
-                 bool unused) override;
+  void printPage(int page_number, blink::WebCanvas* canvas) override;
   virtual void printEnd() override;
 
   virtual bool canRotateView() override;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 8060019..42422e97 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -186,6 +186,10 @@
 #include "media/renderers/default_renderer_factory.h"
 #endif
 
+#if defined(ENABLE_WEBVR)
+#include "content/renderer/vr/vr_dispatcher.h"
+#endif
+
 using blink::WebContextMenuData;
 using blink::WebData;
 using blink::WebDataSource;
@@ -2690,7 +2694,7 @@
   // new navigation.
   navigation_state->set_request_committed(true);
 
-  SendDidCommitProvisionalLoad(frame, commit_type);
+  SendDidCommitProvisionalLoad(frame, commit_type, item);
 
   // Check whether we have new encoding name.
   UpdateEncoding(frame, frame->view()->pageEncoding().utf8());
@@ -3738,6 +3742,15 @@
   return app_banner_client_.get();
 }
 
+#if defined(ENABLE_WEBVR)
+blink::WebVRClient* RenderFrameImpl::webVRClient() {
+  if (!vr_dispatcher_)
+    vr_dispatcher_.reset(new VRDispatcher(GetServiceRegistry()));
+
+  return vr_dispatcher_.get();
+}
+#endif
+
 void RenderFrameImpl::DidPlay(blink::WebMediaPlayer* player) {
   Send(new FrameHostMsg_MediaPlayingNotification(
       routing_id_, reinterpret_cast<int64>(player), player->hasVideo(),
@@ -3796,8 +3809,9 @@
 
 // Tell the embedding application that the URL of the active page has changed.
 void RenderFrameImpl::SendDidCommitProvisionalLoad(
-      blink::WebFrame* frame,
-      blink::WebHistoryCommitType commit_type) {
+    blink::WebFrame* frame,
+    blink::WebHistoryCommitType commit_type,
+    const blink::WebHistoryItem& item) {
   DCHECK(!frame_ || frame_ == frame);
   WebDataSource* ds = frame->dataSource();
   DCHECK(ds);
@@ -3871,6 +3885,8 @@
     params.page_state = HistoryEntryToPageState(entry);
   else
     params.page_state = PageState::CreateFromURL(request.url());
+  params.item_sequence_number = item.itemSequenceNumber();
+  params.document_sequence_number = item.documentSequenceNumber();
 
   if (!frame->parent()) {
     // Top-level navigation.
@@ -4051,12 +4067,8 @@
   bool is_history_navigation = request_params.page_state.IsValid();
   WebURLRequest::CachePolicy cache_policy =
       WebURLRequest::UseProtocolCachePolicy;
-  if (!RenderFrameImpl::PrepareRenderViewForNavigation(
-          common_params.url, is_history_navigation, request_params, &is_reload,
-          &cache_policy)) {
-    Send(new FrameHostMsg_DidDropNavigation(routing_id_));
-    return;
-  }
+  RenderFrameImpl::PrepareRenderViewForNavigation(
+      common_params.url, request_params, &is_reload, &cache_policy);
 
   GetContentClient()->SetActiveURL(common_params.url);
 
@@ -4077,6 +4089,8 @@
   SendFailedProvisionalLoad(failed_request, error, frame_);
 
   if (!ShouldDisplayErrorPageForFailedLoad(error_code, common_params.url)) {
+    // TODO(avi): Remove this; we shouldn't ever be dropping navigations.
+    // http://crbug.com/501960
     Send(new FrameHostMsg_DidDropNavigation(routing_id_));
     return;
   }
@@ -4353,12 +4367,8 @@
   bool is_history_navigation = request_params.page_state.IsValid();
   WebURLRequest::CachePolicy cache_policy =
       WebURLRequest::UseProtocolCachePolicy;
-  if (!RenderFrameImpl::PrepareRenderViewForNavigation(
-          common_params.url, is_history_navigation, request_params, &is_reload,
-          &cache_policy)) {
-    Send(new FrameHostMsg_DidDropNavigation(routing_id_));
-    return;
-  }
+  RenderFrameImpl::PrepareRenderViewForNavigation(
+      common_params.url, request_params, &is_reload, &cache_policy);
 
   GetContentClient()->SetActiveURL(common_params.url);
 
@@ -4617,9 +4627,8 @@
 #endif
 }
 
-bool RenderFrameImpl::PrepareRenderViewForNavigation(
+void RenderFrameImpl::PrepareRenderViewForNavigation(
     const GURL& url,
-    bool is_history_navigation,
     const RequestNavigationParams& request_params,
     bool* is_reload,
     WebURLRequest::CachePolicy* cache_policy) {
@@ -4630,15 +4639,6 @@
   FOR_EACH_OBSERVER(
       RenderViewObserver, render_view_->observers_, Navigate(url));
 
-  // If this is a stale back/forward (due to a recent navigation the browser
-  // didn't know about), ignore it. Only check if swapped in because if the
-  // frame is swapped out, it won't commit before asking the browser.
-  if (!render_view_->is_swapped_out() && is_history_navigation &&
-      render_view_->history_list_offset_ !=
-          request_params.current_history_list_offset) {
-    return false;
-  }
-
   render_view_->history_list_offset_ =
       request_params.current_history_list_offset;
   render_view_->history_list_length_ =
@@ -4649,7 +4649,7 @@
   }
 
   if (!is_swapped_out_ || frame_->parent())
-    return true;
+    return;
 
   // This is a swapped out main frame, so swap the renderer back in.
   // We marked the view as hidden when swapping the view out, so be sure to
@@ -4671,7 +4671,7 @@
 
   render_view_->SetSwappedOut(false);
   is_swapped_out_ = false;
-  return true;
+  return;
 }
 
 void RenderFrameImpl::BeginNavigation(blink::WebURLRequest* request) {
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 281dd864..8940e64 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -114,6 +114,7 @@
 struct ResourceResponseHead;
 struct StartNavigationParams;
 struct StreamOverrideParameters;
+class VRDispatcher;
 
 class CONTENT_EXPORT RenderFrameImpl
     : public RenderFrame,
@@ -530,6 +531,10 @@
   virtual blink::WebPermissionClient* permissionClient();
   virtual blink::WebAppBannerClient* appBannerClient();
 
+#if defined(ENABLE_WEBVR)
+  blink::WebVRClient* webVRClient() override;
+#endif
+
   // WebMediaPlayerDelegate implementation:
   void DidPlay(blink::WebMediaPlayer* player) override;
   void DidPause(blink::WebMediaPlayer* player) override;
@@ -604,7 +609,8 @@
 
   // Builds and sends DidCommitProvisionalLoad to the host.
   void SendDidCommitProvisionalLoad(blink::WebFrame* frame,
-                                    blink::WebHistoryCommitType commit_type);
+                                    blink::WebHistoryCommitType commit_type,
+                                    const blink::WebHistoryItem& item);
 
   // IPC message handlers ------------------------------------------------------
   //
@@ -750,11 +756,9 @@
   // Creates a factory object used for creating audio and video renderers.
   scoped_ptr<MediaStreamRendererFactory> CreateRendererFactory();
 
-  // Checks that the RenderView is ready to display the navigation to |url|. If
-  // the return value is false, the navigation should be abandoned.
-  bool PrepareRenderViewForNavigation(
+  // Does preparation for the navigation to |url|.
+  void PrepareRenderViewForNavigation(
       const GURL& url,
-      bool is_history_navigation,
       const RequestNavigationParams& request_params,
       bool* is_reload,
       blink::WebURLRequest::CachePolicy* cache_policy);
@@ -973,6 +977,11 @@
 
   scoped_ptr<blink::WebAppBannerClient> app_banner_client_;
 
+#if defined(ENABLE_WEBVR)
+  // The VR dispatcher attached to the frame, lazily initialized.
+  scoped_ptr<VRDispatcher> vr_dispatcher_;
+#endif
+
 #if defined(OS_MACOSX) || defined(OS_ANDROID)
   // The external popup for the currently showing select popup.
   scoped_ptr<ExternalPopupMenu> external_popup_menu_;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 481d5ba..c1e5af2 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1041,10 +1041,11 @@
 #endif
     if (!compositor_task_runner_.get()) {
       compositor_thread_.reset(new base::Thread("Compositor"));
-      compositor_thread_->Start();
+      base::Thread::Options compositor_thread_options;
 #if defined(OS_ANDROID)
-      compositor_thread_->SetPriority(base::ThreadPriority::DISPLAY);
+      compositor_thread_options.priority = base::ThreadPriority::DISPLAY;
 #endif
+      compositor_thread_->StartWithOptions(compositor_thread_options);
       compositor_task_runner_ = compositor_thread_->task_runner();
       compositor_task_runner_->PostTask(
           FROM_HERE,
@@ -1527,7 +1528,6 @@
     // is there a new non-windows message I should add here?
     IPC_MESSAGE_HANDLER(ViewMsg_New, OnCreateNewView)
     IPC_MESSAGE_HANDLER(ViewMsg_NetworkTypeChanged, OnNetworkTypeChanged)
-    IPC_MESSAGE_HANDLER(ViewMsg_TempCrashWithData, OnTempCrashWithData)
     IPC_MESSAGE_HANDLER(WorkerProcessMsg_CreateWorker, OnCreateNewSharedWorker)
     IPC_MESSAGE_HANDLER(ViewMsg_TimezoneChange, OnUpdateTimezone)
 #if defined(OS_ANDROID)
@@ -1687,11 +1687,6 @@
       NetConnectionTypeToWebConnectionType(type));
 }
 
-void RenderThreadImpl::OnTempCrashWithData(const GURL& data) {
-  GetContentClient()->SetActiveURL(data);
-  CHECK(false);
-}
-
 void RenderThreadImpl::OnUpdateTimezone(const std::string& zone_id) {
   if (!blink_platform_impl_)
     return;
@@ -1750,8 +1745,7 @@
 
 void RenderThreadImpl::OnMemoryPressure(
     base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
-  base::allocator::ReleaseFreeMemory();
-  discardable_shared_memory_manager()->ReleaseFreeMemory();
+  ReleaseFreeMemory();
 
   // Do not call into blink if it is not initialized.
   if (blink_platform_impl_) {
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 52fc453..23a8041d 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -448,7 +448,6 @@
 #endif
   void OnNetworkTypeChanged(net::NetworkChangeNotifier::ConnectionType type);
   void OnGetAccessibilityTree();
-  void OnTempCrashWithData(const GURL& data);
   void OnUpdateTimezone(const std::string& zoneId);
   void OnMemoryPressure(
       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 5bc9ca6..25a063f 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -57,6 +57,7 @@
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebPerformance.h"
 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
+#include "third_party/WebKit/public/web/WebSettings.h"
 #include "third_party/WebKit/public/web/WebView.h"
 #include "third_party/WebKit/public/web/WebWindowFeatures.h"
 #include "ui/events/event.h"
@@ -370,6 +371,26 @@
   }
 };
 
+class RenderViewImplBlinkSettingsTest : public RenderViewImplTest {
+ public:
+  void DoSetUp() {
+    RenderViewImplTest::SetUp();
+  }
+
+  const blink::WebSettings* settings() {
+    return view()->webview()->settings();
+  }
+
+ protected:
+  // Blink settings may be specified on the command line, which must
+  // be configured before RenderViewImplTest::SetUp runs. Thus we make
+  // SetUp() a no-op, and expose RenderViewImplTest::SetUp() via
+  // DoSetUp(), to allow tests to perform command line modifications
+  // before RenderViewImplTest::SetUp is run. Each test must invoke
+  // DoSetUp manually once pre-SetUp configuration is complete.
+  void SetUp() override {}
+};
+
 // Ensure that the main RenderFrame is deleted and cleared from the RenderView
 // after closing it.
 TEST_F(RenderViewImplTest, RenderFrameClearedAfterClose) {
@@ -936,86 +957,6 @@
   EXPECT_EQ(state_C, state);
 }
 
-// Test that stale back/forward navigations arriving from the browser are
-// ignored.  See http://crbug.com/86758.
-TEST_F(RenderViewImplTest, StaleNavigationsIgnored) {
-  // Load page A.
-  LoadHTML("<div id=pagename>Page A</div>");
-  EXPECT_EQ(1, view()->history_list_length_);
-  EXPECT_EQ(0, view()->history_list_offset_);
-
-  // Load page B, which will trigger an UpdateState message for page A.
-  LoadHTML("<div id=pagename>Page B</div>");
-  EXPECT_EQ(2, view()->history_list_length_);
-  EXPECT_EQ(1, view()->history_list_offset_);
-
-  // Check for a valid UpdateState message for page A.
-  ProcessPendingMessages();
-  const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
-      ViewHostMsg_UpdateState::ID);
-  ASSERT_TRUE(msg_A);
-  ViewHostMsg_UpdateState::Param param;
-  ViewHostMsg_UpdateState::Read(msg_A, &param);
-  int page_id_A = base::get<0>(param);
-  PageState state_A = base::get<1>(param);
-  EXPECT_EQ(1, page_id_A);
-  render_thread_->sink().ClearMessages();
-
-  // Back to page A (nav_entry_id 1) and commit.
-  CommonNavigationParams common_params_A;
-  RequestNavigationParams request_params_A;
-  common_params_A.navigation_type = FrameMsg_Navigate_Type::NORMAL;
-  common_params_A.transition = ui::PAGE_TRANSITION_FORWARD_BACK;
-  request_params_A.current_history_list_length = 2;
-  request_params_A.current_history_list_offset = 1;
-  request_params_A.pending_history_list_offset = 0;
-  request_params_A.page_id = 1;
-  request_params_A.nav_entry_id = 1;
-  request_params_A.page_state = state_A;
-  NavigateMainFrame(common_params_A, StartNavigationParams(), request_params_A);
-  ProcessPendingMessages();
-
-  // A new navigation commits, clearing the forward history.
-  LoadHTML("<div id=pagename>Page C</div>");
-  EXPECT_EQ(2, view()->history_list_length_);
-  EXPECT_EQ(1, view()->history_list_offset_);
-  EXPECT_EQ(3, view()->page_id_); // page C is now page id 3
-  int was_page_c = -1;
-  base::string16 check_page_c = base::ASCIIToUTF16(
-      "Number(document.getElementById('pagename').innerHTML == 'Page C')");
-  EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
-  EXPECT_EQ(1, was_page_c);
-
-  // The browser then sends a stale navigation to B, which should be ignored.
-  CommonNavigationParams common_params_B;
-  RequestNavigationParams request_params_B;
-  common_params_B.navigation_type = FrameMsg_Navigate_Type::NORMAL;
-  common_params_B.transition = ui::PAGE_TRANSITION_FORWARD_BACK;
-  request_params_B.current_history_list_length = 2;
-  request_params_B.current_history_list_offset = 0;
-  request_params_B.pending_history_list_offset = 1;
-  request_params_B.page_id = 2;
-  request_params_B.nav_entry_id = 2;
-  request_params_B.page_state =
-      state_A;  // Doesn't matter, just has to be present.
-  NavigateMainFrame(common_params_B, StartNavigationParams(), request_params_B);
-
-  // State should be unchanged.
-  EXPECT_EQ(2, view()->history_list_length_);
-  EXPECT_EQ(1, view()->history_list_offset_);
-  EXPECT_EQ(3, view()->page_id_); // page C, not page B
-  was_page_c = -1;
-  EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
-  EXPECT_EQ(1, was_page_c);
-
-  // Check for a valid DidDropNavigation message.
-  ProcessPendingMessages();
-  const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
-      FrameHostMsg_DidDropNavigation::ID);
-  ASSERT_TRUE(msg);
-  render_thread_->sink().ClearMessages();
-}
-
 // Test that our IME backend sends a notification message when the input focus
 // changes.
 TEST_F(RenderViewImplTest, OnImeTypeChanged) {
@@ -2409,6 +2350,20 @@
       view()->historyForwardListCount() + 1);
 }
 
+TEST_F(RenderViewImplBlinkSettingsTest, Default) {
+  DoSetUp();
+  EXPECT_EQ(blink::WebSettings::HoverTypeNone, settings()->primaryHoverType());
+  EXPECT_FALSE(settings()->viewportEnabled());
+}
+
+TEST_F(RenderViewImplBlinkSettingsTest, CommandLine) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kBlinkSettings, "primaryHoverType=4,viewportEnabled=true");
+  DoSetUp();
+  EXPECT_EQ(blink::WebSettings::HoverTypeHover, settings()->primaryHoverType());
+  EXPECT_TRUE(settings()->viewportEnabled());
+}
+
 TEST_F(DevToolsAgentTest, DevToolsResumeOnClose) {
   Attach();
   EXPECT_FALSE(IsPaused());
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index f40945c..6bf5d5d0 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -2011,6 +2011,11 @@
       webwidget_mouse_lock_target_.get());
 }
 
+void RenderViewImpl::onMouseDown(const WebNode& mouse_down_node) {
+  FOR_EACH_OBSERVER(
+      RenderViewObserver, observers_, OnMouseDown(mouse_down_node));
+}
+
 void RenderViewImpl::didHandleGestureEvent(
     const WebGestureEvent& event,
     bool event_cancelled) {
@@ -3084,10 +3089,6 @@
   return false;
 }
 
-void RenderViewImpl::DidHandleMouseEvent(const WebMouseEvent& event) {
-  FOR_EACH_OBSERVER(RenderViewObserver, observers_, DidHandleMouseEvent(event));
-}
-
 bool RenderViewImpl::HasTouchEventHandlersAt(const gfx::Point& point) const {
   if (!webview())
     return false;
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 25416d60..7e9e8842 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -328,6 +328,8 @@
   virtual bool isPointerLocked();
   virtual void didHandleGestureEvent(const blink::WebGestureEvent& event,
                                      bool event_cancelled) override;
+  virtual void onMouseDown(const blink::WebNode& mouse_down_node) override;
+
   virtual void initializeLayerTreeView() override;
 
   // blink::WebViewClient implementation --------------------------------------
@@ -463,7 +465,6 @@
   void DidHandleKeyEvent() override;
   bool WillHandleMouseEvent(const blink::WebMouseEvent& event) override;
   bool WillHandleGestureEvent(const blink::WebGestureEvent& event) override;
-  void DidHandleMouseEvent(const blink::WebMouseEvent& event) override;
   bool HasTouchEventHandlersAt(const gfx::Point& point) const override;
   void OnSetFocus(bool enable) override;
   void OnWasHidden() override;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index ed107cd..ff314be 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1272,8 +1272,6 @@
   if (!prevent_default) {
     if (WebInputEvent::isKeyboardEventType(input_event->type))
       DidHandleKeyEvent();
-    if (WebInputEvent::isMouseEventType(input_event->type))
-      DidHandleMouseEvent(*(static_cast<const WebMouseEvent*>(input_event)));
   }
 
 // TODO(rouslan): Fix ChromeOS and Windows 8 behavior of autofill popup with
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 0fd581c..5d58a87 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -569,10 +569,6 @@
   // won't be sent to WebKit.
   virtual bool WillHandleGestureEvent(const blink::WebGestureEvent& event);
 
-  // Called by OnHandleInputEvent() to notify subclasses that a mouse event was
-  // just handled.
-  virtual void DidHandleMouseEvent(const blink::WebMouseEvent& event) {}
-
   // Called by OnHandleInputEvent() to forward a mouse wheel event to the
   // compositor thread, to effect the elastic overscroll effect.
   void ObserveWheelEventAndResult(const blink::WebMouseWheelEvent& wheel_event,
diff --git a/content/renderer/vr/vr_dispatcher.cc b/content/renderer/vr/vr_dispatcher.cc
new file mode 100644
index 0000000..43e2572
--- /dev/null
+++ b/content/renderer/vr/vr_dispatcher.cc
@@ -0,0 +1,69 @@
+// 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/vr/vr_dispatcher.h"
+
+#include "content/public/common/service_registry.h"
+#include "content/renderer/vr/vr_type_converters.h"
+
+namespace content {
+
+VRDispatcher::VRDispatcher(ServiceRegistry* service_registry)
+    : service_registry_(service_registry) {
+}
+
+VRDispatcher::~VRDispatcher() {
+}
+
+VRServicePtr& VRDispatcher::GetVRServicePtr() {
+  if (!vr_service_) {
+    service_registry_->ConnectToRemoteService(mojo::GetProxy(&vr_service_));
+  }
+  return vr_service_;
+}
+
+void VRDispatcher::getDevices(blink::WebVRGetDevicesCallback* callback) {
+  int request_id = pending_requests_.Add(callback);
+  GetVRServicePtr()->GetDevices(base::Bind(&VRDispatcher::OnGetDevices,
+                                           base::Unretained(this), request_id));
+}
+
+void VRDispatcher::getSensorState(unsigned int index,
+                                  blink::WebHMDSensorState& state) {
+  GetVRServicePtr()->GetSensorState(
+      index,
+      base::Bind(&VRDispatcher::OnGetSensorState, base::Unretained(&state)));
+
+  // This call needs to return results synchronously in order to be useful and
+  // provide the lowest latency results possible.
+  GetVRServicePtr().WaitForIncomingResponse();
+}
+
+void VRDispatcher::resetSensor(unsigned int index) {
+  GetVRServicePtr()->ResetSensor(index);
+}
+
+void VRDispatcher::OnGetDevices(int request_id,
+                                const mojo::Array<VRDeviceInfoPtr>& devices) {
+  blink::WebVector<blink::WebVRDevice> web_devices(devices.size());
+
+  blink::WebVRGetDevicesCallback* callback =
+      pending_requests_.Lookup(request_id);
+  if (!callback)
+    return;
+
+  for (size_t i = 0; i < devices.size(); ++i) {
+    web_devices[i] = devices[i].To<blink::WebVRDevice>();
+  }
+
+  callback->onSuccess(&web_devices);
+  pending_requests_.Remove(request_id);
+}
+
+void VRDispatcher::OnGetSensorState(blink::WebHMDSensorState* state,
+                                    const VRSensorStatePtr& mojo_state) {
+  *state = mojo_state.To<blink::WebHMDSensorState>();
+}
+
+}  // namespace content
diff --git a/content/renderer/vr/vr_dispatcher.h b/content/renderer/vr/vr_dispatcher.h
new file mode 100644
index 0000000..7e5d56b
--- /dev/null
+++ b/content/renderer/vr/vr_dispatcher.h
@@ -0,0 +1,55 @@
+// 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_VR_DISPATCHER_H_
+#define CONTENT_RENDERER_VR_DISPATCHER_H_
+
+#include <vector>
+
+#include "base/id_map.h"
+#include "content/common/vr_service.mojom.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+#include "third_party/WebKit/public/platform/modules/vr/WebVR.h"
+#include "third_party/WebKit/public/platform/modules/vr/WebVRClient.h"
+
+namespace content {
+
+class ServiceRegistry;
+
+class VRDispatcher : NON_EXPORTED_BASE(public blink::WebVRClient) {
+ public:
+  explicit VRDispatcher(ServiceRegistry* service_registry);
+  ~VRDispatcher();
+
+  // blink::WebVRClient implementation.
+  void getDevices(blink::WebVRGetDevicesCallback* callback) override;
+
+  void getSensorState(unsigned int index,
+                      blink::WebHMDSensorState& state) override;
+
+  void resetSensor(unsigned int index) override;
+
+ private:
+  // Helper method that returns an initialized PermissionServicePtr.
+  VRServicePtr& GetVRServicePtr();
+
+  // Callback handlers
+  void OnGetDevices(int request_id,
+                    const mojo::Array<VRDeviceInfoPtr>& devices);
+  static void OnGetSensorState(blink::WebHMDSensorState* state,
+                               const VRSensorStatePtr& mojo_state);
+
+  // Tracks requests sent to browser to match replies with callbacks.
+  // Owns callback objects.
+  IDMap<blink::WebVRGetDevicesCallback, IDMapOwnPointer> pending_requests_;
+
+  ServiceRegistry* service_registry_;
+  VRServicePtr vr_service_;
+
+  DISALLOW_COPY_AND_ASSIGN(VRDispatcher);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_VR_DISPATCHER_H_
diff --git a/content/renderer/vr/vr_type_converters.cc b/content/renderer/vr/vr_type_converters.cc
new file mode 100644
index 0000000..68d686c
--- /dev/null
+++ b/content/renderer/vr/vr_type_converters.cc
@@ -0,0 +1,147 @@
+// 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/vr/vr_type_converters.h"
+
+#include <algorithm>
+
+using content::VRVector3Ptr;
+using content::VRVector4Ptr;
+using content::VRRectPtr;
+using content::VRFieldOfViewPtr;
+using content::VREyeParametersPtr;
+using content::VRHMDInfoPtr;
+using content::VRDeviceInfoPtr;
+using content::VRSensorStatePtr;
+
+namespace mojo {
+
+// static
+blink::WebVRVector3 TypeConverter<blink::WebVRVector3, VRVector3Ptr>::Convert(
+    const VRVector3Ptr& input) {
+  blink::WebVRVector3 output;
+  output.x = input->x;
+  output.y = input->y;
+  output.z = input->z;
+  return output;
+}
+
+// static
+blink::WebVRVector4 TypeConverter<blink::WebVRVector4, VRVector4Ptr>::Convert(
+    const VRVector4Ptr& input) {
+  blink::WebVRVector4 output;
+  output.x = input->x;
+  output.y = input->y;
+  output.z = input->z;
+  output.w = input->w;
+  return output;
+}
+
+// static
+blink::WebVRRect TypeConverter<blink::WebVRRect, VRRectPtr>::Convert(
+    const VRRectPtr& input) {
+  blink::WebVRRect output;
+  output.x = input->x;
+  output.y = input->y;
+  output.width = input->width;
+  output.height = input->height;
+  return output;
+}
+
+// static
+blink::WebVRFieldOfView
+TypeConverter<blink::WebVRFieldOfView, VRFieldOfViewPtr>::Convert(
+    const VRFieldOfViewPtr& input) {
+  blink::WebVRFieldOfView output;
+  output.upDegrees = input->upDegrees;
+  output.downDegrees = input->downDegrees;
+  output.leftDegrees = input->leftDegrees;
+  output.rightDegrees = input->rightDegrees;
+  return output;
+}
+
+// static
+blink::WebVREyeParameters
+TypeConverter<blink::WebVREyeParameters, VREyeParametersPtr>::Convert(
+    const VREyeParametersPtr& input) {
+  blink::WebVREyeParameters output;
+  output.minimumFieldOfView =
+      input->minimumFieldOfView.To<blink::WebVRFieldOfView>();
+  output.maximumFieldOfView =
+      input->maximumFieldOfView.To<blink::WebVRFieldOfView>();
+  output.recommendedFieldOfView =
+      input->recommendedFieldOfView.To<blink::WebVRFieldOfView>();
+  output.eyeTranslation = input->eyeTranslation.To<blink::WebVRVector3>();
+  output.renderRect = input->renderRect.To<blink::WebVRRect>();
+  return output;
+}
+
+// static
+blink::WebVRHMDInfo TypeConverter<blink::WebVRHMDInfo, VRHMDInfoPtr>::Convert(
+    const VRHMDInfoPtr& input) {
+  blink::WebVRHMDInfo output;
+  output.leftEye = input->leftEye.To<blink::WebVREyeParameters>();
+  output.rightEye = input->rightEye.To<blink::WebVREyeParameters>();
+  return output;
+}
+
+// static
+blink::WebVRDevice TypeConverter<blink::WebVRDevice, VRDeviceInfoPtr>::Convert(
+    const VRDeviceInfoPtr& input) {
+  blink::WebVRDevice output;
+  memset(&output, 0, sizeof(blink::WebVRDevice));
+
+  output.index = input->index;
+  output.flags = blink::WebVRDeviceTypePosition;
+  output.deviceName = blink::WebString::fromUTF8(input->deviceName.data(),
+                                                 input->deviceName.size());
+
+  if (!input->hmdInfo.is_null()) {
+    output.flags |= blink::WebVRDeviceTypeHMD;
+    output.hmdInfo = input->hmdInfo.To<blink::WebVRHMDInfo>();
+  }
+
+  return output;
+}
+
+// static
+blink::WebHMDSensorState
+TypeConverter<blink::WebHMDSensorState, VRSensorStatePtr>::Convert(
+    const VRSensorStatePtr& input) {
+  blink::WebHMDSensorState output;
+  output.timestamp = input->timestamp;
+  output.frameIndex = input->frameIndex;
+  output.flags = 0;
+
+  if (!input->orientation.is_null()) {
+    output.flags |= blink::WebVRSensorStateOrientation;
+    output.orientation = input->orientation.To<blink::WebVRVector4>();
+  }
+  if (!input->position.is_null()) {
+    output.flags |= blink::WebVRSensorStatePosition;
+    output.position = input->position.To<blink::WebVRVector3>();
+  }
+  if (!input->angularVelocity.is_null()) {
+    output.flags |= blink::WebVRSensorStateAngularVelocity;
+    output.angularVelocity = input->angularVelocity.To<blink::WebVRVector3>();
+  }
+  if (!input->linearVelocity.is_null()) {
+    output.flags |= blink::WebVRSensorStateLinearVelocity;
+    output.linearVelocity = input->linearVelocity.To<blink::WebVRVector3>();
+  }
+  if (!input->angularAcceleration.is_null()) {
+    output.flags |= blink::WebVRSensorStateAngularAcceleration;
+    output.angularAcceleration =
+        input->angularAcceleration.To<blink::WebVRVector3>();
+  }
+  if (!input->linearAcceleration.is_null()) {
+    output.flags |= blink::WebVRSensorStateLinearAcceleration;
+    output.linearAcceleration =
+        input->linearAcceleration.To<blink::WebVRVector3>();
+  }
+
+  return output;
+}
+
+}  // namespace mojo
diff --git a/content/renderer/vr/vr_type_converters.h b/content/renderer/vr/vr_type_converters.h
new file mode 100644
index 0000000..8902cd5a
--- /dev/null
+++ b/content/renderer/vr/vr_type_converters.h
@@ -0,0 +1,62 @@
+// 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_CONTENT_RENDERER_VR_VR_TYPE_CONVERTERS_H_
+#define CHROME_CONTENT_RENDERER_VR_VR_TYPE_CONVERTERS_H_
+
+#include "content/common/vr_service.mojom.h"
+#include "mojo/common/common_type_converters.h"
+#include "third_party/WebKit/public/platform/modules/vr/WebVR.h"
+
+namespace mojo {
+
+// Type/enum conversions from WebVR data types to Mojo data types
+// and vice versa.
+
+template <>
+struct TypeConverter<blink::WebVRVector3, content::VRVector3Ptr> {
+  static blink::WebVRVector3 Convert(const content::VRVector3Ptr& input);
+};
+
+template <>
+struct TypeConverter<blink::WebVRVector4, content::VRVector4Ptr> {
+  static blink::WebVRVector4 Convert(const content::VRVector4Ptr& input);
+};
+
+template <>
+struct TypeConverter<blink::WebVRRect, content::VRRectPtr> {
+  static blink::WebVRRect Convert(const content::VRRectPtr& input);
+};
+
+template <>
+struct TypeConverter<blink::WebVRFieldOfView, content::VRFieldOfViewPtr> {
+  static blink::WebVRFieldOfView Convert(
+      const content::VRFieldOfViewPtr& input);
+};
+
+template <>
+struct TypeConverter<blink::WebVREyeParameters, content::VREyeParametersPtr> {
+  static blink::WebVREyeParameters Convert(
+      const content::VREyeParametersPtr& input);
+};
+
+template <>
+struct TypeConverter<blink::WebVRHMDInfo, content::VRHMDInfoPtr> {
+  static blink::WebVRHMDInfo Convert(const content::VRHMDInfoPtr& input);
+};
+
+template <>
+struct TypeConverter<blink::WebVRDevice, content::VRDeviceInfoPtr> {
+  static blink::WebVRDevice Convert(const content::VRDeviceInfoPtr& input);
+};
+
+template <>
+struct TypeConverter<blink::WebHMDSensorState, content::VRSensorStatePtr> {
+  static blink::WebHMDSensorState Convert(
+      const content::VRSensorStatePtr& input);
+};
+
+}  // namespace mojo
+
+#endif  // CHROME_CONTENT_RENDERER_VR_VR_TYPE_CONVERTERS_H_
\ No newline at end of file
diff --git a/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsApplication.java b/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsApplication.java
index 25e313b..ec7af5f 100644
--- a/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsApplication.java
+++ b/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsApplication.java
@@ -17,7 +17,6 @@
 
     private static final String[] MANDATORY_PAK_FILES = new String[] {
         "content_shell.pak",
-        "icudtl.dat",
         "natives_blob.bin",
         "snapshot_blob.bin"
     };
diff --git a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java b/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java
index 4478a86b..ffc4bd21 100644
--- a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java
+++ b/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java
@@ -16,18 +16,12 @@
 public class ChromiumLinkerTestApplication extends BaseChromiumApplication {
 
     /**
-     * icudtl.dat provides ICU (i18n library) with all the data for its
-     * operation. We use to link it statically to our binary, but not any more
-     * so that we have to install it along with other mandatory pak files.
-     * See src/third_party/icu/README.chromium.
-     *
      *  V8's initial snapshot used to be statically linked to the binary, but
      *  now it's loaded from external files. Therefore we need to install such
      *  snapshots (natives_blob.bin and snapshot.bin) along with pak files.
      */
     private static final String[] MANDATORY_PAK_FILES = new String[] {
         "content_shell.pak",
-        "icudtl.dat",
         "natives_blob.bin",
         "snapshot_blob.bin"
     };
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellApplication.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellApplication.java
index db16f26..c34c6a5 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellApplication.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellApplication.java
@@ -17,18 +17,12 @@
 
     public static final String COMMAND_LINE_FILE = "/data/local/tmp/content-shell-command-line";
     /**
-     * icudtl.dat provides ICU (i18n library) with all the data for its
-     * operation. We use to link it statically to our binary, but not any more
-     * so that we have to install it along with other mandatory pak files.
-     * See src/third_party/icu/README.chromium.
-     *
      *  V8's initial snapshot used to be statically linked to the binary, but
      *  now it's loaded from external files. Therefore we need to install such
      *  snapshots (natives_blob.bin and snapshot.bin) along with pak files.
      */
     private static final String[] MANDATORY_PAK_FILES = new String[] {
         "content_shell.pak",
-        "icudtl.dat",
         "natives_blob.bin",
         "snapshot_blob.bin"
     };
diff --git a/content/shell/app/blink_test_platform_support_android.cc b/content/shell/app/blink_test_platform_support_android.cc
index fe84ca85..90ff93f3 100644
--- a/content/shell/app/blink_test_platform_support_android.cc
+++ b/content/shell/app/blink_test_platform_support_android.cc
@@ -4,7 +4,7 @@
 
 #include "content/shell/app/blink_test_platform_support.h"
 
-#include "third_party/skia/include/ports/SkTypeface_android.h"
+#include "third_party/skia/include/ports/SkFontMgr_android.h"
 
 namespace {
 
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index a73cfdb..a48c00860 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -34,7 +34,6 @@
 #include "content/shell/common/shell_messages.h"
 #include "content/shell/common/shell_switches.h"
 #include "content/shell/renderer/layout_test/blink_test_helpers.h"
-#include "gin/v8_initializer.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "url/gurl.h"
 
@@ -129,12 +128,7 @@
 }
 
 ShellContentBrowserClient::ShellContentBrowserClient()
-    :
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-      v8_natives_fd_(-1),
-      v8_snapshot_fd_(-1),
-#endif  // OS_POSIX && !OS_MACOSX
-      shell_browser_main_parts_(NULL) {
+    : shell_browser_main_parts_(NULL) {
   DCHECK(!g_browser_client);
   g_browser_client = this;
 }
@@ -206,22 +200,6 @@
   return false;
 }
 
-void ShellContentBrowserClient::AppendMappedFileCommandLineSwitches(
-    base::CommandLine* command_line) {
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
-  std::string process_type =
-      command_line->GetSwitchValueASCII(switches::kProcessType);
-  if (process_type != switches::kZygoteProcess) {
-    DCHECK(natives_fd_exists());
-    command_line->AppendSwitch(::switches::kV8NativesPassedByFD);
-    if (snapshot_fd_exists())
-      command_line->AppendSwitch(::switches::kV8SnapshotPassedByFD);
-  }
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // OS_POSIX && !OS_MACOSX
-}
-
 void ShellContentBrowserClient::AppendExtraCommandLineSwitches(
     base::CommandLine* command_line,
     int child_process_id) {
@@ -357,23 +335,6 @@
     const base::CommandLine& command_line,
     int child_process_id,
     FileDescriptorInfo* mappings) {
-#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
-  if (!natives_fd_exists()) {
-    int v8_natives_fd = -1;
-    int v8_snapshot_fd = -1;
-    if (gin::V8Initializer::OpenV8FilesForChildProcesses(&v8_natives_fd,
-                                                         &v8_snapshot_fd)) {
-      v8_natives_fd_.reset(v8_natives_fd);
-      v8_snapshot_fd_.reset(v8_snapshot_fd);
-    }
-  }
-  // V8 can't start up without the source of the natives, but it can
-  // start up (slower) without the snapshot.
-  DCHECK(natives_fd_exists());
-  mappings->Share(kV8NativesDataDescriptor, v8_natives_fd_.get());
-  mappings->Share(kV8SnapshotDataDescriptor, v8_snapshot_fd_.get());
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-
 #if defined(OS_ANDROID)
   int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
   base::FilePath pak_file;
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index fe843a6..0568691 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -46,8 +46,6 @@
   bool IsHandledURL(const GURL& url) override;
   void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                       int child_process_id) override;
-  void AppendMappedFileCommandLineSwitches(
-      base::CommandLine* command_line) override;
   void OverrideWebkitPrefs(RenderViewHost* render_view_host,
                            WebPreferences* prefs) override;
   void ResourceDispatcherHostCreated() override;
@@ -105,13 +103,6 @@
   scoped_ptr<ShellResourceDispatcherHostDelegate>
       resource_dispatcher_host_delegate_;
 
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-  base::ScopedFD v8_natives_fd_;
-  base::ScopedFD v8_snapshot_fd_;
-  bool natives_fd_exists() { return v8_natives_fd_ != -1; }
-  bool snapshot_fd_exists() { return v8_snapshot_fd_ != -1; }
-#endif
-
   base::Closure select_client_certificate_callback_;
 
   ShellBrowserMainParts* shell_browser_main_parts_;
diff --git a/content/test/data/accessibility/html/mark-expected-mac.txt b/content/test/data/accessibility/html/mark-expected-mac.txt
index 41895ea..c275b85 100644
--- a/content/test/data/accessibility/html/mark-expected-mac.txt
+++ b/content/test/data/accessibility/html/mark-expected-mac.txt
@@ -1,5 +1,6 @@
-AXWebArea
-++AXGroup
-++++AXStaticText AXValue='This test is to check '
-++++AXStaticText AXValue='mark tag'
-++++AXStaticText AXValue='.'
+AXWebArea AXRoleDescription='HTML content'
+++AXGroup AXRoleDescription='group'
+++++AXStaticText AXRoleDescription='text' AXValue='This test is to check '
+++++AXGroup AXRoleDescription='highlighted content'
+++++++AXStaticText AXRoleDescription='text' AXValue='mark tag'
+++++AXStaticText AXRoleDescription='text' AXValue='.'
diff --git a/content/test/data/accessibility/html/mark-expected-win.txt b/content/test/data/accessibility/html/mark-expected-win.txt
index 902f229..03e6f81 100644
--- a/content/test/data/accessibility/html/mark-expected-win.txt
+++ b/content/test/data/accessibility/html/mark-expected-win.txt
@@ -1,5 +1,6 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++IA2_ROLE_PARAGRAPH
 ++++ROLE_SYSTEM_STATICTEXT name='This test is to check '
-++++ROLE_SYSTEM_STATICTEXT name='mark tag'
+++++IA2_ROLE_TEXT_FRAME
+++++++ROLE_SYSTEM_STATICTEXT name='mark tag'
 ++++ROLE_SYSTEM_STATICTEXT name='.'
diff --git a/content/test/data/accessibility/html/mark.html b/content/test/data/accessibility/html/mark.html
index 5afffecf..0ec7cdc 100644
--- a/content/test/data/accessibility/html/mark.html
+++ b/content/test/data/accessibility/html/mark.html
@@ -1,3 +1,6 @@
+<!--
+@MAC-ALLOW:AXRole*
+-->
 <!DOCTYPE html>
 <html>
 <body>
diff --git a/content/test/data/frame_tree/page_with_post_message_frames.html b/content/test/data/frame_tree/page_with_post_message_frames.html
index 6126299d..30d7346c 100644
--- a/content/test/data/frame_tree/page_with_post_message_frames.html
+++ b/content/test/data/frame_tree/page_with_post_message_frames.html
@@ -3,7 +3,9 @@
 <script>
   window.addEventListener("message", messageReceived, false);
 
+  var receivedMessages = 0;
   function messageReceived(event) {
+    receivedMessages++;
     document.title = event.data;
     event.source.postMessage(event.data + "-reply", "*");
   }
diff --git a/content/test/data/post_message.html b/content/test/data/post_message.html
index 1f5b694..b293a21a 100644
--- a/content/test/data/post_message.html
+++ b/content/test/data/post_message.html
@@ -15,6 +15,15 @@
     return true;
   }
   
+  function openPopup(url) {
+    popup = window.open(url);
+  }
+
+  function postToPopup(msg) {
+    popup.postMessage(msg, "*");
+    return true;
+  }
+
   // Send a message to parent.
   function postToParent(msg) {
     parent.postMessage(msg, "*");
diff --git a/content/test/gpu/gpu_tests/gpu_test_expectations.py b/content/test/gpu/gpu_tests/gpu_test_expectations.py
index 464c7bc..d7451d2 100644
--- a/content/test/gpu/gpu_tests/gpu_test_expectations.py
+++ b/content/test/gpu/gpu_tests/gpu_test_expectations.py
@@ -15,7 +15,7 @@
 #     vivante
 #
 # Browser types:
-#     android-webview-shell, android-content-shell
+#     android-webview-shell, android-content-shell, debug
 #
 # ANGLE renderer:
 #     d3d9, d3d11, opengl
@@ -30,7 +30,8 @@
 
 ANGLE_MODIFIERS = ['d3d9', 'd3d11', 'opengl']
 
-BROWSER_TYPE_MODIFIERS = ['android-webview-shell', 'android-content-shell']
+BROWSER_TYPE_MODIFIERS = [
+    'android-webview-shell', 'android-content-shell', 'debug' ]
 
 class GpuTestExpectations(test_expectations.TestExpectations):
   def IsValidUserDefinedCondition(self, condition):
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index e3857277..8325da9 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -564,6 +564,15 @@
     self.Skip('deqp/functional/gles3/*', ['win', 'intel'], bug=483282)
     self.Skip('conformance2/*', ['win', 'intel'], bug=483282)
 
+    # Skip all WebGL 2 tests on Win NVIDIA debug.
+    self.Skip('deqp/data/gles3/shaders/*',
+        ['win', 'nvidia', 'debug'], bug=483282)
+    self.Skip('framework/opengl/simplereference/*',
+        ['win', 'nvidia', 'debug'], bug=483282)
+    self.Skip('deqp/functional/gles3/*',
+        ['win', 'nvidia', 'debug'], bug=483282)
+    self.Skip('conformance2/*',
+        ['win', 'nvidia', 'debug'], bug=483282)
 
     self.Fail('deqp/data/gles3/shaders/arrays.html', bug=483282)
     self.Fail('deqp/data/gles3/shaders/constant_expressions.html', bug=483282)
diff --git a/content/test/render_thread_impl_browser_test_ipc_helper.cc b/content/test/render_thread_impl_browser_test_ipc_helper.cc
index 9ee9b8a..7a4bec1 100644
--- a/content/test/render_thread_impl_browser_test_ipc_helper.cc
+++ b/content/test/render_thread_impl_browser_test_ipc_helper.cc
@@ -5,7 +5,6 @@
 #include "content/test/render_thread_impl_browser_test_ipc_helper.h"
 
 #include "content/common/mojo/channel_init.h"
-#include "ipc/mojo/ipc_channel_mojo_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
@@ -47,21 +46,18 @@
   InitializeMojo();
 
   ipc_support_.reset(new IPC::ScopedIPCSupport(ipc_thread_->task_runner()));
-  mojo_host_.reset(new IPC::ChannelMojoHost(ipc_thread_->task_runner()));
   mojo_application_host_.reset(new MojoApplicationHost());
   mojo_application_host_->OverrideIOTaskRunnerForTest(
       ipc_thread_->task_runner());
 
   channel_ = IPC::ChannelProxy::Create(
-      IPC::ChannelMojo::CreateServerFactory(mojo_host_->channel_delegate(),
-                                            ipc_thread_->task_runner(),
+      IPC::ChannelMojo::CreateServerFactory(ipc_thread_->task_runner(),
                                             channel_id_, nullptr),
       dummy_listener_.get(), ipc_thread_->task_runner());
 
   mojo_application_host_->Init();
   mojo_application_host_->Activate(channel_.get(),
                                    base::GetCurrentProcessHandle());
-  mojo_host_->OnClientLaunched(base::GetCurrentProcessHandle());
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
diff --git a/content/test/render_thread_impl_browser_test_ipc_helper.h b/content/test/render_thread_impl_browser_test_ipc_helper.h
index 0d86732..1a534ad 100644
--- a/content/test/render_thread_impl_browser_test_ipc_helper.h
+++ b/content/test/render_thread_impl_browser_test_ipc_helper.h
@@ -13,7 +13,6 @@
 #include "ipc/mojo/scoped_ipc_support.h"
 
 namespace IPC {
-class ChannelMojoHost;
 class ChannelProxy;
 class Sender;
 };
@@ -47,7 +46,6 @@
   scoped_ptr<DummyListener> dummy_listener_;
   scoped_ptr<IPC::ScopedIPCSupport> ipc_support_;
   scoped_ptr<MojoApplicationHost> mojo_application_host_;
-  scoped_ptr<IPC::ChannelMojoHost> mojo_host_;
   std::string channel_id_;
 };
 
diff --git a/content/zygote/zygote_linux.cc b/content/zygote/zygote_linux.cc
index 59a14c1..0a333e3 100644
--- a/content/zygote/zygote_linux.cc
+++ b/content/zygote/zygote_linux.cc
@@ -49,14 +49,6 @@
 void SIGCHLDHandler(int signal) {
 }
 
-// On Linux, when a process is the init process of a PID namespace, it cannot be
-// terminated by signals like SIGTERM or SIGINT, since they are ignored unless
-// we register a handler for them. In the handlers, we exit with this special
-// exit code that GetTerminationStatus understands to mean that we were
-// terminated by an external signal.
-const int kKilledExitCode = 0x80;
-const int kUnexpectedExitCode = 0x81;
-
 int LookUpFd(const base::GlobalDescriptors::Mapping& fd_mapping, uint32_t key) {
   for (size_t index = 0; index < fd_mapping.size(); ++index) {
     if (fd_mapping[index].key == key)
@@ -316,8 +308,12 @@
     process_info_map_.erase(real_pid);
   }
 
-  if (WIFEXITED(*exit_code) && WEXITSTATUS(*exit_code) == kKilledExitCode) {
-    *status = base::TERMINATION_STATUS_PROCESS_WAS_KILLED;
+  if (WIFEXITED(*exit_code)) {
+    const int exit_status = WEXITSTATUS(*exit_code);
+    if (exit_status == sandbox::NamespaceSandbox::SignalExitCode(SIGINT) ||
+        exit_status == sandbox::NamespaceSandbox::SignalExitCode(SIGTERM)) {
+      *status = base::TERMINATION_STATUS_PROCESS_WAS_KILLED;
+    }
   }
 
   return true;
@@ -394,7 +390,7 @@
       pid = sandbox::NamespaceSandbox::ForkInNewPidNamespace(
           /*drop_capabilities_in_child=*/true);
     } else {
-      pid = fork();
+      pid = sandbox::Credentials::ForkAndDropCapabilitiesInChild();
     }
   }
 
@@ -402,17 +398,11 @@
     // If the process is the init process inside a PID namespace, it must have
     // explicit signal handlers.
     if (getpid() == 1) {
-      for (const int sig : {SIGINT, SIGTERM}) {
+      static const int kTerminationSignals[] = {
+          SIGINT, SIGTERM, SIGHUP, SIGQUIT, SIGABRT, SIGPIPE, SIGUSR1, SIGUSR2};
+      for (const int sig : kTerminationSignals) {
         sandbox::NamespaceSandbox::InstallTerminationSignalHandler(
-            sig, kKilledExitCode);
-      }
-
-      static const int kUnexpectedSignals[] = {
-          SIGHUP, SIGQUIT, SIGABRT, SIGPIPE, SIGUSR1, SIGUSR2,
-      };
-      for (const int sig : kUnexpectedSignals) {
-        sandbox::NamespaceSandbox::InstallTerminationSignalHandler(
-            sig, kUnexpectedExitCode);
+            sig, sandbox::NamespaceSandbox::SignalExitCode(sig));
       }
     }
 
diff --git a/device/devices_app/BUILD.gn b/device/devices_app/BUILD.gn
index 233d2633..1621e11 100644
--- a/device/devices_app/BUILD.gn
+++ b/device/devices_app/BUILD.gn
@@ -40,8 +40,8 @@
   ]
 
   deps = [
-    ":lib",
     "//base",
+    "//device/devices_app/public/cpp:factory",
     "//mojo/application/public/cpp",
   ]
 
diff --git a/device/devices_app/devices_app.cc b/device/devices_app/devices_app.cc
index 318ebf62..0aab6cb 100644
--- a/device/devices_app/devices_app.cc
+++ b/device/devices_app/devices_app.cc
@@ -23,8 +23,6 @@
 
 namespace device {
 
-const char kDevicesMojoAppUrl[] = "system:devices";
-
 namespace {
 
 // The number of seconds to wait without any bound DeviceManagers before
@@ -100,16 +98,6 @@
   DISALLOW_COPY_AND_ASSIGN(USBServiceInitializer);
 };
 
-DevicesApp::~DevicesApp() {
-}
-
-// static
-scoped_ptr<mojo::ApplicationDelegate> DevicesApp::CreateDelegate(
-    scoped_refptr<base::SequencedTaskRunner> service_task_runner) {
-  return scoped_ptr<mojo::ApplicationDelegate>(
-      new DevicesApp(service_task_runner));
-}
-
 DevicesApp::DevicesApp(
     scoped_refptr<base::SequencedTaskRunner> service_task_runner)
     : app_impl_(nullptr),
@@ -117,6 +105,9 @@
       active_device_manager_count_(0) {
 }
 
+DevicesApp::~DevicesApp() {
+}
+
 void DevicesApp::Initialize(mojo::ApplicationImpl* app) {
   app_impl_ = app;
   if (!service_task_runner_) {
diff --git a/device/devices_app/devices_app.gyp b/device/devices_app/devices_app.gyp
index 8cf952db..c983893 100644
--- a/device/devices_app/devices_app.gyp
+++ b/device/devices_app/devices_app.gyp
@@ -57,5 +57,27 @@
         'device_usb_mojo_bindings',
       ],
     },
+    {
+      'target_name': 'devices_app_public_cpp',
+      'type': 'static_library',
+      'sources': [
+        'public/cpp/constants.cc',
+        'public/cpp/constants.h',
+      ],
+      'dependencies': [
+        'devices_app_lib',
+      ],
+    },
+    {
+      'target_name': 'devices_app_public_cpp_factory',
+      'type': 'static_library',
+      'sources': [
+        'public/cpp/devices_app_factory.cc',
+        'public/cpp/devices_app_factory.h',
+      ],
+      'dependencies': [
+        'devices_app_lib',
+      ],
+    },
   ],
 }
diff --git a/device/devices_app/devices_app.h b/device/devices_app/devices_app.h
index 4b04568..eda8d4c 100644
--- a/device/devices_app/devices_app.h
+++ b/device/devices_app/devices_app.h
@@ -27,26 +27,17 @@
 class DeviceManager;
 }
 
-extern const char kDevicesMojoAppUrl[];
-
 class DevicesApp : public mojo::ApplicationDelegate,
                    public mojo::InterfaceFactory<usb::DeviceManager>,
                    public mojo::ErrorHandler {
  public:
-  ~DevicesApp() override;
-
-  // |service_task_runner| is the thread TaskRunner on which the UsbService
-  // lives. This argument should be removed once UsbService is owned by the
-  // USB device manager and no longer part of the public device API. If null,
-  // the app will construct its own DeviceClient and UsbService.
-  static scoped_ptr<mojo::ApplicationDelegate> CreateDelegate(
+  explicit DevicesApp(
       scoped_refptr<base::SequencedTaskRunner> service_task_runner);
+  ~DevicesApp() override;
 
  private:
   class USBServiceInitializer;
 
-  DevicesApp(scoped_refptr<base::SequencedTaskRunner> service_task_runner);
-
   // mojo::ApplicationDelegate:
   void Initialize(mojo::ApplicationImpl* app) override;
   bool ConfigureIncomingConnection(
diff --git a/device/devices_app/main.cc b/device/devices_app/main.cc
index e82edb34..4c8e61b 100644
--- a/device/devices_app/main.cc
+++ b/device/devices_app/main.cc
@@ -3,12 +3,13 @@
 // found in the LICENSE file.
 
 #include "base/sequenced_task_runner.h"
-#include "device/devices_app/devices_app.h"
+#include "device/devices_app/public/cpp/devices_app_factory.h"
+#include "mojo/application/public/cpp/application_delegate.h"
 #include "mojo/application/public/cpp/application_runner.h"
 #include "third_party/mojo/src/mojo/public/c/system/main.h"
 
 MojoResult MojoMain(MojoHandle shell_handle) {
   mojo::ApplicationRunner runner(
-      device::DevicesApp::CreateDelegate(nullptr).release());
+      device::DevicesAppFactory::CreateApp(nullptr).release());
   return runner.Run(shell_handle);
 }
diff --git a/device/devices_app/public/cpp/BUILD.gn b/device/devices_app/public/cpp/BUILD.gn
new file mode 100644
index 0000000..301d20f
--- /dev/null
+++ b/device/devices_app/public/cpp/BUILD.gn
@@ -0,0 +1,29 @@
+# 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("cpp") {
+  sources = [
+    "constants.cc",
+    "constants.h",
+  ]
+
+  public_deps = [
+    "//base",
+  ]
+}
+
+source_set("factory") {
+  sources = [
+    "devices_app_factory.cc",
+    "devices_app_factory.h",
+  ]
+
+  deps = [
+    "//device/devices_app:lib",
+  ]
+
+  public_deps = [
+    "//base",
+  ]
+}
diff --git a/device/devices_app/public/cpp/constants.cc b/device/devices_app/public/cpp/constants.cc
new file mode 100644
index 0000000..211017b
--- /dev/null
+++ b/device/devices_app/public/cpp/constants.cc
@@ -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.
+
+#include "device/devices_app/public/cpp/constants.h"
+
+namespace device {
+
+const char kDevicesMojoAppUrl[] = "system:devices";
+
+}  // namespace device
diff --git a/device/devices_app/public/cpp/constants.h b/device/devices_app/public/cpp/constants.h
new file mode 100644
index 0000000..dfe242a
--- /dev/null
+++ b/device/devices_app/public/cpp/constants.h
@@ -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.
+
+#ifndef DEVICE_DEVICES_APP_PUBLIC_CPP_CONSTANTS_H_
+#define DEVICE_DEVICES_APP_PUBLIC_CPP_CONSTANTS_H_
+
+namespace device {
+
+extern const char kDevicesMojoAppUrl[];
+
+}  // namespace device
+
+#endif  // DEVICE_DEVICES_APP_PUBLIC_CPP_CONSTANTS_H_
diff --git a/device/devices_app/public/cpp/devices_app_factory.cc b/device/devices_app/public/cpp/devices_app_factory.cc
new file mode 100644
index 0000000..ee85c097
--- /dev/null
+++ b/device/devices_app/public/cpp/devices_app_factory.cc
@@ -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.
+
+#include "device/devices_app/public/cpp/devices_app_factory.h"
+
+#include "device/devices_app/devices_app.h"
+
+namespace device {
+
+// static
+scoped_ptr<mojo::ApplicationDelegate> DevicesAppFactory::CreateApp(
+    scoped_refptr<base::SequencedTaskRunner> service_task_runner) {
+  return scoped_ptr<mojo::ApplicationDelegate>(
+      new DevicesApp(service_task_runner));
+}
+
+}  // namespace device
diff --git a/device/devices_app/public/cpp/devices_app_factory.h b/device/devices_app/public/cpp/devices_app_factory.h
new file mode 100644
index 0000000..5642741
--- /dev/null
+++ b/device/devices_app/public/cpp/devices_app_factory.h
@@ -0,0 +1,38 @@
+// 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_DEVICES_APP_PUBLIC_CPP_DEVICES_APP_FACTORY_H_
+#define DEVICE_DEVICES_APP_PUBLIC_CPP_DEVICES_APP_FACTORY_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace mojo {
+class ApplicationDelegate;
+}
+
+namespace device {
+
+// Public factory for creating new instances of the devices app.
+class DevicesAppFactory {
+ public:
+  // Creates a DevicesApp delegate which can be used to launch a new instance
+  // of the devices app on a mojo application runner. The caller owns the
+  // delegate.
+  //
+  // |service_task_runner| is the thread TaskRunner on which the UsbService
+  // lives. This argument should be removed once UsbService is owned by the
+  // USB device manager and no longer part of the public device API. If null,
+  // the app will construct its own DeviceClient and UsbService.
+  static scoped_ptr<mojo::ApplicationDelegate> CreateApp(
+      scoped_refptr<base::SequencedTaskRunner> service_task_runner);
+};
+
+}  // namespace device
+
+#endif  // DEVICE_DEVICES_APP_PUBLIC_CPP_DEVICES_APP_FACTORY_H_
diff --git a/extensions/browser/extension_throttle_entry.cc b/extensions/browser/extension_throttle_entry.cc
index 1e22d94f..963c70f 100644
--- a/extensions/browser/extension_throttle_entry.cc
+++ b/extensions/browser/extension_throttle_entry.cc
@@ -87,37 +87,26 @@
 ExtensionThrottleEntry::ExtensionThrottleEntry(
     ExtensionThrottleManager* manager,
     const std::string& url_id,
-    int sliding_window_period_ms,
-    int max_send_threshold,
-    int initial_backoff_ms,
-    double multiply_factor,
-    double jitter_factor,
-    int maximum_backoff_ms)
+    const net::BackoffEntry::Policy* backoff_policy,
+    bool ignore_user_gesture_load_flag_for_tests)
     : sliding_window_period_(
-          base::TimeDelta::FromMilliseconds(sliding_window_period_ms)),
-      max_send_threshold_(max_send_threshold),
+          base::TimeDelta::FromMilliseconds(kDefaultSlidingWindowPeriodMs)),
+      max_send_threshold_(kDefaultMaxSendThreshold),
       is_backoff_disabled_(false),
       backoff_entry_(&backoff_policy_),
       manager_(manager),
       url_id_(url_id),
-      ignore_user_gesture_load_flag_for_tests_(false) {
-  DCHECK_GT(sliding_window_period_ms, 0);
-  DCHECK_GT(max_send_threshold_, 0);
-  DCHECK_GE(initial_backoff_ms, 0);
-  DCHECK_GT(multiply_factor, 0);
-  DCHECK_GE(jitter_factor, 0.0);
-  DCHECK_LT(jitter_factor, 1.0);
-  DCHECK_GE(maximum_backoff_ms, 0);
+      ignore_user_gesture_load_flag_for_tests_(
+          ignore_user_gesture_load_flag_for_tests) {
+  DCHECK_GE(backoff_policy->initial_delay_ms, 0);
+  DCHECK_GT(backoff_policy->multiply_factor, 0);
+  DCHECK_GE(backoff_policy->jitter_factor, 0.0);
+  DCHECK_LT(backoff_policy->jitter_factor, 1.0);
+  DCHECK_GE(backoff_policy->maximum_backoff_ms, 0);
   DCHECK(manager_);
 
   Initialize();
-  backoff_policy_.initial_delay_ms = initial_backoff_ms;
-  backoff_policy_.multiply_factor = multiply_factor;
-  backoff_policy_.jitter_factor = jitter_factor;
-  backoff_policy_.maximum_backoff_ms = maximum_backoff_ms;
-  backoff_policy_.entry_lifetime_ms = -1;
-  backoff_policy_.num_errors_to_ignore = 0;
-  backoff_policy_.always_use_initial_delay = false;
+  backoff_policy_ = *backoff_policy;
 }
 
 bool ExtensionThrottleEntry::IsEntryOutdated() const {
diff --git a/extensions/browser/extension_throttle_entry.h b/extensions/browser/extension_throttle_entry.h
index d0f25cb..61a70b27 100644
--- a/extensions/browser/extension_throttle_entry.h
+++ b/extensions/browser/extension_throttle_entry.h
@@ -71,12 +71,8 @@
   // It is only used by unit tests.
   ExtensionThrottleEntry(ExtensionThrottleManager* manager,
                          const std::string& url_id,
-                         int sliding_window_period_ms,
-                         int max_send_threshold,
-                         int initial_backoff_ms,
-                         double multiply_factor,
-                         double jitter_factor,
-                         int maximum_backoff_ms);
+                         const net::BackoffEntry::Policy* backoff_policy,
+                         bool ignore_user_gesture_load_flag_for_tests);
 
   // Used by the manager, returns true if the entry needs to be garbage
   // collected.
diff --git a/extensions/browser/extension_throttle_manager.cc b/extensions/browser/extension_throttle_manager.cc
index a8491de..bd92f61 100644
--- a/extensions/browser/extension_throttle_manager.cc
+++ b/extensions/browser/extension_throttle_manager.cc
@@ -85,8 +85,14 @@
 
   // Create the entry if needed.
   if (entry.get() == NULL) {
-    entry = new ExtensionThrottleEntry(
-        this, url_id, ignore_user_gesture_load_flag_for_tests_);
+    if (backoff_policy_for_tests_) {
+      entry = new ExtensionThrottleEntry(
+          this, url_id, backoff_policy_for_tests_.get(),
+          ignore_user_gesture_load_flag_for_tests_);
+    } else {
+      entry = new ExtensionThrottleEntry(
+          this, url_id, ignore_user_gesture_load_flag_for_tests_);
+    }
 
     // We only disable back-off throttling on an entry that we have
     // just constructed.  This is to allow unit tests to explicitly override
@@ -109,6 +115,11 @@
   return entry;
 }
 
+void ExtensionThrottleManager::SetBackoffPolicyForTests(
+    scoped_ptr<net::BackoffEntry::Policy> policy) {
+  backoff_policy_for_tests_ = policy.Pass();
+}
+
 void ExtensionThrottleManager::OverrideEntryForTests(
     const GURL& url,
     ExtensionThrottleEntry* entry) {
diff --git a/extensions/browser/extension_throttle_manager.h b/extensions/browser/extension_throttle_manager.h
index d48c05f..00b677f 100644
--- a/extensions/browser/extension_throttle_manager.h
+++ b/extensions/browser/extension_throttle_manager.h
@@ -14,6 +14,7 @@
 #include "base/threading/non_thread_safe.h"
 #include "base/threading/platform_thread.h"
 #include "extensions/browser/extension_throttle_entry.h"
+#include "net/base/backoff_entry.h"
 #include "net/base/net_export.h"
 #include "net/base/network_change_notifier.h"
 #include "url/gurl.h"
@@ -63,6 +64,8 @@
   scoped_refptr<ExtensionThrottleEntryInterface> RegisterRequestUrl(
       const GURL& url);
 
+  void SetBackoffPolicyForTests(scoped_ptr<net::BackoffEntry::Policy> policy);
+
   // Registers a new entry in this service and overrides the existing entry (if
   // any) for the URL. The service will hold a reference to the entry.
   // It is only used by unit tests.
@@ -171,6 +174,9 @@
 
   bool ignore_user_gesture_load_flag_for_tests_;
 
+  // This is NULL when it is not set for tests.
+  scoped_ptr<net::BackoffEntry::Policy> backoff_policy_for_tests_;
+
   DISALLOW_COPY_AND_ASSIGN(ExtensionThrottleManager);
 };
 
diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp
index 16e047b6..7fb6d57 100644
--- a/extensions/extensions.gyp
+++ b/extensions/extensions.gyp
@@ -228,6 +228,7 @@
       'target_name': 'extensions_utility',
       'type': 'static_library',
       'dependencies': [
+        '../content/content.gyp:content_common',
         '../content/content.gyp:content_utility',
         '../third_party/zlib/google/zip.gyp:zip',
         'extensions_common',
diff --git a/extensions/extensions.gypi b/extensions/extensions.gypi
index bd0d294..ac72ad86 100644
--- a/extensions/extensions.gypi
+++ b/extensions/extensions.gypi
@@ -1004,6 +1004,7 @@
       'renderer/utils_native_handler.h',
       'renderer/v8_context_native_handler.cc',
       'renderer/v8_context_native_handler.h',
+      'renderer/v8_helpers.h',
       'renderer/v8_schema_registry.cc',
       'renderer/v8_schema_registry.h',
       'renderer/web_ui_injection_host.cc',
diff --git a/extensions/renderer/logging_native_handler.cc b/extensions/renderer/logging_native_handler.cc
index 8a913a15..86fbc9c 100644
--- a/extensions/renderer/logging_native_handler.cc
+++ b/extensions/renderer/logging_native_handler.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "extensions/renderer/logging_native_handler.h"
-
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
+#include "extensions/renderer/logging_native_handler.h"
+#include "extensions/renderer/script_context.h"
 
 namespace extensions {
 
@@ -72,31 +72,7 @@
     *error_message = "Error: " + std::string(*v8::String::Utf8Value(args[1]));
   }
 
-  v8::Local<v8::StackTrace> stack_trace =
-      v8::StackTrace::CurrentStackTrace(args.GetIsolate(), 10);
-  if (stack_trace.IsEmpty() || stack_trace->GetFrameCount() <= 0) {
-    *error_message += "\n    <no stack trace>";
-  } else {
-    for (size_t i = 0; i < (size_t)stack_trace->GetFrameCount(); ++i) {
-      v8::Local<v8::StackFrame> frame = stack_trace->GetFrame(i);
-      CHECK(!frame.IsEmpty());
-      *error_message += base::StringPrintf(
-          "\n    at %s (%s:%d:%d)",
-          ToStringOrDefault(frame->GetFunctionName(), "<anonymous>").c_str(),
-          ToStringOrDefault(frame->GetScriptName(), "<anonymous>").c_str(),
-          frame->GetLineNumber(),
-          frame->GetColumn());
-    }
-  }
-}
-
-std::string LoggingNativeHandler::ToStringOrDefault(
-    const v8::Local<v8::String>& v8_string,
-    const std::string& dflt) {
-  if (v8_string.IsEmpty())
-    return dflt;
-  std::string ascii_value = *v8::String::Utf8Value(v8_string);
-  return ascii_value.empty() ? dflt : ascii_value;
+  *error_message += "\n" + context()->GetStackTraceAsString();
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/logging_native_handler.h b/extensions/renderer/logging_native_handler.h
index e9f4f20..ca9938c 100644
--- a/extensions/renderer/logging_native_handler.h
+++ b/extensions/renderer/logging_native_handler.h
@@ -46,9 +46,6 @@
   void ParseArgs(const v8::FunctionCallbackInfo<v8::Value>& args,
                  bool* check_value,
                  std::string* error_message);
-
-  std::string ToStringOrDefault(const v8::Local<v8::String>& v8_string,
-                                const std::string& dflt);
 };
 
 }  // namespace extensions
diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc
index 05b51b25..64e3502b 100644
--- a/extensions/renderer/module_system.cc
+++ b/extensions/renderer/module_system.cc
@@ -18,12 +18,15 @@
 #include "extensions/renderer/console.h"
 #include "extensions/renderer/safe_builtins.h"
 #include "extensions/renderer/script_context.h"
+#include "extensions/renderer/v8_helpers.h"
 #include "gin/modules/module_registry.h"
 #include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
 
 namespace extensions {
 
+using namespace v8_helpers;
+
 namespace {
 
 const char* kModuleSystem = "module_system";
@@ -69,7 +72,7 @@
 class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler {
  public:
   explicit DefaultExceptionHandler(ScriptContext* context)
-      : context_(context) {}
+      : ModuleSystem::ExceptionHandler(context) {}
 
   // Fatally dumps the debug info from |try_catch| to the console.
   // Make sure this is never used for exceptions that originate in external
@@ -77,8 +80,9 @@
   void HandleUncaughtException(const v8::TryCatch& try_catch) override {
     v8::HandleScope handle_scope(context_->isolate());
     std::string stack_trace = "<stack trace unavailable>";
-    if (!try_catch.StackTrace().IsEmpty()) {
-      v8::String::Utf8Value stack_value(try_catch.StackTrace());
+    v8::Local<v8::Value> v8_stack_trace;
+    if (try_catch.StackTrace(context_->v8_context()).ToLocal(&v8_stack_trace)) {
+      v8::String::Utf8Value stack_value(v8_stack_trace);
       if (*stack_value)
         stack_trace.assign(*stack_value, stack_value.length());
       else
@@ -113,9 +117,11 @@
     error_message.assign(*error_message_v8, error_message_v8.length());
   }
 
+  auto maybe = message->GetLineNumber(context_->v8_context());
+  int line_number = maybe.IsJust() ? maybe.FromJust() : 0;
   return base::StringPrintf("%s:%d: %s",
                             resource_name.c_str(),
-                            message->GetLineNumber(),
+                            line_number,
                             error_message.c_str());
 }
 
@@ -140,9 +146,9 @@
 
   v8::Local<v8::Object> global(context->v8_context()->Global());
   v8::Isolate* isolate = context->isolate();
-  global->SetHiddenValue(v8::String::NewFromUtf8(isolate, kModulesField),
+  global->SetHiddenValue(ToV8StringUnsafe(isolate, kModulesField),
                          v8::Object::New(isolate));
-  global->SetHiddenValue(v8::String::NewFromUtf8(isolate, kModuleSystem),
+  global->SetHiddenValue(ToV8StringUnsafe(isolate, kModuleSystem),
                          v8::External::New(isolate, this));
 
   gin::ModuleRegistry::From(context->v8_context())->AddObserver(this);
@@ -161,10 +167,8 @@
   {
     v8::HandleScope scope(GetIsolate());
     v8::Local<v8::Object> global = context()->v8_context()->Global();
-    global->DeleteHiddenValue(
-        v8::String::NewFromUtf8(GetIsolate(), kModulesField));
-    global->DeleteHiddenValue(
-        v8::String::NewFromUtf8(GetIsolate(), kModuleSystem));
+    global->DeleteHiddenValue(ToV8StringUnsafe(GetIsolate(), kModulesField));
+    global->DeleteHiddenValue(ToV8StringUnsafe(GetIsolate(), kModuleSystem));
   }
 
   // Invalidate all active and clobbered NativeHandlers we own.
@@ -192,21 +196,28 @@
 }
 
 v8::Local<v8::Value> ModuleSystem::Require(const std::string& module_name) {
+  if (module_name.size() >= v8::String::kMaxLength)
+    return v8::Undefined(GetIsolate());
   v8::EscapableHandleScope handle_scope(GetIsolate());
   return handle_scope.Escape(RequireForJsInner(
-      v8::String::NewFromUtf8(GetIsolate(), module_name.c_str())));
+      ToV8StringUnsafe(GetIsolate(), module_name.c_str())));
 }
 
 void ModuleSystem::RequireForJs(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
-  v8::Local<v8::String> module_name = args[0]->ToString(args.GetIsolate());
+  if (!args[0]->IsString()) {
+    NOTREACHED() << "require() called with a non-string argument";
+    return;
+  }
+  v8::Local<v8::String> module_name = args[0].As<v8::String>();
   args.GetReturnValue().Set(RequireForJsInner(module_name));
 }
 
 v8::Local<v8::Value> ModuleSystem::RequireForJsInner(
     v8::Local<v8::String> module_name) {
   v8::EscapableHandleScope handle_scope(GetIsolate());
-  v8::Context::Scope context_scope(context()->v8_context());
+  v8::Local<v8::Context> v8_context = context()->v8_context();
+  v8::Context::Scope context_scope(v8_context);
 
   v8::Local<v8::Object> global(context()->v8_context()->Global());
 
@@ -214,19 +225,20 @@
   // context keeps a reference to us, but our frame is destroyed (e.g.
   // background page keeps reference to chrome object in a closed popup).
   v8::Local<v8::Value> modules_value = global->GetHiddenValue(
-      v8::String::NewFromUtf8(GetIsolate(), kModulesField));
+      ToV8StringUnsafe(GetIsolate(), kModulesField));
   if (modules_value.IsEmpty() || modules_value->IsUndefined()) {
     Warn(GetIsolate(), "Extension view no longer exists");
     return v8::Undefined(GetIsolate());
   }
 
   v8::Local<v8::Object> modules(v8::Local<v8::Object>::Cast(modules_value));
-  v8::Local<v8::Value> exports(modules->Get(module_name));
-  if (!exports->IsUndefined())
+  v8::Local<v8::Value> exports;
+  if (!GetProperty(v8_context, modules, module_name, &exports) ||
+      !exports->IsUndefined())
     return handle_scope.Escape(exports);
 
   exports = LoadModule(*v8::String::Utf8Value(module_name));
-  modules->Set(module_name, exports);
+  SetProperty(v8_context, modules, module_name, exports);
   return handle_scope.Escape(exports);
 }
 
@@ -260,38 +272,46 @@
                method_name);
 
   v8::EscapableHandleScope handle_scope(GetIsolate());
-  v8::Context::Scope context_scope(context()->v8_context());
+  v8::Local<v8::Context> v8_context = context()->v8_context();
+  v8::Context::Scope context_scope(v8_context);
+
+  v8::Local<v8::String> v8_module_name;
+  v8::Local<v8::String> v8_method_name;
+  if (!ToV8String(GetIsolate(), module_name.c_str(), &v8_module_name) ||
+      !ToV8String(GetIsolate(), method_name.c_str(), &v8_method_name)) {
+    return handle_scope.Escape(v8::Undefined(GetIsolate()));
+  }
 
   v8::Local<v8::Value> module;
   {
     NativesEnabledScope natives_enabled(this);
-    module = RequireForJsInner(
-        v8::String::NewFromUtf8(GetIsolate(), module_name.c_str()));
+    module = RequireForJsInner(v8_module_name);
   }
 
   if (module.IsEmpty() || !module->IsObject()) {
     Fatal(context_,
           "Failed to get module " + module_name + " to call " + method_name);
-    return handle_scope.Escape(
-        v8::Local<v8::Primitive>(v8::Undefined(GetIsolate())));
+    return handle_scope.Escape(v8::Undefined(GetIsolate()));
   }
 
-  v8::Local<v8::Value> value = v8::Local<v8::Object>::Cast(module)->Get(
-      v8::String::NewFromUtf8(GetIsolate(), method_name.c_str()));
-  if (value.IsEmpty() || !value->IsFunction()) {
+  v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(module);
+  v8::Local<v8::Value> value;
+  if (!GetProperty(v8_context, object, v8_method_name, &value) ||
+      !value->IsFunction()) {
     Fatal(context_, module_name + "." + method_name + " is not a function");
-    return handle_scope.Escape(
-        v8::Local<v8::Primitive>(v8::Undefined(GetIsolate())));
+    return handle_scope.Escape(v8::Undefined(GetIsolate()));
   }
 
   v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(value);
   v8::Local<v8::Value> result;
   {
-    v8::TryCatch try_catch;
+    v8::TryCatch try_catch(GetIsolate());
     try_catch.SetCaptureMessage(true);
     result = context_->CallFunction(func, argc, argv);
-    if (try_catch.HasCaught())
+    if (try_catch.HasCaught()) {
       HandleException(try_catch);
+      result = v8::Undefined(GetIsolate());
+    }
   }
   return handle_scope.Escape(result);
 }
@@ -311,22 +331,29 @@
 
 void ModuleSystem::RunString(const std::string& code, const std::string& name) {
   v8::HandleScope handle_scope(GetIsolate());
-  RunString(v8::String::NewFromUtf8(GetIsolate(), code.c_str()),
-            v8::String::NewFromUtf8(GetIsolate(), name.c_str()));
+  v8::Local<v8::String> v8_code;
+  v8::Local<v8::String> v8_name;
+  if (!ToV8String(GetIsolate(), code.c_str(), &v8_code) ||
+      !ToV8String(GetIsolate(), name.c_str(), &v8_name)) {
+    Warn(GetIsolate(), "Too long code or name.");
+    return;
+  }
+  RunString(v8_code, v8_name);
 }
 
 // static
 void ModuleSystem::NativeLazyFieldGetter(
-    v8::Local<v8::String> property,
+    v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  LazyFieldGetterInner(property, info, &ModuleSystem::RequireNativeFromString);
+  LazyFieldGetterInner(property.As<v8::String>(), info,
+                       &ModuleSystem::RequireNativeFromString);
 }
 
 // static
 void ModuleSystem::LazyFieldGetter(
-    v8::Local<v8::String> property,
+    v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  LazyFieldGetterInner(property, info, &ModuleSystem::Require);
+  LazyFieldGetterInner(property.As<v8::String>(), info, &ModuleSystem::Require);
 }
 
 // static
@@ -342,7 +369,7 @@
   v8::Local<v8::Context> context = parameters->CreationContext();
   v8::Local<v8::Object> global(context->Global());
   v8::Local<v8::Value> module_system_value = global->GetHiddenValue(
-      v8::String::NewFromUtf8(info.GetIsolate(), kModuleSystem));
+      ToV8StringUnsafe(info.GetIsolate(), kModuleSystem));
   if (module_system_value.IsEmpty() || !module_system_value->IsExternal()) {
     // ModuleSystem has been deleted.
     // TODO(kalman): See comment in header file.
@@ -354,15 +381,19 @@
   ModuleSystem* module_system = static_cast<ModuleSystem*>(
       v8::Local<v8::External>::Cast(module_system_value)->Value());
 
-  std::string name = *v8::String::Utf8Value(parameters->Get(
-      v8::String::NewFromUtf8(info.GetIsolate(), kModuleName)));
+  v8::Local<v8::Value> v8_module_name;
+  if (!GetProperty(context, parameters, kModuleName, &v8_module_name)) {
+    Warn(info.GetIsolate(), "Cannot find module.");
+    return;
+  }
+  std::string name = *v8::String::Utf8Value(v8_module_name);
 
   // Switch to our v8 context because we need functions created while running
   // the require()d module to belong to our context, not the current one.
   v8::Context::Scope context_scope(context);
   NativesEnabledScope natives_enabled_scope(module_system);
 
-  v8::TryCatch try_catch;
+  v8::TryCatch try_catch(info.GetIsolate());
   v8::Local<v8::Value> module_value = (module_system->*require_function)(name);
   if (try_catch.HasCaught()) {
     module_system->HandleException(try_catch);
@@ -374,11 +405,18 @@
   }
 
   v8::Local<v8::Object> module = v8::Local<v8::Object>::Cast(module_value);
-  v8::Local<v8::String> field =
-      parameters->Get(v8::String::NewFromUtf8(info.GetIsolate(), kModuleField))
-          ->ToString(info.GetIsolate());
+  v8::Local<v8::Value> field_value;
+  if (!GetProperty(context, parameters, kModuleField, &field_value)) {
+    module_system->HandleException(try_catch);
+    return;
+  }
+  v8::Local<v8::String> field;
+  if (!field_value->ToString(context).ToLocal(&field)) {
+    module_system->HandleException(try_catch);
+    return;
+  }
 
-  if (!module->Has(field)) {
+  if (!IsTrue(module->Has(context, field))) {
     std::string field_str = *v8::String::Utf8Value(field);
     Fatal(module_system->context_,
           "Lazy require of " + name + "." + field_str + " did not set the " +
@@ -386,8 +424,8 @@
     return;
   }
 
-  v8::Local<v8::Value> new_field = module->Get(field);
-  if (try_catch.HasCaught()) {
+  v8::Local<v8::Value> new_field;
+  if (!GetProperty(context, module, field, &new_field)) {
     module_system->HandleException(try_catch);
     return;
   }
@@ -401,8 +439,8 @@
   v8::Local<v8::Value> val = info.This();
   if (val->IsObject()) {
     v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(val);
-    object->Delete(property);
-    object->Set(property, new_field);
+    object->Delete(context, property);
+    SetProperty(context, object, property, new_field);
   } else {
     NOTREACHED();
   }
@@ -421,17 +459,21 @@
                                 const std::string& field,
                                 const std::string& module_name,
                                 const std::string& module_field,
-                                v8::AccessorGetterCallback getter) {
+                                v8::AccessorNameGetterCallback getter) {
+  CHECK(field.size() < v8::String::kMaxLength);
+  CHECK(module_name.size() < v8::String::kMaxLength);
+  CHECK(module_field.size() < v8::String::kMaxLength);
   v8::HandleScope handle_scope(GetIsolate());
   v8::Local<v8::Object> parameters = v8::Object::New(GetIsolate());
-  parameters->Set(v8::String::NewFromUtf8(GetIsolate(), kModuleName),
-                  v8::String::NewFromUtf8(GetIsolate(), module_name.c_str()));
-  parameters->Set(v8::String::NewFromUtf8(GetIsolate(), kModuleField),
-                  v8::String::NewFromUtf8(GetIsolate(), module_field.c_str()));
-  object->SetAccessor(v8::String::NewFromUtf8(GetIsolate(), field.c_str()),
-                      getter,
-                      NULL,
-                      parameters);
+  v8::Local<v8::Context> context = context_->v8_context();
+  SetProperty(context, parameters, kModuleName,
+              ToV8StringUnsafe(GetIsolate(), module_name.c_str()));
+  SetProperty(context, parameters, kModuleField,
+              ToV8StringUnsafe(GetIsolate(), module_field.c_str()));
+  auto maybe = object->SetAccessor(
+      context, ToV8StringUnsafe(GetIsolate(), field.c_str()), getter, NULL,
+      parameters);
+  CHECK(IsTrue(maybe));
 }
 
 void ModuleSystem::SetNativeLazyField(v8::Local<v8::Object> object,
@@ -448,27 +490,32 @@
 v8::Local<v8::Value> ModuleSystem::RunString(v8::Local<v8::String> code,
                                              v8::Local<v8::String> name) {
   v8::EscapableHandleScope handle_scope(GetIsolate());
-  v8::Context::Scope context_scope(context()->v8_context());
+  v8::Local<v8::Context> v8_context = context()->v8_context();
+  v8::Context::Scope context_scope(v8_context);
 
   // Prepend extensions:: to |name| so that internal code can be differentiated
   // from external code in stack traces. This has no effect on behaviour.
   std::string internal_name =
       base::StringPrintf("extensions::%s", *v8::String::Utf8Value(name));
 
+  if (internal_name.size() >= v8::String::kMaxLength) {
+    NOTREACHED() << "internal_name is too long.";
+    return v8::Undefined(GetIsolate());
+  }
+
   blink::WebScopedMicrotaskSuppression suppression;
-  v8::TryCatch try_catch;
+  v8::TryCatch try_catch(GetIsolate());
   try_catch.SetCaptureMessage(true);
-  v8::Local<v8::Script> script(v8::Script::Compile(
-      code, v8::String::NewFromUtf8(GetIsolate(), internal_name.c_str(),
-                                    v8::String::kNormalString,
-                                    internal_name.size())));
-  if (try_catch.HasCaught()) {
+  v8::ScriptOrigin origin(
+      ToV8StringUnsafe(GetIsolate(), internal_name.c_str()));
+  v8::Local<v8::Script> script;
+  if (!v8::Script::Compile(v8_context, code, &origin).ToLocal(&script)) {
     HandleException(try_catch);
     return v8::Undefined(GetIsolate());
   }
 
-  v8::Local<v8::Value> result = script->Run();
-  if (try_catch.HasCaught()) {
+  v8::Local<v8::Value> result;
+  if (!script->Run(v8_context).ToLocal(&result)) {
     HandleException(try_catch);
     return v8::Undefined(GetIsolate());
   }
@@ -499,7 +546,7 @@
     // we could crash.
     if (exception_handler_) {
       return GetIsolate()->ThrowException(
-          v8::String::NewFromUtf8(GetIsolate(), "Natives disabled"));
+          ToV8StringUnsafe(GetIsolate(), "Natives disabled"));
     }
     Fatal(context_, "Natives disabled for requireNative(" + native_name + ")");
     return v8::Undefined(GetIsolate());
@@ -507,7 +554,7 @@
 
   if (overridden_native_handlers_.count(native_name) > 0u) {
     return RequireForJsInner(
-        v8::String::NewFromUtf8(GetIsolate(), native_name.c_str()));
+        ToV8StringUnsafe(GetIsolate(), native_name.c_str()));
   }
 
   NativeHandlerMap::iterator i = native_handler_map_.find(native_name);
@@ -523,16 +570,17 @@
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   CHECK_EQ(1, args.Length());
   std::string module_name = *v8::String::Utf8Value(args[0]);
+  v8::Local<v8::Context> v8_context = context_->v8_context();
   v8::Local<v8::Promise::Resolver> resolver(
-      v8::Promise::Resolver::New(GetIsolate()));
+      v8::Promise::Resolver::New(v8_context).ToLocalChecked());
   args.GetReturnValue().Set(resolver->GetPromise());
   scoped_ptr<v8::Global<v8::Promise::Resolver>> global_resolver(
       new v8::Global<v8::Promise::Resolver>(GetIsolate(), resolver));
   gin::ModuleRegistry* module_registry =
-      gin::ModuleRegistry::From(context_->v8_context());
+      gin::ModuleRegistry::From(v8_context);
   if (!module_registry) {
     Warn(GetIsolate(), "Extension view no longer exists");
-    resolver->Reject(v8::Exception::Error(v8::String::NewFromUtf8(
+    resolver->Reject(v8_context, v8::Exception::Error(ToV8StringUnsafe(
         GetIsolate(), "Extension view no longer exists")));
     return;
   }
@@ -547,13 +595,13 @@
 v8::Local<v8::String> ModuleSystem::WrapSource(v8::Local<v8::String> source) {
   v8::EscapableHandleScope handle_scope(GetIsolate());
   // Keep in order with the arguments in RequireForJsInner.
-  v8::Local<v8::String> left = v8::String::NewFromUtf8(
+  v8::Local<v8::String> left = ToV8StringUnsafe(
       GetIsolate(),
       "(function(define, require, requireNative, requireAsync, exports, "
       "console, privates,"
       "$Array, $Function, $JSON, $Object, $RegExp, $String, $Error) {"
       "'use strict';");
-  v8::Local<v8::String> right = v8::String::NewFromUtf8(GetIsolate(), "\n})");
+  v8::Local<v8::String> right = ToV8StringUnsafe(GetIsolate(), "\n})");
   return handle_scope.Escape(v8::Local<v8::String>(
       v8::String::Concat(left, v8::String::Concat(source, right))));
 }
@@ -562,7 +610,7 @@
   CHECK_EQ(1, args.Length());
   if (!args[0]->IsObject() || args[0]->IsNull()) {
     GetIsolate()->ThrowException(
-        v8::Exception::TypeError(v8::String::NewFromUtf8(GetIsolate(),
+        v8::Exception::TypeError(ToV8StringUnsafe(GetIsolate(),
             args[0]->IsUndefined()
                 ? "Method called without a valid receiver (this). "
                   "Did you forget to call .bind()?"
@@ -571,13 +619,13 @@
   }
   v8::Local<v8::Object> obj = args[0].As<v8::Object>();
   v8::Local<v8::String> privates_key =
-      v8::String::NewFromUtf8(GetIsolate(), "privates");
+      ToV8StringUnsafe(GetIsolate(), "privates");
   v8::Local<v8::Value> privates = obj->GetHiddenValue(privates_key);
   if (privates.IsEmpty()) {
     privates = v8::Object::New(args.GetIsolate());
     if (privates.IsEmpty()) {
       GetIsolate()->ThrowException(
-          v8::String::NewFromUtf8(GetIsolate(), "Failed to create privates"));
+          ToV8StringUnsafe(GetIsolate(), "Failed to create privates"));
       return;
     }
     obj->SetHiddenValue(privates_key, privates);
@@ -587,7 +635,8 @@
 
 v8::Local<v8::Value> ModuleSystem::LoadModule(const std::string& module_name) {
   v8::EscapableHandleScope handle_scope(GetIsolate());
-  v8::Context::Scope context_scope(context()->v8_context());
+  v8::Local<v8::Context> v8_context = context()->v8_context();
+  v8::Context::Scope context_scope(v8_context);
 
   v8::Local<v8::Value> source(GetSource(module_name));
   if (source.IsEmpty() || source->IsUndefined()) {
@@ -596,10 +645,14 @@
   }
   v8::Local<v8::String> wrapped_source(
       WrapSource(v8::Local<v8::String>::Cast(source)));
+  v8::Local<v8::String> v8_module_name;
+  if (!ToV8String(GetIsolate(), module_name.c_str(), &v8_module_name)) {
+    NOTREACHED() << "module_name is too long";
+    return v8::Undefined(GetIsolate());
+  }
   // Modules are wrapped in (function(){...}) so they always return functions.
   v8::Local<v8::Value> func_as_value =
-      RunString(wrapped_source,
-                v8::String::NewFromUtf8(GetIsolate(), module_name.c_str()));
+      RunString(wrapped_source, v8_module_name);
   if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) {
     Fatal(context_, "Bad source for require(" + module_name + ")");
     return v8::Undefined(GetIsolate());
@@ -612,24 +665,24 @@
 
   v8::Local<v8::Value> exports = v8::Object::New(GetIsolate());
   v8::Local<v8::Object> natives(NewInstance());
-  CHECK(!natives.IsEmpty());  // this can happen if v8 has issues
+  CHECK(!natives.IsEmpty());  // this can fail if v8 has issues
 
   // These must match the argument order in WrapSource.
   v8::Local<v8::Value> args[] = {
       // AMD.
-      define_object->Get(v8::String::NewFromUtf8(GetIsolate(), "define")),
+      GetPropertyUnsafe(v8_context, define_object, "define"),
       // CommonJS.
-      natives->Get(v8::String::NewFromUtf8(GetIsolate(), "require",
-                                           v8::String::kInternalizedString)),
-      natives->Get(v8::String::NewFromUtf8(GetIsolate(), "requireNative",
-                                           v8::String::kInternalizedString)),
-      natives->Get(v8::String::NewFromUtf8(GetIsolate(), "requireAsync",
-                                           v8::String::kInternalizedString)),
+      GetPropertyUnsafe(v8_context, natives, "require",
+                        v8::NewStringType::kInternalized),
+      GetPropertyUnsafe(v8_context, natives, "requireNative",
+                        v8::NewStringType::kInternalized),
+      GetPropertyUnsafe(v8_context, natives, "requireAsync",
+                        v8::NewStringType::kInternalized),
       exports,
       // Libraries that we magically expose to every module.
       console::AsV8Object(GetIsolate()),
-      natives->Get(v8::String::NewFromUtf8(GetIsolate(), "privates",
-                                           v8::String::kInternalizedString)),
+      GetPropertyUnsafe(v8_context, natives, "privates",
+                        v8::NewStringType::kInternalized),
       // Each safe builtin. Keep in order with the arguments in WrapSource.
       context_->safe_builtins()->GetArray(),
       context_->safe_builtins()->GetFunction(),
@@ -640,7 +693,7 @@
       context_->safe_builtins()->GetError(),
   };
   {
-    v8::TryCatch try_catch;
+    v8::TryCatch try_catch(GetIsolate());
     try_catch.SetCaptureMessage(true);
     context_->CallFunction(func, arraysize(args), args);
     if (try_catch.HasCaught()) {
@@ -680,7 +733,7 @@
   v8::HandleScope handle_scope(GetIsolate());
   v8::Local<v8::Promise::Resolver> resolver_local(
       v8::Local<v8::Promise::Resolver>::New(GetIsolate(), *resolver));
-  resolver_local->Resolve(value);
+  resolver_local->Resolve(context()->v8_context(), value);
 }
 
 void ModuleSystem::ClobberExistingNativeHandler(const std::string& name) {
diff --git a/extensions/renderer/module_system.h b/extensions/renderer/module_system.h
index 82474b6..895aa56 100644
--- a/extensions/renderer/module_system.h
+++ b/extensions/renderer/module_system.h
@@ -51,12 +51,16 @@
 
   class ExceptionHandler {
    public:
+    explicit ExceptionHandler(ScriptContext* context) : context_(context) {}
     virtual ~ExceptionHandler() {}
     virtual void HandleUncaughtException(const v8::TryCatch& try_catch) = 0;
 
    protected:
     // Formats |try_catch| as a nice string.
     std::string CreateExceptionString(const v8::TryCatch& try_catch);
+    // A script context associated with this handler. Owned by the module
+    // system.
+    ScriptContext* context_;
   };
 
   // Enables native bindings for the duration of its lifetime.
@@ -126,7 +130,7 @@
                     const std::string& field,
                     const std::string& module_name,
                     const std::string& module_field,
-                    v8::AccessorGetterCallback getter);
+                    v8::AccessorNameGetterCallback getter);
 
   // Make |object|.|field| lazily evaluate to the result of
   // requireNative(|module_name|)[|module_field|].
@@ -150,12 +154,12 @@
   typedef std::map<std::string, linked_ptr<NativeHandler> > NativeHandlerMap;
 
   // Retrieves the lazily defined field specified by |property|.
-  static void LazyFieldGetter(v8::Local<v8::String> property,
+  static void LazyFieldGetter(v8::Local<v8::Name> property,
                               const v8::PropertyCallbackInfo<v8::Value>& info);
   // Retrieves the lazily defined field specified by |property| on a native
   // object.
   static void NativeLazyFieldGetter(
-      v8::Local<v8::String> property,
+      v8::Local<v8::Name> property,
       const v8::PropertyCallbackInfo<v8::Value>& info);
 
   // Called when an exception is thrown but not caught.
diff --git a/extensions/renderer/module_system_test.cc b/extensions/renderer/module_system_test.cc
index 04de2b1a..50b64167 100644
--- a/extensions/renderer/module_system_test.cc
+++ b/extensions/renderer/module_system_test.cc
@@ -27,6 +27,7 @@
 
 class FailsOnException : public ModuleSystem::ExceptionHandler {
  public:
+  FailsOnException() : ModuleSystem::ExceptionHandler(nullptr) {}
   void HandleUncaughtException(const v8::TryCatch& try_catch) override {
     FAIL() << "Uncaught exception: " << CreateExceptionString(try_catch);
   }
@@ -129,10 +130,10 @@
   context_holder_->SetContext(v8::Context::New(
       isolate, g_v8_extension_configurator.Get().GetConfiguration()));
   context_.reset(new ScriptContext(context_holder_->context(),
-                                   NULL,  // WebFrame
-                                   NULL,  // Extension
+                                   nullptr,  // WebFrame
+                                   nullptr,  // Extension
                                    Feature::BLESSED_EXTENSION_CONTEXT,
-                                   NULL,  // Effective Extension
+                                   nullptr,  // Effective Extension
                                    Feature::BLESSED_EXTENSION_CONTEXT));
   context_->v8_context()->Enter();
   assert_natives_ = new AssertNatives(context_.get());
diff --git a/extensions/renderer/module_system_unittest.cc b/extensions/renderer/module_system_unittest.cc
index e687841d..c5c916e 100644
--- a/extensions/renderer/module_system_unittest.cc
+++ b/extensions/renderer/module_system_unittest.cc
@@ -34,7 +34,8 @@
 
 class TestExceptionHandler : public ModuleSystem::ExceptionHandler {
  public:
-  TestExceptionHandler() : handled_exception_(false) {}
+  TestExceptionHandler()
+      : ModuleSystem::ExceptionHandler(nullptr), handled_exception_(false) {}
 
   void HandleUncaughtException(const v8::TryCatch& try_catch) override {
     handled_exception_ = true;
@@ -404,11 +405,14 @@
                             "    return 'pong';"
                             "  }"
                             "});");
-  gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule(
-      env()->isolate(), "natives", other_env->module_system()->NewInstance());
+  gin::ModuleRegistry::From(env()->context()->v8_context())
+      ->AddBuiltinModule(
+          env()->isolate(), "natives",
+          other_env->module_system()->NewInstance());
   gin::ModuleRegistry::From(other_env->context()->v8_context())
       ->AddBuiltinModule(
-          env()->isolate(), "natives", env()->module_system()->NewInstance());
+          env()->isolate(), "natives",
+          env()->module_system()->NewInstance());
   env()->module_system()->Require("test");
   RunResolvedPromises();
 }
@@ -438,11 +442,14 @@
                             "    return natives.requireAsync('pong');"
                             "  }"
                             "});");
-  gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule(
-      env()->isolate(), "natives", other_env->module_system()->NewInstance());
+  gin::ModuleRegistry::From(env()->context()->v8_context())
+      ->AddBuiltinModule(
+          env()->isolate(), "natives",
+          other_env->module_system()->NewInstance());
   gin::ModuleRegistry::From(other_env->context()->v8_context())
       ->AddBuiltinModule(
-          env()->isolate(), "natives", env()->module_system()->NewInstance());
+          env()->isolate(), "natives",
+          env()->module_system()->NewInstance());
   env()->module_system()->Require("test");
   RunResolvedPromises();
 }
@@ -461,8 +468,10 @@
                         "  });"
                         "});");
   scoped_ptr<ModuleSystemTestEnvironment> other_env = CreateEnvironment();
-  gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule(
-      env()->isolate(), "natives", other_env->module_system()->NewInstance());
+  gin::ModuleRegistry::From(env()->context()->v8_context())
+      ->AddBuiltinModule(
+          env()->isolate(), "natives",
+          other_env->module_system()->NewInstance());
   other_env->ShutdownGin();
   env()->module_system()->Require("test");
   RunResolvedPromises();
@@ -477,8 +486,10 @@
                         "      natives.requireAsync('foo') === undefined);"
                         "});");
   scoped_ptr<ModuleSystemTestEnvironment> other_env = CreateEnvironment();
-  gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule(
-      env()->isolate(), "natives", other_env->module_system()->NewInstance());
+  gin::ModuleRegistry::From(env()->context()->v8_context())
+      ->AddBuiltinModule(
+          env()->isolate(), "natives",
+          other_env->module_system()->NewInstance());
   other_env->ShutdownModuleSystem();
   env()->module_system()->Require("test");
   RunResolvedPromises();
diff --git a/extensions/renderer/resources/guest_view/web_view/web_view_api_methods.js b/extensions/renderer/resources/guest_view/web_view/web_view_api_methods.js
index e939e5d..a28741b 100644
--- a/extensions/renderer/resources/guest_view/web_view/web_view_api_methods.js
+++ b/extensions/renderer/resources/guest_view/web_view/web_view_api_methods.js
@@ -8,8 +8,22 @@
 var WebViewImpl = require('webView').WebViewImpl;
 
 // An array of <webview>'s public-facing API methods. Methods without custom
-// implementations will be given default implementations. Default
-// implementations come from createDefaultApiMethod() in web_view.js.
+// implementations will be given default implementations that call into the
+// internal API method with the same name in |WebViewInternal|. For example, a
+// method called 'someApiMethod' would be given the following default
+// implementation:
+//
+// WebViewImpl.prototype.someApiMethod = function(var_args) {
+//   if (!this.guest.getId()) {
+//     return false;
+//   }
+//   var args = $Array.concat([this.guest.getId()], $Array.slice(arguments));
+//   $Function.apply(WebViewInternal.someApiMethod, null, args);
+//   return true;
+// };
+//
+// These default implementations come from createDefaultApiMethod() in
+// web_view.js.
 var WEB_VIEW_API_METHODS = [
   // Add content scripts for the guest page.
   'addContentScripts',
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc
index 32222b0..e6dcfe5 100644
--- a/extensions/renderer/script_context.cc
+++ b/extensions/renderer/script_context.cc
@@ -59,6 +59,15 @@
   return std::string();
 }
 
+static std::string ToStringOrDefault(
+    const v8::Local<v8::String>& v8_string,
+    const std::string& dflt) {
+  if (v8_string.IsEmpty())
+    return dflt;
+  std::string ascii_value = *v8::String::Utf8Value(v8_string);
+  return ascii_value.empty() ? dflt : ascii_value;
+}
+
 }  // namespace
 
 // A gin::Runner that delegates to its ScriptContext.
@@ -380,6 +389,27 @@
       GetEffectiveContextTypeDescription().c_str());
 }
 
+std::string ScriptContext::GetStackTraceAsString() const {
+  v8::Local<v8::StackTrace> stack_trace =
+      v8::StackTrace::CurrentStackTrace(isolate(), 10);
+  if (stack_trace.IsEmpty() || stack_trace->GetFrameCount() <= 0) {
+    return "    <no stack trace>";
+  } else {
+    std::string result;
+    for (int i = 0; i < stack_trace->GetFrameCount(); ++i) {
+      v8::Local<v8::StackFrame> frame = stack_trace->GetFrame(i);
+      CHECK(!frame.IsEmpty());
+      result += base::StringPrintf(
+          "\n    at %s (%s:%d:%d)",
+          ToStringOrDefault(frame->GetFunctionName(), "<anonymous>").c_str(),
+          ToStringOrDefault(frame->GetScriptName(), "<anonymous>").c_str(),
+          frame->GetLineNumber(),
+          frame->GetColumn());
+    }
+    return result;
+  }
+}
+
 ScriptContext::Runner::Runner(ScriptContext* context) : context_(context) {
 }
 
diff --git a/extensions/renderer/script_context.h b/extensions/renderer/script_context.h
index 945eccbf..669de27 100644
--- a/extensions/renderer/script_context.h
+++ b/extensions/renderer/script_context.h
@@ -183,6 +183,9 @@
   // Returns a string representation of this ScriptContext, for debugging.
   std::string GetDebugString() const;
 
+  // Gets the current stack trace as a multi-line string to be logged.
+  std::string GetStackTraceAsString() const;
+
  private:
   class Runner;
 
diff --git a/extensions/renderer/v8_helpers.h b/extensions/renderer/v8_helpers.h
new file mode 100644
index 0000000..385490f4
--- /dev/null
+++ b/extensions/renderer/v8_helpers.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 EXTENSIONS_RENDERER_V8_HELPERS_H_
+#define EXTENSIONS_RENDERER_V8_HELPERS_H_
+
+#include <string.h>
+
+#include "v8/include/v8.h"
+
+namespace extensions {
+namespace v8_helpers {
+
+// Helper functions for V8 APIs.
+
+// Converts |str| to a V8 string. Returns true on success.
+inline bool ToV8String(v8::Isolate* isolate,
+                       const char* str,
+                       v8::Local<v8::String>* out) {
+  return v8::String::NewFromUtf8(isolate, str, v8::NewStringType::kNormal)
+      .ToLocal(out);
+}
+
+// Converts |str| to a V8 string.
+// This crashes when strlen(str) > v8::String::kMaxLength.
+inline v8::Local<v8::String> ToV8StringUnsafe(
+    v8::Isolate* isolate,
+    const char* str,
+    v8::NewStringType string_type = v8::NewStringType::kNormal) {
+  DCHECK(strlen(str) <= v8::String::kMaxLength);
+  return v8::String::NewFromUtf8(isolate, str, string_type)
+      .ToLocalChecked();
+}
+
+// Returns true if |maybe| is both a value, and that value is true.
+inline bool IsTrue(v8::Maybe<bool> maybe) {
+  return maybe.IsJust() && maybe.FromJust();
+}
+
+// SetProperty() family wraps V8::Object::Set(). Returns true on success.
+inline bool SetProperty(v8::Local<v8::Context> context,
+                        v8::Local<v8::Object> object,
+                        v8::Local<v8::Value> key,
+                        v8::Local<v8::Value> value) {
+  return IsTrue(object->Set(context, key, value));
+}
+
+inline bool SetProperty(v8::Local<v8::Context> context,
+                        v8::Local<v8::Object> object,
+                        uint32_t index,
+                        v8::Local<v8::Value> value) {
+  return IsTrue(object->Set(context, index, value));
+}
+
+inline bool SetProperty(v8::Local<v8::Context> context,
+                        v8::Local<v8::Object> object,
+                        const char* key,
+                        v8::Local<v8::Value> value) {
+  v8::Local<v8::String> v8_key;
+  if (!ToV8String(context->GetIsolate(), key, &v8_key))
+    return false;
+  return SetProperty(context, object, v8_key, value);
+}
+
+// GetProperty() family calls V8::Object::Get() and extracts a value from
+// returned MaybeLocal. Returns true on success.
+inline bool GetProperty(v8::Local<v8::Context> context,
+                        v8::Local<v8::Object> object,
+                        v8::Local<v8::Value> key,
+                        v8::Local<v8::Value>* out) {
+  return object->Get(context, key).ToLocal(out);
+}
+
+inline bool GetProperty(v8::Local<v8::Context> context,
+                        v8::Local<v8::Object> object,
+                        const char* key,
+                        v8::Local<v8::Value>* out) {
+  v8::Local<v8::String> v8_key;
+  if (!ToV8String(context->GetIsolate(), key, &v8_key))
+    return false;
+  return GetProperty(context, object, v8_key, out);
+}
+
+// GetPropertyUnsafe() family wraps v8::Object::Get(). They crash when an
+// exception is thrown.
+inline v8::Local<v8::Value> GetPropertyUnsafe(v8::Local<v8::Context> context,
+                                              v8::Local<v8::Object> object,
+                                              v8::Local<v8::Value> key) {
+  return object->Get(context, key).ToLocalChecked();
+}
+
+inline v8::Local<v8::Value> GetPropertyUnsafe(
+    v8::Local<v8::Context> context,
+    v8::Local<v8::Object> object,
+    const char* key,
+    v8::NewStringType string_type = v8::NewStringType::kNormal) {
+  return object->Get(context,
+                     ToV8StringUnsafe(context->GetIsolate(), key, string_type))
+      .ToLocalChecked();
+}
+
+}  // namespace v8_helpers
+}  // namespace extensions
+
+#endif  // EXTENSIONS_RENDERER_V8_HELPERS_H_
diff --git a/extensions/shell/browser/shell_content_browser_client.cc b/extensions/shell/browser/shell_content_browser_client.cc
index 957b8308..c9314ad6 100644
--- a/extensions/shell/browser/shell_content_browser_client.cc
+++ b/extensions/shell/browser/shell_content_browser_client.cc
@@ -28,7 +28,6 @@
 #include "extensions/shell/browser/shell_browser_main_parts.h"
 #include "extensions/shell/browser/shell_extension_system.h"
 #include "extensions/shell/browser/shell_speech_recognition_manager_delegate.h"
-#include "gin/v8_initializer.h"
 #include "url/gurl.h"
 
 #if !defined(DISABLE_NACL)
@@ -54,12 +53,7 @@
 
 ShellContentBrowserClient::ShellContentBrowserClient(
     ShellBrowserMainDelegate* browser_main_delegate)
-    :
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-      v8_natives_fd_(-1),
-      v8_snapshot_fd_(-1),
-#endif  // OS_POSIX && !OS_MACOSX
-      browser_main_parts_(nullptr),
+    : browser_main_parts_(nullptr),
       browser_main_delegate_(browser_main_delegate) {
   DCHECK(!g_instance);
   g_instance = this;
@@ -200,21 +194,6 @@
                  site_instance->GetId()));
 }
 
-void ShellContentBrowserClient::AppendMappedFileCommandLineSwitches(
-    base::CommandLine* command_line) {
-  std::string process_type =
-      command_line->GetSwitchValueASCII(::switches::kProcessType);
-
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
-  DCHECK(natives_fd_exists());
-  command_line->AppendSwitch(::switches::kV8NativesPassedByFD);
-  if (snapshot_fd_exists())
-    command_line->AppendSwitch(::switches::kV8SnapshotPassedByFD);
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // OS_POSIX && !OS_MACOSX
-}
-
 void ShellContentBrowserClient::AppendExtraCommandLineSwitches(
     base::CommandLine* command_line,
     int child_process_id) {
@@ -254,30 +233,6 @@
   additional_allowed_schemes->push_back(kExtensionScheme);
 }
 
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-void ShellContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
-    const base::CommandLine& command_line,
-    int child_process_id,
-    content::FileDescriptorInfo* mappings) {
-#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
-  if (!natives_fd_exists()) {
-    int v8_natives_fd = -1;
-    int v8_snapshot_fd = -1;
-    if (gin::V8Initializer::OpenV8FilesForChildProcesses(&v8_natives_fd,
-                                                         &v8_snapshot_fd)) {
-      v8_natives_fd_.reset(v8_natives_fd);
-      v8_snapshot_fd_.reset(v8_snapshot_fd);
-    }
-  }
-  // V8 can't start up without the source of the natives, but it can
-  // start up (slower) without the snapshot.
-  DCHECK(natives_fd_exists());
-  mappings->Share(kV8NativesDataDescriptor, v8_natives_fd_.get());
-  mappings->Share(kV8SnapshotDataDescriptor, v8_snapshot_fd_.get());
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-}
-#endif  // OS_POSIX && !OS_MACOSX
-
 content::DevToolsManagerDelegate*
 ShellContentBrowserClient::GetDevToolsManagerDelegate() {
   return new content::ShellDevToolsManagerDelegate(GetBrowserContext());
diff --git a/extensions/shell/browser/shell_content_browser_client.h b/extensions/shell/browser/shell_content_browser_client.h
index d282397..16e51ca 100644
--- a/extensions/shell/browser/shell_content_browser_client.h
+++ b/extensions/shell/browser/shell_content_browser_client.h
@@ -52,21 +52,12 @@
   void SiteInstanceDeleting(content::SiteInstance* site_instance) override;
   void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                       int child_process_id) override;
-  void AppendMappedFileCommandLineSwitches(
-      base::CommandLine* command_line) override;
   content::SpeechRecognitionManagerDelegate*
   CreateSpeechRecognitionManagerDelegate() override;
   content::BrowserPpapiHost* GetExternalBrowserPpapiHost(
       int plugin_process_id) override;
   void GetAdditionalAllowedSchemesForFileSystem(
       std::vector<std::string>* additional_schemes) override;
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-  void GetAdditionalMappedFilesForChildProcess(
-      const base::CommandLine& command_line,
-      int child_process_id,
-      content::FileDescriptorInfo* mappings) override;
-#endif
-
   content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override;
 
  protected:
@@ -82,13 +73,6 @@
   // Returns the extension or app associated with |site_instance| or NULL.
   const Extension* GetExtension(content::SiteInstance* site_instance);
 
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-  base::ScopedFD v8_natives_fd_;
-  base::ScopedFD v8_snapshot_fd_;
-  bool natives_fd_exists() { return v8_natives_fd_ != -1; }
-  bool snapshot_fd_exists() { return v8_snapshot_fd_ != -1; }
-#endif
-
   // Owned by content::BrowserMainLoop.
   ShellBrowserMainParts* browser_main_parts_;
 
diff --git a/extensions/utility/BUILD.gn b/extensions/utility/BUILD.gn
index 52f9d83..5f9923a5eda 100644
--- a/extensions/utility/BUILD.gn
+++ b/extensions/utility/BUILD.gn
@@ -17,6 +17,7 @@
   configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   deps = [
+    "//content/public/common",
     "//content/public/utility",
     "//extensions/common",
     "//skia",
diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc
index 0568fe7..cc854e1 100644
--- a/gin/v8_initializer.cc
+++ b/gin/v8_initializer.cc
@@ -5,10 +5,12 @@
 #include "gin/v8_initializer.h"
 
 #include "base/basictypes.h"
+#include "base/debug/alias.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/memory_mapped_file.h"
 #include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/metrics/histogram.h"
 #include "base/rand_util.h"
 #include "base/strings/sys_string_conversions.h"
@@ -27,10 +29,27 @@
 
 namespace {
 
+// None of these globals are ever freed nor closed.
 base::MemoryMappedFile* g_mapped_natives = nullptr;
 base::MemoryMappedFile* g_mapped_snapshot = nullptr;
 
 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
+
+const base::PlatformFile kInvalidPlatformFile =
+#if defined(OS_WIN)
+    INVALID_HANDLE_VALUE;
+#else
+    -1;
+#endif
+
+// File handles intentionally never closed. Not using File here because its
+// Windows implementation guards against two instances owning the same
+// PlatformFile (which we allow since we know it is never freed).
+base::PlatformFile g_natives_pf = kInvalidPlatformFile;
+base::PlatformFile g_snapshot_pf = kInvalidPlatformFile;
+base::MemoryMappedFile::Region g_natives_region;
+base::MemoryMappedFile::Region g_snapshot_region;
+
 #if !defined(OS_MACOSX)
 const int kV8SnapshotBasePathKey =
 #if defined(OS_ANDROID)
@@ -65,24 +84,20 @@
   DCHECK(!path_out->empty());
 }
 
-static bool MapV8File(base::File file,
+static bool MapV8File(base::PlatformFile platform_file,
                       base::MemoryMappedFile::Region region,
                       base::MemoryMappedFile** mmapped_file_out) {
   DCHECK(*mmapped_file_out == NULL);
-  base::MemoryMappedFile* mmapped_file = *mmapped_file_out =
-      new base::MemoryMappedFile;
-  if (!mmapped_file->Initialize(file.Pass(), region)) {
-    delete mmapped_file;
-    *mmapped_file_out = NULL;
-    return false;
+  scoped_ptr<base::MemoryMappedFile> mmapped_file(new base::MemoryMappedFile());
+  if (mmapped_file->Initialize(base::File(platform_file), region)) {
+    *mmapped_file_out = mmapped_file.release();
+    return true;
   }
-
-  return true;
+  return false;
 }
 
-static bool OpenV8File(const base::FilePath& path,
-                       int flags,
-                       base::File& file) {
+base::PlatformFile OpenV8File(const char* file_name,
+                              base::MemoryMappedFile::Region* region_out) {
   // Re-try logic here is motivated by http://crbug.com/479537
   // for A/V on Windows (https://support.microsoft.com/en-us/kb/316609).
 
@@ -95,10 +110,16 @@
     MAX_VALUE
   };
 
+  base::FilePath path;
+  GetV8FilePath(file_name, &path);
+
   OpenV8FileResult result = OpenV8FileResult::FAILED_IN_USE;
+  int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
+  base::File file;
   for (int attempt = 0; attempt < kMaxOpenAttempts; attempt++) {
     file.Initialize(path, flags);
     if (file.IsValid()) {
+      *region_out = base::MemoryMappedFile::Region::kWholeFile;
       if (attempt == 0) {
         result = OpenV8FileResult::OPENED;
         break;
@@ -108,6 +129,17 @@
       }
     } else if (file.error_details() != base::File::FILE_ERROR_IN_USE) {
       result = OpenV8FileResult::FAILED_OTHER;
+#ifdef OS_WIN
+      // TODO(oth): temporary diagnostics for http://crbug.com/479537
+      std::string narrow(kNativesFileName);
+      base::FilePath::StringType nativesBlob(narrow.begin(), narrow.end());
+      if (path.BaseName().value() == nativesBlob) {
+        base::File::Error file_error = file.error_details();
+        base::debug::Alias(&file_error);
+        LOG(FATAL) << "Failed to open V8 file '" << path.value()
+                   << "' (reason: " << file.error_details() << ")";
+      }
+#endif  // OS_WIN
       break;
     } else if (kMaxOpenAttempts - 1 != attempt) {
       base::PlatformThread::Sleep(
@@ -118,9 +150,19 @@
   UMA_HISTOGRAM_ENUMERATION("V8.Initializer.OpenV8File.Result",
                             result,
                             OpenV8FileResult::MAX_VALUE);
+  return file.TakePlatformFile();
+}
 
-  return result == OpenV8FileResult::OPENED
-         || result == OpenV8FileResult::OPENED_RETRY;
+void OpenNativesFileIfNecessary() {
+  if (g_natives_pf == kInvalidPlatformFile) {
+    g_natives_pf = OpenV8File(kNativesFileName, &g_natives_region);
+  }
+}
+
+void OpenSnapshotFileIfNecessary() {
+  if (g_snapshot_pf == kInvalidPlatformFile) {
+    g_snapshot_pf = OpenV8File(kSnapshotFileName, &g_snapshot_region);
+  }
 }
 
 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
@@ -134,6 +176,20 @@
   if (!memcmp(fingerprint, output, sizeof(output))) {
     return true;
   }
+
+  // TODO(oth): Remove this temporary diagnostics for http://crbug.com/501799
+  uint64_t input[sizeof(output)];
+  memcpy(input, fingerprint, sizeof(input));
+
+  base::debug::Alias(output);
+  base::debug::Alias(input);
+
+  const uint64_t* o64 = reinterpret_cast<const uint64_t*>(output);
+  const uint64_t* f64 = reinterpret_cast<const uint64_t*>(fingerprint);
+  LOG(FATAL) << "Natives length " << (*file)->length()
+             << " H(computed) " << o64[0] << o64[1] << o64[2] << o64[3]
+             << " H(expected) " << f64[0] << f64[1] << f64[2] << f64[3];
+
   delete *file;
   *file = NULL;
   return false;
@@ -163,22 +219,15 @@
   V8_LOAD_MAX_VALUE
 };
 
-static LoadV8FileResult OpenMapVerify(
-    const char* file_name,
+static LoadV8FileResult MapVerify(base::PlatformFile platform_file,
+                                  const base::MemoryMappedFile::Region& region,
 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
-    const unsigned char* fingerprint,
+                                  const unsigned char* fingerprint,
 #endif
-    base::MemoryMappedFile** mmapped_file_out) {
-  base::FilePath path;
-  GetV8FilePath(file_name, &path);
-
-  base::File file;
-  int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
-
-  if (!OpenV8File(path, flags, file))
+                                  base::MemoryMappedFile** mmapped_file_out) {
+  if (platform_file == kInvalidPlatformFile)
     return V8_LOAD_FAILED_OPEN;
-  if (!MapV8File(file.Pass(), base::MemoryMappedFile::Region::kWholeFile,
-                 mmapped_file_out))
+  if (!MapV8File(platform_file, region, mmapped_file_out))
     return V8_LOAD_FAILED_MAP;
 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
   if (!VerifyV8StartupFile(mmapped_file_out, fingerprint))
@@ -192,11 +241,14 @@
   if (g_mapped_snapshot)
     return;
 
-  LoadV8FileResult result = OpenMapVerify(kSnapshotFileName,
+  OpenSnapshotFileIfNecessary();
+  LoadV8FileResult result = MapVerify(g_snapshot_pf, g_snapshot_region,
 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
-                                          g_snapshot_fingerprint,
+                                      g_snapshot_fingerprint,
 #endif
-                                          &g_mapped_snapshot);
+                                      &g_mapped_snapshot);
+  // V8 can't start up without the source of the natives, but it can
+  // start up (slower) without the snapshot.
   UMA_HISTOGRAM_ENUMERATION("V8.Initializer.LoadV8Snapshot.Result", result,
                             V8_LOAD_MAX_VALUE);
 }
@@ -205,11 +257,12 @@
   if (g_mapped_natives)
     return;
 
-  LoadV8FileResult result = OpenMapVerify(kNativesFileName,
+  OpenNativesFileIfNecessary();
+  LoadV8FileResult result = MapVerify(g_natives_pf, g_natives_region,
 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
-                                          g_natives_fingerprint,
+                                      g_natives_fingerprint,
 #endif
-                                          &g_mapped_natives);
+                                      &g_mapped_natives);
   if (result != V8_LOAD_SUCCESS) {
     LOG(FATAL) << "Couldn't mmap v8 natives data file, status code is "
                << static_cast<int>(result);
@@ -223,18 +276,18 @@
   if (g_mapped_snapshot)
     return;
 
-  if (snapshot_pf == reinterpret_cast<base::PlatformFile>(-1))
+  if (snapshot_pf == kInvalidPlatformFile)
     return;
 
   base::MemoryMappedFile::Region snapshot_region =
       base::MemoryMappedFile::Region::kWholeFile;
   if (snapshot_size != 0 || snapshot_offset != 0) {
-    snapshot_region =
-        base::MemoryMappedFile::Region(snapshot_offset, snapshot_size);
+    snapshot_region.offset = snapshot_offset;
+    snapshot_region.size = snapshot_size;
   }
 
   LoadV8FileResult result = V8_LOAD_SUCCESS;
-  if (!MapV8File(base::File(snapshot_pf), snapshot_region, &g_mapped_snapshot))
+  if (!MapV8File(snapshot_pf, snapshot_region, &g_mapped_snapshot))
     result = V8_LOAD_FAILED_MAP;
 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
   if (!VerifyV8StartupFile(&g_mapped_snapshot, g_snapshot_fingerprint))
@@ -251,16 +304,16 @@
   if (g_mapped_natives)
     return;
 
-  CHECK_NE(natives_pf, reinterpret_cast<base::PlatformFile>(-1));
+  CHECK_NE(natives_pf, kInvalidPlatformFile);
 
   base::MemoryMappedFile::Region natives_region =
       base::MemoryMappedFile::Region::kWholeFile;
   if (natives_size != 0 || natives_offset != 0) {
-    natives_region =
-        base::MemoryMappedFile::Region(natives_offset, natives_size);
+    natives_region.offset = natives_offset;
+    natives_region.size = natives_size;
   }
 
-  if (!MapV8File(base::File(natives_pf), natives_region, &g_mapped_natives)) {
+  if (!MapV8File(natives_pf, natives_region, &g_mapped_natives)) {
     LOG(FATAL) << "Couldn't mmap v8 natives data file";
   }
 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
@@ -271,33 +324,21 @@
 }
 
 // static
-bool V8Initializer::OpenV8FilesForChildProcesses(
-    base::PlatformFile* natives_fd_out,
-    base::PlatformFile* snapshot_fd_out) {
-  base::FilePath natives_data_path;
-  base::FilePath snapshot_data_path;
-  GetV8FilePath(kNativesFileName, &natives_data_path);
-  GetV8FilePath(kSnapshotFileName, &snapshot_data_path);
-
-  base::File natives_data_file;
-  base::File snapshot_data_file;
-  int file_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
-
-  bool natives_success =
-      OpenV8File(natives_data_path, file_flags, natives_data_file);
-  if (natives_success) {
-    *natives_fd_out = natives_data_file.TakePlatformFile();
-  }
-  bool snapshot_success =
-      OpenV8File(snapshot_data_path, file_flags, snapshot_data_file);
-  if (snapshot_success) {
-    *snapshot_fd_out = snapshot_data_file.TakePlatformFile();
-  }
-  // We can start up without the snapshot file, but not without the natives.
-  return natives_success;
+base::PlatformFile V8Initializer::GetOpenNativesFileForChildProcesses(
+    base::MemoryMappedFile::Region* region_out) {
+  OpenNativesFileIfNecessary();
+  *region_out = g_natives_region;
+  return g_natives_pf;
 }
 
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+// static
+base::PlatformFile V8Initializer::GetOpenSnapshotFileForChildProcesses(
+    base::MemoryMappedFile::Region* region_out) {
+  OpenSnapshotFileIfNecessary();
+  *region_out = g_snapshot_region;
+  return g_snapshot_pf;
+}
+#endif  // defined(V8_USE_EXTERNAL_STARTUP_DATA)
 
 // static
 void V8Initializer::Initialize(gin::IsolateHolder::ScriptMode mode) {
diff --git a/gin/v8_initializer.h b/gin/v8_initializer.h
index 1e05fd0..e181436 100644
--- a/gin/v8_initializer.h
+++ b/gin/v8_initializer.h
@@ -6,6 +6,7 @@
 #define GIN_V8_INITIALIZER_H_
 
 #include "base/files/file.h"
+#include "base/files/memory_mapped_file.h"
 #include "gin/array_buffer.h"
 #include "gin/gin_export.h"
 #include "gin/public/isolate_holder.h"
@@ -50,12 +51,17 @@
   // so that it will not return if natives cannot be loaded.
   static void LoadV8Natives();
 
-  // Opens the V8 snapshot data files and returns open file descriptors to these
-  // files in |natives_fd_out| and |snapshot_fd_out|, which can be passed to
-  // child processes.
-  static bool OpenV8FilesForChildProcesses(base::PlatformFile* natives_fd_out,
-                                           base::PlatformFile* snapshot_fd_out)
-      WARN_UNUSED_RESULT;
+  // Opens (unless already cached) and returns the V8 natives file.
+  // Use with LoadV8NativesFromFD().
+  // Asserts if the file does not exist.
+  static base::PlatformFile GetOpenNativesFileForChildProcesses(
+      base::MemoryMappedFile::Region* region_out);
+
+  // Opens (unless already cached) and returns the V8 snapshot file.
+  // Use with LoadV8SnapshotFromFD().
+  // Will return -1 if the file does not exist.
+  static base::PlatformFile GetOpenSnapshotFileForChildProcesses(
+      base::MemoryMappedFile::Region* region_out);
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
 };
 
diff --git a/google_apis/gcm/engine/unregistration_request.cc b/google_apis/gcm/engine/unregistration_request.cc
index bb68c72..90bda2f 100644
--- a/google_apis/gcm/engine/unregistration_request.cc
+++ b/google_apis/gcm/engine/unregistration_request.cc
@@ -56,6 +56,7 @@
     scoped_ptr<CustomRequestHandler> custom_request_handler,
     const net::BackoffEntry::Policy& backoff_policy,
     const UnregistrationCallback& callback,
+    int max_retry_count,
     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
     GCMStatsRecorder* recorder)
     : callback_(callback),
@@ -64,8 +65,10 @@
       registration_url_(registration_url),
       backoff_entry_(&backoff_policy),
       request_context_getter_(request_context_getter),
+      retries_left_(max_retry_count),
       recorder_(recorder),
       weak_ptr_factory_(this) {
+  DCHECK_GE(max_retry_count, 0);
 }
 
 UnregistrationRequest::~UnregistrationRequest() {}
@@ -140,6 +143,8 @@
 
 void UnregistrationRequest::RetryWithBackoff(bool update_backoff) {
   if (update_backoff) {
+    DCHECK_GT(retries_left_, 0);
+    --retries_left_;
     url_fetcher_.reset();
     backoff_entry_.InformOfRequest(false);
   }
@@ -151,7 +156,8 @@
              << " milliseconds.";
     recorder_->RecordUnregistrationRetryDelayed(
         request_info_.app_id,
-        backoff_entry_.GetTimeUntilRelease().InMilliseconds());
+        backoff_entry_.GetTimeUntilRelease().InMilliseconds(),
+        retries_left_ + 1);
     base::MessageLoop::current()->PostDelayedTask(
         FROM_HERE,
         base::Bind(&UnregistrationRequest::RetryWithBackoff,
@@ -178,16 +184,28 @@
   recorder_->RecordUnregistrationResponse(request_info_.app_id, status);
 
   if (status == URL_FETCHING_FAILED ||
+      status == HTTP_NOT_OK ||
+      status == NO_RESPONSE_BODY ||
       status == SERVICE_UNAVAILABLE ||
       status == INTERNAL_SERVER_ERROR ||
       status == INCORRECT_APP_ID ||
       status == RESPONSE_PARSING_FAILED) {
-    RetryWithBackoff(true);
-    return;
+    if (retries_left_ > 0) {
+      RetryWithBackoff(true);
+      return;
+    }
+
+    status = REACHED_MAX_RETRIES;
+    recorder_->RecordUnregistrationResponse(request_info_.app_id, status);
+
+    // Only REACHED_MAX_RETRIES is reported because the function will skip
+    // reporting count and time when status is not SUCCESS.
+    DCHECK(custom_request_handler_.get());
+    custom_request_handler_->ReportUMAs(status, 0, base::TimeDelta());
   }
 
-  // status == SUCCESS || HTTP_NOT_OK || NO_RESPONSE_BODY ||
-  // INVALID_PARAMETERS || UNKNOWN_ERROR
+  // status == SUCCESS || INVALID_PARAMETERS || UNKNOWN_ERROR ||
+  //           REACHED_MAX_RETRIES
 
   callback_.Run(status);
 }
diff --git a/google_apis/gcm/engine/unregistration_request.h b/google_apis/gcm/engine/unregistration_request.h
index 0f54538f..501a943 100644
--- a/google_apis/gcm/engine/unregistration_request.h
+++ b/google_apis/gcm/engine/unregistration_request.h
@@ -46,6 +46,7 @@
     INTERNAL_SERVER_ERROR,    // Internal server error happened during request.
     HTTP_NOT_OK,              // HTTP response code was not OK.
     UNKNOWN_ERROR,            // Unknown error.
+    REACHED_MAX_RETRIES,      // Reached maximum number of retries.
     // NOTE: Always keep this entry at the end. Add new status types only
     // immediately above this line. Make sure to update the corresponding
     // histogram enum accordingly.
@@ -102,6 +103,7 @@
       scoped_ptr<CustomRequestHandler> custom_request_handler,
       const net::BackoffEntry::Policy& backoff_policy,
       const UnregistrationCallback& callback,
+      int max_retry_count,
       scoped_refptr<net::URLRequestContextGetter> request_context_getter,
       GCMStatsRecorder* recorder);
   ~UnregistrationRequest() override;
@@ -130,6 +132,7 @@
   scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
   scoped_ptr<net::URLFetcher> url_fetcher_;
   base::TimeTicks request_start_time_;
+  int retries_left_;
 
   // Recorder that records GCM activities for debugging purpose. Not owned.
   GCMStatsRecorder* recorder_;
diff --git a/google_apis/gcm/engine/unregistration_request_unittest.cc b/google_apis/gcm/engine/unregistration_request_unittest.cc
index 2cdde2a5..3f58788 100644
--- a/google_apis/gcm/engine/unregistration_request_unittest.cc
+++ b/google_apis/gcm/engine/unregistration_request_unittest.cc
@@ -18,6 +18,7 @@
 namespace gcm {
 
 namespace {
+const int kMaxRetries = 2;
 const uint64 kAndroidId = 42UL;
 const char kLoginHeader[] = "AidLogin";
 const char kAppId[] = "TestAppId";
@@ -36,7 +37,7 @@
   // exponential back-off rules.
   // Explicitly set to 2 to skip the delay on the first retry, as we are not
   // trying to test the backoff itself, but rather the fact that retry happens.
-  1,
+  2,
 
   // Initial delay for exponential back-off in ms.
   15000,  // 15 seconds.
@@ -71,7 +72,13 @@
                                   const std::string& response_body);
   void CompleteFetch();
 
+  int max_retry_count() const { return max_retry_count_; }
+  void set_max_retry_count(int max_retry_count) {
+    max_retry_count_ = max_retry_count;
+  }
+
  protected:
+  int max_retry_count_;
   bool callback_called_;
   UnregistrationRequest::Status status_;
   scoped_ptr<UnregistrationRequest> request_;
@@ -82,7 +89,8 @@
 };
 
 UnregistrationRequestTest::UnregistrationRequestTest()
-    : callback_called_(false),
+    : max_retry_count_(kMaxRetries),
+      callback_called_(false),
       status_(UnregistrationRequest::UNREGISTRATION_STATUS_COUNT),
       url_request_context_getter_(new net::TestURLRequestContextGetter(
           message_loop_.task_runner())) {}
@@ -138,6 +146,7 @@
       kDefaultBackoffPolicy,
       base::Bind(&UnregistrationRequestTest::UnregistrationCallback,
                  base::Unretained(this)),
+      max_retry_count_,
       url_request_context_getter_.get(),
       &recorder_));
 }
@@ -191,6 +200,7 @@
 }
 
 TEST_F(GCMUnregistrationRequestTest, SuccessfulUnregistration) {
+  set_max_retry_count(0);
   CreateRequest();
   request_->Start();
 
@@ -208,8 +218,13 @@
   SetResponseStatusAndString(net::HTTP_UNAUTHORIZED, "");
   CompleteFetch();
 
+  EXPECT_FALSE(callback_called_);
+
+  SetResponseStatusAndString(net::HTTP_OK, kDeletedAppId);
+  CompleteFetch();
+
   EXPECT_TRUE(callback_called_);
-  EXPECT_EQ(UnregistrationRequest::HTTP_NOT_OK, status_);
+  EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
 }
 
 TEST_F(GCMUnregistrationRequestTest, ResponseEmpty) {
@@ -314,6 +329,39 @@
   EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
 }
 
+TEST_F(GCMUnregistrationRequestTest, MaximumAttemptsReachedWithZeroRetries) {
+  set_max_retry_count(0);
+  CreateRequest();
+  request_->Start();
+
+  SetResponseStatusAndString(net::HTTP_GATEWAY_TIMEOUT, "bad response");
+  CompleteFetch();
+
+  EXPECT_TRUE(callback_called_);
+  EXPECT_EQ(UnregistrationRequest::REACHED_MAX_RETRIES, status_);
+}
+
+TEST_F(GCMUnregistrationRequestTest, MaximumAttemptsReached) {
+  CreateRequest();
+  request_->Start();
+
+  SetResponseStatusAndString(net::HTTP_GATEWAY_TIMEOUT, "bad response");
+  CompleteFetch();
+
+  EXPECT_FALSE(callback_called_);
+
+  SetResponseStatusAndString(net::HTTP_GATEWAY_TIMEOUT, "bad response");
+  CompleteFetch();
+
+  EXPECT_FALSE(callback_called_);
+
+  SetResponseStatusAndString(net::HTTP_GATEWAY_TIMEOUT, "bad response");
+  CompleteFetch();
+
+  EXPECT_TRUE(callback_called_);
+  EXPECT_EQ(UnregistrationRequest::REACHED_MAX_RETRIES, status_);
+}
+
 class InstaceIDDeleteTokenRequestTest : public UnregistrationRequestTest {
  public:
   InstaceIDDeleteTokenRequestTest();
@@ -346,6 +394,7 @@
       kDefaultBackoffPolicy,
       base::Bind(&UnregistrationRequestTest::UnregistrationCallback,
                  base::Unretained(this)),
+      max_retry_count(),
       url_request_context_getter_.get(),
       &recorder_));
 }
@@ -420,8 +469,13 @@
   SetResponseStatusAndString(net::HTTP_UNAUTHORIZED, "");
   CompleteFetch();
 
+  EXPECT_FALSE(callback_called_);
+
+  SetResponseStatusAndString(net::HTTP_OK, kDeletedToken);
+  CompleteFetch();
+
   EXPECT_TRUE(callback_called_);
-  EXPECT_EQ(UnregistrationRequest::HTTP_NOT_OK, status_);
+  EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
 }
 
 }  // namespace gcm
diff --git a/google_apis/gcm/monitoring/fake_gcm_stats_recorder.cc b/google_apis/gcm/monitoring/fake_gcm_stats_recorder.cc
index 0388d24..d4c781e 100644
--- a/google_apis/gcm/monitoring/fake_gcm_stats_recorder.cc
+++ b/google_apis/gcm/monitoring/fake_gcm_stats_recorder.cc
@@ -70,7 +70,8 @@
 
 void FakeGCMStatsRecorder::RecordUnregistrationRetryDelayed(
     const std::string& app_id,
-    int64 delay_msec) {
+    int64 delay_msec,
+    int retries_left) {
 }
 
 void FakeGCMStatsRecorder::RecordDataMessageReceived(
diff --git a/google_apis/gcm/monitoring/fake_gcm_stats_recorder.h b/google_apis/gcm/monitoring/fake_gcm_stats_recorder.h
index 5dcd4483..a48146c 100644
--- a/google_apis/gcm/monitoring/fake_gcm_stats_recorder.h
+++ b/google_apis/gcm/monitoring/fake_gcm_stats_recorder.h
@@ -39,7 +39,8 @@
       const std::string& app_id,
       UnregistrationRequest::Status status) override;
   void RecordUnregistrationRetryDelayed(const std::string& app_id,
-                                        int64 delay_msec) override;
+                                        int64 delay_msec,
+                                        int retries_left) override;
   void RecordDataMessageReceived(const std::string& app_id,
                                  const std::string& from,
                                  int message_byte_size,
diff --git a/google_apis/gcm/monitoring/gcm_stats_recorder.h b/google_apis/gcm/monitoring/gcm_stats_recorder.h
index 83367d8e..5b493f8 100644
--- a/google_apis/gcm/monitoring/gcm_stats_recorder.h
+++ b/google_apis/gcm/monitoring/gcm_stats_recorder.h
@@ -100,7 +100,8 @@
   // Records that an unregistration retry has been requested and delayed due to
   // backoff logic.
   virtual void RecordUnregistrationRetryDelayed(const std::string& app_id,
-                                                int64 delay_msec) = 0;
+                                                int64 delay_msec,
+                                                int retries_left) = 0;
 
   // Records that a data message has been received. If this message is not
   // sent to a registered app, to_registered_app shoudl be false. If it
diff --git a/gpu/command_buffer/service/async_pixel_transfer_manager_egl.cc b/gpu/command_buffer/service/async_pixel_transfer_manager_egl.cc
index 1d76dd5d..6e1c3e7 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_manager_egl.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_manager_egl.cc
@@ -91,7 +91,7 @@
  public:
   TransferThread() : base::Thread(kAsyncTransferThreadName) {
     base::Thread::Options options;
-#if defined(OS_ANDROID) || defined(OS_LINUX)
+#if defined(OS_ANDROID)
     options.priority = base::ThreadPriority::BACKGROUND;
 #endif
     StartWithOptions(options);
@@ -468,16 +468,8 @@
 
 void AsyncPixelTransferDelegateEGL::WaitForTransferCompletion() {
   if (state_->TransferIsInProgress()) {
-#if defined(OS_ANDROID) || defined(OS_LINUX)
-    g_transfer_thread.Pointer()->SetPriority(base::ThreadPriority::DISPLAY);
-#endif
-
     state_->WaitForTransferCompletion();
     DCHECK(!state_->TransferIsInProgress());
-
-#if defined(OS_ANDROID) || defined(OS_LINUX)
-    g_transfer_thread.Pointer()->SetPriority(base::ThreadPriority::BACKGROUND);
-#endif
   }
 }
 
diff --git a/gpu/command_buffer/service/in_process_command_buffer.cc b/gpu/command_buffer/service/in_process_command_buffer.cc
index 692fb8e..24628f5 100644
--- a/gpu/command_buffer/service/in_process_command_buffer.cc
+++ b/gpu/command_buffer/service/in_process_command_buffer.cc
@@ -221,6 +221,21 @@
   }
 }
 
+scoped_refptr<InProcessCommandBuffer::Service> GetInitialService(
+    const scoped_refptr<InProcessCommandBuffer::Service>& service) {
+  if (service)
+    return service;
+
+  // Call base::ThreadTaskRunnerHandle::IsSet() to ensure that it is
+  // instantiated before we create the GPU thread, otherwise shutdown order will
+  // delete the ThreadTaskRunnerHandle before the GPU thread's message loop,
+  // and when the message loop is shutdown, it will recreate
+  // ThreadTaskRunnerHandle, which will re-add a new task to the, AtExitManager,
+  // which causes a deadlock because it's already locked.
+  base::ThreadTaskRunnerHandle::IsSet();
+  return g_default_service.Get().gpu_thread;
+}
+
 }  // anonyous namespace
 
 InProcessCommandBuffer::Service::Service() {}
@@ -271,7 +286,7 @@
       last_put_offset_(-1),
       gpu_memory_buffer_manager_(nullptr),
       flush_event_(false, false),
-      service_(service.get() ? service : g_default_service.Get().gpu_thread),
+      service_(GetInitialService(service)),
       gpu_thread_weak_ptr_factory_(this) {
   DCHECK(service_.get());
   next_image_id_.GetNext();
diff --git a/gpu/command_buffer/service/shader_translator.cc b/gpu/command_buffer/service/shader_translator.cc
index 026f425f..04e1227 100644
--- a/gpu/command_buffer/service/shader_translator.cc
+++ b/gpu/command_buffer/service/shader_translator.cc
@@ -195,6 +195,9 @@
     *info_log = ShGetInfoLog(compiler_);
   }
 
+  // We don't need results in the compiler anymore.
+  ShClearResults(compiler_);
+
   return success;
 }
 
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp
index 4e9a76c..52c81390 100644
--- a/gpu/gpu.gyp
+++ b/gpu/gpu.gyp
@@ -706,7 +706,7 @@
         },
       ],
     }],
-    ['OS == "win"', {
+    ['OS == "win" or OS == "linux"', {
       'targets': [
         {
           # TODO(kbr): port this target to the GN build.
@@ -724,6 +724,10 @@
             'angle_end2end_tests_main.cc',
           ],
         },
+      ],
+    }],
+    ['OS == "win"', {
+      'targets': [
         {
           # TODO(jmadill): port this target to the GN build.
           'target_name': 'angle_perftests',
diff --git a/gpu/skia_runner/BUILD.gn b/gpu/skia_runner/BUILD.gn
new file mode 100644
index 0000000..b275091
--- /dev/null
+++ b/gpu/skia_runner/BUILD.gn
@@ -0,0 +1,29 @@
+# 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")
+
+# GYP version: //gpu/skia_runner/skia_runner.gyp:skia_runner
+executable("skia_runner") {
+  sources = [
+    "in_process_graphics_system.cc",
+    "sk_picture_rasterizer.cc",
+    "skia_runner.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//gpu/command_buffer/common",
+    "//gpu/command_buffer/client:gles2_implementation",
+    "//gpu/command_buffer/client:gl_in_process_context",
+    "//gpu/skia_bindings",
+    "//skia",
+    "//third_party/WebKit/public:blink",
+    "//ui/gfx",
+    "//ui/gl",
+  ]
+
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [ "//build/config/compiler:no_chromium_code" ]
+}
diff --git a/gpu/skia_runner/DEPS b/gpu/skia_runner/DEPS
new file mode 100644
index 0000000..21e329d
--- /dev/null
+++ b/gpu/skia_runner/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  "+third_party/khronos",
+  "+third_party/skia",
+  "+third_party/WebKit/public/platform",
+  "+skia/ext",
+]
diff --git a/gpu/skia_runner/in_process_graphics_system.cc b/gpu/skia_runner/in_process_graphics_system.cc
new file mode 100644
index 0000000..e5294b1
--- /dev/null
+++ b/gpu/skia_runner/in_process_graphics_system.cc
@@ -0,0 +1,150 @@
+// 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 "gpu/skia_runner/in_process_graphics_system.h"
+
+#include <iostream>
+
+#include "base/lazy_instance.h"
+#include "base/memory/discardable_memory.h"
+#include "base/memory/discardable_memory_allocator.h"
+#include "base/thread_task_runner_handle.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/gles2_lib.h"
+#include "gpu/skia_bindings/gl_bindings_skia_cmd_buffer.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+
+namespace {
+
+// TODO(hendrikw): Replace TestDiscardableMemoryAllocator and move to base?
+class NonDiscardableMemory : public base::DiscardableMemory {
+ public:
+  explicit NonDiscardableMemory(size_t size) : data_(new uint8_t[size]) {}
+  bool Lock() override { return false; }
+  void Unlock() override {}
+  void* data() const override { return data_.get(); }
+
+ private:
+  scoped_ptr<uint8_t[]> data_;
+};
+
+class NonDiscardableMemoryAllocator : public base::DiscardableMemoryAllocator {
+ public:
+  // Overridden from DiscardableMemoryAllocator:
+  scoped_ptr<base::DiscardableMemory> AllocateLockedDiscardableMemory(
+      size_t size) override {
+    return make_scoped_ptr(new NonDiscardableMemory(size));
+  }
+};
+
+// Singleton used to initialize and terminate the gles2 library.
+class GLES2Initializer {
+ public:
+  GLES2Initializer() { gles2::Initialize(); }
+  ~GLES2Initializer() { gles2::Terminate(); }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GLES2Initializer);
+};
+
+base::LazyInstance<GLES2Initializer> g_gles2_initializer =
+    LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<NonDiscardableMemoryAllocator> g_discardable;
+
+}  // namespace anonymous
+
+namespace skia_runner {
+
+void BindContext3DGLContextCallback(const GrGLInterface* gl_interface) {
+  gles2::SetGLContext(reinterpret_cast<gpu::GLInProcessContext*>(
+                          gl_interface->fCallbackData)->GetImplementation());
+}
+
+InProcessGraphicsSystem::InProcessGraphicsSystem() {
+  base::DiscardableMemoryAllocator::SetInstance(&g_discardable.Get());
+  g_gles2_initializer.Get();
+
+  if (!gfx::GLSurface::InitializeOneOff()) {
+    LOG(ERROR) << "Unable to initialize gfx::GLSurface.";
+    return;
+  }
+
+  // Create the in process context.
+  in_process_context_ = CreateInProcessContext();
+  if (!in_process_context_) {
+    LOG(ERROR) << "Unable to create in process context.";
+    return;
+  }
+
+  // Create and set up skia's GL bindings.
+  gles2::SetGLContext(in_process_context_->GetImplementation());
+  GrGLInterface* bindings = skia_bindings::CreateCommandBufferSkiaGLBinding();
+  if (!bindings) {
+    LOG(ERROR) << "Unable to create skia command buffer bindings.";
+    return;
+  }
+
+  bindings->fCallback = BindContext3DGLContextCallback;
+  bindings->fCallbackData =
+      reinterpret_cast<GrGLInterfaceCallbackData>(in_process_context_.get());
+
+  // Create skia's graphics context.
+  gr_context_ = skia::AdoptRef(GrContext::Create(
+      kOpenGL_GrBackend, reinterpret_cast<GrBackendContext>(bindings)));
+
+  if (!gr_context_) {
+    LOG(ERROR) << "Unable to create skia graphics context.";
+    return;
+  }
+
+  // Set skia's graphics context cache size.
+  const int kMaxGaneshResourceCacheCount = 2048;
+  const size_t kMaxGaneshResourceCacheBytes = 96 * 1024 * 1024;
+  gr_context_->setResourceCacheLimits(kMaxGaneshResourceCacheCount,
+                                      kMaxGaneshResourceCacheBytes);
+}
+
+InProcessGraphicsSystem::~InProcessGraphicsSystem() {
+}
+
+bool InProcessGraphicsSystem::IsSuccessfullyInitialized() const {
+  return gr_context_;
+}
+
+int InProcessGraphicsSystem::GetMaxTextureSize() const {
+  int max_texture_size = 0;
+  in_process_context_->GetImplementation()->GetIntegerv(GL_MAX_TEXTURE_SIZE,
+                                                        &max_texture_size);
+  return max_texture_size;
+}
+
+scoped_ptr<gpu::GLInProcessContext>
+InProcessGraphicsSystem::CreateInProcessContext() const {
+  const bool is_offscreen = true;
+  const bool share_resources = true;
+  gpu::gles2::ContextCreationAttribHelper attribs;
+  attribs.alpha_size = 8;
+  attribs.blue_size = 8;
+  attribs.green_size = 8;
+  attribs.red_size = 8;
+  attribs.depth_size = 24;
+  attribs.stencil_size = 8;
+  attribs.samples = 0;
+  attribs.sample_buffers = 0;
+  attribs.fail_if_major_perf_caveat = false;
+  attribs.bind_generates_resource = false;
+  gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
+
+  scoped_ptr<gpu::GLInProcessContext> context =
+      make_scoped_ptr(gpu::GLInProcessContext::Create(
+          nullptr, nullptr, is_offscreen, gfx::kNullAcceleratedWidget,
+          gfx::Size(1, 1), nullptr, share_resources, attribs, gpu_preference,
+          gpu::GLInProcessContextSharedMemoryLimits(), nullptr, nullptr));
+
+  DCHECK(context);
+  return context.Pass();
+}
+
+}  // namespace skia_runner
diff --git a/gpu/skia_runner/in_process_graphics_system.h b/gpu/skia_runner/in_process_graphics_system.h
new file mode 100644
index 0000000..a2366cc
--- /dev/null
+++ b/gpu/skia_runner/in_process_graphics_system.h
@@ -0,0 +1,29 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/client/gl_in_process_context.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace skia_runner {
+
+class InProcessGraphicsSystem {
+ public:
+  InProcessGraphicsSystem();
+  ~InProcessGraphicsSystem();
+
+  bool IsSuccessfullyInitialized() const;
+  skia::RefPtr<GrContext> GetGrContext() const { return gr_context_; }
+  int GetMaxTextureSize() const;
+
+ private:
+  scoped_ptr<gpu::GLInProcessContext> CreateInProcessContext() const;
+
+  scoped_ptr<gpu::GLInProcessContext> in_process_context_;
+  skia::RefPtr<GrContext> gr_context_;
+};
+
+}  // namespace skia_runner
diff --git a/gpu/skia_runner/sk_picture_rasterizer.cc b/gpu/skia_runner/sk_picture_rasterizer.cc
new file mode 100644
index 0000000..dbd1f49
--- /dev/null
+++ b/gpu/skia_runner/sk_picture_rasterizer.cc
@@ -0,0 +1,91 @@
+// 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 "gpu/skia_runner/sk_picture_rasterizer.h"
+
+#include <iostream>
+
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/core/SkSurfaceProps.h"
+
+namespace skia_runner {
+
+SkPictureRasterizer::SkPictureRasterizer(skia::RefPtr<GrContext> gr_context,
+                                         int max_texture_size)
+    : use_lcd_text_(false),
+      use_distance_field_text_(false),
+      msaa_sample_count_(0),
+      max_texture_size_(max_texture_size),
+      gr_context_(gr_context) {
+}
+
+SkPictureRasterizer::~SkPictureRasterizer() {
+}
+
+skia::RefPtr<SkImage> SkPictureRasterizer::RasterizeTile(
+    const SkPicture* picture,
+    const SkRect& rect) const {
+  DCHECK(gr_context_);
+
+  SkImageInfo info = SkImageInfo::MakeN32Premul(rect.width(), rect.height());
+  uint32_t flags = 0;
+  if (use_distance_field_text_)
+    flags = SkSurfaceProps::kUseDistanceFieldFonts_Flag;
+
+  SkSurfaceProps surface_props =
+      use_lcd_text_
+          ? SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType)
+          : SkSurfaceProps(flags, kUnknown_SkPixelGeometry);
+
+  skia::RefPtr<SkSurface> sk_surface(skia::AdoptRef(
+      SkSurface::NewRenderTarget(gr_context_.get(), SkSurface::kYes_Budgeted,
+                                 info, msaa_sample_count_, &surface_props)));
+  if (sk_surface) {
+    SkCanvas* canvas = sk_surface->getCanvas();
+    canvas->translate(-rect.left(), -rect.top());
+    canvas->drawPicture(picture);
+
+    return skia::AdoptRef(sk_surface->newImageSnapshot());
+  }
+  return nullptr;
+}
+
+skia::RefPtr<SkImage> SkPictureRasterizer::Rasterize(
+    const SkPicture* picture) const {
+  if (!gr_context_)
+    return nullptr;
+
+  SkRect picture_rect = picture->cullRect();
+  if (picture_rect.width() <= max_texture_size_ &&
+      picture_rect.height() <= max_texture_size_)
+    return RasterizeTile(picture, picture_rect);
+
+  SkImageInfo info =
+      SkImageInfo::MakeN32Premul(picture_rect.width(), picture_rect.height());
+
+  skia::RefPtr<SkSurface> sk_surface(
+      skia::AdoptRef(SkSurface::NewRaster(info)));
+  SkCanvas* canvas = sk_surface->getCanvas();
+
+  int num_tiles_x = picture_rect.width() / max_texture_size_ + 1;
+  int num_tiles_y = picture_rect.height() / max_texture_size_ + 1;
+  for (int y = 0; y < num_tiles_y; ++y) {
+    SkRect tile_rect;
+    tile_rect.fTop = picture_rect.top() + y * max_texture_size_;
+    tile_rect.fBottom =
+        std::min(tile_rect.fTop + max_texture_size_, picture_rect.bottom());
+    for (int x = 0; x < num_tiles_x; ++x) {
+      tile_rect.fLeft = picture_rect.left() + x * max_texture_size_;
+      tile_rect.fRight =
+          std::min(tile_rect.fLeft + max_texture_size_, picture_rect.right());
+      skia::RefPtr<SkImage> tile(RasterizeTile(picture, tile_rect));
+      // canvas.drawBitmap(tile, tile_rect.left(), tile_rect.top());
+      canvas->drawImage(tile.get(), tile_rect.left(), tile_rect.top());
+    }
+  }
+  return skia::AdoptRef(sk_surface->newImageSnapshot());
+}
+
+}  // namepsace skia_runner
diff --git a/gpu/skia_runner/sk_picture_rasterizer.h b/gpu/skia_runner/sk_picture_rasterizer.h
new file mode 100644
index 0000000..380bfa0
--- /dev/null
+++ b/gpu/skia_runner/sk_picture_rasterizer.h
@@ -0,0 +1,40 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/client/gl_in_process_context.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace skia_runner {
+
+class SkPictureRasterizer {
+ public:
+  SkPictureRasterizer(skia::RefPtr<GrContext> gr_context, int max_texture_size);
+  ~SkPictureRasterizer();
+
+  skia::RefPtr<SkImage> Rasterize(const SkPicture* picture) const;
+
+  void set_msaa_sample_count(int msaa_sample_count) {
+    msaa_sample_count_ = msaa_sample_count;
+  }
+  void set_use_lcd_text(bool use_lcd_text) { use_lcd_text_ = use_lcd_text; }
+  void set_use_distance_field_text(bool use_distance_field_text) {
+    use_distance_field_text_ = use_distance_field_text;
+  }
+
+ private:
+  skia::RefPtr<SkImage> RasterizeTile(const SkPicture* picture,
+                                      const SkRect& rect) const;
+
+  bool use_lcd_text_;
+  bool use_distance_field_text_;
+  int msaa_sample_count_;
+
+  int max_texture_size_;
+  skia::RefPtr<GrContext> gr_context_;
+};
+
+}  // namespace skia_runner
diff --git a/gpu/skia_runner/skia_runner.cc b/gpu/skia_runner/skia_runner.cc
new file mode 100644
index 0000000..83262f7
--- /dev/null
+++ b/gpu/skia_runner/skia_runner.cc
@@ -0,0 +1,228 @@
+// 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 <iostream>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "gpu/skia_runner/in_process_graphics_system.h"
+#include "gpu/skia_runner/sk_picture_rasterizer.h"
+#include "third_party/WebKit/public/platform/WebData.h"
+#include "third_party/WebKit/public/platform/WebImage.h"
+#include "third_party/WebKit/public/platform/WebSize.h"
+#include "third_party/skia/include/core/SkOSFile.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "ui/gfx/codec/png_codec.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#endif
+
+namespace {
+
+bool WriteSkImagePNG(const SkImage* image, const base::FilePath& path) {
+  DCHECK(!path.empty());
+
+  if (!image) {
+    std::cout << "Unable to write empty bitmap for " << path.value() << ".\n";
+    return false;
+  }
+
+  std::string file_path = path.MaybeAsASCII();
+  SkFILEWStream stream(file_path.c_str());
+  if (!stream.isValid()) {
+    std::cout << "Unable to write to " << file_path.c_str() << ".\n";
+    return false;
+  }
+
+  SkImageInfo info = SkImageInfo::Make(image->width(), image->height(),
+                                       SkColorType::kBGRA_8888_SkColorType,
+                                       SkAlphaType::kPremul_SkAlphaType);
+  SkImageInfo::MakeN32Premul(image->width(), image->height());
+
+  const size_t rowBytes = image->width() * sizeof(SkPMColor);
+  std::vector<SkPMColor> pixels(image->width() * image->height());
+
+  if (image->readPixels(info, pixels.data(), rowBytes, 0, 0)) {
+    std::vector<unsigned char> png_data;
+
+    if (gfx::PNGCodec::Encode(
+            reinterpret_cast<const unsigned char*>(pixels.data()),
+            gfx::PNGCodec::FORMAT_BGRA,
+            gfx::Size(image->width(), image->height()),
+            static_cast<int>(rowBytes), false,
+            std::vector<gfx::PNGCodec::Comment>(), &png_data)) {
+      if (stream.write(png_data.data(), png_data.size())) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+bool onDecode(const void* buffer, size_t size, SkBitmap* bm) {
+  blink::WebData web_data(static_cast<const char*>(buffer), size);
+  blink::WebImage image = blink::WebImage::fromData(web_data, blink::WebSize());
+  if (!image.isNull()) {
+    *bm = image.getSkBitmap();
+    return true;
+  }
+  std::cout << "Error decoding image.\n";
+  return false;
+}
+
+skia::RefPtr<SkPicture> ReadPicture(const base::FilePath& path) {
+  skia::RefPtr<SkPicture> picture;
+  std::string file_path = path.MaybeAsASCII();
+  SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(file_path.c_str()));
+  if (!stream.get()) {
+    std::cout << "Unable to read " << file_path.c_str() << ".\n";
+    return picture;
+  }
+
+  picture = skia::AdoptRef(SkPicture::CreateFromStream(stream.get(), onDecode));
+  if (!picture) {
+    std::cout << "Unable to load " << file_path.c_str()
+              << " as an SkPicture.\n";
+  }
+  return picture;
+}
+
+base::FilePath MakeDestinationFilename(
+    base::FilePath source_file,
+    base::FilePath destination_folder,
+    base::FilePath::StringType new_extension) {
+  base::FilePath filename = source_file.BaseName().RemoveExtension();
+  filename = filename.AddExtension(new_extension);
+  return destination_folder.AsEndingWithSeparator().Append(filename);
+}
+
+std::vector<std::pair<base::FilePath, base::FilePath>> GetSkpsToRasterize(
+    base::FilePath input_path,
+    base::FilePath output_path) {
+  std::vector<std::pair<base::FilePath, base::FilePath>> files;
+
+  if (base::DirectoryExists(input_path)) {
+    if (!base::DirectoryExists(output_path)) {
+      return files;
+    }
+    base::FilePath::StringType extension = FILE_PATH_LITERAL(".skp");
+    base::FileEnumerator file_iter(input_path, false,
+                                   base::FileEnumerator::FILES);
+    while (!file_iter.Next().empty()) {
+      if (file_iter.GetInfo().GetName().MatchesExtension(extension)) {
+        base::FilePath skp_file = file_iter.GetInfo().GetName();
+        skp_file = input_path.AsEndingWithSeparator().Append(skp_file);
+        base::FilePath png_file = MakeDestinationFilename(
+            skp_file, output_path, FILE_PATH_LITERAL("png"));
+        files.push_back(std::make_pair(skp_file, png_file));
+      }
+    }
+  } else {
+    // Single file passed. If the output file is a folder, make a name.
+    if (base::DirectoryExists(output_path)) {
+      output_path = MakeDestinationFilename(input_path, output_path,
+                                            FILE_PATH_LITERAL("png"));
+    }
+    files.push_back(std::make_pair(input_path, output_path));
+  }
+  return files;
+}
+
+static const char kHelpMessage[] =
+    "This program renders a skia SKP to a PNG using GPU rasterization via the\n"
+    "command buffer.\n\n"
+    "following command line flags to control its behavior:\n"
+    "\n"
+    "  --in-skp=skp[:DIRECTORY_PATH|FILE_PATH]\n"
+    "      Input SKP file. If a directory is provided, all SKP files will be\n"
+    "      converted.\n"
+    "  --out-png=png[:DIRECTORY_PATH|:FILE_PATH]\n"
+    "      Output PNG file. If a directory is provided, the SKP filename is "
+    "used.\n\n"
+    "  --use-lcd-text\n"
+    "      Turn on lcd text rendering.\n"
+    "  --use-distance-field-text\n"
+    "      Turn on distance field text rendering.\n"
+    "  --msaa-sample-count=(0|2|4|8|16)\n"
+    "      Turn on multi-sample anti-aliasing.\n"
+    "  --use-gl=(desktop|osmesa|egl|swiftshader)\n"
+    "      Specify Gl driver. --swiftshader-path required for swiftshader.\n";
+
+}  // namespace anonymous
+
+int main(int argc, char** argv) {
+  base::AtExitManager exit_manager;
+  base::CommandLine::Init(argc, argv);
+#if defined(OS_MACOSX)
+  base::mac::ScopedNSAutoreleasePool autorelease_pool;
+#endif
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  base::FilePath input_path(command_line->GetSwitchValuePath("in-skp"));
+  base::FilePath output_path(command_line->GetSwitchValuePath("out-png"));
+
+  if (input_path.empty() || output_path.empty()) {
+    std::cout << kHelpMessage;
+    return 0;
+  }
+
+  std::vector<std::pair<base::FilePath, base::FilePath>> files =
+      GetSkpsToRasterize(input_path, output_path);
+
+  if (files.empty()) {
+    if (!base::DirectoryExists(output_path))
+      std::cout << "You must specify an existing directory using '--out-png'\n";
+    else
+      std::cout << "No skp files found at " << input_path.value() << "\n";
+    return 0;
+  }
+
+  skia_runner::InProcessGraphicsSystem graphics_system;
+  if (!graphics_system.IsSuccessfullyInitialized()) {
+    LOG(ERROR) << "Unable to initialize rasterizer.";
+    return 0;
+  }
+
+  skia_runner::SkPictureRasterizer picture_rasterizer(
+      graphics_system.GetGrContext(), graphics_system.GetMaxTextureSize());
+
+  // Set up command-line render options.
+  picture_rasterizer.set_use_lcd_text(command_line->HasSwitch("use-lcd-text"));
+  picture_rasterizer.set_use_distance_field_text(
+      command_line->HasSwitch("use-distance-field-text"));
+
+  if (command_line->HasSwitch("msaa-sample-count")) {
+    std::string value = command_line->GetSwitchValueASCII("msaa-sample-count");
+    int msaa = 0;
+    if (base::StringToInt(value, &msaa))
+      picture_rasterizer.set_msaa_sample_count(msaa);
+
+    if (msaa != 0 && msaa != 2 && msaa != 4 && msaa != 8 && msaa != 16) {
+      std::cout << "Error: msaa sample count must be 0, 2, 4, 8 or 16.\n";
+      return 0;
+    }
+  }
+
+  // Disable the security precautions to ensure we correctly read the picture.
+  SkPicture::SetPictureIOSecurityPrecautionsEnabled_Dangerous(false);
+
+  for (auto file_pair : files) {
+    skia::RefPtr<SkPicture> picture = ReadPicture(file_pair.first);
+    if (!picture) {
+      std::cout << "Error reading: " << file_pair.first.value() << "\n";
+      continue;
+    }
+
+    skia::RefPtr<SkImage> image(picture_rasterizer.Rasterize(picture.get()));
+    if (!WriteSkImagePNG(image.get(), file_pair.second))
+      std::cout << "Error writing: " << file_pair.second.value() << "\n";
+    else
+      std::cout << file_pair.second.value() << " successfully created.\n";
+  }
+}
diff --git a/gpu/skia_runner/skia_runner.gyp b/gpu/skia_runner/skia_runner.gyp
new file mode 100644
index 0000000..1a07caa
--- /dev/null
+++ b/gpu/skia_runner/skia_runner.gyp
@@ -0,0 +1,36 @@
+# 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.
+
+{
+  'variables': {
+    'chromium_code': 1,
+  },
+  'targets': [
+    {
+      # GN version: //gpu:skia_runner
+      'target_name': 'skia_runner',
+      'type': 'executable',
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../../gpu/command_buffer/command_buffer.gyp:gles2_utils',
+        '../../gpu/gpu.gyp:command_buffer_service',
+        '../../gpu/gpu.gyp:gles2_implementation',
+        '../../gpu/gpu.gyp:gl_in_process_context',
+        '../../gpu/skia_bindings/skia_bindings.gyp:gpu_skia_bindings',
+        '../../skia/skia.gyp:skia',
+        '../../third_party/WebKit/public/blink.gyp:blink',
+        '../../ui/gfx/gfx.gyp:gfx',
+        '../../ui/gl/gl.gyp:gl',
+      ],
+      'sources': [
+        # Note: sources list duplicated in GN build.
+        'in_process_graphics_system.cc',
+        'in_process_graphics_system.h',
+        'sk_picture_rasterizer.cc',
+        'sk_picture_rasterizer.h',
+        'skia_runner.cc',
+      ],
+    },
+  ],
+}
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS
index 0aa720b4..47c7f58 100644
--- a/ios/chrome/browser/DEPS
+++ b/ios/chrome/browser/DEPS
@@ -10,6 +10,8 @@
   "+components/dom_distiller/core",
   "+components/dom_distiller/ios",
   "+components/enhanced_bookmarks",
+  "+components/favicon/core",
+  "+components/favicon_base",
   "+components/infobars/core",
   "+components/keyed_service/core",
   "+components/keyed_service/ios",
@@ -17,9 +19,9 @@
   "+components/password_manager/core/browser",
   "+components/pref_registry",
   "+components/search_engines",
-  "+components/suggestions",
   "+components/signin/core/browser",
   "+components/signin/ios/browser",
+  "+components/suggestions",
   "+components/sync_driver",
   "+components/translate/core",
   "+components/translate/ios",
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
index 41dca16..9e80a9c3 100644
--- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
+++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -5,9 +5,12 @@
 #include "ios/chrome/browser/browser_state/browser_state_keyed_service_factories.h"
 
 #include "ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h"
+#include "ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_factory.h"
 #include "ios/chrome/browser/enhanced_bookmarks/bookmark_server_cluster_service_factory.h"
 #include "ios/chrome/browser/enhanced_bookmarks/enhanced_bookmark_model_factory.h"
+#include "ios/chrome/browser/favicon/favicon_service_factory.h"
 #include "ios/chrome/browser/suggestions/suggestions_service_factory.h"
+#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #include "ios/chrome/browser/translate/translate_accept_languages_factory.h"
 #include "ios/public/provider/chrome/browser/keyed_service_provider.h"
 
@@ -21,10 +24,13 @@
 // TODO(erg): This needs to be something else. I don't think putting every
 // FooServiceFactory here will scale or is desirable long term.
 void EnsureBrowserStateKeyedServiceFactoriesBuilt() {
+  BookmarkImageServiceFactory::GetInstance();
   dom_distiller::DomDistillerServiceFactory::GetInstance();
   enhanced_bookmarks::BookmarkServerClusterServiceFactory::GetInstance();
   enhanced_bookmarks::EnhancedBookmarkModelFactory::GetInstance();
+  ios::FaviconServiceFactory::GetInstance();
   suggestions::SuggestionsServiceFactory::GetInstance();
+  SyncSetupServiceFactory::GetInstance();
   TranslateAcceptLanguagesFactory::GetInstance();
 
   if (ios::GetKeyedServiceProvider())
diff --git a/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc b/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc
index 0adbfa7..33dda3a 100644
--- a/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc
+++ b/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc
@@ -5,7 +5,6 @@
 #include "ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h"
 
 #include "base/files/file_path.h"
-#include "base/memory/scoped_ptr.h"
 #include "base/memory/singleton.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "components/dom_distiller/core/article_entry.h"
@@ -55,7 +54,7 @@
 
 // static
 DomDistillerService* DomDistillerServiceFactory::GetForBrowserState(
-    web::BrowserState* browser_state) {
+    ios::ChromeBrowserState* browser_state) {
   return static_cast<DomDistillerKeyedService*>(
       GetInstance()->GetServiceForBrowserState(browser_state, true));
 }
@@ -69,8 +68,8 @@
 DomDistillerServiceFactory::~DomDistillerServiceFactory() {
 }
 
-KeyedService* DomDistillerServiceFactory::BuildServiceInstanceFor(
-    web::BrowserState* browser_state) const {
+scoped_ptr<KeyedService> DomDistillerServiceFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
   scoped_refptr<base::SequencedTaskRunner> background_task_runner =
       web::WebThread::GetBlockingPool()->GetSequencedTaskRunner(
           web::WebThread::GetBlockingPool()->GetSequenceToken());
@@ -80,34 +79,31 @@
           background_task_runner));
 
   base::FilePath database_dir(
-      browser_state->GetStatePath().Append(FILE_PATH_LITERAL("Articles")));
+      context->GetStatePath().Append(FILE_PATH_LITERAL("Articles")));
 
   scoped_ptr<DomDistillerStore> dom_distiller_store(
       new DomDistillerStore(db.Pass(), database_dir));
 
   scoped_ptr<DistillerPageFactory> distiller_page_factory(
-      new DistillerPageFactoryIOS(browser_state));
+      new DistillerPageFactoryIOS(context));
   scoped_ptr<DistillerURLFetcherFactory> distiller_url_fetcher_factory(
-      new DistillerURLFetcherFactory(browser_state->GetRequestContext()));
+      new DistillerURLFetcherFactory(context->GetRequestContext()));
 
   dom_distiller::proto::DomDistillerOptions options;
   scoped_ptr<DistillerFactory> distiller_factory(
       new DistillerFactoryImpl(distiller_url_fetcher_factory.Pass(), options));
   scoped_ptr<DistilledPagePrefs> distilled_page_prefs(new DistilledPagePrefs(
-      ios::ChromeBrowserState::FromBrowserState(browser_state)->GetPrefs()));
+      ios::ChromeBrowserState::FromBrowserState(context)->GetPrefs()));
 
-  DomDistillerKeyedService* service =
-      new DomDistillerKeyedService(
-          dom_distiller_store.Pass(), distiller_factory.Pass(),
-          distiller_page_factory.Pass(), distilled_page_prefs.Pass());
-
-  return service;
+  return make_scoped_ptr(new DomDistillerKeyedService(
+      dom_distiller_store.Pass(), distiller_factory.Pass(),
+      distiller_page_factory.Pass(), distilled_page_prefs.Pass()));
 }
 
 web::BrowserState* DomDistillerServiceFactory::GetBrowserStateToUse(
-    web::BrowserState* browser_state) const {
+    web::BrowserState* context) const {
   // Makes normal profile and off-the-record profile use same service instance.
-  return GetBrowserStateRedirectedInIncognito(browser_state);
+  return GetBrowserStateRedirectedInIncognito(context);
 }
 
 }  // namespace dom_distiller
diff --git a/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h b/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h
index 802e0fe..5464abb 100644
--- a/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h
+++ b/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h
@@ -5,19 +5,19 @@
 #ifndef IOS_CHROME_BROWSER_DOM_DISTILLER_DOM_DISTILLER_SERVICE_FACTORY_H_
 #define IOS_CHROME_BROWSER_DOM_DISTILLER_DOM_DISTILLER_SERVICE_FACTORY_H_
 
-#include "components/dom_distiller/core/distilled_page_prefs.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-template <typename T> struct DefaultSingletonTraits;
-
-class KeyedService;
+template <typename T>
+struct DefaultSingletonTraits;
 
 namespace dom_distiller {
 class DomDistillerService;
 }
 
-namespace web {
-class BrowserState;
+namespace ios {
+class ChromeBrowserState;
 }
 
 namespace dom_distiller {
@@ -26,7 +26,7 @@
  public:
   static DomDistillerServiceFactory* GetInstance();
   static DomDistillerService* GetForBrowserState(
-      web::BrowserState* browser_state);
+      ios::ChromeBrowserState* browser_state);
 
  private:
   friend struct DefaultSingletonTraits<DomDistillerServiceFactory>;
@@ -35,10 +35,12 @@
   ~DomDistillerServiceFactory() override;
 
   // BrowserStateKeyedServiceFactory implementation.
-  KeyedService* BuildServiceInstanceFor(
-      web::BrowserState* browser_state) const override;
+  scoped_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
   web::BrowserState* GetBrowserStateToUse(
-      web::BrowserState* browser_state) const override;
+      web::BrowserState* context) const override;
+
+  DISALLOW_COPY(DomDistillerServiceFactory);
 };
 
 }  // namespace dom_distiller
diff --git a/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_factory.h b/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_factory.h
index 28cbf14..edc0e64 100644
--- a/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_factory.h
+++ b/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_factory.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_ENHANCED_BOOKMARKS_BOOKMARK_IMAGE_SERVICE_FACTORY_H_
 
 #include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
 template <typename T>
@@ -15,7 +16,6 @@
 class ChromeBrowserState;
 }
 
-class KeyedService;
 class BookmarkImageServiceIOS;
 
 // Singleton that owns all BookmarkImageServices and associates them with
@@ -33,9 +33,9 @@
   ~BookmarkImageServiceFactory() override;
 
   // BrowserStateKeyedServiceFactory implementation.
-  web::BrowserState* GetBrowserStateToUse(
+  scoped_ptr<KeyedService> BuildServiceInstanceFor(
       web::BrowserState* context) const override;
-  KeyedService* BuildServiceInstanceFor(
+  web::BrowserState* GetBrowserStateToUse(
       web::BrowserState* context) const override;
 
   DISALLOW_COPY_AND_ASSIGN(BookmarkImageServiceFactory);
diff --git a/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_factory.mm b/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_factory.mm
index 82a042d0..dcfa518 100644
--- a/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_factory.mm
+++ b/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_factory.mm
@@ -34,19 +34,19 @@
 BookmarkImageServiceFactory::~BookmarkImageServiceFactory() {
 }
 
-web::BrowserState* BookmarkImageServiceFactory::GetBrowserStateToUse(
-    web::BrowserState* context) const {
-  return context;
-}
-
-KeyedService* BookmarkImageServiceFactory::BuildServiceInstanceFor(
+scoped_ptr<KeyedService> BookmarkImageServiceFactory::BuildServiceInstanceFor(
     web::BrowserState* context) const {
   DCHECK(!context->IsOffTheRecord());
   ios::ChromeBrowserState* browser_state =
       ios::ChromeBrowserState::FromBrowserState(context);
-  return new BookmarkImageServiceIOS(
+  return make_scoped_ptr(new BookmarkImageServiceIOS(
       browser_state->GetStatePath(),
       enhanced_bookmarks::EnhancedBookmarkModelFactory::GetForBrowserState(
           browser_state),
-      browser_state->GetRequestContext(), web::WebThread::GetBlockingPool());
+      browser_state->GetRequestContext(), web::WebThread::GetBlockingPool()));
+}
+
+web::BrowserState* BookmarkImageServiceFactory::GetBrowserStateToUse(
+    web::BrowserState* context) const {
+  return context;
 }
diff --git a/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_ios_unittest.mm b/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_ios_unittest.mm
index 01e3a69..13855e2 100644
--- a/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_ios_unittest.mm
+++ b/ios/chrome/browser/enhanced_bookmarks/bookmark_image_service_ios_unittest.mm
@@ -45,8 +45,9 @@
       : message_loop_(base::MessageLoop::TYPE_IO),
         ui_thread_(web::WebThread::UI, &message_loop_),
         io_thread_(web::WebThread::IO, &message_loop_),
-        request_context_getter_(new net::TestURLRequestContextGetter(
-            message_loop_.message_loop_proxy())) {}
+        request_context_getter_(
+            new net::TestURLRequestContextGetter(message_loop_.task_runner())) {
+  }
 
   ~BookmarkImagesIOSTest() override {}
 
diff --git a/ios/chrome/browser/enhanced_bookmarks/bookmark_server_cluster_service_factory.cc b/ios/chrome/browser/enhanced_bookmarks/bookmark_server_cluster_service_factory.cc
index e1cc57ed..5a24e25c 100644
--- a/ios/chrome/browser/enhanced_bookmarks/bookmark_server_cluster_service_factory.cc
+++ b/ios/chrome/browser/enhanced_bookmarks/bookmark_server_cluster_service_factory.cc
@@ -47,20 +47,21 @@
 BookmarkServerClusterServiceFactory::~BookmarkServerClusterServiceFactory() {
 }
 
-KeyedService* BookmarkServerClusterServiceFactory::BuildServiceInstanceFor(
+scoped_ptr<KeyedService>
+BookmarkServerClusterServiceFactory::BuildServiceInstanceFor(
     web::BrowserState* context) const {
   DCHECK(!context->IsOffTheRecord());
   ios::ChromeBrowserState* browser_state =
       ios::ChromeBrowserState::FromBrowserState(context);
   ios::KeyedServiceProvider* provider = ios::GetKeyedServiceProvider();
-  return new BookmarkServerClusterService(
+  return make_scoped_ptr(new BookmarkServerClusterService(
       GetApplicationContext()->GetApplicationLocale(),
       browser_state->GetRequestContext(),
       provider->GetProfileOAuth2TokenServiceIOSForBrowserState(browser_state),
       provider->GetSigninManagerForBrowserState(browser_state),
       EnhancedBookmarkModelFactory::GetForBrowserState(browser_state),
       provider->GetSyncServiceForBrowserState(browser_state),
-      browser_state->GetPrefs());
+      browser_state->GetPrefs()));
 }
 
 web::BrowserState* BookmarkServerClusterServiceFactory::GetBrowserStateToUse(
diff --git a/ios/chrome/browser/enhanced_bookmarks/bookmark_server_cluster_service_factory.h b/ios/chrome/browser/enhanced_bookmarks/bookmark_server_cluster_service_factory.h
index 5ff0873..76d6f69 100644
--- a/ios/chrome/browser/enhanced_bookmarks/bookmark_server_cluster_service_factory.h
+++ b/ios/chrome/browser/enhanced_bookmarks/bookmark_server_cluster_service_factory.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_ENHANCED_BOOKMARKS_BOOKMARK_SERVER_CLUSTER_SERVICE_FACTORY_H_
 
 #include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
 template <typename T>
@@ -35,7 +36,7 @@
   ~BookmarkServerClusterServiceFactory() override;
 
   // BrowserStateKeyedServiceFactory implementation.
-  KeyedService* BuildServiceInstanceFor(
+  scoped_ptr<KeyedService> BuildServiceInstanceFor(
       web::BrowserState* context) const override;
   web::BrowserState* GetBrowserStateToUse(
       web::BrowserState* context) const override;
diff --git a/ios/chrome/browser/enhanced_bookmarks/enhanced_bookmark_model_factory.cc b/ios/chrome/browser/enhanced_bookmarks/enhanced_bookmark_model_factory.cc
index 97036113..2dabee1 100644
--- a/ios/chrome/browser/enhanced_bookmarks/enhanced_bookmark_model_factory.cc
+++ b/ios/chrome/browser/enhanced_bookmarks/enhanced_bookmark_model_factory.cc
@@ -41,16 +41,16 @@
 EnhancedBookmarkModelFactory::~EnhancedBookmarkModelFactory() {
 }
 
-KeyedService* EnhancedBookmarkModelFactory::BuildServiceInstanceFor(
+scoped_ptr<KeyedService> EnhancedBookmarkModelFactory::BuildServiceInstanceFor(
     web::BrowserState* context) const {
   DCHECK(!context->IsOffTheRecord());
   ios::ChromeBrowserState* browser_state =
       ios::ChromeBrowserState::FromBrowserState(context);
   ios::KeyedServiceProvider* provider = ios::GetKeyedServiceProvider();
-  return new EnhancedBookmarkModel(
+  return make_scoped_ptr(new EnhancedBookmarkModel(
       provider->GetBookmarkModelForBrowserState(browser_state),
       ios::GetChromeBrowserProvider()->GetProductVersionWithPrefix(
-          kVersionPrefix));
+          kVersionPrefix)));
 }
 
 web::BrowserState* EnhancedBookmarkModelFactory::GetBrowserStateToUse(
diff --git a/ios/chrome/browser/enhanced_bookmarks/enhanced_bookmark_model_factory.h b/ios/chrome/browser/enhanced_bookmarks/enhanced_bookmark_model_factory.h
index 9c47b97..9f40a94 100644
--- a/ios/chrome/browser/enhanced_bookmarks/enhanced_bookmark_model_factory.h
+++ b/ios/chrome/browser/enhanced_bookmarks/enhanced_bookmark_model_factory.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_ENHANCED_BOOKMARKS_ENHANCED_BOOKMARK_MODEL_FACTORY_H_
 
 #include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
 template <typename T>
@@ -34,7 +35,7 @@
   ~EnhancedBookmarkModelFactory() override;
 
   // BrowserStateKeyedServiceFactory implementation.
-  KeyedService* BuildServiceInstanceFor(
+  scoped_ptr<KeyedService> BuildServiceInstanceFor(
       web::BrowserState* context) const override;
   web::BrowserState* GetBrowserStateToUse(
       web::BrowserState* context) const override;
diff --git a/ios/chrome/browser/favicon/favicon_client_impl.cc b/ios/chrome/browser/favicon/favicon_client_impl.cc
new file mode 100644
index 0000000..06beba0
--- /dev/null
+++ b/ios/chrome/browser/favicon/favicon_client_impl.cc
@@ -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.
+
+#include "ios/chrome/browser/favicon/favicon_client_impl.h"
+
+#include "base/logging.h"
+
+FaviconClientImpl::FaviconClientImpl() {
+}
+
+FaviconClientImpl::~FaviconClientImpl() {
+}
+
+bool FaviconClientImpl::IsNativeApplicationURL(const GURL& url) {
+  return false;
+}
+
+base::CancelableTaskTracker::TaskId
+FaviconClientImpl::GetFaviconForNativeApplicationURL(
+    const GURL& url,
+    const std::vector<int>& desired_sizes_in_pixel,
+    const favicon_base::FaviconResultsCallback& callback,
+    base::CancelableTaskTracker* tracker) {
+  NOTREACHED();
+  return base::CancelableTaskTracker::kBadTaskId;
+}
diff --git a/ios/chrome/browser/favicon/favicon_client_impl.h b/ios/chrome/browser/favicon/favicon_client_impl.h
new file mode 100644
index 0000000..0680303
--- /dev/null
+++ b/ios/chrome/browser/favicon/favicon_client_impl.h
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_FAVICON_FAVICON_CLIENT_IMPL_H_
+#define IOS_CHROME_BROWSER_FAVICON_FAVICON_CLIENT_IMPL_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "components/favicon/core/favicon_client.h"
+#include "components/favicon_base/favicon_callback.h"
+
+class GURL;
+
+// FaviconClientImpl implements the favicon::FaviconClient interface on iOS.
+class FaviconClientImpl : public favicon::FaviconClient {
+ public:
+  FaviconClientImpl();
+  ~FaviconClientImpl() override;
+
+ private:
+  // favicon::FaviconClient implementation.
+  bool IsNativeApplicationURL(const GURL& url) override;
+  base::CancelableTaskTracker::TaskId GetFaviconForNativeApplicationURL(
+      const GURL& url,
+      const std::vector<int>& desired_sizes_in_pixel,
+      const favicon_base::FaviconResultsCallback& callback,
+      base::CancelableTaskTracker* tracker) override;
+
+  DISALLOW_COPY_AND_ASSIGN(FaviconClientImpl);
+};
+
+#endif  // IOS_CHROME_BROWSER_FAVICON_FAVICON_CLIENT_IMPL_H_
diff --git a/ios/chrome/browser/favicon/favicon_service_factory.cc b/ios/chrome/browser/favicon/favicon_service_factory.cc
new file mode 100644
index 0000000..014aa15b
--- /dev/null
+++ b/ios/chrome/browser/favicon/favicon_service_factory.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 "ios/chrome/browser/favicon/favicon_service_factory.h"
+
+#include "base/memory/singleton.h"
+#include "components/favicon/core/favicon_service.h"
+#include "components/keyed_service/core/service_access_type.h"
+#include "components/keyed_service/ios/browser_state_dependency_manager.h"
+#include "ios/chrome/browser/favicon/favicon_client_impl.h"
+#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#include "ios/public/provider/chrome/browser/keyed_service_provider.h"
+
+namespace ios {
+
+// static
+favicon::FaviconService* FaviconServiceFactory::GetForBrowserState(
+    ios::ChromeBrowserState* browser_state,
+    ServiceAccessType access_type) {
+  if (!browser_state->IsOffTheRecord()) {
+    return static_cast<favicon::FaviconService*>(
+        GetInstance()->GetServiceForBrowserState(browser_state, true));
+  } else if (access_type == ServiceAccessType::EXPLICIT_ACCESS) {
+    return static_cast<favicon::FaviconService*>(
+        GetInstance()->GetServiceForBrowserState(
+            browser_state->GetOriginalChromeBrowserState(), true));
+  }
+
+  // ios::ChromeBrowserState is OffTheRecord without access.
+  NOTREACHED() << "ChromeBrowserState is OffTheRecord";
+  return nullptr;
+}
+
+FaviconServiceFactory* FaviconServiceFactory::GetInstance() {
+  return Singleton<FaviconServiceFactory>::get();
+}
+
+FaviconServiceFactory::FaviconServiceFactory()
+    : BrowserStateKeyedServiceFactory(
+          "FaviconService",
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(ios::GetKeyedServiceProvider()->GetHistoryServiceFactory());
+}
+
+FaviconServiceFactory::~FaviconServiceFactory() {
+}
+
+scoped_ptr<KeyedService> FaviconServiceFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  ios::ChromeBrowserState* browser_state =
+      ios::ChromeBrowserState::FromBrowserState(context);
+  return make_scoped_ptr(new favicon::FaviconService(
+      make_scoped_ptr(new FaviconClientImpl),
+      ios::GetKeyedServiceProvider()->GetHistoryServiceForBrowserState(
+          browser_state, ServiceAccessType::EXPLICIT_ACCESS)));
+}
+
+bool FaviconServiceFactory::ServiceIsNULLWhileTesting() const {
+  return true;
+}
+
+}  // namespace ios
diff --git a/ios/chrome/browser/favicon/favicon_service_factory.h b/ios/chrome/browser/favicon/favicon_service_factory.h
new file mode 100644
index 0000000..16139e7
--- /dev/null
+++ b/ios/chrome/browser/favicon/favicon_service_factory.h
@@ -0,0 +1,49 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_FAVICON_FAVICON_SERVICE_FACTORY_H_
+#define IOS_CHROME_BROWSER_FAVICON_FAVICON_SERVICE_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+
+template <typename T>
+struct DefaultSingletonTraits;
+enum class ServiceAccessType;
+
+namespace favicon {
+class FaviconService;
+}
+
+namespace ios {
+
+class ChromeBrowserState;
+
+// Singleton that owns all FaviconService and associates them with
+// ios::ChromeBrowserState.
+class FaviconServiceFactory : public BrowserStateKeyedServiceFactory {
+ public:
+  static favicon::FaviconService* GetForBrowserState(
+      ios::ChromeBrowserState* browser_state,
+      ServiceAccessType access_type);
+  static FaviconServiceFactory* GetInstance();
+
+ private:
+  friend struct DefaultSingletonTraits<FaviconServiceFactory>;
+
+  FaviconServiceFactory();
+  ~FaviconServiceFactory() override;
+
+  // BrowserStateKeyedServiceFactory implementation.
+  scoped_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
+  bool ServiceIsNULLWhileTesting() const override;
+
+  DISALLOW_COPY_AND_ASSIGN(FaviconServiceFactory);
+};
+
+}  // namespace ios
+
+#endif  // IOS_CHROME_BROWSER_FAVICON_FAVICON_SERVICE_FACTORY_H_
diff --git a/ios/chrome/browser/suggestions/suggestions_service_factory.h b/ios/chrome/browser/suggestions/suggestions_service_factory.h
index 384982e8..417fc91 100644
--- a/ios/chrome/browser/suggestions/suggestions_service_factory.h
+++ b/ios/chrome/browser/suggestions/suggestions_service_factory.h
@@ -6,13 +6,14 @@
 #define IOS_CHROME_BROWSER_SUGGESTIONS_SUGGESTIONS_SERVICE_FACTORY_H_
 
 #include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
 template <typename T>
 struct DefaultSingletonTraits;
 
-namespace web {
-class BrowserState;
+namespace ios {
+class ChromeBrowserState;
 }
 
 namespace suggestions {
@@ -27,7 +28,7 @@
 
   // Returns the SuggestionsService for |browser_state|.
   static SuggestionsService* GetForBrowserState(
-      web::BrowserState* browser_state);
+      ios::ChromeBrowserState* browser_state);
 
  private:
   friend struct DefaultSingletonTraits<SuggestionsServiceFactory>;
@@ -35,9 +36,9 @@
   SuggestionsServiceFactory();
   ~SuggestionsServiceFactory() override;
 
-  // BrowserStateKeyedServiceFactory:
-  KeyedService* BuildServiceInstanceFor(
-      web::BrowserState* browser_state) const override;
+  // BrowserStateKeyedServiceFactory implementation.
+  scoped_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
   void RegisterBrowserStatePrefs(
       user_prefs::PrefRegistrySyncable* registry) override;
 
diff --git a/ios/chrome/browser/suggestions/suggestions_service_factory.mm b/ios/chrome/browser/suggestions/suggestions_service_factory.mm
index f185973..6cbd577 100644
--- a/ios/chrome/browser/suggestions/suggestions_service_factory.mm
+++ b/ios/chrome/browser/suggestions/suggestions_service_factory.mm
@@ -33,7 +33,7 @@
 
 // static
 SuggestionsService* SuggestionsServiceFactory::GetForBrowserState(
-    web::BrowserState* browser_state) {
+    ios::ChromeBrowserState* browser_state) {
   return static_cast<SuggestionsService*>(
       GetInstance()->GetServiceForBrowserState(browser_state, true));
 }
@@ -48,22 +48,22 @@
 SuggestionsServiceFactory::~SuggestionsServiceFactory() {
 }
 
-KeyedService* SuggestionsServiceFactory::BuildServiceInstanceFor(
-    web::BrowserState* browser_state) const {
+scoped_ptr<KeyedService> SuggestionsServiceFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
   base::SequencedWorkerPool* sequenced_worker_pool =
       web::WebThread::GetBlockingPool();
   scoped_refptr<base::SequencedTaskRunner> background_task_runner =
       sequenced_worker_pool->GetSequencedTaskRunner(
           sequenced_worker_pool->GetSequenceToken());
 
-  ios::ChromeBrowserState* chrome_browser_state =
-      ios::ChromeBrowserState::FromBrowserState(browser_state);
+  ios::ChromeBrowserState* browser_state =
+      ios::ChromeBrowserState::FromBrowserState(context);
   base::FilePath database_dir(
-      chrome_browser_state->GetStatePath().Append(kThumbnailDirectory));
+      browser_state->GetStatePath().Append(kThumbnailDirectory));
   scoped_ptr<SuggestionsStore> suggestions_store(
-      new SuggestionsStore(chrome_browser_state->GetPrefs()));
+      new SuggestionsStore(browser_state->GetPrefs()));
   scoped_ptr<BlacklistStore> blacklist_store(
-      new BlacklistStore(chrome_browser_state->GetPrefs()));
+      new BlacklistStore(browser_state->GetPrefs()));
   scoped_ptr<leveldb_proto::ProtoDatabaseImpl<ImageData>> db(
       new leveldb_proto::ProtoDatabaseImpl<ImageData>(background_task_runner));
   scoped_ptr<ImageFetcher> image_fetcher(new ImageFetcherImpl(
@@ -71,9 +71,9 @@
   scoped_ptr<ImageManager> thumbnail_manager(new ImageManager(
       image_fetcher.Pass(), db.Pass(), database_dir,
       web::WebThread::GetTaskRunnerForThread(web::WebThread::DB)));
-  return new SuggestionsService(
+  return make_scoped_ptr(new SuggestionsService(
       browser_state->GetRequestContext(), suggestions_store.Pass(),
-      thumbnail_manager.Pass(), blacklist_store.Pass());
+      thumbnail_manager.Pass(), blacklist_store.Pass()));
 }
 
 void SuggestionsServiceFactory::RegisterBrowserStatePrefs(
diff --git a/ios/chrome/browser/sync/sync_setup_service_factory.cc b/ios/chrome/browser/sync/sync_setup_service_factory.cc
new file mode 100644
index 0000000..12137ea
--- /dev/null
+++ b/ios/chrome/browser/sync/sync_setup_service_factory.cc
@@ -0,0 +1,48 @@
+// 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 "ios/chrome/browser/sync/sync_setup_service_factory.h"
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/ios/browser_state_dependency_manager.h"
+#include "ios/chrome/browser/sync/sync_setup_service.h"
+#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/public/provider/chrome/browser/keyed_service_provider.h"
+
+// static
+SyncSetupService* SyncSetupServiceFactory::GetForBrowserState(
+    ios::ChromeBrowserState* browser_state) {
+  return static_cast<SyncSetupService*>(
+      GetInstance()->GetServiceForBrowserState(browser_state, true));
+}
+
+SyncSetupService* SyncSetupServiceFactory::GetForBrowserStateIfExists(
+    ios::ChromeBrowserState* browser_state) {
+  return static_cast<SyncSetupService*>(
+      GetInstance()->GetServiceForBrowserState(browser_state, false));
+}
+
+SyncSetupServiceFactory* SyncSetupServiceFactory::GetInstance() {
+  return Singleton<SyncSetupServiceFactory>::get();
+}
+
+SyncSetupServiceFactory::SyncSetupServiceFactory()
+    : BrowserStateKeyedServiceFactory(
+          "SyncSetupService",
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(ios::GetKeyedServiceProvider()->GetSyncServiceFactory());
+}
+
+SyncSetupServiceFactory::~SyncSetupServiceFactory() {
+}
+
+scoped_ptr<KeyedService> SyncSetupServiceFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  ios::ChromeBrowserState* browser_state =
+      ios::ChromeBrowserState::FromBrowserState(context);
+  return make_scoped_ptr(new SyncSetupService(
+      ios::GetKeyedServiceProvider()->GetSyncServiceForBrowserState(
+          browser_state),
+      browser_state->GetPrefs()));
+}
diff --git a/ios/chrome/browser/sync/sync_setup_service_factory.h b/ios/chrome/browser/sync/sync_setup_service_factory.h
new file mode 100644
index 0000000..3817e1a
--- /dev/null
+++ b/ios/chrome/browser/sync/sync_setup_service_factory.h
@@ -0,0 +1,45 @@
+// 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 IOS_CHROME_BROWSER_SYNC_SYNC_SETUP_SERVICE_FACTORY_H_
+#define IOS_CHROME_BROWSER_SYNC_SYNC_SETUP_SERVICE_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+
+template <typename T>
+struct DefaultSingletonTraits;
+
+class SyncSetupService;
+
+namespace ios {
+class ChromeBrowserState;
+}
+
+// Singleton that owns all SyncSetupServices and associates them with
+// BrowserStates.
+class SyncSetupServiceFactory : public BrowserStateKeyedServiceFactory {
+ public:
+  static SyncSetupService* GetForBrowserState(
+      ios::ChromeBrowserState* browser_state);
+  static SyncSetupService* GetForBrowserStateIfExists(
+      ios::ChromeBrowserState* browser_state);
+
+  static SyncSetupServiceFactory* GetInstance();
+
+ private:
+  friend struct DefaultSingletonTraits<SyncSetupServiceFactory>;
+
+  SyncSetupServiceFactory();
+  ~SyncSetupServiceFactory() override;
+
+  // BrowserStateKeyedServiceFactory implementation.
+  scoped_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncSetupServiceFactory);
+};
+
+#endif  // IOS_CHROME_BROWSER_SYNC_SYNC_SETUP_SERVICE_FACTORY_H_
diff --git a/ios/chrome/browser/translate/chrome_ios_translate_client.mm b/ios/chrome/browser/translate/chrome_ios_translate_client.mm
index a4d0c27..9eefa03b 100644
--- a/ios/chrome/browser/translate/chrome_ios_translate_client.mm
+++ b/ios/chrome/browser/translate/chrome_ios_translate_client.mm
@@ -131,7 +131,8 @@
 ChromeIOSTranslateClient::GetTranslateAcceptLanguages() {
   DCHECK(web_state());
   return TranslateAcceptLanguagesFactory::GetForBrowserState(
-      web_state()->GetBrowserState());
+      ios::ChromeBrowserState::FromBrowserState(
+          web_state()->GetBrowserState()));
 }
 
 int ChromeIOSTranslateClient::GetInfobarIconID() const {
diff --git a/ios/chrome/browser/translate/translate_accept_languages_factory.cc b/ios/chrome/browser/translate/translate_accept_languages_factory.cc
index 2ad0912..080f68f 100644
--- a/ios/chrome/browser/translate/translate_accept_languages_factory.cc
+++ b/ios/chrome/browser/translate/translate_accept_languages_factory.cc
@@ -28,6 +28,7 @@
 
  private:
   translate::TranslateAcceptLanguages accept_languages_;
+
   DISALLOW_COPY_AND_ASSIGN(TranslateAcceptLanguagesService);
 };
 
@@ -49,7 +50,8 @@
 
 // static
 translate::TranslateAcceptLanguages*
-TranslateAcceptLanguagesFactory::GetForBrowserState(web::BrowserState* state) {
+TranslateAcceptLanguagesFactory::GetForBrowserState(
+    ios::ChromeBrowserState* state) {
   TranslateAcceptLanguagesService* service =
       static_cast<TranslateAcceptLanguagesService*>(
           GetInstance()->GetServiceForBrowserState(state, true));
@@ -65,14 +67,16 @@
 TranslateAcceptLanguagesFactory::~TranslateAcceptLanguagesFactory() {
 }
 
-KeyedService* TranslateAcceptLanguagesFactory::BuildServiceInstanceFor(
-    web::BrowserState* browser_state) const {
-  ios::ChromeBrowserState* chrome_browser_state =
-      ios::ChromeBrowserState::FromBrowserState(browser_state);
-  return new TranslateAcceptLanguagesService(chrome_browser_state->GetPrefs());
+scoped_ptr<KeyedService>
+TranslateAcceptLanguagesFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  ios::ChromeBrowserState* browser_state =
+      ios::ChromeBrowserState::FromBrowserState(context);
+  return make_scoped_ptr(
+      new TranslateAcceptLanguagesService(browser_state->GetPrefs()));
 }
 
 web::BrowserState* TranslateAcceptLanguagesFactory::GetBrowserStateToUse(
-    web::BrowserState* state) const {
-  return GetBrowserStateRedirectedInIncognito(state);
+    web::BrowserState* context) const {
+  return GetBrowserStateRedirectedInIncognito(context);
 }
diff --git a/ios/chrome/browser/translate/translate_accept_languages_factory.h b/ios/chrome/browser/translate/translate_accept_languages_factory.h
index 2ca7bdd..e0814c0 100644
--- a/ios/chrome/browser/translate/translate_accept_languages_factory.h
+++ b/ios/chrome/browser/translate/translate_accept_languages_factory.h
@@ -13,8 +13,8 @@
 class TranslateAcceptLanguages;
 }
 
-namespace web {
-class BrowserState;
+namespace ios {
+class ChromeBrowserState;
 }
 
 // TranslateAcceptLanguagesFactory is a way to associate a
@@ -22,7 +22,7 @@
 class TranslateAcceptLanguagesFactory : public BrowserStateKeyedServiceFactory {
  public:
   static translate::TranslateAcceptLanguages* GetForBrowserState(
-      web::BrowserState* browser_state);
+      ios::ChromeBrowserState* browser_state);
   static TranslateAcceptLanguagesFactory* GetInstance();
 
  private:
@@ -31,11 +31,11 @@
   TranslateAcceptLanguagesFactory();
   ~TranslateAcceptLanguagesFactory() override;
 
-  // BrowserStateKeyedServiceFactory:
-  KeyedService* BuildServiceInstanceFor(
-      web::BrowserState* browser_state) const override;
+  // BrowserStateKeyedServiceFactory implementation.
+  scoped_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
   web::BrowserState* GetBrowserStateToUse(
-      web::BrowserState* browser_state) const override;
+      web::BrowserState* context) const override;
 
   DISALLOW_COPY_AND_ASSIGN(TranslateAcceptLanguagesFactory);
 };
diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp
index c6577fb4..7f5038e 100644
--- a/ios/chrome/ios_chrome.gyp
+++ b/ios/chrome/ios_chrome.gyp
@@ -52,6 +52,7 @@
         '../../components/components.gyp:dom_distiller_core',
         '../../components/components.gyp:dom_distiller_ios',
         '../../components/components.gyp:enhanced_bookmarks',
+        '../../components/components.gyp:favicon_core',
         '../../components/components.gyp:infobars_core',
         '../../components/components.gyp:keyed_service_core',
         '../../components/components.gyp:keyed_service_ios',
@@ -146,6 +147,10 @@
         'browser/enhanced_bookmarks/enhanced_bookmark_model_factory.h',
         'browser/experimental_flags.h',
         'browser/experimental_flags.mm',
+        'browser/favicon/favicon_client_impl.cc',
+        'browser/favicon/favicon_client_impl.h',
+        'browser/favicon/favicon_service_factory.cc',
+        'browser/favicon/favicon_service_factory.h',
         'browser/file_metadata_util.h',
         'browser/file_metadata_util.mm',
         'browser/find_in_page/find_in_page_controller.h',
@@ -232,6 +237,8 @@
         'browser/sync/sync_observer_bridge.mm',
         'browser/sync/sync_setup_service.cc',
         'browser/sync/sync_setup_service.h',
+        'browser/sync/sync_setup_service_factory.cc',
+        'browser/sync/sync_setup_service_factory.h',
         'browser/translate/after_translate_infobar_controller.h',
         'browser/translate/after_translate_infobar_controller.mm',
         'browser/translate/before_translate_infobar_controller.h',
diff --git a/ios/public/test/fake_sync_service_factory.cc b/ios/public/test/fake_sync_service_factory.cc
index 1f85f8e..10fecc9 100644
--- a/ios/public/test/fake_sync_service_factory.cc
+++ b/ios/public/test/fake_sync_service_factory.cc
@@ -5,26 +5,24 @@
 #include "ios/public/test/fake_sync_service_factory.h"
 
 #include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/sync_driver/fake_sync_service.h"
+#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h"
 
 namespace {
 
 class KeyedFakeSyncService : public KeyedService {
  public:
-  KeyedFakeSyncService(sync_driver::FakeSyncService* service)
-      : fake_sync_service_(service) {
-    DCHECK(fake_sync_service_);
-  }
+  KeyedFakeSyncService() {}
 
   sync_driver::FakeSyncService* fake_sync_service() {
-    return fake_sync_service_.get();
+    return &fake_sync_service_;
   }
 
  private:
-  scoped_ptr<sync_driver::FakeSyncService> fake_sync_service_;
+  sync_driver::FakeSyncService fake_sync_service_;
 };
 
 }  // namespace
@@ -38,17 +36,12 @@
 
 // static
 sync_driver::FakeSyncService* FakeSyncServiceFactory::GetForBrowserState(
-    web::BrowserState* browser_state) {
+    ios::ChromeBrowserState* browser_state) {
   return static_cast<KeyedFakeSyncService*>(
              FakeSyncServiceFactory::GetInstance()->GetServiceForBrowserState(
                  browser_state, true))->fake_sync_service();
 }
 
-KeyedService* FakeSyncServiceFactory::BuildServiceInstanceFor(
-    web::BrowserState* context) const {
-  return new KeyedFakeSyncService(new sync_driver::FakeSyncService);
-}
-
 FakeSyncServiceFactory::FakeSyncServiceFactory()
     : BrowserStateKeyedServiceFactory(
           "FakeSyncService",
@@ -58,4 +51,9 @@
 FakeSyncServiceFactory::~FakeSyncServiceFactory() {
 }
 
+scoped_ptr<KeyedService> FakeSyncServiceFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  return make_scoped_ptr(new KeyedFakeSyncService);
+}
+
 }  // namespace ios
diff --git a/ios/public/test/fake_sync_service_factory.h b/ios/public/test/fake_sync_service_factory.h
index 2ab0d8d..9cd315e 100644
--- a/ios/public/test/fake_sync_service_factory.h
+++ b/ios/public/test/fake_sync_service_factory.h
@@ -6,15 +6,20 @@
 #define IOS_PUBLIC_TEST_FAKE_SYNC_SERVICE_FACTORY_H_
 
 #include "base/macros.h"
-#include "base/memory/singleton.h"
+#include "base/memory/scoped_ptr.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
+template <typename T>
+struct DefaultSingletonTraits;
+
 namespace sync_driver {
 class FakeSyncService;
 }
 
 namespace ios {
 
+class ChromeBrowserState;
+
 class FakeSyncServiceFactory : public BrowserStateKeyedServiceFactory {
  public:
   // Returns the singleton FakeSyncServiceFactory instance.
@@ -22,18 +27,18 @@
 
   // Returns the FakeSyncService associated to |browser_state|.
   static sync_driver::FakeSyncService* GetForBrowserState(
-      web::BrowserState* browser_state);
+      ios::ChromeBrowserState* browser_state);
 
  private:
   friend struct DefaultSingletonTraits<FakeSyncServiceFactory>;
 
-  // BrowserStateKeyedServiceFactory implementation:
-  KeyedService* BuildServiceInstanceFor(
-      web::BrowserState* context) const override;
-
   FakeSyncServiceFactory();
   ~FakeSyncServiceFactory() override;
 
+  // BrowserStateKeyedServiceFactory implementation.
+  scoped_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
+
   DISALLOW_COPY_AND_ASSIGN(FakeSyncServiceFactory);
 };
 
diff --git a/ios/public/test/test_keyed_service_provider.cc b/ios/public/test/test_keyed_service_provider.cc
index 99b555f6..016a5b3a 100644
--- a/ios/public/test/test_keyed_service_provider.cc
+++ b/ios/public/test/test_keyed_service_provider.cc
@@ -6,8 +6,10 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/memory/singleton.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/keyed_service/core/keyed_service.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 #include "components/sync_driver/fake_sync_service.h"
@@ -29,7 +31,7 @@
   ~MissingServiceKeyedServiceFactory() override;
 
   // BrowserStateKeyedServiceFactory implementation.
-  KeyedService* BuildServiceInstanceFor(
+  scoped_ptr<KeyedService> BuildServiceInstanceFor(
       web::BrowserState* context) const override;
 
   DISALLOW_COPY_AND_ASSIGN(MissingServiceKeyedServiceFactory);
@@ -50,7 +52,8 @@
 MissingServiceKeyedServiceFactory::~MissingServiceKeyedServiceFactory() {
 }
 
-KeyedService* MissingServiceKeyedServiceFactory::BuildServiceInstanceFor(
+scoped_ptr<KeyedService>
+MissingServiceKeyedServiceFactory::BuildServiceInstanceFor(
     web::BrowserState* context) const {
   NOTREACHED();
   return nullptr;
diff --git a/ios/web/interstitials/html_web_interstitial_impl.h b/ios/web/interstitials/html_web_interstitial_impl.h
index 7bda180..8e469a9 100644
--- a/ios/web/interstitials/html_web_interstitial_impl.h
+++ b/ios/web/interstitials/html_web_interstitial_impl.h
@@ -32,12 +32,8 @@
   // receives a JavaScript command.
   void CommandReceivedFromWebView(NSString* command);
 
-  // WebInterstitial implementation:
-  void SetSize(const gfx::Size& size) override;
-
   // WebInterstitialImpl implementation:
-  UIView* GetView() const override;
-  UIScrollView* GetScrollView() const override;
+  CRWContentView* GetContentView() const override;
 
  protected:
   // WebInterstitialImpl implementation:
@@ -56,6 +52,8 @@
   // The CRWSimpleWebViewController that contains the web view used to show the
   // content. View needs to be resized by the caller.
   base::scoped_nsprotocol<id<CRWSimpleWebViewController>> web_view_controller_;
+  // The CRWContentView used to display |web_view_controller_|'s view.
+  base::scoped_nsobject<CRWContentView> content_view_;
 };
 
 }  // namespace web
diff --git a/ios/web/interstitials/html_web_interstitial_impl.mm b/ios/web/interstitials/html_web_interstitial_impl.mm
index 4a23631..4a2d091b1 100644
--- a/ios/web/interstitials/html_web_interstitial_impl.mm
+++ b/ios/web/interstitials/html_web_interstitial_impl.mm
@@ -8,6 +8,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "ios/web/interstitials/web_interstitial_facade_delegate.h"
 #include "ios/web/public/interstitials/web_interstitial_delegate.h"
+#include "ios/web/public/web_state/ui/crw_web_view_content_view.h"
 #import "ios/web/web_state/ui/crw_simple_web_view_controller.h"
 #include "ios/web/web_state/web_state_impl.h"
 #import "ios/web/web_state/web_view_creation_utils.h"
@@ -82,22 +83,12 @@
   delegate_->CommandReceived(base::SysNSStringToUTF8(command));
 }
 
-void HtmlWebInterstitialImpl::SetSize(const gfx::Size& size) {
-  CGRect frame = [web_view_controller_ view].frame;
-  frame.size = size.ToCGSize();
-  [[web_view_controller_ view] setFrame:frame];
-}
-
-UIView* HtmlWebInterstitialImpl::GetView() const {
-  return [web_view_controller_ view];
-}
-
-UIScrollView* HtmlWebInterstitialImpl::GetScrollView() const {
-  return [web_view_controller_ scrollView];
+CRWContentView* HtmlWebInterstitialImpl::GetContentView() const {
+  return content_view_.get();
 }
 
 void HtmlWebInterstitialImpl::PrepareForDisplay() {
-  if (!web_view_controller_) {
+  if (!content_view_) {
     web_view_controller_delegate_.reset(
         [[CRWWebInterstitialImplCRWSimpleWebViewDelegate alloc]
             initWithInterstitial:this]);
@@ -111,6 +102,9 @@
     NSString* html = base::SysUTF8ToNSString(delegate_->GetHtmlContents());
     [web_view_controller_ loadHTMLString:html
                                  baseURL:net::NSURLWithGURL(GetUrl())];
+    content_view_.reset([[CRWWebViewContentView alloc]
+        initWithWebView:[web_view_controller_ view]
+             scrollView:[web_view_controller_ scrollView]]);
   }
 }
 
diff --git a/ios/web/interstitials/native_web_interstitial_impl.h b/ios/web/interstitials/native_web_interstitial_impl.h
index 87179d89..5957ea9 100644
--- a/ios/web/interstitials/native_web_interstitial_impl.h
+++ b/ios/web/interstitials/native_web_interstitial_impl.h
@@ -23,12 +23,8 @@
                             scoped_ptr<NativeWebInterstitialDelegate> delegate);
   ~NativeWebInterstitialImpl() override;
 
-  // WebInterstitial implementation:
-  void SetSize(const gfx::Size& size) override;
-
   // WebInterstitialImpl implementation:
-  UIView* GetView() const override;
-  UIScrollView* GetScrollView() const override;
+  CRWContentView* GetContentView() const override;
 
  protected:
   // WebInterstitialImpl implementation:
@@ -40,12 +36,8 @@
  private:
   // The native interstitial delegate.
   scoped_ptr<NativeWebInterstitialDelegate> delegate_;
-  // The top-level view containing the scroll view.
-  base::scoped_nsobject<UIView> container_view_;
-  // The scroll view used to display |content_view_|.
-  base::scoped_nsobject<UIScrollView> scroll_view_;
-  // The content view provided by |delegate_|.
-  base::WeakNSObject<UIView> content_view_;
+  // The transient content view containing interstitial content.
+  base::scoped_nsobject<CRWContentView> content_view_;
 };
 
 }  // namespace web
diff --git a/ios/web/interstitials/native_web_interstitial_impl.mm b/ios/web/interstitials/native_web_interstitial_impl.mm
index f205d48..1a86b23 100644
--- a/ios/web/interstitials/native_web_interstitial_impl.mm
+++ b/ios/web/interstitials/native_web_interstitial_impl.mm
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "ios/web/public/interstitials/web_interstitial_delegate.h"
+#import "ios/web/public/web_state/ui/crw_generic_content_view.h"
 #include "ios/web/web_state/web_state_impl.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -31,37 +32,14 @@
 NativeWebInterstitialImpl::~NativeWebInterstitialImpl() {
 }
 
-void NativeWebInterstitialImpl::SetSize(const gfx::Size& size) {
-  if (!content_view_)
-    return;
-
-  // Resize container and scroll view.
-  CGSize cgSize = size.ToCGSize();
-  [container_view_ setFrame:{[container_view_ frame].origin, cgSize}];
-  [scroll_view_ setFrame:[container_view_ bounds]];
-
-  // Resize content.
-  CGSize contentSize = [content_view_ sizeThatFits:cgSize];
-  [content_view_ setFrame:{CGPointZero, contentSize}];
-  [scroll_view_ setContentSize:contentSize];
-}
-
-UIView* NativeWebInterstitialImpl::GetView() const {
-  return container_view_.get();
-}
-
-UIScrollView* NativeWebInterstitialImpl::GetScrollView() const {
-  return scroll_view_.get();
+CRWContentView* NativeWebInterstitialImpl::GetContentView() const {
+  return content_view_.get();
 }
 
 void NativeWebInterstitialImpl::PrepareForDisplay() {
   if (!content_view_) {
-    container_view_.reset([[UIView alloc] initWithFrame:CGRectZero]);
-    scroll_view_.reset([[UIScrollView alloc] initWithFrame:CGRectZero]);
-    content_view_.reset(delegate_->GetContentView());
-    [scroll_view_ addSubview:content_view_];
-    [scroll_view_ setBackgroundColor:delegate_->GetScrollViewBackgroundColor()];
-    [container_view_ addSubview:scroll_view_];
+    content_view_.reset([[CRWGenericContentView alloc]
+        initWithView:delegate_->GetContentView()]);
   }
 }
 
diff --git a/ios/web/interstitials/web_interstitial_impl.h b/ios/web/interstitials/web_interstitial_impl.h
index a438264..c1826ca 100644
--- a/ios/web/interstitials/web_interstitial_impl.h
+++ b/ios/web/interstitials/web_interstitial_impl.h
@@ -8,6 +8,7 @@
 #import <UIKit/UIKit.h>
 
 #import "ios/web/public/interstitials/web_interstitial.h"
+#include "ios/web/public/web_state/ui/crw_content_view.h"
 #include "ios/web/public/web_state/web_state_observer.h"
 #import "ios/web/web_state/ui/web_view_js_utils.h"
 #include "url/gurl.h"
@@ -32,9 +33,8 @@
   WebInterstitialImpl(WebStateImpl* web_state, const GURL& url);
   ~WebInterstitialImpl() override;
 
-  // Returns the view and scroll view used to display the interstitial content.
-  virtual UIView* GetView() const = 0;
-  virtual UIScrollView* GetScrollView() const = 0;
+  // Returns the transient content view used to display interstitial content.
+  virtual CRWContentView* GetContentView() const = 0;
 
   // Returns the url corresponding to this interstitial.
   const GURL& GetUrl() const;
diff --git a/ios/web/interstitials/web_interstitial_impl.mm b/ios/web/interstitials/web_interstitial_impl.mm
index 3e148560..b40a1895 100644
--- a/ios/web/interstitials/web_interstitial_impl.mm
+++ b/ios/web/interstitials/web_interstitial_impl.mm
@@ -46,11 +46,11 @@
 
 void WebInterstitialImpl::Show() {
   PrepareForDisplay();
-  GetWebStateImpl()->DisplayWebInterstitial(this);
+  GetWebStateImpl()->ShowWebInterstitial(this);
 }
 
 void WebInterstitialImpl::Hide() {
-  GetWebStateImpl()->DismissWebInterstitial();
+  GetWebStateImpl()->ClearTransientContentView();
 }
 
 void WebInterstitialImpl::DontProceed() {
diff --git a/ios/web/ios_web.gyp b/ios/web/ios_web.gyp
index 5b0f50b3..7e7eddd 100644
--- a/ios/web/ios_web.gyp
+++ b/ios/web/ios_web.gyp
@@ -184,8 +184,11 @@
         'public/web_state/js/crw_js_injection_receiver.h',
         'public/web_state/page_display_state.h',
         'public/web_state/page_display_state.mm',
+        'public/web_state/ui/crw_content_view.h',
+        'public/web_state/ui/crw_generic_content_view.h',
         'public/web_state/ui/crw_native_content.h',
         'public/web_state/ui/crw_native_content_provider.h',
+        'public/web_state/ui/crw_web_view_content_view.h',
         'public/web_state/url_verification_constants.h',
         'public/web_state/web_state.h',
         'public/web_state/web_state_observer.h',
@@ -232,6 +235,7 @@
         'web_state/ui/crw_context_menu_provider.mm',
         'web_state/ui/crw_debug_web_view.h',
         'web_state/ui/crw_debug_web_view.mm',
+        'web_state/ui/crw_generic_content_view.mm',
         'web_state/ui/crw_simple_web_view_controller.h',
         'web_state/ui/crw_static_file_web_view.h',
         'web_state/ui/crw_static_file_web_view.mm',
@@ -247,6 +251,7 @@
         'web_state/ui/crw_web_controller.mm',
         'web_state/ui/crw_web_controller_container_view.h',
         'web_state/ui/crw_web_controller_container_view.mm',
+        'web_state/ui/crw_web_view_content_view.mm',
         'web_state/ui/crw_wk_simple_web_view_controller.h',
         'web_state/ui/crw_wk_simple_web_view_controller.mm',
         'web_state/ui/crw_wk_web_view_crash_detector.h',
@@ -509,6 +514,8 @@
         'public/test/test_web_state.h',
         'public/test/test_web_thread.h',
         'public/test/test_web_thread_bundle.h',
+        'public/test/test_web_view_content_view.h',
+        'public/test/test_web_view_content_view.mm',
         'public/test/web_test_util.h',
         'test/crw_fake_web_controller_observer.h',
         'test/crw_fake_web_controller_observer.mm',
diff --git a/ios/web/public/interstitials/web_interstitial.h b/ios/web/public/interstitials/web_interstitial.h
index 43629c1..8a573c3a1 100644
--- a/ios/web/public/interstitials/web_interstitial.h
+++ b/ios/web/public/interstitials/web_interstitial.h
@@ -63,9 +63,6 @@
   // the target URL.
   // Warning: 'this' has been deleted when this method returns.
   virtual void Proceed() = 0;
-
-  // Sizes the view showing the actual interstitial page contents.
-  virtual void SetSize(const gfx::Size& size) = 0;
 };
 
 }  // namespace web
diff --git a/ios/web/public/test/test_web_state.h b/ios/web/public/test/test_web_state.h
index 4af4833..17f7a02 100644
--- a/ios/web/public/test/test_web_state.h
+++ b/ios/web/public/test/test_web_state.h
@@ -33,6 +33,7 @@
   const GURL& GetVisibleURL() const override;
   const GURL& GetLastCommittedURL() const override;
   GURL GetCurrentURL(URLVerificationTrustLevel* trust_level) const override;
+  void ShowTransientContentView(CRWContentView* content_view) override {}
   void AddScriptCommandCallback(const ScriptCommandCallback& callback,
                                 const std::string& command_prefix) override {}
   void RemoveScriptCommandCallback(const std::string& command_prefix) override {
diff --git a/ios/web/public/test/test_web_view_content_view.h b/ios/web/public/test/test_web_view_content_view.h
new file mode 100644
index 0000000..560b9c7
--- /dev/null
+++ b/ios/web/public/test/test_web_view_content_view.h
@@ -0,0 +1,23 @@
+// 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_TEST_TEST_WEB_VIEW_CONTENT_VIEW_H_
+#define IOS_WEB_PUBLIC_TEST_TEST_WEB_VIEW_CONTENT_VIEW_H_
+
+#import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
+
+// A test version of CRWWebViewContentView.
+@interface TestWebViewContentView : CRWWebViewContentView
+
+// Initializes the CRWTestWebContentView.  Since |webView| and |scrollView| may
+// be mock objects, they will not be added as subviews.
+- (instancetype)initWithMockWebView:(id)webView scrollView:(id)scrollView;
+
+// CRWTestWebViewContentViews should be initialized via |-initWithMockWebView:
+// scrollView:|.
+- (instancetype)initWithWebView:(UIView*)webView
+                     scrollView:(UIScrollView*)scrollView NS_UNAVAILABLE;
+@end
+
+#endif  // IOS_WEB_PUBLIC_TEST_TEST_WEB_VIEW_CONTENT_VIEW_H_
diff --git a/ios/web/public/test/test_web_view_content_view.mm b/ios/web/public/test/test_web_view_content_view.mm
new file mode 100644
index 0000000..afccc98
--- /dev/null
+++ b/ios/web/public/test/test_web_view_content_view.mm
@@ -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.
+
+#include "ios/web/public/test/test_web_view_content_view.h"
+
+#include "base/logging.h"
+#include "base/mac/scoped_nsobject.h"
+
+@interface TestWebViewContentView () {
+  base::scoped_nsprotocol<id> _mockWebView;
+  base::scoped_nsprotocol<id> _mockScrollView;
+}
+
+@end
+
+@implementation TestWebViewContentView
+
+- (instancetype)initWithMockWebView:(id)webView scrollView:(id)scrollView {
+  self = [super initWithFrame:CGRectZero];
+  if (self) {
+    DCHECK(webView);
+    DCHECK(scrollView);
+    _mockWebView.reset([webView retain]);
+    _mockScrollView.reset([scrollView retain]);
+  }
+  return self;
+}
+
+#pragma mark Accessors
+
+- (UIScrollView*)scrollView {
+  return static_cast<UIScrollView*>(_mockScrollView.get());
+}
+
+- (UIView*)webView {
+  return static_cast<UIView*>(_mockWebView.get());
+}
+
+@end
diff --git a/ios/web/public/web_state/crw_web_delegate.h b/ios/web/public/web_state/crw_web_delegate.h
index 6bf0458..d58c94be 100644
--- a/ios/web/public/web_state/crw_web_delegate.h
+++ b/ios/web/public/web_state/crw_web_delegate.h
@@ -188,8 +188,7 @@
 // Called to ask if external URL should be opened. External URL is one that
 // cannot be presented by CRWWebController.
 - (BOOL)webController:(CRWWebController*)webController
-    shouldOpenExternalURL:(const GURL&)URL
-        userIsInteracting:(BOOL)userIsInteracting;
+    shouldOpenExternalURL:(const GURL&)URL;
 
 // Called when |url| is deemed suitable to be opened in a matching native app.
 // Needs to return whether |url| was opened in a matching native app.
diff --git a/ios/web/public/web_state/crw_web_view_proxy.h b/ios/web/public/web_state/crw_web_view_proxy.h
index 784df4a..dc89d82 100644
--- a/ios/web/public/web_state/crw_web_view_proxy.h
+++ b/ios/web/public/web_state/crw_web_view_proxy.h
@@ -13,6 +13,7 @@
 
 // Provides an interface for embedders to access the WebState's UIWebView in a
 // limited and controlled manner.
+// TODO(kkhorimoto): rename protocol to CRWContentViewProxy.
 @protocol CRWWebViewProxy<NSObject>
 
 // The UIWebView's bounding rectangle (relative to its parent).
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
index 9f9aadb..d46735e 100644
--- 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
@@ -20,6 +20,7 @@
 // needed.
 // The class forwards some of the methods onto the UIScrollView. For more
 // information look at the UIScrollView documentation.
+// TODO(kkhorimoto): rename class to CRWContentViewScrollViewProxy.
 @interface CRWWebViewScrollViewProxy : NSObject<UIScrollViewDelegate>
 @property(nonatomic, assign) CGPoint contentOffset;
 @property(nonatomic, assign) UIEdgeInsets contentInset;
diff --git a/ios/web/public/web_state/ui/crw_content_view.h b/ios/web/public/web_state/ui/crw_content_view.h
new file mode 100644
index 0000000..d84ecc29
--- /dev/null
+++ b/ios/web/public/web_state/ui/crw_content_view.h
@@ -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.
+
+#ifndef IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_CONTENT_VIEW_H_
+#define IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_CONTENT_VIEW_H_
+
+#import <UIKit/UIKit.h>
+
+// UIViews conforming to CRWScrollableContent (i.e. CRWContentViews) are used
+// to display content within a WebState.
+@protocol CRWScrollableContent<NSObject>
+
+// The scroll view used to display the content.  If |scrollView| is non-nil,
+// it will be used to back the CRWContentViewScrollViewProxy and is expected to
+// be a subview of the CRWContentView.
+@property(nonatomic, retain, readonly) UIScrollView* scrollView;
+
+// Returns YES if content is being displayed in the scroll view.
+// TODO(stuartmorgan): See if this can be removed from the public interface.
+- (BOOL)isViewAlive;
+
+@end
+
+// Convenience type for content views.
+typedef UIView<CRWScrollableContent> CRWContentView;
+
+#endif  // IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_CONTENT_VIEW_H_
diff --git a/ios/web/public/web_state/ui/crw_generic_content_view.h b/ios/web/public/web_state/ui/crw_generic_content_view.h
new file mode 100644
index 0000000..985dbb9
--- /dev/null
+++ b/ios/web/public/web_state/ui/crw_generic_content_view.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 IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_GENERIC_CONTENT_VIEW_H_
+#define IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_GENERIC_CONTENT_VIEW_H_
+
+#include "ios/web/public/web_state/ui/crw_content_view.h"
+
+// Wraps an arbitrary native UIView in a CRWContentView.
+@interface CRWGenericContentView : CRWContentView
+
+// The view that was passed to |-initWithContentView:|.  This is the view that
+// is displayed in |self.scrollView|.
+@property(nonatomic, retain, readonly) UIView* view;
+
+// Initializes the CRWNativeContentContainerView to display |view|, which
+// will be added to the scroll view.
+- (instancetype)initWithView:(UIView*)view NS_DESIGNATED_INITIALIZER;
+
+// CRWGenericContentViews should be initialized via |-initWithView:|.
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_GENERIC_CONTENT_VIEW_H_
diff --git a/ios/web/public/web_state/ui/crw_web_view_content_view.h b/ios/web/public/web_state/ui/crw_web_view_content_view.h
new file mode 100644
index 0000000..e2de7bdc
--- /dev/null
+++ b/ios/web/public/web_state/ui/crw_web_view_content_view.h
@@ -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.
+
+#ifndef IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_WEB_VIEW_CONTENT_VIEW_H_
+#define IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_WEB_VIEW_CONTENT_VIEW_H_
+
+#import "ios/web/public/web_state/ui/crw_content_view.h"
+
+// Wraps a web vew in a CRWContentView.
+@interface CRWWebViewContentView : CRWContentView
+
+// The webView passed to |-initWithWebView|.
+@property(nonatomic, retain, readonly) UIView* webView;
+
+// Initializes the CRWWebViewContentView to display |webView|.
+- (instancetype)initWithWebView:(UIView*)webView
+                     scrollView:(UIScrollView*)scrollView
+    NS_DESIGNATED_INITIALIZER;
+
+// CRWWebViewContentViews should be initialized via |-initWithWebView:
+// scrollView:|.
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_WEB_VIEW_CONTENT_VIEW_H_
diff --git a/ios/web/public/web_state/web_state.h b/ios/web/public/web_state/web_state.h
index f4cd765..6cdc4b03 100644
--- a/ios/web/public/web_state/web_state.h
+++ b/ios/web/public/web_state/web_state.h
@@ -23,11 +23,14 @@
 
 #if defined(__OBJC__)
 @class CRWJSInjectionReceiver;
+@protocol CRWScrollableContent;
 @protocol CRWWebViewProxy;
 typedef id<CRWWebViewProxy> CRWWebViewProxyType;
 @class UIView;
+typedef UIView<CRWScrollableContent> CRWContentView;
 #else
 class CRWJSInjectionReceiver;
+typedef void CRWContentView;
 typedef void* CRWWebViewProxyType;
 class UIView;
 #endif  // defined(__OBJC__)
@@ -137,6 +140,10 @@
   // See http://crbug.com/457679
   virtual GURL GetCurrentURL(URLVerificationTrustLevel* trust_level) const = 0;
 
+  // Resizes |content_view| to the content area's size and adds it to the
+  // hierarchy.  A navigation will remove the view from the hierarchy.
+  virtual void ShowTransientContentView(CRWContentView* content_view) = 0;
+
   // Returns true if a WebInterstitial is currently displayed.
   virtual bool IsShowingWebInterstitial() const = 0;
 
diff --git a/ios/web/test/web_test.mm b/ios/web/test/web_test.mm
index c521209..b286b96 100644
--- a/ios/web/test/web_test.mm
+++ b/ios/web/test/web_test.mm
@@ -114,6 +114,7 @@
   while ([webController_ loadPhase] != PAGE_LOADED)
     WaitForBackgroundTasks();
   webController_.get().delegate = existingDelegate;
+  [[webController_ view] layoutIfNeeded];
 }
 
 void WebTestBase::WaitForBackgroundTasks() {
diff --git a/ios/web/web_state/crw_web_view_proxy_impl.h b/ios/web/web_state/crw_web_view_proxy_impl.h
index 7a8386c..3589106 100644
--- a/ios/web/web_state/crw_web_view_proxy_impl.h
+++ b/ios/web/web_state/crw_web_view_proxy_impl.h
@@ -8,21 +8,24 @@
 #import <UIKit/UIKit.h>
 
 #include "ios/web/public/web_state/crw_web_view_proxy.h"
+#include "ios/web/public/web_state/ui/crw_content_view.h"
 
 @class CRWWebController;
 
+// TODO(kkhorimoto): Rename class to CRWContentViewProxyImpl.
 @interface CRWWebViewProxyImpl : NSObject<CRWWebViewProxy>
 
+// Used by CRWWebController to set the content view being managed.
+// |contentView|'s scroll view property will be managed by the
+// WebViewScrollViewProxy.
+@property(nonatomic, weak) CRWContentView* contentView;
+
 // TODO(justincohen): It would be better if we could use something like a
 // ScrollPositionController instead of passing in all of web controller.
 // crbug.com/227744
 // Init with a weak reference of web controller, used for passing thru calls.
 - (instancetype)initWithWebController:(CRWWebController*)webController;
 
-// Used by the CRWWebController to set the web view to be managed.
-// Also sets the UIScrollView to be managed inside the WebViewScrollViewProxy.
-- (void)setWebView:(UIView*)webView scrollView:(UIScrollView*)scrollView;
-
 @end
 
 #endif  // IOS_WEB_WEB_STATE_CRW_WEB_VIEW_PROXY_IMPL_H_
diff --git a/ios/web/web_state/crw_web_view_proxy_impl.mm b/ios/web/web_state/crw_web_view_proxy_impl.mm
index adb13c8..c8d6142 100644
--- a/ios/web/web_state/crw_web_view_proxy_impl.mm
+++ b/ios/web/web_state/crw_web_view_proxy_impl.mm
@@ -7,6 +7,7 @@
 #include "base/ios/weak_nsobject.h"
 #include "base/mac/scoped_nsobject.h"
 #import "ios/web/public/web_state/crw_web_view_scroll_view_proxy.h"
+#import "ios/web/public/web_state/ui/crw_content_view.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 
 namespace {
@@ -64,12 +65,12 @@
 @end
 
 @implementation CRWWebViewProxyImpl {
-  base::WeakNSObject<UIView> _webView;
+  base::WeakNSObject<CRWContentView> _contentView;
   base::WeakNSObject<CRWWebController> _webController;
   base::scoped_nsobject<NSMutableDictionary> _registeredInsets;
   // The WebViewScrollViewProxy is a wrapper around the UIWebView's
   // UIScrollView to give components access in a limited and controlled manner.
-  base::scoped_nsobject<CRWWebViewScrollViewProxy> _webViewScrollViewProxy;
+  base::scoped_nsobject<CRWWebViewScrollViewProxy> _contentViewScrollViewProxy;
 }
 
 - (instancetype)initWithWebController:(CRWWebController*)webController {
@@ -78,21 +79,21 @@
     DCHECK(webController);
     _registeredInsets.reset([[NSMutableDictionary alloc] init]);
     _webController.reset(webController);
-    _webViewScrollViewProxy.reset([[CRWWebViewScrollViewProxy alloc] init]);
+    _contentViewScrollViewProxy.reset([[CRWWebViewScrollViewProxy alloc] init]);
   }
   return self;
 }
 
 - (CRWWebViewScrollViewProxy*)scrollViewProxy {
-  return _webViewScrollViewProxy.get();
+  return _contentViewScrollViewProxy.get();
 }
 
 - (CGRect)bounds {
-  return [_webView bounds];
+  return [_contentView bounds];
 }
 
 - (NSArray*)gestureRecognizers {
-  return [_webView gestureRecognizers];
+  return [_contentView gestureRecognizers];
 }
 
 - (web::WebViewType)webViewType {
@@ -115,25 +116,27 @@
   [_registeredInsets removeObjectForKey:callerValue];
 }
 
-- (void)setWebView:(UIView*)webView scrollView:(UIScrollView*)scrollView {
-  _webView.reset(webView);
-  if (webView)
-    DCHECK(scrollView);
-  [_webViewScrollViewProxy setScrollView:scrollView];
+- (CRWContentView*)contentView {
+  return _contentView.get();
+}
+
+- (void)setContentView:(CRWContentView*)contentView {
+  _contentView.reset(contentView);
+  [_contentViewScrollViewProxy setScrollView:contentView.scrollView];
 }
 
 - (void)addSubview:(UIView*)view {
-  return [_webView addSubview:view];
+  return [_contentView addSubview:view];
 }
 
 - (BOOL)hasSearchableTextContent {
-  return _webView != nil && [_webController contentIsHTML];
+  return _contentView != nil && [_webController contentIsHTML];
 }
 
 - (UIView*)getKeyboardAccessory {
-  if (!_webView)
+  if (!_contentView)
     return nil;
-  UIView* firstResponder = GetFirstResponderSubview(_webView);
+  UIView* firstResponder = GetFirstResponderSubview(_contentView);
   return firstResponder.inputAccessoryView;
 }
 
diff --git a/ios/web/web_state/ui/crw_generic_content_view.mm b/ios/web/web_state/ui/crw_generic_content_view.mm
new file mode 100644
index 0000000..26023132
--- /dev/null
+++ b/ios/web/web_state/ui/crw_generic_content_view.mm
@@ -0,0 +1,74 @@
+// 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 "ios/web/public/web_state/ui/crw_generic_content_view.h"
+
+#include "base/logging.h"
+#include "base/mac/scoped_nsobject.h"
+
+@interface CRWGenericContentView () {
+  // Backing objectect for |self.scrollView|.
+  base::scoped_nsobject<UIScrollView> _scrollView;
+  // Backing object for |self.view|.
+  base::scoped_nsobject<UIView> _view;
+}
+
+@end
+
+@implementation CRWGenericContentView
+
+- (instancetype)initWithView:(UIView*)view {
+  self = [super initWithFrame:CGRectZero];
+  if (self) {
+    DCHECK(view);
+    _view.reset([view retain]);
+    _scrollView.reset([[UIScrollView alloc] initWithFrame:CGRectZero]);
+    [self addSubview:_scrollView];
+    [_scrollView addSubview:_view];
+    [_scrollView setBackgroundColor:[_view backgroundColor]];
+  }
+  return self;
+}
+
+#pragma mark Accessors
+
+- (UIScrollView*)scrollView {
+  if (!_scrollView) {
+    _scrollView.reset([[UIScrollView alloc] initWithFrame:CGRectZero]);
+  }
+  return _scrollView.get();
+}
+
+- (UIView*)view {
+  return _view.get();
+}
+
+#pragma mark Layout
+
+- (void)layoutSubviews {
+  [super layoutSubviews];
+
+  // scrollView layout.
+  self.scrollView.frame = self.bounds;
+
+  // view layout.
+  CGRect contentRect =
+      UIEdgeInsetsInsetRect(self.bounds, self.scrollView.contentInset);
+  CGSize viewSize = [self.view sizeThatFits:contentRect.size];
+  self.view.frame = CGRectMake(0.0, 0.0, viewSize.width, viewSize.height);
+
+  // UIScrollViews only scroll vertically if the content size's height is
+  // creater than that of its content rect.
+  if (viewSize.height <= CGRectGetHeight(contentRect)) {
+    CGFloat singlePixel = 1.0f / [[UIScreen mainScreen] scale];
+    viewSize.height = CGRectGetHeight(contentRect) + singlePixel;
+  }
+  self.scrollView.contentSize = viewSize;
+}
+
+- (BOOL)isViewAlive {
+  return YES;
+}
+
+@end
diff --git a/ios/web/web_state/ui/crw_ui_web_view_web_controller.mm b/ios/web/web_state/ui/crw_ui_web_view_web_controller.mm
index 467607f..465a776 100644
--- a/ios/web/web_state/ui/crw_ui_web_view_web_controller.mm
+++ b/ios/web/web_state/ui/crw_ui_web_view_web_controller.mm
@@ -24,6 +24,7 @@
 #include "ios/web/net/request_group_util.h"
 #include "ios/web/public/url_scheme_util.h"
 #include "ios/web/public/web_client.h"
+#import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
 #import "ios/web/ui_web_view_util.h"
 #include "ios/web/web_state/frame_info.h"
 #import "ios/web/web_state/js/crw_js_invoke_parameter_queue.h"
@@ -439,9 +440,9 @@
 #pragma mark -
 #pragma mark Testing-Only Methods
 
-- (void)injectWebView:(id)webView {
-  [super injectWebView:webView];
-  [self setWebView:webView];
+- (void)injectWebViewContentView:(CRWWebViewContentView*)webViewContentView {
+  [super injectWebViewContentView:webViewContentView];
+  [self setWebView:static_cast<UIWebView*>(webViewContentView.webView)];
 }
 
 #pragma mark CRWJSInjectionEvaluatorMethods
diff --git a/ios/web/web_state/ui/crw_web_controller.h b/ios/web/web_state/ui/crw_web_controller.h
index a6be7b5..2282fe4 100644
--- a/ios/web/web_state/ui/crw_web_controller.h
+++ b/ios/web/web_state/ui/crw_web_controller.h
@@ -52,6 +52,7 @@
 @protocol CRWNativeContentProvider;
 @protocol CRWSwipeRecognizerProvider;
 @protocol CRWWebControllerObserver;
+@class CRWWebViewContentView;
 @protocol CRWWebViewProxy;
 class GURL;
 
@@ -94,10 +95,8 @@
 @property(nonatomic, readonly) web::WebState* webState;
 @property(nonatomic, readonly) web::WebStateImpl* webStateImpl;
 
-// If on a regular webpage, the UIWebView responsible for rendering it. If
-// on an internal page, the native view implementing the functionality. If the
-// view has been purged due to low memory, this will re-create it. It is up
-// to the caller to size the view.
+// The container view used to display content.  If the view has been purged due
+// to low memory, this will recreate it.
 @property(nonatomic, readonly) UIView* view;
 
 // The web view proxy associated with this controller.
@@ -130,10 +129,12 @@
 // Return an image to use as replacement of a missing snapshot.
 + (UIImage*)defaultSnapshotImage;
 
-// Adds |interstitialView| to the content view, copying the current scroll
-// offsets to |scrollView|.
-- (void)displayInterstitialView:(UIView*)interstitialView
-                 withScrollView:(UIScrollView*)scrollView;
+// Replaces the currently displayed content with |contentView|.  The content
+// view will be dismissed for the next navigation.
+- (void)showTransientContentView:(CRWContentView*)contentView;
+
+// Clear the transient content view, if one is shown.
+- (void)clearTransientContentView;
 
 // Give the unload listeners a chance to fire. Returns YES if they complete
 // and the CRWWebController is in a state it may be closed.
@@ -314,9 +315,10 @@
 
 @interface CRWWebController (UsedOnlyForTesting)  // Testing or internal API.
 
-// Injects a web view for testing.  Takes ownership of the |webView|.
-- (void)injectWebView:(id)webView;
-- (void)resetInjectedWebView;
+// Injects a CRWWebViewContentView for testing.  Takes ownership of
+// |webViewContentView|.
+- (void)injectWebViewContentView:(CRWWebViewContentView*)webViewContentView;
+- (void)resetInjectedWebViewContentView;
 // Returns the number of observers registered for this CRWWebController.
 - (NSUInteger)observerCount;
 - (NSString*)windowId;
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index e8bab8fb..73aa287f 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -53,8 +53,10 @@
 #import "ios/web/public/web_state/crw_web_view_scroll_view_proxy.h"
 #import "ios/web/public/web_state/js/crw_js_injection_manager.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
+#import "ios/web/public/web_state/ui/crw_content_view.h"
 #import "ios/web/public/web_state/ui/crw_native_content.h"
 #import "ios/web/public/web_state/ui/crw_native_content_provider.h"
+#import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
 #include "ios/web/public/web_state/url_verification_constants.h"
 #include "ios/web/public/web_state/web_state.h"
 #include "ios/web/web_state/blocked_popup_info.h"
@@ -259,16 +261,26 @@
   base::scoped_nsobject<CRWJSInjectionReceiver> _jsInjectionReceiver;
 }
 
+// The container view.  The container view should be accessed through this
+// property rather than |self.view| from within this class, as |self.view|
+// triggers creation while |self.containerView| will return nil if the view
+// hasn't been instantiated.
+@property(nonatomic, retain, readonly)
+    CRWWebControllerContainerView* containerView;
 // The current page state of the web view. Writing to this property
 // asynchronously applies the passed value to the current web view.
 @property(nonatomic, readwrite) web::PageDisplayState pageDisplayState;
+// The currently displayed native controller, if any.
+@property(nonatomic, readwrite) id<CRWNativeContent> nativeController;
+// Removes the container view from the hierarchy and resets the ivar.
+- (void)resetContainerView;
 // Resets any state that is associated with a specific document object (e.g.,
 // page interaction tracking).
 - (void)resetDocumentSpecificState;
 // Returns YES if the URL looks like it is one CRWWebController can show.
 + (BOOL)webControllerCanShow:(const GURL&)url;
-// Clear any interstitials being displayed.
-- (void)clearInterstitials;
+// Clears the currently-displayed transient content view.
+- (void)clearTransientContentView;
 // Returns a lazily created CRWTouchTrackingRecognizer.
 - (CRWTouchTrackingRecognizer*)touchTrackingRecognizer;
 // Shows placeholder overlay.
@@ -503,11 +515,6 @@
   WebKitErrorPlugInLoadFailed = 204,
 };
 
-// Tag for the interstitial view so we can find it and dismiss it later.
-enum {
-  kInterstitialViewTag = 1000,
-};
-
 // URLs that are fed into UIWebView as history push/replace get escaped,
 // potentially changing their format. Code that attempts to determine whether a
 // URL hasn't changed can be confused by those differences though, so method
@@ -636,24 +643,24 @@
   return _webStateImpl.get();
 }
 
-// WebStateImpl will delete the interstitial page object, which will in turn
-// remove its view from |_contentView|.
-- (void)clearInterstitials {
-  [_webViewProxy setWebView:self.webView scrollView:self.webScrollView];
+- (void)clearTransientContentView {
+  // Early return if there is no transient content view.
+  if (!self.containerView.transientContentView)
+    return;
+
+  // Remove the transient content view from the hierarchy.
+  [self.containerView clearTransientContentView];
+
+  // Notify the WebState so it can perform any required state cleanup.
   if (_webStateImpl)
-    _webStateImpl->ClearWebInterstitialForNavigation();
+    _webStateImpl->ClearTransientContentView();
 }
 
-// Attaches |interstitialView| to |_contentView|.  Note that this class never
-// explicitly removes the interstitial from |_contentView|;
-// web::WebStateImpl::DismissWebInterstitial() takes care of that.
-- (void)displayInterstitialView:(UIView*)interstitialView
-                 withScrollView:(UIScrollView*)scrollView {
-  DCHECK(interstitialView);
-  DCHECK(scrollView);
-  [_webViewProxy setWebView:interstitialView scrollView:scrollView];
-  interstitialView.tag = kInterstitialViewTag;
-  [_containerView addSubview:interstitialView];
+- (void)showTransientContentView:(CRWContentView*)contentView {
+  DCHECK(contentView);
+  DCHECK(contentView.scrollView);
+  DCHECK([contentView.scrollView isDescendantOfView:contentView]);
+  [self.containerView displayTransientContent:contentView];
 }
 
 - (id<CRWWebDelegate>)delegate {
@@ -662,11 +669,11 @@
 
 - (void)setDelegate:(id<CRWWebDelegate>)delegate {
   _delegate.reset(delegate);
-  if ([_nativeController respondsToSelector:@selector(setDelegate:)]) {
+  if ([self.nativeController respondsToSelector:@selector(setDelegate:)]) {
     if ([_delegate respondsToSelector:@selector(webController:titleDidChange:)])
-      [_nativeController setDelegate:self];
+      [self.nativeController setDelegate:self];
     else
-      [_nativeController setDelegate:nil];
+      [self.nativeController setDelegate:nil];
   }
 }
 
@@ -694,10 +701,8 @@
 
   [self abortLoad];
   [self.webView removeFromSuperview];
-  [_webViewProxy setWebView:nil scrollView:nil];
+  [self.containerView resetContent];
   [self resetWebView];
-  // Remove the web toolbars.
-  [_containerView removeAllToolbars];
 }
 
 - (void)dealloc {
@@ -718,24 +723,24 @@
 
 - (void)dismissKeyboard {
   [self.webView endEditing:YES];
-  if ([_nativeController respondsToSelector:@selector(dismissKeyboard)])
-    [_nativeController dismissKeyboard];
+  if ([self.nativeController respondsToSelector:@selector(dismissKeyboard)])
+    [self.nativeController dismissKeyboard];
 }
 
 - (id<CRWNativeContent>)nativeController {
-  return _nativeController.get();
+  return self.containerView.nativeController;
 }
 
 - (void)setNativeController:(id<CRWNativeContent>)nativeController {
   // Check for pointer equality.
-  if (_nativeController.get() == nativeController)
+  if (self.nativeController == nativeController)
     return;
 
   // Unset the delegate on the previous instance.
-  if ([_nativeController respondsToSelector:@selector(setDelegate:)])
-    [_nativeController setDelegate:nil];
+  if ([self.nativeController respondsToSelector:@selector(setDelegate:)])
+    [self.nativeController setDelegate:nil];
 
-  _nativeController.reset([nativeController retain]);
+  [self.containerView displayNativeContent:nativeController];
   [self setNativeControllerWebUsageEnabled:_webUsageEnabled];
 }
 
@@ -746,8 +751,9 @@
 }
 
 - (void)setNativeControllerWebUsageEnabled:(BOOL)webUsageEnabled {
-  if ([_nativeController respondsToSelector:@selector(setWebUsageEnabled:)]) {
-    [_nativeController setWebUsageEnabled:webUsageEnabled];
+  if ([self.nativeController
+          respondsToSelector:@selector(setWebUsageEnabled:)]) {
+    [self.nativeController setWebUsageEnabled:webUsageEnabled];
   }
 }
 
@@ -764,11 +770,11 @@
     if (enabled) {
       // Don't create the web view; let it be lazy created as needed.
     } else {
-      [self clearInterstitials];
+      [self clearTransientContentView];
       [self removeWebViewAllowingCachedReconstruction:YES];
       _touchTrackingRecognizer.get().touchTrackingDelegate = nil;
       _touchTrackingRecognizer.reset();
-      _containerView.reset();
+      [self resetContainerView];
     }
   }
 }
@@ -777,12 +783,16 @@
   [self removeWebViewAllowingCachedReconstruction:NO];
 }
 
+- (void)resetContainerView {
+  [self.containerView removeFromSuperview];
+  _containerView.reset();
+}
+
 - (void)handleLowMemory {
   [self removeWebViewAllowingCachedReconstruction:YES];
-  [self setNativeController:nil];
   _touchTrackingRecognizer.get().touchTrackingDelegate = nil;
   _touchTrackingRecognizer.reset();
-  _containerView.reset();
+  [self resetContainerView];
   _usePlaceholderOverlay = YES;
 }
 
@@ -796,7 +806,7 @@
       _usePlaceholderOverlay = YES;
       _touchTrackingRecognizer.get().touchTrackingDelegate = nil;
       _touchTrackingRecognizer.reset();
-      _containerView.reset();
+      [self resetContainerView];
     }
   }
 }
@@ -806,7 +816,7 @@
 }
 
 - (BOOL)isViewAlive {
-  return self.webView || [_nativeController isViewAlive];
+  return [self.containerView isViewAlive];
 }
 
 - (BOOL)contentIsHTML {
@@ -826,16 +836,16 @@
 }
 
 - (void)dismissModals {
-  if ([_nativeController respondsToSelector:@selector(dismissModals)])
-    [_nativeController dismissModals];
+  if ([self.nativeController respondsToSelector:@selector(dismissModals)])
+    [self.nativeController dismissModals];
 }
 
 // Caller must reset the delegate before calling.
 - (void)close {
   self.nativeProvider = nil;
   self.swipeRecognizerProvider = nil;
-  if ([_nativeController respondsToSelector:@selector(close)])
-    [_nativeController close];
+  if ([self.nativeController respondsToSelector:@selector(close)])
+    [self.nativeController close];
 
   base::scoped_nsobject<NSSet> observers([_observers copy]);
   for (id it in observers.get()) {
@@ -979,8 +989,8 @@
   }
   // Any non-web URL source is trusted.
   *trustLevel = web::URLVerificationTrustLevel::kAbsolute;
-  if (_nativeController)
-    return [_nativeController url];
+  if (self.nativeController)
+    return [self.nativeController url];
   return [self currentNavigationURL];
 }
 
@@ -1159,18 +1169,15 @@
     [webView addGestureRecognizer:recognizer];
   }
 
-  webView.frame = [_containerView bounds];
-
   _URLOnStartLoading = _defaultURL;
 
-  // Do final view setup.
-  CGPoint initialOffset = CGPointMake(0, 0 - [self headerHeight]);
-  [self.webScrollView setContentOffset:initialOffset];
-  [_containerView addToolbars:_webViewToolbars];
+  // Add the web toolbars.
+  [self.containerView addToolbars:_webViewToolbars];
 
-  [_webViewProxy setWebView:self.webView scrollView:self.webScrollView];
-
-  [_containerView addSubview:webView];
+  base::scoped_nsobject<CRWWebViewContentView> webViewContentView(
+      [[CRWWebViewContentView alloc] initWithWebView:self.webView
+                                          scrollView:self.webScrollView]);
+  [self.containerView displayWebViewContentView:webViewContentView];
 }
 
 - (CRWWebController*)createChildWebControllerWithReferrerURL:
@@ -1184,14 +1191,18 @@
 }
 
 - (BOOL)canUseViewForGeneratingOverlayPlaceholderView {
-  return _containerView != nil;
+  return self.containerView != nil;
 }
 
 - (UIView*)view {
   // Kick off the process of lazily creating the view and starting the load if
-  // necessary; this creates _contentView if it doesn't exist.
+  // necessary; this creates _containerView if it doesn't exist.
   [self triggerPendingLoad];
-  DCHECK(_containerView);
+  DCHECK(self.containerView);
+  return self.containerView;
+}
+
+- (CRWWebControllerContainerView*)containerView {
   return _containerView.get();
 }
 
@@ -1308,9 +1319,6 @@
   // See crbug.com/228397.
   [self registerUserAgent];
 
-  // Freeing the native controller removes its view from the view hierarchy.
-  [self setNativeController:nil];
-
   // Clear the set of URLs opened in external applications.
   _openedApplicationURL.reset([[NSMutableSet alloc] init]);
 
@@ -1325,7 +1333,7 @@
   DCHECK(!targetURL.SchemeIs(url::kJavaScriptScheme));
   [self ensureWebViewCreated];
 
-  DCHECK(self.webView && !_nativeController);
+  DCHECK(self.webView && !self.nativeController);
   NSMutableURLRequest* request =
       [NSMutableURLRequest requestWithURL:net::NSURLWithGURL(targetURL)];
   const web::Referrer referrer([self currentSessionEntryReferrer]);
@@ -1393,9 +1401,6 @@
 }
 
 - (void)loadNativeViewWithSuccess:(BOOL)loadSuccess {
-  [_nativeController view].frame = [self visibleFrame];
-  [_containerView addSubview:[_nativeController view]];
-  [[_nativeController view] setNeedsUpdateConstraints];
   const GURL currentURL([self currentURL]);
   [self didStartLoadingURL:currentURL updateHistory:loadSuccess];
   _loadPhase = web::PAGE_LOADED;
@@ -1405,14 +1410,14 @@
 
   // Inform the embedder the title changed.
   if ([_delegate respondsToSelector:@selector(webController:titleDidChange:)]) {
-    NSString* title = [_nativeController title];
+    NSString* title = [self.nativeController title];
     // If a title is present, notify the delegate.
     if (title)
       [_delegate webController:self titleDidChange:title];
     // If the controller handles title change notification, route those to the
     // delegate.
-    if ([_nativeController respondsToSelector:@selector(setDelegate:)]) {
-      [_nativeController setDelegate:self];
+    if ([self.nativeController respondsToSelector:@selector(setDelegate:)]) {
+      [self.nativeController setDelegate:self];
     }
   }
 }
@@ -1518,7 +1523,7 @@
 - (void)loadCurrentURL {
   // If the content view doesn't exist, the tab has either been evicted, or
   // never displayed. Bail, and let the URL be loaded when the tab is shown.
-  if (!_containerView)
+  if (!self.containerView)
     return;
 
   // Reset current WebUI if one exists.
@@ -1535,8 +1540,8 @@
     [self abortLoad];
 
   DCHECK(!_isHalted);
-  // Remove the interstitial before doing anything else.
-  [self clearInterstitials];
+  // Remove the transient content view.
+  [self clearTransientContentView];
 
   const GURL currentURL = [self currentNavigationURL];
   // If it's a chrome URL, but not a native one, create the WebUI instance.
@@ -1566,16 +1571,17 @@
 }
 
 - (void)triggerPendingLoad {
-  if (!_containerView) {
+  if (!self.containerView) {
     DCHECK(!_isBeingDestroyed);
     // Create the top-level parent view, which will contain the content (whether
     // native or web). Note, this needs to be created with a non-zero size
     // to allow for (native) subviews with autosize constraints to be correctly
     // processed.
     _containerView.reset([[CRWWebControllerContainerView alloc]
-        initWithFrame:[[UIScreen mainScreen] bounds]]);
-    [_containerView addGestureRecognizer:[self touchTrackingRecognizer]];
-    [_containerView setAccessibilityIdentifier:web::kContainerViewID];
+        initWithContentViewProxy:_webViewProxy]);
+    self.containerView.frame = [[UIScreen mainScreen] bounds];
+    [self.containerView addGestureRecognizer:[self touchTrackingRecognizer]];
+    [self.containerView setAccessibilityIdentifier:web::kContainerViewID];
     // Is |currentUrl| a web scheme or native chrome scheme.
     BOOL isChromeScheme =
         web::GetWebClient()->IsAppSpecificURL([self currentNavigationURL]);
@@ -1622,7 +1628,7 @@
     // This ensures state processing and delegate calls are consistent.
     [self loadCurrentURL];
   } else {
-    [_nativeController reload];
+    [self.nativeController reload];
   }
 }
 
@@ -1885,7 +1891,7 @@
     return;
   [_webViewToolbars addObject:toolbarView];
   if (self.webView)
-    [_containerView addToolbar:toolbarView];
+    [self.containerView addToolbar:toolbarView];
 }
 
 - (void)removeToolbarViewFromWebView:(UIView*)toolbarView {
@@ -1893,7 +1899,7 @@
     return;
   [_webViewToolbars removeObject:toolbarView];
   if (self.webView)
-    [_containerView removeToolbar:toolbarView];
+    [self.containerView removeToolbar:toolbarView];
 }
 
 - (CRWJSInjectionReceiver*)jsInjectionReceiver {
@@ -2567,18 +2573,17 @@
 #pragma mark -
 
 - (BOOL)wantsKeyboardShield {
-  if (_nativeController &&
-      [_nativeController respondsToSelector:@selector(wantsKeyboardShield)]) {
-    return [_nativeController wantsKeyboardShield];
+  if ([self.nativeController
+          respondsToSelector:@selector(wantsKeyboardShield)]) {
+    return [self.nativeController wantsKeyboardShield];
   }
   return YES;
 }
 
 - (BOOL)wantsLocationBarHintText {
-  if (_nativeController &&
-      [_nativeController
+  if ([self.nativeController
           respondsToSelector:@selector(wantsLocationBarHintText)]) {
-    return [_nativeController wantsLocationBarHintText];
+    return [self.nativeController wantsLocationBarHintText];
   }
   return YES;
 }
@@ -2632,18 +2637,16 @@
 }
 
 - (void)wasShown {
-  if (_nativeController &&
-      [_nativeController respondsToSelector:@selector(wasShown)]) {
-    [_nativeController wasShown];
+  if ([self.nativeController respondsToSelector:@selector(wasShown)]) {
+    [self.nativeController wasShown];
   }
 }
 
 - (void)wasHidden {
   if (_isHalted)
     return;
-  if (_nativeController &&
-      [_nativeController respondsToSelector:@selector(wasHidden)]) {
-    [_nativeController wasHidden];
+  if ([self.nativeController respondsToSelector:@selector(wasHidden)]) {
+    [self.nativeController wasHidden];
   }
 }
 
@@ -3183,7 +3186,7 @@
       setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
                           UIViewAutoresizingFlexibleHeight];
   [_placeholderOverlayView setContentMode:UIViewContentModeScaleAspectFill];
-  [_containerView addSubview:_placeholderOverlayView];
+  [self.containerView addSubview:_placeholderOverlayView];
 
   id callback = ^(UIImage* image) {
     [_placeholderOverlayView setImage:image];
@@ -3223,7 +3226,7 @@
 
   // If we were showing the preview, remove it.
   if (!_overlayPreviewMode && _placeholderOverlayView) {
-    _containerView.reset();
+    [self resetContainerView];
     // Reset |_placeholderOverlayView| directly instead of calling
     // -removePlaceholderOverlay, which removes |_placeholderOverlayView| in an
     // animation.
@@ -3601,7 +3604,7 @@
 #pragma mark Fullscreen
 
 - (CGRect)visibleFrame {
-  CGRect frame = [_containerView bounds];
+  CGRect frame = self.containerView.bounds;
   CGFloat headerHeight = [self headerHeight];
   frame.origin.y = headerHeight;
   frame.size.height -= headerHeight;
@@ -3640,25 +3643,39 @@
 
 - (BOOL)shouldOpenExternalURLRequest:(NSURLRequest*)request
                          targetFrame:(const web::FrameInfo*)targetFrame {
-  // Prevent subrequests from opening an external URL if the main document URL
-  // has changed since the last user interaction.
+  // If targetFrame information is not provided, the request originated from the
+  // main frame if (a) the request's URL matches the request's main document URL
+  // or (b) if the current pending entry matches the request's main document
+  // URL, as this is a redirect from an in-progress main frame load.
   BOOL isMainFrame = targetFrame
                          ? targetFrame->is_main_frame
                          : [request.URL isEqual:request.mainDocumentURL];
-  BOOL documentChangedAfterUserInteraction =
+  if (!targetFrame && !isMainFrame) {
+    web::NavigationItem* pendingItem =
+        [self webStateImpl]->GetNavigationManager()->GetPendingItem();
+    if (pendingItem) {
+      isMainFrame =
+          pendingItem->GetURL() == net::GURLWithNSURL(request.mainDocumentURL);
+    }
+  }
+
+  // If the request's main document URL differs from that at the time of the
+  // last user interaction, then the page has changed since the user last
+  // interacted.
+  BOOL userHasInteractedWithCurrentPage =
       _lastUserInteraction &&
-      net::GURLWithNSURL(request.mainDocumentURL) !=
+      net::GURLWithNSURL(request.mainDocumentURL) ==
           _lastUserInteraction->main_document_url;
-  if (!isMainFrame && documentChangedAfterUserInteraction)
+
+  // Prevent subframe requests from opening an external URL if the user has not
+  // interacted with the page.
+  if (!isMainFrame && !userHasInteractedWithCurrentPage)
     return NO;
 
   GURL requestURL = net::GURLWithNSURL(request.URL);
   return [_delegate respondsToSelector:@selector(webController:
-                                           shouldOpenExternalURL:
-                                               userIsInteracting:)] &&
-         [_delegate webController:self
-             shouldOpenExternalURL:requestURL
-                 userIsInteracting:[self userIsInteracting]];
+                                           shouldOpenExternalURL:)] &&
+         [_delegate webController:self shouldOpenExternalURL:requestURL];
 }
 
 - (BOOL)urlTriggersNativeAppLaunch:(const GURL&)url
@@ -3711,8 +3728,8 @@
 }
 
 - (void)loadHTML:(NSString*)html forURL:(const GURL&)url {
-  // Remove the interstitial before doing anything else.
-  [self clearInterstitials];
+  // Remove the transient content view.
+  [self clearTransientContentView];
 
   DLOG_IF(WARNING, !self.webView)
       << "self.webView null while trying to load HTML";
@@ -3733,7 +3750,7 @@
   [self abortLoad];
   // If discarding the non-committed entries results in an app-specific URL,
   // reload it in its native view.
-  if (!_nativeController &&
+  if (!self.nativeController &&
       [self shouldLoadURLInNativeView:[self currentNavigationURL]]) {
     [self loadCurrentURLInNativeView];
   }
@@ -3748,17 +3765,16 @@
 #pragma mark -
 #pragma mark Testing-Only Methods
 
-- (void)injectWebView:(id)webView {
+- (void)injectWebViewContentView:(id)webViewContentView {
   [self removeWebViewAllowingCachedReconstruction:NO];
 
   _lastRegisteredRequestURL = _defaultURL;
-  CHECK([webView respondsToSelector:@selector(scrollView)]);
-  [_webViewProxy setWebView:webView
-                 scrollView:[static_cast<id>(webView) scrollView]];
+  [self.containerView displayWebViewContentView:webViewContentView];
 }
 
-- (void)resetInjectedWebView {
+- (void)resetInjectedWebViewContentView {
   [self resetWebView];
+  [self resetContainerView];
 }
 
 - (void)addObserver:(id<CRWWebControllerObserver>)observer {
diff --git a/ios/web/web_state/ui/crw_web_controller_container_view.h b/ios/web/web_state/ui/crw_web_controller_container_view.h
index 7eb61f1..bac9967 100644
--- a/ios/web/web_state/ui/crw_web_controller_container_view.h
+++ b/ios/web/web_state/ui/crw_web_controller_container_view.h
@@ -7,10 +7,55 @@
 
 #import <UIKit/UIKit.h>
 
+#include "ios/web/public/web_state/ui/crw_content_view.h"
+
+@class CRWWebViewContentView;
+@class CRWWebViewProxyImpl;
+@protocol CRWNativeContent;
+
 // Container view class that manages the display of content within
 // CRWWebController.
 @interface CRWWebControllerContainerView : UIView
 
+#pragma mark Content Views
+// The web view content view being displayed.
+@property(nonatomic, retain, readonly)
+    CRWWebViewContentView* webViewContentView;
+// The native controller whose content is being displayed.
+@property(nonatomic, retain, readonly) id<CRWNativeContent> nativeController;
+// The currently displayed transient content view.
+@property(nonatomic, retain, readonly) CRWContentView* transientContentView;
+
+// Designated initializer.  |proxy|'s content view will be updated as different
+// content is added to the container.
+- (instancetype)initWithContentViewProxy:(CRWWebViewProxyImpl*)proxy
+    NS_DESIGNATED_INITIALIZER;
+
+// CRWWebControllerContainerView should be initialized via
+// |-initWithContentViewProxy:|.
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
+
+// Returns YES if the container view is currently displaying content.
+- (BOOL)isViewAlive;
+
+// Removes all subviews and resets state to default.
+- (void)resetContent;
+
+// Replaces the currently displayed content with |webViewContentView|.
+- (void)displayWebViewContentView:(CRWWebViewContentView*)webViewContentView;
+
+// Replaces the currently displayed content with |nativeController|'s view.
+- (void)displayNativeContent:(id<CRWNativeContent>)nativeController;
+
+// Adds |transientContentView| as a subview above previously displayed content.
+- (void)displayTransientContent:(CRWContentView*)transientContentView;
+
+// Removes the transient content view, if one is displayed.
+- (void)clearTransientContentView;
+
+#pragma mark Toolbars
+
 // |toolbar| will be resized to the container width, bottom-aligned, and added
 // as the topmost subview.
 - (void)addToolbar:(UIView*)toolbar;
diff --git a/ios/web/web_state/ui/crw_web_controller_container_view.mm b/ios/web/web_state/ui/crw_web_controller_container_view.mm
index e54e303..f7c4849 100644
--- a/ios/web/web_state/ui/crw_web_controller_container_view.mm
+++ b/ios/web/web_state/ui/crw_web_controller_container_view.mm
@@ -4,8 +4,13 @@
 
 #import "ios/web/web_state/ui/crw_web_controller_container_view.h"
 
+#include "base/ios/weak_nsobject.h"
 #include "base/logging.h"
 #include "base/mac/scoped_nsobject.h"
+#import "ios/web/public/web_state/ui/crw_content_view.h"
+#import "ios/web/public/web_state/ui/crw_native_content.h"
+#import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
+#import "ios/web/web_state/crw_web_view_proxy_impl.h"
 
 #pragma mark - CRWToolbarContainerView
 
@@ -84,10 +89,22 @@
 #pragma mark - CRWWebControllerContainerView
 
 @interface CRWWebControllerContainerView () {
-  // Backing object for |self.toolbarContainerView|.
+  // The proxy for the content added to the container.  It is owned by the web
+  // controller.
+  base::WeakNSObject<CRWWebViewProxyImpl> _webViewProxy;
+  // Backing objects for corresponding properties.
+  base::scoped_nsobject<CRWWebViewContentView> _webViewContentView;
+  base::scoped_nsprotocol<id<CRWNativeContent>> _nativeController;
+  base::scoped_nsobject<CRWContentView> _transientContentView;
   base::scoped_nsobject<CRWToolbarContainerView> _toolbarContainerView;
 }
 
+// Redefine properties as readwrite.
+@property(nonatomic, retain, readwrite)
+    CRWWebViewContentView* webViewContentView;
+@property(nonatomic, retain, readwrite) id<CRWNativeContent> nativeController;
+@property(nonatomic, retain, readwrite) CRWContentView* transientContentView;
+
 // Container view that displays any added toolbars.  It is always the top-most
 // subview, and is bottom aligned with the CRWWebControllerContainerView.
 @property(nonatomic, retain, readonly)
@@ -97,9 +114,11 @@
 
 @implementation CRWWebControllerContainerView
 
-- (instancetype)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
+- (instancetype)initWithContentViewProxy:(CRWWebViewProxyImpl*)proxy {
+  self = [super initWithFrame:CGRectZero];
   if (self) {
+    DCHECK(proxy);
+    _webViewProxy.reset(proxy);
     self.backgroundColor = [UIColor whiteColor];
     self.autoresizingMask =
         UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
@@ -107,13 +126,57 @@
   return self;
 }
 
-- (instancetype)init {
-  NOTREACHED();
-  return nil;
+- (void)dealloc {
+  [_webViewProxy setContentView:nil];
+  [super dealloc];
 }
 
 #pragma mark Accessors
 
+- (CRWWebViewContentView*)webViewContentView {
+  return _webViewContentView.get();
+}
+
+- (void)setWebViewContentView:(CRWWebViewContentView*)webViewContentView {
+  if (![_webViewContentView isEqual:webViewContentView]) {
+    [_webViewContentView removeFromSuperview];
+    _webViewContentView.reset([webViewContentView retain]);
+    [self addSubview:_webViewContentView];
+  }
+}
+
+- (id<CRWNativeContent>)nativeController {
+  return _nativeController.get();
+}
+
+- (void)setNativeController:(id<CRWNativeContent>)nativeController {
+  if (![_nativeController isEqual:nativeController]) {
+    // TODO(kkhorimoto): This line isn't strictly necessary since all native
+    // controllers currently inherit from NativeContentController, which removes
+    // its view upon deallocation.  Consider moving NativeContentController into
+    // web/ so this behavior can be depended upon from within web/ without
+    // making assumptions about chrome/ code.
+    base::WeakNSProtocol<id> oldController(_nativeController);
+    [[_nativeController view] removeFromSuperview];
+    _nativeController.reset([nativeController retain]);
+    [self addSubview:[_nativeController view]];
+    [[_nativeController view] setNeedsUpdateConstraints];
+    DCHECK(!oldController);
+  }
+}
+
+- (CRWContentView*)transientContentView {
+  return _transientContentView.get();
+}
+
+- (void)setTransientContentView:(CRWContentView*)transientContentView {
+  if (![_transientContentView isEqual:transientContentView]) {
+    [_transientContentView removeFromSuperview];
+    _transientContentView.reset([transientContentView retain]);
+    [self addSubview:_transientContentView];
+  }
+}
+
 - (void)setToolbarContainerView:(CRWToolbarContainerView*)toolbarContainerView {
   if (![_toolbarContainerView isEqual:toolbarContainerView]) {
     [_toolbarContainerView removeFromSuperview];
@@ -131,6 +194,12 @@
 - (void)layoutSubviews {
   [super layoutSubviews];
 
+  // Resize displayed content to the container's bounds.
+  self.webViewContentView.frame = self.bounds;
+  [self.nativeController view].frame = self.bounds;
+  self.transientContentView.frame = self.bounds;
+
+  // Bottom align the toolbars with the bottom of the container.
   if (self.toolbarContainerView) {
     [self bringSubviewToFront:self.toolbarContainerView];
     CGSize toolbarContainerSize =
@@ -142,6 +211,51 @@
   }
 }
 
+- (BOOL)isViewAlive {
+  return self.webViewContentView || self.transientContentView ||
+         [self.nativeController isViewAlive];
+}
+
+#pragma mark Content Setters
+
+- (void)resetContent {
+  self.webViewContentView = nil;
+  self.nativeController = nil;
+  self.transientContentView = nil;
+  [self removeAllToolbars];
+  [_webViewProxy setContentView:nil];
+}
+
+- (void)displayWebViewContentView:(CRWWebViewContentView*)webViewContentView {
+  DCHECK(webViewContentView);
+  self.webViewContentView = webViewContentView;
+  self.nativeController = nil;
+  self.transientContentView = nil;
+  [_webViewProxy setContentView:self.webViewContentView];
+  [self setNeedsLayout];
+}
+
+- (void)displayNativeContent:(id<CRWNativeContent>)nativeController {
+  DCHECK(nativeController);
+  self.webViewContentView = nil;
+  self.nativeController = nativeController;
+  self.transientContentView = nil;
+  [_webViewProxy setContentView:nil];
+  [self setNeedsLayout];
+}
+
+- (void)displayTransientContent:(CRWContentView*)transientContentView {
+  DCHECK(transientContentView);
+  self.transientContentView = transientContentView;
+  [_webViewProxy setContentView:self.transientContentView];
+  [self setNeedsLayout];
+}
+
+- (void)clearTransientContentView {
+  self.transientContentView = nil;
+  [_webViewProxy setContentView:self.webViewContentView];
+}
+
 #pragma mark Toolbars
 
 - (void)addToolbar:(UIView*)toolbar {
diff --git a/ios/web/web_state/ui/crw_web_controller_container_view_unittest.mm b/ios/web/web_state/ui/crw_web_controller_container_view_unittest.mm
index 69f49c30..6afb6b16 100644
--- a/ios/web/web_state/ui/crw_web_controller_container_view_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_container_view_unittest.mm
@@ -3,10 +3,13 @@
 // found in the LICENSE file.
 
 #import "base/mac/scoped_nsobject.h"
+#import "ios/web/web_state/crw_web_view_proxy_impl.h"
 #import "ios/web/web_state/ui/crw_web_controller_container_view.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
+#include "third_party/ocmock/gtest_support.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
 
 namespace {
 // The frame of CRWWebControllerContainerViewTest's |container_view_|.
@@ -31,10 +34,15 @@
  protected:
   void SetUp() override {
     PlatformTest::SetUp();
+    mock_web_view_proxy_.reset(
+        [[OCMockObject niceMockForClass:[CRWWebViewProxyImpl class]] retain]);
     container_view_.reset([[CRWWebControllerContainerView alloc]
-        initWithFrame:kContainerViewFrame]);
+        initWithContentViewProxy:mock_web_view_proxy_]);
+    [container_view_ setFrame:kContainerViewFrame];
   }
 
+  // The web view proxy object (required for designated initializer).
+  base::scoped_nsobject<id> mock_web_view_proxy_;
   // The container view being tested.
   base::scoped_nsobject<CRWWebControllerContainerView> container_view_;
 };
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index b7b97cf8..4e7a4ed 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -18,8 +18,11 @@
 #include "ios/web/navigation/navigation_item_impl.h"
 #import "ios/web/navigation/navigation_manager_impl.h"
 #include "ios/web/public/referrer.h"
+#include "ios/web/public/test/test_web_view_content_view.h"
 #include "ios/web/public/test/web_test_util.h"
 #import "ios/web/public/web_state/crw_web_controller_observer.h"
+#import "ios/web/public/web_state/ui/crw_content_view.h"
+#import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
 #include "ios/web/public/web_state/url_verification_constants.h"
 #include "ios/web/test/web_test.h"
 #import "ios/web/test/wk_web_view_crash_utils.h"
@@ -27,6 +30,7 @@
 #import "ios/web/web_state/js/crw_js_invoke_parameter_queue.h"
 #import "ios/web/web_state/ui/crw_ui_web_view_web_controller.h"
 #import "ios/web/web_state/ui/crw_web_controller+protected.h"
+#import "ios/web/web_state/ui/crw_web_controller_container_view.h"
 #import "ios/web/web_state/web_state_impl.h"
 #import "net/base/mac/url_conversions.h"
 #include "net/ssl/ssl_info.h"
@@ -46,6 +50,7 @@
 
 @interface CRWWebController (PrivateAPI)
 @property(nonatomic, readwrite) web::PageDisplayState pageDisplayState;
+@property(nonatomic, readonly) CRWWebControllerContainerView* containerView;
 - (void)setJsMessageQueueThrottled:(BOOL)throttle;
 - (void)removeDocumentLoadCommandsFromQueue;
 - (GURL)updateURLForHistoryNavigationFromURL:(const GURL&)startURL
@@ -293,7 +298,10 @@
     mockDelegate_.reset([[MockInteractionLoader alloc]
         initWithRepresentedObject:originalMockDelegate]);
     [WebTestT::webController_ setDelegate:mockDelegate_];
-    [WebTestT::webController_ injectWebView:(UIView*)mockWebView_];
+    base::scoped_nsobject<TestWebViewContentView> webViewContentView(
+        [[TestWebViewContentView alloc] initWithMockWebView:mockWebView_
+                                                 scrollView:mockScrollView_]);
+    [WebTestT::webController_ injectWebViewContentView:webViewContentView];
 
     NavigationManagerImpl& navigationManager =
         [WebTestT::webController_ webStateImpl]->GetNavigationManagerImpl();
@@ -315,7 +323,7 @@
     EXPECT_OCMOCK_VERIFY(mockDelegate_);
     EXPECT_OCMOCK_VERIFY(mockChildWebController_.get());
     EXPECT_OCMOCK_VERIFY(mockWebView_);
-    [WebTestT::webController_ resetInjectedWebView];
+    [WebTestT::webController_ resetInjectedWebViewContentView];
     [WebTestT::webController_ setDelegate:nil];
     WebTestT::TearDown();
   }
@@ -1224,8 +1232,8 @@
            @"</body></html>");
 
   // Get the webview.
-  UIWebView* webView =
-      (UIWebView*)[[[webController_ view] subviews] objectAtIndex:0];
+  UIWebView* webView = static_cast<UIWebView*>(
+      [webController_ containerView].webViewContentView.webView);
   EXPECT_TRUE(webView);
 
   // Create the window and add the webview.
@@ -1473,7 +1481,11 @@
     CR_TEST_REQUIRES_WK_WEB_VIEW();
     WKWebViewWebTest::SetUp();
     webView_.reset(web::CreateTerminatedWKWebView());
-    [webController_ injectWebView:webView_];
+    base::scoped_nsobject<TestWebViewContentView> webViewContentView(
+        [[TestWebViewContentView alloc]
+            initWithMockWebView:webView_
+                     scrollView:[webView_ scrollView]]);
+    [webController_ injectWebViewContentView:webViewContentView];
   }
   base::scoped_nsobject<WKWebView> webView_;
 };
diff --git a/ios/web/web_state/ui/crw_web_view_content_view.mm b/ios/web/web_state/ui/crw_web_view_content_view.mm
new file mode 100644
index 0000000..e3c085e
--- /dev/null
+++ b/ios/web/web_state/ui/crw_web_view_content_view.mm
@@ -0,0 +1,56 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
+
+#include "base/logging.h"
+#include "base/mac/scoped_nsobject.h"
+
+@interface CRWWebViewContentView () {
+  // The web view being shown.
+  base::scoped_nsobject<UIView> _webView;
+  // The web view's scroll view.
+  base::scoped_nsobject<UIScrollView> _scrollView;
+}
+
+@end
+
+@implementation CRWWebViewContentView
+
+- (instancetype)initWithWebView:(UIView*)webView
+                     scrollView:(UIScrollView*)scrollView {
+  self = [super initWithFrame:CGRectZero];
+  if (self) {
+    DCHECK(webView);
+    DCHECK(scrollView);
+    DCHECK([scrollView isDescendantOfView:webView]);
+    _webView.reset([webView retain]);
+    _scrollView.reset([scrollView retain]);
+    [self addSubview:_webView];
+  }
+  return self;
+}
+
+#pragma mark Accessors
+
+- (UIScrollView*)scrollView {
+  return _scrollView.get();
+}
+
+- (UIView*)webView {
+  return _webView.get();
+}
+
+#pragma mark Layout
+
+- (void)layoutSubviews {
+  [super layoutSubviews];
+  self.webView.frame = self.bounds;
+}
+
+- (BOOL)isViewAlive {
+  return YES;
+}
+
+@end
diff --git a/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm b/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm
index 87b2713a3..c77f15b 100644
--- a/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm
+++ b/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm
@@ -19,6 +19,7 @@
 #include "ios/web/public/web_client.h"
 #import "ios/web/public/web_state/js/crw_js_injection_manager.h"
 #import "ios/web/public/web_state/ui/crw_native_content_provider.h"
+#import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
 #import "ios/web/ui_web_view_util.h"
 #include "ios/web/web_state/blocked_popup_info.h"
 #include "ios/web/web_state/frame_info.h"
@@ -268,9 +269,9 @@
 #pragma mark -
 #pragma mark Testing-Only Methods
 
-- (void)injectWebView:(id)webView {
-  [super injectWebView:webView];
-  [self setWebView:webView];
+- (void)injectWebViewContentView:(CRWWebViewContentView*)webViewContentView {
+  [super injectWebViewContentView:webViewContentView];
+  [self setWebView:static_cast<WKWebView*>(webViewContentView.webView)];
 }
 
 #pragma mark - Protected property implementations
diff --git a/ios/web/web_state/ui/web_view_js_utils.mm b/ios/web/web_state/ui/web_view_js_utils.mm
index 7bc4ecb..8f7b202 100644
--- a/ios/web/web_state/ui/web_view_js_utils.mm
+++ b/ios/web/web_state/ui/web_view_js_utils.mm
@@ -28,6 +28,9 @@
   if (result_type == CFBooleanGetTypeID())
     return [result boolValue] ? @"true" : @"false";
 
+  if (result_type == CFNullGetTypeID())
+    return @"";
+
   // TODO(stuartmorgan): Stringify other types.
   NOTREACHED();
   return nil;
diff --git a/ios/web/web_state/ui/web_view_js_utils_unittest.mm b/ios/web/web_state/ui/web_view_js_utils_unittest.mm
index 59caedf..80e608c0 100644
--- a/ios/web/web_state/ui/web_view_js_utils_unittest.mm
+++ b/ios/web/web_state/ui/web_view_js_utils_unittest.mm
@@ -99,4 +99,9 @@
   EXPECT_NSEQ(@"false", EvaluateJavaScript(this->web_view_, @"false"));
 }
 
+// Tests that a script with null result correctly evaluates to empty string.
+WEB_TEST_F(UIWebViewJSUtilsTest, WKWebViewJSUtilsTest, NullEvaluation) {
+  EXPECT_NSEQ(@"", EvaluateJavaScript(this->web_view_, @"null"));
+}
+
 }  // namespace
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index ba236ce0..d5386b2 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -222,6 +222,7 @@
   const GURL& GetVisibleURL() const override;
   const GURL& GetLastCommittedURL() const override;
   GURL GetCurrentURL(URLVerificationTrustLevel* trust_level) const override;
+  void ShowTransientContentView(CRWContentView* content_view) override;
   bool IsShowingWebInterstitial() const override;
   WebInterstitial* GetWebInterstitial() const override;
   void AddScriptCommandCallback(const ScriptCommandCallback& callback,
@@ -234,15 +235,11 @@
                     bool bypass_cache,
                     const ImageDownloadCallback& callback) override;
 
-  // Called before navigation events to clear the currently-displayed
-  // WebInterstitials.
-  void ClearWebInterstitialForNavigation();
-
   // Adds |interstitial|'s view to the web controller's content view.
-  void DisplayWebInterstitial(WebInterstitialImpl* interstitial);
+  void ShowWebInterstitial(WebInterstitialImpl* interstitial);
 
-  // Called to dismiss the currently-displayed WebInterstitial.
-  void DismissWebInterstitial();
+  // Called to dismiss the currently-displayed transient content view.
+  void ClearTransientContentView();
 
   // NavigationManagerDelegate:
   void NavigateToPendingEntry() override;
@@ -294,7 +291,7 @@
   std::string mime_type_;
   std::string content_language_header_;
 
-  // Weak pointer to the he interstital page being displayed, if any.
+  // Weak pointer to the interstitial page being displayed, if any.
   WebInterstitialImpl* interstitial_;
 
   // Returned by reference.
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 7450809..fd6aa9a 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -15,8 +15,10 @@
 #include "ios/web/public/url_util.h"
 #include "ios/web/public/web_client.h"
 #include "ios/web/public/web_state/credential.h"
+#include "ios/web/public/web_state/ui/crw_content_view.h"
 #include "ios/web/public/web_state/web_state_observer.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
+#import "ios/web/web_state/ui/crw_web_controller_container_view.h"
 #include "ios/web/web_state/web_state_facade_delegate.h"
 #import "ios/web/webui/web_ui_ios_controller_factory_registry.h"
 #import "ios/web/webui/web_ui_ios_impl.h"
@@ -265,6 +267,14 @@
   return empty_string16_;
 }
 
+void WebStateImpl::ShowTransientContentView(CRWContentView* content_view) {
+  DCHECK(Configured());
+  DCHECK(content_view);
+  DCHECK(content_view.scrollView);
+  DCHECK([content_view.scrollView isDescendantOfView:content_view]);
+  [web_controller_ showTransientContentView:content_view];
+}
+
 bool WebStateImpl::IsShowingWebInterstitial() const {
   // Technically we could have |interstitial_| set but its view isn't
   // being displayed, but there's no code path where that could occur.
@@ -320,16 +330,15 @@
   content_language_header_ = content_language;
 }
 
-void WebStateImpl::ClearWebInterstitialForNavigation() {
+void WebStateImpl::ShowWebInterstitial(WebInterstitialImpl* interstitial) {
+  DCHECK(Configured());
+  DCHECK(interstitial);
+  interstitial_ = interstitial;
+  ShowTransientContentView(interstitial_->GetContentView());
+}
+
+void WebStateImpl::ClearTransientContentView() {
   if (interstitial_) {
-    // DontProceed() dismisses the interstitial page in the same way as if it
-    // was closed by user action. Clearing interstitial_ early makes
-    // IsShowingWebInterstitial() return false so the code that is triggered in
-    // DontProceed knows that the interstitial page is not visible anymore.
-    WebInterstitialImpl* interstitial = interstitial_;
-    DismissWebInterstitial();
-    // In this case, DontProceed() may not remove an unsafe page from the nav
-    // history.
     CRWSessionController* sessionController =
         navigation_manager_.GetSessionController();
     web::NavigationItem* currentItem =
@@ -342,27 +351,17 @@
       [sessionController goBack];
     }
     [sessionController discardNonCommittedEntries];
-    interstitial->DontProceed();
-  }
-}
-
-void WebStateImpl::DisplayWebInterstitial(WebInterstitialImpl* interstitial) {
-  DCHECK(Configured());
-  DCHECK(interstitial);
-  interstitial_ = interstitial;
-  CGSize content_view_size = [web_controller_ view].bounds.size;
-  interstitial_->SetSize(
-      gfx::Size(content_view_size.width, content_view_size.height));
-  [web_controller_ displayInterstitialView:interstitial_->GetView()
-                            withScrollView:interstitial_->GetScrollView()];
-}
-
-void WebStateImpl::DismissWebInterstitial() {
-  if (interstitial_) {
-    FOR_EACH_OBSERVER(WebStateObserver, observers_, InsterstitialDismissed());
-    [interstitial_->GetView() removeFromSuperview];
+    // Store the currently displayed interstitial in a local variable and reset
+    // |interstitial_| early.  This is to prevent an infinite loop, as
+    // |DontProceed()| internally calls |ClearTransientContentView()|.
+    web::WebInterstitial* interstitial = interstitial_;
     interstitial_ = nullptr;
+    interstitial->DontProceed();
+    // Don't access |interstitial| after calling |DontProceed()|, as it triggers
+    // deletion.
+    FOR_EACH_OBSERVER(WebStateObserver, observers_, InsterstitialDismissed());
   }
+  [web_controller_ clearTransientContentView];
 }
 
 WebUIIOS* WebStateImpl::CreateWebUIIOS(const GURL& url) {
@@ -454,7 +453,7 @@
 
 void WebStateImpl::OpenURL(const WebState::OpenURLParams& params) {
   DCHECK(Configured());
-  ClearWebInterstitialForNavigation();
+  ClearTransientContentView();
   [[web_controller_ delegate] openURLWithParams:params];
 }
 
diff --git a/ipc/mojo/BUILD.gn b/ipc/mojo/BUILD.gn
index 50241fe..d801a7b 100644
--- a/ipc/mojo/BUILD.gn
+++ b/ipc/mojo/BUILD.gn
@@ -18,8 +18,6 @@
     "client_channel.mojom",
     "ipc_channel_mojo.cc",
     "ipc_channel_mojo.h",
-    "ipc_channel_mojo_host.cc",
-    "ipc_channel_mojo_host.h",
     "ipc_message_pipe_reader.cc",
     "ipc_message_pipe_reader.h",
     "ipc_mojo_bootstrap.cc",
diff --git a/ipc/mojo/ipc_channel_mojo.cc b/ipc/mojo/ipc_channel_mojo.cc
index 4886a125..4b0916d 100644
--- a/ipc/mojo/ipc_channel_mojo.cc
+++ b/ipc/mojo/ipc_channel_mojo.cc
@@ -28,13 +28,11 @@
 
 class MojoChannelFactory : public ChannelFactory {
  public:
-  MojoChannelFactory(ChannelMojo::Delegate* delegate,
-                     scoped_refptr<base::TaskRunner> io_runner,
+  MojoChannelFactory(scoped_refptr<base::TaskRunner> io_runner,
                      ChannelHandle channel_handle,
                      Channel::Mode mode,
                      AttachmentBroker* broker)
-      : delegate_(delegate),
-        io_runner_(io_runner),
+      : io_runner_(io_runner),
         channel_handle_(channel_handle),
         mode_(mode),
         broker_(broker) {}
@@ -44,12 +42,11 @@
   }
 
   scoped_ptr<Channel> BuildChannel(Listener* listener) override {
-    return ChannelMojo::Create(delegate_, io_runner_, channel_handle_, mode_,
-                               listener, broker_);
+    return ChannelMojo::Create(io_runner_, channel_handle_, mode_, listener,
+                               broker_);
   }
 
  private:
-  ChannelMojo::Delegate* delegate_;
   scoped_refptr<base::TaskRunner> io_runner_;
   ChannelHandle channel_handle_;
   Channel::Mode mode_;
@@ -62,8 +59,7 @@
                           public ClientChannel,
                           public mojo::ErrorHandler {
  public:
-  ClientChannelMojo(ChannelMojo::Delegate* delegate,
-                    scoped_refptr<base::TaskRunner> io_runner,
+  ClientChannelMojo(scoped_refptr<base::TaskRunner> io_runner,
                     const ChannelHandle& handle,
                     Listener* listener,
                     AttachmentBroker* broker);
@@ -88,17 +84,11 @@
   DISALLOW_COPY_AND_ASSIGN(ClientChannelMojo);
 };
 
-ClientChannelMojo::ClientChannelMojo(ChannelMojo::Delegate* delegate,
-                                     scoped_refptr<base::TaskRunner> io_runner,
+ClientChannelMojo::ClientChannelMojo(scoped_refptr<base::TaskRunner> io_runner,
                                      const ChannelHandle& handle,
                                      Listener* listener,
                                      AttachmentBroker* broker)
-    : ChannelMojo(delegate,
-                  io_runner,
-                  handle,
-                  Channel::MODE_CLIENT,
-                  listener,
-                  broker),
+    : ChannelMojo(io_runner, handle, Channel::MODE_CLIENT, listener, broker),
       binding_(this),
       weak_factory_(this) {
 }
@@ -132,8 +122,7 @@
 
 class ServerChannelMojo : public ChannelMojo, public mojo::ErrorHandler {
  public:
-  ServerChannelMojo(ChannelMojo::Delegate* delegate,
-                    scoped_refptr<base::TaskRunner> io_runner,
+  ServerChannelMojo(scoped_refptr<base::TaskRunner> io_runner,
                     const ChannelHandle& handle,
                     Listener* listener,
                     AttachmentBroker* broker);
@@ -160,17 +149,11 @@
   DISALLOW_COPY_AND_ASSIGN(ServerChannelMojo);
 };
 
-ServerChannelMojo::ServerChannelMojo(ChannelMojo::Delegate* delegate,
-                                     scoped_refptr<base::TaskRunner> io_runner,
+ServerChannelMojo::ServerChannelMojo(scoped_refptr<base::TaskRunner> io_runner,
                                      const ChannelHandle& handle,
                                      Listener* listener,
                                      AttachmentBroker* broker)
-    : ChannelMojo(delegate,
-                  io_runner,
-                  handle,
-                  Channel::MODE_SERVER,
-                  listener,
-                  broker),
+    : ChannelMojo(io_runner, handle, Channel::MODE_SERVER, listener, broker),
       weak_factory_(this) {
 }
 
@@ -262,7 +245,6 @@
 
 // static
 scoped_ptr<ChannelMojo> ChannelMojo::Create(
-    ChannelMojo::Delegate* delegate,
     scoped_refptr<base::TaskRunner> io_runner,
     const ChannelHandle& channel_handle,
     Mode mode,
@@ -270,11 +252,11 @@
     AttachmentBroker* broker) {
   switch (mode) {
     case Channel::MODE_CLIENT:
-      return make_scoped_ptr(new ClientChannelMojo(
-          delegate, io_runner, channel_handle, listener, broker));
+      return make_scoped_ptr(
+          new ClientChannelMojo(io_runner, channel_handle, listener, broker));
     case Channel::MODE_SERVER:
-      return make_scoped_ptr(new ServerChannelMojo(
-          delegate, io_runner, channel_handle, listener, broker));
+      return make_scoped_ptr(
+          new ServerChannelMojo(io_runner, channel_handle, listener, broker));
     default:
       NOTREACHED();
       return nullptr;
@@ -283,26 +265,23 @@
 
 // static
 scoped_ptr<ChannelFactory> ChannelMojo::CreateServerFactory(
-    ChannelMojo::Delegate* delegate,
     scoped_refptr<base::TaskRunner> io_runner,
     const ChannelHandle& channel_handle,
     AttachmentBroker* broker) {
-  return make_scoped_ptr(new MojoChannelFactory(
-      delegate, io_runner, channel_handle, Channel::MODE_SERVER, broker));
+  return make_scoped_ptr(new MojoChannelFactory(io_runner, channel_handle,
+                                                Channel::MODE_SERVER, broker));
 }
 
 // static
 scoped_ptr<ChannelFactory> ChannelMojo::CreateClientFactory(
-    ChannelMojo::Delegate* delegate,
     scoped_refptr<base::TaskRunner> io_runner,
     const ChannelHandle& channel_handle,
     AttachmentBroker* broker) {
-  return make_scoped_ptr(new MojoChannelFactory(
-      delegate, io_runner, channel_handle, Channel::MODE_CLIENT, broker));
+  return make_scoped_ptr(new MojoChannelFactory(io_runner, channel_handle,
+                                                Channel::MODE_CLIENT, broker));
 }
 
-ChannelMojo::ChannelMojo(ChannelMojo::Delegate* delegate,
-                         scoped_refptr<base::TaskRunner> io_runner,
+ChannelMojo::ChannelMojo(scoped_refptr<base::TaskRunner> io_runner,
                          const ChannelHandle& handle,
                          Mode mode,
                          Listener* listener,
@@ -317,11 +296,10 @@
   // ChannelMojo from a different thread.
   bootstrap_ = MojoBootstrap::Create(handle, mode, this, broker);
   if (io_runner == base::MessageLoop::current()->task_runner()) {
-    InitOnIOThread(delegate);
+    InitOnIOThread();
   } else {
-    io_runner->PostTask(FROM_HERE,
-                        base::Bind(&ChannelMojo::InitOnIOThread,
-                                   base::Unretained(this), delegate));
+    io_runner->PostTask(FROM_HERE, base::Bind(&ChannelMojo::InitOnIOThread,
+                                              base::Unretained(this)));
   }
 }
 
@@ -329,13 +307,9 @@
   Close();
 }
 
-void ChannelMojo::InitOnIOThread(ChannelMojo::Delegate* delegate) {
+void ChannelMojo::InitOnIOThread() {
   ipc_support_.reset(
       new ScopedIPCSupport(base::MessageLoop::current()->task_runner()));
-  if (!delegate)
-    return;
-  delegate_ = delegate->ToWeakPtr();
-  delegate_->OnChannelCreated(weak_factory_.GetWeakPtr());
 }
 
 void ChannelMojo::CreateMessagingPipe(
@@ -483,10 +457,6 @@
   return bootstrap_->GetSelfPID();
 }
 
-void ChannelMojo::OnClientLaunched(base::ProcessHandle handle) {
-  bootstrap_->OnClientLaunched(handle);
-}
-
 void ChannelMojo::OnMessageReceived(Message& message) {
   TRACE_EVENT2("ipc,toplevel", "ChannelMojo::OnMessageReceived",
                "class", IPC_MESSAGE_ID_CLASS(message.type()),
diff --git a/ipc/mojo/ipc_channel_mojo.h b/ipc/mojo/ipc_channel_mojo.h
index b84a6e39..6b7d2fc 100644
--- a/ipc/mojo/ipc_channel_mojo.h
+++ b/ipc/mojo/ipc_channel_mojo.h
@@ -57,21 +57,12 @@
       base::Callback<void(mojo::ScopedMessagePipeHandle,
                           mojo::embedder::ChannelInfo*)>;
 
-  class Delegate {
-   public:
-    virtual ~Delegate() {}
-    virtual base::WeakPtr<Delegate> ToWeakPtr() = 0;
-    virtual void OnChannelCreated(base::WeakPtr<ChannelMojo> channel) = 0;
-  };
-
   // True if ChannelMojo should be used regardless of the flag.
   static bool ShouldBeUsed();
 
   // Create ChannelMojo. A bootstrap channel is created as well.
-  // |host| must not be null for server channels.
   // |broker| must outlive the newly created channel.
   static scoped_ptr<ChannelMojo> Create(
-      Delegate* delegate,
       scoped_refptr<base::TaskRunner> io_runner,
       const ChannelHandle& channel_handle,
       Mode mode,
@@ -86,7 +77,6 @@
   // http://crbug.com/493414.
   // |broker| must outlive the factory and all channels it creates.
   static scoped_ptr<ChannelFactory> CreateServerFactory(
-      Delegate* delegate,
       scoped_refptr<base::TaskRunner> io_runner,
       const ChannelHandle& channel_handle,
       AttachmentBroker* broker = nullptr);
@@ -96,16 +86,12 @@
   // http://crbug.com/493414.
   // |broker| must outlive the factory and all channels it creates.
   static scoped_ptr<ChannelFactory> CreateClientFactory(
-      Delegate* delegate,
       scoped_refptr<base::TaskRunner> io_runner,
       const ChannelHandle& channel_handle,
       AttachmentBroker* broker = nullptr);
 
   ~ChannelMojo() override;
 
-  // ChannelMojoHost tells the client handle using this API.
-  void OnClientLaunched(base::ProcessHandle handle);
-
   // Channel implementation
   bool Connect() override;
   void Close() override;
@@ -137,8 +123,7 @@
   void OnPipeError(internal::MessagePipeReader* reader) override;
 
  protected:
-  ChannelMojo(Delegate* delegate,
-              scoped_refptr<base::TaskRunner> io_runner,
+  ChannelMojo(scoped_refptr<base::TaskRunner> io_runner,
               const ChannelHandle& channel_handle,
               Mode mode,
               Listener* listener,
@@ -166,7 +151,7 @@
   // notifications invoked by them.
   typedef internal::MessagePipeReader::DelayedDeleter ReaderDeleter;
 
-  void InitOnIOThread(ChannelMojo::Delegate* delegate);
+  void InitOnIOThread();
 
   static void CreateMessagingPipeOnIOThread(
       mojo::embedder::ScopedPlatformHandle handle,
@@ -177,7 +162,6 @@
                               mojo::embedder::ChannelInfo* channel_info);
 
   scoped_ptr<MojoBootstrap> bootstrap_;
-  base::WeakPtr<Delegate> delegate_;
   Mode mode_;
   Listener* listener_;
   base::ProcessId peer_pid_;
diff --git a/ipc/mojo/ipc_channel_mojo_host.cc b/ipc/mojo/ipc_channel_mojo_host.cc
deleted file mode 100644
index ac914ad..0000000
--- a/ipc/mojo/ipc_channel_mojo_host.cc
+++ /dev/null
@@ -1,120 +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 "ipc/mojo/ipc_channel_mojo_host.h"
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/single_thread_task_runner.h"
-#include "ipc/mojo/ipc_channel_mojo.h"
-
-namespace IPC {
-
-class ChannelMojoHost::ChannelDelegateTraits {
- public:
-  static void Destruct(const ChannelMojoHost::ChannelDelegate* ptr);
-};
-
-// The delete class lives on the IO thread to talk to ChannelMojo on
-// behalf of ChannelMojoHost.
-//
-// The object must be touched only on the IO thread.
-class ChannelMojoHost::ChannelDelegate
-    : public base::RefCountedThreadSafe<ChannelMojoHost::ChannelDelegate,
-                                        ChannelMojoHost::ChannelDelegateTraits>,
-      public ChannelMojo::Delegate {
- public:
-  explicit ChannelDelegate(
-      scoped_refptr<base::SequencedTaskRunner> io_task_runner);
-
-  // ChannelMojo::Delegate
-  base::WeakPtr<Delegate> ToWeakPtr() override;
-  void OnChannelCreated(base::WeakPtr<ChannelMojo> channel) override;
-
-  // Returns an weak ptr of ChannelDelegate instead of Delegate
-  base::WeakPtr<ChannelDelegate> GetWeakPtr();
-  void OnClientLaunched(base::ProcessHandle process);
-  void DeleteThisSoon() const;
-
- private:
-  friend class base::DeleteHelper<ChannelDelegate>;
-
-  ~ChannelDelegate() override;
-
-  scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
-  base::WeakPtr<ChannelMojo> channel_;
-  base::WeakPtrFactory<ChannelDelegate> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ChannelDelegate);
-};
-
-ChannelMojoHost::ChannelDelegate::ChannelDelegate(
-    scoped_refptr<base::SequencedTaskRunner> io_task_runner)
-    : io_task_runner_(io_task_runner), weak_factory_(this) {
-}
-
-ChannelMojoHost::ChannelDelegate::~ChannelDelegate() {
-}
-
-base::WeakPtr<ChannelMojo::Delegate>
-ChannelMojoHost::ChannelDelegate::ToWeakPtr() {
-  return weak_factory_.GetWeakPtr();
-}
-
-base::WeakPtr<ChannelMojoHost::ChannelDelegate>
-ChannelMojoHost::ChannelDelegate::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
-}
-
-void ChannelMojoHost::ChannelDelegate::OnChannelCreated(
-    base::WeakPtr<ChannelMojo> channel) {
-  DCHECK(!channel_);
-  channel_ = channel;
-}
-
-void ChannelMojoHost::ChannelDelegate::OnClientLaunched(
-    base::ProcessHandle process) {
-  if (channel_)
-    channel_->OnClientLaunched(process);
-}
-
-void ChannelMojoHost::ChannelDelegate::DeleteThisSoon() const {
-  io_task_runner_->DeleteSoon(FROM_HERE, this);
-}
-
-//
-// ChannelMojoHost
-//
-
-ChannelMojoHost::ChannelMojoHost(
-    scoped_refptr<base::SequencedTaskRunner> io_task_runner)
-    : io_task_runner_(io_task_runner),
-      channel_delegate_(new ChannelDelegate(io_task_runner)),
-      weak_factory_(this) {
-}
-
-ChannelMojoHost::~ChannelMojoHost() {
-}
-
-void ChannelMojoHost::OnClientLaunched(base::ProcessHandle process) {
-  if (io_task_runner_ == base::MessageLoop::current()->task_runner()) {
-    channel_delegate_->OnClientLaunched(process);
-  } else {
-    io_task_runner_->PostTask(FROM_HERE,
-                              base::Bind(&ChannelDelegate::OnClientLaunched,
-                                         channel_delegate_, process));
-  }
-}
-
-ChannelMojo::Delegate* ChannelMojoHost::channel_delegate() const {
-  return channel_delegate_.get();
-}
-
-// static
-void ChannelMojoHost::ChannelDelegateTraits::Destruct(
-    const ChannelMojoHost::ChannelDelegate* ptr) {
-  ptr->DeleteThisSoon();
-}
-
-}  // namespace IPC
diff --git a/ipc/mojo/ipc_channel_mojo_host.h b/ipc/mojo/ipc_channel_mojo_host.h
deleted file mode 100644
index db60b12..0000000
--- a/ipc/mojo/ipc_channel_mojo_host.h
+++ /dev/null
@@ -1,46 +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 IPC_IPC_CHANNEL_MOJO_HOST_H_
-#define IPC_IPC_CHANNEL_MOJO_HOST_H_
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/process/process_handle.h"
-#include "ipc/ipc_export.h"
-#include "ipc/mojo/ipc_channel_mojo.h"
-
-namespace base {
-class SequencedTaskRunner;
-}
-
-namespace IPC {
-
-// Through ChannelMojoHost, ChannelMojo gets extra information that
-// its client provides, including the child process's process handle. Every
-// server process that uses ChannelMojo must have a ChannelMojoHost
-// instance and call OnClientLaunched().
-class IPC_MOJO_EXPORT ChannelMojoHost {
- public:
-  explicit ChannelMojoHost(
-      scoped_refptr<base::SequencedTaskRunner> io_task_runner);
-  ~ChannelMojoHost();
-
-  void OnClientLaunched(base::ProcessHandle process);
-  ChannelMojo::Delegate* channel_delegate() const;
-
- private:
-  class ChannelDelegate;
-  class ChannelDelegateTraits;
-
-  const scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
-  scoped_refptr<ChannelDelegate> channel_delegate_;
-  base::WeakPtrFactory<ChannelMojoHost> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ChannelMojoHost);
-};
-
-}  // namespace IPC
-
-#endif  // IPC_IPC_CHANNEL_MOJO_HOST_H_
diff --git a/ipc/mojo/ipc_channel_mojo_unittest.cc b/ipc/mojo/ipc_channel_mojo_unittest.cc
index 17242bf12..cb5c459 100644
--- a/ipc/mojo/ipc_channel_mojo_unittest.cc
+++ b/ipc/mojo/ipc_channel_mojo_unittest.cc
@@ -17,7 +17,6 @@
 #include "ipc/ipc_message.h"
 #include "ipc/ipc_test_base.h"
 #include "ipc/ipc_test_channel_listener.h"
-#include "ipc/mojo/ipc_channel_mojo_host.h"
 #include "ipc/mojo/ipc_mojo_handle_attachment.h"
 #include "ipc/mojo/ipc_mojo_message_helper.h"
 #include "ipc/mojo/ipc_mojo_param_traits.h"
@@ -68,10 +67,9 @@
 class ChannelClient {
  public:
   explicit ChannelClient(IPC::Listener* listener, const char* name) {
-    channel_ =
-        IPC::ChannelMojo::Create(NULL, main_message_loop_.task_runner(),
-                                 IPCTestBase::GetChannelName(name),
-                                 IPC::Channel::MODE_CLIENT, listener, nullptr);
+    channel_ = IPC::ChannelMojo::Create(
+        main_message_loop_.task_runner(), IPCTestBase::GetChannelName(name),
+        IPC::Channel::MODE_CLIENT, listener, nullptr);
   }
 
   void Connect() {
@@ -116,20 +114,15 @@
   scoped_ptr<IPC::ChannelFactory> CreateChannelFactory(
       const IPC::ChannelHandle& handle,
       base::SequencedTaskRunner* runner) override {
-    host_.reset(new IPC::ChannelMojoHost(task_runner()));
-    return IPC::ChannelMojo::CreateServerFactory(
-        host_->channel_delegate(), task_runner(), handle, nullptr);
+    return IPC::ChannelMojo::CreateServerFactory(task_runner(), handle,
+                                                 nullptr);
   }
 
   bool DidStartClient() override {
     bool ok = IPCTestBase::DidStartClient();
     DCHECK(ok);
-    host_->OnClientLaunched(client_process().Handle());
     return ok;
   }
-
- private:
-  scoped_ptr<IPC::ChannelMojoHost> host_;
 };
 
 
@@ -224,20 +217,15 @@
   scoped_ptr<IPC::ChannelFactory> CreateChannelFactory(
       const IPC::ChannelHandle& handle,
       base::SequencedTaskRunner* runner) override {
-    host_.reset(new IPC::ChannelMojoHost(task_runner()));
-    return IPC::ChannelMojo::CreateServerFactory(
-        host_->channel_delegate(), task_runner(), handle, nullptr);
+    return IPC::ChannelMojo::CreateServerFactory(task_runner(), handle,
+                                                 nullptr);
   }
 
   bool DidStartClient() override {
     bool ok = IPCTestBase::DidStartClient();
     DCHECK(ok);
-    host_->OnClientLaunched(client_process().Handle());
     return ok;
   }
-
- private:
-  scoped_ptr<IPC::ChannelMojoHost> host_;
 };
 
 class ListenerThatQuits : public IPC::Listener {
@@ -558,24 +546,19 @@
   scoped_ptr<IPC::ChannelFactory> CreateChannelFactory(
       const IPC::ChannelHandle& handle,
       base::SequencedTaskRunner* runner) override {
-    host_.reset(new IPC::ChannelMojoHost(task_runner()));
-    return IPC::ChannelMojo::CreateServerFactory(
-        host_->channel_delegate(), task_runner(), handle, nullptr);
+    return IPC::ChannelMojo::CreateServerFactory(task_runner(), handle,
+                                                 nullptr);
   }
 
   bool DidStartClient() override {
     IPCTestBase::DidStartClient();
-    const base::ProcessHandle client = client_process().Handle();
+    // const base::ProcessHandle client = client_process().Handle();
     // Forces GetFileHandleForProcess() fail. It happens occasionally
     // in production, so we should exercise it somehow.
     // TODO(morrita): figure out how to safely test this. See crbug.com/464109.
     // ::CloseHandle(client);
-    host_->OnClientLaunched(client);
     return true;
   }
-
- private:
-  scoped_ptr<IPC::ChannelMojoHost> host_;
 };
 
 TEST_F(IPCChannelMojoDeadHandleTest, InvalidClientHandle) {
diff --git a/ipc/mojo/ipc_mojo.gyp b/ipc/mojo/ipc_mojo.gyp
index a392283..313ecb2 100644
--- a/ipc/mojo/ipc_mojo.gyp
+++ b/ipc/mojo/ipc_mojo.gyp
@@ -32,8 +32,6 @@
         'async_handle_waiter.h',
         'ipc_channel_mojo.cc',
         'ipc_channel_mojo.h',
-        'ipc_channel_mojo_host.cc',
-        'ipc_channel_mojo_host.h',
         'ipc_mojo_bootstrap.cc',
         'ipc_mojo_bootstrap.h',
         'ipc_mojo_handle_attachment.cc',
diff --git a/ipc/mojo/ipc_mojo_bootstrap.cc b/ipc/mojo/ipc_mojo_bootstrap.cc
index 48d0c27..91f3940 100644
--- a/ipc/mojo/ipc_mojo_bootstrap.cc
+++ b/ipc/mojo/ipc_mojo_bootstrap.cc
@@ -20,42 +20,42 @@
  public:
   MojoServerBootstrap();
 
-  void OnClientLaunched(base::ProcessHandle process) override;
-
  private:
-  void SendClientPipe();
-  void SendClientPipeIfReady();
+  void SendClientPipe(int32 peer_pid);
 
   // Listener implementations
   bool OnMessageReceived(const Message& message) override;
   void OnChannelConnected(int32 peer_pid) override;
 
   mojo::embedder::ScopedPlatformHandle server_pipe_;
-  base::ProcessHandle client_process_;
   bool connected_;
 
   DISALLOW_COPY_AND_ASSIGN(MojoServerBootstrap);
 };
 
-MojoServerBootstrap::MojoServerBootstrap()
-    : client_process_(base::kNullProcessHandle), connected_(false) {
+MojoServerBootstrap::MojoServerBootstrap() : connected_(false) {
 }
 
-void MojoServerBootstrap::SendClientPipe() {
+void MojoServerBootstrap::SendClientPipe(int32 peer_pid) {
   DCHECK_EQ(state(), STATE_INITIALIZED);
-  DCHECK_NE(client_process_, base::kNullProcessHandle);
   DCHECK(connected_);
 
   mojo::embedder::PlatformChannelPair channel_pair;
   server_pipe_ = channel_pair.PassServerHandle();
+
+  base::Process peer_process =
+#if defined(OS_WIN)
+      base::Process::OpenWithAccess(peer_pid, PROCESS_DUP_HANDLE);
+#else
+      base::Process::Open(peer_pid);
+#endif
   PlatformFileForTransit client_pipe = GetFileHandleForProcess(
 #if defined(OS_POSIX)
       channel_pair.PassClientHandle().release().fd,
 #else
       channel_pair.PassClientHandle().release().handle,
 #endif
-      client_process_,
-      true);
+      peer_process.Handle(), true);
   if (client_pipe == IPC::InvalidPlatformFileForTransit()) {
 #if !defined(OS_WIN)
     // GetFileHandleForProcess() only fails on Windows.
@@ -73,30 +73,10 @@
   set_state(STATE_WAITING_ACK);
 }
 
-void MojoServerBootstrap::SendClientPipeIfReady() {
-  // Is the client launched?
-  if (client_process_ == base::kNullProcessHandle)
-    return;
-  // Has the bootstrap channel been made?
-  if (!connected_)
-    return;
-  SendClientPipe();
-}
-
-void MojoServerBootstrap::OnClientLaunched(base::ProcessHandle process) {
-  if (HasFailed())
-    return;
-
-  DCHECK_EQ(state(), STATE_INITIALIZED);
-  DCHECK_NE(process, base::kNullProcessHandle);
-  client_process_ = process;
-  SendClientPipeIfReady();
-}
-
 void MojoServerBootstrap::OnChannelConnected(int32 peer_pid) {
   DCHECK_EQ(state(), STATE_INITIALIZED);
   connected_ = true;
-  SendClientPipeIfReady();
+  SendClientPipe(peer_pid);
 }
 
 bool MojoServerBootstrap::OnMessageReceived(const Message&) {
@@ -120,8 +100,6 @@
  public:
   MojoClientBootstrap();
 
-  void OnClientLaunched(base::ProcessHandle process) override;
-
  private:
   // Listener implementations
   bool OnMessageReceived(const Message& message) override;
@@ -158,11 +136,6 @@
   return true;
 }
 
-void MojoClientBootstrap::OnClientLaunched(base::ProcessHandle process) {
-  // This notification should happen only on server processes.
-  NOTREACHED();
-}
-
 void MojoClientBootstrap::OnChannelConnected(int32 peer_pid) {
 }
 
diff --git a/ipc/mojo/ipc_mojo_bootstrap.h b/ipc/mojo/ipc_mojo_bootstrap.h
index e8861cb3..f22cedce 100644
--- a/ipc/mojo/ipc_mojo_bootstrap.h
+++ b/ipc/mojo/ipc_mojo_bootstrap.h
@@ -21,8 +21,7 @@
 // to be wrapped by Mojo MessagePipe.
 //
 // Clients should implement MojoBootstrapDelegate to get the pipe
-// from MojoBootstrap object. It should also tell the client process handle
-// using OnClientLaunched().
+// from MojoBootstrap object.
 //
 // This lives on IO thread other than Create(), which can be called from
 // UI thread as Channel::Create() can be.
@@ -52,9 +51,6 @@
   // GetSelfPID returns the PID associated with |channel_|.
   base::ProcessId GetSelfPID() const;
 
-  // Each client should call this once the process handle becomes known.
-  virtual void OnClientLaunched(base::ProcessHandle process) = 0;
-
 #if defined(OS_POSIX) && !defined(OS_NACL)
   int GetClientFileDescriptor() const;
   base::ScopedFD TakeClientFileDescriptor();
diff --git a/ipc/mojo/ipc_mojo_bootstrap_unittest.cc b/ipc/mojo/ipc_mojo_bootstrap_unittest.cc
index 65d216f..d8ce3b7 100644
--- a/ipc/mojo/ipc_mojo_bootstrap_unittest.cc
+++ b/ipc/mojo/ipc_mojo_bootstrap_unittest.cc
@@ -55,7 +55,6 @@
 #else
   ASSERT_TRUE(StartClient());
 #endif
-  bootstrap->OnClientLaunched(client_process().Handle());
 
   base::MessageLoop::current()->Run();
 
diff --git a/ipc/mojo/ipc_mojo_perftest.cc b/ipc/mojo/ipc_mojo_perftest.cc
index 34ab5f9e..17a43a0 100644
--- a/ipc/mojo/ipc_mojo_perftest.cc
+++ b/ipc/mojo/ipc_mojo_perftest.cc
@@ -6,7 +6,6 @@
 #include "base/run_loop.h"
 #include "ipc/ipc_perftest_support.h"
 #include "ipc/mojo/ipc_channel_mojo.h"
-#include "ipc/mojo/ipc_channel_mojo_host.h"
 #include "third_party/mojo/src/mojo/edk/embedder/test_embedder.h"
 
 namespace {
@@ -37,20 +36,14 @@
   scoped_ptr<IPC::ChannelFactory> CreateChannelFactory(
       const IPC::ChannelHandle& handle,
       base::SequencedTaskRunner* runner) override {
-    host_.reset(new IPC::ChannelMojoHost(runner));
-    return IPC::ChannelMojo::CreateServerFactory(host_->channel_delegate(),
-                                                 runner, handle, nullptr);
+    return IPC::ChannelMojo::CreateServerFactory(runner, handle, nullptr);
   }
 
   bool DidStartClient() override {
     bool ok = IPCTestBase::DidStartClient();
     DCHECK(ok);
-    host_->OnClientLaunched(client_process().Handle());
     return ok;
   }
-
- private:
-  scoped_ptr<IPC::ChannelMojoHost> host_;
 };
 
 MojoChannelPerfTest::MojoChannelPerfTest() {
@@ -88,7 +81,7 @@
 scoped_ptr<IPC::Channel> MojoTestClient::CreateChannel(
     IPC::Listener* listener) {
   return scoped_ptr<IPC::Channel>(IPC::ChannelMojo::Create(
-      NULL, task_runner(), IPCTestBase::GetChannelName("PerformanceClient"),
+      task_runner(), IPCTestBase::GetChannelName("PerformanceClient"),
       IPC::Channel::MODE_CLIENT, listener, nullptr));
 }
 
diff --git a/mandoline/services/core_services/core_services_application_delegate.cc b/mandoline/services/core_services/core_services_application_delegate.cc
index b31fff8b..fb7194c 100644
--- a/mandoline/services/core_services/core_services_application_delegate.cc
+++ b/mandoline/services/core_services/core_services_application_delegate.cc
@@ -12,7 +12,7 @@
 #include "components/resource_provider/resource_provider_app.h"
 #include "components/view_manager/surfaces/surfaces_service_application.h"
 #include "components/view_manager/view_manager_app.h"
-#include "mandoline/ui/browser/browser.h"
+#include "mandoline/ui/browser/browser_manager.h"
 #include "mojo/application/public/cpp/application_connection.h"
 #include "mojo/application/public/cpp/application_impl.h"
 #include "mojo/application/public/cpp/application_runner.h"
@@ -122,7 +122,7 @@
 
   scoped_ptr<mojo::ApplicationDelegate> delegate;
   if (url == "mojo://browser/")
-    delegate.reset(new mandoline::Browser);
+    delegate.reset(new mandoline::BrowserManager);
   else if (url == "mojo://clipboard/")
     delegate.reset(new clipboard::ClipboardApplicationDelegate);
   else if (url == "mojo://filesystem_service/")
diff --git a/mandoline/ui/aura/BUILD.gn b/mandoline/ui/aura/BUILD.gn
index 275fce86..7cf859c4 100644
--- a/mandoline/ui/aura/BUILD.gn
+++ b/mandoline/ui/aura/BUILD.gn
@@ -11,8 +11,6 @@
     "input_method_mandoline.h",
     "native_widget_view_manager.cc",
     "native_widget_view_manager.h",
-    "screen_mojo.cc",
-    "screen_mojo.h",
     "surface_binding.cc",
     "surface_binding.h",
     "surface_context_factory.cc",
@@ -47,6 +45,7 @@
     "//ui/events",
     "//ui/events:events_base",
     "//ui/gl",
+    "//ui/mojo/init",
     "//ui/resources:ui_test_pak",
     "//ui/views",
   ]
diff --git a/mandoline/ui/aura/DEPS b/mandoline/ui/aura/DEPS
index 91015f8..e7b7185 100644
--- a/mandoline/ui/aura/DEPS
+++ b/mandoline/ui/aura/DEPS
@@ -15,6 +15,7 @@
   "+ui/events",
   "+ui/gfx",
   "+ui/gl",
+  "+ui/mojo/init",
   "+ui/views",
   "+ui/wm",
 ]
diff --git a/mandoline/ui/aura/aura_init.cc b/mandoline/ui/aura/aura_init.cc
index 946a118..9f865f9 100644
--- a/mandoline/ui/aura/aura_init.cc
+++ b/mandoline/ui/aura/aura_init.cc
@@ -7,7 +7,8 @@
 #include "base/i18n/icu_util.h"
 #include "base/lazy_instance.h"
 #include "base/path_service.h"
-#include "mandoline/ui/aura/screen_mojo.h"
+#include "components/view_manager/public/cpp/view.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
 #include "ui/aura/env.h"
 #include "ui/base/ime/input_method_initializer.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -36,11 +37,13 @@
 }  // namespace
 #endif  // defined(OS_ANDROID)
 
-AuraInit::AuraInit(mojo::Shell* shell) {
+// TODO(sky): the 1.f should be view->viewport_metrics().device_scale_factor,
+// but that causes clipping problems. No doubt we're not scaling a size
+// correctly.
+AuraInit::AuraInit(mojo::View* view, mojo::Shell* shell)
+    : ui_init_(view->viewport_metrics().size_in_pixels.To<gfx::Size>(), 1.f) {
   aura::Env::CreateInstance(false);
 
-  screen_.reset(ScreenMojo::Create());
-  gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
   InitializeResources(shell);
 
   ui::InitializeInputMethodForTesting();
diff --git a/mandoline/ui/aura/aura_init.h b/mandoline/ui/aura/aura_init.h
index 98f065b88..11388a6 100644
--- a/mandoline/ui/aura/aura_init.h
+++ b/mandoline/ui/aura/aura_init.h
@@ -5,26 +5,25 @@
 #ifndef MANDOLINE_UI_AURA_AURA_INIT_H_
 #define MANDOLINE_UI_AURA_AURA_INIT_H_
 
-#include "base/memory/scoped_ptr.h"
+#include "ui/mojo/init/ui_init.h"
 
 namespace mojo {
 class Shell;
+class View;
 }
 
 namespace mandoline {
 
-class ScreenMojo;
-
 // Sets up necessary state for aura when run with the viewmanager.
 class AuraInit {
  public:
-  explicit AuraInit(mojo::Shell* shell);
+  AuraInit(mojo::View* root, mojo::Shell* shell);
   ~AuraInit();
 
  private:
   void InitializeResources(mojo::Shell* shell);
 
-  scoped_ptr<ScreenMojo> screen_;
+  ui::mojo::UIInit ui_init_;
 
   DISALLOW_COPY_AND_ASSIGN(AuraInit);
 };
diff --git a/mandoline/ui/aura/screen_mojo.cc b/mandoline/ui/aura/screen_mojo.cc
deleted file mode 100644
index b15a68e..0000000
--- a/mandoline/ui/aura/screen_mojo.cc
+++ /dev/null
@@ -1,73 +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 "mandoline/ui/aura/screen_mojo.h"
-
-#include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gfx/native_widget_types.h"
-
-namespace mandoline {
-
-// static
-ScreenMojo* ScreenMojo::Create() {
-  // TODO(beng): this is bogus & should be derived from the native viewport's
-  //             client area. https://crbug.com/487340
-  return new ScreenMojo(gfx::Rect(1280, 800));
-}
-
-ScreenMojo::~ScreenMojo() {
-}
-
-gfx::Point ScreenMojo::GetCursorScreenPoint() {
-  NOTIMPLEMENTED();
-  return gfx::Point();
-}
-
-gfx::NativeWindow ScreenMojo::GetWindowUnderCursor() {
-  return GetWindowAtScreenPoint(GetCursorScreenPoint());
-}
-
-gfx::NativeWindow ScreenMojo::GetWindowAtScreenPoint(const gfx::Point& point) {
-  NOTIMPLEMENTED();
-  return NULL;
-}
-
-int ScreenMojo::GetNumDisplays() const {
-  return 1;
-}
-
-std::vector<gfx::Display> ScreenMojo::GetAllDisplays() const {
-  return std::vector<gfx::Display>(1, display_);
-}
-
-gfx::Display ScreenMojo::GetDisplayNearestWindow(
-    gfx::NativeWindow window) const {
-  return display_;
-}
-
-gfx::Display ScreenMojo::GetDisplayNearestPoint(const gfx::Point& point) const {
-  return display_;
-}
-
-gfx::Display ScreenMojo::GetDisplayMatching(const gfx::Rect& match_rect) const {
-  return display_;
-}
-
-gfx::Display ScreenMojo::GetPrimaryDisplay() const {
-  return display_;
-}
-
-void ScreenMojo::AddObserver(gfx::DisplayObserver* observer) {
-}
-
-void ScreenMojo::RemoveObserver(gfx::DisplayObserver* observer) {
-}
-
-ScreenMojo::ScreenMojo(const gfx::Rect& screen_bounds) {
-  static int64 synthesized_display_id = 2000;
-  display_.set_id(synthesized_display_id++);
-  display_.SetScaleAndBounds(1.0f, screen_bounds);
-}
-
-}  // namespace mandoline
diff --git a/mandoline/ui/browser/BUILD.gn b/mandoline/ui/browser/BUILD.gn
index c46d3885..d5258a3 100644
--- a/mandoline/ui/browser/BUILD.gn
+++ b/mandoline/ui/browser/BUILD.gn
@@ -29,6 +29,8 @@
   sources = [
     "browser.cc",
     "browser.h",
+    "browser_manager.cc",
+    "browser_manager.h",
     "navigator_host_impl.cc",
     "navigator_host_impl.h",
   ]
diff --git a/mandoline/ui/browser/browser.cc b/mandoline/ui/browser/browser.cc
index 1ffb1ce..96424fd4 100644
--- a/mandoline/ui/browser/browser.cc
+++ b/mandoline/ui/browser/browser.cc
@@ -11,6 +11,7 @@
 #include "mandoline/tab/frame.h"
 #include "mandoline/tab/frame_connection.h"
 #include "mandoline/tab/frame_tree.h"
+#include "mandoline/ui/browser/browser_manager.h"
 #include "mandoline/ui/browser/browser_ui.h"
 #include "mojo/application/public/cpp/application_runner.h"
 #include "mojo/common/common_type_converters.h"
@@ -32,28 +33,15 @@
 
 }  // namespace
 
-Browser::Browser()
-    : root_(nullptr),
+Browser::Browser(mojo::ApplicationImpl* app, BrowserManager* browser_manager)
+    : view_manager_init_(app, this, this),
+      root_(nullptr),
       content_(nullptr),
       omnibox_(nullptr),
       navigator_host_(this),
-      app_(nullptr) {
-}
-
-Browser::~Browser() {
-  // Destruct ui_ manually while |this| is alive and reset the pointer first.
-  // This is to avoid a double delete when OnViewManagerDestroyed gets
-  // called.
-  delete ui_.release();
-}
-
-void Browser::ReplaceContentWithRequest(mojo::URLRequestPtr request) {
-  Embed(request.Pass());
-}
-
-void Browser::Initialize(mojo::ApplicationImpl* app) {
-  app_ = app;
-  view_manager_init_.reset(new mojo::ViewManagerInit(app, this, this));
+      app_(app),
+      browser_manager_(browser_manager) {
+  view_manager_init_.connection()->AddService<ViewEmbedder>(this);
 
   ui_.reset(BrowserUI::Create(this, app));
 
@@ -70,17 +58,38 @@
   }
 }
 
-bool Browser::ConfigureIncomingConnection(
-    mojo::ApplicationConnection* connection) {
-  connection->AddService<LaunchHandler>(this);
-  // TODO: register embed interface here.
-  return true;
+Browser::~Browser() {
+  // Destruct ui_ manually while |this| is alive and reset the pointer first.
+  // This is to avoid a double delete when OnViewManagerDestroyed gets
+  // called.
+  ui_.reset();
 }
 
-bool Browser::ConfigureOutgoingConnection(
-    mojo::ApplicationConnection* connection) {
-  connection->AddService<ViewEmbedder>(this);
-  return true;
+void Browser::ReplaceContentWithRequest(mojo::URLRequestPtr request) {
+  Embed(request.Pass());
+}
+
+void Browser::OnDevicePixelRatioAvailable() {
+  content_ = root_->view_manager()->CreateView();
+  ui_->Init(root_);
+
+  view_manager_init_.view_manager_root()->SetViewportSize(
+      mojo::Size::From(GetInitialViewportSize()));
+
+  root_->AddChild(content_);
+  content_->SetVisible(true);
+
+  view_manager_init_.view_manager_root()->AddAccelerator(
+      mojo::KEYBOARD_CODE_BROWSER_BACK, mojo::EVENT_FLAGS_NONE);
+
+  // Now that we're ready, either load a pending url or the default url.
+  if (pending_request_) {
+    Embed(pending_request_.Pass());
+  } else if (!default_url_.empty()) {
+    mojo::URLRequestPtr request(mojo::URLRequest::New());
+    request->url = mojo::String::From(default_url_);
+    Embed(request.Pass());
+  }
 }
 
 void Browser::OnEmbed(mojo::View* root) {
@@ -95,26 +104,10 @@
   //             know so much about these views. Figure out how to shift more to
   //             the UI class.
   root_ = root;
-  content_ = root->view_manager()->CreateView();
-  ui_->Init(root_);
 
-  view_manager_init_->view_manager_root()->SetViewportSize(
-      mojo::Size::From(GetInitialViewportSize()));
-
-  root_->AddChild(content_);
-  content_->SetVisible(true);
-
-  view_manager_init_->view_manager_root()->AddAccelerator(
-      mojo::KEYBOARD_CODE_BROWSER_BACK, mojo::EVENT_FLAGS_NONE);
-
-  // Now that we're ready, either load a pending url or the default url.
-  if (pending_request_) {
-    Embed(pending_request_.Pass());
-  } else if (!default_url_.empty()) {
-    mojo::URLRequestPtr request(mojo::URLRequest::New());
-    request->url = mojo::String::From(default_url_);
-    Embed(request.Pass());
-  }
+  if (!browser_manager_->InitUIIfNecessary(this, root_))
+    return;  // We'll be called back from OnDevicePixelRatioAvailable().
+  OnDevicePixelRatioAvailable();
 }
 
 void Browser::OnEmbedForDescendant(mojo::View* view,
@@ -149,7 +142,7 @@
 void Browser::OnViewManagerDestroyed(mojo::ViewManager* view_manager) {
   ui_.reset();
   root_ = nullptr;
-  app_->Terminate();
+  browser_manager_->BrowserClosed(this);
 }
 
 void Browser::OnAccelerator(mojo::EventPtr event) {
@@ -199,12 +192,6 @@
   navigator_host_.RecordNavigation(gurl.spec());
 }
 
-void Browser::LaunchURL(const mojo::String& url) {
-  mojo::URLRequestPtr request(mojo::URLRequest::New());
-  request->url = url;
-  ReplaceContentWithRequest(request.Pass());
-}
-
 void Browser::Create(mojo::ApplicationConnection* connection,
                      mojo::InterfaceRequest<mojo::NavigatorHost> request) {
   navigator_host_.Bind(request.Pass());
@@ -215,11 +202,6 @@
   view_embedder_bindings_.AddBinding(this, request.Pass());
 }
 
-void Browser::Create(mojo::ApplicationConnection* connection,
-                     mojo::InterfaceRequest<LaunchHandler> request) {
-  launch_handler_bindings_.AddBinding(this, request.Pass());
-}
-
 void Browser::ShowOmnibox(mojo::URLRequestPtr request) {
   if (!omnibox_) {
     omnibox_ = root_->view_manager()->CreateView();
diff --git a/mandoline/ui/browser/browser.h b/mandoline/ui/browser/browser.h
index 92c7d726..e7d2c55 100644
--- a/mandoline/ui/browser/browser.h
+++ b/mandoline/ui/browser/browser.h
@@ -7,10 +7,10 @@
 
 #include "components/view_manager/public/cpp/view_manager.h"
 #include "components/view_manager/public/cpp/view_manager_delegate.h"
+#include "components/view_manager/public/cpp/view_manager_init.h"
 #include "components/view_manager/public/interfaces/view_manager_root.mojom.h"
 #include "mandoline/services/navigation/public/interfaces/navigation.mojom.h"
 #include "mandoline/ui/browser/navigator_host_impl.h"
-#include "mandoline/ui/browser/public/interfaces/launch_handler.mojom.h"
 #include "mandoline/ui/browser/public/interfaces/omnibox.mojom.h"
 #include "mandoline/ui/browser/public/interfaces/view_embedder.mojom.h"
 #include "mojo/application/public/cpp/application_delegate.h"
@@ -26,20 +26,18 @@
 
 namespace mandoline {
 
+class BrowserManager;
 class BrowserUI;
 class FrameTree;
 
-class Browser : public mojo::ApplicationDelegate,
-                public mojo::ViewManagerDelegate,
+class Browser : public mojo::ViewManagerDelegate,
                 public mojo::ViewManagerRootClient,
                 public OmniboxClient,
                 public ViewEmbedder,
-                public LaunchHandler,
                 public mojo::InterfaceFactory<mojo::NavigatorHost>,
-                public mojo::InterfaceFactory<ViewEmbedder>,
-                public mojo::InterfaceFactory<LaunchHandler> {
+                public mojo::InterfaceFactory<ViewEmbedder> {
  public:
-  Browser();
+  Browser(mojo::ApplicationImpl* app, BrowserManager* browser_manager);
   ~Browser() override;
 
   void ReplaceContentWithRequest(mojo::URLRequestPtr request);
@@ -49,14 +47,13 @@
 
   const GURL& current_url() const { return current_url_; }
 
- private:
-  // Overridden from mojo::ApplicationDelegate:
-  void Initialize(mojo::ApplicationImpl* app) override;
-  bool ConfigureIncomingConnection(
-      mojo::ApplicationConnection* connection) override;
-  bool ConfigureOutgoingConnection(
-      mojo::ApplicationConnection* connection) override;
+  // Called once a valid device_pixel_ratio is determined. We gate construction
+  // of the UI until the device_pixel_ratio is available as it's necessary to
+  // properly setup the ui.
+  // TODO(sky): remove this. Only here until we move to viewmanager.
+  void OnDevicePixelRatioAvailable();
 
+ private:
   // Overridden from mojo::ViewManagerDelegate:
   void OnEmbed(mojo::View* root) override;
   void OnEmbedForDescendant(mojo::View* view,
@@ -73,9 +70,6 @@
   // Overridden from ViewEmbedder:
   void Embed(mojo::URLRequestPtr request) override;
 
-  // Overridden from LaunchHandler:
-  void LaunchURL(const mojo::String& url) override;
-
   // Overridden from mojo::InterfaceFactory<mojo::NavigatorHost>:
   void Create(mojo::ApplicationConnection* connection,
               mojo::InterfaceRequest<mojo::NavigatorHost> request) override;
@@ -84,13 +78,9 @@
   void Create(mojo::ApplicationConnection* connection,
               mojo::InterfaceRequest<ViewEmbedder> request) override;
 
-  // Overridden from mojo::InterfaceFactory<LaunchHandler>:
-  void Create(mojo::ApplicationConnection* connection,
-              mojo::InterfaceRequest<LaunchHandler> request) override;
-
   void ShowOmnibox(mojo::URLRequestPtr request);
 
-  scoped_ptr<mojo::ViewManagerInit> view_manager_init_;
+  mojo::ViewManagerInit view_manager_init_;
 
   // Only support being embedded once, so both application-level
   // and embedding-level state are shared on the same object.
@@ -101,7 +91,6 @@
   mojo::URLRequestPtr pending_request_;
 
   mojo::WeakBindingSet<ViewEmbedder> view_embedder_bindings_;
-  mojo::WeakBindingSet<LaunchHandler> launch_handler_bindings_;
 
   NavigatorHostImpl navigator_host_;
 
@@ -109,6 +98,7 @@
 
   scoped_ptr<BrowserUI> ui_;
   mojo::ApplicationImpl* app_;
+  BrowserManager* browser_manager_;
 
   scoped_ptr<FrameTree> frame_tree_;
 
diff --git a/mandoline/ui/browser/browser_manager.cc b/mandoline/ui/browser/browser_manager.cc
new file mode 100644
index 0000000..7d828fc
--- /dev/null
+++ b/mandoline/ui/browser/browser_manager.cc
@@ -0,0 +1,125 @@
+// 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 "mandoline/ui/browser/browser_manager.h"
+
+#include "components/view_manager/public/cpp/view.h"
+#include "components/view_manager/public/cpp/view_observer.h"
+#include "mandoline/ui/browser/browser.h"
+
+namespace mandoline {
+
+// TODO(sky): make ViewManager not do anything until device_pixel_ratio is
+// determined. At which point this can be nuked.
+class BrowserManager::DevicePixelRatioWaiter : mojo::ViewObserver {
+ public:
+  DevicePixelRatioWaiter(BrowserManager* manager,
+                         Browser* browser,
+                         mojo::View* view)
+      : manager_(manager), browser_(browser), view_(view) {
+    view_->AddObserver(this);
+  }
+  ~DevicePixelRatioWaiter() override { RemoveObserver(); }
+
+ private:
+  void RemoveObserver() {
+    if (!view_)
+      return;
+    view_->RemoveObserver(this);
+    view_ = nullptr;
+  }
+
+  // mojo::ViewObserver:
+  void OnViewViewportMetricsChanged(
+      mojo::View* view,
+      const mojo::ViewportMetrics& old_metrics,
+      const mojo::ViewportMetrics& new_metrics) override {
+    if (new_metrics.device_pixel_ratio == 0)
+      return;
+
+    RemoveObserver();
+    manager_->OnDevicePixelRatioAvailable(browser_, view);
+  }
+  void OnViewDestroyed(mojo::View* view) override { RemoveObserver(); }
+
+  BrowserManager* manager_;
+  Browser* browser_;
+  mojo::View* view_;
+
+  DISALLOW_COPY_AND_ASSIGN(DevicePixelRatioWaiter);
+};
+
+BrowserManager::BrowserManager()
+    : app_(nullptr) {
+}
+
+BrowserManager::~BrowserManager() {
+  DCHECK(browsers_.empty());
+}
+
+Browser* BrowserManager::CreateBrowser() {
+  DCHECK(app_);
+  Browser* browser = new Browser(app_, this);
+  browsers_.insert(browser);
+  return browser;
+}
+
+void BrowserManager::BrowserClosed(Browser* browser) {
+  scoped_ptr<Browser> browser_owner(browser);
+  DCHECK_GT(browsers_.count(browser), 0u);
+  browsers_.erase(browser);
+  if (browsers_.empty())
+    app_->Terminate();
+}
+
+bool BrowserManager::InitUIIfNecessary(Browser* browser, mojo::View* view) {
+  if (view->viewport_metrics().device_pixel_ratio > 0) {
+#if defined(USE_AURA)
+    if (!aura_init_)
+      aura_init_.reset(new AuraInit(view, app_->shell()));
+#endif
+    return true;
+  }
+  DCHECK(!device_pixel_ratio_waiter_.get());
+  device_pixel_ratio_waiter_.reset(
+      new DevicePixelRatioWaiter(this, browser, view));
+  return false;
+}
+
+void BrowserManager::OnDevicePixelRatioAvailable(Browser* browser,
+                                                 mojo::View* view) {
+  device_pixel_ratio_waiter_.reset();
+#if defined(USE_AURA)
+  DCHECK(!aura_init_.get());
+  aura_init_.reset(new AuraInit(view, app_->shell()));
+#endif
+  browser->OnDevicePixelRatioAvailable();
+}
+
+void BrowserManager::LaunchURL(const mojo::String& url) {
+  DCHECK(!browsers_.empty());
+  mojo::URLRequestPtr request(mojo::URLRequest::New());
+  request->url = url;
+  // TODO(fsamuel): Create a new Browser once we support multiple browser
+  // windows.
+  (*browsers_.begin())->ReplaceContentWithRequest(request.Pass());
+}
+
+void BrowserManager::Initialize(mojo::ApplicationImpl* app) {
+  app_ = app;
+  CreateBrowser();
+}
+
+bool BrowserManager::ConfigureIncomingConnection(
+    mojo::ApplicationConnection* connection) {
+  connection->AddService<LaunchHandler>(this);
+  return true;
+}
+
+void BrowserManager::Create(mojo::ApplicationConnection* connection,
+                            mojo::InterfaceRequest<LaunchHandler> request) {
+  launch_handler_bindings_.AddBinding(this, request.Pass());
+}
+
+}  // namespace mandoline
diff --git a/mandoline/ui/browser/browser_manager.h b/mandoline/ui/browser/browser_manager.h
new file mode 100644
index 0000000..cb2927b
--- /dev/null
+++ b/mandoline/ui/browser/browser_manager.h
@@ -0,0 +1,77 @@
+// 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 MANDOLINE_UI_BROWSER_BROWSER_MANAGER_H_
+#define MANDOLINE_UI_BROWSER_BROWSER_MANAGER_H_
+
+#include <set>
+
+#include "base/memory/scoped_vector.h"
+#include "mandoline/ui/browser/public/interfaces/launch_handler.mojom.h"
+#include "mojo/application/public/cpp/application_delegate.h"
+#include "mojo/application/public/cpp/application_impl.h"
+#include "mojo/application/public/cpp/connect.h"
+#include "mojo/common/weak_binding_set.h"
+
+#if defined(USE_AURA)
+#include "mandoline/ui/aura/aura_init.h"
+#endif
+
+namespace mojo {
+class View;
+}
+
+namespace mandoline {
+
+class Browser;
+
+// BrowserManager creates and manages the lifetime of Browsers.
+class BrowserManager : public mojo::ApplicationDelegate,
+                       public LaunchHandler,
+                       public mojo::InterfaceFactory<LaunchHandler> {
+ public:
+  BrowserManager();
+  ~BrowserManager() override;
+
+  // BrowserManager owns the returned Browser.
+  Browser* CreateBrowser();
+
+  // Invoked by |browser| when it has closed.
+  void BrowserClosed(Browser* browser);
+
+  bool InitUIIfNecessary(Browser* browser, mojo::View* view);
+
+ private:
+  class DevicePixelRatioWaiter;
+
+  void OnDevicePixelRatioAvailable(Browser* browser, mojo::View* view);
+
+  // Overridden from LaunchHandler:
+  void LaunchURL(const mojo::String& url) override;
+
+  // Overridden from mojo::ApplicationDelegate:
+  void Initialize(mojo::ApplicationImpl* app) override;
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override;
+
+  // Overridden from mojo::InterfaceFactory<LaunchHandler>:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<LaunchHandler> request) override;
+
+  mojo::ApplicationImpl* app_;
+  // TODO(sky): This should be held in the ui classes, not here.
+#if defined(USE_AURA)
+  scoped_ptr<AuraInit> aura_init_;
+#endif
+  mojo::WeakBindingSet<LaunchHandler> launch_handler_bindings_;
+  std::set<Browser*> browsers_;
+
+  scoped_ptr<DevicePixelRatioWaiter> device_pixel_ratio_waiter_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserManager);
+};
+
+}  // namespace mandoline
+
+#endif  // MANDOLINE_UI_BROWSER_BROWSER_MANAGER_H_
diff --git a/mandoline/ui/browser/desktop/desktop_ui.cc b/mandoline/ui/browser/desktop/desktop_ui.cc
index b417243..9476b87 100644
--- a/mandoline/ui/browser/desktop/desktop_ui.cc
+++ b/mandoline/ui/browser/desktop/desktop_ui.cc
@@ -21,8 +21,7 @@
 // DesktopUI, public:
 
 DesktopUI::DesktopUI(Browser* browser, mojo::ApplicationImpl* application_impl)
-    : aura_init_(application_impl->shell()),
-      browser_(browser),
+    : browser_(browser),
       application_impl_(application_impl),
       omnibox_launcher_(nullptr),
       root_(nullptr),
diff --git a/mandoline/ui/browser/desktop/desktop_ui.h b/mandoline/ui/browser/desktop/desktop_ui.h
index 3be7f10..a83f6b24 100644
--- a/mandoline/ui/browser/desktop/desktop_ui.h
+++ b/mandoline/ui/browser/desktop/desktop_ui.h
@@ -5,7 +5,6 @@
 #ifndef MANDOLINE_UI_BROWSER_DESKTOP_DESKTOP_UI_H_
 #define MANDOLINE_UI_BROWSER_DESKTOP_DESKTOP_UI_H_
 
-#include "mandoline/ui/aura/aura_init.h"
 #include "mandoline/ui/browser/browser_ui.h"
 #include "mandoline/ui/browser/public/interfaces/omnibox.mojom.h"
 #include "ui/views/controls/button/button.h"
@@ -43,7 +42,6 @@
   // Overridden from views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
-  AuraInit aura_init_;
   Browser* browser_;
   mojo::ApplicationImpl* application_impl_;
   views::LabelButton* omnibox_launcher_;
diff --git a/mandoline/ui/browser/main.cc b/mandoline/ui/browser/main.cc
index e95aee4..a7128af 100644
--- a/mandoline/ui/browser/main.cc
+++ b/mandoline/ui/browser/main.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "mandoline/ui/browser/browser.h"
+#include "mandoline/ui/browser/browser_manager.h"
 #include "mojo/application/public/cpp/application_runner.h"
 #include "third_party/mojo/src/mojo/public/c/system/main.h"
 
 MojoResult MojoMain(MojoHandle shell_handle) {
-  mojo::ApplicationRunner runner(new mandoline::Browser);
+  mojo::ApplicationRunner runner(new mandoline::BrowserManager);
   return runner.Run(shell_handle);
 }
diff --git a/mandoline/ui/omnibox/omnibox_impl.cc b/mandoline/ui/omnibox/omnibox_impl.cc
index 0509693..eba94bb 100644
--- a/mandoline/ui/omnibox/omnibox_impl.cc
+++ b/mandoline/ui/omnibox/omnibox_impl.cc
@@ -53,7 +53,7 @@
 
 void OmniboxImpl::OnEmbed(mojo::View* root) {
   if (!aura_init_.get()) {
-    aura_init_.reset(new AuraInit(app_impl_->shell()));
+    aura_init_.reset(new AuraInit(root, app_impl_->shell()));
     edit_ = new views::Textfield;
     edit_->set_controller(this);
   }
diff --git a/media/BUILD.gn b/media/BUILD.gn
index eabae13..b6f8276 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -258,6 +258,8 @@
     "video/gpu_memory_buffer_video_frame_pool.h",
     "video/h264_poc.cc",
     "video/h264_poc.h",
+    "video/jpeg_decode_accelerator.cc",
+    "video/jpeg_decode_accelerator.h",
     "video/picture.cc",
     "video/picture.h",
     "video/video_decode_accelerator.cc",
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 5d0e46d..2aaf132 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -635,7 +635,8 @@
                  natural_size,
                  timestamp) {
   DCHECK_EQ(storage_type, STORAGE_SHMEM);
-  AddSharedMemoryHandle(handle, shared_memory_offset);
+  AddSharedMemoryHandle(handle);
+  shared_memory_offset_ = shared_memory_offset;
 }
 
 VideoFrame::VideoFrame(Format format,
@@ -881,11 +882,9 @@
 }
 #endif
 
-void VideoFrame::AddSharedMemoryHandle(base::SharedMemoryHandle handle,
-                                       size_t shared_memory_offset) {
+void VideoFrame::AddSharedMemoryHandle(base::SharedMemoryHandle handle) {
   storage_type_ = STORAGE_SHMEM;
   shared_memory_handle_ = handle;
-  shared_memory_offset_ = shared_memory_offset;
 }
 
 #if defined(OS_MACOSX)
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index 1eaf4c3..9d97848 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -349,8 +349,7 @@
   bool DuplicateFileDescriptors(const std::vector<int>& fds_in);
 #endif
 
-  void AddSharedMemoryHandle(base::SharedMemoryHandle handle,
-                             size_t shared_memory_offset);
+  void AddSharedMemoryHandle(base::SharedMemoryHandle handle);
 
 #if defined(OS_MACOSX)
   // Returns the backing CVPixelBuffer, if present.
diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc
index 659f3d0..dec3e9e 100644
--- a/media/filters/source_buffer_stream_unittest.cc
+++ b/media/filters/source_buffer_stream_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "media/filters/source_buffer_stream.h"
 
+#include <stdint.h>
 #include <string>
 
 #include "base/bind.h"
@@ -140,8 +141,8 @@
     stream_->Seek(position * frame_duration_);
   }
 
-  void SeekToTimestamp(base::TimeDelta timestamp) {
-    stream_->Seek(timestamp);
+  void SeekToTimestampMs(int64_t timestamp_ms) {
+    stream_->Seek(base::TimeDelta::FromMilliseconds(timestamp_ms));
   }
 
   void RemoveInMs(int start, int end, int duration) {
@@ -791,7 +792,7 @@
   // Verify that the buffered ranges are updated properly and we don't crash.
   CheckExpectedRangesByTimestamp("{ [20,150) }");
 
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(20));
+  SeekToTimestampMs(20);
   CheckExpectedBuffers("20K 50K 80K 110K 120K");
 }
 
@@ -869,7 +870,7 @@
 // track:
 TEST_F(SourceBufferStreamTest, End_Overlap_SingleBuffer) {
   // Seek to start of stream.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(0));
+  SeekToTimestampMs(0);
 
   NewSegmentAppend("0K 30 60 90 120K 150");
   CheckExpectedRangesByTimestamp("{ [0,180) }");
@@ -1641,7 +1642,7 @@
   CheckExpectedRangesByTimestamp("{ [10,160) }");
 
   // Seek to 130ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(130));
+  SeekToTimestampMs(130);
 
   // Overlap with a new segment from 0 to 130ms.
   NewSegmentAppendOneByOne("0K 120D10");
@@ -1650,7 +1651,7 @@
   CheckExpectedBuffers("130K");
 
   // Check the final buffers is correct.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(0));
+  SeekToTimestampMs(0);
   CheckExpectedBuffers("0K 120 130K");
 }
 
@@ -1667,7 +1668,7 @@
 
   // Check the final buffers is correct; the keyframe at 110ms should be
   // deleted.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(0));
+  SeekToTimestampMs(0);
   CheckExpectedBuffers("0K 30 60 90 120K 150 180 210");
 }
 
@@ -1679,7 +1680,7 @@
   CheckExpectedRangesByTimestamp("{ [10,160) }");
 
   // Seek to 70ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(70));
+  SeekToTimestampMs(70);
   CheckExpectedBuffers("10K 40");
 
   // Overlap with a new segment from 0 to 130ms.
@@ -1692,7 +1693,7 @@
   CheckExpectedBuffers("70 120K 130K");
 
   // Check the final result: should not include data from the track buffer.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(0));
+  SeekToTimestampMs(0);
   CheckExpectedBuffers("0K 30 60 90 120K 130K");
 }
 
@@ -1709,7 +1710,7 @@
   CheckExpectedRangesByTimestamp("{ [10,160) }");
 
   // Seek to 70ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(70));
+  SeekToTimestampMs(70);
   CheckExpectedBuffers("10K 40");
 
   // Overlap with a new segment from 0 to 120ms; 70ms and 100ms go in track
@@ -1739,7 +1740,7 @@
   CheckExpectedRangesByTimestamp("{ [10,160) }");
 
   // Seek to 70ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(70));
+  SeekToTimestampMs(70);
   CheckExpectedBuffers("10K 40");
 
   // Overlap with a new segment from 0 to 120ms; 70ms goes in track buffer.
@@ -1774,7 +1775,7 @@
   CheckExpectedRangesByTimestamp("{ [10,160) }");
 
   // Seek to 70ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(70));
+  SeekToTimestampMs(70);
   CheckExpectedBuffers("10K 40");
 
   // Overlap with a new segment from 0 to 120ms; 70ms and 100ms go in track
@@ -1804,7 +1805,7 @@
   CheckExpectedRangesByTimestamp("{ [10,130) }");
 
   // Seek to 70ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(70));
+  SeekToTimestampMs(70);
   CheckExpectedBuffers("10K 40");
 
   // Overlap with a new segment from 0 to 120ms; 70ms goes in track
@@ -1835,7 +1836,7 @@
   CheckExpectedRangesByTimestamp("{ [10,160) [200,260) }");
 
   // Seek to 70ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(70));
+  SeekToTimestampMs(70);
   CheckExpectedBuffers("10K 40");
 
   // Overlap with a new segment from 0 to 120ms.
@@ -1853,7 +1854,7 @@
   CheckNoNextBuffer();
 
   // Check the final result: should not include data from the track buffer.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(0));
+  SeekToTimestampMs(0);
   CheckExpectedBuffers("0K 30 60 90 120K 130K");
   CheckNoNextBuffer();
 }
@@ -2496,7 +2497,7 @@
   CheckExpectedRangesByTimestamp("{ [310,400) [490,670) }");
 
   // Seek to the GOP at 580ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(580));
+  SeekToTimestampMs(580);
 
   // Append 2 GOPs before the existing ranges.
   // So the ranges before GC are "{ [100,280) [310,400) [490,670) }".
@@ -2514,7 +2515,7 @@
   NewSegmentAppend("400K 430 460 490K 520 550 580K 610 640");
 
   // Seek to the GOP at 580ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(580));
+  SeekToTimestampMs(580);
 
   // Append 2 GOPs starting at 220ms, and they will be merged with the existing
   // range.  So the range before GC is "{ [220,670) }".
@@ -2657,7 +2658,7 @@
   CheckExpectedRangesByTimestamp("{ [0,150) [200,380) }");
 
   // Seek to 290ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(290));
+  SeekToTimestampMs(290);
 
   // Now set memory limit to 3 and append a GOP in a separate range after the
   // selected range. Because it is after 290ms, this tests that the GOP is saved
@@ -2698,7 +2699,7 @@
   CheckExpectedRangesByTimestamp("{ [80,200) [400,670) }");
 
   // Seek to 80ms to make the first range the selected range.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(80));
+  SeekToTimestampMs(80);
 
   // Now set memory limit to 3 and append a GOP in the middle of the second
   // range. Because it is after the selected range, this tests that the GOP is
@@ -2723,7 +2724,7 @@
   CheckExpectedRangesByTimestamp("{ [0,270) }");
 
   // Seek to the GOP at 90ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(90));
+  SeekToTimestampMs(90);
 
   // Set the memory limit to 1, then overlap the GOP at 0.
   SetMemoryLimit(1);
@@ -2733,12 +2734,12 @@
   CheckExpectedRangesByTimestamp("{ [0,180) }");
 
   // Seek to 0 and check all buffers.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(0));
+  SeekToTimestampMs(0);
   CheckExpectedBuffers("0K 30 60 90K 120 150");
   CheckNoNextBuffer();
 
   // Now seek back to 90ms and append a GOP at 180ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(90));
+  SeekToTimestampMs(90);
   NewSegmentAppend("180K 210 240");
 
   // Should save the GOP at 90ms and the GOP at 180ms.
@@ -2756,7 +2757,7 @@
   CheckExpectedRangesByTimestamp("{ [0,360) }");
 
   // Seek to the last GOP at 270ms.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(270));
+  SeekToTimestampMs(270);
 
   // Set the memory limit to 1, then overlap the GOP at 90ms.
   SetMemoryLimit(1);
@@ -2784,7 +2785,7 @@
 // the next buffer.
 TEST_F(SourceBufferStreamTest, GarbageCollection_SaveAppendGOP_Selected3) {
   // Seek to start of stream.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(0));
+  SeekToTimestampMs(0);
 
   // Append 3 GOPs starting at 0ms, 90ms, 180ms.
   NewSegmentAppend("0K 30 60 90K 120 150 180K 210 240");
@@ -3205,7 +3206,7 @@
 // accordingly so that successive append operations keep working.
 TEST_F(SourceBufferStreamTest, SetExplicitDuration_UpdateSelectedRange) {
   // Seek to start of stream.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(0));
+  SeekToTimestampMs(0);
 
   NewSegmentAppend("0K 30 60 90");
 
@@ -3299,7 +3300,7 @@
 // range to get split and then merged back into a single range.
 TEST_F(SourceBufferStreamTest, OverlapSplitAndMergeWhileWaitingForMoreData) {
   // Seek to start of stream.
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(0));
+  SeekToTimestampMs(0);
 
   NewSegmentAppend("0K 30 60 90 120K 150");
   CheckExpectedRangesByTimestamp("{ [0,180) }");
@@ -3626,7 +3627,7 @@
   CheckExpectedRangesByTimestamp("{ [150,210) }");
 
 
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(150));
+  SeekToTimestampMs(150);
   CheckExpectedBuffers("150K 180");
   CheckNoNextBuffer();
 }
@@ -3653,7 +3654,7 @@
   // Verify the buffers in the ranges.
   CheckExpectedBuffers("0K 30 60 90");
   CheckNoNextBuffer();
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(240));
+  SeekToTimestampMs(240);
   CheckExpectedBuffers("240K 270 300");
 }
 
@@ -3680,13 +3681,13 @@
 
   // Verify the buffers in the ranges.
   CheckNoNextBuffer();
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(270));
+  SeekToTimestampMs(270);
   CheckExpectedBuffers("270K 300");
 }
 
 TEST_F(SourceBufferStreamTest,
        Remove_PreviousAppendDestroyedAndOverwriteExistingRange) {
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(90));
+  SeekToTimestampMs(90);
 
   NewSegmentAppend("90K 120 150");
   CheckExpectedRangesByTimestamp("{ [90,180) }");
@@ -3816,7 +3817,7 @@
   NewSegmentAppend("0K S(3K 6 9D3 10D5) 15K 20");
   CheckExpectedBuffers("0K 3K 6");
 
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(15));
+  SeekToTimestampMs(15);
   CheckExpectedBuffers("15K 20");
   CheckNoNextBuffer();
 }
@@ -3830,7 +3831,7 @@
   NewSegmentAppend("5K 15K 20");
   CheckExpectedBuffers("3K 6");
 
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(15));
+  SeekToTimestampMs(15);
   CheckExpectedBuffers("15K 20");
   CheckNoNextBuffer();
 }
@@ -4101,7 +4102,7 @@
   // Append a few buffers.
   NewSegmentAppend("10K 15 20D5");
   CheckExpectedRangesByTimestamp("{ [10,25) }");
-  SeekToTimestamp(base::TimeDelta::FromMilliseconds(10));
+  SeekToTimestampMs(10);
   CheckExpectedBuffers("10K 15 20");
   CheckNoNextBuffer();
 
diff --git a/media/media.gyp b/media/media.gyp
index 85dcb332..a83911d 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -605,6 +605,8 @@
         'video/gpu_memory_buffer_video_frame_pool.h',
         'video/h264_poc.cc',
         'video/h264_poc.h',
+        'video/jpeg_decode_accelerator.cc',
+        'video/jpeg_decode_accelerator.h',
         'video/picture.cc',
         'video/picture.h',
         'video/video_decode_accelerator.cc',
diff --git a/media/midi/midi_manager_win.cc b/media/midi/midi_manager_win.cc
index 5c81386..8843040 100644
--- a/media/midi/midi_manager_win.cc
+++ b/media/midi/midi_manager_win.cc
@@ -297,6 +297,12 @@
   }
 }
 
+bool IsUnsupportedDevice(const MidiDeviceInfo& info) {
+  return info.manufacturer_id == MM_MICROSOFT &&
+         (info.product_id == MM_MSFT_WDMAUDIO_MIDIOUT ||
+          info.product_id == MM_MSFT_GENERIC_MIDISYNTH);
+}
+
 using PortNumberCache = base::hash_map<
     MidiDeviceInfo,
     std::priority_queue<uint32, std::vector<uint32>, std::greater<uint32>>,
@@ -799,6 +805,8 @@
         make_scoped_refptr(new MidiOutputDeviceState(MidiDeviceInfo(caps)));
     state->midi_handle = midi_out_handle;
     const auto& state_device_info = state->device_info;
+    if (IsUnsupportedDevice(state_device_info))
+      return;
     bool add_new_port = false;
     uint32 port_number = 0;
     {
diff --git a/media/video/capture/fake_video_capture_device.cc b/media/video/capture/fake_video_capture_device.cc
index 613d8a5..e7b90ad 100644
--- a/media/video/capture/fake_video_capture_device.cc
+++ b/media/video/capture/fake_video_capture_device.cc
@@ -179,7 +179,6 @@
   if (capture_buffer.get()) {
     uint8_t* const data_ptr = static_cast<uint8_t*>(capture_buffer->data());
     DCHECK(data_ptr) << "Buffer has NO backing memory";
-    DCHECK_EQ(capture_buffer->GetType(), gfx::SHARED_MEMORY_BUFFER);
     memset(data_ptr, 0, capture_buffer->size());
 
     DrawPacman(
diff --git a/media/video/capture/fake_video_capture_device_unittest.cc b/media/video/capture/fake_video_capture_device_unittest.cc
index ee0c2af..ab2e7d25 100644
--- a/media/video/capture/fake_video_capture_device_unittest.cc
+++ b/media/video/capture/fake_video_capture_device_unittest.cc
@@ -39,13 +39,12 @@
   int id() const override { return id_; }
   size_t size() const override { return size_; }
   void* data() override { return data_; }
-  gfx::GpuMemoryBufferType GetType() override {
-    return gfx::SHARED_MEMORY_BUFFER;
-  }
   ClientBuffer AsClientBuffer() override { return nullptr; }
-  base::PlatformFile AsPlatformFile() override {
-    return base::PlatformFile();
+#if defined(OS_POSIX)
+  base::FileDescriptor AsPlatformFile() override {
+    return base::FileDescriptor();
   }
+#endif
 
  private:
   const int id_;
diff --git a/media/video/capture/video_capture_device.h b/media/video/capture/video_capture_device.h
index 72aac2e6..59d91cf 100644
--- a/media/video/capture/video_capture_device.h
+++ b/media/video/capture/video_capture_device.h
@@ -203,9 +203,10 @@
       virtual int id() const = 0;
       virtual size_t size() const = 0;
       virtual void* data() = 0;
-      virtual gfx::GpuMemoryBufferType GetType() = 0;
       virtual ClientBuffer AsClientBuffer() = 0;
-      virtual base::PlatformFile AsPlatformFile() = 0;
+#if defined(OS_POSIX)
+      virtual base::FileDescriptor AsPlatformFile() = 0;
+#endif
     };
 
     virtual ~Client() {}
diff --git a/media/video/jpeg_decode_accelerator.cc b/media/video/jpeg_decode_accelerator.cc
new file mode 100644
index 0000000..035c3216
--- /dev/null
+++ b/media/video/jpeg_decode_accelerator.cc
@@ -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.
+
+#include "media/video/jpeg_decode_accelerator.h"
+
+namespace media {
+
+JpegDecodeAccelerator::~JpegDecodeAccelerator() {
+}
+
+}  // namespace media
diff --git a/media/video/jpeg_decode_accelerator.h b/media/video/jpeg_decode_accelerator.h
index 83c5940..fd7608f 100644
--- a/media/video/jpeg_decode_accelerator.h
+++ b/media/video/jpeg_decode_accelerator.h
@@ -17,7 +17,7 @@
 // The output color format is I420. The decoder will convert the color format
 // to I420 if the color space or subsampling does not match that and if it is
 // capable of doing so. The client is responsible for allocating buffers and
-// keeps the ownership of them. All methods must be called on the same thread.
+// keeps the ownership of them.
 // The intended use case of this interface is decoding MJPEG images coming
 // from camera capture. It can also be used for normal still JPEG image
 // decoding, but normal JPEG images may use more JPEG features that may not be
@@ -28,6 +28,8 @@
 
   // Enumeration of decode errors generated by NotifyError callback.
   enum Error {
+    // No error. Decode succeeded.
+    NO_ERRORS,
     // Invalid argument was passed to an API method, e.g. the output buffer is
     // too small, JPEG width/height are too big for JDA.
     INVALID_ARGUMENT,
@@ -71,6 +73,12 @@
     virtual ~Client() {}
   };
 
+  // Destroys the decoder: all pending inputs are dropped immediately. This
+  // call may asynchronously free system resources, but its client-visible
+  // effects are synchronous. After destructor returns, no more callbacks
+  // will be made on the client.
+  virtual ~JpegDecodeAccelerator() = 0;
+
   // JPEG decoder functions.
 
   // Initializes the JPEG decoder. Should be called once per decoder
@@ -78,7 +86,7 @@
   // is successful.
   // Parameters:
   //  |client| is the Client interface for decode callback. The provided
-  //  pointer must be valid until Destroy() is called.
+  //  pointer must be valid until destructor is called.
   virtual bool Initialize(Client* client) = 0;
 
   // Decodes the given bitstream buffer that contains one JPEG picture. It
@@ -97,38 +105,11 @@
   //  Ownership of the |bitstream_buffer| and |video_frame| remains with the
   //  client. The client is not allowed to deallocate them before
   //  VideoFrameReady or NotifyError() is invoked for given id of
-  //  |bitstream_buffer|, or Destroy() returns.
+  //  |bitstream_buffer|, or destructor returns.
   virtual void Decode(const BitstreamBuffer& bitstream_buffer,
                       const scoped_refptr<media::VideoFrame>& video_frame) = 0;
-
-  // Destroys the decoder: all pending inputs are dropped immediately. This
-  // call may asynchronously free system resources, but its client-visible
-  // effects are synchronous. After this method returns, no more callbacks
-  // will be made on the client. Deletes |this| unconditionally, so make sure
-  // to drop all pointers to it!
-  virtual void Destroy() = 0;
-
- protected:
-  // Do not delete directly; use Destroy() or own it with a scoped_ptr, which
-  // will Destroy() it properly by default.
-  virtual ~JpegDecodeAccelerator();
 };
 
 }  // namespace media
 
-namespace base {
-
-template <class T>
-struct DefaultDeleter;
-
-// Specialize DefaultDeleter so that scoped_ptr<JpegDecodeAccelerator> always
-// uses "Destroy()" instead of trying to use the destructor.
-template <>
-struct MEDIA_EXPORT DefaultDeleter<media::JpegDecodeAccelerator> {
- public:
-  void operator()(void* jpeg_decode_accelerator) const;
-};
-
-}  // namespace base
-
 #endif  // MEDIA_VIDEO_JPEG_DECODE_ACCELERATOR_H_
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
index 5bcbc957..7f86bfa 100644
--- a/mojo/BUILD.gn
+++ b/mojo/BUILD.gn
@@ -26,6 +26,7 @@
 group("tests") {
   testonly = true
   deps = [
+    "//ipc/mojo:ipc_mojo_unittests",
     "//mojo/application/public/cpp/tests:mojo_public_application_unittests",
     "//mojo/common:mojo_common_unittests",
     "//mojo/converters/surfaces/tests:mojo_surfaces_lib_unittests",
diff --git a/mojo/common/weak_binding_set.h b/mojo/common/weak_binding_set.h
index 87383889..aeeefe9 100644
--- a/mojo/common/weak_binding_set.h
+++ b/mojo/common/weak_binding_set.h
@@ -42,6 +42,8 @@
     bindings_.clear();
   }
 
+  bool empty() const { return bindings_.empty(); }
+
  private:
   // ErrorHandler implementation.
   void OnConnectionError() override {
diff --git a/mojo/converters/surfaces/surfaces_type_converters.cc b/mojo/converters/surfaces/surfaces_type_converters.cc
index 0420b28..2990da3 100644
--- a/mojo/converters/surfaces/surfaces_type_converters.cc
+++ b/mojo/converters/surfaces/surfaces_type_converters.cc
@@ -51,11 +51,10 @@
 cc::SharedQuadState* ConvertSharedQuadState(const SharedQuadStatePtr& input,
                                             cc::RenderPass* render_pass) {
   cc::SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState();
-  state->SetAll(input->content_to_target_transform.To<gfx::Transform>(),
-                input->content_bounds.To<gfx::Size>(),
-                input->visible_content_rect.To<gfx::Rect>(),
-                input->clip_rect.To<gfx::Rect>(),
-                input->is_clipped,
+  state->SetAll(input->quad_to_target_transform.To<gfx::Transform>(),
+                input->quad_layer_bounds.To<gfx::Size>(),
+                input->visible_quad_layer_rect.To<gfx::Rect>(),
+                input->clip_rect.To<gfx::Rect>(), input->is_clipped,
                 input->opacity,
                 static_cast<::SkXfermode::Mode>(input->blend_mode),
                 input->sorting_context_id);
@@ -345,10 +344,10 @@
 TypeConverter<SharedQuadStatePtr, cc::SharedQuadState>::Convert(
     const cc::SharedQuadState& input) {
   SharedQuadStatePtr state = SharedQuadState::New();
-  state->content_to_target_transform =
-      Transform::From(input.content_to_target_transform);
-  state->content_bounds = Size::From(input.content_bounds);
-  state->visible_content_rect = Rect::From(input.visible_content_rect);
+  state->quad_to_target_transform =
+      Transform::From(input.quad_to_target_transform);
+  state->quad_layer_bounds = Size::From(input.quad_layer_bounds);
+  state->visible_quad_layer_rect = Rect::From(input.visible_quad_layer_rect);
   state->clip_rect = Rect::From(input.clip_rect);
   state->is_clipped = input.is_clipped;
   state->opacity = input.opacity;
diff --git a/mojo/converters/surfaces/surfaces_utils.cc b/mojo/converters/surfaces/surfaces_utils.cc
index b433722a..d62fc08 100644
--- a/mojo/converters/surfaces/surfaces_utils.cc
+++ b/mojo/converters/surfaces/surfaces_utils.cc
@@ -14,9 +14,9 @@
 
 SharedQuadStatePtr CreateDefaultSQS(const gfx::Size& size) {
   SharedQuadStatePtr sqs = SharedQuadState::New();
-  sqs->content_to_target_transform = Transform::From(gfx::Transform());
-  sqs->content_bounds = Size::From(size);
-  sqs->visible_content_rect = Rect::From(gfx::Rect(size));
+  sqs->quad_to_target_transform = Transform::From(gfx::Transform());
+  sqs->quad_layer_bounds = Size::From(size);
+  sqs->visible_quad_layer_rect = Rect::From(gfx::Rect(size));
   sqs->clip_rect = Rect::From(gfx::Rect(size));
   sqs->is_clipped = false;
   sqs->opacity = 1.f;
diff --git a/mojo/converters/surfaces/tests/surface_unittest.cc b/mojo/converters/surfaces/tests/surface_unittest.cc
index 355e5ee..64385be 100644
--- a/mojo/converters/surfaces/tests/surface_unittest.cc
+++ b/mojo/converters/surfaces/tests/surface_unittest.cc
@@ -173,10 +173,10 @@
 }
 
 TEST(SurfaceLibTest, SharedQuadState) {
-  gfx::Transform content_to_target_transform;
-  content_to_target_transform.Scale3d(0.3f, 0.7f, 0.9f);
-  gfx::Size content_bounds(57, 39);
-  gfx::Rect visible_content_rect(3, 7, 28, 42);
+  gfx::Transform quad_to_target_transform;
+  quad_to_target_transform.Scale3d(0.3f, 0.7f, 0.9f);
+  gfx::Size quad_layer_bounds(57, 39);
+  gfx::Rect visible_quad_layer_rect(3, 7, 28, 42);
   gfx::Rect clip_rect(9, 12, 21, 31);
   bool is_clipped = true;
   float opacity = 0.65f;
@@ -184,21 +184,17 @@
   ::SkXfermode::Mode blend_mode = ::SkXfermode::kSrcOver_Mode;
   scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
   cc::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
-  sqs->SetAll(content_to_target_transform,
-              content_bounds,
-              visible_content_rect,
-              clip_rect,
-              is_clipped,
-              opacity,
-              blend_mode,
-              sorting_context_id);
+  sqs->SetAll(quad_to_target_transform, quad_layer_bounds,
+              visible_quad_layer_rect, clip_rect, is_clipped, opacity,
+              blend_mode, sorting_context_id);
 
   SharedQuadStatePtr mojo_sqs = SharedQuadState::From(*sqs);
   ASSERT_FALSE(mojo_sqs.is_null());
-  EXPECT_EQ(Transform::From(content_to_target_transform),
-            mojo_sqs->content_to_target_transform);
-  EXPECT_EQ(Size::From(content_bounds), mojo_sqs->content_bounds);
-  EXPECT_EQ(Rect::From(visible_content_rect), mojo_sqs->visible_content_rect);
+  EXPECT_EQ(Transform::From(quad_to_target_transform),
+            mojo_sqs->quad_to_target_transform);
+  EXPECT_EQ(Size::From(quad_layer_bounds), mojo_sqs->quad_layer_bounds);
+  EXPECT_EQ(Rect::From(visible_quad_layer_rect),
+            mojo_sqs->visible_quad_layer_rect);
   EXPECT_EQ(Rect::From(clip_rect), mojo_sqs->clip_rect);
   EXPECT_EQ(is_clipped, mojo_sqs->is_clipped);
   EXPECT_EQ(opacity, mojo_sqs->opacity);
@@ -219,24 +215,19 @@
                transform_to_root_target,
                has_transparent_background);
 
-  gfx::Transform content_to_target_transform;
-  content_to_target_transform.Scale3d(0.3f, 0.7f, 0.9f);
-  gfx::Size content_bounds(57, 39);
-  gfx::Rect visible_content_rect(3, 7, 28, 42);
+  gfx::Transform quad_to_target_transform;
+  quad_to_target_transform.Scale3d(0.3f, 0.7f, 0.9f);
+  gfx::Size quad_layer_bounds(57, 39);
+  gfx::Rect visible_quad_layer_rect(3, 7, 28, 42);
   gfx::Rect clip_rect(9, 12, 21, 31);
   bool is_clipped = true;
   float opacity = 0.65f;
   int sorting_context_id = 13;
   ::SkXfermode::Mode blend_mode = ::SkXfermode::kSrcOver_Mode;
   cc::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
-  sqs->SetAll(content_to_target_transform,
-              content_bounds,
-              visible_content_rect,
-              clip_rect,
-              is_clipped,
-              opacity,
-              blend_mode,
-              sorting_context_id);
+  sqs->SetAll(quad_to_target_transform, quad_layer_bounds,
+              visible_quad_layer_rect, clip_rect, is_clipped, opacity,
+              blend_mode, sorting_context_id);
 
   gfx::Rect rect(5, 7, 13, 19);
   gfx::Rect opaque_rect(rect);
@@ -306,10 +297,9 @@
 
   cc::SharedQuadState* round_trip_sqs =
       round_trip_pass->shared_quad_state_list.front();
-  EXPECT_EQ(content_to_target_transform,
-            round_trip_sqs->content_to_target_transform);
-  EXPECT_EQ(content_bounds, round_trip_sqs->content_bounds);
-  EXPECT_EQ(visible_content_rect, round_trip_sqs->visible_content_rect);
+  EXPECT_EQ(quad_to_target_transform, round_trip_sqs->quad_to_target_transform);
+  EXPECT_EQ(quad_layer_bounds, round_trip_sqs->quad_layer_bounds);
+  EXPECT_EQ(visible_quad_layer_rect, round_trip_sqs->visible_quad_layer_rect);
   EXPECT_EQ(clip_rect, round_trip_sqs->clip_rect);
   EXPECT_EQ(is_clipped, round_trip_sqs->is_clipped);
   EXPECT_EQ(opacity, round_trip_sqs->opacity);
diff --git a/mojo/tools/apptest_runner.py b/mojo/tools/apptest_runner.py
index 269ff703..f216f38 100755
--- a/mojo/tools/apptest_runner.py
+++ b/mojo/tools/apptest_runner.py
@@ -18,14 +18,18 @@
 
 def main():
   parser = argparse.ArgumentParser(description="An application test runner.")
-  parser.add_argument("test_list_file", type=file,
-                      help="a file listing apptests to run")
-  parser.add_argument("build_dir", type=str, help="the build output directory")
-  parser.add_argument("--verbose", default=False, action='store_true')
-  parser.add_argument('--repeat_count', default=1, metavar='INT',
-                      action='store', type=int)
+  parser.add_argument("build_dir", type=str, help="The build output directory.")
+  parser.add_argument("--verbose", default=False, action='store_true',
+                      help="Print additional logging information.")
+  parser.add_argument('--repeat-count', default=1, metavar='INT',
+                      action='store', type=int,
+                      help="The number of times to repeat the set of tests.")
   parser.add_argument('--write-full-results-to', metavar='FILENAME',
-                      help='Path to write the JSON list of full results.')
+                      help='The path to write the JSON list of full results.')
+  parser.add_argument("--test-list-file", metavar='FILENAME', type=file,
+                      default=os.path.abspath(os.path.join(__file__, os.pardir,
+                                                           "data", "apptests")),
+                      help="The file listing apptests to run.")
   args = parser.parse_args()
 
   gtest.set_color()
@@ -50,7 +54,6 @@
       return result
 
   tests = []
-  passed = []
   failed = []
   for _ in range(args.repeat_count):
     for test_dict in test_list:
@@ -62,22 +65,24 @@
       print "Running %s...%s" % (test_name, ("\n" if args.verbose else "")),
       sys.stdout.flush()
 
-      tests.append(test_name)
       assert test_type in ("gtest", "gtest_isolated")
       isolate = test_type == "gtest_isolated"
-      result = gtest.run_apptest(config, shell, test_args, test, isolate)
-      passed.extend([test_name] if result else [])
-      failed.extend([] if result else [test_name])
+      (test, fail) = gtest.run_apptest(config, shell, test_args, test, isolate)
+      tests.extend(test)
+      failed.extend(fail)
+      result = test and not fail
       print "[  PASSED  ]" if result else "[  FAILED  ]",
       print test_name if args.verbose or not result else ""
 
     if failed:
       break;
 
-  print "[  PASSED  ] %d apptests" % len(passed),
-  print ": %s" % ", ".join(passed) if passed else ""
-  print "[  FAILED  ] %d apptests" % len(failed),
-  print ": %s" % ", ".join(failed) if failed else ""
+  print "[==========] %d tests ran." % len(tests)
+  print "[  PASSED  ] %d tests." % (len(tests) - len(failed))
+  if failed:
+    print "[  FAILED  ] %d tests, listed below:" % len(failed)
+    for failure in failed:
+      print "[  FAILED  ] %s" % failure
 
   if args.write_full_results_to:
     _WriteJSONResults(tests, failed, args.write_full_results_to)
diff --git a/mojo/tools/mopy/gtest.py b/mojo/tools/mopy/gtest.py
index dd47c6f..b4ffbcd 100644
--- a/mojo/tools/mopy/gtest.py
+++ b/mojo/tools/mopy/gtest.py
@@ -24,6 +24,8 @@
 def run_apptest(config, shell, args, apptest, isolate):
   """Run the apptest; optionally isolating fixtures across shell invocations.
 
+  Returns the list of tests run and the list of failures.
+
   Args:
     config: The mopy.config.Config for the build.
     shell: The mopy.android.AndroidShell, if Android is the target platform.
@@ -31,16 +33,19 @@
     apptest: The application test URL.
     isolate: True if the test fixtures should be run in isolation.
   """
+  tests = [apptest]
+  failed = []
   if not isolate:
-    return _run_apptest(config, shell, args, apptest)
-
-  fixtures = _get_fixtures(config, shell, args, apptest)
-  result = True if fixtures else False
-  for fixture in fixtures:
-    arguments = args + ["--gtest_filter=%s" % fixture]
-    if not _run_apptest(config, shell, arguments, apptest):
-      result = False
-  return result
+    # TODO(msw): Parse fixture-granular successes and failures in this case.
+    if not _run_apptest(config, shell, args, apptest):
+      failed.append(apptest)
+  else:
+    tests = _get_fixtures(config, shell, args, apptest)
+    for fixture in tests:
+      arguments = args + ["--gtest_filter=%s" % fixture]
+      if not _run_apptest(config, shell, arguments, apptest):
+        failed.append(fixture)
+  return (tests, failed)
 
 
 def _run_apptest(config, shell, args, apptest):
@@ -69,8 +74,10 @@
 
 def _get_fixtures(config, shell, args, apptest):
   """Returns an apptest's "Suite.Fixture" list via --gtest_list_tests output."""
+  arguments = args + ["--gtest_list_tests"]
+  command = _build_command_line(config, args, apptest)
+  logging.getLogger().debug("Command: %s" % " ".join(command))
   try:
-    arguments = args + ["--gtest_list_tests"]
     tests = _run_test_with_timeout(config, shell, arguments, apptest)
     logging.getLogger().debug("Tests for %s:\n%s" % (apptest, tests))
     # Remove log lines from the output and ensure it matches known formatting.
@@ -88,7 +95,7 @@
       test_list.append(suite + line.strip())
     return test_list
   except Exception as e:
-    _print_error(_build_command_line(config, arguments, apptest), e)
+    _print_error(command, e)
   return []
 
 
@@ -106,9 +113,10 @@
 def _build_command_line(config, args, apptest):
   """Build the apptest command line. This value isn't executed on Android."""
   paths = Paths(config)
-  # On linux always run with xvfb.
-  prefix = [paths.xvfb, paths.build_dir] if (config.target_os ==
-                                             Config.OS_LINUX) else []
+  # On Linux, always run tests with xvfb, but not for --gtest_list_tests.
+  use_xvfb = (config.target_os == Config.OS_LINUX and
+              not "--gtest_list_tests" in args)
+  prefix = [paths.xvfb, paths.build_dir] if use_xvfb else []
   return prefix + [paths.mojo_runner] + args + [apptest]
 
 
diff --git a/native_client_sdk/src/examples/api/video_encode/video_encode.cc b/native_client_sdk/src/examples/api/video_encode/video_encode.cc
index 806bca4..8ca71ee 100644
--- a/native_client_sdk/src/examples/api/video_encode/video_encode.cc
+++ b/native_client_sdk/src/examples/api/video_encode/video_encode.cc
@@ -6,6 +6,7 @@
 #include <stdio.h>
 #include <string.h>
 
+#include <algorithm>
 #include <iostream>
 #include <map>
 #include <sstream>
@@ -175,6 +176,8 @@
   pp::VideoFrame current_track_frame_;
 
   IVFWriter ivf_writer_;
+
+  PP_Time last_encode_tick_;
 };
 
 VideoEncoderInstance::VideoEncoderInstance(PP_Instance instance,
@@ -188,7 +191,8 @@
       video_profile_(PP_VIDEOPROFILE_H264MAIN),
 #endif
       frame_format_(PP_VIDEOFRAME_FORMAT_I420),
-      encoded_frames_(0) {
+      encoded_frames_(0),
+      last_encode_tick_(0) {
   InitializeVideoProfiles();
   ProbeEncoder();
 }
@@ -334,10 +338,12 @@
 }
 
 void VideoEncoderInstance::ScheduleNextEncode() {
+  PP_Time now = pp::Module::Get()->core()->GetTime();
   pp::Module::Get()->core()->CallOnMainThread(
-      1000 / 30,
+      std::min(std::max(now - last_encode_tick_, 0.0), 1000.0 / 30),
       callback_factory_.NewCallback(&VideoEncoderInstance::GetEncoderFrameTick),
       0);
+  last_encode_tick_ = now;
 }
 
 void VideoEncoderInstance::GetEncoderFrameTick(int32_t result) {
diff --git a/native_client_sdk/src/tools/sel_ldr.py b/native_client_sdk/src/tools/sel_ldr.py
index e8d487f0..30be7d20 100755
--- a/native_client_sdk/src/tools/sel_ldr.py
+++ b/native_client_sdk/src/tools/sel_ldr.py
@@ -64,6 +64,8 @@
                            'libraries rather then release')
   parser.add_argument('executable', help='executable (.nexe) to run')
   parser.add_argument('args', nargs='*', help='argument to pass to exectuable')
+  parser.add_argument('--library-path',
+                      help='Pass extra library paths')
 
   # To enable bash completion for this command first install optcomplete
   # and then add this line to your .bashrc:
@@ -139,24 +141,29 @@
 
   if dynamic:
     if options.debug_libs:
-      libpath = os.path.join(NACL_SDK_ROOT, 'lib',
-                            'glibc_%s' % arch_suffix, 'Debug')
+      sdk_lib_dir = os.path.join(NACL_SDK_ROOT, 'lib',
+                                 'glibc_%s' % arch_suffix, 'Debug')
     else:
-      libpath = os.path.join(NACL_SDK_ROOT, 'lib',
-                            'glibc_%s' % arch_suffix, 'Release')
+      sdk_lib_dir = os.path.join(NACL_SDK_ROOT, 'lib',
+                                 'glibc_%s' % arch_suffix, 'Release')
     toolchain = '%s_x86_glibc' % osname
-    sdk_lib_dir = os.path.join(NACL_SDK_ROOT, 'toolchain',
-                               toolchain, 'x86_64-nacl')
+    toolchain_dir = os.path.join(NACL_SDK_ROOT, 'toolchain', toolchain)
+    sdk_lib_dir = os.path.join(toolchain_dir, 'x86_64-nacl')
     if arch == 'x86-64':
-      sdk_lib_dir = os.path.join(sdk_lib_dir, 'lib')
+      lib_dir = os.path.join(sdk_lib_dir, 'lib')
+      usr_lib_dir = os.path.join(toolchain_dir, 'x86_64-nacl', 'usr', 'lib')
     else:
-      sdk_lib_dir = os.path.join(sdk_lib_dir, 'lib32')
+      lib_dir = os.path.join(sdk_lib_dir, 'lib32')
+      usr_lib_dir = os.path.join(toolchain_dir, 'i686-nacl', 'usr', 'lib')
     ldso = os.path.join(sdk_lib_dir, 'runnable-ld.so')
     cmd.append(ldso)
     Log('LD.SO = %s' % ldso)
-    libpath += ':' + sdk_lib_dir
+    libpath = [usr_lib_dir, sdk_lib_dir, lib_dir]
+    if options.library_path:
+      libpath.extend([os.path.abspath(p) for p
+                      in options.library_path.split(':')])
     cmd.append('--library-path')
-    cmd.append(libpath)
+    cmd.append(':'.join(libpath))
 
 
   # Append arguments for the executable itself.
diff --git a/net/base/load_flags_list.h b/net/base/load_flags_list.h
index cdb52059..8e09a8b 100644
--- a/net/base/load_flags_list.h
+++ b/net/base/load_flags_list.h
@@ -86,6 +86,3 @@
 // reduction proxy.
 // TODO(rcs): Remove this flag as soon as http://crbug.com/339237 is resolved.
 LOAD_FLAG(BYPASS_DATA_REDUCTION_PROXY, 1 << 18)
-
-// Indicates the the request is an asynchronous revalidation.
-LOAD_FLAG(ASYNC_REVALIDATION, 1 << 19)
diff --git a/net/base/network_change_notifier_mac.cc b/net/base/network_change_notifier_mac.cc
index 14ec554..f8caee80 100644
--- a/net/base/network_change_notifier_mac.cc
+++ b/net/base/network_change_notifier_mac.cc
@@ -9,6 +9,7 @@
 
 #include "base/basictypes.h"
 #include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
 #include "net/dns/dns_config_service.h"
 
 namespace net {
@@ -102,6 +103,7 @@
 
 NetworkChangeNotifier::ConnectionType
 NetworkChangeNotifierMac::GetCurrentConnectionType() const {
+  base::ThreadRestrictions::ScopedAllowWait allow_wait;
   base::AutoLock lock(connection_type_lock_);
   // Make sure the initial connection type is set before returning.
   while (!connection_type_initialized_) {
diff --git a/net/base/upload_bytes_element_reader.h b/net/base/upload_bytes_element_reader.h
index 4864c9a..a5ee8a7 100644
--- a/net/base/upload_bytes_element_reader.h
+++ b/net/base/upload_bytes_element_reader.h
@@ -10,13 +10,13 @@
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
+#include "net/base/net_export.h"
 #include "net/base/upload_element_reader.h"
 
 namespace net {
 
-// An UploadElementReader implementation for bytes.
-// |data| should outlive this class because this class does not take the
-// ownership of the data.
+// An UploadElementReader implementation for bytes. The caller owns |bytes|,
+// and is responsible for ensuring it outlives the UploadBytesElementReader.
 class NET_EXPORT UploadBytesElementReader : public UploadElementReader {
  public:
   UploadBytesElementReader(const char* bytes, uint64_t length);
diff --git a/net/base/upload_disk_cache_entry_element_reader.cc b/net/base/upload_disk_cache_entry_element_reader.cc
new file mode 100644
index 0000000..0635aeb
--- /dev/null
+++ b/net/base/upload_disk_cache_entry_element_reader.cc
@@ -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.
+
+#include "net/base/upload_disk_cache_entry_element_reader.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/disk_cache.h"
+
+namespace net {
+
+UploadDiskCacheEntryElementReader::UploadDiskCacheEntryElementReader(
+    disk_cache::Entry* disk_cache_entry,
+    int disk_cache_stream_index,
+    int range_offset,
+    int range_length)
+    : disk_cache_entry_(disk_cache_entry),
+      disk_cache_stream_index_(disk_cache_stream_index),
+      range_begin_offset_(range_offset),
+      range_end_offset_(range_offset + range_length),
+      current_read_offset_(range_offset),
+      weak_factory_(this) {
+  DCHECK_LE(0, range_offset);
+  DCHECK_LT(0, range_length);
+  DCHECK_LE(range_offset + range_length,
+            disk_cache_entry_->GetDataSize(disk_cache_stream_index_));
+}
+
+UploadDiskCacheEntryElementReader::~UploadDiskCacheEntryElementReader() {
+}
+
+const UploadDiskCacheEntryElementReader*
+UploadDiskCacheEntryElementReader::AsDiskCacheEntryReaderForTests() const {
+  return this;
+}
+
+int UploadDiskCacheEntryElementReader::Init(
+    const CompletionCallback& callback) {
+  weak_factory_.InvalidateWeakPtrs();
+  current_read_offset_ = range_begin_offset_;
+  return OK;
+}
+
+uint64_t UploadDiskCacheEntryElementReader::GetContentLength() const {
+  return range_end_offset_ - range_begin_offset_;
+}
+
+uint64_t UploadDiskCacheEntryElementReader::BytesRemaining() const {
+  return range_end_offset_ - current_read_offset_;
+}
+
+bool UploadDiskCacheEntryElementReader::IsInMemory() const {
+  return false;
+}
+
+int UploadDiskCacheEntryElementReader::Read(
+    IOBuffer* buf,
+    int buf_length,
+    const CompletionCallback& callback) {
+  DCHECK(!callback.is_null());
+  int bytes_to_read = std::min(buf_length, static_cast<int>(BytesRemaining()));
+
+  CompletionCallback new_callback =
+      base::Bind(&UploadDiskCacheEntryElementReader::OnReadCompleted,
+                 weak_factory_.GetWeakPtr(), callback);
+
+  int result = disk_cache_entry_->ReadData(disk_cache_stream_index_,
+                                           current_read_offset_, buf,
+                                           bytes_to_read, new_callback);
+  if (result == ERR_IO_PENDING)
+    return ERR_IO_PENDING;
+  if (result > 0)
+    current_read_offset_ += result;
+  return result;
+}
+
+void UploadDiskCacheEntryElementReader::OnReadCompleted(
+    const CompletionCallback& callback,
+    int result) {
+  if (result > 0)
+    current_read_offset_ += result;
+  callback.Run(result);
+}
+
+}  // namespace net
diff --git a/net/base/upload_disk_cache_entry_element_reader.h b/net/base/upload_disk_cache_entry_element_reader.h
new file mode 100644
index 0000000..1885b2e9
--- /dev/null
+++ b/net/base/upload_disk_cache_entry_element_reader.h
@@ -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.
+
+#ifndef NET_BASE_UPLOAD_DISK_CACHE_ENTRY_ELEMENT_READER_H_
+#define NET_BASE_UPLOAD_DISK_CACHE_ENTRY_ELEMENT_READER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_export.h"
+#include "net/base/upload_element_reader.h"
+
+namespace disk_cache {
+class Entry;
+}
+
+namespace net {
+
+// An UploadElementReader implementation for disk_cache::Entry objects. The
+// caller keeps ownership of |disk_cache_entry|, and is responsible for ensuring
+// it outlives the UploadDiskCacheEntryElementReader.
+class NET_EXPORT UploadDiskCacheEntryElementReader
+    : public UploadElementReader {
+ public:
+  // Construct a new UploadDiskCacheEntryElementReader which reads from the disk
+  // cache entry |disk_cache_entry| with stream index |disk_cache_stream_index|.
+  // The new upload reader object will read |range_length| bytes, starting from
+  // |range_offset|. To read an whole cache entry give a 0 as |range_offset| and
+  // provide the length of the entry's stream as |range_length|.
+  UploadDiskCacheEntryElementReader(disk_cache::Entry* disk_cache_entry,
+                                    int disk_cache_stream_index,
+                                    int range_offset,
+                                    int range_length);
+  ~UploadDiskCacheEntryElementReader() override;
+
+  int range_offset_for_tests() const { return range_begin_offset_; }
+  int range_length_for_tests() const {
+    return range_end_offset_ - range_begin_offset_;
+  }
+
+  // UploadElementReader overrides:
+  const UploadDiskCacheEntryElementReader* AsDiskCacheEntryReaderForTests()
+      const override;
+  int Init(const CompletionCallback& callback) override;
+  uint64_t GetContentLength() const override;
+  uint64_t BytesRemaining() const override;
+  bool IsInMemory() const override;
+  int Read(IOBuffer* buf,
+           int buf_length,
+           const CompletionCallback& callback) override;
+
+ private:
+  void OnReadCompleted(const CompletionCallback& callback, int result);
+
+  disk_cache::Entry* const disk_cache_entry_;
+  const int disk_cache_stream_index_;
+
+  const int range_begin_offset_;
+  const int range_end_offset_;
+
+  int current_read_offset_;
+
+  base::WeakPtrFactory<UploadDiskCacheEntryElementReader> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(UploadDiskCacheEntryElementReader);
+};
+
+}  // namespace net
+
+#endif  // NET_BASE_UPLOAD_DISK_CACHE_ENTRY_ELEMENT_READER_H_
diff --git a/net/base/upload_disk_cache_entry_element_reader_unittest.cc b/net/base/upload_disk_cache_entry_element_reader_unittest.cc
new file mode 100644
index 0000000..2612cfa
--- /dev/null
+++ b/net/base/upload_disk_cache_entry_element_reader_unittest.cc
@@ -0,0 +1,331 @@
+// 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/base/upload_disk_cache_entry_element_reader.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/disk_cache/disk_cache.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace net {
+namespace {
+
+const int kTestDiskCacheStreamIndex = 0;
+
+const char kDataKey[] = "a key";
+
+const char kData[] = "this is data in a disk cache entry";
+const size_t kDataSize = arraysize(kData) - 1;
+
+// A disk_cache::Entry that arbitrarily delays the completion of a read
+// operation to allow testing some races without flake. This is particularly
+// relevant in this unit test, which uses the always-synchronous MEMORY_CACHE.
+class DelayedReadEntry : public disk_cache::Entry {
+ public:
+  explicit DelayedReadEntry(disk_cache::ScopedEntryPtr entry)
+      : entry_(entry.Pass()) {}
+  ~DelayedReadEntry() override { EXPECT_FALSE(HasPendingReadCallbacks()); }
+
+  bool HasPendingReadCallbacks() { return !pending_read_callbacks_.empty(); }
+
+  void RunPendingReadCallbacks() {
+    std::vector<base::Callback<void(void)>> callbacks;
+    pending_read_callbacks_.swap(callbacks);
+    for (const auto& callback : callbacks)
+      callback.Run();
+  }
+
+  // From disk_cache::Entry:
+  void Doom() override { entry_->Doom(); }
+
+  void Close() override { delete this; }  // Note this is required by the API.
+
+  std::string GetKey() const override { return entry_->GetKey(); }
+
+  base::Time GetLastUsed() const override { return entry_->GetLastUsed(); }
+
+  base::Time GetLastModified() const override {
+    return entry_->GetLastModified();
+  }
+
+  int32 GetDataSize(int index) const override {
+    return entry_->GetDataSize(index);
+  }
+
+  int ReadData(int index,
+               int offset,
+               IOBuffer* buf,
+               int buf_len,
+               const CompletionCallback& original_callback) override {
+    TestCompletionCallback callback;
+    int rv = entry_->ReadData(index, offset, buf, buf_len, callback.callback());
+    DCHECK_NE(rv, ERR_IO_PENDING)
+        << "Test expects to use a MEMORY_CACHE instance, which is synchronous.";
+    pending_read_callbacks_.push_back(base::Bind(original_callback, rv));
+    return ERR_IO_PENDING;
+  }
+
+  int WriteData(int index,
+                int offset,
+                IOBuffer* buf,
+                int buf_len,
+                const CompletionCallback& callback,
+                bool truncate) override {
+    return entry_->WriteData(index, offset, buf, buf_len, callback, truncate);
+  }
+
+  int ReadSparseData(int64 offset,
+                     IOBuffer* buf,
+                     int buf_len,
+                     const CompletionCallback& callback) override {
+    return entry_->ReadSparseData(offset, buf, buf_len, callback);
+  }
+
+  int WriteSparseData(int64 offset,
+                      IOBuffer* buf,
+                      int buf_len,
+                      const CompletionCallback& callback) override {
+    return entry_->WriteSparseData(offset, buf, buf_len, callback);
+  }
+
+  int GetAvailableRange(int64 offset,
+                        int len,
+                        int64* start,
+                        const CompletionCallback& callback) override {
+    return entry_->GetAvailableRange(offset, len, start, callback);
+  }
+
+  bool CouldBeSparse() const override { return entry_->CouldBeSparse(); }
+
+  void CancelSparseIO() override { entry_->CancelSparseIO(); }
+
+  int ReadyForSparseIO(const CompletionCallback& callback) override {
+    return ReadyForSparseIO(callback);
+  }
+
+ private:
+  disk_cache::ScopedEntryPtr entry_;
+  std::vector<base::Callback<void(void)>> pending_read_callbacks_;
+};
+
+class UploadDiskCacheEntryElementReaderTest : public PlatformTest {
+ public:
+  UploadDiskCacheEntryElementReaderTest() {}
+
+  ~UploadDiskCacheEntryElementReaderTest() override {}
+
+  void SetUp() override {
+    TestCompletionCallback callback;
+    int rv = disk_cache::CreateCacheBackend(
+        MEMORY_CACHE, CACHE_BACKEND_DEFAULT, base::FilePath(), 0, false,
+        nullptr, nullptr, &cache_, callback.callback());
+    ASSERT_EQ(OK, callback.GetResult(rv));
+
+    disk_cache::Entry* tmp_entry = nullptr;
+    rv = cache_->CreateEntry(kDataKey, &tmp_entry, callback.callback());
+    ASSERT_EQ(OK, callback.GetResult(rv));
+    entry_.reset(tmp_entry);
+
+    scoped_refptr<IOBuffer> io_buffer = new WrappedIOBuffer(kData);
+    rv = entry_->WriteData(kTestDiskCacheStreamIndex, 0, io_buffer.get(),
+                           kDataSize, callback.callback(), false);
+    EXPECT_EQ(static_cast<int>(kDataSize), callback.GetResult(rv));
+  }
+
+  void set_entry(disk_cache::ScopedEntryPtr entry) { entry_.swap(entry); }
+  disk_cache::Entry* entry() { return entry_.get(); }
+  disk_cache::ScopedEntryPtr release_entry() { return entry_.Pass(); }
+
+ private:
+  scoped_ptr<disk_cache::Backend> cache_;
+  disk_cache::ScopedEntryPtr entry_;
+};
+
+TEST_F(UploadDiskCacheEntryElementReaderTest, ReadAll) {
+  UploadDiskCacheEntryElementReader reader(entry(), kTestDiskCacheStreamIndex,
+                                           0, kDataSize);
+  EXPECT_EQ(static_cast<uint64_t>(kDataSize), reader.BytesRemaining());
+
+  char read_buffer[kDataSize];
+  std::fill(read_buffer, read_buffer + arraysize(read_buffer), '\0');
+
+  scoped_refptr<IOBuffer> io_buffer = new WrappedIOBuffer(read_buffer);
+  TestCompletionCallback callback;
+  int rv = reader.Read(io_buffer.get(), kDataSize, callback.callback());
+  EXPECT_EQ(static_cast<int>(kDataSize), callback.GetResult(rv));
+  EXPECT_EQ(0U, reader.BytesRemaining())
+      << "Expected a single read of |kDataSize| to retrieve entire entry.";
+  EXPECT_EQ(std::string(kData, kDataSize), std::string(read_buffer, kDataSize));
+}
+
+TEST_F(UploadDiskCacheEntryElementReaderTest, ReadPartially) {
+  UploadDiskCacheEntryElementReader reader(entry(), kTestDiskCacheStreamIndex,
+                                           0, kDataSize);
+  EXPECT_EQ(static_cast<uint64_t>(kDataSize), reader.BytesRemaining());
+
+  const size_t kReadBuffer1Size = kDataSize / 3;
+  char read_buffer1[kReadBuffer1Size];
+  std::fill(read_buffer1, read_buffer1 + arraysize(read_buffer1), '\0');
+
+  scoped_refptr<IOBuffer> io_buffer1 = new WrappedIOBuffer(read_buffer1);
+
+  const size_t kReadBuffer2Size = kDataSize - kReadBuffer1Size;
+  char read_buffer2[kReadBuffer2Size];
+  scoped_refptr<IOBuffer> io_buffer2 = new WrappedIOBuffer(read_buffer2);
+
+  TestCompletionCallback callback;
+  int rv = reader.Read(io_buffer1.get(), kReadBuffer1Size, callback.callback());
+  EXPECT_EQ(static_cast<int>(kReadBuffer1Size), callback.GetResult(rv));
+  EXPECT_EQ(static_cast<uint64_t>(kReadBuffer2Size), reader.BytesRemaining());
+
+  rv = reader.Read(io_buffer2.get(), kReadBuffer2Size, callback.callback());
+  EXPECT_EQ(static_cast<int>(kReadBuffer2Size), callback.GetResult(rv));
+  EXPECT_EQ(0U, reader.BytesRemaining());
+
+  EXPECT_EQ(std::string(kData, kDataSize),
+            std::string(read_buffer1, kReadBuffer1Size) +
+                std::string(read_buffer2, kReadBuffer2Size));
+}
+
+TEST_F(UploadDiskCacheEntryElementReaderTest, ReadTooMuch) {
+  UploadDiskCacheEntryElementReader reader(entry(), kTestDiskCacheStreamIndex,
+                                           0, kDataSize);
+  EXPECT_EQ(static_cast<uint64_t>(kDataSize), reader.BytesRemaining());
+
+  const size_t kTooLargeSize = kDataSize + kDataSize / 2;
+
+  char read_buffer[kTooLargeSize];
+  std::fill(read_buffer, read_buffer + arraysize(read_buffer), '\0');
+
+  scoped_refptr<IOBuffer> io_buffer = new WrappedIOBuffer(read_buffer);
+  TestCompletionCallback callback;
+  int rv = reader.Read(io_buffer.get(), kTooLargeSize, callback.callback());
+  EXPECT_EQ(static_cast<int>(kDataSize), callback.GetResult(rv));
+  EXPECT_EQ(0U, reader.BytesRemaining());
+  EXPECT_EQ(std::string(kData, kDataSize), std::string(read_buffer, kDataSize));
+}
+
+TEST_F(UploadDiskCacheEntryElementReaderTest, ReadAsync) {
+  DelayedReadEntry* delayed_read_entry = new DelayedReadEntry(release_entry());
+  set_entry(disk_cache::ScopedEntryPtr(delayed_read_entry));
+
+  UploadDiskCacheEntryElementReader reader(entry(), kTestDiskCacheStreamIndex,
+                                           0, kDataSize);
+
+  char read_buffer[kDataSize];
+  std::fill(read_buffer, read_buffer + arraysize(read_buffer), '\0');
+
+  scoped_refptr<IOBuffer> io_buffer = new WrappedIOBuffer(read_buffer);
+  TestCompletionCallback callback;
+  int rv = reader.Read(io_buffer.get(), kDataSize, callback.callback());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_TRUE(delayed_read_entry->HasPendingReadCallbacks());
+  EXPECT_EQ(static_cast<uint64_t>(kDataSize), reader.BytesRemaining());
+
+  delayed_read_entry->RunPendingReadCallbacks();
+  EXPECT_EQ(static_cast<int>(kDataSize), callback.GetResult(rv));
+  EXPECT_EQ(0U, reader.BytesRemaining())
+      << "Expected a single read of |kDataSize| to retrieve entire entry.";
+  EXPECT_EQ(std::string(kData, kDataSize), std::string(read_buffer, kDataSize));
+}
+
+TEST_F(UploadDiskCacheEntryElementReaderTest, MultipleInit) {
+  UploadDiskCacheEntryElementReader reader(entry(), kTestDiskCacheStreamIndex,
+                                           0, kDataSize);
+  char read_buffer[kDataSize];
+  std::fill(read_buffer, read_buffer + arraysize(read_buffer), '\0');
+
+  scoped_refptr<IOBuffer> io_buffer = new WrappedIOBuffer(read_buffer);
+  TestCompletionCallback callback;
+  int rv = reader.Read(io_buffer.get(), kDataSize, callback.callback());
+  EXPECT_EQ(static_cast<int>(kDataSize), callback.GetResult(rv));
+  EXPECT_EQ(std::string(kData, kDataSize), std::string(read_buffer, kDataSize));
+
+  rv = reader.Init(callback.callback());
+  EXPECT_EQ(OK, callback.GetResult(rv));
+  EXPECT_EQ(static_cast<uint64_t>(kDataSize), reader.BytesRemaining());
+  rv = reader.Read(io_buffer.get(), kDataSize, callback.callback());
+  EXPECT_EQ(static_cast<int>(kDataSize), callback.GetResult(rv));
+  EXPECT_EQ(std::string(kData, kDataSize), std::string(read_buffer, kDataSize));
+}
+
+TEST_F(UploadDiskCacheEntryElementReaderTest, InitDuringAsyncOperation) {
+  DelayedReadEntry* delayed_read_entry = new DelayedReadEntry(release_entry());
+  set_entry(disk_cache::ScopedEntryPtr(delayed_read_entry));
+
+  UploadDiskCacheEntryElementReader reader(entry(), kTestDiskCacheStreamIndex,
+                                           0, kDataSize);
+  char read_buffer[kDataSize];
+  std::fill(read_buffer, read_buffer + arraysize(read_buffer), '\0');
+
+  scoped_refptr<IOBuffer> io_buffer = new WrappedIOBuffer(read_buffer);
+  TestCompletionCallback read_callback;
+  int rv = reader.Read(io_buffer.get(), kDataSize, read_callback.callback());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_TRUE(delayed_read_entry->HasPendingReadCallbacks());
+  EXPECT_EQ(static_cast<uint64_t>(kDataSize), reader.BytesRemaining());
+
+  TestCompletionCallback init_callback;
+  rv = reader.Init(init_callback.callback());
+  EXPECT_EQ(OK, init_callback.GetResult(rv));
+
+  delayed_read_entry->RunPendingReadCallbacks();
+  EXPECT_FALSE(delayed_read_entry->HasPendingReadCallbacks());
+  EXPECT_EQ(static_cast<uint64_t>(kDataSize), reader.BytesRemaining());
+
+  char read_buffer2[kDataSize];
+  std::fill(read_buffer2, read_buffer2 + arraysize(read_buffer2), '\0');
+  scoped_refptr<IOBuffer> io_buffer2 = new WrappedIOBuffer(read_buffer2);
+  TestCompletionCallback read_callback2;
+  rv = reader.Read(io_buffer2.get(), kDataSize, read_callback2.callback());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_TRUE(delayed_read_entry->HasPendingReadCallbacks());
+  EXPECT_EQ(static_cast<uint64_t>(kDataSize), reader.BytesRemaining());
+
+  delayed_read_entry->RunPendingReadCallbacks();
+  EXPECT_FALSE(delayed_read_entry->HasPendingReadCallbacks());
+  read_callback2.WaitForResult();  // Succeeds if this does not deadlock.
+  EXPECT_EQ(std::string(kData, kDataSize),
+            std::string(read_buffer2, kDataSize));
+}
+
+TEST_F(UploadDiskCacheEntryElementReaderTest, Range) {
+  const size_t kOffset = kDataSize / 4;
+  const size_t kLength = kDataSize / 3;
+
+  UploadDiskCacheEntryElementReader reader(entry(), kTestDiskCacheStreamIndex,
+                                           kOffset, kLength);
+  EXPECT_EQ(static_cast<uint64_t>(kLength), reader.BytesRemaining());
+
+  char read_buffer[kLength];
+  std::fill(read_buffer, read_buffer + arraysize(read_buffer), '\0');
+
+  scoped_refptr<IOBuffer> io_buffer = new WrappedIOBuffer(read_buffer);
+  TestCompletionCallback callback;
+  int rv = reader.Read(io_buffer.get(), kLength, callback.callback());
+  EXPECT_EQ(static_cast<int>(kLength), callback.GetResult(rv));
+  EXPECT_EQ(0U, reader.BytesRemaining());
+  EXPECT_EQ(std::string(kData + kOffset, kLength),
+            std::string(read_buffer, kLength));
+}
+
+}  // namespace
+}  // namespace net
diff --git a/net/base/upload_element_reader.cc b/net/base/upload_element_reader.cc
index f44931c..70b657c 100644
--- a/net/base/upload_element_reader.cc
+++ b/net/base/upload_element_reader.cc
@@ -6,12 +6,17 @@
 
 namespace net {
 
+const UploadDiskCacheEntryElementReader*
+UploadElementReader::AsDiskCacheEntryReaderForTests() const {
+  return nullptr;
+}
+
 const UploadBytesElementReader* UploadElementReader::AsBytesReader() const {
-  return NULL;
+  return nullptr;
 }
 
 const UploadFileElementReader* UploadElementReader::AsFileReader() const {
-  return NULL;
+  return nullptr;
 }
 
 bool UploadElementReader::IsInMemory() const {
diff --git a/net/base/upload_element_reader.h b/net/base/upload_element_reader.h
index 2814efa..267df8c 100644
--- a/net/base/upload_element_reader.h
+++ b/net/base/upload_element_reader.h
@@ -13,6 +13,7 @@
 
 class IOBuffer;
 class UploadBytesElementReader;
+class UploadDiskCacheEntryElementReader;
 class UploadFileElementReader;
 
 // An interface to read an upload data element.
@@ -21,6 +22,11 @@
   UploadElementReader() {}
   virtual ~UploadElementReader() {}
 
+  // Returns this instance's pointer as UploadDiskCacheEntryElementReader when
+  // possible, otherwise returns nullptr.
+  virtual const UploadDiskCacheEntryElementReader*
+  AsDiskCacheEntryReaderForTests() const;
+
   // Returns this instance's pointer as UploadBytesElementReader when possible,
   // otherwise returns NULL.
   virtual const UploadBytesElementReader* AsBytesReader() const;
diff --git a/net/base/upload_file_element_reader.h b/net/base/upload_file_element_reader.h
index 010d660..bf46350e 100644
--- a/net/base/upload_file_element_reader.h
+++ b/net/base/upload_file_element_reader.h
@@ -13,6 +13,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "net/base/net_export.h"
 #include "net/base/upload_element_reader.h"
 
 namespace base {
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index 634f8adc..796aeea5 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -30,7 +30,6 @@
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
-#include "net/base/network_delegate.h"
 #include "net/base/upload_data_stream.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/http/disk_based_cert_cache.h"
@@ -295,159 +294,6 @@
 };
 
 //-----------------------------------------------------------------------------
-
-class HttpCache::AsyncValidation {
- public:
-  AsyncValidation(const HttpRequestInfo& original_request, HttpCache* cache)
-      : request_(original_request), cache_(cache) {}
-  ~AsyncValidation() {}
-
-  void Start(const BoundNetLog& net_log,
-             scoped_ptr<Transaction> transaction,
-             NetworkDelegate* network_delegate);
-
- private:
-  void OnStarted(int result);
-  void DoRead();
-  void OnRead(int result);
-
-  // Terminate this request with net error code |result|. Logs the transaction
-  // result and asks HttpCache to delete this object.
-  // If there was a client or server certificate error, it cannot be recovered
-  // asynchronously, so we need to prevent future attempts to asynchronously
-  // fetch the resource. In this case, the cache entry is doomed.
-  void Terminate(int result);
-
-  HttpRequestInfo request_;
-  scoped_refptr<IOBuffer> buf_;
-  CompletionCallback read_callback_;
-  scoped_ptr<Transaction> transaction_;
-  base::Time start_time_;
-
-  // The HttpCache object owns this object. This object is always deleted before
-  // the pointer to the cache becomes invalid.
-  HttpCache* cache_;
-
-  DISALLOW_COPY_AND_ASSIGN(AsyncValidation);
-};
-
-void HttpCache::AsyncValidation::Start(const BoundNetLog& net_log,
-                                       scoped_ptr<Transaction> transaction,
-                                       NetworkDelegate* network_delegate) {
-  transaction_ = transaction.Pass();
-  if (network_delegate) {
-    // This code is necessary to enable async transactions to pass over the
-    // data-reduction proxy. This is a violation of the "once-and-only-once"
-    // principle, since it copies code from URLRequestHttpJob. We cannot use the
-    // original callback passed to HttpCache::Transaction by URLRequestHttpJob
-    // as it will only be valid as long as the URLRequestHttpJob object is
-    // alive, and that object will be deleted as soon as the synchronous request
-    // completes.
-    //
-    // This code is also an encapsulation violation. We are exploiting the fact
-    // that the |request| parameter to NotifyBeforeSendProxyHeaders() is never
-    // actually used for anything, and so can be NULL.
-    //
-    // TODO(ricea): Do this better.
-    transaction_->SetBeforeProxyHeadersSentCallback(
-        base::Bind(&NetworkDelegate::NotifyBeforeSendProxyHeaders,
-                   base::Unretained(network_delegate),
-                   static_cast<URLRequest*>(NULL)));
-    // The above use of base::Unretained is safe because the NetworkDelegate has
-    // to live at least as long as the HttpNetworkSession which has to live as
-    // least as long as the HttpNetworkLayer which has to live at least as long
-    // this HttpCache object.
-  }
-
-  DCHECK_EQ(0, request_.load_flags & LOAD_ASYNC_REVALIDATION);
-  request_.load_flags |= LOAD_ASYNC_REVALIDATION;
-  start_time_ = cache_->clock()->Now();
-  // This use of base::Unretained is safe because |transaction_| is owned by
-  // this object.
-  read_callback_ = base::Bind(&AsyncValidation::OnRead, base::Unretained(this));
-  // This use of base::Unretained is safe as above.
-  int rv = transaction_->Start(
-      &request_,
-      base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)),
-      net_log);
-
-  if (rv == ERR_IO_PENDING)
-    return;
-
-  OnStarted(rv);
-}
-
-void HttpCache::AsyncValidation::OnStarted(int result) {
-  if (result != OK) {
-    DVLOG(1) << "Asynchronous transaction start failed for " << request_.url;
-    Terminate(result);
-    return;
-  }
-
-  while (transaction_->IsReadyToRestartForAuth()) {
-    // This code is based on URLRequestHttpJob::RestartTransactionWithAuth,
-    // however when we do this here cookies on the response will not be
-    // stored. Fortunately only a tiny number of sites set cookies on 401
-    // responses, and none of them use stale-while-revalidate.
-    result = transaction_->RestartWithAuth(
-        AuthCredentials(),
-        base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)));
-    if (result == ERR_IO_PENDING)
-      return;
-    if (result != OK) {
-      DVLOG(1) << "Synchronous transaction restart with auth failed for "
-               << request_.url;
-      Terminate(result);
-      return;
-    }
-  }
-
-  DoRead();
-}
-
-void HttpCache::AsyncValidation::DoRead() {
-  const size_t kBufSize = 4096;
-  if (!buf_.get())
-    buf_ = new IOBuffer(kBufSize);
-
-  int rv = 0;
-  do {
-    rv = transaction_->Read(buf_.get(), kBufSize, read_callback_);
-  } while (rv > 0);
-
-  if (rv == ERR_IO_PENDING)
-    return;
-
-  OnRead(rv);
-}
-
-void HttpCache::AsyncValidation::OnRead(int result) {
-  if (result > 0) {
-    DoRead();
-    return;
-  }
-  Terminate(result);
-}
-
-void HttpCache::AsyncValidation::Terminate(int result) {
-  if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED || IsCertificateError(result)) {
-    // We should not attempt to access this resource asynchronously again until
-    // the certificate problem has been resolved.
-    // TODO(ricea): For ERR_SSL_CLIENT_AUTH_CERT_NEEDED, mark the entry as
-    // requiring synchronous revalidation rather than just deleting it. Other
-    // certificate errors cause the resource to be considered uncacheable
-    // anyway.
-    cache_->DoomEntry(transaction_->key(), transaction_.get());
-  }
-  base::TimeDelta duration = cache_->clock()->Now() - start_time_;
-  UMA_HISTOGRAM_TIMES("HttpCache.AsyncValidationDuration", duration);
-  transaction_->net_log().EndEventWithNetErrorCode(
-      NetLog::TYPE_ASYNC_REVALIDATION, result);
-  cache_->DeleteAsyncValidation(cache_->GenerateCacheKey(&request_));
-  // |this| is deleted.
-}
-
-//-----------------------------------------------------------------------------
 HttpCache::HttpCache(const HttpNetworkSession::Params& params,
                      BackendFactory* backend_factory)
     : net_log_(params.net_log),
@@ -455,7 +301,6 @@
       building_backend_(false),
       bypass_lock_for_test_(false),
       fail_conditionalization_for_test_(false),
-      use_stale_while_revalidate_(params.use_stale_while_revalidate),
       mode_(NORMAL),
       network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))),
       clock_(new base::DefaultClock()),
@@ -473,7 +318,6 @@
       building_backend_(false),
       bypass_lock_for_test_(false),
       fail_conditionalization_for_test_(false),
-      use_stale_while_revalidate_(session->params().use_stale_while_revalidate),
       mode_(NORMAL),
       network_layer_(new HttpNetworkLayer(session)),
       clock_(new base::DefaultClock()),
@@ -488,15 +332,11 @@
       building_backend_(false),
       bypass_lock_for_test_(false),
       fail_conditionalization_for_test_(false),
-      use_stale_while_revalidate_(false),
       mode_(NORMAL),
       network_layer_(network_layer),
       clock_(new base::DefaultClock()),
       weak_factory_(this) {
   SetupQuicServerInfoFactory(network_layer_->GetSession());
-  HttpNetworkSession* session = network_layer_->GetSession();
-  if (session)
-    use_stale_while_revalidate_ = session->params().use_stale_while_revalidate;
 }
 
 HttpCache::~HttpCache() {
@@ -518,7 +358,6 @@
   }
 
   STLDeleteElements(&doomed_entries_);
-  STLDeleteValues(&async_validations_);
 
   // Before deleting pending_ops_, we have to make sure that the disk cache is
   // done with said operations, or it will attempt to use deleted data.
@@ -1180,43 +1019,6 @@
       base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
 }
 
-void HttpCache::PerformAsyncValidation(const HttpRequestInfo& original_request,
-                                       const BoundNetLog& net_log) {
-  DCHECK(use_stale_while_revalidate_);
-  std::string key = GenerateCacheKey(&original_request);
-  AsyncValidation* async_validation =
-      new AsyncValidation(original_request, this);
-  typedef AsyncValidationMap::value_type AsyncValidationKeyValue;
-  bool insert_ok =
-      async_validations_.insert(AsyncValidationKeyValue(key, async_validation))
-          .second;
-  if (!insert_ok) {
-    DVLOG(1) << "Harmless race condition detected on URL "
-             << original_request.url << "; discarding redundant revalidation.";
-    delete async_validation;
-    return;
-  }
-  HttpNetworkSession* network_session = GetSession();
-  NetworkDelegate* network_delegate = NULL;
-  if (network_session)
-    network_delegate = network_session->network_delegate();
-  scoped_ptr<HttpTransaction> transaction;
-  CreateTransaction(IDLE, &transaction);
-  scoped_ptr<Transaction> downcast_transaction(
-      static_cast<Transaction*>(transaction.release()));
-  async_validation->Start(
-      net_log, downcast_transaction.Pass(), network_delegate);
-  // |async_validation| may have been deleted here.
-}
-
-void HttpCache::DeleteAsyncValidation(const std::string& url) {
-  AsyncValidationMap::iterator it = async_validations_.find(url);
-  CHECK(it != async_validations_.end());  // security-critical invariant
-  AsyncValidation* async_validation = it->second;
-  async_validations_.erase(it);
-  delete async_validation;
-}
-
 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
   entry->will_process_pending_queue = false;
   DCHECK(!entry->writer);
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index c730049..8300c29 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -15,7 +15,6 @@
 #define NET_HTTP_HTTP_CACHE_H_
 
 #include <list>
-#include <map>
 #include <set>
 #include <string>
 
@@ -211,16 +210,6 @@
     fail_conditionalization_for_test_ = true;
   }
 
-  bool use_stale_while_revalidate() const {
-    return use_stale_while_revalidate_;
-  }
-
-  // Enable stale_while_revalidate functionality for testing purposes.
-  void set_use_stale_while_revalidate_for_testing(
-      bool use_stale_while_revalidate) {
-    use_stale_while_revalidate_ = use_stale_while_revalidate;
-  }
-
   // HttpTransactionFactory implementation:
   int CreateTransaction(RequestPriority priority,
                         scoped_ptr<HttpTransaction>* trans) override;
@@ -257,11 +246,9 @@
   friend class Transaction;
   friend class ViewCacheHelper;
   struct PendingOp;  // Info for an entry under construction.
-  class AsyncValidation;  // Encapsulates a single async revalidation.
 
   typedef std::list<Transaction*> TransactionList;
   typedef std::list<WorkItem*> WorkItemList;
-  typedef std::map<std::string, AsyncValidation*> AsyncValidationMap;
 
   struct ActiveEntry {
     explicit ActiveEntry(disk_cache::Entry* entry);
@@ -396,15 +383,6 @@
   // Resumes processing the pending list of |entry|.
   void ProcessPendingQueue(ActiveEntry* entry);
 
-  // Called by Transaction to perform an asynchronous revalidation. Creates a
-  // new independent transaction as a copy of the original.
-  void PerformAsyncValidation(const HttpRequestInfo& original_request,
-                              const BoundNetLog& net_log);
-
-  // Remove the AsyncValidation with url |url| from the |async_validations_| set
-  // and delete it.
-  void DeleteAsyncValidation(const std::string& url);
-
   // Events (called via PostTask) ---------------------------------------------
 
   void OnProcessPendingQueue(ActiveEntry* entry);
@@ -437,10 +415,6 @@
   bool bypass_lock_for_test_;
   bool fail_conditionalization_for_test_;
 
-  // true if the implementation of Cache-Control: stale-while-revalidate
-  // directive is enabled (either via command-line flag or experiment).
-  bool use_stale_while_revalidate_;
-
   Mode mode_;
 
   scoped_ptr<QuicServerInfoFactoryAdaptor> quic_server_info_factory_;
@@ -462,9 +436,6 @@
 
   scoped_ptr<PlaybackCacheMap> playback_cache_map_;
 
-  // The async validations currently in progress, keyed by URL.
-  AsyncValidationMap async_validations_;
-
   // A clock that can be swapped out for testing.
   scoped_ptr<base::Clock> clock_;
 
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index e47ddde..9f797af8 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -4,10 +4,7 @@
 
 #include "net/http/http_cache_transaction.h"
 
-#include "base/location.h"
-#include "base/single_thread_task_runner.h"
-#include "base/thread_task_runner_handle.h"
-#include "build/build_config.h"
+#include "build/build_config.h"  // For OS_POSIX
 
 #if defined(OS_POSIX)
 #include <unistd.h>
@@ -19,36 +16,30 @@
 #include "base/bind.h"
 #include "base/compiler_specific.h"
 #include "base/format_macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/metrics/field_trial.h"
+#include "base/location.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/profiler/scoped_tracker.h"
-#include "base/rand_util.h"
-#include "base/strings/string_number_conversions.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"  // For HexEncode.
 #include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
+#include "base/strings/string_util.h"  // For LowerCaseEqualsASCII.
 #include "base/strings/stringprintf.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/time/clock.h"
-#include "base/time/time.h"
 #include "base/values.h"
-#include "net/base/completion_callback.h"
-#include "net/base/io_buffer.h"
+#include "net/base/auth.h"
 #include "net/base/load_flags.h"
 #include "net/base/load_timing_info.h"
 #include "net/base/net_errors.h"
 #include "net/base/upload_data_stream.h"
 #include "net/cert/cert_status_flags.h"
+#include "net/cert/x509_certificate.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/http/disk_based_cert_cache.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_request_info.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_transaction.h"
 #include "net/http/http_util.h"
-#include "net/http/partial_data.h"
-#include "net/log/net_log.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "net/ssl/ssl_config_service.h"
 
@@ -185,18 +176,6 @@
   }
 }
 
-scoped_ptr<base::Value> NetLogAsyncRevalidationInfoCallback(
-    const NetLog::Source& source,
-    const HttpRequestInfo* request,
-    NetLogCaptureMode capture_mode) {
-  scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  source.AddToEventParameters(dict.get());
-
-  dict->SetString("url", request->url.possibly_invalid_spec());
-  dict->SetString("method", request->method);
-  return dict.Pass();
-}
-
 enum ExternallyConditionalizedType {
   EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION,
   EXTERNALLY_CONDITIONALIZED_CACHE_USABLE,
@@ -2139,13 +2118,6 @@
 
   bool skip_validation = (required_validation == VALIDATION_NONE);
 
-  if (required_validation == VALIDATION_ASYNCHRONOUS &&
-      !(request_->method == "GET" && (truncated_ || partial_)) && cache_ &&
-      cache_->use_stale_while_revalidate()) {
-    TriggerAsyncValidation();
-    skip_validation = true;
-  }
-
   if (request_->method == "HEAD" &&
       (truncated_ || response_.headers->response_code() == 206)) {
     DCHECK(!partial_);
@@ -2174,7 +2146,6 @@
   }
 
   if (skip_validation) {
-    // TODO(ricea): Is this pattern okay for asynchronous revalidations?
     UpdateTransactionPattern(PATTERN_ENTRY_USED);
     return SetupEntryForRead();
   } else {
@@ -2354,7 +2325,7 @@
     return VALIDATION_NONE;
   }
 
-  if (effective_load_flags_ & (LOAD_VALIDATE_CACHE | LOAD_ASYNC_REVALIDATION))
+  if (effective_load_flags_ & LOAD_VALIDATE_CACHE)
     return VALIDATION_SYNCHRONOUS;
 
   if (request_->method == "PUT" || request_->method == "DELETE")
@@ -2608,23 +2579,6 @@
   }
 }
 
-void HttpCache::Transaction::TriggerAsyncValidation() {
-  DCHECK(!request_->upload_data_stream);
-  BoundNetLog async_revalidation_net_log(
-      BoundNetLog::Make(net_log_.net_log(), NetLog::SOURCE_ASYNC_REVALIDATION));
-  net_log_.AddEvent(
-      NetLog::TYPE_HTTP_CACHE_VALIDATE_RESOURCE_ASYNC,
-      async_revalidation_net_log.source().ToEventParametersCallback());
-  async_revalidation_net_log.BeginEvent(
-      NetLog::TYPE_ASYNC_REVALIDATION,
-      base::Bind(
-          &NetLogAsyncRevalidationInfoCallback, net_log_.source(), request_));
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&HttpCache::PerformAsyncValidation,
-                            cache_,  // cache_ is a weak pointer.
-                            *request_, async_revalidation_net_log));
-}
-
 void HttpCache::Transaction::FailRangeRequest() {
   response_ = *new_response_;
   partial_->FixResponseHeaders(response_.headers.get(), false);
diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h
index cbf0929..473a610 100644
--- a/net/http/http_cache_transaction.h
+++ b/net/http/http_cache_transaction.h
@@ -8,18 +8,29 @@
 #ifndef NET_HTTP_HTTP_CACHE_TRANSACTION_H_
 #define NET_HTTP_HTTP_CACHE_TRANSACTION_H_
 
+#include <stddef.h>
+
 #include <string>
 
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/load_states.h"
 #include "net/base/request_priority.h"
+#include "net/base/upload_progress.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
 #include "net/http/http_transaction.h"
+#include "net/http/partial_data.h"
 #include "net/log/net_log.h"
 #include "net/socket/connection_attempts.h"
+#include "net/websockets/websocket_handshake_stream_base.h"
 
 namespace net {
 
@@ -340,9 +351,6 @@
   // Fixes the response headers to match expectations for a HEAD request.
   void FixHeadersForHead();
 
-  // Launches an asynchronous revalidation based on this transaction.
-  void TriggerAsyncValidation();
-
   // Changes the response code of a range request to be 416 (Requested range not
   // satisfiable).
   void FailRangeRequest();
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index ea1b9ddf..f490444 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -7283,52 +7283,6 @@
   EXPECT_FALSE(TransactionRequiredNetwork(LOAD_NORMAL));
 }
 
-// Framework for tests of stale-while-revalidate related functionality.  With
-// the default settings (age=3601,stale-while-revalidate=7200,max-age=3600) it
-// will trigger the stale-while-revalidate asynchronous revalidation. Setting
-// |age_| to < 3600 will prevent any revalidation, and |age_| > 10800 will cause
-// synchronous revalidation.
-class HttpCacheStaleWhileRevalidateTest : public ::testing::Test {
- protected:
-  HttpCacheStaleWhileRevalidateTest()
-      : transaction_(kSimpleGET_Transaction),
-        age_(3601),
-        stale_while_revalidate_(7200),
-        validator_("Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT") {
-    cache_.http_cache()->set_use_stale_while_revalidate_for_testing(true);
-  }
-
-  // RunTransactionTest() with the arguments from this fixture.
-  void RunFixtureTransactionTest() {
-    std::string response_headers = base::StringPrintf(
-        "%s\n"
-        "Age: %d\n"
-        "Cache-Control: max-age=3600,stale-while-revalidate=%d\n",
-        validator_.c_str(),
-        age_,
-        stale_while_revalidate_);
-    transaction_.response_headers = response_headers.c_str();
-    RunTransactionTest(cache_.http_cache(), transaction_);
-    transaction_.response_headers = "";
-  }
-
-  // How many times this test has sent requests to the (fake) origin
-  // server. Every test case needs to make at least one request to initialise
-  // the cache.
-  int transaction_count() {
-    return cache_.network_layer()->transaction_count();
-  }
-
-  // How many times an existing cache entry was opened during the test case.
-  int open_count() { return cache_.disk_cache()->open_count(); }
-
-  MockHttpCache cache_;
-  ScopedMockTransaction transaction_;
-  int age_;
-  int stale_while_revalidate_;
-  std::string validator_;
-};
-
 static void CheckResourceFreshnessHeader(const HttpRequestInfo* request,
                                          std::string* response_status,
                                          std::string* response_headers,
@@ -7340,20 +7294,27 @@
 
 // Verify that the Resource-Freshness header is sent on a revalidation if the
 // stale-while-revalidate directive was on the response.
-TEST_F(HttpCacheStaleWhileRevalidateTest, ResourceFreshnessHeaderSent) {
-  age_ = 10801;  // Outside the stale-while-revalidate window.
+TEST(HttpCache, ResourceFreshnessHeaderSent) {
+  MockHttpCache cache;
+
+  ScopedMockTransaction stale_while_revalidate_transaction(
+      kSimpleGET_Transaction);
+  stale_while_revalidate_transaction.response_headers =
+      "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
+      "Age: 10801\n"
+      "Cache-Control: max-age=3600,stale-while-revalidate=7200\n";
 
   // Write to the cache.
-  RunFixtureTransactionTest();
+  RunTransactionTest(cache.http_cache(), stale_while_revalidate_transaction);
 
-  EXPECT_EQ(1, transaction_count());
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
 
   // Send the request again and check that Resource-Freshness header is added.
-  transaction_.handler = CheckResourceFreshnessHeader;
+  stale_while_revalidate_transaction.handler = CheckResourceFreshnessHeader;
 
-  RunFixtureTransactionTest();
+  RunTransactionTest(cache.http_cache(), stale_while_revalidate_transaction);
 
-  EXPECT_EQ(2, transaction_count());
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 }
 
 static void CheckResourceFreshnessAbsent(const HttpRequestInfo* request,
@@ -7365,421 +7326,27 @@
 
 // Verify that the Resource-Freshness header is not sent when
 // stale-while-revalidate is 0.
-TEST_F(HttpCacheStaleWhileRevalidateTest, ResourceFreshnessHeaderNotSent) {
-  age_ = 10801;
-  stale_while_revalidate_ = 0;
+TEST(HttpCache, ResourceFreshnessHeaderNotSent) {
+  MockHttpCache cache;
+
+  ScopedMockTransaction stale_while_revalidate_transaction(
+      kSimpleGET_Transaction);
+  stale_while_revalidate_transaction.response_headers =
+      "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
+      "Age: 10801\n"
+      "Cache-Control: max-age=3600,stale-while-revalidate=0\n";
 
   // Write to the cache.
-  RunFixtureTransactionTest();
+  RunTransactionTest(cache.http_cache(), stale_while_revalidate_transaction);
 
-  EXPECT_EQ(1, transaction_count());
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
 
   // Send the request again and check that Resource-Freshness header is absent.
-  transaction_.handler = CheckResourceFreshnessAbsent;
+  stale_while_revalidate_transaction.handler = CheckResourceFreshnessAbsent;
 
-  RunFixtureTransactionTest();
+  RunTransactionTest(cache.http_cache(), stale_while_revalidate_transaction);
 
-  EXPECT_EQ(2, transaction_count());
-}
-
-// Verify that when stale-while-revalidate applies the response is read from
-// cache.
-TEST_F(HttpCacheStaleWhileRevalidateTest, ReadFromCache) {
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(0, open_count());
-  EXPECT_EQ(1, transaction_count());
-
-  // Read back from the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, open_count());
-  EXPECT_EQ(1, transaction_count());
-}
-
-// Verify that when stale-while-revalidate applies an asynchronous request is
-// sent.
-TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncRequestSent) {
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // Read back from the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // Let the async request execute.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2, transaction_count());
-}
-
-// Verify that tearing down the HttpCache with an async revalidation in progress
-// does not break anything (this test is most likely to find problems when run
-// with a memory checker such as AddressSanitizer).
-TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncTearDown) {
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  // Read back from the cache.
-  RunFixtureTransactionTest();
-}
-
-static void CheckIfModifiedSinceHeader(const HttpRequestInfo* request,
-                                       std::string* response_status,
-                                       std::string* response_headers,
-                                       std::string* response_data) {
-  std::string value;
-  EXPECT_TRUE(request->extra_headers.GetHeader("If-Modified-Since", &value));
-  EXPECT_EQ("Sat, 18 Apr 2007 01:10:43 GMT", value);
-}
-
-// Verify that the async revalidation contains an If-Modified-Since header.
-TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncRequestIfModifiedSince) {
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  transaction_.handler = CheckIfModifiedSinceHeader;
-
-  // Read back from the cache.
-  RunFixtureTransactionTest();
-}
-
-static void CheckIfNoneMatchHeader(const HttpRequestInfo* request,
-                                   std::string* response_status,
-                                   std::string* response_headers,
-                                   std::string* response_data) {
-  std::string value;
-  EXPECT_TRUE(request->extra_headers.GetHeader("If-None-Match", &value));
-  EXPECT_EQ("\"40a1-1320-4f6adefa22a40\"", value);
-}
-
-// If the response had ETag rather than Last-Modified, then that is used to
-// conditionalise the response.
-TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncRequestIfNoneMatch) {
-  validator_ = "Etag: \"40a1-1320-4f6adefa22a40\"";
-
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  transaction_.handler = CheckIfNoneMatchHeader;
-
-  // Read back from the cache.
-  RunFixtureTransactionTest();
-}
-
-static void CheckResourceFreshnessHeaderPresent(const HttpRequestInfo* request,
-                                                std::string* response_status,
-                                                std::string* response_headers,
-                                                std::string* response_data) {
-  EXPECT_TRUE(request->extra_headers.HasHeader("Resource-Freshness"));
-}
-
-TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncRequestHasResourceFreshness) {
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  transaction_.handler = CheckResourceFreshnessHeaderPresent;
-
-  // Read back from the cache.
-  RunFixtureTransactionTest();
-}
-
-// Verify that when age > max-age + stale-while-revalidate stale results are
-// not returned.
-TEST_F(HttpCacheStaleWhileRevalidateTest, NotAppliedIfTooStale) {
-  age_ = 10801;
-
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(0, open_count());
-  EXPECT_EQ(1, transaction_count());
-
-  // Reading back reads from the network.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, open_count());
-  EXPECT_EQ(2, transaction_count());
-}
-
-// HEAD requests should be able to take advantage of stale-while-revalidate.
-TEST_F(HttpCacheStaleWhileRevalidateTest, WorksForHeadMethod) {
-  // Write to the cache. This has to be a GET request; HEAD requests don't
-  // create new cache entries.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(0, open_count());
-  EXPECT_EQ(1, transaction_count());
-
-  // Read back from the cache, and trigger an asynchronous HEAD request.
-  transaction_.method = "HEAD";
-  transaction_.data = "";
-
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, open_count());
-  EXPECT_EQ(1, transaction_count());
-
-  // Let the network request proceed.
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(2, transaction_count());
-}
-
-// POST requests should not use stale-while-revalidate.
-TEST_F(HttpCacheStaleWhileRevalidateTest, NotAppliedToPost) {
-  transaction_ = ScopedMockTransaction(kSimplePOST_Transaction);
-
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(0, open_count());
-  EXPECT_EQ(1, transaction_count());
-
-  // Reading back reads from the network.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(0, open_count());
-  EXPECT_EQ(2, transaction_count());
-}
-
-static void CheckUrlMatches(const HttpRequestInfo* request,
-                            std::string* response_status,
-                            std::string* response_headers,
-                            std::string* response_data) {
-  EXPECT_EQ("http://www.google.com/", request->url.spec());
-}
-
-// Async revalidation is issued to the original URL.
-TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncRequestUrlMatches) {
-  transaction_.url = "http://www.google.com/";
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  // Read back from the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  transaction_.handler = CheckUrlMatches;
-
-  // Let the async request execute and perform the check.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2, transaction_count());
-}
-
-class SyncLoadFlagTest : public HttpCacheStaleWhileRevalidateTest,
-                         public ::testing::WithParamInterface<int> {};
-
-// Flags which should always cause the request to be synchronous.
-TEST_P(SyncLoadFlagTest, MustBeSynchronous) {
-  transaction_.load_flags |= GetParam();
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // Reading back reads from the network.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(2, transaction_count());
-}
-
-INSTANTIATE_TEST_CASE_P(HttpCacheStaleWhileRevalidate,
-                        SyncLoadFlagTest,
-                        ::testing::Values(LOAD_VALIDATE_CACHE,
-                                          LOAD_BYPASS_CACHE,
-                                          LOAD_DISABLE_CACHE));
-
-TEST_F(HttpCacheStaleWhileRevalidateTest,
-       PreferringCacheDoesNotTriggerAsyncRequest) {
-  transaction_.load_flags |= LOAD_PREFERRING_CACHE;
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // Reading back reads from the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // If there was an async transaction created, it would run now.
-  base::RunLoop().RunUntilIdle();
-
-  // There was no async transaction.
-  EXPECT_EQ(1, transaction_count());
-}
-
-TEST_F(HttpCacheStaleWhileRevalidateTest, NotUsedWhenDisabled) {
-  cache_.http_cache()->set_use_stale_while_revalidate_for_testing(false);
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // A synchronous revalidation is performed.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(2, transaction_count());
-}
-
-TEST_F(HttpCacheStaleWhileRevalidateTest,
-       OnlyFromCacheDoesNotTriggerAsyncRequest) {
-  transaction_.load_flags |= LOAD_ONLY_FROM_CACHE;
-  transaction_.return_code = ERR_CACHE_MISS;
-
-  // Writing to the cache should fail, because we are avoiding the network.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(0, transaction_count());
-
-  base::RunLoop().RunUntilIdle();
-
-  // Still nothing.
-  EXPECT_EQ(0, transaction_count());
-}
-
-// A certificate error during an asynchronous fetch should cause the next fetch
-// to proceed synchronously.
-// TODO(ricea): In future, only certificate errors which require user
-// interaction should fail the asynchronous revalidation, and they should cause
-// the next revalidation to be synchronous rather than requiring a total
-// refetch. This test will need to be updated appropriately.
-TEST_F(HttpCacheStaleWhileRevalidateTest, CertificateErrorCausesRefetch) {
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // Now read back. RunTransactionTestBase() expects to receive the network
-  // error back from the HttpCache::Transaction, but since the cache request
-  // will return OK we need to duplicate some of its implementation here.
-  transaction_.return_code = ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
-  TestCompletionCallback callback;
-  scoped_ptr<HttpTransaction> trans;
-  int rv = cache_.http_cache()->CreateTransaction(DEFAULT_PRIORITY, &trans);
-  EXPECT_EQ(OK, rv);
-  ASSERT_TRUE(trans.get());
-
-  MockHttpRequest request(transaction_);
-  rv = trans->Start(&request, callback.callback(), BoundNetLog());
-  ASSERT_EQ(ERR_IO_PENDING, rv);
-  ASSERT_EQ(OK, callback.WaitForResult());
-  ReadAndVerifyTransaction(trans.get(), transaction_);
-
-  EXPECT_EQ(1, transaction_count());
-
-  // Allow the asynchronous fetch to run.
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(2, transaction_count());
-
-  // Now run the transaction again. It should run synchronously.
-  transaction_.return_code = OK;
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(3, transaction_count());
-}
-
-// Ensure that the response cached by the asynchronous request is not truncated,
-// even if the server is slow.
-TEST_F(HttpCacheStaleWhileRevalidateTest, EntireResponseCached) {
-  transaction_.test_mode = TEST_MODE_SLOW_READ;
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  // Read back from the cache.
-  RunFixtureTransactionTest();
-
-  // Let the async request execute.
-  base::RunLoop().RunUntilIdle();
-
-  // The cache entry should still be complete.
-  transaction_.load_flags = LOAD_ONLY_FROM_CACHE;
-  RunFixtureTransactionTest();
-}
-
-// Verify that there are no race conditions in the completely synchronous case.
-TEST_F(HttpCacheStaleWhileRevalidateTest, SynchronousCaseWorks) {
-  transaction_.test_mode = TEST_MODE_SYNC_ALL;
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // Read back from the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // Let the async request execute.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2, transaction_count());
-}
-
-static void CheckLoadFlagsAsyncRevalidation(const HttpRequestInfo* request,
-                                            std::string* response_status,
-                                            std::string* response_headers,
-                                            std::string* response_data) {
-  EXPECT_EQ(LOAD_ASYNC_REVALIDATION, request->load_flags);
-}
-
-// Check that the load flags on the async request are the same as the load flags
-// on the original request, plus LOAD_ASYNC_REVALIDATION.
-TEST_F(HttpCacheStaleWhileRevalidateTest, LoadFlagsAsyncRevalidation) {
-  transaction_.load_flags = LOAD_NORMAL;
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // Read back from the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  transaction_.handler = CheckLoadFlagsAsyncRevalidation;
-  // Let the async request execute.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2, transaction_count());
-}
-
-static void SimpleMockAuthHandler(const HttpRequestInfo* request,
-                                  std::string* response_status,
-                                  std::string* response_headers,
-                                  std::string* response_data) {
-  if (request->extra_headers.HasHeader("X-Require-Mock-Auth") &&
-      !request->extra_headers.HasHeader("Authorization")) {
-    response_status->assign("HTTP/1.1 401 Unauthorized");
-    response_headers->assign("WWW-Authenticate: Basic realm=\"mars\"\n");
-    return;
-  }
-  response_status->assign("HTTP/1.1 200 OK");
-}
-
-TEST_F(HttpCacheStaleWhileRevalidateTest, RestartForAuth) {
-  // Write to the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // Now make the transaction require auth.
-  transaction_.request_headers = "X-Require-Mock-Auth: dummy\r\n\r\n";
-  transaction_.handler = SimpleMockAuthHandler;
-
-  // Read back from the cache.
-  RunFixtureTransactionTest();
-
-  EXPECT_EQ(1, transaction_count());
-
-  // Let the async request execute.
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(2, transaction_count());
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
 }
 
 // Tests that we allow multiple simultaneous, non-overlapping transactions to
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 4b7edeb..c413b87 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -75,7 +75,6 @@
       net_log(NULL),
       host_mapping_rules(NULL),
       ignore_certificate_errors(false),
-      use_stale_while_revalidate(false),
       testing_fixed_http_port(0),
       testing_fixed_https_port(0),
       enable_tcp_fast_open_for_ssl(false),
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 3327195..abd5e3bb 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -79,7 +79,6 @@
     NetLog* net_log;
     HostMappingRules* host_mapping_rules;
     bool ignore_certificate_errors;
-    bool use_stale_while_revalidate;
     uint16 testing_fixed_http_port;
     uint16 testing_fixed_https_port;
     bool enable_tcp_fast_open_for_ssl;
diff --git a/net/http/http_server_properties_impl.cc b/net/http/http_server_properties_impl.cc
index 159adebf..3f6caaa 100644
--- a/net/http/http_server_properties_impl.cc
+++ b/net/http/http_server_properties_impl.cc
@@ -291,21 +291,12 @@
       alternative_service, alternative_probability);
   AlternativeServiceMap::const_iterator it =
       GetAlternateProtocolIterator(origin);
-  if (it != alternative_service_map_.end()) {
-    const AlternativeServiceInfo existing_alternative_service_info = it->second;
-    if (existing_alternative_service_info != alternative_service_info) {
-      LOG(WARNING) << "Changing the alternative service for: "
-                   << origin.ToString() << " from "
-                   << existing_alternative_service_info.ToString() << " to "
-                   << alternative_service_info.ToString() << ".";
-    }
-  } else {
-    if (alternative_probability >= alternative_service_probability_threshold_) {
-      // TODO(rch): Consider the case where multiple requests are started
-      // before the first completes. In this case, only one of the jobs
-      // would reach this code, whereas all of them should should have.
-      HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING);
-    }
+  if (it == alternative_service_map_.end() &&
+      alternative_probability >= alternative_service_probability_threshold_) {
+    // TODO(rch): Consider the case where multiple requests are started
+    // before the first completes. In this case, only one of the jobs
+    // would reach this code, whereas all of them should should have.
+    HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING);
   }
 
   alternative_service_map_.Put(origin, alternative_service_info);
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index d46cbff..23088d4 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -874,29 +874,6 @@
 EVENT_TYPE(HTTP_CACHE_RESTART_PARTIAL_REQUEST)
 EVENT_TYPE(HTTP_CACHE_RE_SEND_PARTIAL_REQUEST)
 
-// Identifies the NetLog::Source() for the asynchronous HttpCache::Transaction
-// that will revalidate this entry.
-// The event parameters are:
-//   {
-//      "source_dependency": <Source identifier for the async Transaction>
-//   }
-EVENT_TYPE(HTTP_CACHE_VALIDATE_RESOURCE_ASYNC)
-
-// The start/end of performing an async revalidation.
-// For the BEGIN phase, the event parameters are:
-//   {
-//      "source_dependency": <Source identifier for the Request>
-//      "url": <String of URL being loaded>,
-//      "method": <Method of request>
-//   }
-//
-// For the END phase, if there was an error, the following parameters are
-// attached:
-//   {
-//      "net_error": <Net error code of the failure>,
-//   }
-EVENT_TYPE(ASYNC_REVALIDATION)
-
 // ------------------------------------------------------------------------
 // Disk Cache / Memory Cache
 // ------------------------------------------------------------------------
diff --git a/net/log/net_log_source_type_list.h b/net/log/net_log_source_type_list.h
index f72c8de3..d538615 100644
--- a/net/log/net_log_source_type_list.h
+++ b/net/log/net_log_source_type_list.h
@@ -25,5 +25,4 @@
 SOURCE_TYPE(FILESTREAM)
 SOURCE_TYPE(DNS_PROBER)
 SOURCE_TYPE(PROXY_CLIENT_SOCKET)
-SOURCE_TYPE(ASYNC_REVALIDATION)
 SOURCE_TYPE(DATA_REDUCTION_PROXY)
diff --git a/net/net.gypi b/net/net.gypi
index d469ebd8..5105dea 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -316,6 +316,8 @@
       'base/upload_data_stream.h',
       'base/upload_element_reader.cc',
       'base/upload_element_reader.h',
+      'base/upload_disk_cache_entry_element_reader.cc',
+      'base/upload_disk_cache_entry_element_reader.h',
       'base/upload_file_element_reader.cc',
       'base/upload_file_element_reader.h',
       'base/upload_progress.h',
@@ -1322,6 +1324,7 @@
       'base/static_cookie_policy_unittest.cc',
       'base/test_completion_callback_unittest.cc',
       'base/upload_bytes_element_reader_unittest.cc',
+      'base/upload_disk_cache_entry_element_reader_unittest.cc',
       'base/upload_file_element_reader_unittest.cc',
       'base/url_util_unittest.cc',
       'cert/cert_policy_enforcer_unittest.cc',
@@ -1531,6 +1534,7 @@
       'quic/iovector_test.cc',
       'quic/network_connection_unittest.cc',
       'quic/port_suggester_unittest.cc',
+      'quic/quic_ack_notifier_manager_test.cc',
       'quic/quic_ack_notifier_test.cc',
       'quic/quic_address_mismatch_test.cc',
       'quic/quic_alarm_test.cc',
@@ -1589,6 +1593,8 @@
       'quic/test_tools/mock_quic_dispatcher.h',
       'quic/test_tools/mock_random.cc',
       'quic/test_tools/mock_random.h',
+      'quic/test_tools/quic_ack_notifier_manager_peer.cc',
+      'quic/test_tools/quic_ack_notifier_manager_peer.h',
       'quic/test_tools/quic_client_session_peer.cc',
       'quic/test_tools/quic_client_session_peer.h',
       'quic/test_tools/quic_config_peer.cc',
diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h
index 3e9468a0..3b1a391 100644
--- a/net/quic/crypto/crypto_protocol.h
+++ b/net/quic/crypto/crypto_protocol.h
@@ -78,6 +78,8 @@
 
 // FEC options
 const QuicTag kFHDR = TAG('F', 'H', 'D', 'R');   // FEC protect headers
+// Set FecSendPolicy for sending FEC packet only when FEC alarm goes off.
+const QuicTag kFSPA = TAG('F', 'S', 'P', 'A');
 
 // Enable bandwidth resumption experiment.
 const QuicTag kBWRE = TAG('B', 'W', 'R', 'E');  // Bandwidth resumption.
diff --git a/net/quic/quic_ack_notifier_manager.h b/net/quic/quic_ack_notifier_manager.h
index d4d97ad..6bf91e95 100644
--- a/net/quic/quic_ack_notifier_manager.h
+++ b/net/quic/quic_ack_notifier_manager.h
@@ -15,6 +15,10 @@
 
 class QuicAckNotifier;
 
+namespace test {
+class AckNotifierManagerPeer;
+}
+
 // The AckNotifierManager is used by the QuicSentPacketManager to keep track of
 // all the AckNotifiers currently active. It owns the AckNotifiers which it gets
 // from the serialized packets passed into OnSerializedPacket. It maintains both
@@ -47,6 +51,8 @@
   void OnSerializedPacket(const SerializedPacket& serialized_packet);
 
  private:
+  friend class test::AckNotifierManagerPeer;
+
   typedef std::list<QuicAckNotifier*> AckNotifierList;
   // TODO(ianswett): Further improvement may come from changing this to a deque.
   typedef base::hash_map<QuicPacketSequenceNumber, AckNotifierList>
diff --git a/net/quic/quic_ack_notifier_manager_test.cc b/net/quic/quic_ack_notifier_manager_test.cc
new file mode 100644
index 0000000..9e664104
--- /dev/null
+++ b/net/quic/quic_ack_notifier_manager_test.cc
@@ -0,0 +1,103 @@
+// 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 "net/quic/quic_ack_notifier_manager.h"
+
+#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/quic_ack_notifier_manager_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+// Test fixture for testing AckNotifierManager.  Instantiates a manager and
+// provides shared code for adding notifiers and verifying the contents of the
+// manager.
+class QuicAckNotifierManagerTest : public ::testing::Test {
+ protected:
+  AckNotifierManager manager_;
+  scoped_refptr<MockAckNotifierDelegate> delegate_;
+  QuicTime::Delta zero_;
+
+  QuicAckNotifierManagerTest() : zero_(QuicTime::Delta::Zero()) {
+    delegate_ = new MockAckNotifierDelegate;
+  }
+
+  ~QuicAckNotifierManagerTest() override {}
+
+  size_t CountPackets() const {
+    return AckNotifierManagerPeer::GetNumberOfRegisteredPackets(&manager_);
+  }
+
+  // Add a mock packet with specified parameters.  The packet with given
+  // sequence number must not exist in the map before.
+  void AddPacket(QuicPacketSequenceNumber sequence_number,
+                 bool retransmittable) {
+    // Create a mock packet.
+    RetransmittableFrames frames(ENCRYPTION_NONE);
+    SerializedPacket packet(sequence_number, PACKET_4BYTE_SEQUENCE_NUMBER,
+                            /*packet=*/nullptr,
+                            /*entropy_hash=*/0,
+                            retransmittable ? &frames : nullptr);
+
+    // Create and register a notifier.  Normally, this would be created by
+    // QuicPacketGenerator.
+    QuicAckNotifier* notifier = new QuicAckNotifier(delegate_.get());
+    packet.notifiers.push_back(notifier);
+
+    // Ensure that exactly one packet is added.
+    const size_t old_packet_count = CountPackets();
+
+    // Actually add the packet.
+    manager_.OnSerializedPacket(packet);
+
+    // Ensure the change in the number of packets.
+    EXPECT_EQ(old_packet_count + 1, CountPackets());
+  }
+};
+
+// This test verifies that QuicAckNotifierManager can handle the trivial case of
+// received packet notification.
+TEST_F(QuicAckNotifierManagerTest, SimpleAck) {
+  AddPacket(1, true);
+  AddPacket(2, true);
+
+  EXPECT_CALL(*delegate_, OnAckNotification(0, 0, zero_)).Times(2);
+  manager_.OnPacketAcked(1, zero_);
+  EXPECT_EQ(1u, CountPackets());
+  manager_.OnPacketAcked(2, zero_);
+  EXPECT_EQ(0u, CountPackets());
+}
+
+// This test verifies that the QuicAckNotifierManager behaves correctly when
+// there are many retransmissions.
+TEST_F(QuicAckNotifierManagerTest, RepeatedRetransmission) {
+  AddPacket(1, true);
+
+  const size_t packet_size = kDefaultMaxPacketSize;
+  const size_t times_lost = 100;
+  const size_t total_size_lost = packet_size * times_lost;
+  const QuicPacketSequenceNumber last_packet = times_lost + 1;
+
+  // Retransmit the packet many times.
+  for (size_t sequence_number = 1; sequence_number < last_packet;
+       sequence_number++) {
+    manager_.OnPacketRetransmitted(sequence_number, sequence_number + 1,
+                                   packet_size);
+    EXPECT_EQ(1u, CountPackets());
+  }
+
+  // Finally get the packet acknowledged.
+  EXPECT_CALL(*delegate_, OnAckNotification(times_lost, total_size_lost, zero_))
+      .Times(1);
+  manager_.OnPacketAcked(last_packet, zero_);
+  EXPECT_EQ(0u, CountPackets());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index e18166e6..dde2b07 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -21,9 +21,9 @@
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "net/base/net_errors.h"
+#include "net/quic/crypto/crypto_protocol.h"
 #include "net/quic/crypto/quic_decrypter.h"
 #include "net/quic/crypto/quic_encrypter.h"
-#include "net/quic/iovector.h"
 #include "net/quic/proto/cached_network_parameters.pb.h"
 #include "net/quic/quic_bandwidth.h"
 #include "net/quic/quic_config.h"
@@ -64,7 +64,7 @@
 const QuicPacketCount kMaxPacketsReceivedBeforeAckSend = 20;
 
 // Maximum number of tracked packets.
-const QuicPacketCount kMaxTrackedPackets = 15 * kMaxTcpCongestionWindow;
+const QuicPacketCount kMaxTrackedPackets = 5000;
 
 bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) {
   QuicPacketSequenceNumber delta = (a > b) ? a - b : b - a;
@@ -314,6 +314,16 @@
         config.ReceivedBytesForConnectionId());
   }
   max_undecryptable_packets_ = config.max_undecryptable_packets();
+
+  if (FLAGS_quic_send_fec_packet_only_on_fec_alarm &&
+      ((perspective_ == Perspective::IS_SERVER &&
+        config.HasReceivedConnectionOptions() &&
+        ContainsQuicTag(config.ReceivedConnectionOptions(), kFSPA)) ||
+       (perspective_ == Perspective::IS_CLIENT &&
+        config.HasSendConnectionOptions() &&
+        ContainsQuicTag(config.SendConnectionOptions(), kFSPA)))) {
+    packet_generator_.set_fec_send_policy(FecSendPolicy::FEC_ALARM_TRIGGER);
+  }
 }
 
 void QuicConnection::OnSendConnectionState(
@@ -1127,12 +1137,12 @@
 
 QuicConsumedData QuicConnection::SendStreamData(
     QuicStreamId id,
-    const IOVector& data,
+    const QuicIOVector& iov,
     QuicStreamOffset offset,
     bool fin,
     FecProtection fec_protection,
     QuicAckNotifier::DelegateInterface* delegate) {
-  if (!fin && data.Empty()) {
+  if (!fin && iov.total_length == 0) {
     LOG(DFATAL) << "Attempt to send empty stream frame";
     return QuicConsumedData(0, false);
   }
@@ -1151,7 +1161,7 @@
   // also if there is possibility of revival. Only bundle an ack if there's no
   // processing left that may cause received_info_ to change.
   ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
-  return packet_generator_.ConsumeData(id, data, offset, fin, fec_protection,
+  return packet_generator_.ConsumeData(id, iov, offset, fin, fec_protection,
                                        delegate);
 }
 
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
index 134d126..2a37360 100644
--- a/net/quic/quic_connection.h
+++ b/net/quic/quic_connection.h
@@ -29,7 +29,6 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string_piece.h"
 #include "net/base/ip_endpoint.h"
-#include "net/quic/iovector.h"
 #include "net/quic/quic_ack_notifier.h"
 #include "net/quic/quic_ack_notifier_manager.h"
 #include "net/quic/quic_alarm.h"
@@ -289,7 +288,7 @@
   // received for all the packets written in this call.
   // The |delegate| is not owned by the QuicConnection and must outlive it.
   QuicConsumedData SendStreamData(QuicStreamId id,
-                                  const IOVector& data,
+                                  const QuicIOVector& iov,
                                   QuicStreamOffset offset,
                                   bool fin,
                                   FecProtection fec_protection,
diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc
index d587bca6..3d40944 100644
--- a/net/quic/quic_connection_test.cc
+++ b/net/quic/quic_connection_test.cc
@@ -4,6 +4,8 @@
 
 #include "net/quic/quic_connection.h"
 
+#include <ostream>
+
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/memory/scoped_ptr.h"
@@ -34,6 +36,7 @@
 
 using base::StringPiece;
 using std::map;
+using std::ostream;
 using std::string;
 using std::vector;
 using testing::AnyNumber;
@@ -458,10 +461,8 @@
       bool fin,
       FecProtection fec_protection,
       QuicAckNotifier::DelegateInterface* delegate) {
-    IOVector data_iov;
-    if (!data.empty()) {
-      data_iov.Append(const_cast<char*>(data.data()), data.size());
-    }
+    struct iovec iov;
+    QuicIOVector data_iov(MakeIOVector(data, &iov));
     return QuicConnection::SendStreamData(id, data_iov, offset, fin,
                                           fec_protection, delegate);
   }
@@ -587,7 +588,33 @@
   MOCK_CONST_METHOD1(Create, QuicPacketWriter*(QuicConnection* connection));
 };
 
-class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> {
+// Run tests with combinations of {QuicVersion, fec_send_policy}.
+struct TestParams {
+  TestParams(QuicVersion version, FecSendPolicy fec_send_policy)
+      : version(version), fec_send_policy(fec_send_policy) {}
+
+  friend ostream& operator<<(ostream& os, const TestParams& p) {
+    os << "{ client_version: " << QuicVersionToString(p.version)
+       << " fec_send_policy: " << p.fec_send_policy << " }";
+    return os;
+  }
+
+  QuicVersion version;
+  FecSendPolicy fec_send_policy;
+};
+
+// Constructs various test permutations.
+vector<TestParams> GetTestParams() {
+  vector<TestParams> params;
+  QuicVersionVector all_supported_versions = QuicSupportedVersions();
+  for (size_t i = 0; i < all_supported_versions.size(); ++i) {
+    params.push_back(TestParams(all_supported_versions[i], FEC_ANY_TRIGGER));
+    params.push_back(TestParams(all_supported_versions[i], FEC_ALARM_TRIGGER));
+  }
+  return params;
+}
+
+class QuicConnectionTest : public ::testing::TestWithParam<TestParams> {
  protected:
   QuicConnectionTest()
       : connection_id_(42),
@@ -617,6 +644,7 @@
     connection_.SetSendAlgorithm(send_algorithm_);
     connection_.SetLossAlgorithm(loss_algorithm_);
     framer_.set_received_entropy_calculator(&entropy_calculator_);
+    generator_->set_fec_send_policy(GetParam().fec_send_policy);
     EXPECT_CALL(
         *send_algorithm_, TimeUntilSend(_, _, _)).WillRepeatedly(Return(
             QuicTime::Delta::Zero()));
@@ -648,9 +676,7 @@
         .WillRepeatedly(Return(SequenceNumberSet()));
   }
 
-  QuicVersion version() {
-    return GetParam();
-  }
+  QuicVersion version() { return GetParam().version; }
 
   QuicAckFrame* outgoing_ack() {
     QuicConnectionPeer::PopulateAckFrame(&connection_, &ack_);
@@ -977,7 +1003,7 @@
 // Run all end to end tests with all supported versions.
 INSTANTIATE_TEST_CASE_P(SupportedVersion,
                         QuicConnectionTest,
-                        ::testing::ValuesIn(QuicSupportedVersions()));
+                        ::testing::ValuesIn(GetTestParams()));
 
 TEST_P(QuicConnectionTest, MaxPacketSize) {
   EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
@@ -1355,7 +1381,8 @@
 TEST_P(QuicConnectionTest, TooManySentPackets) {
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
 
-  for (int i = 0; i < 3100; ++i) {
+  const int num_packets = 5100;
+  for (int i = 0; i < num_packets; ++i) {
     SendStreamDataToPeer(1, "foo", 3 * i, !kFin, nullptr);
   }
 
@@ -1367,26 +1394,29 @@
   EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
 
   // Nack every packet except the last one, leaving a huge gap.
-  QuicAckFrame frame1 = InitAckFrame(3100);
-  for (QuicPacketSequenceNumber i = 1; i < 3100; ++i) {
+  QuicAckFrame frame1 = InitAckFrame(num_packets);
+  for (QuicPacketSequenceNumber i = 1; i < num_packets; ++i) {
     NackPacket(i, &frame1);
   }
   ProcessAckPacket(&frame1);
 }
 
+// Flaky time out on Windows 7. http://crbug.com/501812
+#if !defined(OS_WIN)
 TEST_P(QuicConnectionTest, TooManyReceivedPackets) {
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_CALL(visitor_, OnConnectionClosed(
                             QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS, false));
 
-  // Miss every other packet for 3000 packets.
-  for (QuicPacketSequenceNumber i = 1; i < 3000; ++i) {
+  // Miss every other packet for 5000 packets.
+  for (QuicPacketSequenceNumber i = 1; i < 5000; ++i) {
     ProcessPacket(i * 2);
     if (!connection_.connected()) {
       break;
     }
   }
 }
+#endif
 
 TEST_P(QuicConnectionTest, LargestObservedLower) {
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
@@ -1622,9 +1652,15 @@
           IN_FEC_GROUP, &payload_length);
   connection_.set_max_packet_length(length);
 
-  // Send 4 protected data packets, which should also trigger 1 FEC packet.
-  EXPECT_CALL(*send_algorithm_,
-              OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(5);
+  if (generator_->fec_send_policy() == FEC_ALARM_TRIGGER) {
+    // Send 4 protected data packets. FEC packet is not sent.
+    EXPECT_CALL(*send_algorithm_,
+                OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(4);
+  } else {
+    // Send 4 protected data packets, which should also trigger 1 FEC packet.
+    EXPECT_CALL(*send_algorithm_,
+                OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(5);
+  }
   // The first stream frame will have 2 fewer overhead bytes than the other 3.
   const string payload(payload_length * 4 + 2, 'a');
   connection_.SendStreamDataWithStringWithFec(1, payload, 0, !kFin, nullptr);
@@ -1649,8 +1685,13 @@
   connection_.SendStreamDataWithStringWithFec(1, payload, 0, !kFin, nullptr);
   EXPECT_FALSE(creator_->IsFecGroupOpen());
   EXPECT_FALSE(creator_->IsFecProtected());
-  // Expect the first data packet and the fec packet to be queued.
-  EXPECT_EQ(2u, connection_.NumQueuedPackets());
+  if (generator_->fec_send_policy() == FEC_ALARM_TRIGGER) {
+    // Expect the first data packet to be queued and not the FEC packet.
+    EXPECT_EQ(1u, connection_.NumQueuedPackets());
+  } else {
+    // Expect the first data packet and the fec packet to be queued.
+    EXPECT_EQ(2u, connection_.NumQueuedPackets());
+  }
 }
 
 TEST_P(QuicConnectionTest, FECAlarmStoppedWhenFECPacketSent) {
@@ -1666,11 +1707,21 @@
   connection_.SendStreamDataWithStringWithFec(3, "foo", 0, true, nullptr);
   EXPECT_TRUE(connection_.GetFecAlarm()->IsSet());
 
-  // Second data packet triggers FEC packet out. FEC alarm should not be set.
-  EXPECT_CALL(*send_algorithm_,
-              OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(2);
+  if (generator_->fec_send_policy() == FEC_ALARM_TRIGGER) {
+    // If FEC send policy is FEC_ALARM_TRIGGER, FEC packet is not sent.
+    // FEC alarm should not be set.
+    EXPECT_CALL(*send_algorithm_,
+                OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(1);
+  } else {
+    // Second data packet triggers FEC packet out. FEC alarm should not be set.
+    EXPECT_CALL(*send_algorithm_,
+                OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(2);
+  }
   connection_.SendStreamDataWithStringWithFec(5, "foo", 0, true, nullptr);
-  EXPECT_TRUE(writer_->header().fec_flag);
+  if (generator_->fec_send_policy() == FEC_ANY_TRIGGER) {
+    EXPECT_TRUE(writer_->header().fec_flag);
+  }
+  EXPECT_FALSE(creator_->IsFecGroupOpen());
   EXPECT_FALSE(connection_.GetFecAlarm()->IsSet());
 }
 
@@ -2112,10 +2163,13 @@
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
 
   char data[] = "ABCD";
-  IOVector data_iov;
-  data_iov.AppendNoCoalesce(data, 2);
-  data_iov.AppendNoCoalesce(data + 2, 2);
-  connection_.SendStreamData(1, data_iov, 0, !kFin, MAY_FEC_PROTECT, nullptr);
+  struct iovec iov[2];
+  iov[0].iov_base = data;
+  iov[0].iov_len = 2;
+  iov[1].iov_base = data + 2;
+  iov[1].iov_len = 2;
+  connection_.SendStreamData(1, QuicIOVector(iov, 2, 4), 0, !kFin,
+                             MAY_FEC_PROTECT, nullptr);
 
   EXPECT_EQ(0u, connection_.NumQueuedPackets());
   EXPECT_FALSE(connection_.HasQueuedData());
@@ -2135,10 +2189,13 @@
 
   BlockOnNextWrite();
   char data[] = "ABCD";
-  IOVector data_iov;
-  data_iov.AppendNoCoalesce(data, 2);
-  data_iov.AppendNoCoalesce(data + 2, 2);
-  connection_.SendStreamData(1, data_iov, 0, !kFin, MAY_FEC_PROTECT, nullptr);
+  struct iovec iov[2];
+  iov[0].iov_base = data;
+  iov[0].iov_len = 2;
+  iov[1].iov_base = data + 2;
+  iov[1].iov_len = 2;
+  connection_.SendStreamData(1, QuicIOVector(iov, 2, 4), 0, !kFin,
+                             MAY_FEC_PROTECT, nullptr);
 
   EXPECT_EQ(1u, connection_.NumQueuedPackets());
   EXPECT_TRUE(connection_.HasQueuedData());
@@ -2157,7 +2214,7 @@
 TEST_P(QuicConnectionTest, SendingZeroBytes) {
   // Send a zero byte write with a fin using writev.
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
-  IOVector empty_iov;
+  QuicIOVector empty_iov(nullptr, 0, 0);
   connection_.SendStreamData(1, empty_iov, 0, kFin, MAY_FEC_PROTECT, nullptr);
 
   EXPECT_EQ(0u, connection_.NumQueuedPackets());
@@ -4384,6 +4441,27 @@
       "Attempt to send empty stream frame");
 }
 
+TEST_P(QuicConnectionTest, FecSendPolicyReceivedConnectionOption) {
+  // Test sending SetReceivedConnectionOptions when FEC send policy is
+  // FEC_ANY_TRIGGER.
+  if (GetParam().fec_send_policy == FEC_ALARM_TRIGGER) {
+    return;
+  }
+  ValueRestore<bool> old_flag(&FLAGS_quic_send_fec_packet_only_on_fec_alarm,
+                              true);
+  connection_.set_perspective(Perspective::IS_SERVER);
+
+  // Test ReceivedConnectionOptions.
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  QuicConfig config;
+  QuicTagVector copt;
+  copt.push_back(kFSPA);
+  QuicConfigPeer::SetReceivedConnectionOptions(&config, copt);
+  EXPECT_EQ(FEC_ANY_TRIGGER, generator_->fec_send_policy());
+  connection_.SetFromConfig(config);
+  EXPECT_EQ(FEC_ALARM_TRIGGER, generator_->fec_send_policy());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/quic_data_writer.cc b/net/quic/quic_data_writer.cc
index d3a6699..4925782 100644
--- a/net/quic/quic_data_writer.cc
+++ b/net/quic/quic_data_writer.cc
@@ -95,18 +95,6 @@
   return WriteBytes(val.data(), val.size());
 }
 
-bool QuicDataWriter::WriteIOVector(const IOVector& data) {
-  char *dest = BeginWrite(data.TotalBufferSize());
-  if (!dest) {
-    return false;
-  }
-  for (size_t i = 0; i < data.Size(); ++i) {
-    WriteBytes(data.iovec()[i].iov_base,  data.iovec()[i].iov_len);
-  }
-
-  return true;
-}
-
 char* QuicDataWriter::BeginWrite(size_t length) {
   if (length_ > capacity_) {
     return nullptr;
diff --git a/net/quic/quic_data_writer.h b/net/quic/quic_data_writer.h
index 052ade81..0f1df204 100644
--- a/net/quic/quic_data_writer.h
+++ b/net/quic/quic_data_writer.h
@@ -48,7 +48,6 @@
   // not be represented directly are rounded down.
   bool WriteUFloat16(uint64 value);
   bool WriteStringPiece16(base::StringPiece val);
-  bool WriteIOVector(const IOVector& data);
   bool WriteBytes(const void* data, size_t data_len);
   bool WriteRepeatedByte(uint8 byte, size_t count);
   // Fills the remaining buffer with null characters.
diff --git a/net/quic/quic_flags.cc b/net/quic/quic_flags.cc
index a0b053e4..11e016f 100644
--- a/net/quic/quic_flags.cc
+++ b/net/quic/quic_flags.cc
@@ -51,3 +51,6 @@
 
 // Don't ack acks in QUIC, even when there is a recent missing packet.
 bool FLAGS_quic_dont_ack_acks = true;
+
+// Enables sending of FEC packet only when FEC alarm goes off.
+bool FLAGS_quic_send_fec_packet_only_on_fec_alarm = true;
diff --git a/net/quic/quic_flags.h b/net/quic/quic_flags.h
index 37fe280..4351ced 100644
--- a/net/quic/quic_flags.h
+++ b/net/quic/quic_flags.h
@@ -21,5 +21,6 @@
 NET_EXPORT_PRIVATE extern bool FLAGS_quic_auto_tune_receive_window;
 NET_EXPORT_PRIVATE extern bool FLAGS_quic_stop_early_2;
 NET_EXPORT_PRIVATE extern bool FLAGS_quic_dont_ack_acks;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_send_fec_packet_only_on_fec_alarm;
 
 #endif  // NET_QUIC_QUIC_FLAGS_H_
diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc
index 208dd81f..c01b7f29 100644
--- a/net/quic/quic_framer.cc
+++ b/net/quic/quic_framer.cc
@@ -173,7 +173,6 @@
 
 // static
 size_t QuicFramer::GetMinAckFrameSize(
-    QuicSequenceNumberLength sequence_number_length,
     QuicSequenceNumberLength largest_observed_length) {
   return kQuicFrameTypeSize + kQuicEntropyHashSize +
       largest_observed_length + kQuicDeltaTimeLargestObservedSize;
@@ -301,9 +300,9 @@
   if (!first_frame) {
     return 0;
   }
-  bool can_truncate = frame.type == ACK_FRAME &&
-      free_bytes >= GetMinAckFrameSize(PACKET_6BYTE_SEQUENCE_NUMBER,
-                                       PACKET_6BYTE_SEQUENCE_NUMBER);
+  bool can_truncate =
+      frame.type == ACK_FRAME &&
+      free_bytes >= GetMinAckFrameSize(PACKET_6BYTE_SEQUENCE_NUMBER);
   if (can_truncate) {
     // Truncate the frame so the packet will not exceed kMaxPacketSize.
     // Note that we may not use every byte of the writer in this case.
@@ -1728,8 +1727,7 @@
   QuicSequenceNumberLength missing_sequence_number_length =
       GetMinSequenceNumberLength(ack_info.max_delta);
 
-  size_t ack_size = GetMinAckFrameSize(sequence_number_length,
-                                       largest_observed_length);
+  size_t ack_size = GetMinAckFrameSize(largest_observed_length);
   if (!ack_info.nack_ranges.empty()) {
     ack_size += kNumberOfNackRangesSize  + kNumberOfRevivedPacketsSize;
     ack_size += min(ack_info.nack_ranges.size(), kMaxNackRanges) *
@@ -1919,10 +1917,9 @@
   QuicSequenceNumberLength missing_sequence_number_length =
       GetMinSequenceNumberLength(ack_info.max_delta);
   // Determine whether we need to truncate ranges.
-  size_t available_range_bytes = writer->capacity() - writer->length() -
-      kNumberOfRevivedPacketsSize - kNumberOfNackRangesSize -
-      GetMinAckFrameSize(header.public_header.sequence_number_length,
-                         largest_observed_length);
+  size_t available_range_bytes =
+      writer->capacity() - writer->length() - kNumberOfRevivedPacketsSize -
+      kNumberOfNackRangesSize - GetMinAckFrameSize(largest_observed_length);
   size_t max_num_ranges = available_range_bytes /
       (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER);
   max_num_ranges = min(kMaxNackRanges, max_num_ranges);
diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h
index 3645ca8..c994c2ef 100644
--- a/net/quic/quic_framer.h
+++ b/net/quic/quic_framer.h
@@ -243,7 +243,6 @@
                                       InFecGroup is_in_fec_group);
   // Size in bytes of all ack frame fields without the missing packets.
   static size_t GetMinAckFrameSize(
-      QuicSequenceNumberLength sequence_number_length,
       QuicSequenceNumberLength largest_observed_length);
   // Size in bytes of a stop waiting frame.
   static size_t GetStopWaitingFrameSize(
diff --git a/net/quic/quic_headers_stream.cc b/net/quic/quic_headers_stream.cc
index 3fbb876..35d81d04 100644
--- a/net/quic/quic_headers_stream.cc
+++ b/net/quic/quic_headers_stream.cc
@@ -175,8 +175,8 @@
       stream_id_(kInvalidStreamId),
       fin_(false),
       frame_len_(0),
-      spdy_framer_(SPDY4),
-      spdy_framer_visitor_(new SpdyFramerVisitor(SPDY4, this)) {
+      spdy_framer_(HTTP2),
+      spdy_framer_visitor_(new SpdyFramerVisitor(HTTP2, this)) {
   spdy_framer_.set_visitor(spdy_framer_visitor_.get());
   spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
   // The headers stream is exempt from connection level flow control.
diff --git a/net/quic/quic_headers_stream_test.cc b/net/quic/quic_headers_stream_test.cc
index bf66d34..b2cd2561 100644
--- a/net/quic/quic_headers_stream_test.cc
+++ b/net/quic/quic_headers_stream_test.cc
@@ -108,7 +108,7 @@
         session_(connection_),
         headers_stream_(QuicSpdySessionPeer::GetHeadersStream(&session_)),
         body_("hello world"),
-        framer_(SPDY4) {
+        framer_(HTTP2) {
     headers_[":version"]  = "HTTP/1.1";
     headers_[":status"] = "200 Ok";
     headers_["content-length"] = "11";
@@ -118,9 +118,9 @@
     VLOG(1) << GetParam();
   }
 
-  QuicConsumedData SaveIov(const IOVector& data) {
-    const iovec* iov = data.iovec();
-    int count = data.Capacity();
+  QuicConsumedData SaveIov(const QuicIOVector& data) {
+    const iovec* iov = data.iov;
+    int count = data.iov_count;
     for (int i = 0 ; i < count; ++i) {
       saved_data_.append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len);
     }
diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc
index 9d1f9329..0b39d190 100644
--- a/net/quic/quic_packet_creator.cc
+++ b/net/quic/quic_packet_creator.cc
@@ -194,7 +194,7 @@
   }
   if (!queued_frames_.empty()) {
     // Don't change creator state if there are frames queued.
-    return fec_group_.get() == nullptr ? NOT_IN_FEC_GROUP : IN_FEC_GROUP;
+    return NOT_IN_FEC_GROUP;
   }
 
   // Update sequence number length only on packet and FEC group boundaries.
@@ -261,7 +261,8 @@
 }
 
 size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
-                                            IOVector* data,
+                                            const QuicIOVector& iov,
+                                            size_t iov_offset,
                                             QuicStreamOffset offset,
                                             bool fin,
                                             QuicFrame* frame,
@@ -278,7 +279,7 @@
       << " MinStreamFrameSize: "
       << QuicFramer::GetMinStreamFrameSize(id, offset, true, is_in_fec_group);
 
-  if (data->Empty()) {
+  if (iov_offset == iov.total_length) {
     LOG_IF(DFATAL, !fin)
         << "Creating a stream frame with no data or fin.";
     // Create a new packet for the fin, if necessary.
@@ -286,21 +287,41 @@
     return 0;
   }
 
-  const size_t data_size = data->TotalBufferSize();
+  const size_t data_size = iov.total_length - iov_offset;
   size_t min_frame_size = QuicFramer::GetMinStreamFrameSize(
       id, offset, /* last_frame_in_packet= */ true, is_in_fec_group);
   size_t bytes_consumed = min<size_t>(BytesFree() - min_frame_size, data_size);
 
   bool set_fin = fin && bytes_consumed == data_size;  // Last frame.
   buffer->reset(new char[bytes_consumed]);
-  size_t bytes_copied = data->ConsumeAndCopy(bytes_consumed, buffer->get());
-  LOG_IF(DFATAL, bytes_copied < bytes_consumed)
-      << "Fewer bytes copied than in IOVector.";
+  CopyToBuffer(iov, iov_offset, bytes_consumed, buffer->get());
   *frame = QuicFrame(new QuicStreamFrame(
       id, set_fin, offset, StringPiece(buffer->get(), bytes_consumed)));
   return bytes_consumed;
 }
 
+// static
+void QuicPacketCreator::CopyToBuffer(const QuicIOVector& iov,
+                                     size_t iov_offset,
+                                     size_t length,
+                                     char* buffer) {
+  int iovnum = 0;
+  while (iovnum < iov.iov_count && iov_offset >= iov.iov[iovnum].iov_len) {
+    iov_offset -= iov.iov[iovnum].iov_len;
+    ++iovnum;
+  }
+  while (iovnum < iov.iov_count && length > 0) {
+    const size_t copy_len = min(length, iov.iov[iovnum].iov_len - iov_offset);
+    memcpy(buffer, static_cast<char*>(iov.iov[iovnum].iov_base) + iov_offset,
+           copy_len);
+    iov_offset = 0;
+    length -= copy_len;
+    buffer += copy_len;
+    ++iovnum;
+  }
+  LOG_IF(DFATAL, length > 0) << "Failed to copy entire length to buffer.";
+}
+
 SerializedPacket QuicPacketCreator::ReserializeAllFrames(
     const RetransmittableFrames& frames,
     QuicSequenceNumberLength original_length,
diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h
index 61eff07..493a47b 100644
--- a/net/quic/quic_packet_creator.h
+++ b/net/quic/quic_packet_creator.h
@@ -79,12 +79,14 @@
   bool HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset) const;
 
   // Converts a raw payload to a frame which fits into the currently open
-  // packet if there is one.  Returns the number of bytes consumed from data.
+  // packet.  The payload begins at |iov_offset| into the |iov|.
+  // Returns the number of bytes consumed from data.
   // If data is empty and fin is true, the expected behavior is to consume the
   // fin but return 0.  If any data is consumed, it will be copied into a
   // new buffer that |frame| will point to and will be stored in |buffer|.
   size_t CreateStreamFrame(QuicStreamId id,
-                           IOVector* data,
+                           const QuicIOVector& iov,
+                           size_t iov_offset,
                            QuicStreamOffset offset,
                            bool fin,
                            QuicFrame* frame,
@@ -240,6 +242,14 @@
 
   static bool ShouldRetransmit(const QuicFrame& frame);
 
+  // Copies |length| bytes from iov starting at offset |iov_offset| into buffer.
+  // |iov| must be at least iov_offset+length total length and buffer must be
+  // at least |length| long.
+  static void CopyToBuffer(const QuicIOVector& iov,
+                           size_t iov_offset,
+                           size_t length,
+                           char* buffer);
+
   // Updates lengths and also starts an FEC group if FEC protection is on and
   // there is not already an FEC group open.
   InFecGroup MaybeUpdateLengthsAndStartFec();
diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc
index 8143eccc..46cf528 100644
--- a/net/quic/quic_packet_creator_test.cc
+++ b/net/quic/quic_packet_creator_test.cc
@@ -139,6 +139,10 @@
     return creator_.IsFecProtected();
   }
 
+  QuicIOVector MakeIOVector(StringPiece s) {
+    return ::net::MakeIOVector(s, &iov_);
+  }
+
   static const QuicStreamOffset kOffset = 1u;
 
   QuicFrames frames_;
@@ -147,6 +151,7 @@
   testing::StrictMock<MockFramerVisitor> framer_visitor_;
   QuicConnectionId connection_id_;
   string data_;
+  struct iovec iov_;
   MockRandom mock_random_;
   QuicPacketCreator creator_;
   MockEntropyCalculator entropy_calculator_;
@@ -485,9 +490,9 @@
 
 TEST_P(QuicPacketCreatorTest, ReserializeFramesWithPadding) {
   QuicFrame frame;
-  IOVector io_vector(MakeIOVector("fake handshake message data"));
+  QuicIOVector io_vector(MakeIOVector("fake handshake message data"));
   scoped_ptr<char[]> stream_buffer;
-  creator_.CreateStreamFrame(kCryptoStreamId, &io_vector, 0u, false, &frame,
+  creator_.CreateStreamFrame(kCryptoStreamId, io_vector, 0u, 0u, false, &frame,
                              &stream_buffer);
   RetransmittableFrames frames(ENCRYPTION_NONE);
   frames.AddFrame(frame);
@@ -509,9 +514,9 @@
     size_t bytes_free = 0 - delta;
 
     QuicFrame frame;
-    IOVector io_vector(MakeIOVector(data));
+    QuicIOVector io_vector(MakeIOVector(data));
     scoped_ptr<char[]> stream_buffer;
-    creator_.CreateStreamFrame(kCryptoStreamId, &io_vector, kOffset, false,
+    creator_.CreateStreamFrame(kCryptoStreamId, io_vector, 0, kOffset, false,
                                &frame, &stream_buffer);
     RetransmittableFrames frames(ENCRYPTION_NONE);
     frames.AddFrame(frame);
@@ -617,9 +622,9 @@
 TEST_P(QuicPacketCreatorTest, SwitchFecOnWithStreamFrameQueued) {
   // Add a stream frame to the creator.
   QuicFrame frame;
-  IOVector io_vector(MakeIOVector("test"));
+  QuicIOVector io_vector(MakeIOVector("test"));
   scoped_ptr<char[]> stream_buffer;
-  size_t consumed = creator_.CreateStreamFrame(1u, &io_vector, 0u, false,
+  size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, false,
                                                &frame, &stream_buffer);
   EXPECT_EQ(4u, consumed);
   ASSERT_TRUE(frame.stream_frame);
@@ -648,9 +653,9 @@
 
 TEST_P(QuicPacketCreatorTest, CreateStreamFrame) {
   QuicFrame frame;
-  IOVector io_vector(MakeIOVector("test"));
+  QuicIOVector io_vector(MakeIOVector("test"));
   scoped_ptr<char[]> stream_buffer;
-  size_t consumed = creator_.CreateStreamFrame(1u, &io_vector, 0u, false,
+  size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, false,
                                                &frame, &stream_buffer);
   EXPECT_EQ(4u, consumed);
   CheckStreamFrame(frame, 1u, "test", 0u, false);
@@ -660,9 +665,9 @@
 
 TEST_P(QuicPacketCreatorTest, CreateStreamFrameFin) {
   QuicFrame frame;
-  IOVector io_vector(MakeIOVector("test"));
+  QuicIOVector io_vector(MakeIOVector("test"));
   scoped_ptr<char[]> stream_buffer;
-  size_t consumed = creator_.CreateStreamFrame(1u, &io_vector, 10u, true,
+  size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 10u, true,
                                                &frame, &stream_buffer);
   EXPECT_EQ(4u, consumed);
   CheckStreamFrame(frame, 1u, "test", 10u, true);
@@ -672,10 +677,10 @@
 
 TEST_P(QuicPacketCreatorTest, CreateStreamFrameFinOnly) {
   QuicFrame frame;
-  IOVector io_vector;
+  QuicIOVector io_vector(nullptr, 0, 0);
   scoped_ptr<char[]> stream_buffer;
-  size_t consumed = creator_.CreateStreamFrame(1u, &io_vector, 0u, true, &frame,
-                                               &stream_buffer);
+  size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, true,
+                                               &frame, &stream_buffer);
   EXPECT_EQ(0u, consumed);
   CheckStreamFrame(frame, 1u, string(), 0u, true);
   delete frame.stream_frame;
@@ -692,11 +697,11 @@
                                     kClientDataStreamId1, kOffset));
     if (should_have_room) {
       QuicFrame frame;
-      IOVector io_vector(MakeIOVector("testdata"));
+      QuicIOVector io_vector(MakeIOVector("testdata"));
       scoped_ptr<char[]> stream_buffer;
       size_t bytes_consumed =
-          creator_.CreateStreamFrame(kClientDataStreamId1, &io_vector, kOffset,
-                                     false, &frame, &stream_buffer);
+          creator_.CreateStreamFrame(kClientDataStreamId1, io_vector, 0u,
+                                     kOffset, false, &frame, &stream_buffer);
       EXPECT_LT(0u, bytes_consumed);
       ASSERT_TRUE(creator_.AddSavedFrame(frame));
       char buffer[kMaxPacketSize];
@@ -719,10 +724,10 @@
     string data(capacity + delta, 'A');
     size_t bytes_free = delta > 0 ? 0 : 0 - delta;
     QuicFrame frame;
-    IOVector io_vector(MakeIOVector(data));
+    QuicIOVector io_vector(MakeIOVector(data));
     scoped_ptr<char[]> stream_buffer;
     size_t bytes_consumed =
-        creator_.CreateStreamFrame(kClientDataStreamId1, &io_vector, kOffset,
+        creator_.CreateStreamFrame(kClientDataStreamId1, io_vector, 0u, kOffset,
                                    false, &frame, &stream_buffer);
     EXPECT_EQ(capacity - bytes_free, bytes_consumed);
 
@@ -753,10 +758,10 @@
     string data(capacity + delta, 'A');
     size_t bytes_free = delta > 0 ? 0 : 0 - delta;
     QuicFrame frame;
-    IOVector io_vector(MakeIOVector(data));
+    QuicIOVector io_vector(MakeIOVector(data));
     scoped_ptr<char[]> stream_buffer;
     size_t bytes_consumed =
-        creator_.CreateStreamFrame(kClientDataStreamId1, &io_vector, kOffset,
+        creator_.CreateStreamFrame(kClientDataStreamId1, io_vector, 0u, kOffset,
                                    false, &frame, &stream_buffer);
     EXPECT_EQ(capacity - bytes_free, bytes_consumed);
 
@@ -788,10 +793,10 @@
     size_t bytes_free = delta > 0 ? 0 : 0 - delta;
 
     QuicFrame frame;
-    IOVector io_vector(MakeIOVector(data));
+    QuicIOVector io_vector(MakeIOVector(data));
     scoped_ptr<char[]> stream_buffer;
     size_t bytes_consumed = creator_.CreateStreamFrame(
-        kCryptoStreamId, &io_vector, kOffset, false, &frame, &stream_buffer);
+        kCryptoStreamId, io_vector, 0u, kOffset, false, &frame, &stream_buffer);
     EXPECT_LT(0u, bytes_consumed);
     ASSERT_TRUE(creator_.AddPaddedSavedFrame(frame, nullptr));
     char buffer[kMaxPacketSize];
@@ -824,10 +829,10 @@
     size_t bytes_free = delta > 0 ? 0 : 0 - delta;
 
     QuicFrame frame;
-    IOVector io_vector(MakeIOVector(data));
+    QuicIOVector io_vector(MakeIOVector(data));
     scoped_ptr<char[]> stream_buffer;
     size_t bytes_consumed =
-        creator_.CreateStreamFrame(kClientDataStreamId1, &io_vector, kOffset,
+        creator_.CreateStreamFrame(kClientDataStreamId1, io_vector, 0u, kOffset,
                                    false, &frame, &stream_buffer);
     EXPECT_LT(0u, bytes_consumed);
     ASSERT_TRUE(creator_.AddSavedFrame(frame));
@@ -960,10 +965,10 @@
       PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP, &payload_length));
   QuicFrame frame;
   const string too_long_payload(payload_length * 2, 'a');
-  IOVector io_vector(MakeIOVector(too_long_payload));
+  QuicIOVector io_vector(MakeIOVector(too_long_payload));
   scoped_ptr<char[]> stream_buffer;
-  size_t consumed = creator_.CreateStreamFrame(1u, &io_vector, 0u, true, &frame,
-                                               &stream_buffer);
+  size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, true,
+                                               &frame, &stream_buffer);
   EXPECT_EQ(payload_length, consumed);
   const string payload(payload_length, 'a');
   CheckStreamFrame(frame, 1u, payload, 0u, false);
@@ -991,9 +996,9 @@
   EXPECT_TRUE(creator_.HasPendingFrames());
 
   QuicFrame frame;
-  IOVector io_vector(MakeIOVector("test"));
+  QuicIOVector io_vector(MakeIOVector("test"));
   scoped_ptr<char[]> stream_buffer;
-  size_t consumed = creator_.CreateStreamFrame(1u, &io_vector, 0u, false,
+  size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, false,
                                                &frame, &stream_buffer);
   EXPECT_EQ(4u, consumed);
   ASSERT_TRUE(frame.stream_frame);
@@ -1053,9 +1058,9 @@
 
   // Make sure that an additional stream frame can be added to the packet.
   QuicFrame stream_frame;
-  IOVector io_vector(MakeIOVector("test"));
+  QuicIOVector io_vector(MakeIOVector("test"));
   scoped_ptr<char[]> stream_buffer;
-  size_t consumed = creator_.CreateStreamFrame(2u, &io_vector, 0u, false,
+  size_t consumed = creator_.CreateStreamFrame(2u, io_vector, 0u, 0u, false,
                                                &stream_frame, &stream_buffer);
   EXPECT_EQ(4u, consumed);
   ASSERT_TRUE(stream_frame.stream_frame);
@@ -1199,9 +1204,9 @@
 TEST_P(QuicPacketCreatorTest, ResetFecGroupWithQueuedFrames) {
   // Add a stream frame to the creator.
   QuicFrame frame;
-  IOVector io_vector(MakeIOVector("test"));
+  QuicIOVector io_vector(MakeIOVector("test"));
   scoped_ptr<char[]> stream_buffer;
-  size_t consumed = creator_.CreateStreamFrame(1u, &io_vector, 0u, false,
+  size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, false,
                                                &frame, &stream_buffer);
   EXPECT_EQ(4u, consumed);
   ASSERT_TRUE(frame.stream_frame);
diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc
index 7da50ebd..6b7116479 100644
--- a/net/quic/quic_packet_generator.cc
+++ b/net/quic/quic_packet_generator.cc
@@ -45,7 +45,6 @@
       batch_mode_(false),
       fec_timeout_(QuicTime::Delta::Zero()),
       should_fec_protect_(false),
-      // TODO(rtenneti): Add the ability to set a different policy.
       fec_send_policy_(FEC_ANY_TRIGGER),
       should_send_ack_(false),
       should_send_stop_waiting_(false),
@@ -127,7 +126,7 @@
 
 QuicConsumedData QuicPacketGenerator::ConsumeData(
     QuicStreamId id,
-    const IOVector& data_to_write,
+    const QuicIOVector& iov,
     QuicStreamOffset offset,
     bool fin,
     FecProtection fec_protection,
@@ -157,9 +156,7 @@
     notifier = new QuicAckNotifier(delegate);
   }
 
-  IOVector data = data_to_write;
-  size_t data_size = data.TotalBufferSize();
-  if (!fin && (data_size == 0)) {
+  if (!fin && (iov.total_length == 0)) {
     LOG(DFATAL) << "Attempt to consume empty data without FIN.";
     return QuicConsumedData(0, false);
   }
@@ -170,7 +167,8 @@
     QuicFrame frame;
     scoped_ptr<char[]> buffer;
     size_t bytes_consumed = packet_creator_.CreateStreamFrame(
-        id, &data, offset + total_bytes_consumed, fin, &frame, &buffer);
+        id, iov, total_bytes_consumed, offset + total_bytes_consumed, fin,
+        &frame, &buffer);
     ++frames_created;
 
     // We want to track which packet this stream frame ends up in.
@@ -190,10 +188,10 @@
     ignore_result(buffer.release());
 
     total_bytes_consumed += bytes_consumed;
-    fin_consumed = fin && total_bytes_consumed == data_size;
-    DCHECK(data.Empty() || packet_creator_.BytesFree() == 0u);
+    fin_consumed = fin && total_bytes_consumed == iov.total_length;
+    DCHECK(total_bytes_consumed == iov.total_length ||
+           packet_creator_.BytesFree() == 0u);
 
-    // TODO(ianswett): Restore packet reordering.
     if (!InBatchMode() || !packet_creator_.HasRoomForStreamFrame(id, offset)) {
       // TODO(rtenneti): remove MaybeSendFecPacketAndCloseGroup() from inside
       // SerializeAndSendPacket() and make it an explicit call here (and
@@ -201,7 +199,7 @@
       SerializeAndSendPacket();
     }
 
-    if (data.Empty()) {
+    if (total_bytes_consumed == iov.total_length) {
       // We're done writing the data. Exit the loop.
       // We don't make this a precondition because we could have 0 bytes of data
       // if we're simply writing a fin.
diff --git a/net/quic/quic_packet_generator.h b/net/quic/quic_packet_generator.h
index bac2bed..36aab2c 100644
--- a/net/quic/quic_packet_generator.h
+++ b/net/quic/quic_packet_generator.h
@@ -122,7 +122,7 @@
   // |delegate| (if not nullptr) will be informed once all packets sent as a
   // result of this call are ACKed by the peer.
   QuicConsumedData ConsumeData(QuicStreamId id,
-                               const IOVector& data,
+                               const QuicIOVector& iov,
                                QuicStreamOffset offset,
                                bool fin,
                                FecProtection fec_protection,
@@ -207,6 +207,11 @@
     debug_delegate_ = debug_delegate;
   }
 
+  FecSendPolicy fec_send_policy() { return fec_send_policy_; }
+  void set_fec_send_policy(FecSendPolicy fec_send_policy) {
+    fec_send_policy_ = fec_send_policy;
+  }
+
  private:
   friend class test::QuicPacketGeneratorPeer;
 
diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc
index 7772ee92..bf601e6d 100644
--- a/net/quic/quic_packet_generator_test.cc
+++ b/net/quic/quic_packet_generator_test.cc
@@ -113,7 +113,7 @@
                 Perspective::IS_CLIENT),
         generator_(42, &framer_, &random_, &delegate_),
         creator_(QuicPacketGeneratorPeer::GetPacketCreator(&generator_)) {
-    QuicPacketGeneratorPeer::SetFecSendPolicy(&generator_, GetParam());
+    generator_.set_fec_send_policy(GetParam());
   }
 
   ~QuicPacketGeneratorTest() override {
@@ -203,12 +203,16 @@
     EXPECT_EQ(fec_group, simple_framer_.fec_data().fec_group);
   }
 
-  IOVector CreateData(size_t len) {
+  QuicIOVector CreateData(size_t len) {
     data_array_.reset(new char[len]);
     memset(data_array_.get(), '?', len);
-    IOVector data;
-    data.Append(data_array_.get(), len);
-    return data;
+    iov_.iov_base = data_array_.get();
+    iov_.iov_len = len;
+    return QuicIOVector(&iov_, 1, len);
+  }
+
+  QuicIOVector MakeIOVector(StringPiece s) {
+    return ::net::MakeIOVector(s, &iov_);
   }
 
   QuicFramer framer_;
@@ -221,6 +225,7 @@
 
  private:
   scoped_ptr<char[]> data_array_;
+  struct iovec iov_;
 };
 
 class MockDebugDelegate : public QuicPacketGenerator::DebugDelegate {
@@ -459,8 +464,7 @@
         .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
     EXPECT_CALL(delegate_, OnSerializedPacket(_))
         .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
-    if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-        FEC_ALARM_TRIGGER) {
+    if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
       // FEC packet is not sent when send policy is FEC_ALARM_TRIGGER, but FEC
       // group is closed.
       EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1);
@@ -483,8 +487,7 @@
 
   CheckPacketHasSingleStreamFrame(0);
   CheckPacketHasSingleStreamFrame(1);
-  if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-      FEC_ALARM_TRIGGER) {
+  if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
     // FEC packet is not sent when send policy is FEC_ALARM_TRIGGER.
     CheckPacketHasSingleStreamFrame(2);
   } else {
@@ -505,8 +508,7 @@
     InSequence dummy;
     EXPECT_CALL(delegate_, OnSerializedPacket(_))
         .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
-    if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-        FEC_ALARM_TRIGGER) {
+    if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
       EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1);
     } else {
       EXPECT_CALL(delegate_, OnSerializedPacket(_))
@@ -516,8 +518,7 @@
   consumed = generator_.ConsumeData(5, CreateData(1u), 0, true, MAY_FEC_PROTECT,
                                     nullptr);
   EXPECT_EQ(1u, consumed.bytes_consumed);
-  if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-      FEC_ALARM_TRIGGER) {
+  if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
     CheckPacketHasSingleStreamFrame(3);
   } else {
     CheckPacketHasSingleStreamFrame(4);
@@ -820,8 +821,7 @@
     InSequence dummy;
     EXPECT_CALL(delegate_, OnSerializedPacket(_))
         .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
-    if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-        FEC_ALARM_TRIGGER) {
+    if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
       EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1);
     } else {
       EXPECT_CALL(delegate_, OnSerializedPacket(_))
@@ -831,8 +831,7 @@
   consumed = generator_.ConsumeData(7, CreateData(kDefaultMaxPacketSize), 0,
                                     true, MAY_FEC_PROTECT, nullptr);
   EXPECT_EQ(kDefaultMaxPacketSize, consumed.bytes_consumed);
-  if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-      FEC_ANY_TRIGGER) {
+  if (generator_.fec_send_policy() == FEC_ANY_TRIGGER) {
     // Verify that one FEC packet was sent.
     CheckPacketIsFec(4, /*fec_group=*/1u);
   }
@@ -864,8 +863,7 @@
         .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
     EXPECT_CALL(delegate_, OnSerializedPacket(_))
         .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
-    if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-        FEC_ALARM_TRIGGER) {
+    if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
       // If FEC send policy is FEC_ALARM_TRIGGER, FEC group is closed.
       EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1);
     } else {
@@ -889,8 +887,7 @@
   // data and FEC group is closed.
   CheckPacketHasSingleStreamFrame(1);
   CheckPacketHasSingleStreamFrame(2);
-  if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-      FEC_ALARM_TRIGGER) {
+  if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
     CheckPacketHasSingleStreamFrame(3);
   } else {
     CheckPacketIsFec(3, /*fec_group=*/2u);
@@ -901,8 +898,7 @@
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
   generator_.OnFecTimeout();
-  if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-      FEC_ALARM_TRIGGER) {
+  if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
     CheckPacketIsFec(4, /*fec_group=*/4u);
   } else {
     CheckPacketIsFec(5, /*fec_group=*/5u);
@@ -917,8 +913,7 @@
   EXPECT_FALSE(generator_.HasQueuedFrames());
   EXPECT_FALSE(creator_->IsFecProtected());
   // Verify that one unprotected data packet was sent.
-  if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-      FEC_ALARM_TRIGGER) {
+  if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
     CheckPacketContains(contents, 5);
   } else {
     CheckPacketContains(contents, 6);
@@ -1047,8 +1042,7 @@
     InSequence dummy;
     EXPECT_CALL(delegate_, OnSerializedPacket(_))
         .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
-    if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-        FEC_ALARM_TRIGGER) {
+    if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
       EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1);
     } else {
       EXPECT_CALL(delegate_, OnSerializedPacket(_))
@@ -1060,8 +1054,7 @@
   EXPECT_EQ(1u, consumed.bytes_consumed);
   contents.num_stream_frames = 1u;
   CheckPacketContains(contents, 1);
-  if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-      FEC_ANY_TRIGGER) {
+  if (generator_.fec_send_policy() == FEC_ANY_TRIGGER) {
     // FEC packet is sent when send policy is FEC_ANY_TRIGGER.
     CheckPacketIsFec(2, /*fec_group=*/1u);
   }
@@ -1104,8 +1097,7 @@
     InSequence dummy;
     EXPECT_CALL(delegate_, OnSerializedPacket(_))
         .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
-    if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-        FEC_ALARM_TRIGGER) {
+    if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
       EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1);
     } else {
       EXPECT_CALL(delegate_, OnSerializedPacket(_))
@@ -1116,8 +1108,7 @@
                                     MUST_FEC_PROTECT, nullptr);
   EXPECT_EQ(data_len, consumed.bytes_consumed);
   CheckPacketContains(contents, 1);
-  if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-      FEC_ANY_TRIGGER) {
+  if (generator_.fec_send_policy() == FEC_ANY_TRIGGER) {
     // FEC packet is sent when send policy is FEC_ANY_TRIGGER.
     CheckPacketIsFec(2, /*fec_group=*/1u);
   }
@@ -1142,8 +1133,7 @@
         .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
     EXPECT_CALL(delegate_, OnSerializedPacket(_))
         .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
-    if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-        FEC_ALARM_TRIGGER) {
+    if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
       // FEC packet is not sent when send policy is FEC_ALARM_TRIGGER, but FEC
       // group is closed.
       EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1);
@@ -1163,8 +1153,7 @@
   EXPECT_FALSE(generator_.HasQueuedFrames());
   CheckPacketHasSingleStreamFrame(0);
   CheckPacketHasSingleStreamFrame(1);
-  if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-      FEC_ALARM_TRIGGER) {
+  if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
     // FEC packet is not sent when send policy is FEC_ALARM_TRIGGER.
     CheckPacketHasSingleStreamFrame(2);
   } else {
@@ -1183,8 +1172,7 @@
     // FEC packet is sent after 2 packets and when send policy is
     // FEC_ANY_TRIGGER. When policy is FEC_ALARM_TRIGGER, FEC group is closed
     // and FEC packet is not sent.
-    if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-        FEC_ALARM_TRIGGER) {
+    if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
       EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1);
     } else {
       EXPECT_CALL(delegate_, OnSerializedPacket(_))
@@ -1197,8 +1185,7 @@
     // FEC packet is sent after 2 packets and when send policy is
     // FEC_ANY_TRIGGER. When policy is FEC_ALARM_TRIGGER, FEC group is closed
     // and FEC packet is not sent.
-    if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-        FEC_ALARM_TRIGGER) {
+    if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
       EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1);
     } else {
       EXPECT_CALL(delegate_, OnSerializedPacket(_))
@@ -1210,8 +1197,7 @@
   EXPECT_EQ(data_len, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   EXPECT_FALSE(generator_.HasQueuedFrames());
-  if (QuicPacketGeneratorPeer::GetFecSendPolicy(&generator_) ==
-      FEC_ALARM_TRIGGER) {
+  if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) {
     CheckPacketHasSingleStreamFrame(3);
     CheckPacketHasSingleStreamFrame(4);
     CheckPacketHasSingleStreamFrame(5);
diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h
index 1eea799..f7437a1 100644
--- a/net/quic/quic_protocol.h
+++ b/net/quic/quic_protocol.h
@@ -21,11 +21,12 @@
 #include "base/logging.h"
 #include "base/strings/string_piece.h"
 #include "net/base/int128.h"
+#include "net/base/iovec.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_export.h"
-#include "net/quic/iovector.h"
 #include "net/quic/quic_bandwidth.h"
 #include "net/quic/quic_time.h"
+#include "net/quic/quic_types.h"
 
 namespace net {
 
@@ -1014,7 +1015,7 @@
   const EncryptionLevel encryption_level_;
   IsHandshake has_crypto_handshake_;
   bool needs_padding_;
-  // Data referenced by the IOVector of a QuicStreamFrame.
+  // Data referenced by the StringPiece of a QuicStreamFrame.
   std::vector<const char*> stream_data_;
 
   DISALLOW_COPY_AND_ASSIGN(RetransmittableFrames);
@@ -1070,6 +1071,17 @@
   bool is_fec_packet;
 };
 
+// Convenience wrapper to wrap an iovec array and the total length, which must
+// be less than or equal to the actual total length of the iovecs.
+struct NET_EXPORT_PRIVATE QuicIOVector {
+  QuicIOVector(const struct iovec* iov, int iov_count, size_t total_length)
+      : iov(iov), iov_count(iov_count), total_length(total_length) {}
+
+  const struct iovec* iov;
+  const int iov_count;
+  const size_t total_length;
+};
+
 }  // namespace net
 
 #endif  // NET_QUIC_QUIC_PROTOCOL_H_
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
index 9f35ba0..e5a77a9 100644
--- a/net/quic/quic_session.cc
+++ b/net/quic/quic_session.cc
@@ -312,12 +312,12 @@
 
 QuicConsumedData QuicSession::WritevData(
     QuicStreamId id,
-    const IOVector& data,
+    const QuicIOVector& iov,
     QuicStreamOffset offset,
     bool fin,
     FecProtection fec_protection,
     QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
-  return connection_->SendStreamData(id, data, offset, fin, fec_protection,
+  return connection_->SendStreamData(id, iov, offset, fin, fec_protection,
                                      ack_notifier_delegate);
 }
 
diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h
index 5320784f..28d1fde 100644
--- a/net/quic/quic_session.h
+++ b/net/quic/quic_session.h
@@ -87,7 +87,7 @@
   // we have seen ACKs for all packets resulting from this call.
   virtual QuicConsumedData WritevData(
       QuicStreamId id,
-      const IOVector& data,
+      const QuicIOVector& iov,
       QuicStreamOffset offset,
       bool fin,
       FecProtection fec_protection,
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
index 90d67e7..26590c1 100644
--- a/net/quic/quic_session_test.cc
+++ b/net/quic/quic_session_test.cc
@@ -149,14 +149,14 @@
 
   QuicConsumedData WritevData(
       QuicStreamId id,
-      const IOVector& data,
+      const QuicIOVector& data,
       QuicStreamOffset offset,
       bool fin,
       FecProtection fec_protection,
       QuicAckNotifier::DelegateInterface* ack_notifier_delegate) override {
     // Always consumes everything.
     if (writev_consumes_all_data_) {
-      return QuicConsumedData(data.TotalBufferSize(), fin);
+      return QuicConsumedData(data.total_length, fin);
     } else {
       return QuicSession::WritevData(id, data, offset, fin, fec_protection,
                                      ack_notifier_delegate);
@@ -168,8 +168,9 @@
   }
 
   QuicConsumedData SendStreamData(QuicStreamId id) {
-    return WritevData(id, MakeIOVector("not empty"), 0, true, MAY_FEC_PROTECT,
-                      nullptr);
+    struct iovec iov;
+    return WritevData(id, MakeIOVector("not empty", &iov), 0, true,
+                      MAY_FEC_PROTECT, nullptr);
   }
 
   using QuicSession::PostProcessAfterData;
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index bdbbaf1..4602310 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -830,17 +830,8 @@
         AlternativeService(QUIC, session->server_id().host(), port));
   }
 
-  // We abandon the connection if packet loss rate is too bad.
-  session->CloseSessionOnErrorAndNotifyFactoryLater(ERR_ABORTED,
-                                                    QUIC_BAD_PACKET_LOSS_RATE);
-
-  if (IsQuicDisabled(port))
-    return true;  // Exit if Quic is already disabled for this port.
-
-  if (++number_of_lossy_connections_[port] >=
-      max_number_of_lossy_connections_) {
-    UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicStreamFactory.QuicIsDisabled", port);
-  }
+  bool was_quic_disabled = IsQuicDisabled(port);
+  ++number_of_lossy_connections_[port];
 
   // Collect data for port 443 for packet loss events.
   if (port == 443 && max_number_of_lossy_connections_ > 0) {
@@ -850,7 +841,18 @@
         std::min(number_of_lossy_connections_[port],
                  max_number_of_lossy_connections_));
   }
-  return true;
+
+  bool is_quic_disabled = IsQuicDisabled(port);
+  if (is_quic_disabled) {
+    // Close QUIC connection if Quic is disabled for this port.
+    session->CloseSessionOnErrorAndNotifyFactoryLater(
+        ERR_ABORTED, QUIC_BAD_PACKET_LOSS_RATE);
+
+    // If this bad packet loss rate disabled the QUIC, then record it.
+    if (!was_quic_disabled)
+      UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicStreamFactory.QuicIsDisabled", port);
+  }
+  return is_quic_disabled;
 }
 
 void QuicStreamFactory::OnIdleSession(QuicClientSession* session) {
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
index 25e03ea..3027b59 100644
--- a/net/quic/quic_stream_factory.h
+++ b/net/quic/quic_stream_factory.h
@@ -135,10 +135,13 @@
              const BoundNetLog& net_log,
              QuicStreamRequest* request);
 
-  // Returns false if |packet_loss_rate| is less than |packet_loss_threshold_|
-  // otherwise it returns true and closes the session and marks QUIC as recently
-  // broken for the port of the session. Increments
-  // |number_of_lossy_connections_| by port.
+  // If |packet_loss_rate| is greater than or equal to |packet_loss_threshold_|
+  // it marks QUIC as recently broken for the port of the session. Increments
+  // |number_of_lossy_connections_| by port. If |number_of_lossy_connections_|
+  // is greater than or equal to |max_number_of_lossy_connections_| then it
+  // disables QUIC. If QUIC is disabled then it closes the connection.
+  //
+  // Returns true if QUIC is disabled for the port of the session.
   bool OnHandshakeConfirmed(QuicClientSession* session, float packet_loss_rate);
 
   // Returns true if QUIC is disabled for this port.
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index d5bdfee..ae1e5d6 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -1584,16 +1584,21 @@
   socket_factory_.AddSocketDataProvider(&socket_data);
   socket_data.StopAfter(1);
 
-  DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0);
+  DeterministicSocketData socket_data2(nullptr, 0, nullptr, 0);
   socket_factory_.AddSocketDataProvider(&socket_data2);
   socket_data2.StopAfter(1);
 
-  DeterministicSocketData socket_data3(reads, arraysize(reads), nullptr, 0);
+  DeterministicSocketData socket_data3(nullptr, 0, nullptr, 0);
   socket_factory_.AddSocketDataProvider(&socket_data3);
   socket_data3.StopAfter(1);
 
+  DeterministicSocketData socket_data4(nullptr, 0, nullptr, 0);
+  socket_factory_.AddSocketDataProvider(&socket_data4);
+  socket_data4.StopAfter(1);
+
   HostPortPair server2("mail.example.org", kDefaultServerPort);
   HostPortPair server3("docs.example.org", kDefaultServerPort);
+  HostPortPair server4("images.example.org", kDefaultServerPort);
 
   crypto_client_stream_factory_.set_handshake_mode(
       MockCryptoClientStream::ZERO_RTT);
@@ -1602,6 +1607,7 @@
                                            "192.168.0.1", "");
   host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
   host_resolver_.rules()->AddIPLiteralRule(server3.host(), "192.168.0.1", "");
+  host_resolver_.rules()->AddIPLiteralRule(server4.host(), "192.168.0.1", "");
 
   QuicStreamRequest request(&factory_);
   EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_,
@@ -1625,16 +1631,16 @@
                    &factory_, host_port_pair_.port()));
 
   // Set packet_loss_rate to a higher value than packet_loss_threshold only once
-  // and that should close the session, but shouldn't disable QUIC.
-  EXPECT_TRUE(
+  // and that shouldn't close the session and it shouldn't disable QUIC.
+  EXPECT_FALSE(
       factory_.OnHandshakeConfirmed(session, /*packet_loss_rate=*/1.0f));
   EXPECT_EQ(1, QuicStreamFactoryPeer::GetNumberOfLossyConnections(
                    &factory_, host_port_pair_.port()));
+  EXPECT_TRUE(session->connection()->connected());
   EXPECT_FALSE(
       QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port()));
-  EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(
+  EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession(
       &factory_, host_port_pair_, is_https_));
-  EXPECT_FALSE(HasActiveSession(host_port_pair_));
 
   // Test N-in-a-row high packet loss connections.
 
@@ -1660,17 +1666,16 @@
       QuicStreamFactoryPeer::IsQuicDisabled(&factory_, server2.port()));
 
   // Set packet_loss_rate to a higher value than packet_loss_threshold only once
-  // and that should close the session, but shouldn't disable QUIC.
-  EXPECT_TRUE(
+  // and that shouldn't close the session and it shouldn't disable QUIC.
+  EXPECT_FALSE(
       factory_.OnHandshakeConfirmed(session2, /*packet_loss_rate=*/1.0f));
   EXPECT_EQ(1, QuicStreamFactoryPeer::GetNumberOfLossyConnections(
                    &factory_, server2.port()));
-  EXPECT_FALSE(session2->connection()->connected());
+  EXPECT_TRUE(session2->connection()->connected());
   EXPECT_FALSE(
       QuicStreamFactoryPeer::IsQuicDisabled(&factory_, server2.port()));
-  EXPECT_FALSE(
+  EXPECT_TRUE(
       QuicStreamFactoryPeer::HasActiveSession(&factory_, server2, is_https_));
-  EXPECT_FALSE(HasActiveSession(server2));
 
   DVLOG(1) << "Create 3rd session which also has packet loss";
 
@@ -1682,30 +1687,55 @@
   QuicClientSession* session3 =
       QuicStreamFactoryPeer::GetActiveSession(&factory_, server3, is_https_);
 
+  DVLOG(1) << "Create 4th session with packet loss and test IsQuicDisabled()";
+  TestCompletionCallback callback4;
+  QuicStreamRequest request4(&factory_);
+  EXPECT_EQ(OK,
+            request4.Request(server4, is_https_, privacy_mode_, server4.host(),
+                             "GET", net_log_, callback4.callback()));
+  QuicClientSession* session4 =
+      QuicStreamFactoryPeer::GetActiveSession(&factory_, server4, is_https_);
+
   // Set packet_loss_rate to higher value than packet_loss_threshold 2nd time in
   // a row and that should close the session and disable QUIC.
   EXPECT_TRUE(
       factory_.OnHandshakeConfirmed(session3, /*packet_loss_rate=*/1.0f));
   EXPECT_EQ(2, QuicStreamFactoryPeer::GetNumberOfLossyConnections(
                    &factory_, server3.port()));
-  EXPECT_FALSE(session2->connection()->connected());
+  EXPECT_FALSE(session3->connection()->connected());
   EXPECT_TRUE(QuicStreamFactoryPeer::IsQuicDisabled(&factory_, server3.port()));
   EXPECT_FALSE(
       QuicStreamFactoryPeer::HasActiveSession(&factory_, server3, is_https_));
   EXPECT_FALSE(HasActiveSession(server3));
 
+  // Set packet_loss_rate to higher value than packet_loss_threshold 3rd time in
+  // a row and IsQuicDisabled() should close the session.
+  EXPECT_TRUE(
+      factory_.OnHandshakeConfirmed(session4, /*packet_loss_rate=*/1.0f));
+  EXPECT_EQ(3, QuicStreamFactoryPeer::GetNumberOfLossyConnections(
+                   &factory_, server4.port()));
+  EXPECT_FALSE(session4->connection()->connected());
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsQuicDisabled(&factory_, server4.port()));
+  EXPECT_FALSE(
+      QuicStreamFactoryPeer::HasActiveSession(&factory_, server4, is_https_));
+  EXPECT_FALSE(HasActiveSession(server4));
+
   scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
   EXPECT_TRUE(stream.get());
   scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
   EXPECT_TRUE(stream2.get());
   scoped_ptr<QuicHttpStream> stream3 = request3.ReleaseStream();
   EXPECT_TRUE(stream3.get());
+  scoped_ptr<QuicHttpStream> stream4 = request4.ReleaseStream();
+  EXPECT_TRUE(stream4.get());
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
   EXPECT_TRUE(socket_data2.AllReadDataConsumed());
   EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
   EXPECT_TRUE(socket_data3.AllReadDataConsumed());
   EXPECT_TRUE(socket_data3.AllWriteDataConsumed());
+  EXPECT_TRUE(socket_data4.AllReadDataConsumed());
+  EXPECT_TRUE(socket_data4.AllWriteDataConsumed());
 }
 
 }  // namespace test
diff --git a/net/quic/quic_stream_sequencer.h b/net/quic/quic_stream_sequencer.h
index 0f27ec6..7794819 100644
--- a/net/quic/quic_stream_sequencer.h
+++ b/net/quic/quic_stream_sequencer.h
@@ -52,13 +52,6 @@
   // Returns true if the sequencer has delivered the fin.
   bool IsClosed() const;
 
-  // Returns true if the sequencer has received this frame before.
-  bool IsDuplicate(const QuicStreamFrame& frame) const;
-
-  // Returns true if |frame| contains data which overlaps buffered data
-  // (indicating an invalid stream frame has been received).
-  bool FrameOverlapsBufferedData(const QuicStreamFrame& frame) const;
-
   // Calls |ProcessRawData| on |stream_| for each buffered frame that may
   // be processed.
   void FlushBufferedFrames();
@@ -80,6 +73,13 @@
  private:
   friend class test::QuicStreamSequencerPeer;
 
+  // Returns true if |frame| contains data which overlaps buffered data
+  // (indicating an invalid stream frame has been received).
+  bool FrameOverlapsBufferedData(const QuicStreamFrame& frame) const;
+
+  // Returns true if the sequencer has received this frame before.
+  bool IsDuplicate(const QuicStreamFrame& frame) const;
+
   // Wait until we've seen 'offset' bytes, and then terminate the stream.
   void CloseStreamAtOffset(QuicStreamOffset offset);
 
diff --git a/net/quic/quic_stream_sequencer_test.cc b/net/quic/quic_stream_sequencer_test.cc
index 7ff719e..856e3ad0 100644
--- a/net/quic/quic_stream_sequencer_test.cc
+++ b/net/quic/quic_stream_sequencer_test.cc
@@ -64,9 +64,7 @@
       : connection_(new MockConnection(Perspective::IS_CLIENT)),
         session_(connection_),
         stream_(&session_, 1),
-        sequencer_(new QuicStreamSequencer(&stream_)),
-        buffered_frames_(
-            QuicStreamSequencerPeer::GetBufferedFrames(sequencer_.get())) {}
+        sequencer_(new QuicStreamSequencer(&stream_)) {}
 
   bool VerifyIovec(const iovec& iovec, StringPiece expected) {
     if (iovec.iov_len != expected.length()) {
@@ -100,42 +98,50 @@
     sequencer_->OnStreamFrame(frame);
   }
 
+  size_t NumBufferedFrames() {
+    return QuicStreamSequencerPeer::GetNumBufferedFrames(sequencer_.get());
+  }
+
+  bool FrameOverlapsBufferedData(const QuicStreamFrame& frame) {
+    return QuicStreamSequencerPeer::FrameOverlapsBufferedData(sequencer_.get(),
+                                                              frame);
+  }
+
   MockConnection* connection_;
   MockQuicSpdySession session_;
   testing::StrictMock<MockStream> stream_;
   scoped_ptr<QuicStreamSequencer> sequencer_;
-  map<QuicStreamOffset, string>* buffered_frames_;
 };
 
 TEST_F(QuicStreamSequencerTest, RejectOldFrame) {
   EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
 
   OnFrame(0, "abc");
-  EXPECT_EQ(0u, buffered_frames_->size());
+  EXPECT_EQ(0u, NumBufferedFrames());
   EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
   // Ignore this - it matches a past sequence number and we should not see it
   // again.
   OnFrame(0, "def");
-  EXPECT_EQ(0u, buffered_frames_->size());
+  EXPECT_EQ(0u, NumBufferedFrames());
 }
 
 TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) {
   EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3));
 
   OnFrame(0, "abc");
-  EXPECT_EQ(1u, buffered_frames_->size());
+  EXPECT_EQ(1u, NumBufferedFrames());
   EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
   // Ignore this - it matches a buffered frame.
   // Right now there's no checking that the payload is consistent.
   OnFrame(0, "def");
-  EXPECT_EQ(1u, buffered_frames_->size());
+  EXPECT_EQ(1u, NumBufferedFrames());
 }
 
 TEST_F(QuicStreamSequencerTest, FullFrameConsumed) {
   EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
 
   OnFrame(0, "abc");
-  EXPECT_EQ(0u, buffered_frames_->size());
+  EXPECT_EQ(0u, NumBufferedFrames());
   EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
 }
 
@@ -143,12 +149,12 @@
   sequencer_->SetBlockedUntilFlush();
 
   OnFrame(0, "abc");
-  EXPECT_EQ(1u, buffered_frames_->size());
+  EXPECT_EQ(1u, NumBufferedFrames());
   EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
 
   EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
   sequencer_->FlushBufferedFrames();
-  EXPECT_EQ(0u, buffered_frames_->size());
+  EXPECT_EQ(0u, NumBufferedFrames());
   EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
 
   EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3));
@@ -160,13 +166,13 @@
   sequencer_->SetBlockedUntilFlush();
 
   OnFinFrame(0, "abc");
-  EXPECT_EQ(1u, buffered_frames_->size());
+  EXPECT_EQ(1u, NumBufferedFrames());
   EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
 
   EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
   EXPECT_CALL(stream_, OnFinRead());
   sequencer_->FlushBufferedFrames();
-  EXPECT_EQ(0u, buffered_frames_->size());
+  EXPECT_EQ(0u, NumBufferedFrames());
   EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
 }
 
@@ -174,14 +180,14 @@
   EXPECT_CALL(stream_,
               CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _));
   OnFrame(0, "");
-  EXPECT_EQ(0u, buffered_frames_->size());
+  EXPECT_EQ(0u, NumBufferedFrames());
   EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
 }
 
 TEST_F(QuicStreamSequencerTest, EmptyFinFrame) {
   EXPECT_CALL(stream_, OnFinRead());
   OnFinFrame(0, "");
-  EXPECT_EQ(0u, buffered_frames_->size());
+  EXPECT_EQ(0u, NumBufferedFrames());
   EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
 }
 
@@ -189,38 +195,35 @@
   EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(2));
 
   OnFrame(0, "abc");
-  EXPECT_EQ(1u, buffered_frames_->size());
+  EXPECT_EQ(1u, NumBufferedFrames());
   EXPECT_EQ(2u, sequencer_->num_bytes_consumed());
-  EXPECT_EQ("c", buffered_frames_->find(2)->second);
 }
 
 TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) {
   EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
 
   OnFrame(0, "abc");
-  EXPECT_EQ(1u, buffered_frames_->size());
+  EXPECT_EQ(1u, NumBufferedFrames());
   EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
-  EXPECT_EQ("abc", buffered_frames_->find(0)->second);
   EXPECT_EQ(0, sequencer_->num_early_frames_received());
 }
 
 TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) {
   OnFrame(3, "abc");
-  EXPECT_EQ(1u, buffered_frames_->size());
+  EXPECT_EQ(1u, NumBufferedFrames());
   EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
-  EXPECT_EQ("abc", buffered_frames_->find(3)->second);
   EXPECT_EQ(1, sequencer_->num_early_frames_received());
 }
 
 TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) {
   // Buffer the first
   OnFrame(6, "ghi");
-  EXPECT_EQ(1u, buffered_frames_->size());
+  EXPECT_EQ(1u, NumBufferedFrames());
   EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
   EXPECT_EQ(3u, sequencer_->num_bytes_buffered());
   // Buffer the second
   OnFrame(3, "def");
-  EXPECT_EQ(2u, buffered_frames_->size());
+  EXPECT_EQ(2u, NumBufferedFrames());
   EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
   EXPECT_EQ(6u, sequencer_->num_bytes_buffered());
 
@@ -234,7 +237,7 @@
   EXPECT_EQ(9u, sequencer_->num_bytes_consumed());
   EXPECT_EQ(0u, sequencer_->num_bytes_buffered());
 
-  EXPECT_EQ(0u, buffered_frames_->size());
+  EXPECT_EQ(0u, NumBufferedFrames());
 }
 
 TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) {
@@ -361,47 +364,40 @@
 TEST_F(QuicStreamSequencerTest, FrameOverlapsBufferedData) {
   // Ensure that FrameOverlapsBufferedData returns appropriate responses when
   // there is existing data buffered.
-
-  map<QuicStreamOffset, string>* buffered_frames =
-      QuicStreamSequencerPeer::GetBufferedFrames(sequencer_.get());
-
   const int kBufferedOffset = 10;
   const int kBufferedDataLength = 3;
   const int kNewDataLength = 3;
-  StringPiece data(string(kNewDataLength, '.'));
+  string data(kNewDataLength, '.');
 
   // No overlap if no buffered frames.
-  EXPECT_TRUE(buffered_frames_->empty());
-  EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(
-      QuicStreamFrame(1, false, kBufferedOffset - 1, data)));
-
+  EXPECT_EQ(0u, NumBufferedFrames());
   // Add a buffered frame.
-  buffered_frames->insert(
-      std::make_pair(kBufferedOffset, string(kBufferedDataLength, '.')));
+  sequencer_->OnStreamFrame(QuicStreamFrame(1, false, kBufferedOffset,
+                                            string(kBufferedDataLength, '.')));
 
   // New byte range partially overlaps with buffered frame, start offset
-  // preceeding buffered frame.
-  EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+  // preceding buffered frame.
+  EXPECT_TRUE(FrameOverlapsBufferedData(
       QuicStreamFrame(1, false, kBufferedOffset - 1, data)));
-  EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+  EXPECT_TRUE(FrameOverlapsBufferedData(
       QuicStreamFrame(1, false, kBufferedOffset - kNewDataLength + 1, data)));
 
   // New byte range partially overlaps with buffered frame, start offset
   // inside existing buffered frame.
-  EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+  EXPECT_TRUE(FrameOverlapsBufferedData(
       QuicStreamFrame(1, false, kBufferedOffset + 1, data)));
-  EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(QuicStreamFrame(
+  EXPECT_TRUE(FrameOverlapsBufferedData(QuicStreamFrame(
       1, false, kBufferedOffset + kBufferedDataLength - 1, data)));
 
   // New byte range entirely outside of buffered frames, start offset preceeding
   // buffered frame.
-  EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(
+  EXPECT_FALSE(FrameOverlapsBufferedData(
       QuicStreamFrame(1, false, kBufferedOffset - kNewDataLength, data)));
 
   // New byte range entirely outside of buffered frames, start offset later than
   // buffered frame.
-  EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(QuicStreamFrame(
-      1, false, kBufferedOffset + kBufferedDataLength, data)));
+  EXPECT_FALSE(FrameOverlapsBufferedData(
+      QuicStreamFrame(1, false, kBufferedOffset + kBufferedDataLength, data)));
 }
 
 TEST_F(QuicStreamSequencerTest, DontAcceptOverlappingFrames) {
@@ -412,7 +408,7 @@
   sequencer_->OnStreamFrame(frame1);
 
   QuicStreamFrame frame2(kClientDataStreamId1, false, 2, StringPiece("hello"));
-  EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(frame2));
+  EXPECT_TRUE(FrameOverlapsBufferedData(frame2));
   EXPECT_CALL(stream_, CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _))
       .Times(1);
   sequencer_->OnStreamFrame(frame2);
diff --git a/net/quic/quic_utils.h b/net/quic/quic_utils.h
index 46652f2..fb379e6 100644
--- a/net/quic/quic_utils.h
+++ b/net/quic/quic_utils.h
@@ -9,6 +9,7 @@
 
 #include <string>
 
+#include "base/strings/string_piece.h"
 #include "net/base/int128.h"
 #include "net/base/net_export.h"
 #include "net/quic/quic_protocol.h"
@@ -100,11 +101,13 @@
   DISALLOW_COPY_AND_ASSIGN(QuicUtils);
 };
 
-// Utility function that returns an IOVector object wrapped around |str|.
-inline IOVector MakeIOVector(base::StringPiece str) {
-  IOVector iov;
-  iov.Append(const_cast<char*>(str.data()), str.size());
-  return iov;
+// Utility function that returns an QuicIOVector object wrapped around |str|.
+// |str|'s data is stored in |iov|.
+inline QuicIOVector MakeIOVector(base::StringPiece str, struct iovec* iov) {
+  iov->iov_base = const_cast<char*>(str.data());
+  iov->iov_len = static_cast<size_t>(str.size());
+  QuicIOVector quic_iov(iov, 1, str.size());
+  return quic_iov;
 }
 
 }  // namespace net
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
index 0d190908..caf707f 100644
--- a/net/quic/reliable_quic_stream.cc
+++ b/net/quic/reliable_quic_stream.cc
@@ -371,13 +371,9 @@
     write_length = static_cast<size_t>(send_window);
   }
 
-  // Fill an IOVector with bytes from the iovec.
-  IOVector data;
-  data.AppendIovecAtMostBytes(iov, iov_count, write_length);
-
   QuicConsumedData consumed_data = session()->WritevData(
-      id(), data, stream_bytes_written_, fin, GetFecProtection(),
-      ack_notifier_delegate);
+      id(), QuicIOVector(iov, iov_count, write_length), stream_bytes_written_,
+      fin, GetFecProtection(), ack_notifier_delegate);
   stream_bytes_written_ += consumed_data.bytes_consumed;
 
   AddBytesSent(consumed_data.bytes_consumed);
diff --git a/net/quic/spdy_utils.cc b/net/quic/spdy_utils.cc
index 2db7eb0b..c03d588 100644
--- a/net/quic/spdy_utils.cc
+++ b/net/quic/spdy_utils.cc
@@ -17,7 +17,7 @@
 SpdyMajorVersion SpdyUtils::GetSpdyVersionForQuicVersion(
     QuicVersion quic_version) {
   if (quic_version > QUIC_VERSION_24) {
-    return SPDY4;
+    return HTTP2;
   }
   return SPDY3;
 }
diff --git a/net/quic/test_tools/quic_ack_notifier_manager_peer.cc b/net/quic/test_tools/quic_ack_notifier_manager_peer.cc
new file mode 100644
index 0000000..3e7f90cb
--- /dev/null
+++ b/net/quic/test_tools/quic_ack_notifier_manager_peer.cc
@@ -0,0 +1,18 @@
+// 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 "net/quic/test_tools/quic_ack_notifier_manager_peer.h"
+
+#include "net/quic/quic_ack_notifier_manager.h"
+
+namespace net {
+namespace test {
+
+size_t AckNotifierManagerPeer::GetNumberOfRegisteredPackets(
+    const AckNotifierManager* manager) {
+  return manager->ack_notifier_map_.size();
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/quic/test_tools/quic_ack_notifier_manager_peer.h b/net/quic/test_tools/quic_ack_notifier_manager_peer.h
new file mode 100644
index 0000000..ea128d3a
--- /dev/null
+++ b/net/quic/test_tools/quic_ack_notifier_manager_peer.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_ACK_NOTIFIER_MANAGER_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_ACK_NOTIFIER_MANAGER_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class AckNotifierManager;
+
+namespace test {
+
+// Exposes the internal fields of AckNotifierManager for tests.
+class AckNotifierManagerPeer {
+ public:
+  // Returns total number of packets known to the map.
+  static size_t GetNumberOfRegisteredPackets(const AckNotifierManager* manager);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AckNotifierManagerPeer);
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_QUIC_TEST_TOOLS_QUIC_ACK_NOTIFIER_MANAGER_PEER_H_
diff --git a/net/quic/test_tools/quic_packet_generator_peer.cc b/net/quic/test_tools/quic_packet_generator_peer.cc
index 5f5e231..c6ef402 100644
--- a/net/quic/test_tools/quic_packet_generator_peer.cc
+++ b/net/quic/test_tools/quic_packet_generator_peer.cc
@@ -22,17 +22,5 @@
   return generator->fec_timeout_;
 }
 
-// static
-FecSendPolicy QuicPacketGeneratorPeer::GetFecSendPolicy(
-    QuicPacketGenerator* generator) {
-  return generator->fec_send_policy_;
-}
-
-// static
-void QuicPacketGeneratorPeer::SetFecSendPolicy(QuicPacketGenerator* generator,
-                                               FecSendPolicy fec_send_policy) {
-  generator->fec_send_policy_ = fec_send_policy;
-}
-
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/test_tools/quic_packet_generator_peer.h b/net/quic/test_tools/quic_packet_generator_peer.h
index 9e2078d5..fba8dbee 100644
--- a/net/quic/test_tools/quic_packet_generator_peer.h
+++ b/net/quic/test_tools/quic_packet_generator_peer.h
@@ -18,9 +18,6 @@
  public:
   static QuicPacketCreator* GetPacketCreator(QuicPacketGenerator* generator);
   static QuicTime::Delta GetFecTimeout(QuicPacketGenerator* generator);
-  static FecSendPolicy GetFecSendPolicy(QuicPacketGenerator* generator);
-  static void SetFecSendPolicy(QuicPacketGenerator* generator,
-                               FecSendPolicy fec_send_policy);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(QuicPacketGeneratorPeer);
diff --git a/net/quic/test_tools/quic_stream_sequencer_peer.cc b/net/quic/test_tools/quic_stream_sequencer_peer.cc
index 08a5c5b0..43db645e 100644
--- a/net/quic/test_tools/quic_stream_sequencer_peer.cc
+++ b/net/quic/test_tools/quic_stream_sequencer_peer.cc
@@ -13,9 +13,16 @@
 namespace test {
 
 // static
-map<QuicStreamOffset, string>* QuicStreamSequencerPeer::GetBufferedFrames(
+size_t QuicStreamSequencerPeer::GetNumBufferedFrames(
     QuicStreamSequencer* sequencer) {
-  return &(sequencer->buffered_frames_);
+  return sequencer->buffered_frames_.size();
+}
+
+// static
+bool QuicStreamSequencerPeer::FrameOverlapsBufferedData(
+    QuicStreamSequencer* sequencer,
+    const QuicStreamFrame& frame) {
+  return sequencer->FrameOverlapsBufferedData(frame);
 }
 
 // static
diff --git a/net/quic/test_tools/quic_stream_sequencer_peer.h b/net/quic/test_tools/quic_stream_sequencer_peer.h
index 7b2b28a..22e3a990 100644
--- a/net/quic/test_tools/quic_stream_sequencer_peer.h
+++ b/net/quic/test_tools/quic_stream_sequencer_peer.h
@@ -16,8 +16,10 @@
 
 class QuicStreamSequencerPeer {
  public:
-  static std::map<QuicStreamOffset, std::string>* GetBufferedFrames(
-      QuicStreamSequencer* sequencer);
+  static size_t GetNumBufferedFrames(QuicStreamSequencer* sequencer);
+
+  static bool FrameOverlapsBufferedData(QuicStreamSequencer* sequencer,
+                                        const QuicStreamFrame& frame);
 
   static QuicStreamOffset GetCloseOffset(QuicStreamSequencer* sequencer);
 
diff --git a/net/quic/test_tools/quic_test_packet_maker.cc b/net/quic/test_tools/quic_test_packet_maker.cc
index 307b4fa..63c9ac67 100644
--- a/net/quic/test_tools/quic_test_packet_maker.cc
+++ b/net/quic/test_tools/quic_test_packet_maker.cc
@@ -24,8 +24,8 @@
       connection_id_(connection_id),
       clock_(clock),
       host_(host),
-      spdy_request_framer_(SPDY4),
-      spdy_response_framer_(SPDY4) {
+      spdy_request_framer_(HTTP2),
+      spdy_response_framer_(HTTP2) {
 }
 
 QuicTestPacketMaker::~QuicTestPacketMaker() {
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index c38be57..db45d15 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -686,9 +686,9 @@
       QuicPacketCreator::StreamFramePacketOverhead(
           PACKET_8BYTE_CONNECTION_ID, include_version,
           sequence_number_length, 0u, is_in_fec_group);
-  const size_t ack_length = NullEncrypter().GetCiphertextSize(
-      QuicFramer::GetMinAckFrameSize(
-          sequence_number_length, PACKET_1BYTE_SEQUENCE_NUMBER)) +
+  const size_t ack_length =
+      NullEncrypter().GetCiphertextSize(
+          QuicFramer::GetMinAckFrameSize(PACKET_1BYTE_SEQUENCE_NUMBER)) +
       GetPacketHeaderSize(connection_id_length, include_version,
                           sequence_number_length, is_in_fec_group);
   if (stream_length < ack_length) {
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index 04143a6a..1ab3792 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -434,7 +434,7 @@
   MOCK_METHOD0(CreateOutgoingDynamicStream, QuicDataStream*());
   MOCK_METHOD6(WritevData,
                QuicConsumedData(QuicStreamId id,
-                                const IOVector& data,
+                                const QuicIOVector& data,
                                 QuicStreamOffset offset,
                                 bool fin,
                                 FecProtection fec_protection,
diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc
index 4a8a46dd..40882afc 100644
--- a/net/spdy/buffered_spdy_framer.cc
+++ b/net/spdy/buffered_spdy_framer.cc
@@ -17,7 +17,7 @@
       return SPDY3;
     case kProtoSPDY4_14:
     case kProtoSPDY4:
-      return SPDY4;
+      return HTTP2;
     case kProtoUnknown:
     case kProtoHTTP11:
     case kProtoQUIC1SPDY3:
diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc
index 9a967d2..c5dc50c 100644
--- a/net/spdy/buffered_spdy_framer_unittest.cc
+++ b/net/spdy/buffered_spdy_framer_unittest.cc
@@ -281,7 +281,7 @@
   EXPECT_EQ(0, visitor.error_count_);
   EXPECT_EQ(0, visitor.syn_frame_count_);
   EXPECT_EQ(0, visitor.push_promise_frame_count_);
-  if (spdy_version() < SPDY4) {
+  if (spdy_version() < HTTP2) {
     EXPECT_EQ(1, visitor.syn_reply_frame_count_);
     EXPECT_EQ(0, visitor.headers_frame_count_);
   } else {
@@ -316,7 +316,7 @@
 }
 
 TEST_P(BufferedSpdyFramerTest, ReadPushPromiseHeaderBlock) {
-  if (spdy_version() < SPDY4)
+  if (spdy_version() < HTTP2)
     return;
   SpdyHeaderBlock headers;
   headers["alpha"] = "beta";
diff --git a/net/spdy/spdy_frame_builder.cc b/net/spdy/spdy_frame_builder.cc
index 1e16abd..f3922c6 100644
--- a/net/spdy/spdy_frame_builder.cc
+++ b/net/spdy/spdy_frame_builder.cc
@@ -168,7 +168,7 @@
 
 bool SpdyFrameBuilder::OverwriteLength(const SpdyFramer& framer,
                                        size_t length) {
-  if (version_ < SPDY4) {
+  if (version_ < HTTP2) {
     DCHECK_LE(length,
               SpdyConstants::GetFrameMaximumSize(version_) -
                   framer.GetFrameMinimumSize());
@@ -178,7 +178,7 @@
   bool success = false;
   const size_t old_length = length_;
 
-  if (version_ < SPDY4) {
+  if (version_ < HTTP2) {
     FlagsAndLength flags_length = CreateFlagsAndLength(
         0,  // We're not writing over the flags value anyway.
         length);
diff --git a/net/spdy/spdy_frame_builder_test.cc b/net/spdy/spdy_frame_builder_test.cc
index 38545586..e352303 100644
--- a/net/spdy/spdy_frame_builder_test.cc
+++ b/net/spdy/spdy_frame_builder_test.cc
@@ -18,10 +18,11 @@
   SpdyMajorVersion spdy_version_;
 };
 
-// All tests are run with two different SPDY versions: SPDY/2 and SPDY/3.
+// All tests are run with three different SPDY versions: SPDY/2, SPDY/3 and
+// HTTP/2.
 INSTANTIATE_TEST_CASE_P(SpdyFrameBuilderTests,
                         SpdyFrameBuilderTest,
-                        ::testing::Values(SPDY2, SPDY3, SPDY4));
+                        ::testing::Values(SPDY2, SPDY3, HTTP2));
 
 TEST_P(SpdyFrameBuilderTest, GetWritableBuffer) {
   const size_t builder_size = 10;
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
index 6348df9b..7aee1ece 100644
--- a/net/spdy/spdy_framer.cc
+++ b/net/spdy/spdy_framer.cc
@@ -990,7 +990,7 @@
         }
         if (current_frame_length_ < min_size) {
           // TODO(mlavan): check here for HEADERS with no payload?
-          // (not allowed in SPDY4)
+          // (not allowed in HTTP2)
           set_error(SPDY_INVALID_CONTROL_FRAME);
         } else if (protocol_version() <= SPDY3 &&
                    current_frame_flags_ & ~CONTROL_FLAG_FIN) {
@@ -2123,7 +2123,7 @@
     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) {
-      DCHECK_LE(SPDY4, protocol_version());
+      DCHECK_LE(HTTP2, protocol_version());
       visitor_->OnStreamPadding(current_frame_stream_id_, amount_to_discard);
     }
     data += amount_to_discard;
@@ -2420,7 +2420,7 @@
   builder.WriteUInt32(SpdyConstants::SerializeRstStreamStatus(
       protocol_version(), rst_stream.status()));
 
-  // In SPDY4 and up, RST_STREAM frames may also specify opaque data.
+  // In HTTP2 and up, RST_STREAM frames may also specify opaque data.
   if (protocol_version() > SPDY3 && rst_stream.description().size() > 0) {
     builder.WriteBytes(rst_stream.description().data(),
                        rst_stream.description().size());
@@ -2535,7 +2535,7 @@
                                                              goaway.status()));
   }
 
-  // In SPDY4 and up, GOAWAY frames may also specify opaque data.
+  // In HTTP2 and up, GOAWAY frames may also specify opaque data.
   if ((protocol_version() > SPDY3) && (goaway.description().size() > 0)) {
     builder.WriteBytes(goaway.description().data(),
                        goaway.description().size());
@@ -2635,7 +2635,7 @@
   }
 
   if (debug_visitor_) {
-    // SPDY4 uses HPACK for header compression. However, continue to
+    // HTTP2 uses HPACK for header compression. However, continue to
     // use GetSerializedLength() for an apples-to-apples comparision of
     // compression performance between HPACK and SPDY w/ deflate.
     const size_t payload_len =
@@ -2729,7 +2729,7 @@
                                padding_payload_len);
 
   if (debug_visitor_) {
-    // SPDY4 uses HPACK for header compression. However, continue to
+    // HTTP2 uses HPACK for header compression. However, continue to
     // use GetSerializedLength() for an apples-to-apples comparision of
     // compression performance between HPACK and SPDY w/ deflate.
     const size_t payload_len =
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
index a60d0831..b6e4d99 100644
--- a/net/spdy/spdy_framer_test.cc
+++ b/net/spdy/spdy_framer_test.cc
@@ -673,17 +673,17 @@
 
   bool IsSpdy2() { return spdy_version_ == SPDY2; }
   bool IsSpdy3() { return spdy_version_ == SPDY3; }
-  bool IsSpdy4() { return spdy_version_ == SPDY4; }
+  bool IsHttp2() { return spdy_version_ == HTTP2; }
 
   // Version of SPDY protocol to be used.
   SpdyMajorVersion spdy_version_;
   unsigned char spdy_version_ch_;
 };
 
-// All tests are run with 3 different SPDY versions: SPDY/2, SPDY/3, SPDY/4.
+// All tests are run with 3 different SPDY versions: SPDY/2, SPDY/3, HTTP/2.
 INSTANTIATE_TEST_CASE_P(SpdyFramerTests,
                         SpdyFramerTest,
-                        ::testing::Values(SPDY2, SPDY3, SPDY4));
+                        ::testing::Values(SPDY2, SPDY3, HTTP2));
 
 // Test that we ignore cookie where both name and value are empty.
 TEST_P(SpdyFramerTest, HeaderBlockWithEmptyCookie) {
@@ -1170,9 +1170,9 @@
     0x00, 0x00, 0x00, 0x05,           // RST_STREAM_CANCEL
   };
 
-  // SYN_STREAM doesn't exist in SPDY4, so instead we send
+  // SYN_STREAM doesn't exist in HTTP/2, so instead we send
   // HEADERS frames with PRIORITY and END_HEADERS set.
-  const unsigned char kV4Input[] = {
+  const unsigned char kH2Input[] = {
     0x00, 0x00, 0x05, 0x01,           // HEADERS: PRIORITY | END_HEADERS
     0x24, 0x00, 0x00, 0x00,
     0x01, 0x00, 0x00, 0x00,           // Stream 1, Priority 0
@@ -1228,7 +1228,7 @@
   } else if (IsSpdy3()) {
     visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
   } else {
-    visitor.SimulateInFramer(kV4Input, sizeof(kV4Input));
+    visitor.SimulateInFramer(kH2Input, sizeof(kH2Input));
   }
 
   EXPECT_EQ(0, visitor.syn_reply_frame_count_);
@@ -1236,7 +1236,7 @@
   EXPECT_EQ(0, visitor.error_count_);
   EXPECT_EQ(2, visitor.fin_frame_count_);
 
-  if (IsSpdy4()) {
+  if (IsHttp2()) {
     EXPECT_EQ(3, visitor.headers_frame_count_);
     EXPECT_EQ(0, visitor.syn_frame_count_);
     base::StringPiece reset_stream = "RESETSTREAM";
@@ -1311,9 +1311,9 @@
     0xde, 0xad, 0xbe, 0xef,
   };
 
-  // SYN_STREAM and SYN_REPLY don't exist in SPDY4, so instead we send
+  // SYN_STREAM and SYN_REPLY don't exist in HTTP/2, so instead we send
   // HEADERS frames with PRIORITY(SYN_STREAM only) and END_HEADERS set.
-  const unsigned char kV4Input[] = {
+  const unsigned char kH2Input[] = {
     0x00, 0x00, 0x05, 0x01,           // HEADERS: PRIORITY | END_HEADERS
     0x24, 0x00, 0x00, 0x00,           // Stream 1
     0x01, 0x00, 0x00, 0x00,           // Priority 0
@@ -1342,11 +1342,11 @@
   } else if (IsSpdy3()) {
     visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
   } else {
-    visitor.SimulateInFramer(kV4Input, sizeof(kV4Input));
+    visitor.SimulateInFramer(kH2Input, sizeof(kH2Input));
   }
 
   EXPECT_EQ(0, visitor.error_count_);
-  if (IsSpdy4()) {
+  if (IsHttp2()) {
     EXPECT_EQ(0, visitor.syn_frame_count_);
     EXPECT_EQ(0, visitor.syn_reply_frame_count_);
     EXPECT_EQ(2, visitor.headers_frame_count_);
@@ -1403,9 +1403,9 @@
     'b',
   };
 
-  // SYN_STREAM and SYN_REPLY don't exist in SPDY4, so instead we send
+  // SYN_STREAM and SYN_REPLY don't exist in HTTP/2, so instead we send
   // HEADERS frames with PRIORITY(SYN_STREAM only) and END_HEADERS set.
-  const unsigned char kV4Input[] = {
+  const unsigned char kH2Input[] = {
     0x00, 0x00, 0x05, 0x01,           // HEADERS: PRIORITY | END_HEADERS
     0x24, 0x00, 0x00, 0x00,
     0x01, 0x00, 0x00, 0x00,           // Stream 1, Priority 0
@@ -1422,11 +1422,11 @@
   } else if (IsSpdy3()) {
     visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
   } else {
-    visitor.SimulateInFramer(kV4Input, sizeof(kV4Input));
+    visitor.SimulateInFramer(kH2Input, sizeof(kH2Input));
   }
 
   EXPECT_EQ(0, visitor.error_count_);
-  if (IsSpdy4()) {
+  if (IsHttp2()) {
     EXPECT_EQ(0, visitor.syn_frame_count_);
     EXPECT_EQ(0, visitor.syn_reply_frame_count_);
     EXPECT_EQ(2, visitor.headers_frame_count_);
@@ -1571,15 +1571,15 @@
     0x00, 0x00, 0x00, 0x01,
     0x12, 0x34, 0x56, 0x78
   };
-  const unsigned char kV4FrameData[] = {
+  const unsigned char kH2FrameData[] = {
     0x00, 0x00, 0x04, 0x08,
     0x00, 0x00, 0x00, 0x00,
     0x01, 0x12, 0x34, 0x56,
     0x78
   };
 
-  if (IsSpdy4()) {
-    CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+  if (IsHttp2()) {
+    CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
   } else {
     CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
   }
@@ -1596,19 +1596,26 @@
       'h',  'e',  'l',  'l',
       'o'
     };
-    const unsigned char kV4FrameData[] = {
-      0x00, 0x00, 0x05, 0x00,
-      0x00, 0x00, 0x00, 0x00,
-      0x01, 'h',  'e',  'l',
-      'l',  'o'
-    };
+    const unsigned char kH2FrameData[] = {0x00,
+                                          0x00,
+                                          0x05,
+                                          0x00,
+                                          0x00,
+                                          0x00,
+                                          0x00,
+                                          0x00,
+                                          0x01,
+                                          'h',
+                                          'e',
+                                          'l',
+                                          'l',
+                                          'o'};
     const char bytes[] = "hello";
 
     SpdyDataIR data_ir(1, bytes);
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
-    if (IsSpdy4()) {
-       CompareFrame(
-           kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
        CompareFrame(
            kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
@@ -1619,10 +1626,9 @@
     frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(
         data_header_ir));
     CompareCharArraysWithHexError(
-        kDescription,
-        reinterpret_cast<const unsigned char*>(frame->data()),
+        kDescription, reinterpret_cast<const unsigned char*>(frame->data()),
         framer.GetDataFrameMinimumSize(),
-        IsSpdy4() ? kV4FrameData : kV3FrameData,
+        IsHttp2() ? kH2FrameData : kV3FrameData,
         framer.GetDataFrameMinimumSize());
   }
 
@@ -1635,7 +1641,7 @@
       'o'
     };
 
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0xfd, 0x00,      // Length = 253.  PADDED set.
       0x08, 0x00, 0x00, 0x00,
       0x01, 0xf7,                  // Pad length field.
@@ -1671,9 +1677,8 @@
     // bytes.
     data_ir.set_padding_len(248);
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
-    if (IsSpdy4()) {
-       CompareFrame(
-           kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
        CompareFrame(
            kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
@@ -1681,10 +1686,9 @@
 
     frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir));
     CompareCharArraysWithHexError(
-        kDescription,
-        reinterpret_cast<const unsigned char*>(frame->data()),
+        kDescription, reinterpret_cast<const unsigned char*>(frame->data()),
         framer.GetDataFrameMinimumSize(),
-        IsSpdy4() ? kV4FrameData : kV3FrameData,
+        IsHttp2() ? kH2FrameData : kV3FrameData,
         framer.GetDataFrameMinimumSize());
   }
 
@@ -1697,7 +1701,7 @@
       'o'
     };
 
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x0d, 0x00,      // Length = 13.  PADDED set.
       0x08, 0x00, 0x00, 0x00,
       0x01, 0x07,                  // Pad length field.
@@ -1712,9 +1716,8 @@
     // 7 zeros and the pad length field make the overall padding to be 8 bytes.
     data_ir.set_padding_len(8);
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
-    if (IsSpdy4()) {
-       CompareFrame(
-           kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
        CompareFrame(
            kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
@@ -1731,7 +1734,7 @@
       'o'
     };
 
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x06, 0x00,      // Length = 6.  PADDED set.
       0x08, 0x00, 0x00, 0x00,
       0x01, 0x00,                  // Pad length field.
@@ -1745,9 +1748,8 @@
     // payload is needed.
     data_ir.set_padding_len(1);
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
-    if (IsSpdy4()) {
-       CompareFrame(
-           kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
        CompareFrame(
            kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
@@ -1755,10 +1757,9 @@
 
     frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir));
     CompareCharArraysWithHexError(
-        kDescription,
-        reinterpret_cast<const unsigned char*>(frame->data()),
+        kDescription, reinterpret_cast<const unsigned char*>(frame->data()),
         framer.GetDataFrameMinimumSize(),
-        IsSpdy4() ? kV4FrameData : kV3FrameData,
+        IsHttp2() ? kH2FrameData : kV3FrameData,
         framer.GetDataFrameMinimumSize());
   }
 
@@ -1769,16 +1770,12 @@
       0x00, 0x00, 0x00, 0x01,
       0xff
     };
-    const unsigned char kV4FrameData[] = {
-      0x00, 0x00, 0x01, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x01,
-      0xff
-    };
+    const unsigned char kH2FrameData[] = {
+        0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff};
     SpdyDataIR data_ir(1, "\xff");
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
-    if (IsSpdy4()) {
-       CompareFrame(
-           kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
        CompareFrame(
            kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
@@ -1793,7 +1790,7 @@
       'h', 'e', 'l', 'l',
       'o'
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x05, 0x00,
       0x01, 0x00, 0x00, 0x00,
       0x01, 'h',  'e',  'l',
@@ -1802,9 +1799,8 @@
     SpdyDataIR data_ir(1, "hello");
     data_ir.set_fin(true);
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
-    if (IsSpdy4()) {
-       CompareFrame(
-           kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
        CompareFrame(
            kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
@@ -1817,16 +1813,15 @@
       0x00, 0x00, 0x00, 0x01,
       0x00, 0x00, 0x00, 0x00,
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00,
       0x01,
     };
     SpdyDataIR data_ir(1, "");
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
-    if (IsSpdy4()) {
-       CompareFrame(
-           kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
        CompareFrame(
            kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
@@ -1834,10 +1829,9 @@
 
     frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir));
     CompareCharArraysWithHexError(
-        kDescription,
-        reinterpret_cast<const unsigned char*>(frame->data()),
+        kDescription, reinterpret_cast<const unsigned char*>(frame->data()),
         framer.GetDataFrameMinimumSize(),
-        IsSpdy4() ? kV4FrameData : kV3FrameData,
+        IsHttp2() ? kH2FrameData : kV3FrameData,
         framer.GetDataFrameMinimumSize());
   }
 
@@ -1849,7 +1843,7 @@
       'h',  'e',  'l',  'l',
       'o'
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x05, 0x00,
       0x01, 0x7f, 0xff, 0xff,
       0xff, 'h',  'e',  'l',
@@ -1858,17 +1852,16 @@
     SpdyDataIR data_ir(0x7fffffff, "hello");
     data_ir.set_fin(true);
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
-    if (IsSpdy4()) {
-       CompareFrame(
-           kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
        CompareFrame(
            kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     }
   }
 
-  if (!IsSpdy4()) {
-    // This test does not apply to SPDY 4 because the max frame size is smaller
+  if (!IsHttp2()) {
+    // This test does not apply to HTTP/2 because the max frame size is smaller
     // than 4MB.
     const char kDescription[] = "Large data frame";
     const int kDataSize = 4 * 1024 * 1024;  // 4 MB
@@ -2422,7 +2415,7 @@
       0x00, 0x00, 0x00, 0x01,
       0x00, 0x00, 0x00, 0x01,
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x07, 0x03,
       0x00, 0x00, 0x00, 0x00,
       0x01, 0x00, 0x00, 0x00,
@@ -2430,8 +2423,8 @@
     };
     SpdyRstStreamIR rst_stream(1, RST_STREAM_PROTOCOL_ERROR, "RST");
     scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
-    if (IsSpdy4()) {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     }
@@ -2445,7 +2438,7 @@
       0x7f, 0xff, 0xff, 0xff,
       0x00, 0x00, 0x00, 0x01,
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x04, 0x03,
       0x00, 0x7f, 0xff, 0xff,
       0xff, 0x00, 0x00, 0x00,
@@ -2455,8 +2448,8 @@
                                RST_STREAM_PROTOCOL_ERROR,
                                "");
     scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
-    if (IsSpdy4()) {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     }
@@ -2470,7 +2463,7 @@
       0x7f, 0xff, 0xff, 0xff,
       0x00, 0x00, 0x00, 0x06,
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x04, 0x03,
       0x00, 0x7f, 0xff, 0xff,
       0xff, 0x00, 0x00, 0x00,
@@ -2480,8 +2473,8 @@
                                RST_STREAM_INTERNAL_ERROR,
                                "");
     scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
-    if (IsSpdy4()) {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     }
@@ -2508,7 +2501,7 @@
       0x01, 0x00, 0x00, 0x07,
       0x0a, 0x0b, 0x0c, 0x0d,
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x06, 0x04,
       0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x04, 0x0a,
@@ -2531,7 +2524,7 @@
     } else if (IsSpdy3()) {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     }
   }
 
@@ -2567,7 +2560,7 @@
     // These end up seemingly out of order because of the way that our internal
     // ordering for settings_ir works. HTTP2 has no requirement on ordering on
     // the wire.
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x18, 0x04,
       0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x03,        // 3rd Setting
@@ -2604,7 +2597,7 @@
     } else if (IsSpdy3()) {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     }
   }
 
@@ -2616,15 +2609,15 @@
       0x00, 0x00, 0x00, 0x04,
       0x00, 0x00, 0x00, 0x00,
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x00, 0x04,
       0x00, 0x00, 0x00, 0x00,
       0x00,
     };
     SpdySettingsIR settings_ir;
     scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
-    if (IsSpdy4()) {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     }
@@ -2641,14 +2634,14 @@
       0x00, 0x00, 0x00, 0x04,
       0x12, 0x34, 0x56, 0x78,
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x08, 0x06,
       0x00, 0x00, 0x00, 0x00,
       0x00, 0x12, 0x34, 0x56,
       0x78, 0x9a, 0xbc, 0xde,
       0xff,
     };
-    const unsigned char kV4FrameDataWithAck[] = {
+    const unsigned char kH2FrameDataWithAck[] = {
       0x00, 0x00, 0x08, 0x06,
       0x01, 0x00, 0x00, 0x00,
       0x00, 0x12, 0x34, 0x56,
@@ -2656,19 +2649,19 @@
       0xff,
     };
     scoped_ptr<SpdyFrame> frame;
-    if (IsSpdy4()) {
+    if (IsHttp2()) {
       const SpdyPingId kPingId = 0x123456789abcdeffULL;
       SpdyPingIR ping_ir(kPingId);
       // Tests SpdyPingIR when the ping is not an ack.
       ASSERT_FALSE(ping_ir.is_ack());
       frame.reset(framer.SerializePing(ping_ir));
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
 
       // Tests SpdyPingIR when the ping is an ack.
       ping_ir.set_is_ack(true);
       frame.reset(framer.SerializePing(ping_ir));
-      CompareFrame(kDescription, *frame,
-                   kV4FrameDataWithAck, arraysize(kV4FrameDataWithAck));
+      CompareFrame(kDescription, *frame, kH2FrameDataWithAck,
+                   arraysize(kH2FrameDataWithAck));
 
     } else {
       frame.reset(framer.SerializePing(SpdyPingIR(0x12345678ull)));
@@ -2693,7 +2686,7 @@
       0x00, 0x00, 0x00, 0x00,  // Stream Id
       0x00, 0x00, 0x00, 0x00,  // Status
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x0a, 0x07,
       0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00,  // Stream id
@@ -2707,7 +2700,7 @@
     } else if (IsSpdy3()) {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     }
   }
 
@@ -2724,7 +2717,7 @@
       0x7f, 0xff, 0xff, 0xff,  // Stream Id
       0x00, 0x00, 0x00, 0x01,  // Status: PROTOCOL_ERROR.
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x0a, 0x07,
       0x00, 0x00, 0x00, 0x00,
       0x00, 0x7f, 0xff, 0xff,  // Stream Id
@@ -2738,7 +2731,7 @@
     } else if (IsSpdy3()) {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     }
   }
 }
@@ -2774,7 +2767,7 @@
       'o',  0x00, 0x00, 0x00,
       0x03, 'b',  'a',  'r'
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
        0x00, 0x00, 0x12, 0x01,  // Headers: END_HEADERS
        0x04, 0x00, 0x00, 0x00,  // Stream 1
        0x01, 0x00, 0x03, 0x62,  // @.ba
@@ -2792,7 +2785,7 @@
     } else if (IsSpdy3()) {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     }
   }
 
@@ -2824,7 +2817,7 @@
       0x00, 0x03, 'b',  'a',
       'r'
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x0f, 0x01,  // Headers: FIN | END_HEADERS
       0x05, 0x7f, 0xff, 0xff,  // Stream 0x7fffffff
       0xff, 0x00, 0x00, 0x03,  // @..
@@ -2842,7 +2835,7 @@
     } else if (IsSpdy3()) {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     }
   }
 
@@ -2874,7 +2867,7 @@
       'o',  0x00, 0x00, 0x00,
       0x00
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x0f, 0x01,  // Headers: FIN | END_HEADERS
       0x05, 0x7f, 0xff, 0xff,  // Stream 0x7fffffff
       0xff, 0x00, 0x03, 0x62,  // @.b
@@ -2892,7 +2885,7 @@
     } else if (IsSpdy3()) {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     }
   }
 
@@ -2900,7 +2893,7 @@
     const char kDescription[] =
         "HEADERS frame with a 0-length header val, FIN, max stream ID, pri";
 
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
         0x00, 0x00, 0x14, 0x01,  // Headers: FIN | END_HEADERS | PRIORITY
         0x25, 0x7f, 0xff, 0xff,  // Stream 0x7fffffff
         0xff, 0x00, 0x00, 0x00,  // exclusive, parent stream
@@ -2920,7 +2913,7 @@
     if (IsSpdy2() || IsSpdy3()) {
       // HEADERS with priority not supported.
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     }
   }
 
@@ -2929,7 +2922,7 @@
         "HEADERS frame with a 0-length header val, FIN, max stream ID, pri, "
         "exclusive=true, parent_stream=0";
 
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
         0x00, 0x00, 0x14, 0x01,  // Headers: FIN | END_HEADERS | PRIORITY
         0x25, 0x7f, 0xff, 0xff,  // Stream 0x7fffffff
         0xff, 0x80, 0x00, 0x00,  // exclusive, parent stream
@@ -2951,7 +2944,7 @@
     if (IsSpdy2() || IsSpdy3()) {
       // HEADERS with priority not supported.
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     }
   }
 
@@ -2960,7 +2953,7 @@
         "HEADERS frame with a 0-length header val, FIN, max stream ID, pri, "
         "exclusive=false, parent_stream=max stream ID";
 
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
         0x00, 0x00, 0x14, 0x01,  // Headers: FIN | END_HEADERS | PRIORITY
         0x25, 0x7f, 0xff, 0xff,  // Stream 0x7fffffff
         0xff, 0x7f, 0xff, 0xff,  // exclusive, parent stream
@@ -2982,7 +2975,7 @@
     if (IsSpdy2() || IsSpdy3()) {
       // HEADERS with priority not supported.
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     }
   }
 
@@ -2990,7 +2983,7 @@
     const char kDescription[] =
         "HEADERS frame with a 0-length header name, FIN, max stream ID, padded";
 
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
         0x00, 0x00, 0x15, 0x01,  // Headers
         0x0d, 0x7f, 0xff, 0xff,  // FIN | END_HEADERS | PADDED, Stream
                                  // 0x7fffffff
@@ -3011,7 +3004,7 @@
     if (IsSpdy2() || IsSpdy3()) {
       // Padding is not supported.
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     }
   }
 }
@@ -3138,7 +3131,7 @@
       0x00, 0x00, 0x00, 0x01,
       0x00, 0x00, 0x00, 0x01,
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x04, 0x08,
       0x00, 0x00, 0x00, 0x00,
       0x01, 0x00, 0x00, 0x00,
@@ -3146,8 +3139,8 @@
     };
     scoped_ptr<SpdyFrame> frame(
         framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 1)));
-    if (IsSpdy4()) {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     }
@@ -3161,7 +3154,7 @@
       0x7f, 0xff, 0xff, 0xff,
       0x00, 0x00, 0x00, 0x01,
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x04, 0x08,
       0x00, 0x7f, 0xff, 0xff,
       0xff, 0x00, 0x00, 0x00,
@@ -3169,8 +3162,8 @@
     };
     scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
         SpdyWindowUpdateIR(0x7FFFFFFF, 1)));
-    if (IsSpdy4()) {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     }
@@ -3184,7 +3177,7 @@
       0x00, 0x00, 0x00, 0x01,
       0x7f, 0xff, 0xff, 0xff,
     };
-    const unsigned char kV4FrameData[] = {
+    const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x04, 0x08,
       0x00, 0x00, 0x00, 0x00,
       0x01, 0x7f, 0xff, 0xff,
@@ -3192,8 +3185,8 @@
     };
     scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
         SpdyWindowUpdateIR(1, 0x7FFFFFFF)));
-    if (IsSpdy4()) {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    if (IsHttp2()) {
+      CompareFrame(kDescription, *frame, kH2FrameData, arraysize(kH2FrameData));
     } else {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     }
@@ -3574,7 +3567,7 @@
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame->data()),
       control_frame->size());
-  if (IsSpdy4()) {
+  if (IsHttp2()) {
     EXPECT_EQ(0, visitor.syn_reply_frame_count_);
     EXPECT_EQ(1, visitor.headers_frame_count_);
   } else {
@@ -3693,7 +3686,7 @@
   // Create a frame at exatly that size.
   string big_value(kBigValueSize, 'x');
   syn_stream.SetHeader("aa", big_value);
-  // Upstream branches here and wraps SPDY4 with EXPECT_DEBUG_DFATAL. We
+  // Upstream branches here and wraps HTTP/2 with EXPECT_DEBUG_DFATAL. We
   // neither support that in Chromium, nor do we use the same DFATAL (see
   // SpdyFrameBuilder::WriteFramePrefix()).
   control_frame.reset(framer.SerializeSynStream(syn_stream));
@@ -3859,7 +3852,7 @@
   const size_t less_than_min_length =
       framer.GetGoAwayMinimumSize() - framer.GetControlFrameHeaderSize() - 1;
   ASSERT_LE(less_than_min_length, std::numeric_limits<unsigned char>::max());
-  const unsigned char kV4FrameData[] = {
+  const unsigned char kH2FrameData[] = {
     0x00, 0x00, static_cast<unsigned char>(less_than_min_length), 0x07,
     0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00,  // Stream Id
@@ -3868,12 +3861,12 @@
   };
   const size_t pad_length =
       length + framer.GetControlFrameHeaderSize() -
-      (IsSpdy4() ? sizeof(kV4FrameData) : sizeof(kV3FrameData));
+      (IsHttp2() ? sizeof(kH2FrameData) : sizeof(kV3FrameData));
   string pad(pad_length, 'A');
   TestSpdyVisitor visitor(spdy_version_);
 
-  if (IsSpdy4()) {
-    visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData));
+  if (IsHttp2()) {
+    visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData));
   } else {
     visitor.SimulateInFramer(kV3FrameData, sizeof(kV3FrameData));
   }
@@ -3902,7 +3895,7 @@
     // Should generate an error, since zero-len settings frames are unsupported.
     EXPECT_EQ(1, visitor.error_count_);
   } else {
-    // Zero-len settings frames are permitted as of SPDY 4.
+    // Zero-len settings frames are permitted as of HTTP/2.
     EXPECT_EQ(0, visitor.error_count_);
   }
 }
@@ -4008,7 +4001,7 @@
     0x00, 0x00, 0x00, 0x03,  // 3rd (unprocessed) Setting
     0x00, 0x00, 0x00, 0x03,
   };
-  const unsigned char kV4FrameData[] = {
+  const unsigned char kH2FrameData[] = {
     0x00, 0x00, 0x12, 0x04,
     0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x01,  // 1st Setting
@@ -4026,14 +4019,14 @@
   } else if (IsSpdy3()) {
     visitor.SimulateInFramer(kV3FrameData, sizeof(kV3FrameData));
   } else {
-    visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData));
+    visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData));
   }
 
-  if (!IsSpdy4()) {
+  if (!IsHttp2()) {
     EXPECT_EQ(1, visitor.setting_count_);
     EXPECT_EQ(1, visitor.error_count_);
   } else {
-    // In SPDY 4+, duplicate settings are allowed;
+    // In HTTP/2, duplicate settings are allowed;
     // each setting replaces the previous value for that setting.
     EXPECT_EQ(3, visitor.setting_count_);
     EXPECT_EQ(0, visitor.error_count_);
@@ -4059,7 +4052,7 @@
     0x00, 0x00, 0x00, 0x10,  // 1st Setting
     0x00, 0x00, 0x00, 0x02,
   };
-  const unsigned char kV4FrameData[] = {
+  const unsigned char kH2FrameData[] = {
     0x00, 0x00, 0x06, 0x04,
     0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x10,  // 1st Setting
@@ -4073,14 +4066,14 @@
   } else if (IsSpdy3()) {
     visitor.SimulateInFramer(kV3FrameData, sizeof(kV3FrameData));
   } else {
-    visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData));
+    visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData));
   }
 
-  if (!IsSpdy4()) {
+  if (!IsHttp2()) {
     EXPECT_EQ(0, visitor.setting_count_);
     EXPECT_EQ(1, visitor.error_count_);
   } else {
-    // In SPDY 4+, we ignore unknown settings because of extensions.
+    // In HTTP/2, we ignore unknown settings because of extensions.
     EXPECT_EQ(0, visitor.setting_count_);
     EXPECT_EQ(0, visitor.error_count_);
   }
@@ -4112,7 +4105,7 @@
     0x00, 0x00, 0x01, 0x03,  // 3rd (unprocessed) Setting
     0x00, 0x00, 0x00, 0x03,
   };
-  const unsigned char kV4FrameData[] = {
+  const unsigned char kH2FrameData[] = {
     0x00, 0x00, 0x12, 0x04,
     0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x02,  // 1st Setting
@@ -4130,14 +4123,14 @@
   } else if (IsSpdy3()) {
     visitor.SimulateInFramer(kV3FrameData, sizeof(kV3FrameData));
   } else {
-    visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData));
+    visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData));
   }
 
-  if (!IsSpdy4()) {
+  if (!IsHttp2()) {
     EXPECT_EQ(1, visitor.setting_count_);
     EXPECT_EQ(1, visitor.error_count_);
   } else {
-    // In SPDY 4+, settings are allowed in any order.
+    // In HTTP/2, settings are allowed in any order.
     EXPECT_EQ(3, visitor.setting_count_);
     EXPECT_EQ(0, visitor.error_count_);
   }
@@ -4701,7 +4694,7 @@
 }
 
 TEST_P(SpdyFramerTest, ReadGarbageWithValidLength) {
-  if (!IsSpdy4()) {
+  if (!IsHttp2()) {
     return;
   }
   SpdyFramer framer(spdy_version_);
@@ -4718,8 +4711,8 @@
 }
 
 TEST_P(SpdyFramerTest, ReadGarbageWithValidVersion) {
-  if (IsSpdy4()) {
-    // Not valid for SPDY 4 since there is no version field.
+  if (IsHttp2()) {
+    // Not valid for HTTP/2 since there is no version field.
     return;
   }
   SpdyFramer framer(spdy_version_);
@@ -4754,7 +4747,7 @@
 
 TEST_P(SpdyFramerTest, SizesTest) {
   SpdyFramer framer(spdy_version_);
-  if (IsSpdy4()) {
+  if (IsHttp2()) {
     EXPECT_EQ(9u, framer.GetDataFrameMinimumSize());
     EXPECT_EQ(9u, framer.GetControlFrameHeaderSize());
     EXPECT_EQ(14u, framer.GetSynStreamMinimumSize());
@@ -4904,8 +4897,8 @@
 }
 
 TEST_P(SpdyFramerTest, CatchProbableHttpResponse) {
-  if (IsSpdy4()) {
-    // TODO(hkhalil): catch probable HTTP response in SPDY 4?
+  if (IsHttp2()) {
+    // TODO(hkhalil): catch probable HTTP response in HTTP/2?
     return;
   }
   {
@@ -5283,23 +5276,23 @@
     framer.set_visitor(&visitor);
 
     SpdyHeadersIR headers_ir(57);
-    if (IsSpdy4() && (flags & HEADERS_FLAG_PRIORITY)) {
+    if (IsHttp2() && (flags & HEADERS_FLAG_PRIORITY)) {
       headers_ir.set_priority(3);
       headers_ir.set_has_priority(true);
     }
     headers_ir.SetHeader("foo", "bar");
     scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
     uint8 set_flags = flags;
-    if (IsSpdy4()) {
+    if (IsHttp2()) {
       // TODO(jgraettinger): Add padding to SpdyHeadersIR,
       // and implement framing.
       set_flags &= ~HEADERS_FLAG_PADDED;
     }
     SetFrameFlags(frame.get(), set_flags, spdy_version_);
 
-    if (!IsSpdy4() && flags & ~CONTROL_FLAG_FIN) {
+    if (!IsHttp2() && flags & ~CONTROL_FLAG_FIN) {
       EXPECT_CALL(visitor, OnError(_));
-    } else if (IsSpdy4() && flags & ~(CONTROL_FLAG_FIN |
+    } else if (IsHttp2() && flags & ~(CONTROL_FLAG_FIN |
                                       HEADERS_FLAG_END_HEADERS |
                                       HEADERS_FLAG_END_SEGMENT |
                                       HEADERS_FLAG_PADDED |
@@ -5312,17 +5305,16 @@
                                        3,     // priority
                                        flags & CONTROL_FLAG_FIN,  // fin?
                                        (flags & HEADERS_FLAG_END_HEADERS) ||
-                                           !IsSpdy4()));  // end headers?
+                                           !IsHttp2()));  // end headers?
       } else {
-        EXPECT_CALL(visitor, OnHeaders(57, false, 0,
-                                       flags & CONTROL_FLAG_FIN,
+        EXPECT_CALL(visitor, OnHeaders(57, false, 0, flags & CONTROL_FLAG_FIN,
                                        (flags & HEADERS_FLAG_END_HEADERS) ||
-                                        !IsSpdy4()));
+                                           !IsHttp2()));
       }
       EXPECT_CALL(visitor, OnControlFrameHeaderData(57, _, _))
           .WillRepeatedly(testing::Return(true));
-      if (flags & DATA_FLAG_FIN  && (!IsSpdy4() ||
-                                     flags & HEADERS_FLAG_END_HEADERS)) {
+      if (flags & DATA_FLAG_FIN &&
+          (!IsHttp2() || flags & HEADERS_FLAG_END_HEADERS)) {
         EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
       } else {
         // Do not close the stream if we are expecting a CONTINUATION frame.
@@ -5331,12 +5323,12 @@
     }
 
     framer.ProcessInput(frame->data(), frame->size());
-    if (!IsSpdy4() && flags & ~CONTROL_FLAG_FIN) {
+    if (!IsHttp2() && flags & ~CONTROL_FLAG_FIN) {
       EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
       EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
                 framer.error_code())
           << SpdyFramer::ErrorCodeToString(framer.error_code());
-    } else if (IsSpdy4() && flags & ~(CONTROL_FLAG_FIN |
+    } else if (IsHttp2() && flags & ~(CONTROL_FLAG_FIN |
                                       HEADERS_FLAG_END_HEADERS |
                                       HEADERS_FLAG_END_SEGMENT |
                                       HEADERS_FLAG_PADDED |
@@ -5345,7 +5337,7 @@
       EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
                 framer.error_code())
           << SpdyFramer::ErrorCodeToString(framer.error_code());
-    } else if (IsSpdy4() && ~(flags & HEADERS_FLAG_END_HEADERS)) {
+    } else if (IsHttp2() && ~(flags & HEADERS_FLAG_END_HEADERS)) {
       EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
       EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
           << SpdyFramer::ErrorCodeToString(framer.error_code());
@@ -5590,7 +5582,7 @@
     0x00, 0x00, 0x00, 0x01,
     0x00, 0x00, 0x00, kRstStreamStatusTooLow
   };
-  const unsigned char kV4RstStreamInvalid[] = {
+  const unsigned char kH2RstStreamInvalid[] = {
     0x00, 0x00, 0x04, 0x03,
     0x00, 0x00, 0x00, 0x00,
     0x01, 0x00, 0x00, 0x00,
@@ -5603,7 +5595,7 @@
     0x00, 0x00, 0x00, 0x01,
     0x00, 0x00, 0x00, kRstStreamStatusTooHigh
   };
-  const unsigned char kV4RstStreamNumStatusCodes[] = {
+  const unsigned char kH2RstStreamNumStatusCodes[] = {
     0x00, 0x00, 0x04, 0x03,
     0x00, 0x00, 0x00, 0x00,
     0x01, 0x00, 0x00, 0x00,
@@ -5614,10 +5606,10 @@
   SpdyFramer framer(spdy_version_);
   framer.set_visitor(&visitor);
 
-  if (IsSpdy4()) {
+  if (IsHttp2()) {
     EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INTERNAL_ERROR));
-    framer.ProcessInput(reinterpret_cast<const char*>(kV4RstStreamInvalid),
-                        arraysize(kV4RstStreamInvalid));
+    framer.ProcessInput(reinterpret_cast<const char*>(kH2RstStreamInvalid),
+                        arraysize(kH2RstStreamInvalid));
   } else {
     EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
     framer.ProcessInput(reinterpret_cast<const char*>(kV3RstStreamInvalid),
@@ -5630,11 +5622,11 @@
 
   framer.Reset();
 
-  if (IsSpdy4()) {
+  if (IsHttp2()) {
     EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INTERNAL_ERROR));
     framer.ProcessInput(
-        reinterpret_cast<const char*>(kV4RstStreamNumStatusCodes),
-        arraysize(kV4RstStreamNumStatusCodes));
+        reinterpret_cast<const char*>(kH2RstStreamNumStatusCodes),
+        arraysize(kH2RstStreamNumStatusCodes));
   } else {
     EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
     framer.ProcessInput(
@@ -5659,7 +5651,7 @@
     0x00, 0x00, 0x00, 0x01,  // Stream Id
     0xff, 0xff, 0xff, 0xff,  // Status
   };
-  const unsigned char kV4FrameData[] = {
+  const unsigned char kH2FrameData[] = {
     0x00, 0x00, 0x0a, 0x07,
     0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00,  // Stream id
@@ -5675,8 +5667,8 @@
                         arraysize(kV3FrameData));
   } else {
     EXPECT_CALL(visitor, OnGoAway(1, GOAWAY_INTERNAL_ERROR));
-    framer.ProcessInput(reinterpret_cast<const char*>(kV4FrameData),
-                        arraysize(kV4FrameData));
+    framer.ProcessInput(reinterpret_cast<const char*>(kH2FrameData),
+                        arraysize(kH2FrameData));
   }
   EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
   EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
@@ -5696,7 +5688,7 @@
     0xff, 0xff, 0xff, 0xff,
     0x00, 0x00, 0x00, 0x00,
   };
-  const unsigned char kV4FrameData[] = {
+  const unsigned char kH2FrameData[] = {
     0x00, 0x00, 0x08, 0x07,
     0x00, 0x00, 0x00, 0x00,
     0x00, 0xff, 0xff, 0xff,
@@ -5716,8 +5708,8 @@
     framer.ProcessInput(reinterpret_cast<const char*>(kV3FrameData),
                         arraysize(kV3FrameData));
   } else {
-    framer.ProcessInput(reinterpret_cast<const char*>(kV4FrameData),
-                        arraysize(kV4FrameData));
+    framer.ProcessInput(reinterpret_cast<const char*>(kH2FrameData),
+                        arraysize(kH2FrameData));
   }
   EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
   EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
diff --git a/net/spdy/spdy_headers_block_parser_test.cc b/net/spdy/spdy_headers_block_parser_test.cc
index bd4a01bf..dafaff8d 100644
--- a/net/spdy/spdy_headers_block_parser_test.cc
+++ b/net/spdy/spdy_headers_block_parser_test.cc
@@ -108,10 +108,10 @@
 const char *const SpdyHeadersBlockParserTest::kBaseKey = "test_key";
 const char *const SpdyHeadersBlockParserTest::kBaseValue = "test_value";
 
-// All tests are run with 3 different SPDY versions: SPDY/2, SPDY/3, SPDY/4.
+// All tests are run with 3 different SPDY versions: SPDY/2, SPDY/3, HTTP/2.
 INSTANTIATE_TEST_CASE_P(SpdyHeadersBlockParserTests,
                         SpdyHeadersBlockParserTest,
-                        ::testing::Values(SPDY2, SPDY3, SPDY4));
+                        ::testing::Values(SPDY2, SPDY3, HTTP2));
 
 TEST_P(SpdyHeadersBlockParserTest, BasicTest) {
   // Sanity test, verify that we parse out correctly a block with
diff --git a/net/spdy/spdy_http_utils.cc b/net/spdy/spdy_http_utils.cc
index 5397473..02543c0b 100644
--- a/net/spdy/spdy_http_utils.cc
+++ b/net/spdy/spdy_http_utils.cc
@@ -43,14 +43,14 @@
   std::string version;
   std::string status;
 
-  // The "status" header is required. "version" is required below SPDY4.
+  // The "status" header is required. "version" is required below HTTP/2.
   SpdyHeaderBlock::const_iterator it;
   it = headers.find(status_key);
   if (it == headers.end())
     return false;
   status = it->second;
 
-  if (protocol_version >= SPDY4) {
+  if (protocol_version >= HTTP2) {
     version = "HTTP/1.1";
   } else {
     it = headers.find(version_key);
@@ -127,7 +127,7 @@
                                  : HttpUtil::SpecForRequest(info.url);
     }
   } else {
-    if (protocol_version < SPDY4) {
+    if (protocol_version < HTTP2) {
       (*headers)[":version"] = kHttpProtocolVersion;
       (*headers)[":host"] = GetHostAndOptionalPort(info.url);
     } else {
@@ -155,7 +155,7 @@
   const std::string status_line = response_headers.GetStatusLine();
   std::string::const_iterator after_version =
       std::find(status_line.begin(), status_line.end(), ' ');
-  if (protocol_version < SPDY4) {
+  if (protocol_version < HTTP2) {
     (*headers)[version_key] = std::string(status_line.begin(), after_version);
   }
   (*headers)[status_key] = std::string(after_version + 1, status_line.end());
@@ -198,7 +198,7 @@
   std::string url = it->second;
   url.append("://");
 
-  it = headers.find(protocol_version >= SPDY4 ? ":authority" : ":host");
+  it = headers.find(protocol_version >= HTTP2 ? ":authority" : ":host");
   if (it == headers.end())
     return GURL();
   url.append(it->second);
diff --git a/net/spdy/spdy_http_utils_unittest.cc b/net/spdy/spdy_http_utils_unittest.cc
index 48cd4ede..1fbb705b 100644
--- a/net/spdy/spdy_http_utils_unittest.cc
+++ b/net/spdy/spdy_http_utils_unittest.cc
@@ -72,14 +72,14 @@
   EXPECT_EQ("Chrome/1.1", headers["user-agent"]);
 }
 
-TEST(SpdyHttpUtilsTest, CreateSpdyHeadersFromHttpRequestSPDY4) {
+TEST(SpdyHttpUtilsTest, CreateSpdyHeadersFromHttpRequestHTTP2) {
   GURL url("https://www.google.com/index.html");
   HttpRequestInfo request;
   request.method = "GET";
   request.url = url;
   request.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, "Chrome/1.1");
   SpdyHeaderBlock headers;
-  CreateSpdyHeadersFromHttpRequest(request, request.extra_headers, SPDY4,
+  CreateSpdyHeadersFromHttpRequest(request, request.extra_headers, HTTP2,
                                    kDirect, &headers);
   EXPECT_EQ("GET", headers[":method"]);
   EXPECT_EQ("https", headers[":scheme"]);
@@ -123,14 +123,14 @@
   EXPECT_EQ("Chrome/1.1", headers["user-agent"]);
 }
 
-TEST(SpdyHttpUtilsTest, CreateSpdyHeadersFromHttpRequestProxySPDY4) {
+TEST(SpdyHttpUtilsTest, CreateSpdyHeadersFromHttpRequestProxyHTTP2) {
   GURL url("https://www.google.com/index.html");
   HttpRequestInfo request;
   request.method = "GET";
   request.url = url;
   request.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, "Chrome/1.1");
   SpdyHeaderBlock headers;
-  CreateSpdyHeadersFromHttpRequest(request, request.extra_headers, SPDY4,
+  CreateSpdyHeadersFromHttpRequest(request, request.extra_headers, HTTP2,
                                    !kDirect, &headers);
   EXPECT_EQ("GET", headers[":method"]);
   EXPECT_EQ("https", headers[":scheme"]);
@@ -174,14 +174,14 @@
   EXPECT_EQ("Chrome/1.1", headers["user-agent"]);
 }
 
-TEST(SpdyHttpUtilsTest, CreateSpdyHeadersFromHttpRequestConnectSPDY4) {
+TEST(SpdyHttpUtilsTest, CreateSpdyHeadersFromHttpRequestConnectHTTP2) {
   GURL url("https://www.google.com/index.html");
   HttpRequestInfo request;
   request.method = "CONNECT";
   request.url = url;
   request.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, "Chrome/1.1");
   SpdyHeaderBlock headers;
-  CreateSpdyHeadersFromHttpRequest(request, request.extra_headers, SPDY4,
+  CreateSpdyHeadersFromHttpRequest(request, request.extra_headers, HTTP2,
                                    kDirect, &headers);
   EXPECT_EQ("CONNECT", headers[":method"]);
   EXPECT_TRUE(headers.end() == headers.find(":scheme"));
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index a67a23a..ba18cd1 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -2878,7 +2878,7 @@
 }
 
 TEST_P(SpdyNetworkTransactionTest, ServerPushInvalidAssociatedStreamID0) {
-  if (spdy_util_.spdy_version() == SPDY4) {
+  if (spdy_util_.spdy_version() == HTTP2) {
     // PUSH_PROMISE with stream id 0 is connection-level error.
     // TODO(baranovich): Test session going away.
     return;
@@ -3074,8 +3074,8 @@
   test_cases[2].expected_headers["hello"] = "bye";
   test_cases[2].expected_headers["status"] = "200";
 
-  if (spdy_util_.spdy_version() < SPDY4) {
-    // SPDY4/HTTP2 eliminates use of the :version header.
+  if (spdy_util_.spdy_version() < HTTP2) {
+    // HTTP/2 eliminates use of the :version header.
     test_cases[0].expected_headers["version"] = "HTTP/1.1";
     test_cases[1].expected_headers["version"] = "HTTP/1.1";
     test_cases[2].expected_headers["version"] = "HTTP/1.1";
@@ -3319,10 +3319,10 @@
 }
 
 // Verify that we don't crash on some corrupt frames.
-// TODO(jgraettinger): SPDY4 and up treats a header decompression failure as a
+// TODO(jgraettinger): HTTP/2 treats a header decompression failure as a
 // connection error. I'd like to backport this behavior to SPDY3 as well.
 TEST_P(SpdyNetworkTransactionTest, CorruptFrameSessionError) {
-  if (spdy_util_.spdy_version() >= SPDY4) {
+  if (spdy_util_.spdy_version() >= HTTP2) {
     return;
   }
   // This is the length field that's too short.
@@ -3330,9 +3330,9 @@
       spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
   BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
   size_t right_size =
-      (spdy_util_.spdy_version() < SPDY4) ?
-      syn_reply_wrong_length->size() - framer.GetControlFrameHeaderSize() :
-      syn_reply_wrong_length->size();
+      (spdy_util_.spdy_version() < HTTP2)
+          ? syn_reply_wrong_length->size() - framer.GetControlFrameHeaderSize()
+          : syn_reply_wrong_length->size();
   size_t wrong_size = right_size - 4;
   test::SetFrameLength(syn_reply_wrong_length.get(),
                        wrong_size,
@@ -3370,9 +3370,9 @@
   }
 }
 
-// SPDY4 treats a header decompression failure as a connection-level error.
+// HTTP/2 treats a header decompression failure as a connection-level error.
 TEST_P(SpdyNetworkTransactionTest, CorruptFrameSessionErrorSpdy4) {
-  if (spdy_util_.spdy_version() < SPDY4) {
+  if (spdy_util_.spdy_version() < HTTP2) {
     return;
   }
   // This is the length field that's too short.
@@ -3521,7 +3521,7 @@
 // In this test, we enable compression, but get a uncompressed SynReply from
 // the server.  Verify that teardown is all clean.
 TEST_P(SpdyNetworkTransactionTest, DecompressFailureOnSynReply) {
-  if (spdy_util_.spdy_version() >= SPDY4) {
+  if (spdy_util_.spdy_version() >= HTTP2) {
     // HPACK doesn't use deflate compression.
     return;
   }
@@ -3625,8 +3625,8 @@
                      spdy_util_.default_url().scheme());
   expected.push_back(std::string(spdy_util_.GetMethodKey()) + ": GET");
   expected.push_back("user-agent: Chrome");
-  if (spdy_util_.spdy_version() < SPDY4) {
-    // SPDY4/HTTP2 eliminates use of the :version header.
+  if (spdy_util_.spdy_version() < HTTP2) {
+    // HTTP/2 eliminates use of the :version header.
     expected.push_back(std::string(spdy_util_.GetVersionKey()) + ": HTTP/1.1");
   }
   EXPECT_EQ(expected.size(), header_list->GetSize());
@@ -4073,8 +4073,8 @@
 // Test that if the server requests persistence of settings, that we save
 // the settings in the HttpServerProperties.
 TEST_P(SpdyNetworkTransactionTest, SettingsSaved) {
-  if (spdy_util_.spdy_version() >= SPDY4) {
-    // SPDY4 doesn't support settings persistence.
+  if (spdy_util_.spdy_version() >= HTTP2) {
+    // HTTP/2 doesn't support settings persistence.
     return;
   }
   static const SpdyHeaderInfo kSynReplyInfo = {
@@ -4180,8 +4180,8 @@
 // Test that when there are settings saved that they are sent back to the
 // server upon session establishment.
 TEST_P(SpdyNetworkTransactionTest, SettingsPlayback) {
-  if (spdy_util_.spdy_version() >= SPDY4) {
-    // SPDY4 doesn't support settings persistence.
+  if (spdy_util_.spdy_version() >= HTTP2) {
+    // HTTP/2 doesn't support settings persistence.
     return;
   }
   static const SpdyHeaderInfo kSynReplyInfo = {
@@ -4363,8 +4363,8 @@
 
 // HTTP_1_1_REQUIRED results in ERR_HTTP_1_1_REQUIRED.
 TEST_P(SpdyNetworkTransactionTest, HTTP11RequiredError) {
-  // HTTP_1_1_REQUIRED is only supported by SPDY4.
-  if (spdy_util_.spdy_version() < SPDY4)
+  // HTTP_1_1_REQUIRED is only supported by HTTP/2.
+  if (spdy_util_.spdy_version() < HTTP2)
     return;
 
   NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY,
@@ -4386,8 +4386,8 @@
 // protocol negotiation happens, instead this test forces protocols for both
 // sockets.
 TEST_P(SpdyNetworkTransactionTest, HTTP11RequiredRetry) {
-  // HTTP_1_1_REQUIRED is only supported by SPDY4.
-  if (spdy_util_.spdy_version() < SPDY4)
+  // HTTP_1_1_REQUIRED is only supported by HTTP/2.
+  if (spdy_util_.spdy_version() < HTTP2)
     return;
   // HTTP_1_1_REQUIRED implementation relies on the assumption that HTTP/2 is
   // only spoken over SSL.
@@ -4477,8 +4477,8 @@
 // proxy.  Note that no actual protocol negotiation happens, instead this test
 // forces protocols for both sockets.
 TEST_P(SpdyNetworkTransactionTest, HTTP11RequiredProxyRetry) {
-  // HTTP_1_1_REQUIRED is only supported by SPDY4.
-  if (spdy_util_.spdy_version() < SPDY4)
+  // HTTP_1_1_REQUIRED is only supported by HTTP/2.
+  if (spdy_util_.spdy_version() < HTTP2)
     return;
   // HTTP_1_1_REQUIRED implementation relies on the assumption that HTTP/2 is
   // only spoken over SSL.
@@ -5194,8 +5194,8 @@
   };
 
   scoped_ptr<SpdyHeaderBlock> initial_headers(new SpdyHeaderBlock());
-  if (spdy_util_.spdy_version() < SPDY4) {
-    // In SPDY4 PUSH_PROMISE headers won't show up in the response headers.
+  if (spdy_util_.spdy_version() < HTTP2) {
+    // In HTTP/2 PUSH_PROMISE headers won't show up in the response headers.
     (*initial_headers)["alpha"] = "beta";
   }
   spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat"),
@@ -5216,8 +5216,8 @@
 
   scoped_ptr<SpdyHeaderBlock> late_headers(new SpdyHeaderBlock());
   (*late_headers)[spdy_util_.GetStatusKey()] = "200";
-  if (spdy_util_.spdy_version() < SPDY4) {
-    // SPDY4/HTTP2 eliminates use of the :version header.
+  if (spdy_util_.spdy_version() < HTTP2) {
+    // HTTP/2 eliminates use of the :version header.
     (*late_headers)[spdy_util_.GetVersionKey()] = "HTTP/1.1";
   }
   scoped_ptr<SpdyFrame> stream2_headers2(
@@ -5309,7 +5309,7 @@
   EXPECT_EQ("HTTP/1.1 200 OK", response2.headers->GetStatusLine());
 
   // Verify we got all the headers from all header blocks.
-  if (spdy_util_.spdy_version() < SPDY4)
+  if (spdy_util_.spdy_version() < HTTP2)
     EXPECT_TRUE(response2.headers->HasHeaderValue("alpha", "beta"));
   EXPECT_TRUE(response2.headers->HasHeaderValue("hello", "bye"));
   EXPECT_TRUE(response2.headers->HasHeaderValue("status", "200"));
diff --git a/net/spdy/spdy_protocol.cc b/net/spdy/spdy_protocol.cc
index f0f223b..38569a9 100644
--- a/net/spdy/spdy_protocol.cc
+++ b/net/spdy/spdy_protocol.cc
@@ -39,7 +39,7 @@
       }
 
       return true;
-    case SPDY4:
+    case HTTP2:
       // Check for recognized extensions.
       if (frame_type_field == SerializeFrameType(version, ALTSVC) ||
           frame_type_field == SerializeFrameType(version, BLOCKED)) {
@@ -87,7 +87,7 @@
           return WINDOW_UPDATE;
       }
       break;
-    case SPDY4:
+    case HTTP2:
       switch (frame_type_field) {
         case 0:
           return DATA;
@@ -147,7 +147,7 @@
           LOG(DFATAL) << "Serializing unhandled frame type " << frame_type;
           return -1;
       }
-    case SPDY4:
+    case HTTP2:
       switch (frame_type) {
         case DATA:
           return 0;
@@ -189,7 +189,7 @@
     case SPDY2:
     case SPDY3:
       return 0;
-    case SPDY4:
+    case HTTP2:
       return SerializeFrameType(version, DATA);
   }
 
@@ -215,7 +215,7 @@
       }
 
       return true;
-    case SPDY4:
+    case HTTP2:
       // HEADER_TABLE_SIZE is the first valid setting id.
       if (setting_id_field <
           SerializeSettingId(version, SETTINGS_HEADER_TABLE_SIZE)) {
@@ -257,7 +257,7 @@
           return SETTINGS_INITIAL_WINDOW_SIZE;
       }
       break;
-    case SPDY4:
+    case HTTP2:
       switch (setting_id_field) {
         case 1:
           return SETTINGS_HEADER_TABLE_SIZE;
@@ -303,7 +303,7 @@
           LOG(DFATAL) << "Serializing unhandled setting id " << id;
           return -1;
       }
-    case SPDY4:
+    case HTTP2:
       switch (id) {
         case SETTINGS_HEADER_TABLE_SIZE:
           return 1;
@@ -344,7 +344,7 @@
       }
 
       return true;
-    case SPDY4:
+    case HTTP2:
       // NO_ERROR is the first valid status code.
       if (rst_stream_status_field <
           SerializeRstStreamStatus(version, RST_STREAM_PROTOCOL_ERROR)) {
@@ -405,7 +405,7 @@
           return RST_STREAM_FRAME_TOO_LARGE;
       }
       break;
-    case SPDY4:
+    case HTTP2:
       switch (rst_stream_status_field) {
         case 1:
           return RST_STREAM_PROTOCOL_ERROR;
@@ -471,7 +471,7 @@
                       << rst_stream_status;
           return -1;
       }
-    case SPDY4:
+    case HTTP2:
       switch (rst_stream_status) {
         case RST_STREAM_PROTOCOL_ERROR:
           return 1;
@@ -522,7 +522,7 @@
       }
 
       return true;
-    case SPDY4:
+    case HTTP2:
       // GOAWAY_NO_ERROR is the first valid status.
       if (goaway_status_field < SerializeGoAwayStatus(version,
                                                       GOAWAY_NO_ERROR)) {
@@ -555,7 +555,7 @@
           return GOAWAY_INTERNAL_ERROR;
       }
       break;
-    case SPDY4:
+    case HTTP2:
       switch (goaway_status_field) {
         case 0:
           return GOAWAY_NO_ERROR;
@@ -600,7 +600,7 @@
     case 3:
       return SPDY3;
     case 4:
-      return SPDY4;
+      return HTTP2;
     default:
       LOG(DFATAL) << "Unsupported SPDY version number: " << version_number;
       return SPDY3;
@@ -613,7 +613,7 @@
       return 2;
     case SPDY3:
       return 3;
-    case SPDY4:
+    case HTTP2:
       return 4;
     default:
       LOG(DFATAL) << "Unsupported SPDY major version: " << version;
@@ -627,7 +627,7 @@
       return "spdy/2";
     case SPDY3:
       return "spdy/3";
-    case SPDY4:
+    case HTTP2:
       return "h2-14";
     default:
       LOG(DFATAL) << "Unsupported SPDY major version: " << version;
@@ -662,7 +662,7 @@
           LOG(DFATAL) << "Serializing unhandled GOAWAY status " << status;
           return -1;
       }
-    case SPDY4:
+    case HTTP2:
       switch (status) {
         case GOAWAY_NO_ERROR:
           return 0;
@@ -706,7 +706,7 @@
     case SPDY2:
     case SPDY3:
       return 8;
-    case SPDY4:
+    case HTTP2:
       return 9;
   }
   LOG(DFATAL) << "Unhandled SPDY version.";
@@ -718,7 +718,7 @@
     case SPDY2:
     case SPDY3:
       return 8;
-    case SPDY4:
+    case HTTP2:
       return 9;
   }
   LOG(DFATAL) << "Unhandled SPDY version.";
@@ -735,7 +735,7 @@
 }
 
 size_t SpdyConstants::GetFrameMaximumSize(SpdyMajorVersion version) {
-  if (version < SPDY4) {
+  if (version < HTTP2) {
     // 24-bit length field plus eight-byte frame header.
     return ((1<<24) - 1) + 8;
   } else {
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h
index 6d8edbe..8a8131ef 100644
--- a/net/spdy/spdy_protocol.h
+++ b/net/spdy/spdy_protocol.h
@@ -35,9 +35,8 @@
   SPDY2 = 2,
   SPDY_MIN_VERSION = SPDY2,
   SPDY3 = 3,
-  SPDY4 = 4,
-  HTTP2 = SPDY4,
-  SPDY_MAX_VERSION = SPDY4
+  HTTP2 = 4,
+  SPDY_MAX_VERSION = HTTP2
 };
 
 // A SPDY stream id is a 31 bit entity.
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 66da508..a648ae9f 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -458,7 +458,7 @@
       if (it->first == "url")
         to_insert = request_headers;
     } else {
-      const char* host = protocol_version >= SPDY4 ? ":authority" : ":host";
+      const char* host = protocol_version >= HTTP2 ? ":authority" : ":host";
       static const char scheme[] = ":scheme";
       static const char path[] = ":path";
       if (it->first == host || it->first == scheme || it->first == path)
@@ -1000,7 +1000,7 @@
   }
 
   // We don't enforce transport security standards for older SPDY versions.
-  if (GetProtocolVersion() < SPDY4) {
+  if (GetProtocolVersion() < HTTP2) {
     return true;
   }
 
@@ -2114,7 +2114,7 @@
                                  clear_persisted));
   }
 
-  if (GetProtocolVersion() >= SPDY4) {
+  if (GetProtocolVersion() >= HTTP2) {
     // Send an acknowledgment of the setting.
     SpdySettingsIR settings_ir;
     settings_ir.set_is_ack(true);
@@ -2311,9 +2311,8 @@
   stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
   last_compressed_frame_len_ = 0;
 
-  if (GetProtocolVersion() >= SPDY4) {
-    const std::string& error =
-        "SPDY4 wasn't expecting SYN_REPLY.";
+  if (GetProtocolVersion() >= HTTP2) {
+    const std::string& error = "HTTP/2 wasn't expecting SYN_REPLY.";
     stream->LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
     ResetStreamIterator(it, RST_STREAM_PROTOCOL_ERROR, error);
     return;
@@ -2361,7 +2360,7 @@
   base::TimeTicks recv_first_byte_time = time_func_();
 
   if (it->second.waiting_for_syn_reply) {
-    if (GetProtocolVersion() < SPDY4) {
+    if (GetProtocolVersion() < HTTP2) {
       const std::string& error =
           "Was expecting SYN_REPLY, not HEADERS.";
       stream->LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
@@ -2601,9 +2600,9 @@
   }
 
   if (associated_stream_id == 0) {
-    // In SPDY4 0 stream id in PUSH_PROMISE frame leads to framer error and
+    // In HTTP/2 0 stream id in PUSH_PROMISE frame leads to framer error and
     // session going away. We should never get here.
-    CHECK_GT(SPDY4, GetProtocolVersion());
+    CHECK_GT(HTTP2, GetProtocolVersion());
     std::string description = base::StringPrintf(
         "Received invalid associated stream id %d for pushed stream %d",
         associated_stream_id,
@@ -2686,7 +2685,7 @@
   stream->set_stream_id(stream_id);
 
   // In spdy4/http2 PUSH_PROMISE arrives on associated stream.
-  if (associated_it != active_streams_.end() && GetProtocolVersion() >= SPDY4) {
+  if (associated_it != active_streams_.end() && GetProtocolVersion() >= HTTP2) {
     associated_it->second.stream->IncrementRawReceivedBytes(
         last_compressed_frame_len_);
   } else {
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index 5ed45ae..81e55812 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -1372,9 +1372,9 @@
 // also clears the persisted data. Verify that persisted data is
 // correct.
 TEST_P(SpdySessionTest, ClearSettings) {
-  if (spdy_util_.spdy_version() >= SPDY4) {
-    // SPDY4 doesn't include settings persistence, or a CLEAR_SETTINGS flag.
-    // Flag 0x1, CLEAR_SETTINGS in SPDY3, is instead settings ACK in SPDY4.
+  if (spdy_util_.spdy_version() >= HTTP2) {
+    // HTTP/2 doesn't include settings persistence, or a CLEAR_SETTINGS flag.
+    // Flag 0x1, CLEAR_SETTINGS in SPDY3, is instead settings ACK in HTTP/2.
     return;
   }
   session_deps_.host_resolver->set_synchronous_mode(true);
@@ -1747,7 +1747,7 @@
       histogram_tester.ExpectBucketCount(
           "Net.SpdySynStreamCompressionPercentage", 30, 1);
       break;
-    case SPDY4:
+    case HTTP2:
       histogram_tester.ExpectBucketCount(
           "Net.SpdySynStreamCompressionPercentage", 81, 1);
       break;
@@ -4905,8 +4905,8 @@
 }
 
 TEST_P(SpdySessionTest, IgnoreReservedRemoteStreamsCount) {
-  // Streams in reserved remote state exist only in SPDY4.
-  if (spdy_util_.spdy_version() < SPDY4)
+  // Streams in reserved remote state exist only in HTTP/2.
+  if (spdy_util_.spdy_version() < HTTP2)
     return;
 
   scoped_ptr<SpdyFrame> push_a(spdy_util_.ConstructSpdyPush(
@@ -5005,8 +5005,8 @@
 }
 
 TEST_P(SpdySessionTest, CancelReservedStreamOnHeadersReceived) {
-  // Streams in reserved remote state exist only in SPDY4.
-  if (spdy_util_.spdy_version() < SPDY4)
+  // Streams in reserved remote state exist only in HTTP/2.
+  if (spdy_util_.spdy_version() < HTTP2)
     return;
 
   const char kPushedUrl[] = "http://www.example.org/a.dat";
diff --git a/net/spdy/spdy_stream_test_util.h b/net/spdy/spdy_stream_test_util.h
index 16f4efb2..66db00c 100644
--- a/net/spdy/spdy_stream_test_util.h
+++ b/net/spdy/spdy_stream_test_util.h
@@ -88,7 +88,7 @@
 // stream, e.g. its id when it was open.
 class StreamDelegateDoNothing : public StreamDelegateBase {
  public:
-  StreamDelegateDoNothing(const base::WeakPtr<SpdyStream>& stream);
+  explicit StreamDelegateDoNothing(const base::WeakPtr<SpdyStream>& stream);
   ~StreamDelegateDoNothing() override;
 };
 
@@ -123,7 +123,8 @@
 // Test delegate that closes stream in OnResponseHeadersUpdated().
 class StreamDelegateCloseOnHeaders : public StreamDelegateBase {
  public:
-  StreamDelegateCloseOnHeaders(const base::WeakPtr<SpdyStream>& stream);
+  explicit StreamDelegateCloseOnHeaders(
+      const base::WeakPtr<SpdyStream>& stream);
   ~StreamDelegateCloseOnHeaders() override;
 
   SpdyResponseHeadersStatus OnResponseHeadersUpdated(
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index b675374..555c202 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -990,7 +990,7 @@
                                            int stream_id,
                                            int associated_stream_id,
                                            const char* url) {
-  if (spdy_version() < SPDY4) {
+  if (spdy_version() < HTTP2) {
     SpdySynStreamIR syn_stream(stream_id);
     syn_stream.set_associated_to_stream_id(associated_stream_id);
     syn_stream.SetHeader("hello", "bye");
@@ -1034,7 +1034,7 @@
                                            const char* url,
                                            const char* status,
                                            const char* location) {
-  if (spdy_version() < SPDY4) {
+  if (spdy_version() < HTTP2) {
     SpdySynStreamIR syn_stream(stream_id);
     syn_stream.set_associated_to_stream_id(associated_stream_id);
     syn_stream.SetHeader("hello", "bye");
@@ -1077,7 +1077,7 @@
     scoped_ptr<SpdyHeaderBlock> headers,
     int stream_id,
     int associated_stream_id) {
-  if (spdy_version() < SPDY4) {
+  if (spdy_version() < HTTP2) {
     SpdySynStreamIR syn_stream(stream_id);
     syn_stream.set_associated_to_stream_id(associated_stream_id);
     SetPriority(LOWEST, &syn_stream);
diff --git a/net/spdy/spdy_test_utils.cc b/net/spdy/spdy_test_utils.cc
index 8ea16c8a..7f0d4e2 100644
--- a/net/spdy/spdy_test_utils.cc
+++ b/net/spdy/spdy_test_utils.cc
@@ -94,7 +94,7 @@
   switch (spdy_version) {
     case SPDY2:
     case SPDY3:
-    case SPDY4:
+    case HTTP2:
       frame->data()[4] = flags;
       break;
     default:
@@ -116,7 +116,7 @@
         memcpy(frame->data() + 5, reinterpret_cast<char*>(&wire_length) + 1, 3);
       }
       break;
-    case SPDY4:
+    case HTTP2:
       CHECK_GT(1u<<14, length);
       {
         int32 wire_length = base::HostToNet32(length);
diff --git a/net/tools/flip_server/sm_connection.cc b/net/tools/flip_server/sm_connection.cc
index 4acdea4..c132b8c 100644
--- a/net/tools/flip_server/sm_connection.cc
+++ b/net/tools/flip_server/sm_connection.cc
@@ -332,7 +332,7 @@
       if (!strncmp(reinterpret_cast<const char*>(npn_proto),
                    "spdy/4a2",
                    npn_proto_len)) {
-        *version_negotiated = SPDY4;
+        *version_negotiated = HTTP2;
         return true;
       }
     }
diff --git a/net/tools/flip_server/spdy_interface_test.cc b/net/tools/flip_server/spdy_interface_test.cc
index 6aa3d30..5f89a2b 100644
--- a/net/tools/flip_server/spdy_interface_test.cc
+++ b/net/tools/flip_server/spdy_interface_test.cc
@@ -205,10 +205,8 @@
   virtual ~SpdySMServerTest() {}
 };
 
-INSTANTIATE_TEST_CASE_P(SpdySMProxyTest,
-                        SpdySMProxyTest,
-                        Values(SPDY3, SPDY4));
-INSTANTIATE_TEST_CASE_P(SpdySMServerTest, SpdySMServerTest, Values(SPDY4));
+INSTANTIATE_TEST_CASE_P(SpdySMProxyTest, SpdySMProxyTest, Values(SPDY3, HTTP2));
+INSTANTIATE_TEST_CASE_P(SpdySMServerTest, SpdySMServerTest, Values(HTTP2));
 
 TEST_P(SpdySMProxyTest, InitSMConnection) {
   {
@@ -307,9 +305,9 @@
   ASSERT_EQ(interface_->spdy_version(), SPDY3);
 
   interface_->ResetForNewConnection();
-  interface_->CreateFramer(SPDY4);
+  interface_->CreateFramer(HTTP2);
   ASSERT_TRUE(interface_->spdy_framer() != NULL);
-  ASSERT_EQ(interface_->spdy_version(), SPDY4);
+  ASSERT_EQ(interface_->spdy_version(), HTTP2);
 }
 
 TEST_P(SpdySMProxyTest, PostAcceptHook) {
@@ -363,7 +361,7 @@
 
   {
     InSequence s;
-    if (GetParam() < SPDY4) {
+    if (GetParam() < HTTP2) {
       EXPECT_CALL(*spdy_framer_visitor_,
                   OnSynReply(stream_id, false, _))
           .WillOnce(SaveArg<2>(&actual_header_block));
@@ -443,7 +441,7 @@
 
   {
     InSequence s;
-    if (GetParam() < SPDY4) {
+    if (GetParam() < HTTP2) {
       EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
           .WillOnce(SaveArg<2>(&actual_header_block));
     } else {
@@ -569,7 +567,7 @@
 
   {
     InSequence s;
-    if (GetParam() < SPDY4) {
+    if (GetParam() < HTTP2) {
       EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
           .WillOnce(SaveArg<2>(&actual_header_block));
     } else {
diff --git a/net/tools/quic/quic_spdy_server_stream_test.cc b/net/tools/quic/quic_spdy_server_stream_test.cc
index b58a311..1a01bde 100644
--- a/net/tools/quic/quic_spdy_server_stream_test.cc
+++ b/net/tools/quic/quic_spdy_server_stream_test.cc
@@ -127,12 +127,12 @@
 
 QuicConsumedData ConsumeAllData(
     QuicStreamId id,
-    const IOVector& data,
+    const QuicIOVector& data,
     QuicStreamOffset offset,
     bool fin,
     FecProtection /*fec_protection_*/,
     QuicAckNotifier::DelegateInterface* /*ack_notifier_delegate*/) {
-  return QuicConsumedData(data.TotalBufferSize(), fin);
+  return QuicConsumedData(data.total_length, fin);
 }
 
 INSTANTIATE_TEST_CASE_P(Tests, QuicSpdyServerStreamTest,
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index b333dc1..14c5c8f0 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/compiler_specific.h"
 #include "base/location.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/power_monitor/power_monitor.h"
 #include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
@@ -17,6 +18,7 @@
 #include "net/base/auth.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/io_buffer.h"
+#include "net/base/load_flags.h"
 #include "net/base/load_states.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_delegate.h"
@@ -543,6 +545,24 @@
       }
       request_->set_status(status);
     }
+
+    // If the request succeeded (And wasn't cancelled) and the response code was
+    // 4xx or 5xx, record whether or not the main frame was blank.  This is
+    // intended to be a short-lived histogram, used to figure out how important
+    // fixing http://crbug.com/331745 is.
+    if (request_->status().is_success()) {
+      int response_code = GetResponseCode();
+      if (400 <= response_code && response_code <= 599) {
+        bool page_has_content = (postfilter_bytes_read_ != 0);
+        if (request_->load_flags() & net::LOAD_MAIN_FRAME) {
+          UMA_HISTOGRAM_BOOLEAN("Net.ErrorResponseHasContentMainFrame",
+                                page_has_content);
+        } else {
+          UMA_HISTOGRAM_BOOLEAN("Net.ErrorResponseHasContentNonMainFrame",
+                                page_has_content);
+        }
+      }
+    }
   }
 
   // Complete this notification later.  This prevents us from re-entering the
diff --git a/net/url_request/url_request_simple_job_unittest.cc b/net/url_request/url_request_simple_job_unittest.cc
index 1f8d5a4..439995a 100644
--- a/net/url_request/url_request_simple_job_unittest.cc
+++ b/net/url_request/url_request_simple_job_unittest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/bind_helpers.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
@@ -28,10 +29,6 @@
                       static_cast<int>(arraysize(kTestData) - 1),
               "invalid range");
 
-// This function does nothing.
-void DoNothing() {
-}
-
 class MockSimpleJob : public URLRequestSimpleJob {
  public:
   MockSimpleJob(URLRequest* request,
@@ -70,7 +67,7 @@
 
 class CancelURLRequestDelegate : public URLRequest::Delegate {
  public:
-  explicit CancelURLRequestDelegate()
+  CancelURLRequestDelegate()
       : buf_(new IOBuffer(kBufferSize)), run_loop_(new base::RunLoop) {}
 
   void OnResponseStarted(URLRequest* request) override {
@@ -222,8 +219,8 @@
   // Feed a dummy task to the SequencedTaskRunner to make sure that the
   // callbacks which are invoked in ReadRawData have completed safely.
   base::RunLoop run_loop;
-  EXPECT_TRUE(task_runner_->PostTaskAndReply(FROM_HERE, base::Bind(&DoNothing),
-                                             run_loop.QuitClosure()));
+  EXPECT_TRUE(task_runner_->PostTaskAndReply(
+      FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure()));
   run_loop.Run();
 }
 
diff --git a/ppapi/examples/video_encode/video_encode.cc b/ppapi/examples/video_encode/video_encode.cc
index c6a0921..7a55df2 100644
--- a/ppapi/examples/video_encode/video_encode.cc
+++ b/ppapi/examples/video_encode/video_encode.cc
@@ -6,6 +6,7 @@
 #include <stdio.h>
 #include <string.h>
 
+#include <algorithm>
 #include <iostream>
 #include <map>
 #include <sstream>
@@ -176,6 +177,8 @@
   pp::VideoFrame current_track_frame_;
 
   IVFWriter ivf_writer_;
+
+  PP_Time last_encode_tick_;
 };
 
 VideoEncoderInstance::VideoEncoderInstance(PP_Instance instance,
@@ -189,7 +192,8 @@
       video_profile_(PP_VIDEOPROFILE_H264MAIN),
 #endif
       frame_format_(PP_VIDEOFRAME_FORMAT_I420),
-      encoded_frames_(0) {
+      encoded_frames_(0),
+      last_encode_tick_(0) {
   InitializeVideoProfiles();
   ProbeEncoder();
 }
@@ -339,10 +343,12 @@
 }
 
 void VideoEncoderInstance::ScheduleNextEncode() {
+  PP_Time now = pp::Module::Get()->core()->GetTime();
   pp::Module::Get()->core()->CallOnMainThread(
-      1000 / 30,
+      std::min(std::max(now - last_encode_tick_, 0.0), 1000.0 / 30),
       callback_factory_.NewCallback(&VideoEncoderInstance::GetEncoderFrameTick),
       0);
+  last_encode_tick_ = now;
 }
 
 void VideoEncoderInstance::GetEncoderFrameTick(int32_t result) {
diff --git a/remoting/remoting_webapp_files.gypi b/remoting/remoting_webapp_files.gypi
index abae9774..9db4c63f 100644
--- a/remoting/remoting_webapp_files.gypi
+++ b/remoting/remoting_webapp_files.gypi
@@ -94,6 +94,7 @@
       'webapp/base/js/typecheck_unittest.js',
       'webapp/base/js/viewport_unittest.js',
       'webapp/base/js/window_shape_unittest.js',
+      'webapp/base/js/xhr_event_writer_unittest.js',
       'webapp/base/js/xhr_unittest.js',
       'webapp/base/js/xmpp_connection_unittest.js',
       'webapp/base/js/xmpp_login_handler_unittest.js',
@@ -181,6 +182,7 @@
       'webapp/base/js/plugin_settings.js',
       'webapp/base/js/suspend_detector.js',
       'webapp/base/js/typecheck.js',
+      'webapp/base/js/xhr_event_writer.js',
       'webapp/base/js/xhr.js',
     ],
     # Host JavaScript files.
diff --git a/remoting/webapp/base/js/xhr.js b/remoting/webapp/base/js/xhr.js
index a0fe8ed..ab2442f 100644
--- a/remoting/webapp/base/js/xhr.js
+++ b/remoting/webapp/base/js/xhr.js
@@ -395,6 +395,7 @@
   if (!base.isOnline()) {
     this.deferred_.reject(
         new remoting.Error(remoting.Error.Tag.NETWORK_FAILURE));
+    return;
   }
 
   var that = this;
diff --git a/remoting/webapp/base/js/xhr_event_writer.js b/remoting/webapp/base/js/xhr_event_writer.js
new file mode 100644
index 0000000..21ab587
--- /dev/null
+++ b/remoting/webapp/base/js/xhr_event_writer.js
@@ -0,0 +1,135 @@
+// 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.
+
+/** @suppress {duplicate} */
+var remoting = remoting || {};
+
+(function() {
+
+'use strict';
+
+/**
+ * A class that writes log events to our back-end using XHR.
+ * If a log request fails due to network failure, it will be stored to
+ * |storage| for retrying in the future by calling flush().
+ *
+ * @param {!string} url
+ * @param {!StorageArea} storage
+ * @param {!string} storageKey
+ * @constructor
+ */
+remoting.XhrEventWriter = function(url, storage, storageKey) {
+  /** @private */
+  this.url_ = url;
+  /** @private */
+  this.storage_ = storage;
+  /** @private @const */
+  this.storageKey_ = storageKey;
+  /** @private */
+  this.pendingRequests_ = new Map();
+};
+
+/**
+ * @return {Promise} A promise that resolves when initialization is completed.
+ */
+remoting.XhrEventWriter.prototype.loadPendingRequests = function() {
+  var that = this;
+  var deferred = new base.Deferred();
+  this.storage_.get(this.storageKey_, function(entry) {
+    try {
+      that.pendingRequests_ = new Map(entry[that.storageKey_]);
+    } catch(e) {
+      that.pendingRequests_ = new Map();
+    }
+    deferred.resolve(that.pendingRequests_);
+  });
+  return deferred.promise();
+};
+
+/**
+ * @param {Object} event  The event to be written to the server.
+ * @return {Promise} A promise that resolves on success.
+ */
+remoting.XhrEventWriter.prototype.write = function(event) {
+  this.markPending_(event);
+  return this.flush();
+};
+
+/**
+ * @return {Promise} A promise that resolves on success.
+ */
+remoting.XhrEventWriter.prototype.flush = function() {
+  var promises = /** @type {!Array<!Promise>} */ ([]);
+  var that = this;
+  // Map.forEach enumerates the entires of the map in insertion order.
+  this.pendingRequests_.forEach(
+    function(/** Object */ event, /** string */ requestId) {
+      promises.push(that.doXhr_(requestId, event));
+    });
+  return Promise.all(promises);
+};
+
+/**
+ * @return {Promise} A promise that resolves when the pending requests are
+ *     written to disk.
+ */
+remoting.XhrEventWriter.prototype.writeToStorage = function() {
+  var deferred = new base.Deferred();
+  var map = [];
+  this.pendingRequests_.forEach(
+    function(/** Object */ request, /** string */ id) {
+      map.push([id, request]);
+    });
+
+  var entry = {};
+  entry[this.storageKey_] = map;
+  this.storage_.set(entry, deferred.resolve.bind(deferred));
+  return deferred.promise();
+};
+
+/**
+ * @param {string} requestId
+ * @param {Object} event
+ * @return {Promise}
+ * @private
+ */
+remoting.XhrEventWriter.prototype.doXhr_ = function(requestId, event) {
+  var that = this;
+  var XHR_RETRY_ATTEMPTS = 20;
+  var xhr = new remoting.AutoRetryXhr(
+      {method: 'POST', url: this.url_, jsonContent: event}, XHR_RETRY_ATTEMPTS);
+  return xhr.start().then(function(response) {
+    var error = remoting.Error.fromHttpStatus(response.status);
+    // Only store requests that are failed with NETWORK_FAILURE, so that
+    // malformed requests won't be stuck in the client forever.
+    if (!error.hasTag(remoting.Error.Tag.NETWORK_FAILURE)) {
+      that.pendingRequests_.delete(requestId);
+    }
+    if (!error.isNone()) {
+      throw error;
+    }
+  });
+};
+
+/**
+ * @param {number} length
+ * @return {string} A random string of length |length|
+ */
+function randomString(length) {
+  var random = new Uint8Array(length);
+  window.crypto.getRandomValues(random);
+  return window.btoa(String.fromCharCode.apply(null, random));
+}
+
+/**
+ * @param {Object} event
+ * @private
+ */
+remoting.XhrEventWriter.prototype.markPending_ = function(event) {
+  var requestId = Date.now() + '_' + randomString(16);
+  base.debug.assert(!this.pendingRequests_.has(requestId));
+  this.pendingRequests_.set(requestId, event);
+};
+
+})();
diff --git a/remoting/webapp/base/js/xhr_event_writer_unittest.js b/remoting/webapp/base/js/xhr_event_writer_unittest.js
new file mode 100644
index 0000000..9aa1b807
--- /dev/null
+++ b/remoting/webapp/base/js/xhr_event_writer_unittest.js
@@ -0,0 +1,139 @@
+// 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.
+
+(function() {
+
+'use strict';
+
+/** @type {remoting.XhrEventWriter} */
+var eventWriter = null;
+/** @type {!chromeMocks.StorageArea} */
+var mockStorage;
+/** @type {sinon.TestStub} */
+var isOnlineStub;
+
+/**
+ * Flush the writer and ensure no outgoing requests is made.
+ *
+ * @param {QUnit.Assert} assert
+ * @param {remoting.XhrEventWriter} writer
+ * @return {Promise}
+ */
+function flushAndEnsureNoRequests(assert, writer) {
+  var requestReceived = false;
+  remoting.MockXhr.setResponseFor(
+    'POST', 'fake_url', function(/** remoting.MockXhr */ xhr) {
+      xhr.setTextResponse(200, '');
+      requestReceived = true;
+    });
+  return writer.flush().then(function(){
+    assert.ok(!requestReceived);
+  });
+}
+
+QUnit.module('XhrEventWriter', {
+  beforeEach: function() {
+    remoting.MockXhr.activate();
+    mockStorage = new chromeMocks.StorageArea();
+    eventWriter = new remoting.XhrEventWriter(
+        'fake_url', mockStorage, 'fake-storage-key');
+    isOnlineStub = sinon.stub(base, 'isOnline');
+  },
+  afterEach: function() {
+    isOnlineStub.restore();
+    remoting.MockXhr.restore();
+  }
+});
+
+QUnit.test('loadPendingRequests() handles empty storage.', function(assert){
+  return eventWriter.loadPendingRequests().then(function(){
+    return flushAndEnsureNoRequests(assert, eventWriter);
+  });
+});
+
+QUnit.test('loadPendingRequests() handles corrupted data.', function(assert){
+  var storage = mockStorage.mock$getStorage();
+  storage['fake-storage-key'] = 'corrupted_data';
+  return eventWriter.loadPendingRequests().then(function(){
+    return flushAndEnsureNoRequests(assert, eventWriter);
+  });
+});
+
+QUnit.test('write() should post XHR to server.', function(assert){
+  isOnlineStub.returns(true);
+  var requestReceived = false;
+  remoting.MockXhr.setResponseFor(
+    'POST', 'fake_url', function(/** remoting.MockXhr */ xhr) {
+      assert.deepEqual(xhr.params.jsonContent, {hello: 'world'});
+      xhr.setTextResponse(200, '');
+      requestReceived = true;
+    });
+
+  return eventWriter.write({ hello: 'world'}).then(function(){
+    assert.ok(requestReceived);
+  });
+});
+
+QUnit.test('flush() should retry requests if OFFLINE.', function(assert){
+  var requestReceived = false;
+  isOnlineStub.returns(false);
+
+  return eventWriter.write({ hello: 'world'}).then(function(){
+    assert.ok(false, 'Expect to fail.');
+  }).catch(function() {
+    isOnlineStub.returns(true);
+    remoting.MockXhr.setResponseFor(
+      'POST', 'fake_url', function(/** remoting.MockXhr */ xhr) {
+        assert.deepEqual(xhr.params.jsonContent, {hello: 'world'});
+        requestReceived = true;
+        xhr.setTextResponse(200, '');
+      });
+    return eventWriter.flush();
+  }).then(function() {
+    assert.ok(requestReceived);
+  });
+});
+
+QUnit.test('flush() should not retry on server error.', function(assert){
+  isOnlineStub.returns(true);
+  remoting.MockXhr.setResponseFor(
+    'POST', 'fake_url', function(/** remoting.MockXhr */ xhr) {
+    assert.deepEqual(xhr.params.jsonContent, {hello: 'world'});
+    xhr.setTextResponse(500, '');
+  });
+
+  return eventWriter.write({ hello: 'world'}).then(function(){
+    assert.ok(false, 'Expect to fail.');
+  }).catch(function() {
+    return flushAndEnsureNoRequests(assert, eventWriter);
+  });
+});
+
+QUnit.test('writeToStorage() should save pending requests.', function(assert){
+  var requestReceived = false;
+  var newEventWriter = new remoting.XhrEventWriter(
+      'fake_url', mockStorage, 'fake-storage-key');
+  isOnlineStub.returns(false);
+
+  return eventWriter.write({ hello: 'world'}).then(function(){
+    assert.ok(false, 'Expect to fail.');
+  }).catch(function(){
+    return eventWriter.writeToStorage();
+  }).then(function() {
+    return newEventWriter.loadPendingRequests();
+  }).then(function() {
+    isOnlineStub.returns(true);
+    remoting.MockXhr.setResponseFor(
+      'POST', 'fake_url', function(/** remoting.MockXhr */ xhr) {
+      assert.deepEqual(xhr.params.jsonContent, {hello: 'world'});
+      requestReceived = true;
+      xhr.setTextResponse(200, '');
+    });
+    return newEventWriter.flush();
+  }).then(function(){
+    assert.ok(requestReceived);
+  });
+});
+
+})();
diff --git a/remoting/webapp/files.gni b/remoting/webapp/files.gni
index 39bc081..7640a43 100644
--- a/remoting/webapp/files.gni
+++ b/remoting/webapp/files.gni
@@ -90,6 +90,7 @@
   "base/js/typecheck_unittest.js",
   "base/js/viewport_unittest.js",
   "base/js/window_shape_unittest.js",
+  "base/js/xhr_event_writer_unittest.js",
   "base/js/xhr_unittest.js",
   "base/js/xmpp_connection_unittest.js",
   "base/js/xmpp_login_handler_unittest.js",
@@ -176,6 +177,7 @@
   "base/js/plugin_settings.js",
   "base/js/suspend_detector.js",
   "base/js/typecheck.js",
+  "base/js/xhr_event_writer.js",
   "base/js/xhr.js",
 ]
 
diff --git a/remoting/webapp/js_proto/chrome_mocks.js b/remoting/webapp/js_proto/chrome_mocks.js
index 492e7fe..9aa5517 100644
--- a/remoting/webapp/js_proto/chrome_mocks.js
+++ b/remoting/webapp/js_proto/chrome_mocks.js
@@ -123,32 +123,34 @@
 
 // Sample implementation of chrome.StorageArea according to
 // https://developer.chrome.com/apps/storage#type-StorageArea
-/** @constructor */
+/**
+ * @constructor
+ * @extends {StorageArea}
+ */
 chromeMocks.StorageArea = function() {
-  /** @type {Object} */
+  /** @type {!Object} */
   this.storage_ = {};
 };
 
 /**
- * @param {!Object} keys
+ * @param {Object|string} keys
  * @return {Array<string>}
  */
 function getKeys(keys) {
   if (typeof keys === 'string') {
     return [keys];
   } else if (typeof keys === 'object') {
-    return Object.keys(keys);
+    var objectKeys = /** @type {!Object} */ (keys);
+    return Object.keys(objectKeys);
   }
   return [];
 }
 
-/**
- * @param {!Object} keys
- * @param {Function} onDone
- */
 chromeMocks.StorageArea.prototype.get = function(keys, onDone) {
   if (!keys) {
-    onDone(base.deepCopy(this.storage_));
+    // No keys are specified, returns the entire storage.
+    var storageCopy = base.deepCopy(this.storage_);
+    onDone(/** @type {Object} */ (storageCopy));
     return;
   }
 
@@ -163,26 +165,33 @@
   onDone(result);
 };
 
-/** @param {Object} value */
-chromeMocks.StorageArea.prototype.set = function(value) {
+chromeMocks.StorageArea.prototype.set = function(value, opt_onDone) {
   for (var key in value) {
     this.storage_[key] = base.deepCopy(value[key]);
   }
+  if (opt_onDone) {
+    opt_onDone();
+  }
 };
 
-/**
- * @param {!Object} keys
- */
-chromeMocks.StorageArea.prototype.remove = function(keys) {
+chromeMocks.StorageArea.prototype.remove = function(keys, opt_onDone) {
   getKeys(keys).forEach(
       /** @param {string} key */
       function(key) {
         delete this.storage_[key];
       }, this);
+  if (opt_onDone) {
+    opt_onDone();
+  }
+};
+
+/** @return {!Object} */
+chromeMocks.StorageArea.prototype.mock$getStorage = function() {
+  return this.storage_;
 };
 
 chromeMocks.StorageArea.prototype.clear = function() {
-  this.storage_ = null;
+  this.storage_ = {};
 };
 
 /** @type {Object} */
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
index 35bb4dc..dc62f1b 100644
--- a/sandbox/linux/services/credentials.cc
+++ b/sandbox/linux/services/credentials.cc
@@ -296,4 +296,15 @@
   return true;
 }
 
+pid_t Credentials::ForkAndDropCapabilitiesInChild() {
+  pid_t pid = fork();
+  if (pid != 0) {
+    return pid;
+  }
+
+  // Since we just forked, we are single threaded.
+  PCHECK(DropAllCapabilitiesOnCurrentThread());
+  return 0;
+}
+
 }  // namespace sandbox.
diff --git a/sandbox/linux/services/credentials.h b/sandbox/linux/services/credentials.h
index 0001dc7..095d636 100644
--- a/sandbox/linux/services/credentials.h
+++ b/sandbox/linux/services/credentials.h
@@ -95,6 +95,9 @@
   //   - DropAllCapabilities() must be called to prevent escapes.
   static bool DropFileSystemAccess(int proc_fd) WARN_UNUSED_RESULT;
 
+  // Forks and drops capabilities in the child.
+  static pid_t ForkAndDropCapabilitiesInChild();
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(Credentials);
 };
diff --git a/sandbox/linux/services/namespace_sandbox.cc b/sandbox/linux/services/namespace_sandbox.cc
index 2379644..4b2e588 100644
--- a/sandbox/linux/services/namespace_sandbox.cc
+++ b/sandbox/linux/services/namespace_sandbox.cc
@@ -24,6 +24,8 @@
 #include "base/process/process.h"
 #include "sandbox/linux/services/credentials.h"
 #include "sandbox/linux/services/namespace_utils.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
 
 namespace sandbox {
 
@@ -65,6 +67,7 @@
   // An empty string causes the env var to be unset in the child process.
   (*environ)[env_var] = value ? "1" : "";
 }
+#endif  // !defined(OS_NACL_NONSFI)
 
 // Linux supports up to 64 signals. This should be updated if that ever changes.
 int g_signal_exit_codes[64];
@@ -77,9 +80,8 @@
     _exit(g_signal_exit_codes[sig_idx]);
   }
 
-  _exit(NamespaceSandbox::kDefaultExitCode);
+  _exit(NamespaceSandbox::SignalExitCode(sig));
 }
-#endif  // !defined(OS_NACL_NONSFI)
 
 }  // namespace
 
@@ -129,11 +131,12 @@
 
   return base::LaunchProcess(argv, launch_options);
 }
+#endif  // !defined(OS_NACL_NONSFI)
 
 // static
 pid_t NamespaceSandbox::ForkInNewPidNamespace(bool drop_capabilities_in_child) {
   const pid_t pid =
-      base::ForkWithFlags(CLONE_NEWPID | SIGCHLD, nullptr, nullptr);
+      base::ForkWithFlags(CLONE_NEWPID | LINUX_SIGCHLD, nullptr, nullptr);
   if (pid < 0) {
     return pid;
   }
@@ -153,11 +156,12 @@
 // static
 void NamespaceSandbox::InstallDefaultTerminationSignalHandlers() {
   static const int kDefaultTermSignals[] = {
-      SIGHUP, SIGINT, SIGABRT, SIGQUIT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2,
+      LINUX_SIGHUP,  LINUX_SIGINT,  LINUX_SIGABRT, LINUX_SIGQUIT,
+      LINUX_SIGPIPE, LINUX_SIGTERM, LINUX_SIGUSR1, LINUX_SIGUSR2,
   };
 
   for (const int sig : kDefaultTermSignals) {
-    InstallTerminationSignalHandler(sig, kDefaultExitCode);
+    InstallTerminationSignalHandler(sig, SignalExitCode(sig));
   }
 }
 
@@ -166,12 +170,16 @@
     int sig,
     int exit_code) {
   struct sigaction old_action;
-  PCHECK(sigaction(sig, nullptr, &old_action) == 0);
+  PCHECK(sys_sigaction(sig, nullptr, &old_action) == 0);
 
+#if !defined(OS_NACL_NONSFI)
   if (old_action.sa_flags & SA_SIGINFO &&
       old_action.sa_sigaction != nullptr) {
     return false;
-  } else if (old_action.sa_handler != SIG_DFL) {
+  }
+#endif
+
+  if (old_action.sa_handler != LINUX_SIG_DFL) {
     return false;
   }
 
@@ -185,10 +193,9 @@
 
   struct sigaction action = {};
   action.sa_handler = &TerminationSignalHandler;
-  PCHECK(sigaction(sig, &action, nullptr) == 0);
+  PCHECK(sys_sigaction(sig, &action, nullptr) == 0);
   return true;
 }
-#endif  // !defined(OS_NACL_NONSFI)
 
 // static
 bool NamespaceSandbox::InNewUserNamespace() {
diff --git a/sandbox/linux/services/namespace_sandbox.h b/sandbox/linux/services/namespace_sandbox.h
index 80097fb1..977650e 100644
--- a/sandbox/linux/services/namespace_sandbox.h
+++ b/sandbox/linux/services/namespace_sandbox.h
@@ -38,8 +38,6 @@
 class SANDBOX_EXPORT NamespaceSandbox {
  public:
 #if !defined(OS_NACL_NONSFI)
-  static const int kDefaultExitCode = 1;
-
   // Launch a new process inside its own user/PID/network namespaces (depending
   // on kernel support). Requires at a minimum that user namespaces are
   // supported (use Credentials::CanCreateProcessInNewUserNS to check this).
@@ -51,6 +49,7 @@
                                      const base::LaunchOptions& options);
   static base::Process LaunchProcess(const std::vector<std::string>& argv,
                                      const base::LaunchOptions& options);
+#endif  // !defined(OS_NACL_NONSFI)
 
   // Forks a process in its own PID namespace. The child process is the init
   // process inside of the PID namespace, so if the child needs to fork further,
@@ -70,8 +69,8 @@
   //
   // SIGHUP, SIGINT, SIGABRT, SIGQUIT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2
   //
-  // that exits with kDefaultExitCode. These are signals whose default action is
-  // to terminate the program (apart from SIGILL, SIGFPE, and SIGSEGV, which
+  // that exits with SignalExitCode(sig). These are signals whose default action
+  // is to terminate the program (apart from SIGILL, SIGFPE, and SIGSEGV, which
   // will still terminate the process if e.g. an illegal instruction is
   // encountered, etc.).
   //
@@ -83,7 +82,10 @@
   // signal handler was already present for |sig|, does nothing and returns
   // false.
   static bool InstallTerminationSignalHandler(int sig, int exit_code);
-#endif  // !defined(OS_NACL_NONSFI)
+
+  // Returns an exit code corresponding to the process being killed by sig. This
+  // is the same as exit code that NaCl's default signal handler uses.
+  static int SignalExitCode(int sig) { return -sig & 0xff; }
 
   // Returns whether the namespace sandbox created a new user, PID, and network
   // namespace. In particular, InNewUserNamespace should return true iff the
diff --git a/sandbox/linux/services/namespace_sandbox_unittest.cc b/sandbox/linux/services/namespace_sandbox_unittest.cc
index 547ef672..3b22e841 100644
--- a/sandbox/linux/services/namespace_sandbox_unittest.cc
+++ b/sandbox/linux/services/namespace_sandbox_unittest.cc
@@ -120,7 +120,6 @@
 }
 
 const int kNormalExitCode = 0;
-const int kSignalTerminationExitCode = 255;
 
 // Ensure that CHECK(false) is distinguishable from _exit(kNormalExitCode).
 // Allowing noise since CHECK(false) will write a stack trace to stderr.
@@ -182,7 +181,7 @@
     CHECK_EQ(1, getpid());
     CHECK(!Credentials::HasAnyCapability());
     CHECK(NamespaceSandbox::InstallTerminationSignalHandler(
-        SIGTERM, kSignalTerminationExitCode));
+        SIGTERM, NamespaceSandbox::SignalExitCode(SIGTERM)));
     while (true) {
       raise(SIGTERM);
     }
@@ -191,7 +190,7 @@
   int status;
   PCHECK(waitpid(pid, &status, 0) == pid);
   CHECK(WIFEXITED(status));
-  CHECK_EQ(kSignalTerminationExitCode, WEXITSTATUS(status));
+  CHECK_EQ(NamespaceSandbox::SignalExitCode(SIGTERM), WEXITSTATUS(status));
 }
 
 volatile sig_atomic_t signal_handler_called;
@@ -206,7 +205,7 @@
 
   NamespaceSandbox::InstallDefaultTerminationSignalHandlers();
   CHECK(!NamespaceSandbox::InstallTerminationSignalHandler(
-            SIGUSR1, kSignalTerminationExitCode));
+            SIGUSR1, NamespaceSandbox::SignalExitCode(SIGUSR1)));
 
   raise(SIGUSR1);
   CHECK_EQ(1, signal_handler_called);
diff --git a/sandbox/linux/services/scoped_process_unittest.cc b/sandbox/linux/services/scoped_process_unittest.cc
index 8bd2847..86f97a8 100644
--- a/sandbox/linux/services/scoped_process_unittest.cc
+++ b/sandbox/linux/services/scoped_process_unittest.cc
@@ -12,6 +12,7 @@
 #include <unistd.h>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
@@ -35,8 +36,6 @@
   _exit(0);
 }
 
-void DoNothing() {}
-
 TEST(ScopedProcess, ScopedProcessNormalExit) {
   const int kCustomExitCode = 12;
   ScopedProcess process(base::Bind(&ExitWithCode, kCustomExitCode));
@@ -64,7 +63,7 @@
 }
 
 TEST(ScopedProcess, ScopedProcessSignaled) {
-  ScopedProcess process(base::Bind(&DoNothing));
+  ScopedProcess process(base::Bind(&base::DoNothing));
   bool got_signaled = false;
   ASSERT_EQ(0, kill(process.GetPid(), SIGKILL));
   int exit_code = process.WaitForExit(&got_signaled);
@@ -92,7 +91,7 @@
 }
 
 TEST(ScopedProcess, SynchronizationBasic) {
-  ScopedProcess process1(base::Bind(&DoNothing));
+  ScopedProcess process1(base::Bind(&base::DoNothing));
   EXPECT_TRUE(process1.WaitForClosureToRun());
 
   ScopedProcess process2(base::Bind(&DoExit));
diff --git a/sandbox/linux/services/syscall_wrappers.cc b/sandbox/linux/services/syscall_wrappers.cc
index 264eb68..1984288 100644
--- a/sandbox/linux/services/syscall_wrappers.cc
+++ b/sandbox/linux/services/syscall_wrappers.cc
@@ -141,9 +141,12 @@
 
 int sys_sigprocmask(int how, const sigset_t* set, decltype(nullptr) oldset) {
   // In some toolchain (in particular Android and PNaCl toolchain),
-  // sigset_t is 32 bits, but Linux ABI requires 64 bits.
-  uint64_t linux_value = 0;
-  std::memcpy(&linux_value, set, std::min(sizeof(sigset_t), sizeof(uint64_t)));
+  // sigset_t is 32 bits, but the Linux ABI uses more.
+  LinuxSigSet linux_value;
+  std::memset(&linux_value, 0, sizeof(LinuxSigSet));
+  std::memcpy(&linux_value, set, std::min(sizeof(sigset_t),
+                                          sizeof(LinuxSigSet)));
+
   return syscall(__NR_rt_sigprocmask, how, &linux_value, nullptr,
                  sizeof(linux_value));
 }
@@ -186,14 +189,6 @@
   return sigaction(signum, act, oldact);
 }
 #else
-// struct sigaction is different ABI from the Linux's.
-struct KernelSigAction {
-  void (*kernel_handler)(int);
-  uint32_t sa_flags;
-  void (*sa_restorer)(void);
-  uint64_t sa_mask;
-};
-
 // On X86_64 arch, it is necessary to set sa_restorer always.
 #if defined(ARCH_CPU_X86_64)
 #if !defined(SA_RESTORER)
@@ -213,30 +208,32 @@
 int sys_sigaction(int signum,
                   const struct sigaction* act,
                   struct sigaction* oldact) {
-  KernelSigAction kernel_act = {};
+  LinuxSigAction linux_act = {};
   if (act) {
-    kernel_act.kernel_handler = act->sa_handler;
-    std::memcpy(&kernel_act.sa_mask, &act->sa_mask,
-                std::min(sizeof(kernel_act.sa_mask), sizeof(act->sa_mask)));
-    kernel_act.sa_flags = act->sa_flags;
+    linux_act.kernel_handler = act->sa_handler;
+    std::memcpy(&linux_act.sa_mask, &act->sa_mask,
+                std::min(sizeof(linux_act.sa_mask), sizeof(act->sa_mask)));
+    linux_act.sa_flags = act->sa_flags;
 
 #if defined(ARCH_CPU_X86_64)
-    if (!(kernel_act.sa_flags & SA_RESTORER)) {
-      kernel_act.sa_flags |= SA_RESTORER;
-      kernel_act.sa_restorer = sys_rt_sigreturn;
+    if (!(linux_act.sa_flags & SA_RESTORER)) {
+      linux_act.sa_flags |= SA_RESTORER;
+      linux_act.sa_restorer = sys_rt_sigreturn;
     }
 #endif
   }
 
-  KernelSigAction kernel_oldact = {};
-  int result = syscall(__NR_rt_sigaction, signum, act ? &kernel_act : nullptr,
-                       oldact ? &kernel_oldact : nullptr, sizeof(uint64_t));
+  LinuxSigAction linux_oldact = {};
+  int result = syscall(__NR_rt_sigaction, signum, act ? &linux_act : nullptr,
+                       oldact ? &linux_oldact : nullptr,
+                       sizeof(LinuxSigSet));
+
   if (result == 0 && oldact) {
-    oldact->sa_handler = kernel_oldact.kernel_handler;
+    oldact->sa_handler = linux_oldact.kernel_handler;
     sigemptyset(&oldact->sa_mask);
-    std::memcpy(&oldact->sa_mask, &kernel_oldact.sa_mask,
-                std::min(sizeof(kernel_act.sa_mask), sizeof(act->sa_mask)));
-    oldact->sa_flags = kernel_oldact.sa_flags;
+    std::memcpy(&oldact->sa_mask, &linux_oldact.sa_mask,
+                std::min(sizeof(linux_act.sa_mask), sizeof(act->sa_mask)));
+    oldact->sa_flags = linux_oldact.sa_flags;
   }
   return result;
 }
diff --git a/sandbox/linux/services/yama_unittests.cc b/sandbox/linux/services/yama_unittests.cc
index 204cfd6..398eafe 100644
--- a/sandbox/linux/services/yama_unittests.cc
+++ b/sandbox/linux/services/yama_unittests.cc
@@ -10,6 +10,7 @@
 #include <unistd.h>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/strings/string_util.h"
@@ -151,14 +152,12 @@
   }
 }
 
-void DoNothing() {}
-
 SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
   if (!Yama::IsPresent() || HasLinux32Bug())
     return;
 
   CHECK(Yama::DisableYamaRestrictions());
-  ScopedProcess process1(base::Bind(&DoNothing));
+  ScopedProcess process1(base::Bind(&base::DoNothing));
 
   if (Yama::IsEnforcing()) {
     // Check that process1 is protected by Yama, even though it has
diff --git a/sandbox/linux/system_headers/linux_signal.h b/sandbox/linux/system_headers/linux_signal.h
index 5db7fc5..2aa84196 100644
--- a/sandbox/linux/system_headers/linux_signal.h
+++ b/sandbox/linux/system_headers/linux_signal.h
@@ -8,6 +8,27 @@
 // NOTE: On some toolchains, signal related ABI is incompatible with Linux's
 // (not undefined, but defined different values and in different memory
 // layouts). So, fill the gap here.
+#define LINUX_SIGHUP 1
+#define LINUX_SIGINT 2
+#define LINUX_SIGQUIT 3
+#define LINUX_SIGABRT 6
+#define LINUX_SIGBUS 7
+#define LINUX_SIGUSR1 10
+#define LINUX_SIGSEGV 11
+#define LINUX_SIGUSR2 12
+#define LINUX_SIGPIPE 13
+#define LINUX_SIGTERM 15
+#define LINUX_SIGCHLD 17
+#define LINUX_SIGSYS 31
+
+#define LINUX_SIG_BLOCK 0
+#define LINUX_SIG_UNBLOCK 1
+
+#define LINUX_SA_SIGINFO 4
+#define LINUX_SA_NODEFER 0x40000000
+#define LINUX_SA_RESTART 0x10000000
+
+#define LINUX_SIG_DFL 0
 
 #if defined(__native_client_nonsfi__)
 #if !defined(__i386__) && !defined(__arm__)
@@ -16,20 +37,6 @@
 
 #include <signal.h>
 
-#define LINUX_SIGBUS 7    // 10 in PNaCl toolchain.
-#define LINUX_SIGSEGV 11  // 11 in PNaCl toolchain. Defined for consistency.
-#define LINUX_SIGCHLD 17  // 20 in PNaCl toolchain.
-#define LINUX_SIGSYS 31   // 12 in PNaCl toolchain.
-
-#define LINUX_SIG_BLOCK 0    // 1 in PNaCl toolchain.
-#define LINUX_SIG_UNBLOCK 1  // 2 in PNaCl toolchain.
-
-#define LINUX_SA_SIGINFO 4           // 2 in PNaCl toolchain.
-#define LINUX_SA_NODEFER 0x40000000  // Undefined in PNaCl toolchain.
-#define LINUX_SA_RESTART 0x10000000  // Undefined in PNaCl toolchain.
-
-#define LINUX_SIG_DFL 0  // In PNaCl toolchain, unneeded cast is applied.
-
 struct LinuxSigInfo {
   int si_signo;
   int si_errno;
@@ -44,22 +51,27 @@
 
 #else  // !defined(__native_client_nonsfi__)
 
-// Just alias the toolchain's value.
 #include <signal.h>
 
-#define LINUX_SIGBUS SIGBUS
-#define LINUX_SIGSEGV SIGSEGV
-#define LINUX_SIGCHLD SIGCHLD
-#define LINUX_SIGSYS SIGSYS
-
-#define LINUX_SIG_BLOCK SIG_BLOCK
-#define LINUX_SIG_UNBLOCK SIG_UNBLOCK
-
-#define LINUX_SA_SIGINFO SA_SIGINFO
-#define LINUX_SA_NODEFER SA_NODEFER
-#define LINUX_SA_RESTART SA_RESTART
-
-#define LINUX_SIG_DFL SIG_DFL
+static_assert(LINUX_SIGHUP == SIGHUP, "LINUX_SIGHUP == SIGHUP");
+static_assert(LINUX_SIGINT == SIGINT, "LINUX_SIGINT == SIGINT");
+static_assert(LINUX_SIGQUIT == SIGQUIT, "LINUX_SIGQUIT == SIGQUIT");
+static_assert(LINUX_SIGABRT == SIGABRT, "LINUX_SIGABRT == SIGABRT");
+static_assert(LINUX_SIGBUS == SIGBUS, "LINUX_SIGBUS == SIGBUS");
+static_assert(LINUX_SIGUSR1 == SIGUSR1, "LINUX_SIGUSR1 == SIGUSR1");
+static_assert(LINUX_SIGSEGV == SIGSEGV, "LINUX_SIGSEGV == SIGSEGV");
+static_assert(LINUX_SIGUSR2 == SIGUSR2, "LINUX_SIGUSR2 == SIGUSR2");
+static_assert(LINUX_SIGPIPE == SIGPIPE, "LINUX_SIGPIPE == SIGPIPE");
+static_assert(LINUX_SIGTERM == SIGTERM, "LINUX_SIGTERM == SIGTERM");
+static_assert(LINUX_SIGCHLD == SIGCHLD, "LINUX_SIGCHLD == SIGCHLD");
+static_assert(LINUX_SIGSYS == SIGSYS, "LINUX_SIGSYS == SIGSYS");
+static_assert(LINUX_SIG_BLOCK == SIG_BLOCK, "LINUX_SIG_BLOCK == SIG_BLOCK");
+static_assert(LINUX_SIG_UNBLOCK == SIG_UNBLOCK,
+              "LINUX_SIG_UNBLOCK == SIG_UNBLOCK");
+static_assert(LINUX_SA_SIGINFO == SA_SIGINFO, "LINUX_SA_SIGINFO == SA_SIGINFO");
+static_assert(LINUX_SA_NODEFER == SA_NODEFER, "LINUX_SA_NODEFER == SA_NODEFER");
+static_assert(LINUX_SA_RESTART == SA_RESTART, "LINUX_SA_RESTART == SA_RESTART");
+static_assert(LINUX_SIG_DFL == SIG_DFL, "LINUX_SIG_DFL == SIG_DFL");
 
 typedef siginfo_t LinuxSigInfo;
 
@@ -70,4 +82,32 @@
 
 #endif  // !defined(__native_client_nonsfi__)
 
+// struct sigset_t is different size in PNaCl from the Linux's.
+#if defined(__mips__)
+#if !defined(_NSIG_WORDS)
+#define _NSIG_WORDS 4
+#endif
+struct LinuxSigSet {
+  unsigned long sig[_NSIG_WORDS];
+};
+#else
+typedef uint64_t LinuxSigSet;
+#endif
+
+// struct sigaction is different in PNaCl from the Linux's.
+#if defined(__mips__)
+struct LinuxSigAction {
+  unsigned int sa_flags;
+  void (*kernel_handler)(int);
+  LinuxSigSet sa_mask;
+};
+#else
+struct LinuxSigAction {
+  void (*kernel_handler)(int);
+  uint32_t sa_flags;
+  void (*sa_restorer)(void);
+  LinuxSigSet sa_mask;
+};
+#endif
+
 #endif  // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SIGNAL_H_
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 5f4453e1..4e0caea 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -273,10 +273,6 @@
 #   define SK_SUPPORT_LEGACY_INT_COLORMATRIX
 #endif
 
-#ifndef    SK_SUPPORT_LEGACY_DATARELEASEPROC_PARAMS
-#   define SK_SUPPORT_LEGACY_DATARELEASEPROC_PARAMS
-#endif
-
 ///////////////////////// Imported from BUILD.gn and skia_common.gypi
 
 /* In some places Skia can use static initializers for global initialization,
diff --git a/storage/browser/blob/blob_data_builder.cc b/storage/browser/blob/blob_data_builder.cc
index 7c305a2..94fb9e5 100644
--- a/storage/browser/blob/blob_data_builder.cc
+++ b/storage/browser/blob/blob_data_builder.cc
@@ -5,6 +5,7 @@
 #include "storage/browser/blob/blob_data_builder.h"
 
 #include "base/time/time.h"
+#include "net/disk_cache/disk_cache.h"
 #include "storage/browser/blob/shareable_file_reference.h"
 
 namespace storage {
@@ -60,4 +61,16 @@
   items_.push_back(new BlobDataItem(element.Pass()));
 }
 
+void BlobDataBuilder::AppendDiskCacheEntry(
+    const scoped_refptr<DataHandle>& data_handle,
+    disk_cache::Entry* disk_cache_entry,
+    int disk_cache_stream_index) {
+  scoped_ptr<DataElement> element(new DataElement());
+  element->SetToDiskCacheEntryRange(
+      0U, disk_cache_entry->GetDataSize(disk_cache_stream_index));
+  items_.push_back(
+      new BlobDataItem(element.Pass(), data_handle, disk_cache_entry,
+                       disk_cache_stream_index));
+}
+
 }  // namespace storage
diff --git a/storage/browser/blob/blob_data_builder.h b/storage/browser/blob/blob_data_builder.h
index 8119538c..115d1f4 100644
--- a/storage/browser/blob/blob_data_builder.h
+++ b/storage/browser/blob/blob_data_builder.h
@@ -16,11 +16,17 @@
 #include "storage/browser/blob/blob_data_snapshot.h"
 #include "storage/browser/storage_browser_export.h"
 
+namespace disk_cache {
+class Entry;
+}
+
 namespace storage {
 class BlobStorageContext;
 
 class STORAGE_EXPORT BlobDataBuilder {
  public:
+  using DataHandle = BlobDataItem::DataHandle;
+
   explicit BlobDataBuilder(const std::string& uuid);
   ~BlobDataBuilder();
 
@@ -49,6 +55,10 @@
                             uint64_t length,
                             const base::Time& expected_modification_time);
 
+  void AppendDiskCacheEntry(const scoped_refptr<DataHandle>& data_handle,
+                            disk_cache::Entry* disk_cache_entry,
+                            int disk_cache_stream_index);
+
   void set_content_type(const std::string& content_type) {
     content_type_ = content_type;
   }
diff --git a/storage/browser/blob/blob_data_item.cc b/storage/browser/blob/blob_data_item.cc
index 504a1c3e..c1ec3b3 100644
--- a/storage/browser/blob/blob_data_item.cc
+++ b/storage/browser/blob/blob_data_item.cc
@@ -6,12 +6,33 @@
 
 namespace storage {
 
+BlobDataItem::DataHandle::~DataHandle() {
+}
+
+BlobDataItem::BlobDataItem(scoped_ptr<DataElement> item)
+    : item_(item.Pass()),
+      disk_cache_entry_(nullptr),
+      disk_cache_stream_index_(-1) {
+}
+
 BlobDataItem::BlobDataItem(scoped_ptr<DataElement> item,
-                           scoped_refptr<ShareableFileReference> file_handle)
-    : item_(item.Pass()), file_handle_(file_handle) {
+                           const scoped_refptr<DataHandle>& data_handle)
+    : item_(item.Pass()),
+      data_handle_(data_handle),
+      disk_cache_entry_(nullptr),
+      disk_cache_stream_index_(-1) {
 }
-BlobDataItem::BlobDataItem(scoped_ptr<DataElement> item) : item_(item.Pass()) {
+
+BlobDataItem::BlobDataItem(scoped_ptr<DataElement> item,
+                           const scoped_refptr<DataHandle>& data_handle,
+                           disk_cache::Entry* entry,
+                           int disk_cache_stream_index)
+    : item_(item.Pass()),
+      data_handle_(data_handle),
+      disk_cache_entry_(entry),
+      disk_cache_stream_index_(disk_cache_stream_index) {
 }
+
 BlobDataItem::~BlobDataItem() {
 }
 
diff --git a/storage/browser/blob/blob_data_item.h b/storage/browser/blob/blob_data_item.h
index 1953a1a6..5b25db4 100644
--- a/storage/browser/blob/blob_data_item.h
+++ b/storage/browser/blob/blob_data_item.h
@@ -7,21 +7,37 @@
 
 #include "base/basictypes.h"
 #include "base/memory/ref_counted.h"
-#include "storage/browser/blob/shareable_file_reference.h"
 #include "storage/browser/storage_browser_export.h"
 #include "storage/common/data_element.h"
 
+namespace disk_cache {
+class Entry;
+}
+
 namespace storage {
 class BlobDataBuilder;
 class BlobStorageContext;
 
-// Ref counted blob item.  This class owns the backing data of the blob item.
-// The backing data is immutable, and cannot change after creation.
-// The purpose of this class is to allow the resource to stick around in the
-// snapshot even after the resource was swapped in the blob (either to disk or
-// to memory) by the BlobStorageContext.
+// Ref counted blob item. This class owns the backing data of the blob item. The
+// backing data is immutable, and cannot change after creation. The purpose of
+// this class is to allow the resource to stick around in the snapshot even
+// after the resource was swapped in the blob (either to disk or to memory) by
+// the BlobStorageContext.
 class STORAGE_EXPORT BlobDataItem : public base::RefCounted<BlobDataItem> {
  public:
+  // The DataHandle class is used to persist resources that are needed for
+  // reading this BlobDataItem. This object will stay around while any reads are
+  // pending. If all blobs with this item are deleted or the item is swapped for
+  // a different backend version (mem-to-disk or the reverse), then the item
+  // will be destructed after all pending reads are complete.
+  class STORAGE_EXPORT DataHandle : public base::RefCounted<DataHandle> {
+   protected:
+    virtual ~DataHandle() = 0;
+
+   private:
+    friend class base::RefCounted<DataHandle>;
+  };
+
   DataElement::Type type() const { return item_->type(); }
   const char* bytes() const { return item_->bytes(); }
   const base::FilePath& path() const { return item_->path(); }
@@ -35,6 +51,9 @@
   const DataElement& data_element() const { return *item_; }
   const DataElement* data_element_ptr() const { return item_.get(); }
 
+  disk_cache::Entry* disk_cache_entry() const { return disk_cache_entry_; }
+  int disk_cache_stream_index() const { return disk_cache_stream_index_; }
+
  private:
   friend class BlobDataBuilder;
   friend class BlobStorageContext;
@@ -42,16 +61,27 @@
 
   BlobDataItem(scoped_ptr<DataElement> item);
   BlobDataItem(scoped_ptr<DataElement> item,
-               scoped_refptr<ShareableFileReference> file_handle);
+               const scoped_refptr<DataHandle>& data_handle);
+  BlobDataItem(scoped_ptr<DataElement> item,
+               const scoped_refptr<DataHandle>& data_handle,
+               disk_cache::Entry* entry,
+               int disk_cache_stream_index_);
   virtual ~BlobDataItem();
 
   scoped_ptr<DataElement> item_;
-  scoped_refptr<ShareableFileReference> file_handle_;
+  scoped_refptr<DataHandle> data_handle_;
+
+  // This naked pointer is safe because the scope is protected by the DataHandle
+  // instance for disk cache entries during the lifetime of this BlobDataItem.
+  disk_cache::Entry* disk_cache_entry_;
+  int disk_cache_stream_index_;  // For TYPE_DISK_CACHE_ENTRY.
 };
 
 #if defined(UNIT_TEST)
 inline bool operator==(const BlobDataItem& a, const BlobDataItem& b) {
-  return a.data_element() == b.data_element();
+  return a.disk_cache_entry() == b.disk_cache_entry() &&
+         a.disk_cache_stream_index() == b.disk_cache_stream_index() &&
+         a.data_element() == b.data_element();
 }
 
 inline bool operator!=(const BlobDataItem& a, const BlobDataItem& b) {
diff --git a/storage/browser/blob/blob_storage_context.cc b/storage/browser/blob/blob_storage_context.cc
index 66f67ea1..d08ccb3 100644
--- a/storage/browser/blob/blob_storage_context.cc
+++ b/storage/browser/blob/blob_storage_context.cc
@@ -16,6 +16,7 @@
 #include "base/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "storage/browser/blob/blob_data_builder.h"
+#include "storage/browser/blob/shareable_file_reference.h"
 #include "url/gurl.h"
 
 namespace storage {
@@ -283,6 +284,9 @@
                               ipc_data.length());
       blob_item = new BlobDataItem(element.Pass());
       break;
+    case DataElement::TYPE_DISK_CACHE_ENTRY:  // This type can't be sent by IPC.
+      NOTREACHED();
+      break;
     default:
       NOTREACHED();
       break;
@@ -365,6 +369,13 @@
       }
       break;
     }
+    case DataElement::TYPE_DISK_CACHE_ENTRY: {
+      UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.CacheEntry",
+                           (length - offset) / 1024);
+      target_blob_builder->AppendSharedBlobItem(
+          new ShareableBlobDataItem(target_blob_uuid, blob_item));
+      break;
+    }
     default:
       NOTREACHED();
       break;
@@ -440,7 +451,7 @@
                                     item.expected_modification_time());
         target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem(
             target_blob_uuid,
-            new BlobDataItem(element.Pass(), item.file_handle_)));
+            new BlobDataItem(element.Pass(), item.data_handle_)));
       } break;
       case DataElement::TYPE_FILE_FILESYSTEM: {
         UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.BlobSlice.FileSystem",
@@ -452,6 +463,16 @@
         target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem(
             target_blob_uuid, new BlobDataItem(element.Pass())));
       } break;
+      case DataElement::TYPE_DISK_CACHE_ENTRY: {
+        scoped_ptr<DataElement> element(new DataElement());
+        element->SetToDiskCacheEntryRange(item.offset() + offset,
+                                          new_length);
+        target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem(
+            target_blob_uuid,
+            new BlobDataItem(element.Pass(), item.data_handle_,
+                             item.disk_cache_entry(),
+                             item.disk_cache_stream_index())));
+      } break;
       default:
         CHECK(false) << "Illegal blob item type: " << item.type();
     }
diff --git a/storage/browser/blob/blob_url_request_job.cc b/storage/browser/blob/blob_url_request_job.cc
index 26607cd..f429020 100644
--- a/storage/browser/blob/blob_url_request_job.cc
+++ b/storage/browser/blob/blob_url_request_job.cc
@@ -22,6 +22,7 @@
 #include "base/trace_event/trace_event.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
+#include "net/disk_cache/disk_cache.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
@@ -356,6 +357,8 @@
   const BlobDataItem& item = *items.at(current_item_index_);
   if (item.type() == DataElement::TYPE_BYTES)
     return ReadBytesItem(item, bytes_to_read);
+  if (item.type() == DataElement::TYPE_DISK_CACHE_ENTRY)
+    return ReadDiskCacheEntryItem(item, bytes_to_read);
   if (!IsFileType(item.type())) {
     NOTREACHED();
     return false;
@@ -412,6 +415,8 @@
 
 bool BlobURLRequestJob::ReadFileItem(FileStreamReader* reader,
                                      int bytes_to_read) {
+  DCHECK(!GetStatus().is_io_pending())
+      << "Can't begin IO while another IO operation is pending.";
   DCHECK_GE(read_buf_->BytesRemaining(), bytes_to_read);
   DCHECK(reader);
   int chunk_number = current_file_chunk_number_++;
@@ -420,13 +425,9 @@
   const int result =
       reader->Read(read_buf_.get(), bytes_to_read,
                    base::Bind(&BlobURLRequestJob::DidReadFile,
-                              base::Unretained(this), chunk_number));
+                              weak_factory_.GetWeakPtr(), chunk_number));
   if (result >= 0) {
-    // Data is immediately available.
-    if (GetStatus().is_io_pending())
-      DidReadFile(chunk_number, result);
-    else
-      AdvanceBytesRead(result);
+    AdvanceBytesRead(result);
     return true;
   }
   if (result == net::ERR_IO_PENDING)
@@ -437,23 +438,18 @@
 }
 
 void BlobURLRequestJob::DidReadFile(int chunk_number, int result) {
+  DCHECK(GetStatus().is_io_pending())
+      << "Asynchronous IO completed while IO wasn't pending?";
   TRACE_EVENT_ASYNC_END1("Blob", "BlobRequest::ReadFileItem", this, "uuid",
                          blob_data_->uuid());
   if (result <= 0) {
-    NotifyFailure(net::ERR_FAILED);
+    NotifyFailure(result);
     return;
   }
   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
 
   AdvanceBytesRead(result);
 
-  // If the read buffer is completely filled, we're done.
-  if (!read_buf_->BytesRemaining()) {
-    int bytes_read = BytesReadCompleted();
-    NotifyReadComplete(bytes_read);
-    return;
-  }
-
   // Otherwise, continue the reading.
   int bytes_read = 0;
   if (ReadLoop(&bytes_read))
@@ -468,6 +464,43 @@
   }
 }
 
+bool BlobURLRequestJob::ReadDiskCacheEntryItem(const BlobDataItem& item,
+                                               int bytes_to_read) {
+  DCHECK(!GetStatus().is_io_pending())
+      << "Can't begin IO while another IO operation is pending.";
+  DCHECK_GE(read_buf_->BytesRemaining(), bytes_to_read);
+
+  const int result = item.disk_cache_entry()->ReadData(
+      item.disk_cache_stream_index(), current_item_offset_, read_buf_.get(),
+      bytes_to_read, base::Bind(&BlobURLRequestJob::DidReadDiskCacheEntry,
+                                weak_factory_.GetWeakPtr()));
+  if (result >= 0) {
+    AdvanceBytesRead(result);
+    return true;
+  }
+  if (result == net::ERR_IO_PENDING)
+    SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
+  else
+    NotifyFailure(result);
+  return false;
+}
+
+void BlobURLRequestJob::DidReadDiskCacheEntry(int result) {
+  DCHECK(GetStatus().is_io_pending())
+      << "Asynchronous IO completed while IO wasn't pending?";
+  if (result <= 0) {
+    NotifyFailure(result);
+    return;
+  }
+  SetStatus(net::URLRequestStatus());
+
+  AdvanceBytesRead(result);
+
+  int bytes_read = 0;
+  if (ReadLoop(&bytes_read))
+    NotifyReadComplete(bytes_read);
+}
+
 int BlobURLRequestJob::BytesReadCompleted() {
   int bytes_read = read_buf_->BytesConsumed();
   read_buf_ = NULL;
diff --git a/storage/browser/blob/blob_url_request_job.h b/storage/browser/blob/blob_url_request_job.h
index a4e10eda..74d07ad 100644
--- a/storage/browser/blob/blob_url_request_job.h
+++ b/storage/browser/blob/blob_url_request_job.h
@@ -6,6 +6,7 @@
 #define STORAGE_BROWSER_BLOB_BLOB_URL_REQUEST_JOB_H_
 
 #include <map>
+#include <vector>
 
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
@@ -19,10 +20,6 @@
 class SingleThreadTaskRunner;
 }
 
-namespace storage {
-class FileSystemContext;
-}
-
 namespace net {
 class DrainableIOBuffer;
 class IOBuffer;
@@ -31,6 +28,7 @@
 namespace storage {
 
 class FileStreamReader;
+class FileSystemContext;
 
 // A request job that handles reading blob URLs.
 class STORAGE_EXPORT BlobURLRequestJob
@@ -71,11 +69,14 @@
   void AdvanceItem();
   void AdvanceBytesRead(int result);
   bool ReadBytesItem(const BlobDataItem& item, int bytes_to_read);
-  bool ReadFileItem(FileStreamReader* reader, int bytes_to_read);
 
+  bool ReadFileItem(FileStreamReader* reader, int bytes_to_read);
   void DidReadFile(int chunk_number, int result);
   void DeleteCurrentFileReader();
 
+  bool ReadDiskCacheEntryItem(const BlobDataItem& item, int bytes_to_read);
+  void DidReadDiskCacheEntry(int result);
+
   int ComputeBytesToRead() const;
   int BytesReadCompleted();
 
diff --git a/storage/browser/blob/shareable_file_reference.h b/storage/browser/blob/shareable_file_reference.h
index e2739dc..10f986fa 100644
--- a/storage/browser/blob/shareable_file_reference.h
+++ b/storage/browser/blob/shareable_file_reference.h
@@ -5,8 +5,7 @@
 #ifndef STORAGE_BROWSER_BLOB_SHAREABLE_FILE_REFERENCE_H_
 #define STORAGE_BROWSER_BLOB_SHAREABLE_FILE_REFERENCE_H_
 
-#include <vector>
-
+#include "storage/browser/blob/blob_data_item.h"
 #include "storage/browser/blob/scoped_file.h"
 #include "storage/browser/storage_browser_export.h"
 
@@ -16,8 +15,7 @@
 // same path if it already exists in its internal map.
 // This class is non-thread-safe and all methods must be called on a single
 // thread.
-class STORAGE_EXPORT ShareableFileReference
-    : public base::RefCounted<ShareableFileReference> {
+class STORAGE_EXPORT ShareableFileReference : public BlobDataItem::DataHandle {
  public:
   typedef ScopedFile::ScopeOutCallback FinalReleaseCallback;
 
@@ -60,10 +58,8 @@
   void AddFinalReleaseCallback(const FinalReleaseCallback& callback);
 
  private:
-  friend class base::RefCounted<ShareableFileReference>;
-
   ShareableFileReference(ScopedFile scoped_file);
-  ~ShareableFileReference();
+  ~ShareableFileReference() override;
 
   ScopedFile scoped_file_;
 
diff --git a/storage/browser/blob/view_blob_internals_job.cc b/storage/browser/blob/view_blob_internals_job.cc
index ab1f310..1b5e91ef 100644
--- a/storage/browser/blob/view_blob_internals_job.cc
+++ b/storage/browser/blob/view_blob_internals_job.cc
@@ -17,6 +17,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "net/base/escape.h"
 #include "net/base/net_errors.h"
+#include "net/disk_cache/disk_cache.h"
 #include "net/url_request/url_request.h"
 #include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/blob/internal_blob_data.h"
@@ -218,6 +219,10 @@
               out);
         }
         break;
+      case DataElement::TYPE_DISK_CACHE_ENTRY:
+        AddHTMLListItem(kType, "disk cache entry", out);
+        AddHTMLListItem(kURL, item.disk_cache_entry()->GetKey(), out);
+        break;
       case DataElement::TYPE_UNKNOWN:
         NOTREACHED();
         break;
diff --git a/storage/common/data_element.cc b/storage/common/data_element.cc
index c6d52f4..f26058b 100644
--- a/storage/common/data_element.cc
+++ b/storage/common/data_element.cc
@@ -46,4 +46,10 @@
   expected_modification_time_ = expected_modification_time;
 }
 
+void DataElement::SetToDiskCacheEntryRange(uint64 offset, uint64 length) {
+  type_ = TYPE_DISK_CACHE_ENTRY;
+  offset_ = offset;
+  length_ = length;
+}
+
 }  // namespace storage
diff --git a/storage/common/data_element.h b/storage/common/data_element.h
index 0efe1550..2eeca8e 100644
--- a/storage/common/data_element.h
+++ b/storage/common/data_element.h
@@ -27,6 +27,7 @@
     TYPE_FILE,
     TYPE_BLOB,
     TYPE_FILE_FILESYSTEM,
+    TYPE_DISK_CACHE_ENTRY,
   };
 
   DataElement();
@@ -102,6 +103,9 @@
                                uint64 offset, uint64 length,
                                const base::Time& expected_modification_time);
 
+  // Sets to TYPE_DISK_CACHE_ENTRY with range.
+  void SetToDiskCacheEntryRange(uint64 offset, uint64 length);
+
  private:
   Type type_;
   std::vector<char> buf_;  // For TYPE_BYTES.
@@ -130,6 +134,10 @@
       return a.blob_uuid() == b.blob_uuid();
     case DataElement::TYPE_FILE_FILESYSTEM:
       return a.filesystem_url() == b.filesystem_url();
+    case DataElement::TYPE_DISK_CACHE_ENTRY:
+      // We compare only length and offset; we trust the entry itself was
+      // compared at some higher level such as in BlobDataItem.
+      return true;
     case DataElement::TYPE_UNKNOWN:
       NOTREACHED();
       return false;
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 722dd21..565a91720 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -11,6 +11,13 @@
         ],
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py"
+      },
+      {
+        "args": [
+          "cc_perftests"
+        ],
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py"
       }
     ]
   },
@@ -26,6 +33,13 @@
         ],
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py"
+      },
+      {
+        "args": [
+          "cc_perftests"
+        ],
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py"
       }
     ]
   },
@@ -41,6 +55,13 @@
         ],
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py"
+      },
+      {
+        "args": [
+          "cc_perftests"
+        ],
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py"
       }
     ]
   },
@@ -56,6 +77,13 @@
         ],
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py"
+      },
+      {
+        "args": [
+          "cc_perftests"
+        ],
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py"
       }
     ]
   },
@@ -64,6 +92,13 @@
       {
         "name": "host_info",
         "script": "host_info.py"
+      },
+      {
+        "args": [
+          "cc_perftests"
+        ],
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py"
       }
     ]
   },
@@ -79,6 +114,13 @@
         ],
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py"
+      },
+      {
+        "args": [
+          "cc_perftests"
+        ],
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py"
       }
     ]
   },
@@ -94,6 +136,13 @@
         ],
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py"
+      },
+      {
+        "args": [
+          "cc_perftests"
+        ],
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py"
       }
     ]
   },
@@ -109,6 +158,14 @@
     "scripts": [
       {
         "args": [
+          "cc_perftests",
+          "--test-launcher-print-test-stdio=always"
+        ],
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py"
+      },
+      {
+        "args": [
           "load_library_perf_tests",
           "--test-launcher-print-test-stdio=always"
         ],
@@ -133,6 +190,14 @@
     "scripts": [
       {
         "args": [
+          "cc_perftests",
+          "--test-launcher-print-test-stdio=always"
+        ],
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py"
+      },
+      {
+        "args": [
           "performance_browser_tests",
           "--test-launcher-print-test-stdio=always",
           "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*",
@@ -168,6 +233,14 @@
     "scripts": [
       {
         "args": [
+          "cc_perftests",
+          "--test-launcher-print-test-stdio=always"
+        ],
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py"
+      },
+      {
+        "args": [
           "performance_browser_tests",
           "--test-launcher-print-test-stdio=always",
           "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*",
diff --git a/testing/buildbot/chromium.webkit.json b/testing/buildbot/chromium.webkit.json
index 80221c9..2e9f5ec 100644
--- a/testing/buildbot/chromium.webkit.json
+++ b/testing/buildbot/chromium.webkit.json
@@ -723,6 +723,32 @@
       }
     ]
   },
+  "WebKit Mac10.10": {
+    "gtest_tests": [
+      {
+        "test": "blink_heap_unittests"
+      },
+      {
+        "test": "blink_platform_unittests"
+      },
+      {
+        "test": "webkit_unit_tests"
+      },
+      {
+        "test": "wtf_unittests"
+      }
+    ],
+    "scripts": [
+      {
+        "name": "webkit_lint",
+        "script": "webkit_lint.py"
+      },
+      {
+        "name": "webkit_python_tests",
+        "script": "webkit_python_tests.py"
+      }
+    ]
+  },
   "WebKit Mac10.6": {
     "gtest_tests": [
       {
@@ -853,7 +879,7 @@
       }
     ]
   },
-  "WebKit Mac10.8 (retina)": {
+  "WebKit Mac10.9": {
     "gtest_tests": [
       {
         "test": "blink_heap_unittests"
@@ -879,7 +905,7 @@
       }
     ]
   },
-  "WebKit Mac10.9": {
+  "WebKit Mac10.9 (retina)": {
     "gtest_tests": [
       {
         "test": "blink_heap_unittests"
diff --git a/testing/scripts/mojo_apptest.py b/testing/scripts/mojo_apptest.py
index 09e3f23..49b855da2 100755
--- a/testing/scripts/mojo_apptest.py
+++ b/testing/scripts/mojo_apptest.py
@@ -13,11 +13,10 @@
 
 def main_run(args):
   runner = os.path.join(common.SRC_DIR, 'mojo', 'tools', 'apptest_runner.py')
-  tests = os.path.join(common.SRC_DIR, 'mojo', 'tools', 'data', 'apptests')
   build_dir = os.path.join(common.SRC_DIR, 'out', args.build_config_fs)
 
   with common.temporary_file() as tempfile_path:
-    rc = common.run_command([runner, tests, build_dir, '--verbose',
+    rc = common.run_command([runner, build_dir, '--verbose',
                              '--write-full-results-to', tempfile_path])
     with open(tempfile_path) as f:
       results = json.load(f)
diff --git a/testing/variations/fieldtrial_testing_config_win.json b/testing/variations/fieldtrial_testing_config_win.json
index c80a14e1e..15c27bd 100644
--- a/testing/variations/fieldtrial_testing_config_win.json
+++ b/testing/variations/fieldtrial_testing_config_win.json
@@ -19,6 +19,11 @@
             "group_name": "Default"
         }
     ],
+    "EnableSessionCrashedBubbleUI": [
+        {
+            "group_name": "Enabled"
+        }
+    ],
     "ExtensionContentVerification": [
         {
             "group_name": "Enforce"
diff --git a/third_party/cardboard-java/BUILD.gn b/third_party/cardboard-java/BUILD.gn
index a6ff1b6c..4effd70a 100644
--- a/third_party/cardboard-java/BUILD.gn
+++ b/third_party/cardboard-java/BUILD.gn
@@ -5,9 +5,9 @@
 import("//build/config/android/rules.gni")
 
 # GYP: //third_party/cardboard-java/cardboard.gyp:cardboard_jar
-java_prebuilt("cardboard-java") {
+android_java_prebuilt("cardboard-java") {
   jar_path = "src/CardboardSample/libs/cardboard.jar"
   deps = [
-    "//third_party/android_protobuf/android_protobuf.gyp:protobuf_nano_javalib",
+    "//third_party/android_protobuf:protobuf_nano_javalib",
   ]
 }
diff --git a/third_party/mockito/OWNERS b/third_party/mockito/OWNERS
index bd675fb8..64c090f 100644
--- a/third_party/mockito/OWNERS
+++ b/third_party/mockito/OWNERS
@@ -1,2 +1,2 @@
 jbudorick@chromium.org
-klundberg@chromium.org
+mikecase@chromium.org
diff --git a/third_party/qcms/README.chromium b/third_party/qcms/README.chromium
index 36bb39b..df998d7 100644
--- a/third_party/qcms/README.chromium
+++ b/third_party/qcms/README.chromium
@@ -75,6 +75,8 @@
    - https://code.google.com/p/chromium/issues/detail?id=491826
  - Add tone reproduction curve (TRC) extraction api
    - https://code.google.com/p/chromium/issues/detail?id=491826
+ - lut_inverse_interp16: interpolate degenerate zeros in TRC curves
+   - https://code.google.com/p/chromium/issues/detail?id=458024
 
 For the Chromium changes, since the import, in a patch format run:
   git diff b8456f38 src
diff --git a/third_party/qcms/src/transform_util.c b/third_party/qcms/src/transform_util.c
index c1a9d156..34f29f7 100644
--- a/third_party/qcms/src/transform_util.c
+++ b/third_party/qcms/src/transform_util.c
@@ -334,8 +334,6 @@
                 if (Value == 0) return 0;
                 // if (Value == 0xFFFF) return 0xFFFF;
                 sample = (length-1) * ((double) Value * (1./65535.));
-                if (LutTable[sample] == 0)
-                    return 0;
                 if (LutTable[sample] == 0xffff)
                     return 0xffff;
 
@@ -353,6 +351,13 @@
                     l = 1;
                 if (r > 0x10000)
                     r = 0x10000;
+
+                // If the search range is inverted due to degeneracy,
+                // deem LutTable non-invertible in this search range.
+                // Refer to https://bugzil.la/1132467
+
+                if (r <= l)
+                    return 0;
         }
 
         // Seems not a degenerated case... apply binary search
@@ -376,14 +381,20 @@
 
         // Not found, should we interpolate?
 
-
         // Get surrounding nodes
 
+        assert(x >= 1);
+
         val2 = (length-1) * ((double) (x - 1) / 65535.0);
 
         cell0 = (int) floor(val2);
         cell1 = (int) ceil(val2);
 
+        assert(cell0 >= 0);
+        assert(cell1 >= 0);
+        assert(cell0 < length);
+        assert(cell1 < length);
+
         if (cell0 == cell1) return (uint16_fract_t) x;
 
         y0 = LutTable[cell0] ;
diff --git a/third_party/robolectric/OWNERS b/third_party/robolectric/OWNERS
index bd675fb8..64c090f 100644
--- a/third_party/robolectric/OWNERS
+++ b/third_party/robolectric/OWNERS
@@ -1,2 +1,2 @@
 jbudorick@chromium.org
-klundberg@chromium.org
+mikecase@chromium.org
diff --git a/third_party/ub-uiautomator/OWNERS b/third_party/ub-uiautomator/OWNERS
index c35d7ace..2fe333e2 100644
--- a/third_party/ub-uiautomator/OWNERS
+++ b/third_party/ub-uiautomator/OWNERS
@@ -1,2 +1,3 @@
 jbudorick@chromium.org
 perezju@chromium.org
+mikecase@chromium.org
\ No newline at end of file
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 5b991414..96ae810 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -76,6 +76,9 @@
                             help='analyze whether changes to a set of files '
                                  'will cause a set of binaries to be rebuilt.')
     AddCommonOptions(subp)
+    subp.add_argument('--swarming-targets-file',
+                      help='save runtime dependencies for targets listed '
+                           'in file.')
     subp.add_argument('path', nargs=1,
                       help='path build was generated into.')
     subp.add_argument('input_path', nargs=1,
@@ -140,9 +143,9 @@
   def CmdGen(self):
     vals = self.GetConfig()
     if vals['type'] == 'gn':
-      return self.RunGNGen(self.args.path[0], vals)
+      return self.RunGNGen(vals)
     if vals['type'] == 'gyp':
-      return self.RunGYPGen(self.args.path[0], vals)
+      return self.RunGYPGen(vals)
 
     raise MBErr('Unknown meta-build type "%s"' % vals['type'])
 
@@ -337,7 +340,9 @@
         self.FlattenMixins(mixin_vals['mixins'], vals, visited)
     return vals
 
-  def RunGNGen(self, path, vals):
+  def RunGNGen(self, vals):
+    path = self.args.path[0]
+
     cmd = self.GNCmd('gen', path, vals['gn_args'])
 
     swarming_targets = []
@@ -387,7 +392,9 @@
       cmd.append('--args=%s' % gn_args)
     return cmd
 
-  def RunGYPGen(self, path, vals):
+  def RunGYPGen(self, vals):
+    path = self.args.path[0]
+
     output_dir, gyp_config = self.ParseGYPConfigPath(path)
     if gyp_config != vals['gyp_config']:
       raise MBErr('The last component of the path (%s) must match the '
@@ -573,7 +580,13 @@
       cmd += ['-D', d]
     return cmd
 
-  def RunGNAnalyze(self, _vals):
+  def RunGNAnalyze(self, vals):
+    # analyze runs before 'gn gen' now, so we need to run gn gen
+    # in order to ensure that we have a build directory.
+    ret = self.RunGNGen(vals)
+    if ret:
+      return ret
+
     inp = self.ReadInputJSON(['files', 'targets'])
     if self.args.verbose:
       self.Print()
diff --git a/tools/mb/mb_unittest.py b/tools/mb/mb_unittest.py
index 4eb79b4..d0326ca 100644
--- a/tools/mb/mb_unittest.py
+++ b/tools/mb/mb_unittest.py
@@ -180,8 +180,12 @@
                "targets": ["bar_unittests"]
              }"""}
     mbw = self.fake_mbw(files)
-    mbw.Call = lambda cmd: (
-        1, 'The input matches no targets, configs, or files\n', '')
+    mbw.cmds = [
+        (0, '', ''),
+        (1, 'The input matches no targets, configs, or files\n', ''),
+        (1, 'The input matches no targets, configs, or files\n', ''),
+    ]
+    mbw.Call = lambda cmd: mbw.cmds.pop(0)
 
     self.check(['analyze', '-c', 'gn_debug', '//out/Default',
                 '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index aa84279..e3d28247 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -4462,6 +4462,23 @@
   </summary>
 </histogram>
 
+<histogram name="CustomTabs.IntentToFirstCommitNavigationTime" units="ms">
+  <owner>lizeb@chromium.org</owner>
+  <summary>
+    Time between the intent arrival in Chrome and the first navigation commit,
+    if the navigation is successful. Similar in principle to
+    Startup.FirstCommitNavigationTime.
+  </summary>
+</histogram>
+
+<histogram name="CustomTabs.IntentToPageLoadedTime" units="ms">
+  <owner>lizeb@chromium.org</owner>
+  <summary>
+    Time between the intent arrival in Chrome and the first &quot;page
+    loaded&quot; event, if the navigation is successful.
+  </summary>
+</histogram>
+
 <histogram name="CustomTabs.PredictionStatus" enum="PredictionStatus">
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -4769,8 +4786,9 @@
     option. These samples include:
 
     Displays and clicks on the &quot;Load images&quot; snackbar. Displays and
-    clicks on the &quot;Load image&quot; context menu option. Count of pages
-    where the user has clicked &quot;Load image&quot; at least once
+    clicks on the &quot;Load image&quot; and &quot;Load images&quot;context menu
+    options. Count of pages where the user has clicked &quot;Load image&quot; at
+    least once.
   </summary>
 </histogram>
 
@@ -13176,6 +13194,9 @@
 </histogram>
 
 <histogram name="HttpCache.AsyncValidationDuration" units="milliseconds">
+  <obsolete>
+    Deprecated as of 3/2015.
+  </obsolete>
   <owner>ricea@chromium.org</owner>
   <summary>
     The time spent performing an asynchronous revalidation that was triggered by
@@ -16029,6 +16050,13 @@
   </summary>
 </histogram>
 
+<histogram name="Media.VAJDA.DecoderFailure" enum="VAJDADecoderFailure">
+  <owner>kcwu@chromium.org</owner>
+  <summary>
+    Error codes reported by jpeg decode using VA-API hardware jpeg decoder.
+  </summary>
+</histogram>
+
 <histogram name="Media.VAVDA.DecoderFailure" enum="VAVDADecoderFailure">
   <owner>posciak@chromium.org</owner>
   <summary>
@@ -20049,6 +20077,25 @@
   </summary>
 </histogram>
 
+<histogram name="Net.ErrorResponseHasContentMainFrame" units="BooleanSuccess">
+  <owner>mmenke@chromium.org</owner>
+  <summary>
+    The number of main frame 4xx/5xx responses that have content, to determine
+    if it's worth hooking up an error page for those that don't.  Intended to be
+    removed from the binary soon.
+  </summary>
+</histogram>
+
+<histogram name="Net.ErrorResponseHasContentNonMainFrame"
+    units="BooleanSuccess">
+  <owner>mmenke@chromium.org</owner>
+  <summary>
+    The number of non-main frame 4xx/5xx responses that have content, to
+    determine if it's worth hooking up an error page for those that don't.
+    Intended to be removed from the binary soon.
+  </summary>
+</histogram>
+
 <histogram name="Net.FileError_Flush">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>
@@ -27529,6 +27576,16 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.FillSuggestionsIncludeAndroidAppCredentials"
+    enum="PasswordManagerOfferedAndroidCredentials">
+  <owner>msramek@chromium.org</owner>
+  <owner>engedy@chromium.org</owner>
+  <summary>
+    When offering to fill the username and password, whether at least one of the
+    credentials in the dropdown comes from an Android app.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.FormDataDeserializationStatus"
     enum="FormDataDeserializationStatus">
   <owner>gcasto@chromium.org</owner>
@@ -43538,6 +43595,14 @@
   <summary>Enumeration of types of session configuration failures.</summary>
 </histogram>
 
+<histogram name="Sync.SessionsRefreshDelay" units="milliseconds">
+  <owner>zea@chromium.org</owner>
+  <summary>
+    Delay from the time chrome://history is loaded until the other devices'
+    sessions data became available.
+  </summary>
+</histogram>
+
 <histogram name="Sync.SessionsStartFailure" enum="SyncStartResult">
   <obsolete>
     Replaced by SessionsConfigureFailure. See crbug.com/478226.
@@ -47129,6 +47194,13 @@
   </summary>
 </histogram>
 
+<histogram name="WebCore.IndexedDB.LevelDB.CloseTime" units="milliseconds">
+  <owner>cmumford@chromium.org</owner>
+  <summary>
+    The time that it takes to close IndexedDB's LevelDB backing store.
+  </summary>
+</histogram>
+
 <histogram name="WebCore.IndexedDB.LevelDB.FreeDiskSpaceFailure"
     enum="LevelDBErrorCount">
   <owner>dgrogan@chromium.org</owner>
@@ -49936,6 +50008,7 @@
   <int value="86" label="SWDH_UPDATE_NO_HOST"/>
   <int value="87" label="SWDH_UPDATE_BAD_REGISTRATION_ID"/>
   <int value="88" label="SWDH_UPDATE_CANNOT"/>
+  <int value="89" label="SWDH_UNREGISTER_BAD_REGISTRATION_ID"/>
 </enum>
 
 <enum name="BadMessageReasonExtensions" type="int">
@@ -51625,6 +51698,8 @@
   <int value="3" label="'Load image' context menu item clicked"/>
   <int value="4"
       label="Pages where the user has clicked 'Load image' at least once"/>
+  <int value="5" label="'Load images' context menu item shown"/>
+  <int value="6" label="'Load images' context menu item clicked"/>
 </enum>
 
 <enum name="DataReductionProxyNetworkChangeEvent" type="int">
@@ -53208,6 +53283,7 @@
       label="Allow users to opt in to Safe Browsing extended reporting"/>
   <int value="300" label="Allow proceeding from the SSL warning page"/>
   <int value="301" label="Allows QUIC protocol"/>
+  <int value="302" label="Key Permissions"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations" type="int">
@@ -56610,6 +56686,7 @@
   <int value="7" label="Internal server error"/>
   <int value="8" label="HTTP reponse code not OK"/>
   <int value="9" label="Unknown error"/>
+  <int value="10" label="Reached maximum number of retries"/>
 </enum>
 
 <enum name="GCReason" type="int">
@@ -58869,6 +58946,7 @@
   <int value="-1346722635" label="gesture-selection"/>
   <int value="-1341092934" label="enable-accelerated-overflow-scroll"/>
   <int value="-1340055960" label="enable-streamlined-hosted-apps"/>
+  <int value="-1337185440" label="enable-webvr"/>
   <int value="-1334327410" label="ash-enable-touch-view-testing"/>
   <int value="-1322882747" label="disable-datasaver-prompt"/>
   <int value="-1319688939" label="ignore-gpu-blacklist"/>
@@ -60126,6 +60204,7 @@
   <int value="3" label="Autoplay disabled but user manually started media"/>
   <int value="4"
       label="Autoplay enabled through a user-gesture triggered load() call."/>
+  <int value="5" label="Autoplay disabled by sandbox flags."/>
 </enum>
 
 <enum name="MediaGalleriesUsageType" type="int">
@@ -62378,6 +62457,11 @@
   <int value="1" label="A password change form"/>
 </enum>
 
+<enum name="PasswordManagerOfferedAndroidCredentials" type="int">
+  <int value="0" label="None from Android"/>
+  <int value="1" label="1+ from Android"/>
+</enum>
+
 <enum name="PasswordManagerOsPasswordStatus" type="int">
   <int value="0" label="Unknown"/>
   <int value="1" label="Unsupported platform"/>
@@ -67922,6 +68006,10 @@
   <int value="3" label="FAILED_OTHER">Failed for other reason</int>
 </enum>
 
+<enum name="VAJDADecoderFailure" type="int">
+  <int value="0" label="VAAPI_ERROR"/>
+</enum>
+
 <enum name="ValidateMenuItemSelectorType" type="int">
   <int value="0"
       label="The menu items' associated action is an unknown selector."/>
diff --git a/tools/perf/benchmarks/blink_perf.py b/tools/perf/benchmarks/blink_perf.py
index a03b840..9d9f44a 100644
--- a/tools/perf/benchmarks/blink_perf.py
+++ b/tools/perf/benchmarks/blink_perf.py
@@ -122,21 +122,6 @@
     assert 'content-shell' in options.browser_type
     options.AppendExtraBrowserArgs(['--expose-internals-for-testing'])
 
-# http://crbug.com/499472
-@benchmark.Disabled()
-class BlinkPerfAnimation(perf_benchmark.PerfBenchmark):
-  tag = 'animation'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.animation'
-
-  def CreatePageSet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'Animation')
-    return CreatePageSetFromPath(path, SKIPPED_FILE)
-
-
 @benchmark.Disabled  # http://crbug.com/500958
 class BlinkPerfBindings(perf_benchmark.PerfBenchmark):
   tag = 'bindings'
diff --git a/tools/perf/benchmarks/dromaeo.py b/tools/perf/benchmarks/dromaeo.py
index 9b2f4243..2c19ded 100644
--- a/tools/perf/benchmarks/dromaeo.py
+++ b/tools/perf/benchmarks/dromaeo.py
@@ -134,6 +134,7 @@
     return 'dromaeo.domcoreattr'
 
 
+@benchmark.Disabled('xp')  # crbug.com/501625
 class DromaeoDomCoreModify(_DromaeoBenchmark):
   """Dromaeo DOMCore modify JavaScript benchmark.
 
diff --git a/tools/perf/benchmarks/smoothness.py b/tools/perf/benchmarks/smoothness.py
index 50fea2f..f4ae21a4 100644
--- a/tools/perf/benchmarks/smoothness.py
+++ b/tools/perf/benchmarks/smoothness.py
@@ -49,8 +49,6 @@
     return 'smoothness.tough_path_rendering_cases'
 
 
-# crbug.com/388877, crbug.com/396127
-@benchmark.Disabled('mac', 'win', 'android')
 class SmoothnessToughCanvasCases(perf_benchmark.PerfBenchmark):
   """Measures frame rate and a variety of other statistics.
 
diff --git a/tools/perf/page_sets/tough_canvas_cases.py b/tools/perf/page_sets/tough_canvas_cases.py
index dd8f9f5..0cff0d1 100644
--- a/tools/perf/page_sets/tough_canvas_cases.py
+++ b/tools/perf/page_sets/tough_canvas_cases.py
@@ -66,7 +66,8 @@
       'http://spielzeugz.de/html5/liquid-particles.html',
       'http://hakim.se/experiments/html5/magnetic/02/',
       'http://ie.microsoft.com/testdrive/Performance/LetItSnow/',
-      'http://ie.microsoft.com/testdrive/Graphics/WorkerFountains/Default.html',
+      # crbug.com/501406 causes OOM failures on perf bots
+      # 'http://ie.microsoft.com/testdrive/Graphics/WorkerFountains/Default.html',
       'http://ie.microsoft.com/testdrive/Graphics/TweetMap/Default.html',
       'http://ie.microsoft.com/testdrive/Graphics/VideoCity/Default.html',
       'http://ie.microsoft.com/testdrive/Performance/AsteroidBelt/Default.html',
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 6b02634..403b3e6 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder.py
@@ -173,7 +173,7 @@
                                              False, browser_directory))
     else:
       raise exceptions.PathMissingError(
-          '%s specified by --browser-executable does not exist',
+          '%s specified by --browser-executable does not exist' %
           normalized_executable)
 
   def AddIfFound(browser_type, build_dir, type_dir, app_name, content_shell):
diff --git a/tools/telemetry/telemetry/core/platform/__init__.py b/tools/telemetry/telemetry/core/platform/__init__.py
index 59764672..c60def63c 100644
--- a/tools/telemetry/telemetry/core/platform/__init__.py
+++ b/tools/telemetry/telemetry/core/platform/__init__.py
@@ -109,6 +109,12 @@
     """Returns True if the device has been thermally throttled."""
     return self._platform_backend.HasBeenThermallyThrottled()
 
+  def GetDeviceTypeName(self):
+    """Returns a string description of the Platform device, or None.
+
+    Examples: Nexus 7, Nexus 6, Desktop"""
+    return self._platform_backend.GetDeviceTypeName()
+
   def GetArchName(self):
     """Returns a string description of the Platform architecture.
 
diff --git a/tools/telemetry/telemetry/core/platform/android_platform_backend.py b/tools/telemetry/telemetry/core/platform/android_platform_backend.py
index 7ad3194..92bde5d 100644
--- a/tools/telemetry/telemetry/core/platform/android_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/android_platform_backend.py
@@ -257,6 +257,9 @@
   def GetOSName(self):
     return 'android'
 
+  def GetDeviceTypeName(self):
+    return self._device.product_model()
+
   @decorators.Cache
   def GetOSVersionName(self):
     return self._device.GetProp('ro.build.id')[0]
diff --git a/tools/telemetry/telemetry/core/platform/desktop_platform_backend.py b/tools/telemetry/telemetry/core/platform/desktop_platform_backend.py
index def9c38d..e043bfa 100644
--- a/tools/telemetry/telemetry/core/platform/desktop_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/desktop_platform_backend.py
@@ -23,3 +23,6 @@
     assert flush_command, 'You must build clear_system_cache first'
 
     subprocess.check_call([flush_command, '--recurse', directory])
+
+  def GetDeviceTypeName(self):
+    return 'Desktop'
diff --git a/tools/telemetry/telemetry/core/platform/platform_backend.py b/tools/telemetry/telemetry/core/platform/platform_backend.py
index 99d49ba..d57427f 100644
--- a/tools/telemetry/telemetry/core/platform/platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/platform_backend.py
@@ -202,6 +202,9 @@
   def GetCommandLine(self, pid):
     raise NotImplementedError()
 
+  def GetDeviceTypeName(self):
+    raise NotImplementedError()
+
   def GetArchName(self):
     raise NotImplementedError()
 
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt
index e1b75c83..d42189b7 100644
--- a/tools/valgrind/memcheck/suppressions.txt
+++ b/tools/valgrind/memcheck/suppressions.txt
@@ -3545,36 +3545,3 @@
     fun:_ZN5blink13CSSParserImpl23consumeDeclarationValueENS_19CSSParserTokenRangeENS_13CSSPropertyIDEbNS_13StyleRuleBase4TypeE
 fun:_ZN5blink13CSSParserImpl18consumeDeclarationENS_19CSSParserTokenRangeENS_13StyleRuleBase4TypeE
 }
-{
-   bug_501183_a
-   Memcheck:Leak
-   fun:_Znw*
-   ...
-   fun:_ZN4base9TupleLeafILm1ESt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEEEC2ERKS8_
-   fun:_ZN4base13TupleBaseImplINS_13IndexSequenceIJLm0ELm1ELm2ELm3EEEEJ13scoped_refptrIN3gcm12GCMStoreImpl7BackendEESt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEESsNS_8CallbackIFvbEEEEEC2ERKS7_RKSF_RSC_RKSI_
-   ...
-   fun:_ZN3gcm12GCMStoreImpl20SetGServicesSettingsERKSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEERS5_RKN4base8CallbackIFvbEEE
-}
-{
-   bug_501183_b
-   Memcheck:Leak
-   fun:_Znw*
-   fun:_ZN3gcm12GCMStoreImplC1ERKN4base8FilePathE13scoped_refptrINS1_19SequencedTaskRunnerEE10scoped_ptrINS_9EncryptorENS1_14DefaultDeleterIS9_EEE
-   fun:_ZN3gcm13GCMClientImpl10InitializeERKNS_9GCMClient15ChromeBuildInfoERKN4base8FilePathERK13scoped_refptrINS5_19SequencedTaskRunnerEERKS9_IN3net23URLRequestContextGetterEE10scoped_ptrINS_9EncryptorENS5_14DefaultDeleterISK_EEEPNS1_8DelegateE
-   fun:_ZN3gcm17GCMClientImplTest19InitializeGCMClientEv
-}
-{
-   bug_501183_c
-   Memcheck:Leak
-   fun:_Znw*
-   ...
-   fun:_ZN7leveldb2DB4OpenERKNS_7OptionsERKSsPPS0_
-   fun:_ZN3gcm12GCMStoreImpl7Backend20OpenStoreAndLoadDataENS_8GCMStore13StoreOpenModeEPNS2_10LoadResultE
-}
-{
-   bug_501183_d
-   Memcheck:Leak
-   fun:_Znw*
-   fun:_ZN3gcm17GCMClientImplTestC2Ev
-   fun:_ZN3gcm*GCMClientImpl*TestC2Ev
-}
diff --git a/ui/accessibility/ax_enums.idl b/ui/accessibility/ax_enums.idl
index c6fead9f..da88a544 100644
--- a/ui/accessibility/ax_enums.idl
+++ b/ui/accessibility/ax_enums.idl
@@ -125,6 +125,7 @@
     location_bar,
     log,
     main,
+    mark,
     marquee,
     math,
     menu_bar,
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index d07f239b..8ae61c67 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -221,11 +221,11 @@
 
 void AXTree::DestroyNodeAndSubtree(AXNode* node,
                                    AXTreeUpdateState* update_state) {
+  if (delegate_)
+    delegate_->OnNodeWillBeDeleted(this, node);
   id_map_.erase(node->id());
   for (int i = 0; i < node->child_count(); ++i)
     DestroyNodeAndSubtree(node->ChildAtIndex(i), update_state);
-  if (delegate_)
-    delegate_->OnNodeWillBeDeleted(this, node);
   if (update_state) {
     update_state->pending_nodes.erase(node);
   }
diff --git a/ui/accessibility/ax_tree_unittest.cc b/ui/accessibility/ax_tree_unittest.cc
index 40aeeff..77ef8cd 100644
--- a/ui/accessibility/ax_tree_unittest.cc
+++ b/ui/accessibility/ax_tree_unittest.cc
@@ -325,8 +325,8 @@
   EXPECT_TRUE(tree.Unserialize(update));
 
   ASSERT_EQ(2U, fake_delegate.deleted_ids().size());
-  EXPECT_EQ(2, fake_delegate.deleted_ids()[0]);
-  EXPECT_EQ(1, fake_delegate.deleted_ids()[1]);
+  EXPECT_EQ(1, fake_delegate.deleted_ids()[0]);
+  EXPECT_EQ(2, fake_delegate.deleted_ids()[1]);
 
   ASSERT_EQ(1U, fake_delegate.subtree_deleted_ids().size());
   EXPECT_EQ(1, fake_delegate.subtree_deleted_ids()[0]);
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
index 3bfa7b0f..dd6ae35 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -77,6 +77,7 @@
       {ui::AX_ROLE_LIST_MARKER, @"AXListMarker"},
       {ui::AX_ROLE_LOG, NSAccessibilityGroupRole},
       {ui::AX_ROLE_MAIN, NSAccessibilityGroupRole},
+      {ui::AX_ROLE_MARK, NSAccessibilityGroupRole},
       {ui::AX_ROLE_MARQUEE, NSAccessibilityGroupRole},
       {ui::AX_ROLE_MATH, NSAccessibilityGroupRole},
       {ui::AX_ROLE_MENU, NSAccessibilityMenuRole},
diff --git a/ui/app_list/app_list_constants.cc b/ui/app_list/app_list_constants.cc
index ad73455f..f7321f0 100644
--- a/ui/app_list/app_list_constants.cc
+++ b/ui/app_list/app_list_constants.cc
@@ -85,6 +85,9 @@
 
 // Preferred search result icon sizes.
 const int kListIconSize = 24;
+const int kListBadgeIconSize = 16;
+const int kListBadgeIconOffsetX = 6;
+const int kListBadgeIconOffsetY = 6;
 const int kTileIconSize = 48;
 
 // Preferred number of columns and rows in the centered app list apps grid.
diff --git a/ui/app_list/app_list_constants.h b/ui/app_list/app_list_constants.h
index 2efcee2..3174e12f 100644
--- a/ui/app_list/app_list_constants.h
+++ b/ui/app_list/app_list_constants.h
@@ -60,6 +60,9 @@
 APP_LIST_EXPORT extern const int kGridIconDimension;
 
 APP_LIST_EXPORT extern const int kListIconSize;
+APP_LIST_EXPORT extern const int kListBadgeIconSize;
+APP_LIST_EXPORT extern const int kListBadgeIconOffsetX;
+APP_LIST_EXPORT extern const int kListBadgeIconOffsetY;
 APP_LIST_EXPORT extern const int kTileIconSize;
 
 APP_LIST_EXPORT extern const int kCenteredPreferredCols;
diff --git a/ui/app_list/search_result.cc b/ui/app_list/search_result.cc
index 89ecf18..f1abf588 100644
--- a/ui/app_list/search_result.cc
+++ b/ui/app_list/search_result.cc
@@ -49,6 +49,11 @@
                     OnIconChanged());
 }
 
+void SearchResult::SetBadgeIcon(const gfx::ImageSkia& badge_icon) {
+  badge_icon_ = badge_icon;
+  FOR_EACH_OBSERVER(SearchResultObserver, observers_, OnBadgeIconChanged());
+}
+
 void SearchResult::SetActions(const Actions& sets) {
   actions_ = sets;
   FOR_EACH_OBSERVER(SearchResultObserver,
diff --git a/ui/app_list/search_result.h b/ui/app_list/search_result.h
index a09e186d..90d9f74 100644
--- a/ui/app_list/search_result.h
+++ b/ui/app_list/search_result.h
@@ -91,8 +91,11 @@
   const gfx::ImageSkia& icon() const { return icon_; }
   void SetIcon(const gfx::ImageSkia& icon);
 
+  const gfx::ImageSkia& badge_icon() const { return badge_icon_; }
+  void SetBadgeIcon(const gfx::ImageSkia& badge_icon);
+
   const base::string16& title() const { return title_; }
-  void set_title(const base::string16& title) { title_ = title;}
+  void set_title(const base::string16& title) { title_ = title; }
 
   const Tags& title_tags() const { return title_tags_; }
   void set_title_tags(const Tags& tags) { title_tags_ = tags; }
@@ -174,6 +177,7 @@
   virtual void Open(int event_flags);
 
   gfx::ImageSkia icon_;
+  gfx::ImageSkia badge_icon_;
 
   base::string16 title_;
   Tags title_tags_;
diff --git a/ui/app_list/search_result_observer.h b/ui/app_list/search_result_observer.h
index d8f8900..72ed4fb 100644
--- a/ui/app_list/search_result_observer.h
+++ b/ui/app_list/search_result_observer.h
@@ -14,6 +14,9 @@
   // Invoked when the SearchResult's icon has changed.
   virtual void OnIconChanged() {}
 
+  // Invoked when the SearchResult's badge icon has changed.
+  virtual void OnBadgeIconChanged() {}
+
   // Invoked when the SearchResult's actions have changed.
   virtual void OnActionsChanged() {}
 
diff --git a/ui/app_list/views/contents_view.cc b/ui/app_list/views/contents_view.cc
index 8f05ddca..e8dc14c 100644
--- a/ui/app_list/views/contents_view.cc
+++ b/ui/app_list/views/contents_view.cc
@@ -220,6 +220,8 @@
   int search_page = GetPageIndexForState(AppListModel::STATE_SEARCH_RESULTS);
   DCHECK_GE(search_page, 0);
 
+  search_results_page_view_->ClearSelectedIndex();
+
   SetActiveStateInternal(show ? search_page : page_before_search_, show, true);
 }
 
diff --git a/ui/app_list/views/search_result_container_view.cc b/ui/app_list/views/search_result_container_view.cc
index b8a9852..b024fde 100644
--- a/ui/app_list/views/search_result_container_view.cc
+++ b/ui/app_list/views/search_result_container_view.cc
@@ -10,7 +10,8 @@
 namespace app_list {
 
 SearchResultContainerView::SearchResultContainerView()
-    : selected_index_(-1),
+    : delegate_(nullptr),
+      selected_index_(-1),
       num_results_(0),
       results_(NULL),
       update_factory_(this) {
@@ -86,7 +87,8 @@
   update_factory_.InvalidateWeakPtrs();
   num_results_ = Update();
   Layout();
-  PreferredSizeChanged();
+  if (delegate_)
+    delegate_->OnSearchResultContainerResultsChanged();
 }
 
 }  // namespace app_list
diff --git a/ui/app_list/views/search_result_container_view.h b/ui/app_list/views/search_result_container_view.h
index 14f7e2c..cb7bcb6 100644
--- a/ui/app_list/views/search_result_container_view.h
+++ b/ui/app_list/views/search_result_container_view.h
@@ -19,9 +19,15 @@
 class APP_LIST_EXPORT SearchResultContainerView : public views::View,
                                                   public ui::ListModelObserver {
  public:
+  class Delegate {
+   public:
+    virtual void OnSearchResultContainerResultsChanged() = 0;
+  };
   SearchResultContainerView();
   ~SearchResultContainerView() override;
 
+  void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
   // Sets the search results to listen to.
   void SetResults(AppListModel::SearchResults* results);
   AppListModel::SearchResults* results() { return results_; }
@@ -83,6 +89,8 @@
   // Batching method that actually performs the update and updates layout.
   void DoUpdate();
 
+  Delegate* delegate_;
+
   int selected_index_;
   int num_results_;
 
diff --git a/ui/app_list/views/search_result_page_view.cc b/ui/app_list/views/search_result_page_view.cc
index 52cbe531..e4288127 100644
--- a/ui/app_list/views/search_result_page_view.cc
+++ b/ui/app_list/views/search_result_page_view.cc
@@ -43,11 +43,6 @@
   }
 
   ~SearchCardView() override {}
-
-  void ChildPreferredSizeChanged(views::View* child) override {
-    Layout();
-    PreferredSizeChanged();
-  }
 };
 
 }  // namespace
@@ -78,7 +73,7 @@
   if (select)
     SetSelectedIndex(0, false);
   else
-    result_container_views_[selected_index_]->ClearSelectedIndex();
+    ClearSelectedIndex();
 }
 
 void SearchResultPageView::AddSearchResultContainerView(
@@ -91,11 +86,14 @@
   AddChildView(view_to_add);
   result_container_views_.push_back(result_container);
   result_container->SetResults(results_model);
+  result_container->set_delegate(this);
 }
 
 bool SearchResultPageView::OnKeyPressed(const ui::KeyEvent& event) {
-  if (result_container_views_.at(selected_index_)->OnKeyPressed(event))
+  if (HasSelection() &&
+      result_container_views_.at(selected_index_)->OnKeyPressed(event)) {
     return true;
+  }
 
   int dir = 0;
   bool directional_movement = false;
@@ -130,12 +128,20 @@
   return false;
 }
 
+void SearchResultPageView::ClearSelectedIndex() {
+  if (HasSelection())
+    result_container_views_[selected_index_]->ClearSelectedIndex();
+
+  selected_index_ = -1;
+}
+
 void SearchResultPageView::SetSelectedIndex(int index,
                                             bool directional_movement) {
   bool from_bottom = index < selected_index_;
 
   // Reset the old selected view's selection.
-  result_container_views_[selected_index_]->ClearSelectedIndex();
+  ClearSelectedIndex();
+
   selected_index_ = index;
   // Set the new selected view's selection to its first result.
   result_container_views_[selected_index_]->OnContainerSelected(
@@ -146,9 +152,26 @@
   return index >= 0 && index < static_cast<int>(result_container_views_.size());
 }
 
-void SearchResultPageView::ChildPreferredSizeChanged(views::View* child) {
+void SearchResultPageView::OnSearchResultContainerResultsChanged() {
   DCHECK(!result_container_views_.empty());
 
+  // Only sort and layout the containers when they have all updated.
+  for (SearchResultContainerView* view : result_container_views_) {
+    if (view->UpdateScheduled()) {
+      return;
+    }
+  }
+
+  SearchResultContainerView* old_selection =
+      HasSelection() ? result_container_views_[selected_index_] : nullptr;
+
+  // Truncate the currently selected container's selection if necessary. If
+  // there are no results, the selection will be cleared below.
+  if (old_selection && old_selection->num_results() > 0 &&
+      old_selection->selected_index() >= old_selection->num_results()) {
+    old_selection->SetSelectedIndex(old_selection->num_results() - 1);
+  }
+
   if (switches::IsExperimentalAppListEnabled()) {
     // Sort the result container views by their score.
     std::sort(result_container_views_.begin(), result_container_views_.end(),
@@ -160,19 +183,34 @@
     int result_y_index = 0;
     for (size_t i = 0; i < result_container_views_.size(); ++i) {
       SearchResultContainerView* view = result_container_views_[i];
-      view->ClearSelectedIndex();
       ReorderChildView(view->parent(), i);
 
-      // Only notify containers that have finished updating.
-      if (!view->UpdateScheduled())
-        view->NotifyFirstResultYIndex(result_y_index);
+      view->NotifyFirstResultYIndex(result_y_index);
 
       result_y_index += view->GetYSize();
     }
   }
 
   Layout();
-  SetSelectedIndex(0, false);
+
+  SearchResultContainerView* new_selection = nullptr;
+  if (HasSelection() &&
+      result_container_views_[selected_index_]->num_results() > 0) {
+    new_selection = result_container_views_[selected_index_];
+  }
+
+  // If there was no previous selection or the new view at the selection index
+  // is different from the old one, update the selected view.
+  if (!HasSelection() || old_selection != new_selection) {
+    if (old_selection)
+      old_selection->ClearSelectedIndex();
+
+    int new_selection_index = new_selection ? selected_index_ : 0;
+    // Clear the current selection so that the selection always comes in from
+    // the top.
+    ClearSelectedIndex();
+    SetSelectedIndex(new_selection_index, false);
+  }
 }
 
 gfx::Rect SearchResultPageView::GetPageBoundsForState(
@@ -205,4 +243,8 @@
              : AppListPage::GetSearchBoxZHeight();
 }
 
+void SearchResultPageView::OnHidden() {
+  ClearSelectedIndex();
+}
+
 }  // namespace app_list
diff --git a/ui/app_list/views/search_result_page_view.h b/ui/app_list/views/search_result_page_view.h
index 31d8dd886..8a15b67 100644
--- a/ui/app_list/views/search_result_page_view.h
+++ b/ui/app_list/views/search_result_page_view.h
@@ -12,20 +12,23 @@
 #include "ui/app_list/app_list_export.h"
 #include "ui/app_list/app_list_model.h"
 #include "ui/app_list/views/app_list_page.h"
+#include "ui/app_list/views/search_result_container_view.h"
 
 namespace app_list {
 
 class AppListMainView;
 class AppListViewDelegate;
-class SearchResultContainerView;
 
 // The start page for the experimental app list.
-class APP_LIST_EXPORT SearchResultPageView : public AppListPage {
+class APP_LIST_EXPORT SearchResultPageView
+    : public AppListPage,
+      public SearchResultContainerView::Delegate {
  public:
   SearchResultPageView();
   ~SearchResultPageView() override;
 
   int selected_index() { return selected_index_; }
+  bool HasSelection() { return selected_index_ > -1; }
   void SetSelection(bool select);  // Set or unset result selection.
 
   void AddSearchResultContainerView(
@@ -38,7 +41,6 @@
 
   // Overridden from views::View:
   bool OnKeyPressed(const ui::KeyEvent& event) override;
-  void ChildPreferredSizeChanged(views::View* child) override;
 
   // AppListPage overrides:
   gfx::Rect GetPageBoundsForState(AppListModel::State state) const override;
@@ -46,6 +48,12 @@
                           AppListModel::State from_state,
                           AppListModel::State to_state) override;
   int GetSearchBoxZHeight() const override;
+  void OnHidden() override;
+
+  void ClearSelectedIndex();
+
+  // Overridden from SearchResultContainerView::Delegate :
+  void OnSearchResultContainerResultsChanged() override;
 
  private:
   // |directional_movement| is true if the navigation was caused by directional
@@ -57,6 +65,7 @@
   // the views hierarchy.
   std::vector<SearchResultContainerView*> result_container_views_;
 
+  // -1 indicates no selection.
   int selected_index_;
 
   DISALLOW_COPY_AND_ASSIGN(SearchResultPageView);
diff --git a/ui/app_list/views/search_result_page_view_unittest.cc b/ui/app_list/views/search_result_page_view_unittest.cc
index a198a7f..98d81895 100644
--- a/ui/app_list/views/search_result_page_view_unittest.cc
+++ b/ui/app_list/views/search_result_page_view_unittest.cc
@@ -56,6 +56,7 @@
   void SetUpSearchResults(const std::vector<
       std::pair<SearchResult::DisplayType, int>> result_types) {
     AppListModel::SearchResults* results = GetResults();
+    results->DeleteAll();
     double relevance = result_types.size();
     for (const auto& data : result_types) {
       // Set the relevance of the results in each group in decreasing order (so
@@ -252,5 +253,83 @@
   EXPECT_EQ(tile_list_view(), view()->result_container_views()[1]);
 }
 
+TEST_F(SearchResultPageViewTest, UpdateWithSelection) {
+  {
+    std::vector<std::pair<SearchResult::DisplayType, int>> result_types;
+    result_types.push_back(std::make_pair(SearchResult::DISPLAY_TILE, 3));
+    result_types.push_back(std::make_pair(SearchResult::DISPLAY_LIST, 2));
+
+    SetUpSearchResults(result_types);
+  }
+
+  EXPECT_EQ(0, GetSelectedIndex());
+  EXPECT_EQ(0, tile_list_view()->selected_index());
+  EXPECT_EQ(-1, list_view()->selected_index());
+
+  // Navigate to the second result in the list group.
+  EXPECT_TRUE(KeyPress(ui::VKEY_DOWN));
+  EXPECT_EQ(1, GetSelectedIndex());
+  EXPECT_EQ(-1, tile_list_view()->selected_index());
+  EXPECT_EQ(0, list_view()->selected_index());
+
+  EXPECT_TRUE(KeyPress(ui::VKEY_DOWN));
+  EXPECT_EQ(1, GetSelectedIndex());
+  EXPECT_EQ(-1, tile_list_view()->selected_index());
+  EXPECT_EQ(1, list_view()->selected_index());
+
+  {
+    std::vector<std::pair<SearchResult::DisplayType, int>> result_types;
+    result_types.push_back(std::make_pair(SearchResult::DISPLAY_TILE, 3));
+    result_types.push_back(std::make_pair(SearchResult::DISPLAY_LIST, 3));
+
+    SetUpSearchResults(result_types);
+  }
+
+  // The second list result should still be selected after the update.
+  EXPECT_EQ(1, GetSelectedIndex());
+  EXPECT_EQ(-1, tile_list_view()->selected_index());
+  EXPECT_EQ(1, list_view()->selected_index());
+
+  {
+    std::vector<std::pair<SearchResult::DisplayType, int>> result_types;
+    result_types.push_back(std::make_pair(SearchResult::DISPLAY_TILE, 3));
+    result_types.push_back(std::make_pair(SearchResult::DISPLAY_LIST, 1));
+
+    SetUpSearchResults(result_types);
+  }
+
+  // The first list result should be selected after the update as the second
+  // result has vanished.
+  EXPECT_EQ(1, GetSelectedIndex());
+  EXPECT_EQ(-1, tile_list_view()->selected_index());
+  EXPECT_EQ(0, list_view()->selected_index());
+
+  {
+    std::vector<std::pair<SearchResult::DisplayType, int>> result_types;
+    result_types.push_back(std::make_pair(SearchResult::DISPLAY_LIST, 1));
+    result_types.push_back(std::make_pair(SearchResult::DISPLAY_TILE, 3));
+
+    SetUpSearchResults(result_types);
+  }
+
+  // The tile container should be selected because we hold the selected
+  // container index constant.
+  EXPECT_EQ(1, GetSelectedIndex());
+  EXPECT_EQ(0, tile_list_view()->selected_index());
+  EXPECT_EQ(-1, list_view()->selected_index());
+
+  {
+    std::vector<std::pair<SearchResult::DisplayType, int>> result_types;
+    result_types.push_back(std::make_pair(SearchResult::DISPLAY_LIST, 3));
+
+    SetUpSearchResults(result_types);
+  }
+
+  // The selected container has vanished so we reset the selection to 0.
+  EXPECT_EQ(0, GetSelectedIndex());
+  EXPECT_EQ(-1, tile_list_view()->selected_index());
+  EXPECT_EQ(0, list_view()->selected_index());
+}
+
 }  // namespace test
 }  // namespace app_list
diff --git a/ui/app_list/views/search_result_view.cc b/ui/app_list/views/search_result_view.cc
index 3bfd656..909a05f 100644
--- a/ui/app_list/views/search_result_view.cc
+++ b/ui/app_list/views/search_result_view.cc
@@ -78,11 +78,14 @@
       is_last_result_(false),
       list_view_(list_view),
       icon_(new views::ImageView),
+      badge_icon_(new views::ImageView),
       actions_view_(new SearchResultActionsView(this)),
       progress_bar_(new ProgressBarView) {
   icon_->set_interactive(false);
+  badge_icon_->set_interactive(false);
 
   AddChildView(icon_);
+  AddChildView(badge_icon_);
   AddChildView(actions_view_);
   AddChildView(progress_bar_);
   set_context_menu_controller(this);
@@ -100,6 +103,7 @@
     result_->AddObserver(this);
 
   OnIconChanged();
+  OnBadgeIconChanged();
   OnActionsChanged();
   UpdateTitleText();
   UpdateDetailsText();
@@ -177,6 +181,13 @@
   icon_bounds.Intersect(rect);
   icon_->SetBoundsRect(icon_bounds);
 
+  gfx::Rect badge_icon_bounds(
+      icon_bounds.right() - kListBadgeIconSize + kListBadgeIconOffsetX,
+      icon_bounds.bottom() - kListBadgeIconSize + kListBadgeIconOffsetY,
+      kListBadgeIconSize, kListBadgeIconSize);
+  badge_icon_bounds.Intersect(rect);
+  badge_icon_->SetBoundsRect(badge_icon_bounds);
+
   const int max_actions_width =
       (rect.right() - kActionButtonRightMargin - icon_bounds.right()) / 2;
   int actions_width = std::min(max_actions_width,
@@ -312,7 +323,7 @@
 }
 
 void SearchResultView::OnIconChanged() {
-  gfx::ImageSkia image(result_ ? result_->icon() : gfx::ImageSkia());
+  const gfx::ImageSkia image(result_ ? result_->icon() : gfx::ImageSkia());
   // Note this might leave the view with an old icon. But it is needed to avoid
   // flash when a SearchResult's icon is loaded asynchronously. In this case, it
   // looks nicer to keep the stale icon for a little while on screen instead of
@@ -321,22 +332,42 @@
   if (image.isNull())
     return;
 
+  SetIconImage(image, icon_, kListIconSize);
+}
+
+void SearchResultView::OnBadgeIconChanged() {
+  const gfx::ImageSkia image(result_ ? result_->badge_icon()
+                                     : gfx::ImageSkia());
+  if (image.isNull()) {
+    badge_icon_->SetVisible(false);
+    return;
+  }
+
+  SetIconImage(image, badge_icon_, kListBadgeIconSize);
+  badge_icon_->SetVisible(true);
+}
+
+void SearchResultView::SetIconImage(const gfx::ImageSkia& source,
+                                    views::ImageView* const icon,
+                                    const int icon_dimension) {
+  // Copy.
+  gfx::ImageSkia image(source);
+
   // Scales down big icons but leave small ones unchanged.
-  if (image.width() > kListIconSize || image.height() > kListIconSize) {
+  if (image.width() > icon_dimension || image.height() > icon_dimension) {
     image = gfx::ImageSkiaOperations::CreateResizedImage(
-        image,
-        skia::ImageOperations::RESIZE_BEST,
-        gfx::Size(kListIconSize, kListIconSize));
+        image, skia::ImageOperations::RESIZE_BEST,
+        gfx::Size(icon_dimension, icon_dimension));
   } else {
-    icon_->ResetImageSize();
+    icon->ResetImageSize();
   }
 
   // Set the image to an empty image before we reset the image because
   // since we're using the same backing store for our images, sometimes
   // ImageView won't detect that we have a new image set due to the pixel
   // buffer pointers remaining the same despite the image changing.
-  icon_->SetImage(gfx::ImageSkia());
-  icon_->SetImage(image);
+  icon->SetImage(gfx::ImageSkia());
+  icon->SetImage(image);
 }
 
 void SearchResultView::OnActionsChanged() {
diff --git a/ui/app_list/views/search_result_view.h b/ui/app_list/views/search_result_view.h
index 279a2a3e..1167cc2 100644
--- a/ui/app_list/views/search_result_view.h
+++ b/ui/app_list/views/search_result_view.h
@@ -92,11 +92,16 @@
 
   // SearchResultObserver overrides:
   void OnIconChanged() override;
+  void OnBadgeIconChanged() override;
   void OnActionsChanged() override;
   void OnIsInstallingChanged() override;
   void OnPercentDownloadedChanged() override;
   void OnItemInstalled() override;
 
+  void SetIconImage(const gfx::ImageSkia& source,
+                    views::ImageView* const icon,
+                    const int icon_dimension);
+
   // SearchResultActionsViewDelegate overrides:
   void OnSearchResultActionActivated(size_t index, int event_flags) override;
 
@@ -108,6 +113,7 @@
   SearchResultListView* list_view_;
 
   views::ImageView* icon_;  // Owned by views hierarchy.
+  views::ImageView* badge_icon_;  // Owned by views hierarchy.
   scoped_ptr<gfx::RenderText> title_text_;
   scoped_ptr<gfx::RenderText> details_text_;
   SearchResultActionsView* actions_view_;  // Owned by the views hierarchy.
diff --git a/ui/aura/window_tree_host_ozone.cc b/ui/aura/window_tree_host_ozone.cc
index fd2854c6..8671e3a6 100644
--- a/ui/aura/window_tree_host_ozone.cc
+++ b/ui/aura/window_tree_host_ozone.cc
@@ -101,7 +101,8 @@
 }
 
 void WindowTreeHostOzone::OnAcceleratedWidgetAvailable(
-    gfx::AcceleratedWidget widget) {
+    gfx::AcceleratedWidget widget,
+    float device_pixel_ratio) {
   widget_ = widget;
   CreateCompositor(widget_);
 }
diff --git a/ui/aura/window_tree_host_ozone.h b/ui/aura/window_tree_host_ozone.h
index 67f01d99..b356a30 100644
--- a/ui/aura/window_tree_host_ozone.h
+++ b/ui/aura/window_tree_host_ozone.h
@@ -48,7 +48,8 @@
   void OnClosed() override;
   void OnWindowStateChanged(ui::PlatformWindowState new_state) override;
   void OnLostCapture() override;
-  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override;
+  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget,
+                                    float device_pixel_ratio) override;
   void OnActivationChanged(bool active) override;
 
   // Platform-specific part of this WindowTreeHost.
diff --git a/ui/aura/window_tree_host_win.cc b/ui/aura/window_tree_host_win.cc
index 3252093..6f879ab 100644
--- a/ui/aura/window_tree_host_win.cc
+++ b/ui/aura/window_tree_host_win.cc
@@ -151,7 +151,8 @@
 }
 
 void WindowTreeHostWin::OnAcceleratedWidgetAvailable(
-    gfx::AcceleratedWidget widget) {
+    gfx::AcceleratedWidget widget,
+    float device_pixel_ratio) {
   widget_ = widget;
   CreateCompositor(widget);
 }
diff --git a/ui/aura/window_tree_host_win.h b/ui/aura/window_tree_host_win.h
index 6d0054c..e49890d2 100644
--- a/ui/aura/window_tree_host_win.h
+++ b/ui/aura/window_tree_host_win.h
@@ -47,7 +47,8 @@
   void OnClosed() override;
   void OnWindowStateChanged(ui::PlatformWindowState new_state) override;
   void OnLostCapture() override;
-  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override;
+  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget,
+                                    float device_pixel_ratio) override;
   void OnActivationChanged(bool active) override;
 
   bool has_capture_;
diff --git a/ui/base/resource/data_pack_unittest.cc b/ui/base/resource/data_pack_unittest.cc
index 4ab4faa..b0932fc 100644
--- a/ui/base/resource/data_pack_unittest.cc
+++ b/ui/base/resource/data_pack_unittest.cc
@@ -110,7 +110,7 @@
 
   // Load the file through the data pack API.
   DataPack pack(SCALE_FACTOR_100P);
-  base::MemoryMappedFile::Region region(sizeof(kPadding), kSamplePakSize);
+  base::MemoryMappedFile::Region region = {sizeof(kPadding), kSamplePakSize};
   ASSERT_TRUE(pack.LoadFromFileRegion(file.Pass(), region));
 
   base::StringPiece data;
diff --git a/ui/chromeos/ime/candidate_window_view_unittest.cc b/ui/chromeos/ime/candidate_window_view_unittest.cc
index 6bfa059..6b18a48 100644
--- a/ui/chromeos/ime/candidate_window_view_unittest.cc
+++ b/ui/chromeos/ime/candidate_window_view_unittest.cc
@@ -74,6 +74,11 @@
     candidate_window_view_->InitWidget();
   }
 
+  void TearDown() override {
+    candidate_window_view_->GetWidget()->CloseNow();
+    views::ViewsTestBase::TearDown();
+  }
+
   CandidateWindowView* candidate_window_view() {
     return candidate_window_view_;
   }
@@ -109,8 +114,7 @@
   }
 
  private:
-  // owned by |parent_|.
-  CandidateWindowView* candidate_window_view_;
+  CandidateWindowView* candidate_window_view_;  // Owned by its Widget.
 
   DISALLOW_COPY_AND_ASSIGN(CandidateWindowViewTest);
 };
diff --git a/ui/events/ozone/evdev/event_converter_evdev.cc b/ui/events/ozone/evdev/event_converter_evdev.cc
index cea9c036..ff7bbc0 100644
--- a/ui/events/ozone/evdev/event_converter_evdev.cc
+++ b/ui/events/ozone/evdev/event_converter_evdev.cc
@@ -77,12 +77,8 @@
   return 0;
 }
 
-void EventConverterEvdev::SetAllowedKeys(
-    scoped_ptr<std::set<DomCode>> allowed_keys) {
-  NOTREACHED();
-}
-
-void EventConverterEvdev::AllowAllKeys() {
+void EventConverterEvdev::SetKeyFilter(bool enable_filter,
+                                       std::vector<DomCode> allowed_keys) {
   NOTREACHED();
 }
 
diff --git a/ui/events/ozone/evdev/event_converter_evdev.h b/ui/events/ozone/evdev/event_converter_evdev.h
index b23d2b3..d4180f2d 100644
--- a/ui/events/ozone/evdev/event_converter_evdev.h
+++ b/ui/events/ozone/evdev/event_converter_evdev.h
@@ -74,11 +74,10 @@
   // called unless HasTouchscreen() returns true
   virtual int GetTouchPoints() const;
 
-  // Sets which keyboard keys should be processed.
-  virtual void SetAllowedKeys(scoped_ptr<std::set<DomCode>> allowed_keys);
-
-  // Allows all keys to be processed.
-  virtual void AllowAllKeys();
+  // Sets which keyboard keys should be processed. If |enable_filter| is
+  // false, all keys are allowed and |allowed_keys| is ignored.
+  virtual void SetKeyFilter(bool enable_filter,
+                            std::vector<DomCode> allowed_keys);
 
   // Update caps lock LED state.
   virtual void SetCapsLockLed(bool enabled);
diff --git a/ui/events/ozone/evdev/event_converter_evdev_impl.cc b/ui/events/ozone/evdev/event_converter_evdev_impl.cc
index c12c76a4..6fb020de 100644
--- a/ui/events/ozone/evdev/event_converter_evdev_impl.cc
+++ b/ui/events/ozone/evdev/event_converter_evdev_impl.cc
@@ -87,16 +87,15 @@
   return has_caps_lock_led_;
 }
 
-void EventConverterEvdevImpl::SetAllowedKeys(
-    scoped_ptr<std::set<DomCode>> allowed_keys) {
-  DCHECK(HasKeyboard());
-  if (!allowed_keys) {
+void EventConverterEvdevImpl::SetKeyFilter(bool enable_filter,
+                                           std::vector<DomCode> allowed_keys) {
+  if (!enable_filter) {
     blocked_keys_.reset();
     return;
   }
 
   blocked_keys_.set();
-  for (const DomCode& it : *allowed_keys) {
+  for (const DomCode& it : allowed_keys) {
     int evdev_code =
         NativeCodeToEvdevCode(KeycodeConverter::DomCodeToNativeKeycode(it));
     blocked_keys_.reset(evdev_code);
@@ -110,11 +109,6 @@
   }
 }
 
-void EventConverterEvdevImpl::AllowAllKeys() {
-  DCHECK(HasKeyboard());
-  blocked_keys_.reset();
-}
-
 void EventConverterEvdevImpl::OnStopped() {
   ReleaseKeys();
   ReleaseMouseButtons();
diff --git a/ui/events/ozone/evdev/event_converter_evdev_impl.h b/ui/events/ozone/evdev/event_converter_evdev_impl.h
index d3df402ef..099c380 100644
--- a/ui/events/ozone/evdev/event_converter_evdev_impl.h
+++ b/ui/events/ozone/evdev/event_converter_evdev_impl.h
@@ -42,8 +42,8 @@
   bool HasKeyboard() const override;
   bool HasTouchpad() const override;
   bool HasCapsLockLed() const override;
-  void SetAllowedKeys(scoped_ptr<std::set<DomCode>> allowed_keys) override;
-  void AllowAllKeys() override;
+  void SetKeyFilter(bool enable_filter,
+                    std::vector<DomCode> allowed_keys) override;
   void OnStopped() override;
 
   void ProcessEvents(const struct input_event* inputs, int count);
diff --git a/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc b/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc
index f67ae69d..bbe2cb6 100644
--- a/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc
+++ b/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc
@@ -575,9 +575,9 @@
   EXPECT_EQ(ui::VKEY_POWER, event->key_code());
 
   ClearDispatchedEvents();
-  scoped_ptr<std::set<ui::DomCode>> allowed_keys(new std::set<ui::DomCode>);
-  allowed_keys->insert(ui::DomCode::POWER);
-  dev->SetAllowedKeys(allowed_keys.Pass());
+  std::vector<ui::DomCode> allowed_keys;
+  allowed_keys.push_back(ui::DomCode::POWER);
+  dev->SetKeyFilter(true /* enable_filter */, allowed_keys);
   dev->ProcessEvents(mock_kernel_queue, arraysize(mock_kernel_queue));
 
   ASSERT_EQ(2u, size());
@@ -589,7 +589,7 @@
   EXPECT_EQ(ui::VKEY_POWER, event->key_code());
 
   ClearDispatchedEvents();
-  dev->AllowAllKeys();
+  dev->SetKeyFilter(false /* enable_filter */, std::vector<ui::DomCode>());
   dev->ProcessEvents(mock_kernel_queue, arraysize(mock_kernel_queue));
 
   event = dispatched_event(0);
@@ -628,8 +628,8 @@
   // Block all key events. Calling SetAllowKeys() should dispatch a synthetic
   // key release for VKEY_A.
   ClearDispatchedEvents();
-  scoped_ptr<std::set<ui::DomCode>> allowed_keys(new std::set<ui::DomCode>);
-  dev->SetAllowedKeys(allowed_keys.Pass());
+  std::vector<ui::DomCode> allowed_keys;
+  dev->SetKeyFilter(true /* enable_filter */, allowed_keys);
   ASSERT_EQ(1u, size());
   event = dispatched_event(0);
   EXPECT_EQ(ui::ET_KEY_RELEASED, event->type());
diff --git a/ui/events/ozone/evdev/input_controller_evdev.cc b/ui/events/ozone/evdev/input_controller_evdev.cc
index 037e488..ecc58eab 100644
--- a/ui/events/ozone/evdev/input_controller_evdev.cc
+++ b/ui/events/ozone/evdev/input_controller_evdev.cc
@@ -77,27 +77,17 @@
   keyboard_->GetAutoRepeatRate(delay, interval);
 }
 
-void InputControllerEvdev::DisableInternalTouchpad() {
-  if (input_device_factory_)
-    input_device_factory_->DisableInternalTouchpad();
+void InputControllerEvdev::SetInternalTouchpadEnabled(bool enabled) {
+  input_device_settings_.enable_internal_touchpad = enabled;
+  ScheduleUpdateDeviceSettings();
 }
 
-void InputControllerEvdev::EnableInternalTouchpad() {
-  if (input_device_factory_)
-    input_device_factory_->EnableInternalTouchpad();
-}
-
-void InputControllerEvdev::DisableInternalKeyboardExceptKeys(
-    scoped_ptr<std::set<DomCode>> excepted_keys) {
-  if (input_device_factory_) {
-    input_device_factory_->DisableInternalKeyboardExceptKeys(
-        excepted_keys.Pass());
-  }
-}
-
-void InputControllerEvdev::EnableInternalKeyboard() {
-  if (input_device_factory_)
-    input_device_factory_->EnableInternalKeyboard();
+void InputControllerEvdev::SetInternalKeyboardFilter(
+    bool enable_filter,
+    std::vector<DomCode> allowed_keys) {
+  input_device_settings_.enable_internal_keyboard_filter = enable_filter;
+  input_device_settings_.internal_keyboard_allowed_keys = allowed_keys;
+  ScheduleUpdateDeviceSettings();
 }
 
 void InputControllerEvdev::SetTouchpadSensitivity(int value) {
diff --git a/ui/events/ozone/evdev/input_controller_evdev.h b/ui/events/ozone/evdev/input_controller_evdev.h
index 535934fb..5459a10e 100644
--- a/ui/events/ozone/evdev/input_controller_evdev.h
+++ b/ui/events/ozone/evdev/input_controller_evdev.h
@@ -57,11 +57,9 @@
   void GetTouchDeviceStatus(const GetTouchDeviceStatusReply& reply) override;
   void GetTouchEventLog(const base::FilePath& out_dir,
                         const GetTouchEventLogReply& reply) override;
-  void DisableInternalTouchpad() override;
-  void EnableInternalTouchpad() override;
-  void DisableInternalKeyboardExceptKeys(
-      scoped_ptr<std::set<DomCode>> excepted_keys) override;
-  void EnableInternalKeyboard() override;
+  void SetInternalTouchpadEnabled(bool enabled) override;
+  void SetInternalKeyboardFilter(bool enable_filter,
+                                 std::vector<DomCode> allowed_keys) override;
 
  private:
   // Post task to update settings.
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.cc b/ui/events/ozone/evdev/input_device_factory_evdev.cc
index 64a037f0..1ff24fa 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev.cc
+++ b/ui/events/ozone/evdev/input_device_factory_evdev.cc
@@ -276,48 +276,6 @@
   }
 }
 
-void InputDeviceFactoryEvdev::DisableInternalTouchpad() {
-  for (const auto& it : converters_) {
-    EventConverterEvdev* converter = it.second;
-    if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
-        converter->HasTouchpad()) {
-      DCHECK(!converter->HasKeyboard());
-      converter->set_ignore_events(true);
-    }
-  }
-}
-
-void InputDeviceFactoryEvdev::EnableInternalTouchpad() {
-  for (const auto& it : converters_) {
-    EventConverterEvdev* converter = it.second;
-    if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
-        converter->HasTouchpad()) {
-      DCHECK(!converter->HasKeyboard());
-      converter->set_ignore_events(false);
-    }
-  }
-}
-
-void InputDeviceFactoryEvdev::DisableInternalKeyboardExceptKeys(
-    scoped_ptr<std::set<DomCode>> excepted_keys) {
-  for (const auto& it : converters_) {
-    EventConverterEvdev* converter = it.second;
-    if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
-        converter->HasKeyboard()) {
-      converter->SetAllowedKeys(excepted_keys.Pass());
-    }
-  }
-}
-
-void InputDeviceFactoryEvdev::EnableInternalKeyboard() {
-  for (const auto& it : converters_) {
-    EventConverterEvdev* converter = it.second;
-    if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
-        converter->HasKeyboard()) {
-      converter->AllowAllKeys();
-    }
-  }
-}
 
 void InputDeviceFactoryEvdev::SetCapsLockLed(bool enabled) {
   caps_lock_led_enabled_ = enabled;
@@ -379,6 +337,18 @@
 
   SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Paused",
                             input_device_settings_.tap_to_click_paused);
+
+  for (const auto& it : converters_) {
+    EventConverterEvdev* converter = it.second;
+    converter->set_ignore_events(!IsDeviceEnabled(converter));
+
+    if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
+        converter->HasKeyboard()) {
+      converter->SetKeyFilter(
+          input_device_settings_.enable_internal_keyboard_filter,
+          input_device_settings_.internal_keyboard_allowed_keys);
+    }
+  }
 }
 
 void InputDeviceFactoryEvdev::ApplyCapsLockLed() {
@@ -388,6 +358,16 @@
   }
 }
 
+bool InputDeviceFactoryEvdev::IsDeviceEnabled(
+    const EventConverterEvdev* converter) {
+  if (!input_device_settings_.enable_internal_touchpad &&
+      converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
+      converter->HasTouchpad())
+    return false;
+
+  return true;
+}
+
 void InputDeviceFactoryEvdev::UpdateDirtyFlags(
     const EventConverterEvdev* converter) {
   if (converter->HasTouchscreen())
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.h b/ui/events/ozone/evdev/input_device_factory_evdev.h
index 0a43694e..8f9b3a73 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev.h
+++ b/ui/events/ozone/evdev/input_device_factory_evdev.h
@@ -53,19 +53,6 @@
   // Device thread handler for initial scan completion.
   void OnStartupScanComplete();
 
-  // Disables the internal touchpad.
-  void DisableInternalTouchpad();
-
-  // Enables the internal touchpad.
-  void EnableInternalTouchpad();
-
-  // Disables all keys on the internal keyboard except |excepted_keys|.
-  void DisableInternalKeyboardExceptKeys(
-      scoped_ptr<std::set<DomCode>> excepted_keys);
-
-  // Enables all keys on the internal keyboard.
-  void EnableInternalKeyboard();
-
   // LED state.
   void SetCapsLockLed(bool enabled);
 
@@ -88,6 +75,9 @@
   void ApplyInputDeviceSettings();
   void ApplyCapsLockLed();
 
+  // Policy for device enablement.
+  bool IsDeviceEnabled(const EventConverterEvdev* converter);
+
   // Update observers on device changes.
   void UpdateDirtyFlags(const EventConverterEvdev* converter);
   void NotifyDevicesUpdated();
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
index a52ea83..2ebf0578 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
+++ b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
@@ -60,32 +60,6 @@
                             input_device_factory_));
 }
 
-void InputDeviceFactoryEvdevProxy::DisableInternalTouchpad() {
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(&InputDeviceFactoryEvdev::DisableInternalTouchpad,
-                            input_device_factory_));
-}
-
-void InputDeviceFactoryEvdevProxy::EnableInternalTouchpad() {
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(&InputDeviceFactoryEvdev::EnableInternalTouchpad,
-                            input_device_factory_));
-}
-
-void InputDeviceFactoryEvdevProxy::DisableInternalKeyboardExceptKeys(
-    scoped_ptr<std::set<DomCode>> excepted_keys) {
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&InputDeviceFactoryEvdev::DisableInternalKeyboardExceptKeys,
-                 input_device_factory_, base::Passed(&excepted_keys)));
-}
-
-void InputDeviceFactoryEvdevProxy::EnableInternalKeyboard() {
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(&InputDeviceFactoryEvdev::EnableInternalKeyboard,
-                            input_device_factory_));
-}
-
 void InputDeviceFactoryEvdevProxy::SetCapsLockLed(bool enabled) {
   task_runner_->PostTask(FROM_HERE,
                          base::Bind(&InputDeviceFactoryEvdev::SetCapsLockLed,
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
index 8913f49e..46b707e 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
+++ b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
@@ -41,11 +41,6 @@
   void AddInputDevice(int id, const base::FilePath& path);
   void RemoveInputDevice(const base::FilePath& path);
   void OnStartupScanComplete();
-  void DisableInternalTouchpad();
-  void EnableInternalTouchpad();
-  void DisableInternalKeyboardExceptKeys(
-      scoped_ptr<std::set<DomCode>> excepted_keys);
-  void EnableInternalKeyboard();
   void SetCapsLockLed(bool enabled);
   void UpdateInputDeviceSettings(const InputDeviceSettingsEvdev& settings);
   void GetTouchDeviceStatus(const GetTouchDeviceStatusReply& reply);
diff --git a/ui/events/ozone/evdev/input_device_settings_evdev.h b/ui/events/ozone/evdev/input_device_settings_evdev.h
index 92dfaf4..0d95796 100644
--- a/ui/events/ozone/evdev/input_device_settings_evdev.h
+++ b/ui/events/ozone/evdev/input_device_settings_evdev.h
@@ -5,8 +5,12 @@
 #ifndef UI_EVENTS_OZONE_EVDEV_INPUT_DEVICE_SETTINGS_EVDEV_H_
 #define UI_EVENTS_OZONE_EVDEV_INPUT_DEVICE_SETTINGS_EVDEV_H_
 
+#include <vector>
+
 namespace ui {
 
+enum class DomCode;
+
 struct InputDeviceSettingsEvdev {
   InputDeviceSettingsEvdev();
   InputDeviceSettingsEvdev(const InputDeviceSettingsEvdev& other);
@@ -24,6 +28,10 @@
 
   int touchpad_sensitivity = kDefaultSensitivity;
   int mouse_sensitivity = kDefaultSensitivity;
+
+  bool enable_internal_touchpad = true;
+  bool enable_internal_keyboard_filter = false;
+  std::vector<DomCode> internal_keyboard_allowed_keys;
 };
 
 }  // namespace ui
diff --git a/ui/file_manager/audio_player/js/background.js b/ui/file_manager/audio_player/js/background.js
index 233bea3..e0173165 100644
--- a/ui/file_manager/audio_player/js/background.js
+++ b/ui/file_manager/audio_player/js/background.js
@@ -12,6 +12,8 @@
  */
 var AUDIO_PLAYER_ICON = 'icons/audio-player-64.png';
 
+var AUDIO_PLAYER_APP_URL = 'audio_player.html';
+
 /**
  * Configuration of the audio player panel.
  * @type {Object}
@@ -35,7 +37,7 @@
  * Wrapper of audio player window.
  * @type {SingletonAppWindowWrapper}
  */
-var audioPlayer = new SingletonAppWindowWrapper('audio_player.html',
+var audioPlayer = new SingletonAppWindowWrapper(AUDIO_PLAYER_APP_URL,
                                                 audioPlayerCreateOptions);
 
 /**
@@ -160,11 +162,9 @@
   }).then(function() {
     audioPlayer.setIcon(AUDIO_PLAYER_ICON);
     audioPlayer.rawAppWindow.focus();
+    return AUDIO_PLAYER_APP_URL;
   }).catch(function(error) {
     console.error('Launch failed' + error.stack || error);
     return Promise.reject(error);
   });
 }
-
-// Register the test utils.
-test.util.registerRemoteTestUtils();
diff --git a/ui/file_manager/audio_player/js/test_util.js b/ui/file_manager/audio_player/js/test_util.js
new file mode 100644
index 0000000..24a7317
--- /dev/null
+++ b/ui/file_manager/audio_player/js/test_util.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.
+
+/**
+ * Opens the audio player and waits until it is ready.
+ *
+ * @param {Array<string>} urls URLs to be opened.
+ *
+ */
+test.util.async.openAudioPlayer = function(urls, callback) {
+  open({items: urls, position: 0}, false).then(callback);
+};
+
+// Register the test utils.
+test.util.registerRemoteTestUtils();
diff --git a/ui/file_manager/audio_player/manifest.json b/ui/file_manager/audio_player/manifest.json
index 75c050d..38fb787 100644
--- a/ui/file_manager/audio_player/manifest.json
+++ b/ui/file_manager/audio_player/manifest.json
@@ -62,6 +62,7 @@
         "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/background/js/test_util_base.js",
         "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/background/js/volume_manager.js",
         "js/error_util.js",
+        "js/test_util.js",
         // The main background script must be at the end.
         "js/background.js"
       ]
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 fb9fcb13..b50d48df 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
@@ -73,6 +73,7 @@
   'oobinhbdbiehknkpbpejbbpdbkdjmoco',  // File Manager test
   'ejhcmmdhhpdhhgmifplfmjobgegbibkn',  // Gallery test
   'ljoplibgfehghmibaoaepfagnmbbfiga',  // Video Player test
+  'ddabbgbggambiildohfagdkliahiecfl',  // Audio Player test
 ];
 
 /**
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 6a5eb19..bf95115 100644
--- a/ui/file_manager/file_manager/common/js/importer_common.js
+++ b/ui/file_manager/file_manager/common/js/importer_common.js
@@ -345,7 +345,7 @@
             if (id) {
               return id;
             }
-            var id = importer.generateId();
+            id = importer.generateId();
             return storage.set(importer.Setting.MACHINE_ID, id)
                 .then(
                     function() {
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 1aedba3a..6121506 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -1504,6 +1504,7 @@
 }
 
 .status-icon {
+  -webkit-margin-end: 10px;
   height: 24px;
   width: 24px;
 }
diff --git a/ui/file_manager/file_manager/foreground/css/file_status.css b/ui/file_manager/file_manager/foreground/css/file_status.css
index 3c182bc..0684c74 100644
--- a/ui/file_manager/file_manager/foreground/css/file_status.css
+++ b/ui/file_manager/file_manager/foreground/css/file_status.css
@@ -5,11 +5,15 @@
 /* Small icons for file statuses, used in lists and menus. */
 [file-status-icon] {
   background-image: none;
-  background-position: center;
+  background-position: right center;
   background-repeat: no-repeat;
   background-size: 16px 16px;
 }
 
+html[dir='rtl'] [file-status-icon] {
+  background-position: left center;
+}
+
 [file-status-icon='copied'] {
   background-image: -webkit-image-set(
       url(../images/files/ui/cloud_import_syncing.png) 1x,
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 03348206..7c80240 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -445,6 +445,7 @@
     this.toolbarController_ = new ToolbarController(
         this.ui_.toolbar,
         this.ui_.dialogNavigationList,
+        this.ui_.listContainer,
         assert(this.ui_.locationLine),
         this.selectionHandler_,
         this.directoryModel_);
@@ -747,10 +748,8 @@
     // Show the window as soon as the UI pre-initialization is done.
     if (this.dialogType == DialogType.FULL_PAGE && !util.runningInBrowser()) {
       chrome.app.window.current().show();
-      setTimeout(callback, 100);  // Wait until the animation is finished.
-    } else {
-      callback();
     }
+    callback();
   };
 
   /**
diff --git a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
index 069d4836..97cac5f 100644
--- a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
@@ -9,6 +9,7 @@
  * @param {!HTMLElement} toolbar Toolbar element which contains controls.
  * @param {!HTMLElement} navigationList Navigation list on the left pane. The
  *     position of silesSelectedLabel depends on the navitaion list's width.
+ * @param {!ListContainer} listContainer List container.
  * @param {!LocationLine} locationLine Location line shown on the left side of
  *     the toolbar.
  * @param {!FileSelectionHandler} selectionHandler
@@ -18,6 +19,7 @@
  */
 function ToolbarController(toolbar,
                            navigationList,
+                           listContainer,
                            locationLine,
                            selectionHandler,
                            directoryModel) {
@@ -69,6 +71,12 @@
   this.navigationList_ = navigationList;
 
   /**
+   * @private {!ListContainer}
+   * @const
+   */
+  this.listContainer_ = listContainer;
+
+  /**
    * @private {!LocationLine}
    * @const
    */
@@ -162,7 +170,7 @@
  */
 ToolbarController.prototype.onDeleteButtonClicked_ = function() {
   this.deleteButton_.blur();
-  this.deleteCommand_.execute();
+  this.deleteCommand_.execute(this.listContainer_.currentList);
 }
 
 /**
diff --git a/ui/file_manager/file_manager_resources.grd b/ui/file_manager/file_manager_resources.grd
index 6a0d2b5a..e7dc3700 100644
--- a/ui/file_manager/file_manager_resources.grd
+++ b/ui/file_manager/file_manager_resources.grd
@@ -165,6 +165,8 @@
       <include name="IDR_AUDIO_PLAYER_ELEMENTS_VOLUME_CONTROLLER_HTML" file="audio_player/elements/volume_controller.html" type="BINDATA" />
       <include name="IDR_AUDIO_PLAYER_ELEMENTS_VOLUME_CONTROLLER_JS" file="audio_player/elements/volume_controller.js" type="BINDATA" />
       <include name="IDR_AUDIO_PLAYER_METADATA_WORKER_JS" file="audio_player/js/metadata_worker.js" flattenhtml="true" type="BINDATA" />
+      <include name="IDR_AUDIO_PLAYER_ERROR_UTIL_JS" file="audio_player/js/error_util.js" flattenhtml="false" type="BINDATA" />
+      <include name="IDR_AUDIO_PLAYER_TEST_UTIL_JS" file="audio_player/js/test_util.js" flattenhtml="false" type="BINDATA" />
       <include name="IDR_AUDIO_PLAYER" file="audio_player/audio_player.html" allowexternalscript="true" flattenhtml="true" type="BINDATA" />
       <include name="IDR_AUDIO_PLAYER_JS" file="audio_player/js/audio_player_scripts.js" flattenhtml="true" type="BINDATA" />
       <include name="IDR_AUDIO_PLAYER_ICON_FAVICON_16" file="audio_player/icons/audio-player-favicon-16.png" type="BINDATA" />
diff --git a/ui/file_manager/gallery/js/gallery_util.js b/ui/file_manager/gallery/js/gallery_util.js
index 7b04bfd..860dbb1 100644
--- a/ui/file_manager/gallery/js/gallery_util.js
+++ b/ui/file_manager/gallery/js/gallery_util.js
@@ -49,7 +49,7 @@
       // file extensions is enough.
       return FileType.isImage(entry) || FileType.isRaw(entry);
     }).sort(function(a, b) {
-      return a.name.localeCompare(b.name);
+      return util.compareName(a, b);
     });
   });
 };
diff --git a/ui/file_manager/gallery/js/gallery_util_unittest.html b/ui/file_manager/gallery/js/gallery_util_unittest.html
index 5021661..68bf4850 100644
--- a/ui/file_manager/gallery/js/gallery_util_unittest.html
+++ b/ui/file_manager/gallery/js/gallery_util_unittest.html
@@ -6,6 +6,7 @@
 
 <script src="../../file_manager/common/js/file_type.js"></script>
 <script src="../../file_manager/common/js/mock_entry.js"></script>
+<script src="../../file_manager/common/js/util.js"></script>
 <script src="../../file_manager/common/js/unittest_util.js"></script>
 <script src="gallery_util.js"></script>
 
diff --git a/ui/file_manager/integration_tests/audio_player/background.js b/ui/file_manager/integration_tests/audio_player/background.js
new file mode 100644
index 0000000..f389ec96
--- /dev/null
+++ b/ui/file_manager/integration_tests/audio_player/background.js
@@ -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.
+
+'use strict';
+
+/**
+ * Extension ID of audio player.
+ * @type {string}
+ * @const
+ */
+var AUDIO_PLAYER_APP_ID = 'cjbfomnbifhcdnihkgipgfcihmgjfhbf';
+
+/**
+ * Extension ID of Files.app.
+ * @type {string}
+ * @const
+ */
+var FILE_MANAGER_EXTENSIONS_ID = 'hhaomjibdihmijegdhdafkllkbggdgoj';
+
+var remoteCallFilesApp = new RemoteCallFilesApp(FILE_MANAGER_EXTENSIONS_ID);
+var remoteCallAudioPlayer = new RemoteCall(AUDIO_PLAYER_APP_ID);
+
+/**
+ * Launches the audio player with the given entries.
+ *
+ * @param {string} testVolumeName Test volume name passed to the addEntries
+ *     function. Either 'drive' or 'local'.
+ * @param {VolumeManagerCommon.VolumeType} volumeType Volume type.
+ * @param {Array<TestEntryInfo>} entries Entries to be parepared and passed to
+ *     the application.
+ * @param {Array<TestEntryInfo>=} opt_selected Entries to be selected. Should
+ *     be a sub-set of the entries argument.
+ * @return {Promise} Promise to be fulfilled with the audio player element.
+ */
+function launch(testVolumeName, volumeType, entries, opt_selected) {
+  var entriesPromise = addEntries([testVolumeName], entries).then(function() {
+    var selectedEntries = opt_selected || entries;
+    var selectedEntryNames =
+        selectedEntries.map(function(entry) { return entry.nameText; });
+    return remoteCallAudioPlayer.getFilesUnderVolume(
+        volumeType, selectedEntryNames);
+  });
+
+  var appWindow = null;
+  return entriesPromise.then(function(urls) {
+    return remoteCallAudioPlayer.callRemoteTestUtil(
+        'openAudioPlayer', null, [urls, 0]);
+  }).then(function(windowId) {
+    appWindow = windowId;
+    return remoteCallAudioPlayer.waitForElement(appWindow, 'body');
+  }).then(function() {
+    return Promise.all([
+      remoteCallAudioPlayer.waitForElement(
+          appWindow, 'audio-player[playing]'),
+    ]);
+  }).then(function(args) {
+    return [appWindow, args[0]];
+  });
+}
+
+/**
+ * Verifies if there are no Javascript errors in any of the app windows.
+ * @param {function()} Completion callback.
+ */
+function checkIfNoErrorsOccured(callback) {
+  var countPromise = remoteCallAudioPlayer.callRemoteTestUtil(
+      'getErrorCount', null, []);
+  countPromise.then(function(count) {
+    chrome.test.assertEq(0, count, 'The error count is not 0.');
+    callback();
+  });
+}
+
+/**
+ * Adds check of chrome.test to the end of the given promise.
+ * @param {Promise} promise Promise.
+ */
+function testPromise(promise) {
+  promise.then(function() {
+    return new Promise(checkIfNoErrorsOccured);
+  }).then(chrome.test.callbackPass(function() {
+    // The callbacPass is necessary to avoid prematurely finishing tests.
+    // Don't put chrome.test.succeed() here to avoid doubled success log.
+  }), function(error) {
+    chrome.test.fail(error.stack || error);
+  });
+};
+
+/**
+ * Namespace for test cases.
+ */
+var testcase = {};
+
+// Ensure the test cases are loaded.
+window.addEventListener('load', function() {
+  var steps = [
+    // Check for the guest mode.
+    function() {
+      chrome.test.sendMessage(
+          JSON.stringify({name: 'isInGuestMode'}), steps.shift());
+    },
+    // Obtain the test case name.
+    function(result) {
+      if (JSON.parse(result) != chrome.extension.inIncognitoContext)
+        return;
+      chrome.test.sendMessage(
+          JSON.stringify({name: 'getRootPaths'}), steps.shift());
+    },
+    // Obtain the root entry paths.
+    function(result) {
+      var roots = JSON.parse(result);
+      RootPath.DOWNLOADS = roots.downloads;
+      RootPath.DRIVE = roots.drive;
+      chrome.test.sendMessage(
+          JSON.stringify({name: 'getTestName'}), steps.shift());
+    },
+    // Run the test case.
+    function(testCaseName) {
+      var targetTest = testcase[testCaseName];
+      if (!targetTest) {
+        chrome.test.fail(testCaseName + ' is not found.');
+        return;
+      }
+      // Specify the name of test to the test system.
+      targetTest.generatedName = testCaseName;
+      chrome.test.runTests([function() {
+        return testPromise(targetTest());
+      }]);
+    }
+  ];
+  steps.shift()();
+});
diff --git a/ui/file_manager/integration_tests/audio_player/open_audio_files.js b/ui/file_manager/integration_tests/audio_player/open_audio_files.js
new file mode 100644
index 0000000..3da079d6
--- /dev/null
+++ b/ui/file_manager/integration_tests/audio_player/open_audio_files.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.
+
+'use strict';
+
+/**
+ * Test of Audio Palyer window and initial elements.
+ *
+ * @param {string} volumeName Test volume name passed to the addEntries
+ * @param {VolumeManagerCommon.VolumeType} volumeType Volume type.
+ * @return {Promise} Promise to be fulfilled with on success.
+ */
+function openAudioPlayer(volumeName, volumeType) {
+  var test = launch(volumeName, volumeType, [ENTRIES.newlyAdded]);
+  return test.then(function(args) {
+    var appWindow = args[0];
+    return Promise.all([
+      remoteCallAudioPlayer.waitForElement(
+          appWindow, 'audio-player /deep/ track-list'),
+      remoteCallAudioPlayer.waitForElement(
+          appWindow, 'audio-player /deep/ control-panel'),
+      remoteCallAudioPlayer.waitForElement(
+          appWindow, 'audio-player /deep/ audio'),
+    ]);
+  });
+}
+
+/**
+ * The open audio player test for Downloads.
+ * @return {Promise} Promise to be fulfilled with on success.
+ */
+testcase.openAudioOnDownloads = function() {
+  return openAudioPlayer('local', 'downloads');
+};
+
+/**
+ * The open audio player test for Drive
+ * @return {Promise} Promise to be fulfilled with on success.
+ */
+testcase.openAudioOnDrive = function() {
+  return openAudioPlayer('drive', 'drive');
+};
diff --git a/ui/file_manager/integration_tests/audio_player_test_manifest.json b/ui/file_manager/integration_tests/audio_player_test_manifest.json
new file mode 100644
index 0000000..0ac5ce6
--- /dev/null
+++ b/ui/file_manager/integration_tests/audio_player_test_manifest.json
@@ -0,0 +1,17 @@
+{
+  // chrome-extension:/ddabbgbggambiildohfagdkliahiecfl/
+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqyMPfoy88DMSxVONd2NEK9u7OQfKjWQujB541dn+NNSFUEqSF6lYehgX77x7eGEs1DCHhZynX71o5JYLe2oeWt/HBDR/tWpdwwh80owIQDwaKJrfRtisyVL++dMmf0pNjFv1F5tFXObO84F22oZyBA89Z0S0jNlHjyVzm7U4BwbDHhHBoGXGJxf5AXL5quLdwlVe69d6ZjiSkYvaAAGwwYDWuqG8VFaZuz4Onh/bSYocBtd6rpeRwS7tAkTugCU0Mnivaj/x9WYajomcg+/QSQhwjbH+9+8zqsmpnKIeCY/qxadI9CYMMyg3Kbo53THQKRe6/DaXazgthMqAL9TGAwIDAQAB",
+  "name": "chrome os audio player browser tests",
+  "version": "0.1",
+  "manifest_version": 2,
+  "web_accessible_resources": ["*"],
+  "background": {
+    "scripts": [
+      "test_util.js",
+      "remote_call.js",
+      "audio_player/background.js",
+      "audio_player/open_audio_files.js"
+    ]},
+  "incognito" : "split",
+  "permissions": ["commandLinePrivate"]
+}
diff --git a/ui/keyboard/keyboard_util.cc b/ui/keyboard/keyboard_util.cc
index 4348dfb..2906b03 100644
--- a/ui/keyboard/keyboard_util.cc
+++ b/ui/keyboard/keyboard_util.cc
@@ -172,7 +172,7 @@
   std::string keyboard_switch =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           switches::kGestureEditing);
-  return keyboard_switch != switches::kGestureEditingDisabled;
+  return keyboard_switch == switches::kGestureEditingEnabled;
 }
 
 bool IsSmartDeployEnabled() {
diff --git a/ui/message_center/views/message_popup_collection_unittest.cc b/ui/message_center/views/message_popup_collection_unittest.cc
index 466181b..74934dd5 100644
--- a/ui/message_center/views/message_popup_collection_unittest.cc
+++ b/ui/message_center/views/message_popup_collection_unittest.cc
@@ -168,6 +168,8 @@
   // See crbug.com/236448
   GetWidget(id1)->CloseNow();
   collection()->OnMouseExited(GetToast(id2));
+
+  GetWidget(id2)->CloseNow();
 }
 
 TEST_F(MessagePopupCollectionTest, DefaultPositioning) {
@@ -206,6 +208,7 @@
 
   CloseAllToasts();
   EXPECT_EQ(0u, GetToastCounts());
+  WaitForTransitionsDone();
 }
 
 TEST_F(MessagePopupCollectionTest, DefaultPositioningWithRightTaskbar) {
@@ -236,6 +239,8 @@
   // Restore simulated taskbar position to bottom.
   SetDisplayInfo(gfx::Rect(0, 0, 600, 390),  // Work-area.
                  gfx::Rect(0, 0, 600, 400)); // Display-bounds.
+
+  WaitForTransitionsDone();
 }
 
 TEST_F(MessagePopupCollectionTest, TopDownPositioningWithTopTaskbar) {
@@ -260,6 +265,7 @@
 
   CloseAllToasts();
   EXPECT_EQ(0u, GetToastCounts());
+  WaitForTransitionsDone();
 
   // Restore simulated taskbar position to bottom.
   SetDisplayInfo(gfx::Rect(0, 0, 600, 390),   // Work-area.
@@ -291,6 +297,7 @@
 
   CloseAllToasts();
   EXPECT_EQ(0u, GetToastCounts());
+  WaitForTransitionsDone();
 
   // Restore simulated taskbar position to bottom.
   SetDisplayInfo(gfx::Rect(0, 0, 600, 390),   // Work-area.
@@ -322,6 +329,7 @@
 
   CloseAllToasts();
   EXPECT_EQ(0u, GetToastCounts());
+  WaitForTransitionsDone();
 
   // Restore simulated taskbar position to bottom.
   SetDisplayInfo(gfx::Rect(0, 0, 600, 390),   // Work-area.
@@ -353,6 +361,7 @@
 
   CloseAllToasts();
   EXPECT_EQ(0u, GetToastCounts());
+  WaitForTransitionsDone();
 
   // Restore simulated taskbar position to bottom.
   SetDisplayInfo(gfx::Rect(0, 0, 600, 390),   // Work-area.
@@ -387,9 +396,9 @@
   EXPECT_TRUE(MouseInCollection());
   toast1->OnMouseEntered(event);
   EXPECT_TRUE(MouseInCollection());
-  toast0->WindowClosing();
+  toast0->GetWidget()->CloseNow();
   EXPECT_TRUE(MouseInCollection());
-  toast1->WindowClosing();
+  toast1->GetWidget()->CloseNow();
   EXPECT_FALSE(MouseInCollection());
 }
 
@@ -417,6 +426,9 @@
   WaitForTransitionsDone();
   views::WidgetDelegateView* toast2 = GetToast(id2);
   EXPECT_TRUE(toast2 != NULL);
+
+  CloseAllToasts();
+  WaitForTransitionsDone();
 }
 
 
diff --git a/ui/mojo/init/BUILD.gn b/ui/mojo/init/BUILD.gn
new file mode 100644
index 0000000..e6745ad
--- /dev/null
+++ b/ui/mojo/init/BUILD.gn
@@ -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.
+
+source_set("init") {
+  sources = [
+    "screen_mojo.cc",
+    "screen_mojo.h",
+    "ui_init.cc",
+    "ui_init.h",
+  ]
+
+  deps = [
+    "//base",
+    "//ui/gfx/geometry",
+  ]
+
+  if (is_android) {
+    deps += [
+      "//ui/events:gesture_detection",
+      "//ui/gfx",
+    ]
+  }
+}
diff --git a/ui/mojo/init/DEPS b/ui/mojo/init/DEPS
new file mode 100644
index 0000000..e419b31
--- /dev/null
+++ b/ui/mojo/init/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+ui/events/gesture_detection",
+  "+ui/gfx",
+]
diff --git a/ui/mojo/init/screen_mojo.cc b/ui/mojo/init/screen_mojo.cc
new file mode 100644
index 0000000..1ed0d41
--- /dev/null
+++ b/ui/mojo/init/screen_mojo.cc
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/mojo/init/screen_mojo.h"
+
+namespace ui {
+namespace mojo {
+
+ScreenMojo::ScreenMojo(const gfx::Size& screen_size_in_pixels,
+                       float device_pixel_ratio)
+    : screen_size_in_pixels_(screen_size_in_pixels),
+      device_pixel_ratio_(device_pixel_ratio) {
+  static int64 synthesized_display_id = 2000;
+  display_.set_id(synthesized_display_id++);
+  display_.SetScaleAndBounds(device_pixel_ratio,
+                             gfx::Rect(screen_size_in_pixels));
+}
+
+gfx::Point ScreenMojo::GetCursorScreenPoint() {
+  return gfx::Point();
+}
+
+gfx::NativeWindow ScreenMojo::GetWindowUnderCursor() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+gfx::NativeWindow ScreenMojo::GetWindowAtScreenPoint(const gfx::Point& point) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+gfx::Display ScreenMojo::GetPrimaryDisplay() const {
+  return display_;
+}
+
+gfx::Display ScreenMojo::GetDisplayNearestWindow(gfx::NativeView view) const {
+  return GetPrimaryDisplay();
+}
+
+gfx::Display ScreenMojo::GetDisplayNearestPoint(const gfx::Point& point) const {
+  return GetPrimaryDisplay();
+}
+
+int ScreenMojo::GetNumDisplays() const {
+  return 1;
+}
+
+std::vector<gfx::Display> ScreenMojo::GetAllDisplays() const {
+  return std::vector<gfx::Display>(1, GetPrimaryDisplay());
+}
+
+gfx::Display ScreenMojo::GetDisplayMatching(const gfx::Rect& match_rect) const {
+  return GetPrimaryDisplay();
+}
+
+void ScreenMojo::AddObserver(gfx::DisplayObserver* observer) {
+  // TODO: add support for display changes.
+}
+
+void ScreenMojo::RemoveObserver(gfx::DisplayObserver* observer) {
+  // TODO: add support for display changes.
+}
+
+}  // namespace mojo
+}  // namespace ui
diff --git a/mandoline/ui/aura/screen_mojo.h b/ui/mojo/init/screen_mojo.h
similarity index 69%
rename from mandoline/ui/aura/screen_mojo.h
rename to ui/mojo/init/screen_mojo.h
index 964c21a..db7f48f 100644
--- a/mandoline/ui/aura/screen_mojo.h
+++ b/ui/mojo/init/screen_mojo.h
@@ -2,48 +2,43 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MANDOLINE_UI_AURA_SCREEN_MOJO_H_
-#define MANDOLINE_UI_AURA_SCREEN_MOJO_H_
+#ifndef UI_MOJO_INIT_SCREEN_MOJO_H_
+#define UI_MOJO_INIT_SCREEN_MOJO_H_
 
-#include "base/macros.h"
 #include "ui/gfx/display.h"
+#include "ui/gfx/geometry/size.h"
 #include "ui/gfx/screen.h"
 
-namespace gfx {
-class Rect;
-class Transform;
-}
+namespace ui {
+namespace mojo {
 
-namespace mandoline {
-
-// A minimal implementation of gfx::Screen for mojo.
 class ScreenMojo : public gfx::Screen {
  public:
-  static ScreenMojo* Create();
-  ~ScreenMojo() override;
+  ScreenMojo(const gfx::Size& screen_size_in_pixels, float device_pixel_ratio);
 
- protected:
-  // gfx::Screen overrides:
+  // gfx::Screen:
   gfx::Point GetCursorScreenPoint() override;
   gfx::NativeWindow GetWindowUnderCursor() override;
   gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override;
-  int GetNumDisplays() const override;
-  std::vector<gfx::Display> GetAllDisplays() const override;
+  gfx::Display GetPrimaryDisplay() const override;
   gfx::Display GetDisplayNearestWindow(gfx::NativeView view) const override;
   gfx::Display GetDisplayNearestPoint(const gfx::Point& point) const override;
+  int GetNumDisplays() const override;
+  std::vector<gfx::Display> GetAllDisplays() const override;
   gfx::Display GetDisplayMatching(const gfx::Rect& match_rect) const override;
-  gfx::Display GetPrimaryDisplay() const override;
   void AddObserver(gfx::DisplayObserver* observer) override;
   void RemoveObserver(gfx::DisplayObserver* observer) override;
 
  private:
-  explicit ScreenMojo(const gfx::Rect& screen_bounds);
+  const gfx::Size screen_size_in_pixels_;
+  const float device_pixel_ratio_;
 
   gfx::Display display_;
 
   DISALLOW_COPY_AND_ASSIGN(ScreenMojo);
 };
 
-}  // namespace mandoline
+}  // namespace mojo
+}  // namespace ui
 
-#endif  // MANDOLINE_UI_AURA_SCREEN_MOJO_H_
+#endif  // UI_MOJO_INIT_SCREEN_MOJO_H_
diff --git a/ui/mojo/init/ui_init.cc b/ui/mojo/init/ui_init.cc
new file mode 100644
index 0000000..937112ee
--- /dev/null
+++ b/ui/mojo/init/ui_init.cc
@@ -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.
+
+#include "ui/mojo/init/ui_init.h"
+
+#include "base/memory/singleton.h"
+#include "ui/mojo/init/screen_mojo.h"
+
+#if defined(OS_ANDROID)
+#include "ui/events/gesture_detection/gesture_configuration.h"
+#endif
+
+namespace ui {
+namespace mojo {
+
+#if defined(OS_ANDROID)
+// TODO(sky): this needs to come from system.
+class GestureConfigurationMojo : public ui::GestureConfiguration {
+ public:
+  GestureConfigurationMojo() : GestureConfiguration() {
+    set_double_tap_enabled(false);
+    set_double_tap_timeout_in_ms(semi_long_press_time_in_ms());
+    set_gesture_begin_end_types_enabled(true);
+    set_min_gesture_bounds_length(default_radius());
+    set_min_pinch_update_span_delta(0);
+    set_min_scaling_touch_major(default_radius() * 2);
+    set_velocity_tracker_strategy(
+        ui::VelocityTracker::Strategy::LSQ2_RESTRICTED);
+    set_span_slop(max_touch_move_in_pixels_for_click() * 2);
+    set_swipe_enabled(true);
+    set_two_finger_tap_enabled(true);
+  }
+
+  ~GestureConfigurationMojo() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GestureConfigurationMojo);
+};
+#endif
+
+UIInit::UIInit(const gfx::Size& screen_size_in_pixels, float device_pixel_ratio)
+    : screen_(new ScreenMojo(screen_size_in_pixels, device_pixel_ratio)) {
+  gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
+#if defined(OS_ANDROID)
+  gesture_configuration_.reset(new GestureConfigurationMojo);
+  ui::GestureConfiguration::SetInstance(gesture_configuration_.get());
+#endif
+}
+
+UIInit::~UIInit() {
+  gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, nullptr);
+#if defined(OS_ANDROID)
+  ui::GestureConfiguration::SetInstance(nullptr);
+#endif
+}
+
+}  // namespace mojo
+}  // namespace ui
diff --git a/ui/mojo/init/ui_init.h b/ui/mojo/init/ui_init.h
new file mode 100644
index 0000000..eeaebc6
--- /dev/null
+++ b/ui/mojo/init/ui_init.h
@@ -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.
+
+#ifndef UI_MOJO_INIT_UI_INIT_H_
+#define UI_MOJO_INIT_UI_INIT_H_
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace gfx {
+class Screen;
+class Size;
+}
+
+namespace ui {
+class GestureConfiguration;
+
+namespace mojo {
+
+class GestureConfigurationMojo;
+
+// UIInit configures any state needed by apps that make use of ui classes (not
+// including aura).
+class UIInit {
+ public:
+  UIInit(const gfx::Size& screen_size_in_pixels, float device_pixel_ratio);
+  ~UIInit();
+
+ private:
+  scoped_ptr<gfx::Screen> screen_;
+#if defined(OS_ANDROID)
+  scoped_ptr<GestureConfigurationMojo> gesture_configuration_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(UIInit);
+};
+
+}  // namespace mojo
+}  // namespace ui
+
+#endif  // UI_MOJO_INIT_UI_INIT_H_
diff --git a/ui/ozone/demo/ozone_demo.cc b/ui/ozone/demo/ozone_demo.cc
index b5b3ac8..3e1a37f92 100644
--- a/ui/ozone/demo/ozone_demo.cc
+++ b/ui/ozone/demo/ozone_demo.cc
@@ -144,7 +144,8 @@
   void OnClosed() override {}
   void OnWindowStateChanged(ui::PlatformWindowState new_state) override {}
   void OnLostCapture() override {}
-  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override {
+  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget,
+                                    float device_pixel_ratio) override {
     DCHECK_NE(widget, gfx::kNullAcceleratedWidget);
     widget_ = widget;
   }
diff --git a/ui/ozone/platform/caca/caca_window.cc b/ui/ozone/platform/caca/caca_window.cc
index f0dc0a7..c4e0a4a 100644
--- a/ui/ozone/platform/caca/caca_window.cc
+++ b/ui/ozone/platform/caca/caca_window.cc
@@ -36,7 +36,7 @@
       weak_ptr_factory_(this) {
   widget_ = manager_->AddWindow(this);
   ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
-  delegate_->OnAcceleratedWidgetAvailable(widget_);
+  delegate_->OnAcceleratedWidgetAvailable(widget_, 1.f);
 }
 
 CacaWindow::~CacaWindow() {
diff --git a/ui/ozone/platform/cast/platform_window_cast.cc b/ui/ozone/platform/cast/platform_window_cast.cc
index c9ea0ed4..b0f66302 100644
--- a/ui/ozone/platform/cast/platform_window_cast.cc
+++ b/ui/ozone/platform/cast/platform_window_cast.cc
@@ -12,7 +12,7 @@
                                        const gfx::Rect& bounds)
     : delegate_(delegate), bounds_(bounds) {
   widget_ = (bounds.width() << 16) + bounds.height();
-  delegate_->OnAcceleratedWidgetAvailable(widget_);
+  delegate_->OnAcceleratedWidgetAvailable(widget_, 1.f);
 }
 
 gfx::Rect PlatformWindowCast::GetBounds() {
diff --git a/ui/ozone/platform/drm/gpu/crtc_controller.cc b/ui/ozone/platform/drm/gpu/crtc_controller.cc
index 44d6ec4..34390a25 100644
--- a/ui/ozone/platform/drm/gpu/crtc_controller.cc
+++ b/ui/ozone/platform/drm/gpu/crtc_controller.cc
@@ -67,7 +67,7 @@
     const OverlayPlaneList& overlays,
     bool test_only,
     scoped_refptr<PageFlipRequest> page_flip_request) {
-  DCHECK(!page_flip_request_.get());
+  DCHECK(!page_flip_request_.get() || test_only);
   DCHECK(!is_disabled_);
   const OverlayPlane* primary = OverlayPlane::GetPrimaryPlane(overlays);
   if (!primary) {
diff --git a/ui/ozone/platform/drm/host/drm_window_host.cc b/ui/ozone/platform/drm/host/drm_window_host.cc
index 4ce21c3..6d336d3 100644
--- a/ui/ozone/platform/drm/host/drm_window_host.cc
+++ b/ui/ozone/platform/drm/host/drm_window_host.cc
@@ -52,7 +52,7 @@
   sender_->AddChannelObserver(this);
   PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
   cursor_->OnWindowAdded(widget_, bounds_, GetCursorConfinedBounds());
-  delegate_->OnAcceleratedWidgetAvailable(widget_);
+  delegate_->OnAcceleratedWidgetAvailable(widget_, 1.f);
 }
 
 gfx::AcceleratedWidget DrmWindowHost::GetAcceleratedWidget() {
diff --git a/ui/ozone/platform/egltest/ozone_platform_egltest.cc b/ui/ozone/platform/egltest/ozone_platform_egltest.cc
index 31f35a6..2ec2a66 100644
--- a/ui/ozone/platform/egltest/ozone_platform_egltest.cc
+++ b/ui/ozone/platform/egltest/ozone_platform_egltest.cc
@@ -122,7 +122,7 @@
       bounds_(bounds),
       window_id_(SHIM_NO_WINDOW_ID) {
   window_id_ = eglplatform_shim_->ShimCreateWindow();
-  delegate_->OnAcceleratedWidgetAvailable(window_id_);
+  delegate_->OnAcceleratedWidgetAvailable(window_id_, 1.f);
   ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
 }
 
diff --git a/ui/ozone/platform/test/test_window.cc b/ui/ozone/platform/test/test_window.cc
index 2890ccf3..a6a79ee4 100644
--- a/ui/ozone/platform/test/test_window.cc
+++ b/ui/ozone/platform/test/test_window.cc
@@ -19,7 +19,7 @@
                        const gfx::Rect& bounds)
     : delegate_(delegate), manager_(manager), bounds_(bounds) {
   widget_ = manager_->AddWindow(this);
-  delegate_->OnAcceleratedWidgetAvailable(widget_);
+  delegate_->OnAcceleratedWidgetAvailable(widget_, 1.f);
 }
 
 TestWindow::~TestWindow() {
diff --git a/ui/ozone/public/input_controller.cc b/ui/ozone/public/input_controller.cc
index 92cc2484..dd508a3 100644
--- a/ui/ozone/public/input_controller.cc
+++ b/ui/ozone/public/input_controller.cc
@@ -39,11 +39,9 @@
   void GetTouchDeviceStatus(const GetTouchDeviceStatusReply& reply) override;
   void GetTouchEventLog(const base::FilePath& out_dir,
                         const GetTouchEventLogReply& reply) override;
-  void DisableInternalTouchpad() override;
-  void EnableInternalTouchpad() override;
-  void DisableInternalKeyboardExceptKeys(
-      scoped_ptr<std::set<DomCode>> excepted_keys) override;
-  void EnableInternalKeyboard() override;
+  void SetInternalTouchpadEnabled(bool enabled) override;
+  void SetInternalKeyboardFilter(bool enable_filter,
+                                 std::vector<DomCode> allowed_keys) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(StubInputController);
@@ -122,17 +120,12 @@
   reply.Run(make_scoped_ptr(new std::vector<base::FilePath>));
 }
 
-void StubInputController::DisableInternalTouchpad() {
+void StubInputController::SetInternalTouchpadEnabled(bool enabled) {
 }
 
-void StubInputController::EnableInternalTouchpad() {
-}
-
-void StubInputController::DisableInternalKeyboardExceptKeys(
-    scoped_ptr<std::set<DomCode>> excepted_keys) {
-}
-
-void StubInputController::EnableInternalKeyboard() {
+void StubInputController::SetInternalKeyboardFilter(
+    bool enable_filter,
+    std::vector<DomCode> allowed_keys) {
 }
 
 }  // namespace
diff --git a/ui/ozone/public/input_controller.h b/ui/ozone/public/input_controller.h
index 7a276eb..b8dae61 100644
--- a/ui/ozone/public/input_controller.h
+++ b/ui/ozone/public/input_controller.h
@@ -73,18 +73,12 @@
   // experience in some use cases (e.g., typing, watching video).
   virtual void SetTapToClickPaused(bool state) = 0;
 
-  // Disables the internal touchpad.
-  virtual void DisableInternalTouchpad() = 0;
+  virtual void SetInternalTouchpadEnabled(bool enabled) = 0;
 
-  // Enables the internal touchpad.
-  virtual void EnableInternalTouchpad() = 0;
-
-  // Disables all keys on the internal keyboard except |excepted_keys|.
-  virtual void DisableInternalKeyboardExceptKeys(
-      scoped_ptr<std::set<DomCode>> excepted_keys) = 0;
-
-  // Enables all keys on the internal keyboard.
-  virtual void EnableInternalKeyboard() = 0;
+  // If |enable_filter| is true, all keys on the internal keyboard except
+  // |allowed_keys| are disabled.
+  virtual void SetInternalKeyboardFilter(bool enable_filter,
+                                         std::vector<DomCode> allowed_keys) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(InputController);
diff --git a/ui/platform_window/android/platform_window_android.cc b/ui/platform_window/android/platform_window_android.cc
index 6d6af40..eac8fea4 100644
--- a/ui/platform_window/android/platform_window_android.cc
+++ b/ui/platform_window/android/platform_window_android.cc
@@ -84,7 +84,7 @@
     base::android::ScopedJavaLocalFrame scoped_local_reference_frame(env);
     window_ = ANativeWindow_fromSurface(env, jsurface);
   }
-  delegate_->OnAcceleratedWidgetAvailable(window_);
+  delegate_->OnAcceleratedWidgetAvailable(window_, device_pixel_ratio);
 }
 
 void PlatformWindowAndroid::SurfaceDestroyed(JNIEnv* env, jobject obj) {
diff --git a/ui/platform_window/platform_window_delegate.h b/ui/platform_window/platform_window_delegate.h
index b2152757..67c63a7 100644
--- a/ui/platform_window/platform_window_delegate.h
+++ b/ui/platform_window/platform_window_delegate.h
@@ -43,7 +43,8 @@
 
   virtual void OnLostCapture() = 0;
 
-  virtual void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) = 0;
+  virtual void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget,
+                                            float device_pixel_ratio) = 0;
 
   virtual void OnActivationChanged(bool active) = 0;
 };
diff --git a/ui/platform_window/win/win_window.cc b/ui/platform_window/win/win_window.cc
index 936b250..c2554d4 100644
--- a/ui/platform_window/win/win_window.cc
+++ b/ui/platform_window/win/win_window.cc
@@ -171,7 +171,8 @@
   tracked_objects::ScopedTracker tracking_profile(
       FROM_HERE_WITH_EXPLICIT_FUNCTION("440919 WinWindow::OnCreate"));
 
-  delegate_->OnAcceleratedWidgetAvailable(hwnd());
+  // TODO(sky): provide real scale factor.
+  delegate_->OnAcceleratedWidgetAvailable(hwnd(), 1.f);
   return 0;
 }
 
diff --git a/ui/platform_window/x11/x11_window.cc b/ui/platform_window/x11/x11_window.cc
index ed6d353..0646315 100644
--- a/ui/platform_window/x11/x11_window.cc
+++ b/ui/platform_window/x11/x11_window.cc
@@ -198,7 +198,8 @@
   size_hints.win_gravity = StaticGravity;
   XSetWMNormalHints(xdisplay_, xwindow_, &size_hints);
 
-  delegate_->OnAcceleratedWidgetAvailable(xwindow_);
+  // TODO(sky): provide real scale factor.
+  delegate_->OnAcceleratedWidgetAvailable(xwindow_, 1.f);
 
   XMapWindow(xdisplay_, xwindow_);
 
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 37a35e88..673f1fea 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -265,13 +265,9 @@
     # views_unittests not yet compiling on Mac. http://crbug.com/378134
     sources -= [
       "bubble/bubble_window_targeter_unittest.cc",
-      "controls/button/custom_button_unittest.cc",
-      "controls/button/menu_button_unittest.cc",
-      "controls/menu/menu_controller_unittest.cc",
       "controls/native/native_view_host_unittest.cc",
       "focus/focus_manager_unittest.cc",
       "ime/input_method_bridge_unittest.cc",
-      "widget/widget_unittest.cc",
       "widget/window_reorderer_unittest.cc",
     ]
   }
diff --git a/ui/views/controls/button/custom_button_unittest.cc b/ui/views/controls/button/custom_button_unittest.cc
index 7a8fcc0..de3be98 100644
--- a/ui/views/controls/button/custom_button_unittest.cc
+++ b/ui/views/controls/button/custom_button_unittest.cc
@@ -5,9 +5,6 @@
 #include "ui/views/controls/button/custom_button.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/aura/test/test_cursor_client.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/layout.h"
 #include "ui/events/event_utils.h"
 #include "ui/gfx/screen.h"
@@ -20,6 +17,12 @@
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/test/views_test_base.h"
 
+#if defined(USE_AURA)
+#include "ui/aura/test/test_cursor_client.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#endif
+
 namespace views {
 
 namespace {
@@ -46,13 +49,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestCustomButton);
 };
 
-void PerformGesture(CustomButton* button, ui::EventType event_type) {
-  ui::GestureEventDetails gesture_details(event_type);
-  base::TimeDelta time_stamp = base::TimeDelta::FromMicroseconds(0);
-  ui::GestureEvent gesture_event(0, 0, 0, time_stamp, gesture_details);
-  button->OnGestureEvent(&gesture_event);
-}
-
 class CustomButtonTest : public ViewsTestBase {
  public:
   CustomButtonTest() {}
@@ -81,6 +77,11 @@
     widget_->SetContentsView(button_);
   }
 
+  void TearDown() override {
+    widget_.reset();
+    ViewsTestBase::TearDown();
+  }
+
   Widget* widget() { return widget_.get(); }
   TestCustomButton* button() { return button_; }
 
@@ -95,9 +96,6 @@
 
 // Tests that hover state changes correctly when visiblity/enableness changes.
 TEST_F(CustomButtonTest, HoverStateOnVisibilityChange) {
-  aura::test::TestCursorClient cursor_client(
-      widget()->GetNativeView()->GetRootWindow());
-
   gfx::Point center(10, 10);
   button()->OnMousePressed(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(),
@@ -121,6 +119,12 @@
   button()->SetVisible(true);
   EXPECT_EQ(CustomButton::STATE_HOVERED, button()->state());
 
+// Disabling cursor events occurs for touch events and the Ash magnifier. There
+// is no touch on desktop Mac. Tracked in http://crbug.com/445520.
+#if !defined(OS_MACOSX) || defined(USE_AURA)
+  aura::test::TestCursorClient cursor_client(
+      widget()->GetNativeView()->GetRootWindow());
+
   // In Aura views, no new hover effects are invoked if mouse events
   // are disabled.
   cursor_client.DisableMouseEvents();
@@ -136,6 +140,7 @@
 
   button()->SetVisible(true);
   EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state());
+#endif  // !defined(OS_MACOSX) || defined(USE_AURA)
 }
 
 // Tests the different types of NotifyActions.
@@ -173,6 +178,20 @@
   EXPECT_FALSE(button()->notified());
 }
 
+// No touch on desktop Mac. Tracked in http://crbug.com/445520.
+#if !defined(OS_MACOSX) || defined(USE_AURA)
+
+namespace {
+
+void PerformGesture(CustomButton* button, ui::EventType event_type) {
+  ui::GestureEventDetails gesture_details(event_type);
+  base::TimeDelta time_stamp = base::TimeDelta::FromMicroseconds(0);
+  ui::GestureEvent gesture_event(0, 0, 0, time_stamp, gesture_details);
+  button->OnGestureEvent(&gesture_event);
+}
+
+}  // namespace
+
 // Tests that gesture events correctly change the button state.
 TEST_F(CustomButtonTest, GestureEventsSetState) {
   aura::test::TestCursorClient cursor_client(
@@ -190,6 +209,8 @@
   EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state());
 }
 
+#endif  // !defined(OS_MACOSX) || defined(USE_AURA)
+
 // Ensure subclasses of CustomButton are correctly recognized as CustomButton.
 TEST_F(CustomButtonTest, AsCustomButton) {
   base::string16 text;
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc
index 3b29d7bc..1fd47a5 100644
--- a/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -6,15 +6,18 @@
 
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
-#include "ui/aura/scoped_window_targeter.h"
-#include "ui/aura/window.h"
 #include "ui/events/event_handler.h"
 #include "ui/events/null_event_targeter.h"
 #include "ui/events/platform/platform_event_source.h"
 #include "ui/views/controls/menu/menu_item_view.h"
 #include "ui/views/controls/menu/submenu_view.h"
 #include "ui/views/test/views_test_base.h"
+
+#if defined(USE_AURA)
+#include "ui/aura/scoped_window_targeter.h"
+#include "ui/aura/window.h"
 #include "ui/wm/public/dispatcher_client.h"
+#endif
 
 #if defined(OS_WIN)
 #include "base/message_loop/message_pump_dispatcher.h"
@@ -26,6 +29,8 @@
 #include "ui/events/test/events_test_utils_x11.h"
 #elif defined(USE_OZONE)
 #include "ui/events/event.h"
+#elif defined(OS_MACOSX)
+#include "ui/events/test/event_generator.h"
 #endif
 
 namespace views {
@@ -58,6 +63,7 @@
   DISALLOW_COPY_AND_ASSIGN(TestPlatformEventSource);
 };
 
+#if defined(USE_AURA)
 class TestDispatcherClient : public aura::client::DispatcherClient {
  public:
   TestDispatcherClient() : dispatcher_(nullptr) {}
@@ -93,6 +99,7 @@
 
   DISALLOW_COPY_AND_ASSIGN(TestDispatcherClient);
 };
+#endif  // USE_AURA
 
 }  // namespace
 
@@ -150,8 +157,10 @@
     widget->Init(params);
     widget->Show();
 
+#if defined(USE_AURA)
     aura::client::SetDispatcherClient(
         widget->GetNativeWindow()->GetRootWindow(), &dispatcher_client_);
+#endif
     return widget.Pass();
   }
 
@@ -228,6 +237,16 @@
 #elif defined(USE_OZONE)
     ui::KeyEvent event(' ', ui::VKEY_SPACE, ui::EF_NONE);
     event_source_.Dispatch(&event);
+#elif defined(OS_MACOSX) && !defined(USE_AURA)
+    // Since this is not an interactive test, on Mac there will be no key
+    // window. Any system event will just get ignored, so use the EventGenerator
+    // to generate a dummy event. Without Aura, these will be native events.
+    gfx::NativeWindow window = controller_->owner()->GetNativeWindow();
+    ui::test::EventGenerator generator(window, window);
+    // Send "up", since this will not activate a menu item. But note that the
+    // test has already set exit_type_ = EXIT_ALL just before the first call
+    // to this function.
+    generator.PressKey(ui::VKEY_UP, 0);
 #else
 #error Unsupported platform
 #endif
@@ -249,7 +268,9 @@
   MenuController* controller_;
   scoped_ptr<base::RunLoop> run_loop_;
   TestPlatformEventSource event_source_;
+#if defined(USE_AURA)
   TestDispatcherClient dispatcher_client_;
+#endif
 
   DISALLOW_COPY_AND_ASSIGN(MenuControllerTest);
 };
diff --git a/ui/views/controls/menu/menu_message_loop_mac.cc b/ui/views/controls/menu/menu_message_loop_mac.cc
index 9fcb0b8c..ae5670a 100644
--- a/ui/views/controls/menu/menu_message_loop_mac.cc
+++ b/ui/views/controls/menu/menu_message_loop_mac.cc
@@ -4,6 +4,7 @@
 
 #include "ui/views/controls/menu/menu_message_loop_mac.h"
 
+#include "base/auto_reset.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
@@ -34,11 +35,13 @@
   base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
   base::MessageLoop::ScopedNestableTaskAllower allow(loop);
   base::RunLoop run_loop;
+  base::AutoReset<base::RunLoop*> reset_run_loop(&run_loop_, &run_loop);
   run_loop.Run();
 }
 
 void MenuMessageLoopMac::QuitNow() {
-  base::MessageLoop::current()->QuitNow();
+  DCHECK(run_loop_);
+  run_loop_->Quit();
 }
 
 void MenuMessageLoopMac::ClearOwner() {
diff --git a/ui/views/controls/menu/menu_message_loop_mac.h b/ui/views/controls/menu/menu_message_loop_mac.h
index bddf609..82ef8093 100644
--- a/ui/views/controls/menu/menu_message_loop_mac.h
+++ b/ui/views/controls/menu/menu_message_loop_mac.h
@@ -8,6 +8,10 @@
 #include "base/compiler_specific.h"
 #include "ui/views/controls/menu/menu_message_loop.h"
 
+namespace base {
+class RunLoop;
+}
+
 namespace views {
 
 class MenuMessageLoopMac : public MenuMessageLoop {
@@ -26,6 +30,8 @@
   void ClearOwner() override;
 
  private:
+  base::RunLoop* run_loop_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(MenuMessageLoopMac);
 };
 
diff --git a/ui/views/controls/native/native_view_host_aura_unittest.cc b/ui/views/controls/native/native_view_host_aura_unittest.cc
index 5f03fc8..16053ede 100644
--- a/ui/views/controls/native/native_view_host_aura_unittest.cc
+++ b/ui/views/controls/native/native_view_host_aura_unittest.cc
@@ -28,6 +28,7 @@
     EVENT_SHOWN,
     EVENT_HIDDEN,
     EVENT_BOUNDS_CHANGED,
+    EVENT_DESTROYED,
   };
 
   struct EventDetails {
@@ -67,6 +68,11 @@
     events_.push_back(event);
   }
 
+  void OnWindowDestroyed(aura::Window* window) override {
+    EventDetails event = { EVENT_DESTROYED, window, gfx::Rect() };
+    events_.push_back(event);
+  }
+
  private:
   std::vector<EventDetails> events_;
   gfx::Rect bounds_at_visibility_changed_;
@@ -253,11 +259,24 @@
   aura::Window* root_window = child_win->GetRootWindow();
   aura::WindowTreeHost* child_win_tree_host = child_win->GetHost();
 
+  NativeViewHostWindowObserver test_observer;
+  child_win->AddObserver(&test_observer);
+
   host()->Detach();
   EXPECT_EQ(root_window, child_win->GetRootWindow());
   EXPECT_EQ(child_win_tree_host, child_win->GetHost());
 
   DestroyHost();
+
+  // The window is detached, so no longer associated with any Widget hierarchy.
+  // The root window still owns it, but the test harness checks for orphaned
+  // windows during TearDown().
+  DestroyTopLevel();
+  EXPECT_EQ(0u, test_observer.events().size());
+  delete child_win;
+  ASSERT_EQ(1u, test_observer.events().size());
+  EXPECT_EQ(NativeViewHostWindowObserver::EVENT_DESTROYED,
+            test_observer.events().back().type);
 }
 
 // Ensure the clipping window is hidden before setting the native view's bounds.
@@ -269,7 +288,9 @@
 
   NativeViewHostWindowObserver test_observer;
   clipping_window()->AddObserver(&test_observer);
-  child()->GetNativeView()->AddObserver(&test_observer);
+
+  aura::Window* child_win = child()->GetNativeView();
+  child_win->AddObserver(&test_observer);
 
   host()->Detach();
 
@@ -288,6 +309,7 @@
   child()->GetNativeView()->RemoveObserver(&test_observer);
 
   DestroyHost();
+  delete child_win;  // See comments in ParentAfterDetach.
 }
 
 // Ensure the native view receives the correct bounds notification when it is
diff --git a/ui/views/controls/native/native_view_host_test_base.cc b/ui/views/controls/native/native_view_host_test_base.cc
index 425d24a..ab40692 100644
--- a/ui/views/controls/native/native_view_host_test_base.cc
+++ b/ui/views/controls/native/native_view_host_test_base.cc
@@ -29,6 +29,11 @@
 NativeViewHostTestBase::~NativeViewHostTestBase() {
 }
 
+void NativeViewHostTestBase::TearDown() {
+  DestroyTopLevel();
+  ViewsTestBase::TearDown();
+}
+
 void NativeViewHostTestBase::CreateTopLevel() {
   toplevel_.reset(new Widget);
   Widget::InitParams toplevel_params =
diff --git a/ui/views/controls/native/native_view_host_test_base.h b/ui/views/controls/native/native_view_host_test_base.h
index 6a39f4d98..f663956 100644
--- a/ui/views/controls/native/native_view_host_test_base.h
+++ b/ui/views/controls/native/native_view_host_test_base.h
@@ -20,6 +20,9 @@
   NativeViewHostTestBase();
   ~NativeViewHostTestBase() override;
 
+  // testing::Test:
+  void TearDown() override;
+
   // Create the |toplevel_| widget.
   void CreateTopLevel();
 
diff --git a/ui/views/test/views_test_helper_aura.cc b/ui/views/test/views_test_helper_aura.cc
index 9f5b877..f3705b6d 100644
--- a/ui/views/test/views_test_helper_aura.cc
+++ b/ui/views/test/views_test_helper_aura.cc
@@ -42,6 +42,15 @@
 }
 
 void ViewsTestHelperAura::TearDown() {
+  // Ensure all Widgets (and windows) are closed in unit tests. This is done
+  // automatically when the RootWindow is torn down, but is an error on
+  // platforms that must ensure no Compositors are alive when the ContextFactory
+  // is torn down.
+  // So, although it's optional, check the root window to detect failures before
+  // they hit the CQ on other platforms.
+  DCHECK(aura_test_helper_->root_window()->children().empty())
+      << "Not all windows were closed.";
+
   if (screen_position_client_.get() ==
       aura::client::GetScreenPositionClient(GetContext()))
     aura::client::SetScreenPositionClient(GetContext(), nullptr);
diff --git a/ui/views/test/views_test_helper_mac.h b/ui/views/test/views_test_helper_mac.h
index 5f54fbc..1fa8c89 100644
--- a/ui/views/test/views_test_helper_mac.h
+++ b/ui/views/test/views_test_helper_mac.h
@@ -19,6 +19,9 @@
   ViewsTestHelperMac();
   ~ViewsTestHelperMac() override;
 
+  // ViewsTestHelper:
+  void TearDown() override;
+
  private:
   // Disable animations during tests.
   scoped_ptr<ui::ScopedAnimationDurationScaleMode> zero_duration_mode_;
diff --git a/ui/views/test/views_test_helper_mac.mm b/ui/views/test/views_test_helper_mac.mm
index ac88817..8ef73d2 100644
--- a/ui/views/test/views_test_helper_mac.mm
+++ b/ui/views/test/views_test_helper_mac.mm
@@ -6,8 +6,10 @@
 
 #import <Cocoa/Cocoa.h>
 
+#import "base/mac/scoped_nsautorelease_pool.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/views/test/event_generator_delegate_mac.h"
+#include "ui/views/widget/widget.h"
 
 namespace views {
 
@@ -31,4 +33,17 @@
 ViewsTestHelperMac::~ViewsTestHelperMac() {
 }
 
+void ViewsTestHelperMac::TearDown() {
+  // Ensure all Widgets are closed explicitly in tests. The Widget may be
+  // hosting a Compositor. If that's torn down after the test ContextFactory
+  // then a lot of confusing use-after-free errors result. In browser tests,
+  // this is handled automatically by views::Widget::CloseAllSecondaryWidgets().
+  // Unit tests on Aura may create Widgets owned by a RootWindow that gets torn
+  // down, but on Mac we need to be more explicit.
+  base::mac::ScopedNSAutoreleasePool pool;  // Ensure the NSArray is released.
+  NSArray* native_windows = [NSApp windows];
+  for (NSWindow* window : native_windows)
+    DCHECK(!Widget::GetWidgetForNativeWindow(window)) << "Widget not closed.";
+}
+
 }  // namespace views
diff --git a/ui/views/touchui/touch_selection_menu_runner_views_unittest.cc b/ui/views/touchui/touch_selection_menu_runner_views_unittest.cc
index b5adfb3..eee4258 100644
--- a/ui/views/touchui/touch_selection_menu_runner_views_unittest.cc
+++ b/ui/views/touchui/touch_selection_menu_runner_views_unittest.cc
@@ -104,6 +104,9 @@
   ui::TouchSelectionMenuRunner::GetInstance()->OpenMenu(
       this, anchor_rect, handle_size, GetContext());
   EXPECT_EQ(anchor_rect, GetMenuAnchorRect());
+
+  ui::TouchSelectionMenuRunner::GetInstance()->CloseMenu();
+  RunPendingMessages();
 }
 
 }  // namespace views
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index 2bc5f39..dc07bcb 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -431,12 +431,35 @@
   did_paint_ = true;
 }
 
+namespace {
+
+// Helper class to create a Widget with standard parameters that is closed when
+// the helper class goes out of scope.
+class ScopedTestPaintWidget {
+ public:
+  explicit ScopedTestPaintWidget(const Widget::InitParams& params)
+        : widget_(new Widget) {
+    widget_->Init(params);
+    widget_->GetRootView()->SetBounds(0, 0, 25, 26);
+  }
+
+  ~ScopedTestPaintWidget() {
+    widget_->CloseNow();
+  }
+
+  Widget* operator->() { return widget_; }
+
+ private:
+  Widget* widget_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedTestPaintWidget);
+};
+
+}  // namespace
+
 TEST_F(ViewTest, PaintEmptyView) {
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   // |v1| is empty.
   TestView* v1 = new TestView;
@@ -467,11 +490,8 @@
 }
 
 TEST_F(ViewTest, PaintWithUnknownInvalidation) {
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetBounds(10, 11, 12, 13);
@@ -510,11 +530,8 @@
 }
 
 TEST_F(ViewTest, PaintContainsChildren) {
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetBounds(10, 11, 12, 13);
@@ -546,12 +563,8 @@
 
 TEST_F(ViewTest, PaintContainsChildrenInRTL) {
   ScopedRTL rtl;
-
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetBounds(10, 11, 12, 13);
@@ -593,11 +606,8 @@
 }
 
 TEST_F(ViewTest, PaintIntersectsChildren) {
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetBounds(10, 11, 12, 13);
@@ -629,12 +639,8 @@
 
 TEST_F(ViewTest, PaintIntersectsChildrenInRTL) {
   ScopedRTL rtl;
-
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetBounds(10, 11, 12, 13);
@@ -676,11 +682,8 @@
 }
 
 TEST_F(ViewTest, PaintIntersectsChildButNotGrandChild) {
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetBounds(10, 11, 12, 13);
@@ -712,12 +715,8 @@
 
 TEST_F(ViewTest, PaintIntersectsChildButNotGrandChildInRTL) {
   ScopedRTL rtl;
-
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetBounds(10, 11, 12, 13);
@@ -759,11 +758,8 @@
 }
 
 TEST_F(ViewTest, PaintIntersectsNoChildren) {
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetBounds(10, 11, 12, 13);
@@ -795,12 +791,8 @@
 
 TEST_F(ViewTest, PaintIntersectsNoChildrenInRTL) {
   ScopedRTL rtl;
-
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetBounds(10, 11, 12, 13);
@@ -842,11 +834,8 @@
 }
 
 TEST_F(ViewTest, PaintIntersectsOneChild) {
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetBounds(10, 11, 12, 13);
@@ -890,12 +879,8 @@
 
 TEST_F(ViewTest, PaintIntersectsOneChildInRTL) {
   ScopedRTL rtl;
-
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetBounds(10, 11, 12, 13);
@@ -949,11 +934,8 @@
 }
 
 TEST_F(ViewTest, PaintInPromotedToLayer) {
-  Widget* widget = new Widget;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-  widget->Init(params);
+  ScopedTestPaintWidget widget(CreateParams(Widget::InitParams::TYPE_POPUP));
   View* root_view = widget->GetRootView();
-  root_view->SetBounds(0, 0, 25, 26);
 
   TestView* v1 = new TestView;
   v1->SetPaintToLayer(true);
@@ -1590,6 +1572,8 @@
   result_view = NULL;
   result_view = root_view->GetTooltipHandlerForPoint(point_in_v);
   EXPECT_EQ(root_view, result_view);
+
+  widget->CloseNow();
 }
 
 TEST_F(ViewTest, NotifyEnterExitOnChild) {
diff --git a/ui/views/view_unittest_aura.cc b/ui/views/view_unittest_aura.cc
index 5b64910..f9f72f7d 100644
--- a/ui/views/view_unittest_aura.cc
+++ b/ui/views/view_unittest_aura.cc
@@ -153,8 +153,7 @@
     ui::Layer* v7_new_layer = w1_new_layer->children()[3];
     ASSERT_EQ("v8 v9", ui::test::ChildLayerNamesAsString(*v7_new_layer));
   }
-  // The views and the widgets are destroyed when AuraTestHelper::TearDown()
-  // destroys root_window().
+  w1->CloseNow();
 }
 
 }  // namespace views
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index 6fc3d42..3943ef6 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -897,8 +897,6 @@
           # views_unittests not yet compiling on Mac. http://crbug.com/378134
           'sources!': [
             'bubble/bubble_window_targeter_unittest.cc',
-            'controls/button/custom_button_unittest.cc',
-            'controls/menu/menu_controller_unittest.cc',
             'controls/native/native_view_host_unittest.cc',
             'focus/focus_manager_unittest.cc',
             'ime/input_method_bridge_unittest.cc',
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index 2ee6351..cbe70fa 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -104,6 +104,12 @@
     // This widget instance will get deleted when the window is
     // destroyed.
     top_level_handler->top_level_widget_ = new Widget();
+    // Ensure that we always use the DesktopNativeWidgetAura instance as the
+    // native widget here. If we enter this code path in tests then it is
+    // possible that we may end up with a NativeWidgetAura instance as the
+    // native widget which breaks this code path.
+    init_params.native_widget =
+        new DesktopNativeWidgetAura(top_level_handler->top_level_widget_);
     top_level_handler->top_level_widget_->Init(init_params);
 
     top_level_handler->top_level_widget_->SetFullscreen(full_screen);
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index 3c5b05a..680abb3 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -493,7 +493,9 @@
 }
 
 bool NativeWidgetMac::IsMouseEventsEnabled() const {
-  NOTIMPLEMENTED();
+  // On platforms with touch, mouse events get disabled and calls to this method
+  // can affect hover states. Since there is no touch on desktop Mac, this is
+  // always true. Touch on Mac is tracked in http://crbug.com/445520.
   return true;
 }
 
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm
index 05fd824..4f1f019 100644
--- a/ui/views/widget/native_widget_mac_unittest.mm
+++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -403,6 +403,8 @@
   id hit = [widget->GetNativeWindow() accessibilityHitTest:midpoint];
   id title = [hit accessibilityAttributeValue:NSAccessibilityTitleAttribute];
   EXPECT_NSEQ(title, @"Green");
+
+  widget->CloseNow();
 }
 
 // Tests creating a views::Widget parented off a native NSWindow.
@@ -535,6 +537,8 @@
   // Move the mouse off of any view, tooltip should clear.
   event_generator.MoveMouseTo(gfx::Point(5, 5));
   EXPECT_TRUE(TooltipTextForWidget(widget).empty());
+
+  widget->CloseNow();
 }
 
 namespace {
diff --git a/ui/views/widget/native_widget_unittest.cc b/ui/views/widget/native_widget_unittest.cc
index 20b091e..1193032 100644
--- a/ui/views/widget/native_widget_unittest.cc
+++ b/ui/views/widget/native_widget_unittest.cc
@@ -81,19 +81,24 @@
 
 // |toplevel_widget| has the toplevel NativeWidget.
 TEST_F(NativeWidgetTest, GetTopLevelNativeWidget2) {
-  ScopedTestWidget toplevel_widget(CreateNativeWidget());
-
-  // |toplevel_widget| owns |child_host|.
-  NativeViewHost* child_host = new NativeViewHost;
-  toplevel_widget->GetWidget()->SetContentsView(child_host);
-
-  // |child_host| owns |child_widget|.
   internal::NativeWidgetPrivate* child_widget = CreateNativeSubWidget();
-  child_host->Attach(child_widget->GetWidget()->GetNativeView());
+  {
+    ScopedTestWidget toplevel_widget(CreateNativeWidget());
 
-  EXPECT_EQ(toplevel_widget.get(),
-            internal::NativeWidgetPrivate::GetTopLevelNativeWidget(
-                child_widget->GetWidget()->GetNativeView()));
+    // |toplevel_widget| owns |child_host|.
+    NativeViewHost* child_host = new NativeViewHost;
+    toplevel_widget->GetWidget()->SetContentsView(child_host);
+
+    // |child_host| hosts |child_widget|'s NativeView.
+    child_host->Attach(child_widget->GetWidget()->GetNativeView());
+
+    EXPECT_EQ(toplevel_widget.get(),
+              internal::NativeWidgetPrivate::GetTopLevelNativeWidget(
+                  child_widget->GetWidget()->GetNativeView()));
+  }
+  // NativeViewHost only had a weak reference to the |child_widget|'s
+  // NativeView. Delete it and the associated Widget.
+  child_widget->CloseNow();
 }
 
 }  // namespace views
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index 6fb2a143..ecdacc7 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -1025,6 +1025,9 @@
 
   child2->OnNativeWidgetSizeChanged(gfx::Size());
   EXPECT_EQ(child2, widget_bounds_changed());
+
+  child2->CloseNow();
+  child1->CloseNow();
 }
 
 // An extension to WidgetBoundsChanged to ensure notifications are forwarded
@@ -1825,6 +1828,8 @@
 
   widget->SynthesizeMouseMoveEvent();
   EXPECT_EQ(1, v2->GetEventCount(ui::ET_MOUSE_ENTERED));
+
+  widget->CloseNow();
 }
 
 // No touch on desktop Mac. Tracked in http://crbug.com/445520.
@@ -2961,6 +2966,7 @@
 
   EXPECT_EQ(expected.size(), widgets.size());
   EXPECT_TRUE(std::equal(expected.begin(), expected.end(), widgets.begin()));
+  toplevel->CloseNow();
 }
 
 // Used by DestroyChildWidgetsInOrder. On destruction adds the supplied name to
@@ -3439,6 +3445,7 @@
   EXPECT_TRUE(widget->IsAlwaysOnTop());
   widget->SetAlwaysOnTop(false);
   EXPECT_FALSE(widget->IsAlwaysOnTop());
+  widget->CloseNow();
 }
 
 }  // namespace test
diff --git a/ui/views/window/dialog_delegate_unittest.cc b/ui/views/window/dialog_delegate_unittest.cc
index 9a653e6..42923399 100644
--- a/ui/views/window/dialog_delegate_unittest.cc
+++ b/ui/views/window/dialog_delegate_unittest.cc
@@ -256,6 +256,8 @@
   dialog()->GetWidget()->UpdateWindowTitle();
   EXPECT_EQ(frame1->GetPreferredSize().height(),
             frame2->GetPreferredSize().height());
+
+  dialog2->TearDown();
 }
 
 }  // namespace views